Commit some new networking code, adding integration, broker still not 100%, hasn't been committed

This commit is contained in:
Josip Milovac 2023-04-26 13:02:27 +10:00
parent 1af6d56e2d
commit 050e69e23f
18 changed files with 1829 additions and 359 deletions

View file

@ -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

View file

@ -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;
} }
} }

View 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;

View file

@ -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;

View 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));
});
});

View file

@ -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 = {

View file

@ -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;
} }
} }

View file

@ -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,

View file

@ -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();

View file

@ -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
View 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
View 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);

View file

@ -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
View 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
View 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>

View file

@ -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);

View file

@ -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) {