Commit some new networking code, adding integration, broker still not 100%, hasn't been committed
This commit is contained in:
parent
1af6d56e2d
commit
050e69e23f
18 changed files with 1829 additions and 359 deletions
|
@ -20,8 +20,21 @@ function getData(block, key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const acceptableMembers = new Set();
|
||||||
|
acceptableMembers.add("timestamp");
|
||||||
|
acceptableMembers.add("lastHash");
|
||||||
|
acceptableMembers.add("hash");
|
||||||
|
acceptableMembers.add("reward");
|
||||||
|
acceptableMembers.add("payments");
|
||||||
|
acceptableMembers.add("sensorRegistrations");
|
||||||
|
acceptableMembers.add("brokerRegistrations");
|
||||||
|
acceptableMembers.add("integrations");
|
||||||
|
acceptableMembers.add("compensations");
|
||||||
|
acceptableMembers.add("nonce");
|
||||||
|
acceptableMembers.add("difficulty");
|
||||||
|
|
||||||
class Block {
|
class Block {
|
||||||
constructor(timestamp, lastHash, hash, reward, payments, sensorRegistrations, brokerRegistrations, integrations, nonce, difficulty) {
|
constructor(timestamp, lastHash, hash, reward, payments, sensorRegistrations, brokerRegistrations, integrations, compensations, nonce, difficulty) {
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.lastHash = lastHash;
|
this.lastHash = lastHash;
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
|
@ -38,6 +51,9 @@ class Block {
|
||||||
if (integrations !== null && integrations.length !== 0) {
|
if (integrations !== null && integrations.length !== 0) {
|
||||||
this.integrations = integrations;
|
this.integrations = integrations;
|
||||||
}
|
}
|
||||||
|
if (compensations !== null && compensations.length !== 0) {
|
||||||
|
this.compensations = compensations;
|
||||||
|
}
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
if (difficulty === undefined) {
|
if (difficulty === undefined) {
|
||||||
this.difficulty = DIFFICULTY;
|
this.difficulty = DIFFICULTY;
|
||||||
|
@ -62,6 +78,10 @@ class Block {
|
||||||
return getData(block, "integrations");
|
return getData(block, "integrations");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getCompensations(block) {
|
||||||
|
return getData(block, "compensations");
|
||||||
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return `Block -
|
return `Block -
|
||||||
Timestamp : ${this.timestamp}
|
Timestamp : ${this.timestamp}
|
||||||
|
@ -78,7 +98,7 @@ class Block {
|
||||||
return new this('Genesis time', '-----', 'f1r57-h45h', null, null, null, null, null, 0, DIFFICULTY);
|
return new this('Genesis time', '-----', 'f1r57-h45h', null, null, null, null, null, 0, DIFFICULTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static hash(timestamp, lastHash, reward, payments, sensorRegistrations, brokerRegistrations, integrations, nonce, difficulty) {
|
static hash(timestamp, lastHash, reward, payments, sensorRegistrations, brokerRegistrations, integrations, compensations, nonce, difficulty) {
|
||||||
//backwards compatible hashing:
|
//backwards compatible hashing:
|
||||||
//if we add a new type of thing to the chain, the hash of previous blocks won't change as it will be undefined
|
//if we add a new type of thing to the chain, the hash of previous blocks won't change as it will be undefined
|
||||||
let hashing = `${timestamp}${lastHash}${nonce}${difficulty}${reward}`;
|
let hashing = `${timestamp}${lastHash}${nonce}${difficulty}${reward}`;
|
||||||
|
@ -86,6 +106,7 @@ class Block {
|
||||||
hashing = concatIfNotUndefined(hashing, 'sensorRegistrations', sensorRegistrations);
|
hashing = concatIfNotUndefined(hashing, 'sensorRegistrations', sensorRegistrations);
|
||||||
hashing = concatIfNotUndefined(hashing, 'brokerRegistrations', brokerRegistrations);
|
hashing = concatIfNotUndefined(hashing, 'brokerRegistrations', brokerRegistrations);
|
||||||
hashing = concatIfNotUndefined(hashing, 'integrations', integrations);
|
hashing = concatIfNotUndefined(hashing, 'integrations', integrations);
|
||||||
|
hashing = concatIfNotUndefined(hashing, 'compensations', compensations);
|
||||||
|
|
||||||
return ChainUtil.hash(hashing).toString();
|
return ChainUtil.hash(hashing).toString();
|
||||||
}
|
}
|
||||||
|
@ -99,6 +120,7 @@ class Block {
|
||||||
block.sensorRegistrations,
|
block.sensorRegistrations,
|
||||||
block.brokerRegistrations,
|
block.brokerRegistrations,
|
||||||
block.integrations,
|
block.integrations,
|
||||||
|
block.compensations,
|
||||||
block.nonce,
|
block.nonce,
|
||||||
block.difficulty);
|
block.difficulty);
|
||||||
}
|
}
|
||||||
|
@ -127,7 +149,7 @@ class Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static debugMine(lastBlock, reward, payments, sensorRegistrations,brokerRegistrations,integrations) {
|
static debugMine(lastBlock, reward, payments, sensorRegistrations,brokerRegistrations,integrations,compensations) {
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const difficulty = Block.adjustDifficulty(lastBlock, timestamp);
|
const difficulty = Block.adjustDifficulty(lastBlock, timestamp);
|
||||||
|
|
||||||
|
@ -144,6 +166,7 @@ class Block {
|
||||||
sensorRegistrations,
|
sensorRegistrations,
|
||||||
brokerRegistrations,
|
brokerRegistrations,
|
||||||
integrations,
|
integrations,
|
||||||
|
compensations,
|
||||||
nonce,
|
nonce,
|
||||||
difficulty);
|
difficulty);
|
||||||
} while (hash.substring(0, difficulty) !== '0'.repeat(difficulty));
|
} while (hash.substring(0, difficulty) !== '0'.repeat(difficulty));
|
||||||
|
@ -157,9 +180,116 @@ class Block {
|
||||||
sensorRegistrations,
|
sensorRegistrations,
|
||||||
brokerRegistrations,
|
brokerRegistrations,
|
||||||
integrations,
|
integrations,
|
||||||
|
compensations,
|
||||||
nonce,
|
nonce,
|
||||||
difficulty);
|
difficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static validateIsBlock(block) {
|
||||||
|
if (!(block instanceof Object)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Is not an object"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in block) {
|
||||||
|
if (!acceptableMembers.has(key)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: `Block has key not in acceptable members`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("timestamp" in block)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Block doesn't have a timestamp"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const timestampRes = ChainUtil.validateIsIntegerWithMin(block.timestamp, 0);
|
||||||
|
if (!timestampRes.result) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Timestamp validation failed: " + timestampRes.reason
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("lastHash" in block)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Block doesn't have lastHash"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const lastHashRes = ChainUtil.validateIsString(block.lastHash);
|
||||||
|
if (!lastHashRes.result) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "lastHash validation failed: " + lastHashRes.reason
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("hash" in block)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Block doesn't have hash"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const hashRes = ChainUtil.validateIsString(block.hash);
|
||||||
|
if (!hashRes.result) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "hash validation failed: " + hashRes.reason
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("reward" in block)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Block doesn't have reward"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const rewardRes = ChainUtil.validateIsPublicKey(block.reward);
|
||||||
|
if (!rewardRes.result) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "reward validation failed: " + rewardRes.reason
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("nonce" in block)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Block doesn't have nonce"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const nonceRes = ChainUtil.validateIsIntegerWithMin(block.nonce);
|
||||||
|
if (!nonceRes.result) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "nonce validation failed: " + nonceRes.reason
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!("difficulty" in block)) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Block doesn't have difficulty"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const difficultyRes = ChainUtil.validateIsIntegerWithMin(block.difficulty);
|
||||||
|
if (!difficultyRes.result) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "difficulty validation failed: " + difficultyRes.reason
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: true
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Block;
|
module.exports = Block;
|
File diff suppressed because it is too large
Load diff
|
@ -21,14 +21,14 @@ function validateMetadata(t) {
|
||||||
|
|
||||||
for (const triple of t) {
|
for (const triple of t) {
|
||||||
switch (triple.p) {
|
switch (triple.p) {
|
||||||
case "IoT device metadata/Cost_of_Using_IoT_Devices/Cost_Per_Minute": costPerMinute.push(triple); break;
|
case "http://SSM/Cost_of_Using_IoT_Devices/Cost_Per_Minute": costPerMinute.push(triple); break;
|
||||||
case "IoT device metadata/Cost_of_Using_IoT_Devices/Cost_Per_Kbyte": costPerKB.push(triple); break;
|
case "http://SSM/Cost_of_Using_IoT_Devices/Cost_Per_Kbyte": costPerKB.push(triple); break;
|
||||||
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#type":
|
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#type":
|
||||||
if (triple.o === "SSM/Broker") {
|
if (triple.o === "http://SSM/Broker") {
|
||||||
isBroker.push(triple.s);
|
isBroker.push(triple.s);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "IoT device metadata/Integration/Endpoint": integrationEndpoint.push(triple); break;
|
case "http://SSM/Integration/Endpoint": integrationEndpoint.push(triple); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
65
blockchain/compensation.js
Normal file
65
blockchain/compensation.js
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
const ChainUtil = require('../chain-util');
|
||||||
|
const Integration = require('./integration');
|
||||||
|
|
||||||
|
const integrationValidation = {
|
||||||
|
input: ChainUtil.valdiateIsPublicKey,
|
||||||
|
counter: ChainUtil.createValidateIsIntegerWithMin(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
const baseValidation = {
|
||||||
|
input: ChainUtil.validateIsPublicKey,
|
||||||
|
brokerName: ChainUtil.validateIsString,
|
||||||
|
integration: ChainUtil.createValidateObject(integrationValidation),
|
||||||
|
signature: ChainUtil.validateIsSignature
|
||||||
|
};
|
||||||
|
|
||||||
|
class Compensation {
|
||||||
|
constructor(senderKeyPair, brokerName, integration) {
|
||||||
|
const verifyIntegration = Integration.verify(integration);
|
||||||
|
|
||||||
|
if (!verifyIntegration.result) {
|
||||||
|
throw new Error(verifyIntegration.reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.input = senderKeyPair.getPublic().encode('hex');
|
||||||
|
this.brokerName = brokerName;
|
||||||
|
this.integration = {
|
||||||
|
input: integration.input,
|
||||||
|
counter: integration.counter
|
||||||
|
};
|
||||||
|
this.signature = senderKeyPair.sign(Compensation.hashToSign(this));
|
||||||
|
|
||||||
|
const verification = Compensation.verify(this);
|
||||||
|
if (!verification.result) {
|
||||||
|
throw new Error(verification.reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static hashToSign(transaction) {
|
||||||
|
return ChainUtil.hash([
|
||||||
|
transaction.input,
|
||||||
|
transaction.brokerName,
|
||||||
|
transaction.integration]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static verify(transaction) {
|
||||||
|
const validationRes = ChainUtil.validateObject(transaction, baseValidation);
|
||||||
|
if (!validationRes.result) {
|
||||||
|
return validationRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyRes = ChainUtil.verifySignature(
|
||||||
|
transaction.input,
|
||||||
|
transaction.signature,
|
||||||
|
Compensation.hashToSign(transaction));
|
||||||
|
if (!verifyRes.result) {
|
||||||
|
return verifyRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Compensation;
|
|
@ -1,23 +1,32 @@
|
||||||
const ChainUtil = require('../chain-util');
|
const ChainUtil = require('../chain-util');
|
||||||
|
const SeedRandom = require('seedrandom');
|
||||||
|
|
||||||
const outputValidation = {
|
const outputValidation = {
|
||||||
publicKey: ChainUtil.validateIsPublicKey,
|
publicKey: ChainUtil.validateIsPublicKey,
|
||||||
sensor: ChainUtil.validateIsString,
|
sensor: ChainUtil.validateIsString,
|
||||||
amount: ChainUtil.createValidateIsIntegerWithMin(1),
|
amount: ChainUtil.createValidateIsIntegerWithMin(1),
|
||||||
|
counter: ChainUtil.createValidateIsIntegerWithMin(1)
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateOutputs(t) {
|
function validateOutputs(t) {
|
||||||
if (!ChainUtil.validateArray(t, (output) => {
|
const validateArrayRes = ChainUtil.validateArray(t, (output) => {
|
||||||
return ChainUtil.validateObject(output, outputValidation).result;
|
return ChainUtil.validateObject(output, outputValidation);
|
||||||
})) {
|
});
|
||||||
return false;
|
|
||||||
|
if (!validateArrayRes.result) {
|
||||||
|
return validateArrayRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t.outputs.length <= 0) {
|
if (t.length <= 0) {
|
||||||
return false;
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Integration must have at least 1 output"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return {
|
||||||
|
result: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseValidation = {
|
const baseValidation = {
|
||||||
|
@ -25,17 +34,19 @@ const baseValidation = {
|
||||||
counter: ChainUtil.createValidateIsIntegerWithMin(1),
|
counter: ChainUtil.createValidateIsIntegerWithMin(1),
|
||||||
rewardAmount: ChainUtil.createValidateIsIntegerWithMin(0),
|
rewardAmount: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||||
outputs: validateOutputs,
|
outputs: validateOutputs,
|
||||||
|
witnessCount: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||||
signature: ChainUtil.validateIsSignature
|
signature: ChainUtil.validateIsSignature
|
||||||
};
|
};
|
||||||
|
|
||||||
class Integration {
|
class Integration {
|
||||||
constructor(senderKeyPair, counter, outputs, rewardAmount) {
|
constructor(senderKeyPair, counter, outputs, witnessCount, rewardAmount) {
|
||||||
this.input = senderKeyPair.getPublic().encode('hex');
|
this.input = senderKeyPair.getPublic().encode('hex');
|
||||||
this.counter = counter;
|
this.counter = counter;
|
||||||
this.rewardAmount = rewardAmount;
|
this.rewardAmount = rewardAmount;
|
||||||
this.outputs = outputs;
|
this.outputs = outputs;
|
||||||
this.signature = senderKeyPair.sign(Integration.hashToSign(this));
|
this.witnessCount = witnessCount;
|
||||||
|
|
||||||
|
this.signature = senderKeyPair.sign(Integration.hashToSign(this));
|
||||||
|
|
||||||
const verification = Integration.verify(this);
|
const verification = Integration.verify(this);
|
||||||
if (!verification.result) {
|
if (!verification.result) {
|
||||||
|
@ -43,31 +54,33 @@ class Integration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static createOutput(recipientPublicKey, sensorId, amount) {
|
static createOutput(recipientPublicKey, sensorId, amount, counter) {
|
||||||
return {
|
return {
|
||||||
publicKey: recipientPublicKey,
|
publicKey: recipientPublicKey,
|
||||||
sensor: sensorId,
|
sensor: sensorId,
|
||||||
amount: amount
|
amount: amount,
|
||||||
|
counter: counter
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static hashToSign(registration) {
|
static hashToSign(integration) {
|
||||||
return ChainUtil.hash([
|
return ChainUtil.hash([
|
||||||
registration.counter,
|
integration.counter,
|
||||||
registration.rewardAmount,
|
integration.rewardAmount,
|
||||||
registration.outputs]);
|
integration.witnesses,
|
||||||
|
integration.outputs]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static verify(registration) {
|
static verify(integration) {
|
||||||
const validationRes = ChainUtil.validateObject(registration, baseValidation);
|
const validationRes = ChainUtil.validateObject(integration, baseValidation);
|
||||||
if (!validationRes.result) {
|
if (!validationRes.result) {
|
||||||
return validationRes;
|
return validationRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const verifyRes = ChainUtil.verifySignature(
|
const verifyRes = ChainUtil.verifySignature(
|
||||||
registration.input,
|
integration.input,
|
||||||
registration.signature,
|
integration.signature,
|
||||||
Integration.hashToSign(registration));
|
Integration.hashToSign(integration));
|
||||||
if (!verifyRes.result) {
|
if (!verifyRes.result) {
|
||||||
return verifyRes;
|
return verifyRes;
|
||||||
}
|
}
|
||||||
|
@ -76,6 +89,45 @@ class Integration {
|
||||||
result: true
|
result: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static chooseWitnesses(integration, brokerList) {
|
||||||
|
const brokerListCopy = [...brokerList];
|
||||||
|
brokerListCopy.sort();
|
||||||
|
|
||||||
|
const witnessCount = integration.witnessCount;
|
||||||
|
|
||||||
|
if (witnessCount > brokerList.length) {
|
||||||
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Not enough brokers for the number of witnesses requested"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (witnessCount === brokerList.length) {
|
||||||
|
return {
|
||||||
|
result: true,
|
||||||
|
witnesses: brokerListCopy
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const rng = new SeedRandom.alea(integration.signature, Integration.hashToSign(integration));
|
||||||
|
|
||||||
|
const witnesses = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < witnessCount; ++i) {
|
||||||
|
const chosen = Math.floor(rng() * brokerListCopy.length);
|
||||||
|
|
||||||
|
witnesses.push(brokerListCopy[chosen]);
|
||||||
|
brokerListCopy[chosen] = brokerListCopy[brokerListCopy.length - 1];
|
||||||
|
brokerListCopy.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
result: true,
|
||||||
|
witnesses: witnesses
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Integration;
|
module.exports = Integration;
|
43
blockchain/integration.test.js
Normal file
43
blockchain/integration.test.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
const Integration = require('./integration');
|
||||||
|
const ChainUtil = require('../chain-util');
|
||||||
|
|
||||||
|
function createDummyIntegration(keyPair, witnesses) {
|
||||||
|
return new Integration(
|
||||||
|
keyPair,
|
||||||
|
1,
|
||||||
|
[Integration.createOutput(keyPair.getPublic().encode('hex'), 'a', 5, 1)],
|
||||||
|
witnesses,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Integration', () => {
|
||||||
|
let keyPair;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
keyPair = ChainUtil.genKeyPair();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Choose witnesses doesn't care about brokers ordering, 1 witness", () => {
|
||||||
|
const brokers_f = ['a', 'b', 'c'];
|
||||||
|
const brokers_b = ['c', 'b', 'a'];
|
||||||
|
|
||||||
|
const integration = createDummyIntegration(keyPair, 1);
|
||||||
|
expect(Integration.chooseWitnesses(integration, brokers_f)).toEqual(Integration.chooseWitnesses(integration, brokers_b));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Choose witnesses doesn't care about brokers ordering, 2 witness", () => {
|
||||||
|
const brokers_f = ['a', 'b', 'c'];
|
||||||
|
const brokers_b = ['c', 'b', 'a'];
|
||||||
|
|
||||||
|
const integration = createDummyIntegration(keyPair, 2);
|
||||||
|
expect(Integration.chooseWitnesses(integration, brokers_f)).toEqual(Integration.chooseWitnesses(integration, brokers_b));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Choose witnesses doesn't care about brokers ordering, 3 witness", () => {
|
||||||
|
const brokers_f = ['a', 'b', 'c'];
|
||||||
|
const brokers_b = ['c', 'b', 'a'];
|
||||||
|
|
||||||
|
const integration = createDummyIntegration(keyPair, 3);
|
||||||
|
expect(Integration.chooseWitnesses(integration, brokers_f)).toEqual(Integration.chooseWitnesses(integration, brokers_b));
|
||||||
|
});
|
||||||
|
});
|
|
@ -6,17 +6,23 @@ const outputValidation = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateOutputs(t) {
|
function validateOutputs(t) {
|
||||||
if (!ChainUtil.validateArray(t, function (output) {
|
let validateRes = ChainUtil.validateArray(t, function (output) {
|
||||||
return ChainUtil.validateObject(output, outputValidation).result;
|
return ChainUtil.validateObject(output, outputValidation);
|
||||||
})) {
|
});
|
||||||
return false;
|
if (!validateRes.result) {
|
||||||
|
return validateRes
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t.length <= 0) {
|
if (t.length <= 0) {
|
||||||
return false;
|
return {
|
||||||
|
result: false,
|
||||||
|
reason: "Outputs length isn't positive"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return {
|
||||||
|
result: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseValidation = {
|
const baseValidation = {
|
||||||
|
|
|
@ -20,14 +20,14 @@ function validateMetadata(t) {
|
||||||
|
|
||||||
for (const triple of t) {
|
for (const triple of t) {
|
||||||
switch (triple.p) {
|
switch (triple.p) {
|
||||||
case "IoT device metadata/Cost_of_Using_IoT_Devices/Cost_Per_Minute": costPerMinute.push(triple); break;
|
case "http://SSM/Cost_of_Using_IoT_Devices/Cost_Per_Minute": costPerMinute.push(triple); break;
|
||||||
case "IoT device metadata/Cost_of_Using_IoT_Devices/Cost_Per_Kbyte": costPerKB.push(triple); break;
|
case "http://SSM/Cost_of_Using_IoT_Devices/Cost_Per_Kbyte": costPerKB.push(triple); break;
|
||||||
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#type":
|
case "http://www.w3.org/1999/02/22-rdf-syntax-ns#type":
|
||||||
if (triple.o === "http://www.w3.org/ns/sosa/Sensor") {
|
if (triple.o === "http://www.w3.org/ns/sosa/Sensor") {
|
||||||
isSensor.push(triple.s);
|
isSensor.push(triple.s);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "IoT device metadata/Integration/Broker": integrationBroker.push(triple); break;
|
case "http://SSM/Integration/Broker": integrationBroker.push(triple); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,9 @@ const DIFFICULTY = 3;
|
||||||
const MINE_RATE = 3000;
|
const MINE_RATE = 3000;
|
||||||
const MINING_REWARD = 50;
|
const MINING_REWARD = 50;
|
||||||
|
|
||||||
|
const DEFAULT_UI_HTML = "./ui/wallet-ui.html";
|
||||||
|
const DEFAULT_UI_JS = "./ui/wallet-logic.js";
|
||||||
|
|
||||||
const DEFAULT_PORT_MINER_BASE = 3000;
|
const DEFAULT_PORT_MINER_BASE = 3000;
|
||||||
const DEFAULT_PORT_MINER_API = DEFAULT_PORT_MINER_BASE + 1;
|
const DEFAULT_PORT_MINER_API = DEFAULT_PORT_MINER_BASE + 1;
|
||||||
const DEFAULT_PORT_MINER_CHAIN = DEFAULT_PORT_MINER_BASE + 2;
|
const DEFAULT_PORT_MINER_CHAIN = DEFAULT_PORT_MINER_BASE + 2;
|
||||||
|
@ -28,6 +31,9 @@ module.exports = {
|
||||||
MINE_RATE,
|
MINE_RATE,
|
||||||
MINING_REWARD,
|
MINING_REWARD,
|
||||||
|
|
||||||
|
DEFAULT_UI_HTML,
|
||||||
|
DEFAULT_UI_JS,
|
||||||
|
|
||||||
DEFAULT_PORT_MINER_API,
|
DEFAULT_PORT_MINER_API,
|
||||||
DEFAULT_PORT_MINER_CHAIN,
|
DEFAULT_PORT_MINER_CHAIN,
|
||||||
DEFAULT_PORT_MINER_TX_SHARE,
|
DEFAULT_PORT_MINER_TX_SHARE,
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const P2pServer = require('../p2p-server');
|
const P2pServer = require('../p2p-server');
|
||||||
|
const BlockchainProp = require('../network/blockchain-prop');
|
||||||
const QueryEngine = require('@comunica/query-sparql-rdfjs').QueryEngine;
|
const QueryEngine = require('@comunica/query-sparql-rdfjs').QueryEngine;
|
||||||
|
|
||||||
const Blockchain = require('../blockchain/blockchain');
|
const Blockchain = require('../blockchain/blockchain');
|
||||||
const Miner = require('./miner');
|
const Miner = require('./miner');
|
||||||
'use strict';/* "use strict" is to indicate that the code should be executed in "strict mode".
|
'use strict';/* "use strict" is to indicate that the code should be executed in "strict mode".
|
||||||
|
@ -73,17 +73,9 @@ const chainServerPeers = config.get({
|
||||||
key: "miner-chain-server-peers",
|
key: "miner-chain-server-peers",
|
||||||
default: []
|
default: []
|
||||||
});
|
});
|
||||||
const txShareServerPort = config.get({
|
const minerPublicAddress = config.get({
|
||||||
key: "miner-tx-share-server-port",
|
key: "miner-public-address",
|
||||||
default: DEFAULT_PORT_MINER_TX_SHARE
|
default: "-"
|
||||||
});
|
|
||||||
const txShareServerPeers = config.get({
|
|
||||||
key: "miner-tx-share-server-peers",
|
|
||||||
default: []
|
|
||||||
});
|
|
||||||
const txRecvServerPort = config.get({
|
|
||||||
key: "miner-tx-recv-port",
|
|
||||||
default: DEFAULT_PORT_MINER_TX_RECV
|
|
||||||
});
|
});
|
||||||
const apiPort = config.get({
|
const apiPort = config.get({
|
||||||
key: "miner-api-port",
|
key: "miner-api-port",
|
||||||
|
@ -92,42 +84,10 @@ const apiPort = config.get({
|
||||||
|
|
||||||
const blockchain = Blockchain.loadFromDisk(blockchainLocation);
|
const blockchain = Blockchain.loadFromDisk(blockchainLocation);
|
||||||
|
|
||||||
function onMined(block) {
|
const chainServer = new BlockchainProp("Chain-server", true, blockchain);
|
||||||
if (!blockchain.addBlock(block)) {
|
const miner = new Miner(blockchain, minerPublicKey);
|
||||||
//invalid block, return
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
miner.onNewBlock(block);
|
chainServer.start(chainServerPort, minerPublicAddress, chainServerPeers);
|
||||||
blockchain.saveToDisk(blockchainLocation);
|
|
||||||
chainServer.broadcast(blockchain.serialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChainServerConnect(socket) {
|
|
||||||
console.log("onChainServerConnect");
|
|
||||||
P2pServer.send(socket, blockchain.serialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
function onChainServerRecv(data) {
|
|
||||||
const replaceResult = blockchain.replaceChain(data);
|
|
||||||
if (!replaceResult.result) {
|
|
||||||
//failed to replace
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = replaceResult.chainDifference; i < blockchain.chain.length; i++) {
|
|
||||||
miner.onNewBlock(blockchain.chain[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
blockchain.saveToDisk(blockchainLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
const chainServer = new P2pServer("Chain-server");
|
|
||||||
const txShareServer = new P2pServer("Tx-share-server");
|
|
||||||
const txRecvServer = new P2pServer("Tx-share-server");
|
|
||||||
const miner = new Miner(blockchain, minerPublicKey, onMined);
|
|
||||||
|
|
||||||
chainServer.start(chainServerPort, chainServerPeers, onChainServerConnect, onChainServerRecv);
|
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const myEngine = new QueryEngine();
|
const myEngine = new QueryEngine();
|
||||||
|
|
|
@ -4,6 +4,7 @@ const Payment = require('../blockchain/payment');
|
||||||
const Integration = require('../blockchain/integration');
|
const Integration = require('../blockchain/integration');
|
||||||
const SensorRegistration = require('../blockchain/sensor-registration');
|
const SensorRegistration = require('../blockchain/sensor-registration');
|
||||||
const BrokerRegistration = require('../blockchain/broker-registration');
|
const BrokerRegistration = require('../blockchain/broker-registration');
|
||||||
|
const Compensation = require('../blockchain/compensation');
|
||||||
|
|
||||||
const ITERATIONS = 1;
|
const ITERATIONS = 1;
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ function mine(miner) {
|
||||||
miner.txs.sensorRegistrations.mining,
|
miner.txs.sensorRegistrations.mining,
|
||||||
miner.txs.brokerRegistrations.mining,
|
miner.txs.brokerRegistrations.mining,
|
||||||
miner.txs.integrations.mining,
|
miner.txs.integrations.mining,
|
||||||
|
miner.txs.compensations.mining,
|
||||||
miner.nonce,
|
miner.nonce,
|
||||||
difficulty);
|
difficulty);
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ function mine(miner) {
|
||||||
//success
|
//success
|
||||||
const endTime = process.hrtime.bigint();
|
const endTime = process.hrtime.bigint();
|
||||||
console.log(`Mined a block of difficulty ${difficulty} in ${Number(endTime - miner.minedStartTime) / 1000000}ms`);
|
console.log(`Mined a block of difficulty ${difficulty} in ${Number(endTime - miner.minedStartTime) / 1000000}ms`);
|
||||||
miner.onMined(new Block(
|
miner.blockchain.addBlock(new Block(
|
||||||
timestamp,
|
timestamp,
|
||||||
miner.lastBlock.hash,
|
miner.lastBlock.hash,
|
||||||
hash,
|
hash,
|
||||||
|
@ -43,6 +45,7 @@ function mine(miner) {
|
||||||
miner.txs.sensorRegistrations.mining,
|
miner.txs.sensorRegistrations.mining,
|
||||||
miner.txs.brokerRegistrations.mining,
|
miner.txs.brokerRegistrations.mining,
|
||||||
miner.txs.integrations.mining,
|
miner.txs.integrations.mining,
|
||||||
|
miner.txs.compensations.mining,
|
||||||
miner.nonce,
|
miner.nonce,
|
||||||
difficulty));
|
difficulty));
|
||||||
miner.state = STATE_INTERRUPTED;
|
miner.state = STATE_INTERRUPTED;
|
||||||
|
@ -69,8 +72,7 @@ function startMine(miner) {
|
||||||
miner.txs.integrations.mining = [...miner.txs.integrations.pool];
|
miner.txs.integrations.mining = [...miner.txs.integrations.pool];
|
||||||
miner.txs.sensorRegistrations.mining = [...miner.txs.sensorRegistrations.pool];
|
miner.txs.sensorRegistrations.mining = [...miner.txs.sensorRegistrations.pool];
|
||||||
miner.txs.brokerRegistrations.mining = [...miner.txs.brokerRegistrations.pool];
|
miner.txs.brokerRegistrations.mining = [...miner.txs.brokerRegistrations.pool];
|
||||||
|
miner.txs.compensations.mining = [...miner.txs.compensations.pool];
|
||||||
miner.lastBlock = miner.blockchain.chain[miner.blockchain.chain.length - 1];
|
|
||||||
|
|
||||||
miner.nonce = 0;
|
miner.nonce = 0;
|
||||||
miner.state = STATE_RUNNING;
|
miner.state = STATE_RUNNING;
|
||||||
|
@ -89,23 +91,24 @@ function clearFromBlock(miner, txs, blockTxs) {
|
||||||
if (foundIndex !== -1) {
|
if (foundIndex !== -1) {
|
||||||
txs.pool.splice(foundIndex, 1);
|
txs.pool.splice(foundIndex, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (txs.mining.some(findTx(tx))) {
|
|
||||||
miner.state = STATE_INTERRUPTED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Miner {
|
class Miner {
|
||||||
constructor(blockchain, reward, onMined) {
|
constructor(blockchain, reward) {
|
||||||
this.blockchain = blockchain;
|
this.lastBlock = blockchain.lastBlock();
|
||||||
this.onMined = onMined;
|
|
||||||
this.state = STATE_INTERRUPTED;
|
this.state = STATE_INTERRUPTED;
|
||||||
this.lastBlock = null;
|
|
||||||
this.reward = reward;
|
this.reward = reward;
|
||||||
|
|
||||||
this.minedStartTime = null;
|
this.minedStartTime = null;
|
||||||
|
|
||||||
|
this.blockchain = blockchain;
|
||||||
|
blockchain.addListener((newBlocks, oldBlocks, difference) => {
|
||||||
|
for (var i = difference; i < newBlocks.length; i++) {
|
||||||
|
this.onNewBlock(newBlocks[i]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.txs = {
|
this.txs = {
|
||||||
payments: {
|
payments: {
|
||||||
pool: [],
|
pool: [],
|
||||||
|
@ -122,6 +125,10 @@ class Miner {
|
||||||
brokerRegistrations: {
|
brokerRegistrations: {
|
||||||
pool: [],
|
pool: [],
|
||||||
mining: []
|
mining: []
|
||||||
|
},
|
||||||
|
compensations: {
|
||||||
|
pool: [],
|
||||||
|
mining: []
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,6 +149,7 @@ class Miner {
|
||||||
case Integration: txs = this.txs.integrations; break;
|
case Integration: txs = this.txs.integrations; break;
|
||||||
case SensorRegistration: txs = this.txs.sensorRegistrations; break;
|
case SensorRegistration: txs = this.txs.sensorRegistrations; break;
|
||||||
case BrokerRegistration: txs = this.txs.brokerRegistrations; break;
|
case BrokerRegistration: txs = this.txs.brokerRegistrations; break;
|
||||||
|
case Compensation: txs = this.txs.compensations; break;
|
||||||
default: throw new Error(`unknown tx type: ${tx.type.name()}`);
|
default: throw new Error(`unknown tx type: ${tx.type.name()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +170,11 @@ class Miner {
|
||||||
clearFromBlock(this, this.txs.integrations, Block.getIntegrations(block));
|
clearFromBlock(this, this.txs.integrations, Block.getIntegrations(block));
|
||||||
clearFromBlock(this, this.txs.sensorRegistrations, Block.getSensorRegistrations(block));
|
clearFromBlock(this, this.txs.sensorRegistrations, Block.getSensorRegistrations(block));
|
||||||
clearFromBlock(this, this.txs.brokerRegistrations, Block.getBrokerRegistrations(block));
|
clearFromBlock(this, this.txs.brokerRegistrations, Block.getBrokerRegistrations(block));
|
||||||
|
clearFromBlock(this, this.txs.compensations, Block.getCompensations(block));
|
||||||
|
|
||||||
|
this.state = STATE_INTERRUPTED;
|
||||||
|
|
||||||
|
this.lastBlock = block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
283
network/blockchain-prop.js
Normal file
283
network/blockchain-prop.js
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
const Websocket = require('ws');
|
||||||
|
const Assert = require('assert');
|
||||||
|
const ChainUtil = require('../chain-util');
|
||||||
|
const Block = require('../blockchain/block');
|
||||||
|
const Blockchain = require('../blockchain/blockchain');
|
||||||
|
|
||||||
|
const STATE_INIT = 0;
|
||||||
|
const STATE_CONNECTING = 1;
|
||||||
|
const STATE_RUNNING = 2;
|
||||||
|
|
||||||
|
const PEER_OK = 0;
|
||||||
|
const PEER_DEAD = 1;
|
||||||
|
|
||||||
|
const chainValidation = {
|
||||||
|
start: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||||
|
blocks: ChainUtil.createValidateArray(Block.validateIsBlock)
|
||||||
|
};
|
||||||
|
|
||||||
|
class Connection {
|
||||||
|
constructor(parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.address = null;
|
||||||
|
this.socket = null;
|
||||||
|
this.state = STATE_INIT;
|
||||||
|
|
||||||
|
this.prev = null;
|
||||||
|
this.next = null;
|
||||||
|
|
||||||
|
this.blockIndex = null;
|
||||||
|
|
||||||
|
this.queue = null;
|
||||||
|
this.queueTimer = null;
|
||||||
|
|
||||||
|
this.sub = {
|
||||||
|
txs: false
|
||||||
|
};
|
||||||
|
|
||||||
|
this.logName = `${parent.logName}:${parent.connectionCounter}`;
|
||||||
|
parent.connectionCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
accepted(socket) {
|
||||||
|
console.log(`${this.logName} accepted`);
|
||||||
|
this.socket = socket;
|
||||||
|
this.state = STATE_RUNNING;
|
||||||
|
|
||||||
|
this.socket.on("error", () => {
|
||||||
|
this.onError();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on("open", () => {
|
||||||
|
this.onConnection();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on("message", (data) => {
|
||||||
|
this.onMessage(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.onConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(address) {
|
||||||
|
console.log(`${this.logName} connecting`);
|
||||||
|
this.address = address;
|
||||||
|
this.state = STATE_CONNECTING;
|
||||||
|
|
||||||
|
this.reconnectWait = 1;
|
||||||
|
this.socket = new Websocket(this.address);
|
||||||
|
|
||||||
|
this.socket.on("error", () => {
|
||||||
|
this.onError();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on("open", () => {
|
||||||
|
this.onConnection();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on("message", (data) => {
|
||||||
|
this.onMessage(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
retryDead(address) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onError() {
|
||||||
|
switch (this.state) {
|
||||||
|
case STATE_CONNECTING:
|
||||||
|
//this.reconnectWait seconds + random [0,1000] ms
|
||||||
|
setTimeout(() => this.socket = new Websocket(this.address),
|
||||||
|
1000 * this.reconnectWait + Math.floor(Math.random() * 1000));
|
||||||
|
this.reconnectWait *= 2;
|
||||||
|
if (this.reconnectWait > 64) {
|
||||||
|
this.reconnectWait = 64;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_RUNNING:
|
||||||
|
this.socket.close();
|
||||||
|
this.next.prev = this.prev;
|
||||||
|
this.prev.next = this.next;
|
||||||
|
this.next = null;
|
||||||
|
this.prev = null;
|
||||||
|
if (this.address !== null) {
|
||||||
|
this.state = STATE_CONNECTING;
|
||||||
|
this.reconnectWait = 1;
|
||||||
|
this.socket = new Websocket(this.address);
|
||||||
|
} else {
|
||||||
|
//do nothing?
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onConnection() {
|
||||||
|
this.state = STATE_RUNNING;
|
||||||
|
|
||||||
|
this.prev = this.parent.connected;
|
||||||
|
this.next = this.parent.connected.next;
|
||||||
|
this.next.prev = this;
|
||||||
|
this.prev.next = this;
|
||||||
|
|
||||||
|
const sending = {
|
||||||
|
sub: {
|
||||||
|
txs: this.parent.subTxs
|
||||||
|
},
|
||||||
|
address: this.parent.myAddress
|
||||||
|
};
|
||||||
|
|
||||||
|
const blocks = this.parent.blockchain.blocks();
|
||||||
|
|
||||||
|
if (blocks.length > 1) {
|
||||||
|
sending.chain = {
|
||||||
|
blocks: blocks.slice(1),
|
||||||
|
start: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.send(JSON.stringify(sending));
|
||||||
|
|
||||||
|
this.blockIndex = blocks.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
onQueueTimer() {
|
||||||
|
this.queueTimer = null;
|
||||||
|
if (this.state !== STATE_RUNNING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checkSend();
|
||||||
|
|
||||||
|
// we don't retimer as we wait for external to send
|
||||||
|
}
|
||||||
|
|
||||||
|
onMessage(event) {
|
||||||
|
var recved = null;
|
||||||
|
try {
|
||||||
|
recved = JSON.parse(event);
|
||||||
|
} catch (ex) {
|
||||||
|
console.log(`Bad message on ${this.logName}, not a json object`);
|
||||||
|
this.onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("chain" in recved) {
|
||||||
|
const validationRes = ChainUtil.validateObject(recved.chain, chainValidation);
|
||||||
|
if (!validationRes.result) {
|
||||||
|
console.log(`${this.logName} couldn't validate chain message: ${validationRes.reason}`);
|
||||||
|
this.onError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${this.logName} recved chain with start: ${recved.chain.start}`);
|
||||||
|
|
||||||
|
var newBlocks = this.parent.blockchain.blocks().slice(0, recved.chain.start + 1);
|
||||||
|
newBlocks = newBlocks.concat(recved.chain.blocks);
|
||||||
|
|
||||||
|
this.parent.updatingConnection = this;
|
||||||
|
this.parent.blockchain.replaceChain(newBlocks);
|
||||||
|
this.parent.updatingConnection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendChain(oldBlocks, blocks) {
|
||||||
|
if (this.queue === null) {
|
||||||
|
this.queue = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var startIndex = this.blockIndex - 1;
|
||||||
|
|
||||||
|
while (oldBlocks[startIndex].hash !== blocks[startIndex].hash) {
|
||||||
|
startIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queue.chain = {
|
||||||
|
blocks: blocks.slice(startIndex + 1),
|
||||||
|
start: startIndex + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
this.checkSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSend() {
|
||||||
|
if (this.queue === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.socket.bufferedAmount === 0) {
|
||||||
|
this.socket.send(JSON.stringify(this.queue));
|
||||||
|
|
||||||
|
if ("chain" in this.queue) {
|
||||||
|
this.blockIndex = this.queue.chain.start + this.queue.chain.blocks.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queue = null;
|
||||||
|
} else if (this.queueTimer === null) {
|
||||||
|
this.queueTimer = setTimeout(this.onQueueTimer, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBlocksImpl(server, newBlocks, oldBlocks) {
|
||||||
|
if (server.updatingConnection !== null) {
|
||||||
|
server.updatingConnection.blockIndex = blocks.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var connection = server.connected.next; connection !== server.connected; connection = connection.next) {
|
||||||
|
if (connection === server.updatingConnection) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
connection.sendChain(oldBlocks, newBlocks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//this acts as a publisher, and subscriber
|
||||||
|
class PropServer {
|
||||||
|
constructor(logName, subTxs, blockchain) {
|
||||||
|
this.logName = logName;
|
||||||
|
this.peerState = new Map();
|
||||||
|
this.connected = {
|
||||||
|
next: null,
|
||||||
|
prev: null
|
||||||
|
};
|
||||||
|
this.connected.next = this.connected;
|
||||||
|
this.connected.prev = this.connected;
|
||||||
|
this.blockchain = blockchain;
|
||||||
|
this.blockchain.addListener((newBlocks, oldBlocks, difference) => {
|
||||||
|
updateBlocksImpl(this, newBlocks, oldBlocks);
|
||||||
|
});
|
||||||
|
this.port = null;
|
||||||
|
this.myAddress = null;
|
||||||
|
this.server = null;
|
||||||
|
this.connectionCounter = 0;
|
||||||
|
this.subTxs = subTxs;
|
||||||
|
this.updatingConnection = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
start(port, myAddress, peers) {
|
||||||
|
if (this.port !== null) {
|
||||||
|
console.log(`Couldn't start BlockchainPub '${this.logName}', already started`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.port = port;
|
||||||
|
this.myAddress = myAddress;
|
||||||
|
for (const peer of peers) {
|
||||||
|
if (!this.peerState.has(peer)) {
|
||||||
|
this.peerState.set(peer, PEER_OK);
|
||||||
|
|
||||||
|
const connection = new Connection(this);
|
||||||
|
connection.connect(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.server = new Websocket.Server({ port: port });
|
||||||
|
this.server.on('connection', socket => {
|
||||||
|
const connection = new Connection(this);
|
||||||
|
connection.accepted(socket);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PropServer;
|
15
network/test.js
Normal file
15
network/test.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
const PropServer = require('./blockchain-prop');
|
||||||
|
const Block = require('../blockchain/block');
|
||||||
|
|
||||||
|
const s1 = new PropServer('s1', false);
|
||||||
|
const s2 = new PropServer('s2', false);
|
||||||
|
const s3 = new PropServer('s3', false);
|
||||||
|
|
||||||
|
s1.start(9100, 'ws://127.0.0.1:9100', []);
|
||||||
|
s2.start(9101, 'ws://127.0.0.1:9101', ['ws://127.0.0.1:9100']);
|
||||||
|
s3.start(9102, 'ws://127.0.0.1:9102', ['ws://127.0.0.1:9101']);
|
||||||
|
|
||||||
|
const blocks = [Block.genesis()];
|
||||||
|
blocks.push(Block.debugMine(blocks[blocks.length - 1], 'eh', [], [], [], [], []));
|
||||||
|
|
||||||
|
s3.updateBlocks(blocks);
|
|
@ -31,6 +31,7 @@
|
||||||
"mqtt": "^4.1.0",
|
"mqtt": "^4.1.0",
|
||||||
"multer": "^1.3.1",
|
"multer": "^1.3.1",
|
||||||
"n3": "^1.16.3",
|
"n3": "^1.16.3",
|
||||||
|
"seedrandom": "^3.0.5",
|
||||||
"uuid": "^9.0.0",
|
"uuid": "^9.0.0",
|
||||||
"ws": "^8.9.0"
|
"ws": "^8.9.0"
|
||||||
}
|
}
|
||||||
|
|
346
ui/wallet-logic.js
Normal file
346
ui/wallet-logic.js
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
function startSenshamartWalletUI() {
|
||||||
|
const publicKeySpan = document.getElementById("publicKey");
|
||||||
|
const coinCountSpan = document.getElementById("coinCount");
|
||||||
|
const status = document.getElementById("status");
|
||||||
|
|
||||||
|
var currentTab = document.getElementById("payTab");
|
||||||
|
|
||||||
|
const initTab = function (baseName) {
|
||||||
|
const buttonName = baseName + "Button";
|
||||||
|
const button = document.getElementById(buttonName);
|
||||||
|
if (button === null) {
|
||||||
|
console.log("Couldn't find: " + buttonName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tabName = baseName + "Tab";
|
||||||
|
const tab = document.getElementById(tabName);
|
||||||
|
if (tab === null) {
|
||||||
|
console.log("Couldn't find: " + tabName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tab.style.display = "none";
|
||||||
|
|
||||||
|
button.onclick = function (_) {
|
||||||
|
currentTab.style.display = "none";
|
||||||
|
tab.style.display = "initial";
|
||||||
|
currentTab = tab;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
initTab("pay");
|
||||||
|
initTab("query");
|
||||||
|
initTab("sensorInfo");
|
||||||
|
initTab("brokerInfo");
|
||||||
|
initTab("registerSensor");
|
||||||
|
initTab("registerBroker");
|
||||||
|
initTab("integrate");
|
||||||
|
currentTab.style.display = "initial";
|
||||||
|
|
||||||
|
const refreshInfo = {
|
||||||
|
balance: {
|
||||||
|
onNew: [],
|
||||||
|
onDel: [],
|
||||||
|
onChange: [],
|
||||||
|
vals: {}
|
||||||
|
},
|
||||||
|
sensor: {
|
||||||
|
onNew: [],
|
||||||
|
onDel: [],
|
||||||
|
onChange: [],
|
||||||
|
vals: {}
|
||||||
|
},
|
||||||
|
broker: {
|
||||||
|
onNew: [],
|
||||||
|
onDel: [],
|
||||||
|
onChange: [],
|
||||||
|
vals: {}
|
||||||
|
},
|
||||||
|
integration: {
|
||||||
|
onNew: [],
|
||||||
|
onDel: [],
|
||||||
|
onChange: [],
|
||||||
|
vals: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ourPubKey = null;
|
||||||
|
|
||||||
|
//sensorInfo
|
||||||
|
const sensorFilter = document.getElementById("sensorFilter");
|
||||||
|
const inSensorsSelect = {};
|
||||||
|
const sensorsSelect = document.getElementById("sensors");
|
||||||
|
const refreshButton = document.getElementById("refresh");
|
||||||
|
|
||||||
|
|
||||||
|
let refreshCounter = 0;
|
||||||
|
let refreshFailed = false;
|
||||||
|
let loaded = false;
|
||||||
|
|
||||||
|
const statusOK = function (str) {
|
||||||
|
status.innerHTML = str;
|
||||||
|
status.style.backgroundColor = 'lightgreen';
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusWorking = function (str) {
|
||||||
|
status.innerHTML = str;
|
||||||
|
status.style.backgroundColor = 'yellow';
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusError = function (str) {
|
||||||
|
status.innerHTML = str;
|
||||||
|
status.style.backgroundColor = 'red';
|
||||||
|
};
|
||||||
|
|
||||||
|
const refresh = function () {
|
||||||
|
if (loaded !== true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (refreshCounter !== 0) {
|
||||||
|
status.innerHTML = "Couldn't refresh, already currently refreshing";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateInfo = function (type, newData) {
|
||||||
|
const oldData = type.vals;
|
||||||
|
type.vals = newData;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(newData)) {
|
||||||
|
if (!(key in oldData)) {
|
||||||
|
for (const handler of type.onNew) {
|
||||||
|
handler(key, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const handler of type.onChange) {
|
||||||
|
handler(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const [key, value] of Object.entries(oldData)) {
|
||||||
|
if (!(key in newData)) {
|
||||||
|
for (const handler of type.onDel) {
|
||||||
|
handler(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshFetch = function (type, path) {
|
||||||
|
fetch(path).then(function (res) {
|
||||||
|
return res.json();
|
||||||
|
}).then(function (data) {
|
||||||
|
updateInfo(type, data);
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
statusError(`Error: ${err}`);
|
||||||
|
refreshFailed = true;
|
||||||
|
}).finally(function () {
|
||||||
|
refreshCounter--;
|
||||||
|
if (refreshCounter === 0 && refreshFailed === false) {
|
||||||
|
statusOK("Refresh finished");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
refreshCounter = 2;
|
||||||
|
refreshFailed = false;
|
||||||
|
statusWorking("Refreshing");
|
||||||
|
|
||||||
|
refreshFetch(refreshInfo.sensor, "/Sensors");
|
||||||
|
refreshFetch(refreshInfo.balance, "/Balances");
|
||||||
|
};
|
||||||
|
|
||||||
|
refreshButton.onclick = function (_) {
|
||||||
|
refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch("/public-key").then(function (res) {
|
||||||
|
return res.json();
|
||||||
|
}).then(function (pubKey) {
|
||||||
|
ourPubKey = pubKey;
|
||||||
|
publicKeySpan.innerHTML = pubKey;
|
||||||
|
loaded = true;
|
||||||
|
refresh();
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
//our balace header
|
||||||
|
refreshInfo.balance.onNew.push(function (key, data) {
|
||||||
|
if (key === ourPubKey) {
|
||||||
|
coinCountSpan.innerHTML = data.balance;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
refreshInfo.balance.onChange.push(function (key, data) {
|
||||||
|
if (key === ourPubKey) {
|
||||||
|
coinCountSpan.innerHTML = data.balance;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//pay
|
||||||
|
const payWallets = document.getElementById("payWallets");
|
||||||
|
const payInfo = document.getElementById("payInfo");
|
||||||
|
|
||||||
|
const payAmount = document.getElementById("payAmount");
|
||||||
|
const payTo = document.getElementById("payTo");
|
||||||
|
const payReward = document.getElementById("payReward");
|
||||||
|
const payDo = document.getElementById("payDo");
|
||||||
|
|
||||||
|
payDo.onclick = function (_) {
|
||||||
|
if (payTo.value === "") {
|
||||||
|
statusError("Empty wallet to pay to");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payAmountValue = Number.parseInt(payAmount.value);
|
||||||
|
|
||||||
|
if (Number.isNaN(payAmountValue)) {
|
||||||
|
statusError("Couldn't convert pay amount to a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (payAmountValue <= 0) {
|
||||||
|
statusError("Trying to pay a non-positive amount");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payRewardValue = Number.parseInt(payReward.value);
|
||||||
|
|
||||||
|
if (Number.isNaN(payRewardValue)) {
|
||||||
|
statusError("Couldn't convert pay reward to a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (payReward.value < 0) {
|
||||||
|
statusError("Trying to reward a negative amount");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/Payment', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
rewardAmount: payRewardValue,
|
||||||
|
outputs: [{
|
||||||
|
publicKey: payTo.value,
|
||||||
|
amount: payAmountValue
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
refreshInfo.balance.onNew.push(function (key, data) {
|
||||||
|
const newOption = new Option(key,key);
|
||||||
|
payWallets.appendChild(newOption);
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshInfo.balance.onDel.push(function (key) {
|
||||||
|
const child = payWallets.namedItem(key);
|
||||||
|
if (child !== null) {
|
||||||
|
payWallets.removeChild(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshInfo.balance.onChange.push(function (key, data) {
|
||||||
|
const child = payWallets.namedItem(key);
|
||||||
|
if (child === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (child.selected) {
|
||||||
|
payInfo.innerHTML = data.balance;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
payWallets.oninput = function (_) {
|
||||||
|
if (payWallets.selectedIndex === -1) {
|
||||||
|
payInfo.innerHTML = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedIndex = payWallets.selectedIndex;
|
||||||
|
const selectedOption = payWallets.item(selectedIndex);
|
||||||
|
const selectedBalance = refreshInfo.balance.vals[selectedOption.value];
|
||||||
|
|
||||||
|
payInfo.innerHTML = selectedBalance.balance;
|
||||||
|
payTo.value = selectedOption.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
//query
|
||||||
|
|
||||||
|
const queryInput = document.getElementById("queryInput");
|
||||||
|
const queryGo = document.getElementById("queryGo");
|
||||||
|
const queryHead = document.getElementById("queryHead");
|
||||||
|
const queryBody = document.getElementById("queryBody");
|
||||||
|
|
||||||
|
const queryClearTable = function (obj) {
|
||||||
|
while (obj.rows.length !== 0) {
|
||||||
|
obj.deleteRow(-1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
queryGo.onclick = function (_) {
|
||||||
|
const input = queryInput.value;
|
||||||
|
|
||||||
|
queryGo.disabled = true;
|
||||||
|
|
||||||
|
queryClearTable(queryHead);
|
||||||
|
queryClearTable(queryBody);
|
||||||
|
|
||||||
|
fetch("/sparql", {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
query: input
|
||||||
|
})
|
||||||
|
}).then(function (res) {
|
||||||
|
return res.json();
|
||||||
|
}).then(function (entries) {
|
||||||
|
|
||||||
|
const headers = new Map();
|
||||||
|
|
||||||
|
for (const obj of entries) {
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
if (!headers.has(key)) {
|
||||||
|
headers.set(key, headers.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerRow = queryHead.insertRow(-1);
|
||||||
|
const headerCells = [];
|
||||||
|
for (var i = 0; i < headers.size; ++i) {
|
||||||
|
const created = document.createElement('th');
|
||||||
|
headerRow.appendChild(created);
|
||||||
|
headerCells.push(created);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of headers) {
|
||||||
|
headerCells[value].innerHTML = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const obj of entries) {
|
||||||
|
const dataRow = queryBody.insertRow();
|
||||||
|
|
||||||
|
const cells = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < headers.size; ++i) {
|
||||||
|
cells.push(dataRow.insertCell());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
cells[headers.get(key)].innerHTML = value.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryGo.disabled = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
//sensor
|
||||||
|
const sensorSensors = document.getElementById("sensorSensors");
|
||||||
|
const sensorInfo = document.getElementById("sensorInfo");
|
||||||
|
|
||||||
|
refreshInfo.sensor.onNew.push(function(key, data) {
|
||||||
|
const newOption = new Option(key, key);
|
||||||
|
});
|
||||||
|
}
|
92
ui/wallet-ui.html
Normal file
92
ui/wallet-ui.html
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="/logic.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<span>Senshamart simple ui</span><button id="refresh">Refresh</button>
|
||||||
|
</div>
|
||||||
|
<div id="status">LOADING</div>
|
||||||
|
<div>
|
||||||
|
<span>Wallet public key: </span><span id="publicKey">LOADING</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Current amount: </span><span id="coinCount">LOADING</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="payButton">pay</button>
|
||||||
|
<button id="queryButton">query</button>
|
||||||
|
<button id="sensorInfoButton">sensor info</button>
|
||||||
|
<button id="brokerInfoButton">broker info</button>
|
||||||
|
<button id="registerSensorButton">register sensor</button>
|
||||||
|
<button id="registerBrokerButton">register broker</button>
|
||||||
|
<button id="integrateButton">integrate</button>
|
||||||
|
</div>
|
||||||
|
<div id="payTab">
|
||||||
|
<div>
|
||||||
|
<span>Pay:</span><input id="payAmount" type="number" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>To:</span><input id="payTo" type="text" style="width:50%"/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Reward:</span><input id="payReward" type="number"/>
|
||||||
|
</div>
|
||||||
|
<button input id="payDo">Pay</button>
|
||||||
|
<div>Existing Wallets:</div>
|
||||||
|
<div>
|
||||||
|
<select id="payWallets" style="width:50%" size="10">
|
||||||
|
</select>
|
||||||
|
<label id="payInfo" style="width:50%">TEST</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="queryTab">
|
||||||
|
<div>Query:</div>
|
||||||
|
<div>
|
||||||
|
<textarea id="queryInput" spellcheck="false">
|
||||||
|
Query here!
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="queryGo">Go!</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<thead id="queryHead"></thead>
|
||||||
|
<tbody id="queryBody"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="sensorInfoTab">
|
||||||
|
<div>
|
||||||
|
Sensors:
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
<select id="sensorSensors" style="width:50%"size="10">
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<label id="sensorInfo"style="width:50%">TEST</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="brokerInfoTab">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="registerSensorTab">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="registerBrokerTab">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="integrateTab">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
startSenshamartWalletUI();
|
||||||
|
</script>
|
|
@ -1,7 +1,9 @@
|
||||||
//WALLET
|
//WALLET
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const P2pServer = require('../p2p-server');
|
const BlockchainProp = require('../network/blockchain-prop');
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
const N3 = require('n3');
|
const N3 = require('n3');
|
||||||
|
|
||||||
|
@ -13,6 +15,8 @@ const QueryEngine = require('@comunica/query-sparql-rdfjs').QueryEngine;
|
||||||
const Blockchain = require('../blockchain/blockchain');
|
const Blockchain = require('../blockchain/blockchain');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
DEFAULT_UI_HTML,
|
||||||
|
DEFAULT_UI_JS,
|
||||||
DEFAULT_PORT_WALLET_API,
|
DEFAULT_PORT_WALLET_API,
|
||||||
DEFAULT_PORT_WALLET_CHAIN,
|
DEFAULT_PORT_WALLET_CHAIN,
|
||||||
DEFAULT_PORT_MINER_CHAIN
|
DEFAULT_PORT_MINER_CHAIN
|
||||||
|
@ -41,32 +45,47 @@ const chainServerPort = config.get({
|
||||||
key: "wallet-chain-server-port",
|
key: "wallet-chain-server-port",
|
||||||
default: DEFAULT_PORT_WALLET_CHAIN
|
default: DEFAULT_PORT_WALLET_CHAIN
|
||||||
});
|
});
|
||||||
|
const chainServerPublicAddress = config.get({
|
||||||
|
key: "wallet-chain-server-public-address",
|
||||||
|
default: "-"
|
||||||
|
});
|
||||||
const chainServerPeers = config.get({
|
const chainServerPeers = config.get({
|
||||||
key: "wallet-chain-server-peers",
|
key: "wallet-chain-server-peers",
|
||||||
default: ["ws://127.0.0.1:" + DEFAULT_PORT_MINER_CHAIN]
|
default: ["ws://127.0.0.1:" + DEFAULT_PORT_MINER_CHAIN]
|
||||||
});
|
});
|
||||||
|
const uiHtmlLocation = config.get({
|
||||||
|
key: "wallet-ui-html",
|
||||||
|
default: DEFAULT_UI_HTML
|
||||||
|
});
|
||||||
|
const uiJsLocation = config.get({
|
||||||
|
key: "wallet-ui-js",
|
||||||
|
default: DEFAULT_UI_JS
|
||||||
|
});
|
||||||
|
|
||||||
const blockchain = Blockchain.loadFromDisk(blockchainLocation);
|
const blockchain = Blockchain.loadFromDisk(blockchainLocation);
|
||||||
|
|
||||||
function onChainServerRecv(data) {
|
const chainServer = new BlockchainProp("Wallet-chain-server", false, blockchain);
|
||||||
const replaceResult = blockchain.replaceChain(Blockchain.deserialize(data));
|
|
||||||
if (!replaceResult.result) {
|
|
||||||
console.log(`Failed to replace chain: ${replaceResult.reason}`);
|
|
||||||
//failed to replace
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockchain.saveToDisk(blockchainLocation);
|
chainServer.start(chainServerPort, chainServerPublicAddress, chainServerPeers);
|
||||||
}
|
|
||||||
|
|
||||||
const chainServer = new P2pServer("Chain-server");
|
|
||||||
|
|
||||||
chainServer.start(chainServerPort, chainServerPeers, (_) => { }, onChainServerRecv);
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
app.listen(apiPort, () => console.log(`Listening on port ${apiPort}`));
|
app.listen(apiPort, () => console.log(`Listening on port ${apiPort}`));
|
||||||
|
|
||||||
|
//UI
|
||||||
|
|
||||||
|
app.get('/logic.js', (req, res) => {
|
||||||
|
res.type('.js').sendFile(uiJsLocation, {
|
||||||
|
root:"./"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/ui.html', (req, res) => {
|
||||||
|
res.type('.html').sendFile(uiHtmlLocation, {
|
||||||
|
root:"./"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.get('/ChainServer/sockets', (req, res) => {
|
app.get('/ChainServer/sockets', (req, res) => {
|
||||||
res.json(chainServer.sockets);
|
res.json(chainServer.sockets);
|
||||||
});
|
});
|
||||||
|
@ -91,20 +110,34 @@ app.get('/Balance', (req, res) => {
|
||||||
res.json(balance);
|
res.json(balance);
|
||||||
});
|
});
|
||||||
app.get('/Balances', (req, res) => {
|
app.get('/Balances', (req, res) => {
|
||||||
const balances = blockchain.balances;
|
const balances = blockchain.chain.balances.current;
|
||||||
res.json(balances);
|
res.json(balances);
|
||||||
});
|
});
|
||||||
|
app.get('/Sensors', (req, res) => {
|
||||||
|
res.json(blockchain.chain.sensors.current);
|
||||||
|
});
|
||||||
|
app.get('/Brokers', (req, res) => {
|
||||||
|
res.json(blockchain.chain.sensors.current);
|
||||||
|
});
|
||||||
|
app.get('/Integrations', (req, res) => {
|
||||||
|
res.json(blockchain.chain.integrations.current);
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/Payment', (req, res) => {
|
app.post('/Payment', (req, res) => {
|
||||||
|
console.log(JSON.stringify(req.body));
|
||||||
|
const rewardAmount = req.body.rewardAmount;
|
||||||
|
const outputs = req.body.outputs;
|
||||||
|
|
||||||
res.json(wallet.createPayment(
|
res.json(wallet.createPayment(
|
||||||
req.body.rewardAmount,
|
rewardAmount,
|
||||||
req.body.outputs,
|
outputs,
|
||||||
blockchain));
|
blockchain));
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post('/Integration', (req, res) => {
|
app.post('/Integration', (req, res) => {
|
||||||
res.json(wallet.createIntegration(
|
res.json(wallet.createIntegration(
|
||||||
req.body.rewardAmount,
|
req.body.rewardAmount,
|
||||||
|
req.body.witnessCount,
|
||||||
req.body.outputs,
|
req.body.outputs,
|
||||||
blockchain));
|
blockchain));
|
||||||
});
|
});
|
||||||
|
@ -166,7 +199,7 @@ app.post('/BrokerRegistration', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (quad.predicate.id === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
if (quad.predicate.id === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
|
||||||
&& quad.object.id === "SSM/Broker") {
|
&& quad.object.id === "http://SSM/Broker") {
|
||||||
brokers.push(quad.subject.id);
|
brokers.push(quad.subject.id);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -265,13 +298,13 @@ app.post('/sparql', (req, res) => {
|
||||||
req.body.query,
|
req.body.query,
|
||||||
{
|
{
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
sources: blockchain.stores
|
sources: blockchain.chain.stores
|
||||||
});
|
});
|
||||||
bindingsStream.on('data', (binding) => {
|
bindingsStream.on('data', (binding) => {
|
||||||
result.push(binding);
|
result.push(binding.entries);
|
||||||
});
|
});
|
||||||
bindingsStream.on('end', () => {
|
bindingsStream.on('end', () => {
|
||||||
res.json(JSON.stringify(result));
|
res.json(result);
|
||||||
});
|
});
|
||||||
bindingsStream.on('error', (err) => {
|
bindingsStream.on('error', (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
|
@ -19,6 +19,9 @@ class Wallet {
|
||||||
//TODO: API for multiple outputs
|
//TODO: API for multiple outputs
|
||||||
//returns Transaction
|
//returns Transaction
|
||||||
createPayment(rewardAmount, outputs, blockchain) {
|
createPayment(rewardAmount, outputs, blockchain) {
|
||||||
|
console.log(`${outputs}`);
|
||||||
|
console.log(`${rewardAmount}`);
|
||||||
|
|
||||||
const balance = blockchain.getBalanceCopy(this.publicKey);
|
const balance = blockchain.getBalanceCopy(this.publicKey);
|
||||||
|
|
||||||
if (balance.counter > this.counter) {
|
if (balance.counter > this.counter) {
|
||||||
|
@ -49,7 +52,7 @@ class Wallet {
|
||||||
|
|
||||||
//TODO: API for multiple sensors
|
//TODO: API for multiple sensors
|
||||||
//returns Transaction
|
//returns Transaction
|
||||||
createIntegration(rewardAmount, outputs, blockchain) {
|
createIntegration(rewardAmount, witnessCount, outputs, blockchain) {
|
||||||
const balance = blockchain.getBalanceCopy(this.publicKey);
|
const balance = blockchain.getBalanceCopy(this.publicKey);
|
||||||
|
|
||||||
if (balance.counter > this.counter) {
|
if (balance.counter > this.counter) {
|
||||||
|
@ -69,7 +72,7 @@ class Wallet {
|
||||||
const counterToUse = this.counter + 1;
|
const counterToUse = this.counter + 1;
|
||||||
this.counter++;
|
this.counter++;
|
||||||
|
|
||||||
return new Integration(this.keyPair, counterToUse, outputs, rewardAmount);
|
return new Integration(this.keyPair, counterToUse, outputs, witnessCount, rewardAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
createIntegrationAsTransaction(rewardAmount, outputs, blockchain) {
|
createIntegrationAsTransaction(rewardAmount, outputs, blockchain) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue