demo version
This commit is contained in:
parent
fbb282a801
commit
672d6daa8e
125 changed files with 17918 additions and 1481 deletions
|
@ -1,9 +1,14 @@
|
|||
const ChainUtil = require('../chain-util');
|
||||
const { DIFFICULTY, MINE_RATE } = require('../constants');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const { DIFFICULTY, MINE_RATE } = require('../util/constants');
|
||||
const BrokerRegistration = require('./broker-registration');
|
||||
const SensorRegistration = require('./sensor-registration');
|
||||
const Integration = require('./integration');
|
||||
const Payment = require('./payment');
|
||||
const Compensation = require('./compensation');
|
||||
|
||||
function concatIfNotUndefined(concatTo, prefix, concatting) {
|
||||
if (typeof concatting !== "undefined" && concatting.length !== 0) {
|
||||
return concatTo + `${prefix}${concatting}`;
|
||||
return concatTo + `${prefix}${concatting.signature}`;
|
||||
} else {
|
||||
return concatTo;
|
||||
}
|
||||
|
@ -20,18 +25,24 @@ 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");
|
||||
const baseValidation = {
|
||||
timestamp: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
lastHash: ChainUtil.validateIsString,
|
||||
hash: ChainUtil.validateIsString,
|
||||
reward: ChainUtil.validateIsPublicKey,
|
||||
nonce: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
difficulty: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
sensorRegistrations: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(SensorRegistration.verify)),
|
||||
brokerRegistrations: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(BrokerRegistration.verify)),
|
||||
integrations: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(Integration.verify)),
|
||||
compensations: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(Compensation.verify)),
|
||||
payments: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(Payment.verify))
|
||||
}
|
||||
|
||||
class Block {
|
||||
constructor(timestamp, lastHash, hash, reward, payments, sensorRegistrations, brokerRegistrations, integrations, compensations, nonce, difficulty) {
|
||||
|
@ -185,104 +196,17 @@ class Block {
|
|||
difficulty);
|
||||
}
|
||||
|
||||
static validateIsBlock(block) {
|
||||
if (!(block instanceof Object)) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Is not an object"
|
||||
};
|
||||
}
|
||||
static verify(block) {
|
||||
const validationRes = ChainUtil.validateObject(block, baseValidation);
|
||||
|
||||
for (const key in block) {
|
||||
if (!acceptableMembers.has(key)) {
|
||||
return {
|
||||
result: false,
|
||||
reason: `Block has key not in acceptable members`
|
||||
};
|
||||
}
|
||||
}
|
||||
if (!validationRes.result) {
|
||||
return validationRes;
|
||||
}
|
||||
|
||||
if (!("timestamp" in block)) {
|
||||
if (!Block.checkHash(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
|
||||
reason: "Couldn't verify hash"
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,13 @@ const BrokerRegistration = require('./broker-registration');
|
|||
const Integration = require('./integration');
|
||||
const Compensation = require('./compensation');
|
||||
const fs = require('fs');
|
||||
const ChainUtil = require('../chain-util');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const RdsStore = require('./rds-store');
|
||||
const {
|
||||
MINING_REWARD} = require('../constants');
|
||||
MINING_REWARD,
|
||||
SENSHAMART_URI_REPLACE } = require('../util/constants');
|
||||
|
||||
const URIS = require('./uris');
|
||||
|
||||
function makeIntegrationKey(publicKey, counter) {
|
||||
return `${publicKey}/${counter}`;
|
||||
|
@ -80,7 +83,9 @@ class PropertyHistory {
|
|||
throw new Error("Finishing Property History with null backing");
|
||||
}
|
||||
|
||||
this.backing.undos.push(...this.undos);
|
||||
for (const undo of this.undos) {
|
||||
this.backing.undos.push(undo);
|
||||
}
|
||||
Object.assign(this.backing.current, this.current);
|
||||
|
||||
this.backing = null;
|
||||
|
@ -113,6 +118,30 @@ function getPropertyClone(propertyHistory, key, fallback) {
|
|||
}
|
||||
}
|
||||
|
||||
function namedNode(x) {
|
||||
return DataFactory.namedNode(x);
|
||||
}
|
||||
|
||||
function literal(x) {
|
||||
return DataFactory.literal(x);
|
||||
}
|
||||
|
||||
function makeBlockName(block) {
|
||||
return URIS.OBJECT.BLOCK + '/' + block.hash;
|
||||
}
|
||||
|
||||
function makeSensorTransactionName(sensorRegistration) {
|
||||
return URIS.OBJECT.SENSOR_REGISTRATION + '/' + SensorRegistration.hashToSign(sensorRegistration);
|
||||
}
|
||||
|
||||
function makeBrokerTransactionName(brokerRegistration) {
|
||||
return URIS.OBJECT.BROKER_REGISTRATION + '/' + BrokerRegistration.hashToSign(brokerRegistration);
|
||||
}
|
||||
|
||||
function makeWalletName(input) {
|
||||
return URIS.OBJECT.WALLET + '/' + input;
|
||||
}
|
||||
|
||||
class Updater {
|
||||
constructor(parent, block) {
|
||||
this.parent = parent;
|
||||
|
@ -126,20 +155,23 @@ class Updater {
|
|||
this.store.startPush();
|
||||
|
||||
if (block !== null) {
|
||||
this.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(this.block.hash),
|
||||
DataFactory.namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
|
||||
DataFactory.namedNode("http://SSM/Block")));
|
||||
this.pushQuad(
|
||||
namedNode(makeBlockName(this.block)),
|
||||
namedNode(URIS.PREDICATE.TYPE),
|
||||
namedNode(URIS.OBJECT.BLOCK));
|
||||
|
||||
this.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(this.block.hash),
|
||||
DataFactory.namedNode("http://SSM/lastBlock"),
|
||||
DataFactory.namedNode(this.parent.getBlockFromTop(0).hash)));
|
||||
this.pushQuad(
|
||||
namedNode(makeBlockName(this.block.hash)),
|
||||
namedNode(URIS.PREDICATE.LAST_BLOCK),
|
||||
namedNode(makeBlockName(this.parent.getBlockFromTop(0))));
|
||||
}
|
||||
}
|
||||
|
||||
pushQuad(subject, predicate, object) {
|
||||
this.store.push(
|
||||
DataFactory.quad(subject, predicate, object));
|
||||
}
|
||||
|
||||
getBalanceCopy(publicKey) {
|
||||
if (publicKey in this.balances) {
|
||||
return Object.assign({}, this.balances[publicKey]);
|
||||
|
@ -177,9 +209,9 @@ class Updater {
|
|||
}
|
||||
|
||||
getBrokerPublicKeys() {
|
||||
const keys = this.parent.getBrokerPublicKeysSet();
|
||||
const keys = this.parent.getBrokerKeysSet();
|
||||
|
||||
for (const [key, value] of this.brokers) {
|
||||
for (const [key, value] of Object.entries(this.brokers)) {
|
||||
keys.add(value.input);
|
||||
}
|
||||
|
||||
|
@ -339,7 +371,9 @@ class Chain {
|
|||
throw new Error("Finishing Blockchain Metadata with null parent");
|
||||
}
|
||||
|
||||
this.parent.blocks.push(...this.blocks);
|
||||
for (const block of this.blocks) {
|
||||
this.parent.blocks.push(block);
|
||||
}
|
||||
this.balances.finish();
|
||||
this.sensors.finish();
|
||||
this.brokers.finish();
|
||||
|
@ -349,13 +383,30 @@ class Chain {
|
|||
}
|
||||
}
|
||||
|
||||
function addRDF(store, metadata) {
|
||||
|
||||
function uriReplacePrefix(testing, sensorName) {
|
||||
if (testing.startsWith(SENSHAMART_URI_REPLACE)) {
|
||||
return sensorName.concat(testing.slice(SENSHAMART_URI_REPLACE.length));
|
||||
} else {
|
||||
return testing;
|
||||
}
|
||||
}
|
||||
|
||||
function addNodeRDF(updater, metadata, sensorName) {
|
||||
for (const triple of metadata) {
|
||||
store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(triple.s),
|
||||
DataFactory.namedNode(triple.p),
|
||||
DataFactory.namedNode(triple.o)));
|
||||
updater.pushQuad(
|
||||
namedNode(uriReplacePrefix(triple.s, sensorName)),
|
||||
namedNode(uriReplacePrefix(triple.p, sensorName)),
|
||||
namedNode(uriReplacePrefix(triple.o, sensorName)));
|
||||
}
|
||||
}
|
||||
|
||||
function addLiteralRDF(updater, metadata, sensorName) {
|
||||
for (const triple of metadata) {
|
||||
updater.pushQuad(
|
||||
namedNode(uriReplacePrefix(triple.s, sensorName)),
|
||||
namedNode(uriReplacePrefix(triple.p, sensorName)),
|
||||
literal(triple.o));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -442,7 +493,7 @@ function stepIntegration(updater, reward, integration) {
|
|||
inputBalance.balance -= integration.rewardAmount;
|
||||
|
||||
for (const output of integration.outputs) {
|
||||
const foundSensor = updater.getSensorCopy(output.sensor);
|
||||
const foundSensor = updater.getSensorCopy(output.sensorName);
|
||||
|
||||
if (foundSensor === null) {
|
||||
return {
|
||||
|
@ -450,12 +501,29 @@ function stepIntegration(updater, reward, integration) {
|
|||
reason: `Integration references non-existant sensor: ${output.sensor}`
|
||||
};
|
||||
}
|
||||
if (foundSensor.counter !== output.counter) {
|
||||
if (SensorRegistration.hashToSign(foundSensor) !== output.sensorHash) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Integration references non-current version of sensor"
|
||||
};
|
||||
}
|
||||
|
||||
const foundBroker = updater.getBrokerCopy(SensorRegistration.getIntegrationBroker(foundSensor));
|
||||
|
||||
if (foundBroker === null) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Internal consitency error, can't find broker referenced by commited sensor registration"
|
||||
};
|
||||
}
|
||||
|
||||
if (BrokerRegistration.hashToSign(foundBroker) !== output.brokerHash) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Integration references non-current version of sensor's broker"
|
||||
};
|
||||
}
|
||||
|
||||
if (inputBalance.balance < output.amount) {
|
||||
return {
|
||||
result: false,
|
||||
|
@ -471,14 +539,21 @@ function stepIntegration(updater, reward, integration) {
|
|||
updater.setBalance(reward, rewardBalance);
|
||||
|
||||
const integrationCopy = Object.assign({}, integration);
|
||||
const brokers = updater.getBrokerKeys();
|
||||
const brokers = updater.getBrokerPublicKeys();
|
||||
|
||||
const witnesses = Integration.chooseWitnesses(integration, brokers);
|
||||
|
||||
if (!witnesses.result) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Couldn't choose witnesses: " + witnesses.reason
|
||||
};
|
||||
}
|
||||
|
||||
integrationCopy.witnesses = {};
|
||||
integrationCopy.compensationCount = 0;
|
||||
|
||||
for (const witness of witnesses) {
|
||||
for (const witness of witnesses.witnesses) {
|
||||
integrationCopy.witnesses[witness] = false;
|
||||
}
|
||||
|
||||
|
@ -567,16 +642,7 @@ function stepSensorRegistration(updater, reward, sensorRegistration) {
|
|||
};
|
||||
}
|
||||
|
||||
const extInfo = SensorRegistration.getExtInformation(sensorRegistration);
|
||||
|
||||
if (!extInfo.result) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Couldn't get sensor registration ext information: " + extInfo.reason
|
||||
};
|
||||
}
|
||||
|
||||
const foundBroker = updater.getBrokerCopy(extInfo.metadata.integrationBroker);
|
||||
const foundBroker = updater.getBrokerCopy(SensorRegistration.getIntegrationBroker(sensorRegistration));
|
||||
|
||||
if (foundBroker === null) {
|
||||
return {
|
||||
|
@ -609,32 +675,65 @@ function stepSensorRegistration(updater, reward, sensorRegistration) {
|
|||
rewardBalance.balance += sensorRegistration.rewardAmount;
|
||||
updater.setBalance(reward, rewardBalance);
|
||||
|
||||
addRDF(updater.store, sensorRegistration.metadata);
|
||||
const sensorName = SensorRegistration.getSensorName(sensorRegistration);
|
||||
|
||||
const newSensor = extInfo.metadata;
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(newSensor.sensorName),
|
||||
DataFactory.namedNode("http://SSM/transactionCounter"),
|
||||
DataFactory.literal(sensorRegistration.counter)));
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(newSensor.sensorName),
|
||||
DataFactory.namedNode("http://SSM/OwnedBy"),
|
||||
DataFactory.namedNode("http://SSM/Wallet/" + sensorRegistration.input)));
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(updater.block.hash),
|
||||
DataFactory.namedNode("http://SSM/Transaction"),
|
||||
DataFactory.namedNode(newSensor.sensorName)));
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(updater.block.hash),
|
||||
DataFactory.namedNode("http://SSM/SensorRegistration"),
|
||||
DataFactory.namedNode(newSensor.sensorName)));
|
||||
const foundExistingSensor = updater.getSensorCopy(sensorName);
|
||||
|
||||
newSensor.counter = sensorRegistration.counter;
|
||||
updater.setSensor(newSensor.sensorName, newSensor);
|
||||
if (foundExistingSensor !== null) {
|
||||
if(foundExistingSensor.input !== sensorRegistration.input) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "A sensor has already been defined with this name"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
addNodeRDF(updater, SensorRegistration.getExtraNodeMetadata(sensorRegistration), sensorName);
|
||||
addLiteralRDF(updater, SensorRegistration.getExtraLiteralMetadata(sensorRegistration), sensorName);
|
||||
|
||||
const transactionName = makeSensorTransactionName(sensorRegistration);
|
||||
|
||||
if (updater.block !== null) {
|
||||
updater.pushQuad(
|
||||
namedNode(makeBlockName(updater.block)),
|
||||
namedNode(URIS.PREDICATE.CONTAINS_TRANSACTION),
|
||||
namedNode(transactionName));
|
||||
updater.pushQuad(
|
||||
namedNode(makeBlockName(updater.block)),
|
||||
namedNode(URIS.PREDICATE.CONTAINS_SENSOR_REGISTRATION),
|
||||
namedNode(transactionName));
|
||||
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.TYPE),
|
||||
namedNode(URIS.OBJECT.SENSOR_REGISTRATION));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.HAS_COUNTER),
|
||||
literal(sensorRegistration.counter));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.IS_OWNED_BY),
|
||||
namedNode(makeWalletName(sensorRegistration.input)));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.DEFINES),
|
||||
namedNode(sensorName));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.COSTS_PER_MINUTE),
|
||||
literal(SensorRegistration.getCostPerMinute(sensorRegistration)));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.COSTS_PER_KB),
|
||||
literal(SensorRegistration.getCostPerKB(sensorRegistration)));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.USES_BROKER),
|
||||
namedNode(makeBrokerTransactionName(foundBroker)));
|
||||
}
|
||||
|
||||
updater.setSensor(sensorName, sensorRegistration);
|
||||
|
||||
return {
|
||||
result: true
|
||||
|
@ -650,15 +749,6 @@ function stepBrokerRegistration(updater, reward, brokerRegistration) {
|
|||
};
|
||||
}
|
||||
|
||||
const extInfo = BrokerRegistration.getExtInformation(brokerRegistration);
|
||||
|
||||
if (!extInfo.result) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Couldn't get broker registration ext information: " + extInfo.reason
|
||||
};
|
||||
}
|
||||
|
||||
const inputBalance = updater.getBalanceCopy(brokerRegistration.input);
|
||||
|
||||
if (brokerRegistration.counter <= inputBalance.counter) {
|
||||
|
@ -683,33 +773,56 @@ function stepBrokerRegistration(updater, reward, brokerRegistration) {
|
|||
rewardBalance.balance += brokerRegistration.rewardAmount;
|
||||
updater.setBalance(reward, rewardBalance);
|
||||
|
||||
addRDF(updater.store, brokerRegistration.metadata);
|
||||
const brokerName = BrokerRegistration.getBrokerName(brokerRegistration);
|
||||
|
||||
const newBroker = extInfo.metadata;
|
||||
newBroker.input = brokerRegistration.input;
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(newBroker.brokerName),
|
||||
DataFactory.namedNode("http://SSM/transactionCounter"),
|
||||
DataFactory.literal(brokerRegistration.counter)));
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(newBroker.brokerName),
|
||||
DataFactory.namedNode("http://SSM/OwnedBy"),
|
||||
DataFactory.namedNode("http://SSM/Wallet/" + brokerRegistration.input)));
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(updater.block.hash),
|
||||
DataFactory.namedNode("http://SSM/Transaction"),
|
||||
DataFactory.namedNode(newBroker.brokerName)));
|
||||
updater.store.push(
|
||||
DataFactory.quad(
|
||||
DataFactory.namedNode(updater.block.hash),
|
||||
DataFactory.namedNode("http://SSM/BrokerRegistration"),
|
||||
DataFactory.namedNode(newBroker.brokerName)));
|
||||
const foundExistingBroker = updater.getBrokerCopy(brokerName);
|
||||
|
||||
newBroker.counter = brokerRegistration.counter;
|
||||
updater.setBroker(newBroker.brokerName, newBroker);
|
||||
if (foundExistingBroker !== null) {
|
||||
if(foundExistingBroker.input !== brokerRegistration.input) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "A broker has already been defined with this name"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
addNodeRDF(updater, BrokerRegistration.getExtraNodeMetadata(brokerRegistration), brokerName);
|
||||
addLiteralRDF(updater, BrokerRegistration.getExtraLiteralMetadata(brokerRegistration), brokerName);
|
||||
|
||||
const transactionName = makeBrokerTransactionName(brokerRegistration);
|
||||
|
||||
if (updater.block !== null) {
|
||||
updater.pushQuad(
|
||||
namedNode(makeBlockName(updater.block)),
|
||||
namedNode(URIS.PREDICATE.CONTAINS_TRANSACTION),
|
||||
namedNode(transactionName));
|
||||
updater.pushQuad(
|
||||
namedNode(makeBlockName(updater.block)),
|
||||
namedNode(URIS.PREDICATE.CONTAINS_BROKER_REGISTRATION),
|
||||
namedNode(transactionName));
|
||||
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.TYPE),
|
||||
namedNode(URIS.OBJECT.BROKER_REGISTRATION));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.HAS_COUNTER),
|
||||
literal(brokerRegistration.counter));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.IS_OWNED_BY),
|
||||
namedNode(makeWalletName(brokerRegistration.input)));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.DEFINES),
|
||||
namedNode(brokerName));
|
||||
updater.pushQuad(
|
||||
namedNode(transactionName),
|
||||
namedNode(URIS.PREDICATE.HAS_ENDPOINT),
|
||||
literal(BrokerRegistration.getEndpoint(brokerRegistration)));
|
||||
}
|
||||
updater.setBroker(BrokerRegistration.getBrokerName(brokerRegistration), brokerRegistration);
|
||||
|
||||
return {
|
||||
result: true
|
||||
|
@ -850,6 +963,8 @@ function findBlocksDifference(oldBlocks, newBlocks) {
|
|||
const verifyRes = verifyBlockHash(newBlocks[i - 1], newBlocks[i]);
|
||||
|
||||
if (!verifyRes.result) {
|
||||
console.log(`${newBlocks[i - 1].hash}`);
|
||||
console.log(`${newBlocks[i].lastHash}`);
|
||||
return {
|
||||
result: false,
|
||||
reason: `Couldn't verify hashes for block ${i}: ${verifyRes.reason}`
|
||||
|
@ -976,9 +1091,9 @@ class Blockchain {
|
|||
return true;
|
||||
}
|
||||
|
||||
wouldBeValidBlock(rewardee, payments, sensorRegistrations, brokerRegistrations, integrations) {
|
||||
wouldBeValidBlock(rewardee, payments, sensorRegistrations, brokerRegistrations, integrations, compensations) {
|
||||
const updater = this.chain.createUpdater(null);
|
||||
return verifyTxs(updater, rewardee, payments, sensorRegistrations, brokerRegistrations, integrations).result;
|
||||
return verifyTxs(updater, rewardee, payments, sensorRegistrations, brokerRegistrations, integrations, compensations).result;
|
||||
}
|
||||
|
||||
static isValidChain(blocks) {
|
||||
|
@ -1025,8 +1140,11 @@ class Blockchain {
|
|||
this.chain = baseChain;
|
||||
verifyResult.newChain.finish();
|
||||
|
||||
onChange(this, this.blocks(), oldChain, chainDifferenceRes.difference);
|
||||
console.log(`new chain of length: ${this.blocks().length}`);
|
||||
|
||||
onChange(this, this.blocks(), oldChain.blocks, chainDifferenceRes.difference);
|
||||
|
||||
|
||||
return {
|
||||
result: true,
|
||||
chainDifference: chainDifferenceRes.difference,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const Blockchain = require('./index');
|
||||
const Blockchain = require('./blockchain');
|
||||
const Block = require('./block');
|
||||
|
||||
describe('Blockchain', () => {
|
||||
|
@ -15,13 +15,13 @@ describe('Blockchain', () => {
|
|||
|
||||
it('adds a new block', () => {
|
||||
const reward = 'test-reward-key';
|
||||
expect(bc.addBlock(Block.debugMine(bc.lastBlock(),reward,[],[]))).toBe(true);
|
||||
expect(bc.addBlock(Block.debugMine(bc.lastBlock(),reward))).toBe(true);
|
||||
|
||||
expect(bc.lastBlock().reward).toEqual(reward);
|
||||
});
|
||||
|
||||
it('validates a valid chain', () => {
|
||||
expect(bc2.addBlock(Block.debugMine(bc2.lastBlock(), 'test-reward-key', [], []))).toBe(true);
|
||||
expect(bc2.addBlock(Block.debugMine(bc2.lastBlock(), 'test-reward-key'))).toBe(true);
|
||||
|
||||
expect(Blockchain.isValidChain(bc2.chain)).toBe(true);
|
||||
});
|
||||
|
|
|
@ -1,151 +1,91 @@
|
|||
const ChainUtil = require('../chain-util');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const SENSHAMART_URI_PREFIX = require('../util/constants').SENSHAMART_URI_PREFIX;
|
||||
|
||||
const tripleValidator = {
|
||||
s: ChainUtil.validateIsString,
|
||||
p: ChainUtil.validateIsString,
|
||||
o: ChainUtil.validateIsString
|
||||
};
|
||||
function validateTerm(t) {
|
||||
const stringRes = ChainUtil.validateIsString(t);
|
||||
|
||||
function validateMetadata(t) {
|
||||
|
||||
let isBroker = [];
|
||||
let costPerMinute = [];
|
||||
let costPerKB = [];
|
||||
let integrationEndpoint = [];
|
||||
|
||||
const validationRes = ChainUtil.validateArray(t, ChainUtil.createValidateObject(tripleValidator));
|
||||
|
||||
if (!validationRes.result) {
|
||||
return validationRes;
|
||||
if (!stringRes.result) {
|
||||
return stringRes;
|
||||
}
|
||||
|
||||
for (const triple of t) {
|
||||
switch (triple.p) {
|
||||
case "http://SSM/Cost_of_Using_IoT_Devices/Cost_Per_Minute": costPerMinute.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":
|
||||
if (triple.o === "http://SSM/Broker") {
|
||||
isBroker.push(triple.s);
|
||||
}
|
||||
break;
|
||||
case "http://SSM/Integration/Endpoint": integrationEndpoint.push(triple); break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isBroker.length === 0) {
|
||||
if (t.startsWith(SENSHAMART_URI_PREFIX)) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No broker is defined"
|
||||
};
|
||||
} else if (isBroker.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple brokers are defined"
|
||||
};
|
||||
}
|
||||
|
||||
const brokerName = isBroker[0];
|
||||
|
||||
if (costPerMinute.length === 0) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No cost per minute was defined"
|
||||
};
|
||||
} else if (costPerMinute.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple cost per minutes were defined"
|
||||
}
|
||||
}
|
||||
const CostPerMinuteValue = Number.parseInt(costPerMinute[0].o);
|
||||
if (CostPerMinuteValue === NaN) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Couldn't parse cost per minute as an integer"
|
||||
};
|
||||
} else if (CostPerMinuteValue < 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per minute was negative"
|
||||
}
|
||||
} else if (costPerMinute[0].s != brokerName) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per minute object isn't the broker"
|
||||
};
|
||||
}
|
||||
|
||||
if (costPerKB.length === 0) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No cost per KB was defined"
|
||||
};
|
||||
} else if (costPerKB.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple cost per KB were defined"
|
||||
}
|
||||
}
|
||||
const CostPerKBValue = Number.parseInt(costPerKB[0].o);
|
||||
if (CostPerKBValue === NaN) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Couldn't parse cost per KB as an integer"
|
||||
};
|
||||
} else if (CostPerKBValue < 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per KB was negative"
|
||||
}
|
||||
} else if (costPerKB[0].s != brokerName) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per KB object isn't the broker"
|
||||
};
|
||||
}
|
||||
|
||||
if (integrationEndpoint.length === 0) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No integration endpoint was defined"
|
||||
};
|
||||
} else if (integrationEndpoint.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple integration endpoints were defined"
|
||||
};
|
||||
} else if (integrationEndpoint[0].s != brokerName) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Integration endpoint object isn't the broker"
|
||||
reason: "Starts with reserved prefix"
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
result: true,
|
||||
metadata: {
|
||||
brokerName: brokerName,
|
||||
costPerMinute: CostPerMinuteValue,
|
||||
costPerKB: CostPerKBValue,
|
||||
integrationEndpoint: integrationEndpoint[0].o
|
||||
}
|
||||
result: true
|
||||
};
|
||||
}
|
||||
|
||||
function validateLiteral(t) {
|
||||
const termRes = validateTerm(t);
|
||||
if (termRes.result) {
|
||||
return termRes;
|
||||
}
|
||||
|
||||
const numberRes = ChainUtil.validateIsNumber(t);
|
||||
|
||||
if (numberRes.result) {
|
||||
return numberRes;
|
||||
}
|
||||
|
||||
return {
|
||||
result: false,
|
||||
reason: "Wasn't a string or a number"
|
||||
};
|
||||
}
|
||||
|
||||
const nodeValidator = {
|
||||
s: validateTerm,
|
||||
p: validateTerm,
|
||||
o: validateTerm
|
||||
};
|
||||
|
||||
const literalValidator = {
|
||||
s: validateTerm,
|
||||
p: validateTerm,
|
||||
o: validateLiteral
|
||||
};
|
||||
|
||||
const metadataValidation = {
|
||||
name: ChainUtil.validateIsString,
|
||||
endpoint: ChainUtil.validateIsString,
|
||||
extraNodes: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(
|
||||
ChainUtil.createValidateObject(
|
||||
nodeValidator))),
|
||||
extraLiterals: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(
|
||||
ChainUtil.createValidateObject(
|
||||
literalValidator)))
|
||||
}
|
||||
|
||||
const baseValidation = {
|
||||
input: ChainUtil.validateIsPublicKey,
|
||||
counter: ChainUtil.validateIsInteger,
|
||||
counter: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
rewardAmount: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
metadata: validateMetadata,
|
||||
metadata: ChainUtil.createValidateObject(metadataValidation),
|
||||
signature: ChainUtil.validateIsSignature
|
||||
};
|
||||
|
||||
class BrokerRegistration {
|
||||
constructor(senderKeyPair, counter, metadata, rewardAmount) {
|
||||
constructor(senderKeyPair, counter, brokerName, endpoint, nodeMetadata, literalMetadata, rewardAmount) {
|
||||
this.input = senderKeyPair.getPublic().encode('hex');
|
||||
this.counter = counter;
|
||||
this.rewardAmount = rewardAmount;
|
||||
this.metadata = metadata;
|
||||
this.metadata = {
|
||||
name: brokerName,
|
||||
endpoint: endpoint
|
||||
};
|
||||
if (typeof nodeMetadata !== undefined && nodeMetadata !== null) {
|
||||
this.metadata.extraNodes = nodeMetadata;
|
||||
};
|
||||
if (typeof literalMetadata !== undefined && literalMetadata !== null) {
|
||||
this.metadata.extraLiterals = literalMetadata;
|
||||
};
|
||||
this.signature = senderKeyPair.sign(BrokerRegistration.hashToSign(this));
|
||||
|
||||
const verification = BrokerRegistration.verify(this);
|
||||
|
@ -154,6 +94,30 @@ class BrokerRegistration {
|
|||
}
|
||||
}
|
||||
|
||||
static getBrokerName(registration) {
|
||||
return registration.metadata.name;
|
||||
}
|
||||
|
||||
static getEndpoint(registration) {
|
||||
return registration.metadata.endpoint;
|
||||
}
|
||||
|
||||
static getExtraNodeMetadata(registration) {
|
||||
if ("extraNodes" in registration.metadata) {
|
||||
return registration.metadata.extraNodes;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static getExtraLiteralMetadata(registration) {
|
||||
if ("extraLiterals" in registration.metadata) {
|
||||
return registration.metadata.extraLiterals;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static hashToSign(registration) {
|
||||
return ChainUtil.hash([
|
||||
registration.counter,
|
||||
|
@ -173,7 +137,7 @@ class BrokerRegistration {
|
|||
BrokerRegistration.hashToSign(registration));
|
||||
|
||||
if (!signatureRes.result) {
|
||||
return signatureRes.reason;
|
||||
return signatureRes;
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -181,8 +145,8 @@ class BrokerRegistration {
|
|||
};
|
||||
}
|
||||
|
||||
static getExtInformation(registration) {
|
||||
return validateMetadata(registration.metadata);
|
||||
static name() {
|
||||
return "BrokerRegistration";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
205
blockchain/broker-registration.test.js
Normal file
205
blockchain/broker-registration.test.js
Normal file
|
@ -0,0 +1,205 @@
|
|||
const BrokerRegistration = require('./broker-registration');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const SENSHAMART_URI_PREFIX = require('../util/constants').SENSHAMART_URI_PREFIX;
|
||||
|
||||
describe('Broker Registration', () => {
|
||||
let keyPair;
|
||||
|
||||
beforeEach(() => {
|
||||
keyPair = ChainUtil.genKeyPair();
|
||||
});
|
||||
|
||||
it("Construct a broker", () => {
|
||||
new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [], 0);
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid counter", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, "hello", "test", 0, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid name", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, 5, 0, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with negative costPerMinute", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", -1, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid costPerMinute", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 1.5, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with negative costPerKB", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, -1, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid costPerKB", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, "hello", "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid broker", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, 5, null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with negative rewardAmount", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", null, -1)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid rewardAmount", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", null, "0")).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with extra metadata", () => {
|
||||
new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
});
|
||||
|
||||
it("Construct a broker invalid subject in extra metadata", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: 0,
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker reserved subject in extra metadata", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: SENSHAMART_URI_PREFIX + "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid predicate in extra metadata", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: {},
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with reserved predicate in extra metadata", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: SENSHAMART_URI_PREFIX + "and",
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with invalid object in extra metadata", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: []
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a broker with reserved object in extra metadata", () => {
|
||||
expect(() => new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: SENSHAMART_URI_PREFIX + "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Changing input fails verify", () => {
|
||||
const changing = new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.input = ChainUtil.genKeyPair();
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing counter fails verify", () => {
|
||||
const changing = new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.counter++;
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing rewardAmount fails verify", () => {
|
||||
const changing = new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.rewardAmount++;
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata name fails verify", () => {
|
||||
const changing = new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.name = "else";
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata costPerMinute fails verify", () => {
|
||||
const changing = new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.costPerMinute++;
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata costPerKB fails verify", () => {
|
||||
const changing = new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.costPerKB++;
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata endpoint fails verify", () => {
|
||||
const changing = new BrokerRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.endpoint += "a";
|
||||
|
||||
expect(BrokerRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
const ChainUtil = require('../chain-util');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const Integration = require('./integration');
|
||||
|
||||
const integrationValidation = {
|
||||
|
@ -60,6 +60,10 @@ class Compensation {
|
|||
result: true,
|
||||
};
|
||||
}
|
||||
|
||||
static name() {
|
||||
return "Compensation";
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Compensation;
|
|
@ -1,11 +1,11 @@
|
|||
const ChainUtil = require('../chain-util');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const SeedRandom = require('seedrandom');
|
||||
|
||||
const outputValidation = {
|
||||
publicKey: ChainUtil.validateIsPublicKey,
|
||||
sensor: ChainUtil.validateIsString,
|
||||
sensorName: ChainUtil.validateIsString,
|
||||
amount: ChainUtil.createValidateIsIntegerWithMin(1),
|
||||
counter: ChainUtil.createValidateIsIntegerWithMin(1)
|
||||
sensorHash: ChainUtil.validateIsString,
|
||||
brokerHash: ChainUtil.validateIsString
|
||||
};
|
||||
|
||||
function validateOutputs(t) {
|
||||
|
@ -54,12 +54,12 @@ class Integration {
|
|||
}
|
||||
}
|
||||
|
||||
static createOutput(recipientPublicKey, sensorId, amount, counter) {
|
||||
static createOutput(amount, sensorName, sensorRegistrationHash, brokerRegistrationHash) {
|
||||
return {
|
||||
publicKey: recipientPublicKey,
|
||||
sensor: sensorId,
|
||||
amount: amount,
|
||||
counter: counter
|
||||
sensorName: sensorName,
|
||||
sensorHash: sensorRegistrationHash,
|
||||
brokerHash: brokerRegistrationHash
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,10 @@ class Integration {
|
|||
witnesses: witnesses
|
||||
};
|
||||
}
|
||||
|
||||
static name() {
|
||||
return "Integration";
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Integration;
|
|
@ -1,5 +1,5 @@
|
|||
const Integration = require('./integration');
|
||||
const ChainUtil = require('../chain-util');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
|
||||
function createDummyIntegration(keyPair, witnesses) {
|
||||
return new Integration(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const ChainUtil = require('../chain-util');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
|
||||
const outputValidation = {
|
||||
publicKey: ChainUtil.validateIsPublicKey,
|
||||
|
@ -79,6 +79,10 @@ class Payment {
|
|||
result: true,
|
||||
};
|
||||
}
|
||||
|
||||
static name() {
|
||||
return "Payment";
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Payment;
|
|
@ -1,79 +0,0 @@
|
|||
const Transaction = require('./transaction');
|
||||
const Wallet = require('./index');
|
||||
const { MINING_REWARD } = require('../constants');
|
||||
|
||||
describe('Transaction', () => {
|
||||
let transaction, wallet, recipient, amount;
|
||||
|
||||
beforeEach(() => {
|
||||
wallet = new Wallet();
|
||||
amount = 50;
|
||||
recipient = 'r3c1p13nt';
|
||||
transaction = Transaction.newTransaction(wallet, recipient, amount);
|
||||
});
|
||||
|
||||
it('outputs the `amount` subtracted from the wallet balance', () => {
|
||||
expect(transaction.outputs.find(output => output.address === wallet.publicKey).amount)
|
||||
.toEqual(wallet.balance - amount);
|
||||
});
|
||||
|
||||
it('outputs the `amount` added to the recipient', () => {
|
||||
expect(transaction.outputs.find(output => output.address === recipient).amount)
|
||||
.toEqual(amount);
|
||||
});
|
||||
|
||||
it('inputs the balance of the wallet', () => {
|
||||
expect(transaction.input.amount).toEqual(wallet.balance);
|
||||
});
|
||||
|
||||
it('validates a valid transaction', () => {
|
||||
expect(Transaction.verifyTransaction(transaction)).toBe(true);
|
||||
});
|
||||
|
||||
it('invalidates a corrupt transaction', () => {
|
||||
transaction.outputs[0].amount = 50000;
|
||||
expect(Transaction.verifyTransaction(transaction)).toBe(false);
|
||||
});
|
||||
|
||||
describe('transacting with an amount that exceeds the balance', () => {
|
||||
beforeEach(() => {
|
||||
amount = 50000;
|
||||
transaction = Transaction.newTransaction(wallet, recipient, amount);
|
||||
});
|
||||
|
||||
it('does not create the transaction', () => {
|
||||
expect(transaction).toEqual(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and updating a transaction', () => {
|
||||
let nextAmount, nextRecipient;
|
||||
|
||||
beforeEach(() => {
|
||||
nextAmount = 20;
|
||||
nextRecipient = 'n3xt-4ddr355';
|
||||
transaction = transaction.update(wallet, nextRecipient, nextAmount);
|
||||
});
|
||||
|
||||
it(`subtracts the next amount from the sender's output`, () => {
|
||||
expect(transaction.outputs.find(output => output.address === wallet.publicKey).amount)
|
||||
.toEqual(wallet.balance - amount - nextAmount);
|
||||
});
|
||||
|
||||
it('outputs an amount for the next recipient', () => {
|
||||
expect(transaction.outputs.find(output => output.address === nextRecipient).amount)
|
||||
.toEqual(nextAmount);
|
||||
});
|
||||
});
|
||||
|
||||
describe('creating a reward transaction', () => {
|
||||
beforeEach(() => {
|
||||
transaction = Transaction.rewardTransaction(wallet, Wallet.blockchainWallet());
|
||||
});
|
||||
|
||||
it(`reward the miner's wallet`, () => {
|
||||
expect(transaction.outputs.find(output => output.address === wallet.publicKey).amount)
|
||||
.toEqual(MINING_REWARD);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,4 @@
|
|||
const Stream = require("stream");
|
||||
const DataFactory = require('n3').DataFactory;
|
||||
|
||||
//class NamedNode {
|
||||
// constructor(value) {
|
||||
|
@ -294,32 +293,29 @@ function addQuadToMap(counter, map, key, quad, toPop) {
|
|||
if (toPop.has(key)) {
|
||||
popper = toPop.get(key);
|
||||
} else {
|
||||
popper = {
|
||||
delete: false,
|
||||
removing: []
|
||||
};
|
||||
popper = [];
|
||||
toPop.set(key, popper);
|
||||
}
|
||||
|
||||
if (map.has(key)) {
|
||||
quadMap = map.get(key);
|
||||
popper.removing.push(counter);
|
||||
} else {
|
||||
quadMap = new Map();
|
||||
map.set(key, quadMap);
|
||||
popper.delete = true;
|
||||
}
|
||||
popper.push(counter);
|
||||
quadMap.set(counter, quad);
|
||||
}
|
||||
|
||||
function popFromSource(list, map) {
|
||||
for (const [key, popper] of list) {
|
||||
if (popper.delete) {
|
||||
const innerMap = map.get(key);
|
||||
|
||||
if (popper.length === innerMap.size) {
|
||||
map.delete(key)
|
||||
} else {
|
||||
const keyMap = map.get(key);
|
||||
for (const counter of popper.removing) {
|
||||
keyMap.delete(counter);
|
||||
for (const counter of popper) {
|
||||
innerMap.delete(counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -342,12 +338,12 @@ class Source {
|
|||
this.objects = new Map();
|
||||
this.graphs = new Map();
|
||||
this.all = [];
|
||||
this.pop = [];
|
||||
this.popping = [];
|
||||
this.counter = 0;
|
||||
}
|
||||
|
||||
startPush() {
|
||||
this.pop.push({
|
||||
this.popping.push({
|
||||
subjects: new Map(),
|
||||
predicates: new Map(),
|
||||
objects: new Map(),
|
||||
|
@ -357,7 +353,7 @@ class Source {
|
|||
}
|
||||
|
||||
push(quad) {
|
||||
const toPop = this.pop[this.pop.length - 1];
|
||||
const toPop = this.popping[this.popping.length - 1];
|
||||
|
||||
addQuadToMap(this.counter, this.subjects, quad.subject.value, quad, toPop.subjects);
|
||||
addQuadToMap(this.counter, this.predicates, quad.predicate.value, quad, toPop.predicates);
|
||||
|
@ -369,11 +365,11 @@ class Source {
|
|||
}
|
||||
|
||||
pop() {
|
||||
if (this.pop.length === 0) {
|
||||
if (this.popping.length === 0) {
|
||||
throw new Error("Nothing to pop");
|
||||
}
|
||||
|
||||
const toPop = this.pop.pop();
|
||||
const toPop = this.popping.pop();
|
||||
|
||||
this.all.slice(0, -toPop.count);
|
||||
|
||||
|
@ -453,7 +449,7 @@ class Source {
|
|||
cloneTermMap(this.graphs, returning.graphs);
|
||||
|
||||
this.all.forEach(item => returning.all.push(item));
|
||||
this.pop.forEach(item => returning.pop.push(item));
|
||||
this.popping.forEach(item => returning.popping.push(item));
|
||||
returning.counter = this.counter;
|
||||
|
||||
return returning;
|
||||
|
@ -461,7 +457,7 @@ class Source {
|
|||
|
||||
pushInto(parent) {
|
||||
let on = 0;
|
||||
for (const toPop of this.pop) {
|
||||
for (const toPop of this.popping) {
|
||||
parent.startPush();
|
||||
for (const quad of this.all.slice(on, on + toPop.count)) {
|
||||
parent.push(quad);
|
||||
|
|
|
@ -1,150 +1,95 @@
|
|||
const ChainUtil = require('../chain-util');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const SENSHAMART_URI_PREFIX = require('../util/constants').SENSHAMART_URI_PREFIX;
|
||||
|
||||
const tripleValidator = {
|
||||
s: ChainUtil.validateIsString,
|
||||
p: ChainUtil.validateIsString,
|
||||
o: ChainUtil.validateIsString
|
||||
};
|
||||
function validateTerm(t) {
|
||||
const stringRes = ChainUtil.validateIsString(t);
|
||||
|
||||
function validateMetadata(t) {
|
||||
let isSensor = [];
|
||||
let costPerMinute = [];
|
||||
let costPerKB = [];
|
||||
let integrationBroker = [];
|
||||
|
||||
const validationRes = ChainUtil.validateArray(t, ChainUtil.createValidateObject(tripleValidator));
|
||||
|
||||
if (!validationRes.result) {
|
||||
return validationRes;
|
||||
if (!stringRes.result) {
|
||||
return stringRes;
|
||||
}
|
||||
|
||||
for (const triple of t) {
|
||||
switch (triple.p) {
|
||||
case "http://SSM/Cost_of_Using_IoT_Devices/Cost_Per_Minute": costPerMinute.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":
|
||||
if (triple.o === "http://www.w3.org/ns/sosa/Sensor") {
|
||||
isSensor.push(triple.s);
|
||||
}
|
||||
break;
|
||||
case "http://SSM/Integration/Broker": integrationBroker.push(triple); break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSensor.length === 0) {
|
||||
if (t.startsWith(SENSHAMART_URI_PREFIX)) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No sensor is defined"
|
||||
};
|
||||
} else if (isSensor.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple sensors are defined"
|
||||
};
|
||||
}
|
||||
|
||||
const sensorName = isSensor[0];
|
||||
|
||||
if (costPerMinute.length === 0) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No cost per minute was defined"
|
||||
};
|
||||
} else if (costPerMinute.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple cost per minutes were defined"
|
||||
}
|
||||
}
|
||||
const CostPerMinuteValue = Number.parseInt(costPerMinute[0].o);
|
||||
if (CostPerMinuteValue === NaN) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Couldn't parse cost per minute as an integer"
|
||||
};
|
||||
} else if (CostPerMinuteValue < 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per minute was negative"
|
||||
}
|
||||
} else if (costPerMinute[0].s != sensorName) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per minute object isn't the broker"
|
||||
};
|
||||
}
|
||||
|
||||
if (costPerKB.length === 0) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No cost per KB was defined"
|
||||
};
|
||||
} else if (costPerKB.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple cost per KB were defined"
|
||||
}
|
||||
}
|
||||
const CostPerKBValue = Number.parseInt(costPerKB[0].o);
|
||||
if (CostPerKBValue === NaN) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Couldn't parse cost per KB as an integer"
|
||||
};
|
||||
} else if (CostPerKBValue < 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per KB was negative"
|
||||
}
|
||||
} else if (costPerKB[0].s != sensorName) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Cost per KB object isn't the broker"
|
||||
};
|
||||
}
|
||||
|
||||
if (integrationBroker.length === 0) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "No integration broker was defined"
|
||||
};
|
||||
} else if (integrationBroker.length > 1) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Multiple integration brokers were defined"
|
||||
};
|
||||
} else if (integrationBroker[0].s != sensorName) {
|
||||
return {
|
||||
result: false,
|
||||
reason: "Integration broker subjsect isn't the sensor"
|
||||
reason: "Starts with reserved prefix"
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
result: true,
|
||||
metadata: {
|
||||
sensorName: sensorName,
|
||||
costPerMinute: CostPerMinuteValue,
|
||||
costPerKB: CostPerKBValue,
|
||||
integrationBroker: integrationBroker[0].o
|
||||
}
|
||||
result: true
|
||||
};
|
||||
}
|
||||
|
||||
function validateLiteral(t) {
|
||||
const termRes = validateTerm(t);
|
||||
if (termRes.result) {
|
||||
return termRes;
|
||||
}
|
||||
|
||||
const numberRes = ChainUtil.validateIsNumber(t);
|
||||
|
||||
if (numberRes.result) {
|
||||
return numberRes;
|
||||
}
|
||||
|
||||
return {
|
||||
result: false,
|
||||
reason: "Wasn't a string or a number"
|
||||
};
|
||||
}
|
||||
|
||||
const nodeValidator = {
|
||||
s: validateTerm,
|
||||
p: validateTerm,
|
||||
o: validateTerm
|
||||
};
|
||||
|
||||
const literalValidator = {
|
||||
s: validateTerm,
|
||||
p: validateTerm,
|
||||
o: validateLiteral
|
||||
};
|
||||
|
||||
const metadataValidation = {
|
||||
name: ChainUtil.validateIsString,
|
||||
costPerMinute: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
costPerKB: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
integrationBroker: ChainUtil.validateIsString,
|
||||
extraNodes: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(
|
||||
ChainUtil.createValidateObject(
|
||||
nodeValidator))),
|
||||
extraLiterals: ChainUtil.createValidateOptional(
|
||||
ChainUtil.createValidateArray(
|
||||
ChainUtil.createValidateObject(
|
||||
literalValidator)))
|
||||
};
|
||||
|
||||
const baseValidation = {
|
||||
input: ChainUtil.validateIsPublicKey,
|
||||
counter: ChainUtil.createValidateIsIntegerWithMin(1),
|
||||
rewardAmount: ChainUtil.createValidateIsIntegerWithMin(0),
|
||||
metadata: validateMetadata,
|
||||
metadata: ChainUtil.createValidateObject(metadataValidation),
|
||||
signature: ChainUtil.validateIsSignature
|
||||
};
|
||||
|
||||
class SensorRegistration {
|
||||
constructor(senderKeyPair, counter, metadata, rewardAmount) {
|
||||
constructor(senderKeyPair, counter, sensorName, costPerMinute, costPerKB, integrationBroker, nodeMetadata, literalMetadata, rewardAmount) {
|
||||
this.input = senderKeyPair.getPublic().encode('hex');
|
||||
this.counter = counter;
|
||||
this.rewardAmount = rewardAmount;
|
||||
this.metadata = metadata;
|
||||
this.metadata = {
|
||||
name: sensorName,
|
||||
costPerMinute: costPerMinute,
|
||||
costPerKB: costPerKB,
|
||||
integrationBroker: integrationBroker,
|
||||
};
|
||||
if (typeof nodeMetadata !== undefined && nodeMetadata !== null) {
|
||||
this.metadata.extraNodes = nodeMetadata;
|
||||
}
|
||||
if (typeof literalMetadata !== undefined && literalMetadata !== null) {
|
||||
this.metadata.extraLiterals = literalMetadata;
|
||||
}
|
||||
this.signature = senderKeyPair.sign(SensorRegistration.hashToSign(this));
|
||||
|
||||
const verification = SensorRegistration.verify(this);
|
||||
|
@ -153,6 +98,38 @@ class SensorRegistration {
|
|||
}
|
||||
}
|
||||
|
||||
static getSensorName(registration) {
|
||||
return registration.metadata.name;
|
||||
}
|
||||
|
||||
static getCostPerMinute(registration) {
|
||||
return registration.metadata.costPerMinute;
|
||||
}
|
||||
|
||||
static getCostPerKB(registration) {
|
||||
return registration.metadata.costPerKB;
|
||||
}
|
||||
|
||||
static getIntegrationBroker(registration) {
|
||||
return registration.metadata.integrationBroker;
|
||||
}
|
||||
|
||||
static getExtraNodeMetadata(registration) {
|
||||
if ("extraNodes" in registration.metadata) {
|
||||
return registration.metadata.extraNodes;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static getExtraLiteralMetadata(registration) {
|
||||
if ("extraLiterals" in registration.metadata) {
|
||||
return registration.metadata.extraLiterals;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
static hashToSign(registration) {
|
||||
return ChainUtil.hash([
|
||||
registration.counter,
|
||||
|
@ -163,8 +140,7 @@ class SensorRegistration {
|
|||
static verify(registration) {
|
||||
const validationResult = ChainUtil.validateObject(registration, baseValidation);
|
||||
if (!validationResult.result) {
|
||||
console.log(`Failed validation: ${validationResult.reason}`);
|
||||
return false;
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const verifyRes = ChainUtil.verifySignature(
|
||||
|
@ -180,8 +156,8 @@ class SensorRegistration {
|
|||
};
|
||||
}
|
||||
|
||||
static getExtInformation(registration) {
|
||||
return validateMetadata(registration.metadata);
|
||||
static name() {
|
||||
return "SensorRegistration";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,109 +1,205 @@
|
|||
const Transaction = require('./transaction');
|
||||
const Metadata = require('./metadata');
|
||||
const Wallet = require('./index');
|
||||
const { MINING_REWARD } = require('../constants');
|
||||
const SensorRegistration = require('./sensor-registration');
|
||||
const ChainUtil = require('../util/chain-util');
|
||||
const SENSHAMART_URI_PREFIX = require('../util/constants').SENSHAMART_URI_PREFIX;
|
||||
|
||||
describe('Transaction & Metadata', () => {
|
||||
let transaction, metadata, wallet, recipient, amount,
|
||||
senderWallet,Name,Geo ,IP_URL , Topic_Token, Permission,
|
||||
RequestDetail, OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte,
|
||||
PaymentPerMinute, Protocol, MessageAttributes, Interval,
|
||||
FurtherDetails, SSNmetadata;
|
||||
describe('Sensor Registration', () => {
|
||||
let keyPair;
|
||||
|
||||
beforeEach(() => {
|
||||
wallet = new Wallet();
|
||||
amount = 50;
|
||||
recipient = 'r3c1p13nt';
|
||||
senderWallet = new Wallet();
|
||||
Name = 'IoT_Lab_Temp_Sensor'
|
||||
Geo = [1.045,0.0135]
|
||||
IP_URL = 'www.IoT-locationbar.com/sensors/temp'
|
||||
Topic_Token = 'ACCESS_TOKEN'
|
||||
Permission = 'Public'
|
||||
RequestDetail = 'Null'
|
||||
OrgOwner = 'Swinburne_University'
|
||||
DepOwner = 'Computer_Science'
|
||||
PrsnOwner = 'Anas_Dawod'
|
||||
PaymentPerKbyte = 10
|
||||
PaymentPerMinute = 5
|
||||
Protocol = 'MQTT'
|
||||
MessageAttributes = 'null'
|
||||
Interval = 10
|
||||
FurtherDetails = 'null'
|
||||
SSNmetadata = 'null'
|
||||
transaction = Transaction.newTransaction(wallet, recipient, amount);
|
||||
metadata = Metadata.newMetadata(senderWallet,Name,Geo ,IP_URL , Topic_Token, Permission,
|
||||
RequestDetail, OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte,
|
||||
PaymentPerMinute, Protocol, MessageAttributes, Interval,
|
||||
FurtherDetails, SSNmetadata)
|
||||
keyPair = ChainUtil.genKeyPair();
|
||||
});
|
||||
|
||||
it('outputs the `amount` subtracted from the wallet balance', () => {
|
||||
expect(transaction.outputs.find(output => output.address === wallet.publicKey).amount)
|
||||
.toEqual(wallet.balance - amount);
|
||||
it("Construct a sensor", () => {
|
||||
new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [], 0);
|
||||
});
|
||||
|
||||
it('outputs the `amount` added to the recipient', () => {
|
||||
expect(transaction.outputs.find(output => output.address === recipient).amount)
|
||||
.toEqual(amount);
|
||||
it("Construct a sensor with invalid counter", () => {
|
||||
expect(() => new SensorRegistration(keyPair, "hello", "test", 0, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it('inputs the balance of the wallet', () => {
|
||||
expect(transaction.input.amount).toEqual(wallet.balance);
|
||||
it("Construct a sensor with invalid name", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, 5, 0, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it('validates a valid transaction', () => {
|
||||
expect(Transaction.verifyTransaction(transaction)).toBe(true);
|
||||
it("Construct a sensor with negative costPerMinute", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", -1, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it('validates a valid metadata', () => {
|
||||
expect(Metadata.verifyMetadata(metadata)).toBe(true);
|
||||
it("Construct a sensor with invalid costPerMinute", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 1.5, 0, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
it('invalidates a corrupt transaction', () => {
|
||||
transaction.outputs[0].amount = 50000;
|
||||
expect(Transaction.verifyTransaction(transaction)).toBe(false);
|
||||
it("Construct a sensor with negative costPerKB", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, -1, "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
describe('transacting with an amount that exceeds the balance', () => {
|
||||
beforeEach(() => {
|
||||
amount = 50000;
|
||||
transaction = Transaction.newTransaction(wallet, recipient, amount);
|
||||
});
|
||||
|
||||
it('does not create the transaction', () => {
|
||||
expect(transaction).toEqual(undefined);
|
||||
});
|
||||
it("Construct a sensor with invalid costPerKB", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, "hello", "test", null, 0)).toThrow();
|
||||
});
|
||||
|
||||
describe('and updating a transaction', () => {
|
||||
let nextAmount, nextRecipient;
|
||||
|
||||
beforeEach(() => {
|
||||
nextAmount = 20;
|
||||
nextRecipient = 'n3xt-4ddr355';
|
||||
transaction = transaction.update(wallet, nextRecipient, nextAmount);
|
||||
});
|
||||
|
||||
it(`subtracts the next amount from the sender's output`, () => {
|
||||
expect(transaction.outputs.find(output => output.address === wallet.publicKey).amount)
|
||||
.toEqual(wallet.balance - amount - nextAmount);
|
||||
});
|
||||
|
||||
it('outputs an amount for the next recipient', () => {
|
||||
expect(transaction.outputs.find(output => output.address === nextRecipient).amount)
|
||||
.toEqual(nextAmount);
|
||||
});
|
||||
it("Construct a sensor with invalid broker", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, 5, null, 0)).toThrow();
|
||||
});
|
||||
|
||||
describe('creating a reward transaction', () => {
|
||||
beforeEach(() => {
|
||||
transaction = Transaction.rewardTransaction(wallet, Wallet.blockchainWallet());
|
||||
});
|
||||
it("Construct a sensor with negative rewardAmount", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", null, -1)).toThrow();
|
||||
});
|
||||
|
||||
it(`reward the miner's wallet`, () => {
|
||||
expect(transaction.outputs.find(output => output.address === wallet.publicKey).amount)
|
||||
.toEqual(MINING_REWARD);
|
||||
});
|
||||
it("Construct a sensor with invalid rewardAmount", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", null, "0")).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a sensor with extra metadata", () => {
|
||||
new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
});
|
||||
|
||||
it("Construct a sensor invalid subject in extra metadata", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: 0,
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a sensor reserved subject in extra metadata", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: SENSHAMART_URI_PREFIX + "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a sensor with invalid predicate in extra metadata", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: {},
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a sensor with reserved predicate in extra metadata", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: SENSHAMART_URI_PREFIX + "and",
|
||||
o: "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a sensor with invalid object in extra metadata", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: []
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Construct a sensor with reserved object in extra metadata", () => {
|
||||
expect(() => new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: SENSHAMART_URI_PREFIX + "something else"
|
||||
}], 0)).toThrow();
|
||||
});
|
||||
|
||||
it("Changing input fails verify", () => {
|
||||
const changing = new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.input = ChainUtil.genKeyPair();
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing counter fails verify", () => {
|
||||
const changing = new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.counter++;
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing rewardAmount fails verify", () => {
|
||||
const changing = new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.rewardAmount++;
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata name fails verify", () => {
|
||||
const changing = new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.name = "else";
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata costPerMinute fails verify", () => {
|
||||
const changing = new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.costPerMinute++;
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata costPerKB fails verify", () => {
|
||||
const changing = new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.costPerKB++;
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
|
||||
it("Changing metadata integrationBroker fails verify", () => {
|
||||
const changing = new SensorRegistration(keyPair, 1, "test", 0, 0, "test", [{
|
||||
s: "something",
|
||||
p: "and",
|
||||
o: "something else"
|
||||
}], 0);
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(true);
|
||||
|
||||
changing.metadata.integrationBroker += "a";
|
||||
|
||||
expect(SensorRegistration.verify(changing).result).toBe(false);
|
||||
});
|
||||
});
|
|
@ -1,7 +1,8 @@
|
|||
const Payment = require('./payment');
|
||||
const Integration = require('./integration');
|
||||
const SensorRegistration = require('./sensor-registration');
|
||||
const BrokerRegistration = require('./broker-registration');
|
||||
const Integration = require('./integration');
|
||||
const Payment = require('./payment');
|
||||
const Compensation = require('./compensation');
|
||||
|
||||
class Transaction {
|
||||
constructor(transaction, type) {
|
||||
|
@ -10,9 +11,13 @@ class Transaction {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
static mapId(type) {
|
||||
return type.name();
|
||||
}
|
||||
static ALL_TYPES = [
|
||||
SensorRegistration,
|
||||
BrokerRegistration,
|
||||
Integration,
|
||||
Payment,
|
||||
Compensation
|
||||
];
|
||||
};
|
||||
|
||||
module.exports = Transaction;
|
26
blockchain/uris.js
Normal file
26
blockchain/uris.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
const PREFIX = require('../util/constants').SENSHAMART_URI_PREFIX;
|
||||
|
||||
module.exports = {
|
||||
PREDICATE: {
|
||||
IS_OWNED_BY: PREFIX + "IsOwnedBy",
|
||||
DEFINES: PREFIX + "Defines",
|
||||
HAS_COUNTER: PREFIX + "HasCounter",
|
||||
COSTS_PER_MINUTE: PREFIX + "CostsPerMinute",
|
||||
COSTS_PER_KB: PREFIX + "CostsPerKB",
|
||||
USES_BROKER: PREFIX + "UsesBroker",
|
||||
HAS_ENDPOINT: PREFIX + "HasEndpoint",
|
||||
CONTAINS_TRANSACTION: PREFIX + "ContainsTransaction",
|
||||
CONTAINS_SENSOR_REGISTRATION: PREFIX + "ContainsSensorRegistration",
|
||||
CONTAINS_BROKER_REGISTRATION: PREFIX + "ContainsBrokerRegistration",
|
||||
TYPE: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
|
||||
LAST_BLOCK: PREFIX + "LastBlock"
|
||||
},
|
||||
OBJECT: {
|
||||
SENSOR_REGISTRATION: PREFIX + "SensorRegistration",
|
||||
BROKER_REGISTRATION: PREFIX + "BrokerRegistration",
|
||||
TRANSACTION: PREFIX + "Transaction",
|
||||
WALLET: PREFIX + "Wallet",
|
||||
BLOCK: PREFIX + "Block",
|
||||
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue