v6 initial commit, maybe more to follow

This commit is contained in:
Josip Milovac 2022-11-22 13:30:19 +11:00
parent 7f91be86c0
commit d6a32870bc
34 changed files with 16875 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
/.vs
/.vscode
/node_modules
/.metals

13
Notes Normal file
View file

@ -0,0 +1,13 @@
npm run dev
PORT=3002 P2P_PORT=5002 PEERS=ws://localhost:5001 npm run dev
PORT=3003 P2P_PORT=5003 PEERS=ws://localhost:5001,ws://localhost:5002 npm run dev
PORT=3004 P2P_PORT=5004 PEERS=ws://localhost:5001,ws://localhost:5002,ws://localhost:5003 npm run dev
PORT=3005 P2P_PORT=5005 PEERS=ws://localhost:5001,ws://localhost:5002,ws://localhost:5003,ws://localhost:5004 npm run dev
.
.
.
.
.
* in this version, on top of adding Mqtt broker to each node and adding new module called integration and integrationVirtual
* we ad blockNum to each block, and we limited the number of transaction for each block

47
Query.json Normal file
View file

@ -0,0 +1,47 @@
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined

BIN
app/.DS_Store vendored Normal file

Binary file not shown.

898
app/index.js Normal file
View file

@ -0,0 +1,898 @@
/**
* npm run dev
* HTTP_PORT=3002 P2P_PORT=5002 MQTT_PORT=1884 PEERS=ws://localhost:5001 npm run dev
* HTTP_PORT=3003 P2P_PORT=5003 MQTT_PORT=1885 PEERS=ws://localhost:5001,ws://localhost:5002 npm run dev
* HTTP_PORT=3004 P2P_PORT=5004 MQTT_PORT=1886 PEERS=ws://localhost:5001,ws://localhost:5002,ws://localhost:5003 npm run dev
* HTTP_PORT=3005 P2P_PORT=5005 MQTT_PORT=1887 PEERS=ws://localhost:5001,ws://localhost:5002,ws://localhost:5003,ws://localhost:5004 npm run dev
*/
/**
* npm run dev //node1
* HTTP_PORT=3002 P2P_PORT=5002 MQTT_PORT=1884 PEERS=ws://45.113.235.182:5001 npm run dev //node2
* HTTP_PORT=3003 P2P_PORT=5003 MQTT_PORT=1885 PEERS=ws://45.113.235.182:5001,ws://45.113.234.151:5002 npm run dev //node3
* HTTP_PORT=3004 P2P_PORT=5004 MQTT_PORT=1886 PEERS=ws://IP:5001,ws://IP:5002,ws://IP:5003 npm run dev //node4
* HTTP_PORT=3005 P2P_PORT=5005 MQTT_PORT=1887 PEERS=ws://IP:5001,ws://IP:5002,ws://IP:5003,ws://IP:5004 npm run dev //node5
*/
/**
* for monitoring the memory and cpu as well as run node in the background use the following,
* note: the second section of the instruction is to change the heap memory
* pm2 start app/index.js --node-args="--max_old_space_size=8192"
* HTTP_PORT=3002 P2P_PORT=5002 MQTT_PORT=1884 PEERS=ws://45.113.235.182:5001 pm2 start app/index.js --node-args="--max_old_space_size=8192"
* HTTP_PORT=3003 P2P_PORT=5003 MQTT_PORT=1885 PEERS=ws://45.113.235.182:5001,ws://45.113.234.151:5002 pm2 start app/index.js --node-args="--max_old_space_size=8192"
* HTTP_PORT=3004 P2P_PORT=5004 MQTT_PORT=1886 PEERS=ws://IP:5001,ws://IP:5002,ws://IP:5003 pm2 start app/index.js --node-args="--max_old_space_size=8192"
* HTTP_PORT=3005 P2P_PORT=5005 MQTT_PORT=1887 PEERS=ws://IP:5001,ws://IP:5002,ws://IP:5003,ws://IP:5004 pm2 start app/index.js --node-args="--max_old_space_size=8192"
* use
* $ pm2 monit
* to monitor the node
*
*/
const express = require('express');
const bodyParser = require('body-parser');
const Blockchain = require('../blockchain');
const P2pServer = require('./p2p-server');
const Wallet = require('../wallet');
const TransactionPool = require('../wallet/transaction-pool');
const Miner = require('./miner');
const morgan = require('morgan');//AddedM
const multer = require('multer');
//const productRoutes = require("./products");//addedM
const newEngine = require('@comunica/actor-init-sparql').newEngine;
const N3 = require('n3');
const jsonld = require('jsonld');
const DataFactory = require('n3').DataFactory;
const fs = require('fs');
const swaggerUi = require('swagger-ui-express')
const swaggerFile = require('./swagger_output.json')
var lodash = require('lodash');
var mqtt = require('mqtt');
var aedes = require('aedes')();
var MQTTserver = require('net').createServer(aedes.handle);
var mosca = require('mosca');
//var awsIot = require('aws-iot-device-sdk');
'use strict';// "use strict" is to indicate that the code should be executed in "strict mode".
// With strict mode, you can not, for example, use undeclared variables.
const parser = new N3.Parser({format: 'application/n-quads'});
const store = new N3.Store();
const store2 = new N3.Store();
const myEngine = newEngine();
const app = express();
const bc = new Blockchain();
const wallet = new Wallet();
const tp = new TransactionPool();
const p2pServer = new P2pServer(bc, tp);
const miner = new Miner(bc, tp, wallet, p2pServer);
//var client = mqtt.connect('mqtt://broker.hivemq.com');
var MOSCAsettings = { MOSCAport:1883 }
//var MOSCAserver = new mosca.Server(MOSCAsettings);
//Mosca mqtt server intialization
// MOSCAserver.on('ready', function(){
// console.log("ready");
// });
//aedes mqtt server intialization
const MQTTport = process.env.MQTT_PORT || 1883;
MQTTserver.listen(MQTTport, function () {
console.log('MQTTserver listening on port', MQTTport)
})
//
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/');
},
filename: function(req, file, cb) {
cb(null, new Date().toISOString() + file.originalname);
}
});
//filtering the type of uploaded files
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === 'application/json' || file.mimetype === 'text/plain' ) {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
const port = process.env.HTTP_PORT || 3001;
app.listen(port, () => console.log(`Listening on port ${port}`));
p2pServer.listen();
app.use('/doc', swaggerUi.serve, swaggerUi.setup(swaggerFile))
function log(message) {
console.log(`New block added: ${message.toString()}`);
// fs.writeFileSync('./blocks.json', JSON.stringify(message),{'flags': 'a'}); //the problem with this function was overwrite even when i changed the flag to 'a'
// fs.appendFile('./blocks.json', JSON.stringify(message) ,function(err){ //this function makes erorrs when the data is big
// if(err) throw err;
// console.log('IS WRITTEN')
// });
var BlockStream = fs.createWriteStream("blocks.json", {flags:'a'});
BlockStream.write(message+ "\n");
}
function logQuery(message) {
//console.log(`New block added: ${message.toString()}`);
// fs.writeFileSync('./blocks.json', JSON.stringify(message),{'flags': 'a'}); //the problem with this function was overwrite even when i changed the flag to 'a'
// fs.appendFile('./blocks.json', JSON.stringify(message) ,function(err){ //this function makes erorrs when the data is big
// if(err) throw err;
// console.log('IS WRITTEN')
// });
var QueryStream = fs.createWriteStream("Query.json", {flags:'a'});
QueryStream.write(message+ "\n");
}
///////////////////////////////
app.use(morgan("dev"));//AddedM
app.use('/uploads', express.static('uploads'));
app.use(bodyParser.urlencoded({ extended: false }));//addedM
app.use(bodyParser.json());
//addedM
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
if (req.method === 'OPTIONS') {
res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
return res.status(200).json({});
}
next();
});
//finished AddedM
//GET functions
app.get('/blocks', (req, res) => {
res.json(bc.chain);
});
app.get('/MetaDataTransactions', (req, res) => {
res.json(tp.metaDataTransactions);
});
app.get('/CoinTransactions', (req, res) => {
res.json(tp.cointransactions);
});
app.get('/Transactions', (req, res) => {
res.json(tp);
});
app.get('/mine-transactions', (req, res) => {
const block = miner.mine();
console.log(`New block added: ${block.toString()}`);
//res.redirect('/blocks');
res.json("Block mined");
});
app.get('/public-key', (req, res) => {
res.json({ publicKey: wallet.publicKey });
});
app.get('/Balance', (req, res) => {
res.json({ Balance: wallet.balance });
});
/////////////////////////////
//POST functions
app.post('/mine', (req, res) => {
const block = bc.addBlock(req.body.data);
console.log(`New block added: ${block.toString()}`);
p2pServer.syncChains();
res.redirect('/blocks');
});
app.post('/RegistringIoTdevice', (req, res)=> {
const {Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail,
OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte,
PaymentPerMinute,Protocol, MessageAttributes, Interval,
FurtherDetails} = req.body;
fs.readdir('./uploads', function(err, files) {
console.log(files[files.length-2]);
var FileName = files[files.length-2];
let rawdata = fs.readFileSync(`./uploads/${FileName}`);
let SSNmetadata = JSON.parse(rawdata);
let NameIn = Name;
let GeoIn = Geo;
let IP_URLIn = IP_URL;
let Topic_TokenIn = Topic_Token;
let PermissionIn = Permission;
let RequestDetailIn = RequestDetail;
let OrgOwnerIn = OrgOwner;
let DepOwnerIn = DepOwner;
let PrsnOwnerIn = PrsnOwner;
let PaymentPerKbyteIn = PaymentPerKbyte;
let PaymentPerMinuteIn = PaymentPerMinute;
let ProtocolIn = Protocol;
let MessageAttributesIn = MessageAttributes;
let IntervalIn = Interval;
let FurtherDetailsIn = FurtherDetails;
var metaDataTransaction = wallet.createMetaDataTransaction(NameIn,
GeoIn, IP_URLIn,Topic_TokenIn,
PermissionIn, RequestDetailIn, OrgOwnerIn,
DepOwnerIn, PrsnOwnerIn, PaymentPerKbyteIn,
PaymentPerMinuteIn,ProtocolIn,
MessageAttributesIn, IntervalIn,
FurtherDetailsIn,
SSNmetadata, tp);
p2pServer.broadcastMetaDataTransaction(metaDataTransaction);});
res.json("MetadataTransactionCreated");});
/**
* the following piece of code
* is for storing the metadata as a Nquad format inside the blockchain
*/
// jsonld.toRDF(metaDataTransaction.SSNmetadata, {format: 'application/n-quads'},
// (err, nquads) => {
// // nquads is a string of N-Quads
// parser.parse(
// nquads,
// (error, quadN, prefixes) => {
// // console.log(quadN)
// if (quadN)
// //console.log(quadN.predicate)
// store.addQuad(DataFactory.quad(
// DataFactory.namedNode(quadN.subject.id),
// DataFactory.namedNode(quadN.predicate.id),
// DataFactory.namedNode(quadN.object.id)));
// });
// });
// metaDataTransaction.SSNmetadata= store;
app.post('/IoTdevicePaymentTransaction', (req, res) => {
const { Recipient_payment_address, Amount_of_money, Payment_method,
Further_details} = req.body;
if (Payment_method == "SensorCoin"){
const PaymentTransaction = wallet.createCoinTransaction(
Recipient_payment_address, Amount_of_money, bc, tp);
p2pServer.broadcastCoinTransaction(PaymentTransaction);
res.json("PaymentTransactionCreated");
}
else if (Payment_method == "Bitcoin") {
res.redirect('/BitcoinTransaction')
}
else if (Payment_method == "PayPal") {
res.redirect('/PayPalTransaction')
}
});
app.post("/UploadMetafile", upload.single('file'), (req, res) => {
// recipient: req.body.recipient,
// amount : req.body.amount,
// const Geo = req.body.Geo;
// const IPSO = req.body.IPSO;
// const Type = req.body.Type;
// const Permission = req.body.Permission;
// const OrgOwner = req.body.OrgOwner;
const file = req.file;
//file : req.body.file
res.status(201).json({
message: 'Uploading Metadata was successful',
MetadataFile : file
});
});
//////simple search engine
app.post('/selectedMeta', (req, res) => {
const {Name}= req.body;
data =bc.chain.map (a => a.data);
var PickedSensors = [];
for (let i= 1; i<data.length; i++ ){
//var pickeditems = [null];
var metadata= data[i][1];
//pickeditems.push(...metadata);
// }
// return meta_array.Geo === 30;
//meta_array=bc.chain.map(b => b.input);
var picked = lodash.find(metadata, x=> x.Name === Name);
if (picked != null){
PickedSensors.push(picked);
}
}
res.json(PickedSensors);
});
//Start of comunica sparql query code
/**
* this code under construction
* try Comunica SPARQL RDFJS
* I believe we need to change the way of storing the metadata
*/
app.post('/sparql', (req, res) => {
const {Select,subject,predicate,object,Limit}= req.body; /**these
variable are used for the sparql query*/
var meta = []//represents the array of all metadata inside blockchain
var queryResult
BlockData =bc.chain.map (a => a.data); /** extracting the data section
from each block inside the whole blockchain */
var i;//i represents the number of blocks inside the whole blockchain
for ( i= 1; i < BlockData.length; i++ ){
var j //represents number of metadata transaction inside each block
for ( j= 0; j<BlockData[i][1].length ;j++){
meta.push(BlockData[i][1][j]["SSNmetadata"]); } }
parser.parse(
nquads,
(error, quadN, prefixes) => {
if (quadN)
store.addQuad(DataFactory.quad(
DataFactory.namedNode(quadN.subject.id),
DataFactory.namedNode(quadN.predicate.id),
DataFactory.namedNode(quadN.object.id)));
else {(console.log("no metadata"))
store.addQuad(DataFactory.quad(
DataFactory.namedNode('http://ex.org/null'),
DataFactory.namedNode('http://ex.org/null'),
DataFactory.namedNode('http://ex.org/null')));}});
const start = async function (a,b){
const result = await myEngine.query(`SELECT ${Select} WHERE
{${subject} ${predicate} ${object}} LIMIT
${Limit}`, { sources: [{ type: 'rdfjsSource',
value: store}] })
result.bindingsStream.on('data', (data) =>
console.log(data.toObject()));
queryResult= result.bindingsStream};
start()
logQuery(queryResult);
res.json(queryResult);});
// this code to query the nquad data straight forward from the blockchain without changing the formt
app.post('/sparql2', (req, res) => {
//find a way to define default values for the comming variables
const {Select,subject,predicate,object,Limit}= req.body; // these variable are used for the sparql query
var meta = [] // represents the array of all metadata inside the blockchain
var queryResult
/**
* change the following code to custome map function to remove the for loop
* and make the code faster
*/
BlockData =bc.chain.map (a => a.data); //extracting the data section from each block inside the whole blockchain
var i;//i represents the number of blocks inside the whole blockchain
for ( i= 1; i < BlockData.length; i++ ){ /**the purpose of this for loop is passing each BlockData to check for metadata
this loop could be avoided if we used custome map function */
var j // j represents the number of metadata transaction inside each block
for ( j= 0; j<BlockData[i][1].length ;j++){ /** the purpose of this for loop is passing each metadata transaction inside each block
this loop could be avoided if we used custome map function */
meta.push(BlockData[i][1][j]["SSNmetadata"]); /**this array depends on the structure of the data section from chain from bc
i represents the number of blocks inside the whole blockchain
j represents the number of metadarta transaction inside each block */
}
}
console.log(meta) // printing the metadata just for testing purposes
// jsonld.toRDF(meta, {format: 'application/n-quads'}, (err, nquads) => { /**
// parser.parse( /**this piece of code is used for parse the metadata and store it in N3store */
// nquads,
// (error, quadN, prefixes) => {
// // console.log(quadN)
// if (quadN)
// store2.addQuad(DataFactory.quad(
// DataFactory.namedNode(meta.subject.id), DataFactory.namedNode(meta.predicate.id), DataFactory.namedNode(meta.object.id)));
// // console.log(store)
// });
// const start = async function (a,b){ // we need this line of code to allow "await" function to work because it requires async function
// const result = await myEngine.query(`SELECT ${Select} WHERE {${subject} ${predicate} ${object}} LIMIT ${Limit}`,
// { sources: [{ type: 'rdfjsSource', value: meta}] })
// result.bindingsStream.on('data', (data) => console.log(data.toObject()));
// queryResult= result.bindingsStream
// // console.log(queryResult)
// };
// start()
// res.json(queryResult);
});
//try to make it return the query results insted of the metadata
//});
/**
* this part is an implementation for sparql-engine
* any line code will be added for this reason will have a comment of "sparql-engne"
*
*/
// const {Parser, Store }=require('n3');
// const {Graph, HashMapDataset, PlanBuilder}= require('sparql-engine');//sparql-engine related
// const CustomGraph =require(/*import your Grapg subclass */);
// // Format a triple pattern acccording to N3 API
// // SPARQL variables must be replaced by `null` values
// function formatTriplePattern (triple){
// let subject = null
// let predicate = null
// let object = null
// if (!triple.subject.startWith('?')){
// subject = triple.subject
// }
// if (!triple.predicate.startWith('?')){
// predicate = triple.predicate
// }
// if(!triple.object.startWith('?')){
// object = triple.object
// }
// return { subject, predicate, object}
// }
//////////////////////END OF SPARQL_ENGINE CODE
// ////////////////sparqle-engine code startred/////////
// /**
// * the following code is for checking sparql-engine
// */
// // class CustomGraph extends Graph {
// // /**
// // * Returns an iterator that finds RDF triples matching a triple pattern in the graph.
// // * @param triple - Triple pattern to find
// // * @return An observable which finds RDF triples matching a triple pattern
// // */
// // find (triple:TripleObject, options: Object): Observable<TripleObject> {/* */}
// // }
// class N3Graph extends Graph {
// constructor (){
// super()
// this._store = store()
// }
// insert (triple){
// return new Promise((resolve, reject) => {
// try {
// this._store.addTriple(triple.subject, triple.predicate, triple.object)
// resolve()
// } catch (e) {
// reject (e)
// }
// })
// }
// find (triple) {
// const {subject, predicate, object}= formatTriplePattern(triple)
// return this._store.getTriple (subject, predicate, object)
// }
// estimateCardinality (triple){
// const {subject,predicate,object} = formatTriplePattern(triple)
// return Promise.resolve(this._store.countTriples(subject,predicate,object))
// }
// }
// const graph = new N3Graph()
// const dataset = new HashMapDataset ('http://example.org#default', graph)
// //load some RDF data into the graph
// const parser = new Parser()
// parser.parse(`
// @prefix foaf: <http://xmlns.com/foaf/0.1/> .
// @prefic : <http://example.org#> .
// :a foaf:name "a" .
// :b foaf:name "b" .
// `).forEach(t => {graph._store.addTriple(t)
// })
// // const GRAPH_A_IRI ='http://example.org#graph-a';
// // const GRAPH_B_IRI ='http://example.org#graph-b';
// // const graph_a =new CustomGraph(/* */);
// // const graph_b = new CustomGraph(/* */);
// // //we set graph_a as a defualt RDF dataset
// // const dataset = new HashMapDataset(GRAPH_A_IRI, graph_a);
// // //insert graph_b as a Named Grapg
// // dataset.addNamedGraph(GRAPH_B_IRI, graph_b);
// //Get the Name of all the people in the default Graph
// // const query= `
// // PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
// // PREFIX foaf: <http://xmlns.com/foaf/0.1>
// // SELECT ?name
// // WHERE{
// // ?s a foaf:Person .
// // ?s rdfs:label ?label .
// // }`
// const query = `
// PREFIX foaf: <http://xmlns.com/foaf/0.1/>
// SELECT ?name
// WHERE {
// ?s foaf:name ?name .
// }`
// // Creates a plan builder for the RDF dataset
// const builder = new PlanBuilder(dataset);
// // Get an iterator to evaluate the query
// const iterator = builder.build(query);
// //read results
// iterator.subscribe(
// binding => console.log(bindings),
// err => console.error(err),
// () => console.log('Query evaluation complete')
// );
///////////////////////////////////////////////////////////Integration///////////////////////////////////////////////////////////
DistributedBrokers = ["mqtt.eclipse.org", "test.mosquitto.org","broker.hivemq.com"];
DistributedBrokersPorts = [1883,1883,1883];
function makeTopic(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
paymentAmount = [];
paymentAddress = [];
IoTDeviceAddress = [];
IoTApplicationAddress=[];
MassagiesRecived = [];
var MiddlewareClients =[];
MassageCounter =[];
StartSending = [];
MiddlewareTracking =[];
Durations = [];
Intervals = [];
i = 0;//RequestsIndex
app.post('/IoTdeviceIntegration-Control', (req, res) => {
const {IoTDeviceID,paymentTransactionID,Duration,Protocol}= req.body;
Durations.push(Duration);
MassageCounter.push(0)
MassagiesRecived.push(false);
data =bc.chain.map (a => a.data);
MetaANDTransFound = false;
for (let j= data.length-1; j>0; j-- ){/** this for loop load
Blockchain and search for metadata and payment transaction that match
the provided MetadataID and TransactionID */
var metadata = data[j][1];
var transaction = data [j][0];
var pickedMetadata = lodash.find(metadata, x=>
x.id === IoTDeviceID);
var pickedTransction = lodash.find(transaction, x=>
x.id === paymentTransactionID);
if (pickedMetadata != null && pickedTransction !=null){
MetaANDTransFound = true;
break;} }
if (MetaANDTransFound){
//Loading the IoTdevice parameters in order to connect to it
var IoTDeviceBroker = pickedMetadata.IP_URL.toString();//mqtt broker
var IoTDeviceTopic = pickedMetadata.Topic_Token;// mqtt topic
paymentAmount.push(pickedTransction.outputs[1].amount);
paymentAddress.push(pickedTransction.outputs[1].address);
IoTDeviceAddress.push(pickedMetadata.Signiture.address);
IoTApplicationAddress.push(pickedTransction.input.address);
Intervals.push(pickedMetadata.Interval)
if (paymentAddress[i] == wallet.publicKey){
res.redirect('/IoTdataObtainingAndForward');}
else{
console.log("payment Address not match")}}
else{
console.log("Metadata or Transaction not found")
if (pickedMetadata == null){
console.log("metadata not found")}
if (pickedTransction == null){
console.log("Transaction not found")} }
MetaANDTransFound = false;
i++;
res.json("true");});
app.get ('/IoTdataObtainingAndForward', (req, res) => {
console.log (`transaction of IoT Application ${i} approved`)
BrokerRandomNumber = (Math.floor(
Math.random()*DistributedBrokers.length)+1)-1 /** collect
a random number to select a random broker*/
MiddlewareBroker = DistributedBrokers[BrokerRandomNumber];
MiddlewareTopic = makeTopic(5);// generate random topic
MiddlewarePort = DistributedBrokersPorts[BrokerRandomNumber];
//loading the configuration massage
configurationMessage = {"host/broker":MiddlewareBroker,
"topic":MiddlewareTopic,
"port":MiddlewarePort,
"duration":Duration} /** add pk of the node
connect to the IoT device and send the configuration massage*/
var IoTDeviceClient = mqtt.connect(IoTDeviceBroker);
MiddlewareClients.push(mqtt.connect(`mqtt://${MiddlewareBroker}`))
var MiddlewareClient = MiddlewareClients[i]
IoTDeviceClient.on("connect", ack => {
console.log("connected! to IoT Device Client");
IoTDeviceClient.subscribe(IoTDeviceTopic, err => {
console.log(err); });
IoTDeviceClient.publish(IoTDeviceTopic, JSON.stringify(
configurationMessage));});
IoTDeviceClient.on("error", err => {
console.log(err);});
IoTDeviceClient.on("message", (topic, message) => {
console.log(message.toString())
IoTDeviceClient.end(true)});
/** connect the randomly choosed mqtt middlware broker to
* listen to the transmitted massagies */
MiddlewareClient.on("connect", ack => {
console.log("connected!");
console.log(MiddlewareBroker)
StartSending.push(Date.now());
MiddlewareClient.subscribe(MiddlewareTopic, err => {
console.log(err); });});
MiddlewareTracking.push({index:i,
TrackingTopic:MiddlewareTopic})/** this used to track the connection
in case there are multiple conection at the same time */
MiddlewareClient.on("message", (topic, message) => {/** call back,
will run each time a massage recived, I did it in a way if there are
multiple connections, it will run for all the massagies, then truck the
massagies by MiddlwareTracking Array */
var MiddlewareFound = MiddlewareTracking.filter(function(item) {
return item.TrackingTopic == topic;});
console.log(MiddlewareFound);
console.log(message.toString());
MiddlewareIndex = MiddlewareFound[0].index/** this is the index of
the connection or the Middleware*/
console.log(MiddlewareIndex)
MassageCounter[MiddlewareIndex]++;/** this used to track the number
of recived massagies of each connection */
console.log(Date.now()-StartSending[MiddlewareIndex])
if (Date.now() - StartSending[MiddlewareIndex] >=
(Durations[MiddlewareIndex]*1000)
-Intervals[MiddlewareIndex]*1000){
console.log("sending time finished")
if (MassageCounter[MiddlewareIndex] > 0.75*(
Durations[MiddlewareIndex]/Intervals[MiddlewareIndex])
){/** which means most of massagies have been sent */
console.log("massages recived")
MassagiesRecived[MiddlewareIndex] = true;}
if (MassagiesRecived[MiddlewareIndex]){/** if massagies recived,
pay the 10% as service fees */
const PaymentTransaction = wallet.createPaymentTransaction(
NodeAddress,(0.1*paymentAmount[MiddlewareIndex]) , bc, tp);
p2pServer.broadcastPaymentTransaction(PaymentTransaction);
console.log("amount paid to the IoT device")
console.log(MiddlewareIndex)
MiddlewareClient = MiddlewareClients[MiddlewareIndex];
/** disconnect the middleware mqtt broker */
MiddlewareClient.end(true)}
else{// if massagies not recived, pay the IoT application back
res.redirect('/IoTapplicationCompensationTransaction')}};});
app.post('/IoTApplicationCompensationTransaction', (req, res) => {
const { Recipient_payment_address, Amount_of_money, Payment_method,
Further_details} = req.body;
if (Payment_method == "SensorCoin"){
const PaymentTransaction = wallet.createPaymentTransaction(
Recipient_payment_address, Amount_of_money, bc, tp);
p2pServer.broadcastPaymentTransaction(PaymentTransaction);
res.json("PaymentTransactionCreated");
}
else if (Payment_method == "Bitcoin") {
res.redirect('/BitcoinTransaction')
}
else if (Payment_method == "PayPal") {
res.redirect('/PayPalTransaction')
}
});
app.post ('/IoTapplicationCompensation', (req, res) => {
const PaymentTransaction = wallet.createPaymentTransaction(IoTApplicationAddress[MiddlewareIndex],
(paymentAmount[MiddlewareIndex]) , bc, tp);
p2pServer.broadcastPaymentTransaction(PaymentTransaction);
console.log("amount paid back to the IoT Application")
});
var IoTDeviceMassage ="test"
app.post('/integrateVirtual', (req, res) => {
const {IoTDeviceID,paymentTransactionID,Duration,Protocol}= req.body;
Durations.push(Duration);
MassageCounter.push(0)
MassagiesRecived.push(false);
data =bc.chain.map (a => a.data);
MetaANDTransFound = true;
// for (let j= 1; j<data.length; j++ ){// this for loop load the Blockchain and search for metadata and payment transaction that match the provided MetadataID and TransactionID
// var metadata = data[j][1];
// var transaction = data [j][0];
// var pickedMetadata = lodash.find(metadata, x=> x.id === IoTDeviceID); ////one thing to consider, what if the IoT device has multiple meatadata (updated metadata)??
// var pickedTransction = lodash.find(transaction, x=> x.id === paymentTransactionID);
// if (pickedMetadata != null && pickedTransction !=null){
// MetaANDTransFound = true;
// break;
// }
// }
if (MetaANDTransFound){
//Loading the IoTdevice parameters in order to connect to it
// var IoTDeviceBroker = pickedMetadata.IP_URL.toString();//mqtt broker
// var IoTDeviceTopic = pickedMetadata.Topic_Token;// mqtt topic
paymentAmount.push(10)//pickedTransction.outputs[1].amount);
paymentAddress.push("ADsf")//pickedTransction.outputs[1].address);
IoTDeviceAddress.push("fth")//pickedMetadata.Signiture.address);
IoTApplicationAddress.push("dtyuyf")//pickedTransction.input.address);
Intervals.push(10)//pickedMetadata.Interval)
var device = awsIot.device({
keyPath: './aws-iot-device-sdk/node_modules/certs/private.pem.key',
certPath: './aws-iot-device-sdk/node_modules/certs/certificate.pem.crt',
caPath: './aws-iot-device-sdk/node_modules/certs/RootCA1.pem',
clientId: 'arn:aws:iot:us-east-1:712303746524:thing/SecondVirtualIoTdevice',
host: 'a11joipjrff8s7-ats.iot.us-east-1.amazonaws.com'
});
//if (paymentAddress[i] == wallet.publicKey){
console.log (`transaction of IoT Application ${i} approved`) // add later the check if the amount is match with the required duration
BrokerRandomNumber = (Math.floor(Math.random()*DistributedBrokers.length)+1)-1 // collect a random number to select a random broker
MiddlewareBroker = DistributedBrokers[BrokerRandomNumber];
MiddlewareTopic = makeTopic(5);// generate random topic
MiddlewarePort = DistributedBrokersPorts[BrokerRandomNumber];
//loading the configuration massage
// configurationMessage = {"host/broker":MiddlewareBroker,"topic":MiddlewareTopic, "port":MiddlewarePort, "duration":Duration} // add pk of the node
// connect to the IoT device and send the configuration massage
// var IoTDeviceClient = mqtt.connect(IoTDeviceBroker);
// MiddlewareClients.push(mqtt.connect(`mqtt://${MiddlewareBroker}`))
// var MiddlewareClient = MiddlewareClients[i]
// IoTDeviceClient.on("connect", ack => {
// console.log("connected! to IoT Device Client");
// IoTDeviceClient.subscribe(IoTDeviceTopic, err => {
// console.log(err);
// });
// IoTDeviceClient.publish(IoTDeviceTopic, JSON.stringify(configurationMessage));
// });
// IoTDeviceClient.on("error", err => {
// console.log(err);
// });
// IoTDeviceClient.on("message", (topic, message) => {
// console.log(message.toString())
// IoTDeviceClient.end(true)
// });
// IoTDeviceClient.on("close", ack => {
// console.log("Disconnected from IoT Device Client");
// });
device
.on('connect', function() {
console.log('connect');
device.subscribe('/weather/data');// change it to a topic from the metadata
// device.publish('/weather/data', JSON.stringify({ test_data: 2}));
});
device
.on('message', function(topic, payload) {
// console.log('message', topic, payload.toString());
IoTDeviceMassage = payload.toString();
MiddlewareClient.publish(MiddlewareTopic,IoTDeviceMassage)
});
// connect the randomly choosed mqtt middlware broker to listen to the transmitted massagies
MiddlewareClients.push(mqtt.connect(`mqtt://${MiddlewareBroker}`))
var MiddlewareClient = MiddlewareClients[i]
MiddlewareClient.on("connect", ack => {
console.log("connected!");
console.log(MiddlewareBroker)
StartSending.push(Date.now());
MiddlewareClient.subscribe(MiddlewareTopic, err => {
console.log(err);
});
MiddlewareClient.publish(MiddlewareTopic,IoTDeviceMassage)
});
MiddlewareTracking.push({index:i, TrackingTopic:MiddlewareTopic})// this used to track the connection in case there are multiple conection at the same time
MiddlewareClient.on("message", (topic, message) => {// call back, will run each time a massage recived, I did it in a way if there are multiple connections, it will run for all the massagies, then we truck the massahies by MiddlwareTracking Array
console.log(message.toString());
var MiddlewareFound = MiddlewareTracking.filter(function(item) {
return item.TrackingTopic == topic;
});
console.log(MiddlewareFound);
MiddlewareIndex = MiddlewareFound[0].index;// this is the index of the connection or the Middleware
console.log(MiddlewareIndex);
MassageCounter[MiddlewareIndex]++;// this used to track the number of recived massagies of each connection
console.log(Date.now()-StartSending[MiddlewareIndex])
if (Date.now() - StartSending[MiddlewareIndex] >= (Durations[MiddlewareIndex]*1000)-Intervals[MiddlewareIndex]*1000){
console.log("sending time finished")
if (MassageCounter[MiddlewareIndex] > 0.75*(Durations[MiddlewareIndex]/Intervals[MiddlewareIndex])){// which means most of massagies have been sent
console.log("massages recived")
MassagiesRecived[MiddlewareIndex] = true;
}
if (MassagiesRecived[MiddlewareIndex]){// if massagies recived, pay the IoT device and substract 10% as service fees
const cointransaction = wallet.createCoinTransaction(IoTDeviceAddress[MiddlewareIndex],(paymentAmount[MiddlewareIndex]-0.1*paymentAmount[MiddlewareIndex]) , bc, tp);
p2pServer.broadcastCoinTransaction(cointransaction);
console.log("amount paid to the IoT device")
console.log(MiddlewareIndex)
MiddlewareClient = MiddlewareClients[MiddlewareIndex];// disconnect the middleware mqtt broker
MiddlewareClient.end(true)
}
else{// if massagies not recived, pay the ioT application back
const cointransaction = wallet.createCoinTransaction(IoTApplicationAddress[MiddlewareIndex],(paymentAmount[MiddlewareIndex]) , bc, tp);
p2pServer.broadcastCoinTransaction(cointransaction);
console.log("amount paid back to the IoT Application")
console.log(MiddlewareIndex)
MiddlewareClient = MiddlewareClients[MiddlewareIndex];// disconnect the middleware mqtt broker
MiddlewareClient.end(true)
}
}
});
// }
// else{
// console.log("payment Address not match")
// }
}
else{
console.log("Metadata or Transaction not found")
if (pickedMetadata == null){
console.log("metadata not found")
}
if (pickedTransction == null){
console.log("Transaction not found")
}
}
MetaANDTransFound = false;
i++;
//
// Device is an instance returned by mqtt.Client(), see mqtt.js for full
// documentation.
//
res.json("true");
});
});

63
app/miner.js Normal file
View file

@ -0,0 +1,63 @@
const Wallet = require('../wallet');
const CoinTransaction = require('../wallet/CoinTransaction');
const MetaDataTransaction = require('../wallet/MetaDataTransaction');
//const MetaDataPool= require('../wallet/metaData-Pool');
const { MaxNumOfCoinTransactions, MaxNumOfMetadataTransactions} = require('../config');
var TransactionPointer;
var NumberOfClearedCoins;
var NumberOfClearedMeta;
class Miner {
constructor(blockchain, transactionPool, wallet, p2pServer) {
this.blockchain = blockchain;
this.transactionPool = transactionPool;
this.wallet = wallet;
this.p2pServer = p2pServer;
}
mine() {
//const validCoinTransactions = this.transactionPool.validCoinTransactions(); Temeperarly changed without checking transaction validity
var SelectedCoinTransactions = this.transactionPool.cointransactions.slice(0, MaxNumOfCoinTransactions);
var SelectedMetadataTransactions = this.transactionPool.metaDataTransactions.slice(0, MaxNumOfMetadataTransactions);
//const validTransactions = validCoinTransactions.Concat(validMetaDataTransactions);
// SelectedCoinTransactions = validCoinTransactions.splice (0, MaxNumOfCoinTransactions); //this will return only limited number of transactions to be stored
// for (TransactionPointer=0; TransactionPointer<MaxNumOfCoinTransactions; TransactionPointer++){
// SelectedCoinTransactions.push(validCoinTransactions[TransactionPointer]);
// }
// SelectedMetadataTransactions = validMetadataTransactions.splice (0, MaxNumOfMetadataTransactions);
// for (TransactionPointer=0; TransactionPointer<MaxNumOfCoinTransactions; TransactionPointer++){
// SelectedCoinTransactions.push(validCoinTransactions[TransactionPointer]);
// }
// include a reward transaction for the miner
SelectedCoinTransactions.push(CoinTransaction.rewardCoinTransaction(this.wallet, Wallet.blockchainWallet()));
//CoinTransaction.rewardCoinTransaction(this.wallet, Wallet.blockchainWallet()));
// create a block consisting of the valid transactions
const block = this.blockchain.addBlock([SelectedCoinTransactions,SelectedMetadataTransactions]);
//const block = this.blockchain.addBlock(validTransactions);
// synchronize chains in the peer-to-peer server
this.p2pServer.syncChains();
// clear the transaction pool
// broadcast to every miner to clear their transaction pools
// if (validCoinTransactions.length>MaxNumOfCoinTransactions){
// this.transactionPool.clearCoin();// clears only selected cointransactions
this.transactionPool.clearCoin(SelectedCoinTransactions.length-1);
this.transactionPool.clearMeta(SelectedMetadataTransactions.length);
this.p2pServer.broadcastClearCoinTransactions();
this.p2pServer.broadcastClearMetadataTransactions();
SelectedCoinTransactions = [];
SelectedMetadataTransactions =[];
// }
// else {
// this.transactionPool.clearAll();
// this.p2pServer.broadcastClearAllTransactions();
// }
return block;
}
}
//module.exports = {NumberOfClearedCoins, NumberOfClearedMeta};
module.exports = Miner;

124
app/p2p-server.js Normal file
View file

@ -0,0 +1,124 @@
const Websocket = require('ws');
const P2P_PORT = process.env.P2P_PORT || 5001;
const peers = process.env.PEERS ? process.env.PEERS.split(',') : [];
const MESSAGE_TYPES = {
chain: 'CHAIN',
cointransaction: 'COINTRANSACTION',
clear_payment_transactions: 'CLEAR_Payment_TRANSACTIONS',
clear_meta_transactions: 'CLEAR_META_TRANSACTIONS',
clear_Comp_transactions: 'CLEAR_COMP_TRANSACTIONS',
clear_Integration_transactions: 'CLEAR_Integration_TRANSACTIONS',
metaDataTransaction: 'METADATATRANSACTION',};
class P2pServer {
constructor(blockchain, transactionPool) {
this.blockchain = blockchain;
this.transactionPool = transactionPool;
this.sockets = [];}
listen() {
const server = new Websocket.Server({ port: P2P_PORT });
server.on('connection', socket => this.connectSocket(socket));
this.connectToPeers();
console.log(`Listening for peer-to-peer connections on: ${P2P_PORT}`);}
connectToPeers() {
peers.forEach(peer => {
const socket = new Websocket(peer);
socket.on('open', () => this.connectSocket(socket)); });}
connectSocket(socket) {
this.sockets.push(socket);
console.log('Socket connected');
this.messageHandler(socket);
this.sendChain(socket);}
messageHandler(socket) {
socket.on('message', message => {
const data = JSON.parse(message);
switch(data.type) {
case MESSAGE_TYPES.chain:
this.blockchain.replaceChain(data.chain);
break;
case MESSAGE_TYPES.paymenttransaction:
this.transactionPool.updateOrAddPaymentTransaction(
data.Paymenttransaction);
break;
case MESSAGE_TYPES.metaDataTransaction:
this.transactionPool.updateOrAddMetaDataTransaction(
data.metaDataTransaction);
break;
case MESSAGE_TYPES.CompTransaction:
this.transactionPool.updateOrAddCompTransaction(
data.CompTransaction);
break;
case MESSAGE_TYPES.IntegrationTransaction:
this.transactionPool.updateOrAddIntegrationTransaction(
data.IntegrationTransaction);
break;
case MESSAGE_TYPES.clear_Payment_transactions:
this.transactionPool.clearPayment(this.blockchain.chain[this.
blockchain.chain.length-1].data[0].length-1);
break;
case MESSAGE_TYPES.clear_meta_transactions:
this.transactionPool.clearMeta(this.blockchain.chain[this.
blockchain.chain.length-1].data[1].length);
break;
case MESSAGE_TYPES.clear_comp_transactions:
this.transactionPool.clearMeta(this.blockchain.chain[this.
blockchain.chain.length-1].data[1].length);
break;
case MESSAGE_TYPES.clear_intgration_transactions:
this.transactionPool.clearMeta(this.blockchain.chain[this.
blockchain.chain.length-1].data[1].length);
break;}});}
sendChain(socket) {
socket.send(JSON.stringify({
type: MESSAGE_TYPES.chain,
chain: this.blockchain.chain}));}
ClearedPayments (socket){
socket.send(JSON.stringify({
type: MESSAGE_TYPES.clear_payment_transactions,})); }
ClearedMeta (socket){
socket.send(JSON.stringify({
type: MESSAGE_TYPES.clear_meta_transactions,}));}
ClearedComp (socket){
socket.send(JSON.stringify({
type: MESSAGE_TYPES.clear_comp_transactions,}));}
ClearedIntegration (socket){
socket.send(JSON.stringify({
type: MESSAGE_TYPES.clear_integration_transactions,}));}
sendPaymentTransaction(socket, paymenttransaction) {
socket.send(JSON.stringify({
type: MESSAGE_TYPES.paymenttransaction,
paymenttransaction}));}
sendMetaDataTransaction(socket, metaDataTransaction) {
socket.send(JSON.stringify({
type: MESSAGE_TYPES.metaDataTransaction,
metaDataTransaction}));}
sendIntegrationTransaction(socket, integrationTransaction) {
socket.send(JSON.stringify({
type: MESSAGE_TYPES.integrationTransaction,
integrationTransaction}));}
sendCompTransaction(socket, compTransaction) {
socket.send(JSON.stringify({
type: MESSAGE_TYPES.compTransaction,
compTransaction}));}
syncChains() {
this.sockets.forEach(socket => this.sendChain(socket));}
broadcastPaymentTransaction(paymenttransaction) {
this.sockets.forEach(socket => this.sendPaymentTransaction(socket,
paymenttransaction));}
broadcastMetaDataTransaction(metaDataTransaction) {
this.sockets.forEach(socket => this.sendMetaDataTransaction(socket,
metaDataTransaction));}
broadcastCompTransaction(compTransaction) {
this.sockets.forEach(socket => this.sendCompTransaction(socket,
CompTransaction));}
broadcastIntegrationTransaction(integrationTransaction) {
this.sockets.forEach(socket => this.sendIntegrationTransaction(socket,
integrationTransaction));}
broadcastClearPaymentTransactions() {
this.sockets.forEach(socket => this.ClearedCoins(socket));}
broadcastClearMetadataTransactions() {
this.sockets.forEach(socket => this.ClearedMeta(socket));}
broadcastClearCompTransactions() {
this.sockets.forEach(socket => this.ClearedComp(socket));}
broadcastClearIntegrationTransactions() {
this.sockets.forEach(socket => this.ClearedIntegration(socket));}}
module.exports = P2pServer ;

6
app/swagger.js Normal file
View file

@ -0,0 +1,6 @@
const swaggerAutogen = require('swagger-autogen')()
const outputFile = './swagger_output.json'
const APIfiles = ['./index.js']
swaggerAutogen(outputFile, APIfiles)

369
app/swagger_output.json Normal file
View file

@ -0,0 +1,369 @@
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "REST API",
"description": ""
},
"host": "localhost:3000",
"basePath": "/",
"schemes": [
"http"
],
"paths": {
"/blocks": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/MetaDataTransactions": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/CoinTransactions": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/Transactions": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/mine-transactions": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/public-key": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/Balance": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/mine": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"data": {
"example": "any"
}
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/IoTdeviceRegistration": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"Name": {
"example": "any"
},
"Geo": {
"example": "any"
},
"IP_URL": {
"example": "any"
},
"Topic_Token": {
"example": "any"
},
"Permission": {
"example": "any"
},
"RequestDetail": {
"example": "any"
},
"OrgOwner": {
"example": "any"
},
"DepOwner": {
"example": "any"
},
"PrsnOwner": {
"example": "any"
},
"PaymentPerKbyte": {
"example": "any"
},
"PaymentPerMinute": {
"example": "any"
},
"Protocol": {
"example": "any"
},
"MessageAttributes": {
"example": "any"
},
"Interval": {
"example": "any"
},
"FurtherDetails": {
"example": "any"
}
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/IoTdevicePaymentTransaction": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"Recipient_payment_address": {
"example": "any"
},
"Amount_of_money": {
"example": "any"
},
"Payment_method": {
"example": "any"
},
"Further_details": {
"example": "any"
}
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/UploadMetafile": {
"post": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
},
"201": {
"description": "Created"
}
}
}
},
"/selectedMeta": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"Name": {
"example": "any"
}
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/sparql": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"Select": {
"example": "any"
},
"subject": {
"example": "any"
},
"predicate": {
"example": "any"
},
"object": {
"example": "any"
},
"Limit": {
"example": "any"
}
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/sparql2": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"Select": {
"example": "any"
},
"subject": {
"example": "any"
},
"predicate": {
"example": "any"
},
"object": {
"example": "any"
},
"Limit": {
"example": "any"
}
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/IoTdeviceIntegration-Control": {
"post": {
"description": "",
"parameters": [
{
"name": "body",
"in": "body",
"schema": {
"type": "object",
"properties": {
"IoTDeviceID": {
"example": "any"
},
"paymentTransactionID": {
"example": "any"
},
"Duration": {
"example": "any"
},
"Protocol": {
"example": "any"
}
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/IoTdataObtainingAndForward": {
"get": {
"description": "",
"parameters": [],
"responses": {
"200": {
"description": "OK"
}
}
}
}
}
}

BIN
blockchain/.DS_Store vendored Normal file

Binary file not shown.

58
blockchain/block.js Normal file
View file

@ -0,0 +1,58 @@
const ChainUtil = require('../chain-util');
const { DIFFICULTY, MINE_RATE } = require('../config');
class Block {
constructor(timestamp,BlockNum, lastHash, hash, data, nonce,
difficulty) {
this.timestamp = timestamp;
this.BlockNum = BlockNum;
this.lastHash = lastHash;
this.hash = hash;
this.data = data;
this.nonce = nonce;
this.difficulty = difficulty || DIFFICULTY;
}
toString() {
return `Block -
Timestamp : ${this.timestamp}
BlockNum : ${this.BlockNum}
Last Hash : ${this.lastHash.substring(0, 10)}
Hash : ${this.hash.substring(0, 10)}
Nonce : ${this.nonce}
Difficulty: ${this.difficulty}
Data : ${this.data[0].length}`;
}
static genesis() {
return new this('Genesis-time',0, '-----', 'First-Hash',
[], 0, DIFFICULTY);
}
static mineBlock(lastBlock, data) {
let hash, timestamp;
const lastHash = lastBlock.hash;
let { difficulty } = lastBlock;
let nonce = 0;
let BlockNum = lastBlock.BlockNum + 1;
do {
nonce++;
timestamp = Date.now();
difficulty = Block.adjustDifficulty(lastBlock, timestamp);
hash = Block.hash(timestamp, lastHash, data, nonce, difficulty);
} while (hash.substring(0, difficulty) !== '0'.repeat(difficulty));
return new this(timestamp,BlockNum, lastHash, hash, data, nonce,
difficulty);}
static hash(timestamp,lastHash, data, nonce, difficulty) {
return ChainUtil.hash(`${timestamp}${lastHash}${data}${nonce}
${difficulty}`).toString();
}
static blockHash(block) {
const { timestamp, lastHash, data, nonce, difficulty } = block;
return Block.hash(timestamp, lastHash, data, nonce, difficulty);
}
static adjustDifficulty(lastBlock, currentTime) {
let { difficulty } = lastBlock;
difficulty = lastBlock.timestamp + MINE_RATE > currentTime ?
difficulty + 1 : difficulty - 1;
return difficulty;
}
}
module.exports = Block;

34
blockchain/block.test.js Normal file
View file

@ -0,0 +1,34 @@
const Block = require('./block');
describe('Block', () => {
let data, lastBlock, block;
beforeEach(() => {
data = 'bar';
lastBlock = Block.genesis();
block = Block.mineBlock(lastBlock, data);
});
it('sets the `data` to match the input', () => {
expect(block.data).toEqual(data);
});
it('sets the `lastHash` to match the hash of the last block', () => {
expect(block.lastHash).toEqual(lastBlock.hash);
});
it('generates a hash that matches the difficulty', () => {
expect(block.hash.substring(0, block.difficulty))
.toEqual('0'.repeat(block.difficulty));
});
it('lowers the difficulty for slowly mined blocks', () => {
expect(Block.adjustDifficulty(block, block.timestamp+360000))
.toEqual(block.difficulty-1);
});
it('raises the difficulty for quickly mined blocks', () => {
expect(Block.adjustDifficulty(block, block.timestamp+1))
.toEqual(block.difficulty+1);
});
});

45
blockchain/index.js Normal file
View file

@ -0,0 +1,45 @@
const Block = require('./block');
class Blockchain {
constructor() {
this.chain = [Block.genesis()];
}
addBlock(data) {
const block = Block.mineBlock(this.chain[this.chain.length-1], data);
this.chain.push(block);
return block;
}
isValidChain(chain) {
if(JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis())) return false;
for (let i=1; i<chain.length; i++) {
const block = chain[i];
const lastBlock = chain[i-1];
if (block.lastHash !== lastBlock.hash ||
block.hash !== Block.blockHash(block)) {
return false;
}
}
return true;
}
replaceChain(newChain) {
if (newChain.length <= this.chain.length) {
console.log('Received chain is not longer than the current chain.');
return;
} else if (!this.isValidChain(newChain)) {
console.log('The received chain is not valid.');
return;
}
console.log('Replacing blockchain with the new chain.');
this.chain = newChain;
}
}
module.exports = Blockchain;

55
blockchain/index.test.js Normal file
View file

@ -0,0 +1,55 @@
const Blockchain = require('./index');
const Block = require('./block');
describe('Blockchain', () => {
let bc, bc2;
beforeEach(() => {
bc = new Blockchain();
bc2 = new Blockchain();
});
it('starts with genesis block', () => {
expect(bc.chain[0]).toEqual(Block.genesis());
});
it('adds a new block', () => {
const data = 'foo';
bc.addBlock(data);
expect(bc.chain[bc.chain.length-1].data).toEqual(data);
});
it('validates a valid chain', () => {
bc2.addBlock('foo');
expect(bc.isValidChain(bc2.chain)).toBe(true);
});
it('invalidates a chain with a corrupt genesis block', () => {
bc2.chain[0].data = 'Bad data';
expect(bc.isValidChain(bc2.chain)).toBe(false);
});
it('invalidates a corrupt chain', () => {
bc2.addBlock('foo');
bc2.chain[1].data = 'Not foo';
expect(bc.isValidChain(bc2.chain)).toBe(false);
});
it('replaces the chain with a valid chain', () => {
bc2.addBlock('goo');
bc.replaceChain(bc2.chain);
expect(bc.chain).toEqual(bc2.chain);
});
it('does not replace the chain with one of less than or equal to length', () => {
bc.addBlock('foo');
bc.replaceChain(bc2.chain);
expect(bc.chain).not.toEqual(bc2.chain);
})
});

296
blocks.json Normal file
View file

@ -0,0 +1,296 @@
Block -
Timestamp : 1581892713022
Last Hash : f1r57-h45h
Hash : 0000f64170
Nonce : 94889
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581892714610
Last Hash : 0000f64170
Hash : 000001941a
Nonce : 121452
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581892736795
Last Hash : 000004bdbe
Hash : 0000af3e65
Nonce : 1109949
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581892725766
Last Hash : 00001402a6
Hash : 000004bdbe
Nonce : 114910
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581892752249
Last Hash : 0000145513
Hash : 0000085224
Nonce : 484976
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581892762535
Last Hash : 0000085224
Hash : 00001cd286
Nonce : 995419
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581892763338
Last Hash : 00001cd286
Hash : 00000c8fa5
Nonce : 80136
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581892724628
Last Hash : 000001941a
Hash : 00001402a6
Nonce : 964391
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581892746837
Last Hash : 0000af3e65
Hash : 0005cb06ba
Nonce : 960133
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581892747191
Last Hash : 0005cb06ba
Hash : 0000145513
Nonce : 35175
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581894328935
Last Hash : f1r57-h45h
Hash : 0000027b9b
Nonce : 41326
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581894353562
Last Hash : 00084950c6
Hash : 0000cc1d6a
Nonce : 16931
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581894360160
Last Hash : 0000cc1d6a
Hash : 00000a55b4
Nonce : 681141
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581894370451
Last Hash : 00000a55b4
Hash : 000062a73f
Nonce : 1065366
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581894372244
Last Hash : 000062a73f
Hash : 000006be7e
Nonce : 182919
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581894383168
Last Hash : 000006be7e
Hash : 0000947649
Nonce : 1129947
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581894385832
Last Hash : 0000947649
Hash : 0000063c81
Nonce : 274322
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581894339035
Last Hash : 0000027b9b
Hash : 0007a1423c
Nonce : 995688
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581894343276
Last Hash : 0007a1423c
Hash : 00008d4e58
Nonce : 379871
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581894353389
Last Hash : 00008d4e58
Hash : 00084950c6
Nonce : 1043732
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581914042391
Last Hash : f1r57-h45h
Hash : 00005dbdc7
Nonce : 16289
Difficulty: 4
Data : [object Object],[object Object],[object Object]
Block -
Timestamp : 1581914052481
Last Hash : 00005dbdc7
Hash : 0002715612
Nonce : 945209
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581914052947
Last Hash : 0002715612
Hash : 00006f867b
Nonce : 44547
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581914073313
Last Hash : 0002826b45
Hash : 0000595eff
Nonce : 7524
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581914083348
Last Hash : 0000595eff
Hash : 000387395e
Nonce : 974338
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581914083523
Last Hash : 000387395e
Hash : 0000d7ada9
Nonce : 15912
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581914091333
Last Hash : 0000d7ada9
Hash : 00000ce8de
Nonce : 733896
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581914063218
Last Hash : 000873faba
Hash : 0000c9a5a8
Nonce : 23871
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581914073229
Last Hash : 0000c9a5a8
Hash : 0002826b45
Nonce : 1004347
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581914062966
Last Hash : 00006f867b
Hash : 000873faba
Nonce : 964936
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581914568417
Last Hash : f1r57-h45h
Hash : 0000646876
Nonce : 8365
Difficulty: 4
Data : [object Object],[object Object],[object Object]
Block -
Timestamp : 1581914578440
Last Hash : 0000646876
Hash : 00034edf74
Nonce : 948376
Difficulty: 3
Data : [object Object],
Block -
Timestamp : 1581914579386
Last Hash : 00034edf74
Hash : 00003f7371
Nonce : 88529
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581914847561
Last Hash : f1r57-h45h
Hash : 0000a45f66
Nonce : 58810
Difficulty: 4
Data : [object Object],[object Object]
Block -
Timestamp : 1581914850074
Last Hash : 0000a45f66
Hash : 0000053121
Nonce : 229566
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581914860093
Last Hash : 0000053121
Hash : 0000f0fa54
Nonce : 968591
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1581915028382
Last Hash : f1r57-h45h
Hash : 000012342d
Nonce : 74682
Difficulty: 4
Data : [object Object],[object Object]
Block -
Timestamp : 1581915029059
Last Hash : 000012342d
Hash : 000009fa64
Nonce : 59238
Difficulty: 5
Data : [object Object],
Block -
Timestamp : 1581915039087
Last Hash : 000009fa64
Hash : 0000219469
Nonce : 963908
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1631336762365
Last Hash : f1r57-h45h
Hash : 0000cb4fc4
Nonce : 87088
Difficulty: 4
Data : [object Object],
Mined Block
Block -
Timestamp : 1631336852594
Last Hash : f1r57-h45h
Hash : 0000fa68c7
Nonce : 31088
Difficulty: 4
Data : [object Object],
Block -
Timestamp : 1631337166316
Last Hash : f1r57-h45h
Hash : 0000b73e4b
Nonce : 31185
Difficulty: 4
Data : [object Object],
Mined Block

24
chain-util.js Normal file
View file

@ -0,0 +1,24 @@
const EC = require('elliptic').ec;
const SHA256 = require('crypto-js/sha256');
const uuidV1 = require('uuid/v1');
const ec = new EC('secp256k1');
class ChainUtil {
static genKeyPair() {
return ec.genKeyPair();
}
static id() {
return uuidV1();
}
static hash(data) {
return SHA256(JSON.stringify(data)).toString();
}
static verifySignature(publicKey, signature, dataHash) {
return ec.keyFromPublic(publicKey, 'hex').verify(dataHash, signature);
}
}
module.exports = ChainUtil;

8
config.js Normal file
View file

@ -0,0 +1,8 @@
const DIFFICULTY = 5;
const MINE_RATE = 20000;
const INITIAL_BALANCE = 500;
const MINING_REWARD = 50;
const MaxNumOfCoinTransactions = 3;
const MaxNumOfMetadataTransactions = 3;
module.exports = { DIFFICULTY, MINE_RATE, INITIAL_BALANCE, MINING_REWARD, MaxNumOfCoinTransactions, MaxNumOfMetadataTransactions} ;

3
dev-test.js Normal file
View file

@ -0,0 +1,3 @@
const Wallet = require('./wallet');
const wallet = new Wallet();
console.log(wallet.toString());

5
nodemon.json Normal file
View file

@ -0,0 +1,5 @@
{
"env": {
"MONGO_ATLAS_PW":"11112222-m"
}
}

13938
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

55
package.json Normal file
View file

@ -0,0 +1,55 @@
{
"name": "Sen-Sha-Mart",
"version": "6.0.0",
"description": "A novel Marketplace for sharing sensors in decentralized environment",
"main": "index.js",
"scripts": {
"test": "jest --watchAll",
"dev-test": "nodemon dev-test",
"start": "node ./app",
"dev": "nodemon ./app",
"sparql": "nodemon ./test/comunicaSPARQL",
"jsonld": "nodemon ./test/jsonld",
"sparqlEngine": "nodemon ./test/sparqlEngine",
"N3": "nodemon ./test/N3sudo yarn ",
"swagger-autogen": "node ./app/swagger.js"
},
"jest": {
"testEnvironment": "node"
},
"keywords": [],
"author": "AnasDawod",
"license": "ISC",
"devDependencies": {
"jest": "^22.1.4",
"nodemon": "^1.14.12",
"swagger-autogen": "^2.22.0"
},
"dependencies": {
"@comunica/actor-init-sparql": "^1.9.1",
"@comunica/actor-init-sparql-file": "^1.9.1",
"@comunica/actor-init-sparql-rdfjs": "^1.9.1",
"aedes": "^0.42.5",
"body-parser": "^1.18.2",
"crypto-js": "^3.1.9-1",
"elliptic": "^6.4.0",
"express": "^4.16.2",
"http": "0.0.0",
"jsonld": "^1.7.0",
"level": "^5.0.1",
"level-browserify": "^2.0.0",
"levelgraph": "^2.1.1",
"lodash": "^4.17.11",
"log4js": "^6.1.2",
"morgan": "^1.9.1",
"mosca": "^2.8.3",
"mqtt": "^4.1.0",
"multer": "^1.3.1",
"n3": "^1.2.0",
"sparql-engine": "^0.5.3",
"stream": "^0.0.2",
"swagger-ui-express": "^4.5.0",
"uuid": "^3.2.1",
"ws": "^4.0.0"
}
}

BIN
test/.DS_Store vendored Normal file

Binary file not shown.

49
test/N3/index.js Normal file
View file

@ -0,0 +1,49 @@
const N3 = require('n3');
const newEngine = require('@comunica/actor-init-sparql-rdfjs').newEngine;
const jsonld = require('jsonld');
const N3Store = require('n3').Store;
const DataFactory = require('n3').DataFactory;
const myEngine = newEngine();
const parser = new N3.Parser();
const store = new N3Store();
const doc = {
"http://schema.org/name": "Manu Sporny",
"http://schema.org/url": {"@id": "http://manu.sporny.org/"},
"http://schema.org/image": {"@id": "http://manu.sporny.org/images/manu.png"}
};
jsonld.toRDF(doc, {format: 'application/n-quads'}, (err, nquads) => {
// nquads is a string of N-Quads
//console.log(nquads);
parser.parse(
nquads,
(error, quad, prefixes) => {
if (quad)
//console.log(quad);
store.addQuad(quad);
else
console.log(store);
const start = async function (a,b){
const result = await myEngine.query('SELECT * { ?s ?p <http://manu.sporny.org/>. ?s ?p ?o} LIMIT 100',
{ sources: [ { value: store } ] });
//result.bindingsStream.on('data', (data) => {
// Each data object contains a mapping from variables to RDFJS terms.
console.log(result);
// console.log(data.get('?p'));
// console.log(data.get('?o'));
// });
};
start();
});
});

View file

@ -0,0 +1,68 @@
const newEngine = require('@comunica/actor-init-sparql').newEngine;
const N3 = require('n3');
const jsonld = require('jsonld')
const DataFactory = require('n3').DataFactory;
const parser = new N3.Parser({format: 'application/n-quads'});
const store = new N3.Store();
const myEngine = newEngine();
const doc = {
"http://schema.org/name": "Manu Sporny",
"http://schema.org/url": {"@id": "http://manu.sporny.org/"},
"http://schema.org/image": {"@id": "http://manu.sporny.org/images/manu.png"}
};
jsonld.toRDF(doc, {format: 'application/n-quads'}, (err, nquads) => {
// nquads is a string of N-Quads
// console.log(nquads);
var quad= [];
parser.parse(
nquads,
(error, quadN, prefixes) => {
// console.log(quadN)
if (quadN)
{
store.addQuad(DataFactory.quad(
DataFactory.namedNode(quadN.subject.id), DataFactory.namedNode(quadN.predicate.id), DataFactory.namedNode(quadN.object.id)));
}
else
console.log("finished");
// console.log(quadN)
});
const start = async function (a,b){
const result = await myEngine.query('SELECT * WHERE {?s ?p ?o } LIMIT 100',
{ sources: [{ type: 'rdfjsSource', value: store}] })
result.bindingsStream.on('data', (data) => console.log(data.toObject()));
};
start()
console.log(quad)
// store.addQuad(DataFactory.quad(
// DataFactory.namedNode(quad.subject.id), DataFactory.namedNode(quad.predicate.id), DataFactory.namedNode(quad.object.id)));
// store.addQuad(DataFactory.quad(
// DataFactory.namedNode('http://schema.org/image'), DataFactory.namedNode('http://manu.sporny.org/images/manu.png'), DataFactory.namedNode('http://schema.org/name')));
// store.addQuad(DataFactory.quad(
// DataFactory.namedNode('http://schema.org/url'), DataFactory.namedNode('http://manu.sporny.org/'), DataFactory.namedNode('http://dbpedia.org/resource/Ghent')));
//console.log(store)
// const start = async function (a,b){
// const result = await myEngine.query('SELECT * WHERE {?s ?p <http://manu.sporny.org/images/manu.png>. ?s ?p ?o } LIMIT 100',
// { sources: [{ type: 'rdfjsSource', value: store}] })
// result.bindingsStream.on('data', (data) => console.log(data.toObject()));
// };
// start()
});
// store.addQuad(DataFactory.quad(
// DataFactory.namedNode('http://schema.org/image'), DataFactory.namedNode('http://manu.sporny.org/images/manu.png'), DataFactory.namedNode('http://schema.org/name')));
// store.addQuad(DataFactory.quad(
// DataFactory.namedNode('http://schema.org/url'), DataFactory.namedNode('http://manu.sporny.org/'), DataFactory.namedNode('http://dbpedia.org/resource/Ghent')));
// // console.log(store)

20
test/jsonld/index.js Normal file
View file

@ -0,0 +1,20 @@
const jsonld = require('jsonld');
const doc = {
"http://schema.org/name": "Manu Sporny",
"http://schema.org/url": {"@id": "http://manu.sporny.org/"},
"http://schema.org/PrsnOwner": "0400985d4fca84fe0e8cff7e8902326a6703ba182cc8d6d8e20866b0acfc79ecb6bfd3d3b5d6ad7f48cd10fadc6d4348cab918f13db2ebb387ba16c57802bf47b1",
};
jsonld.toRDF(doc, {format: 'application/n-quads'}, (err, nquads) => {
// nquads is a string of N-Quads
console.log(nquads);
});
// const start = async function (a,b){
// const rdf = await jsonld.toRDF(doc, {format: 'application/n-quads'});
// console.log(rdf);
// };
// start();

View file

@ -0,0 +1,78 @@
'use strict'
const { BindingBase, HashMapDataset, Graph, PlanBuilder } = require('sparql-engine')
const level = require('level')
const levelgraph = require('levelgraph')
const { Transform } = require('stream')
// An utility class used to convert LevelGraph bindings
// into a format undestood by sparql-engine
class FormatterStream extends Transform {
constructor () {
super({objectMode: true})
}
_transform (item, encoding, callback) {
// Transform LevelGraph objects into set of mappings
// using BindingBase.fromObject
this.push(BindingBase.fromObject(item))
callback()
}
}
class LevelRDFGraph extends Graph {
constructor (db) {
super()
this._db = db
}
evalBGP (bgp) {
// rewrite variables using levelgraph API
bgp = bgp.map(t => {
if (t.subject.startsWith('?')) {
t.subject = this._db.v(t.subject.substring(1))
}
if (t.predicate.startsWith('?')) {
t.predicate = this._db.v(t.predicate.substring(1))
}
if (t.object.startsWith('?')) {
t.object = this._db.v(t.object.substring(1))
}
return t
})
// Transform the Stream returned by LevelGraph into an Stream of Bindings
return new FormatterStream(this._db.searchStream(bgp))
}
}
const db = levelgraph(level('testing_db'))
// insert some triples
var triple1 = { subject: 'http://example.org#a1', predicate: 'http://xmlns.com/foaf/0.1/name', object: '"c"' }
var triple2 = { subject: 'http://example.org#a2', predicate: 'http://xmlns.com/foaf/0.1/name', object: '"d"' }
db.put([triple1, triple2], () => {
const graph = new LevelRDFGraph(db)
const dataset = new HashMapDataset('http://example.org#default', graph)
const query = `
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name
WHERE {
?s foaf:name ?name .
}`
// Creates a plan builder for the RDF dataset
const builder = new PlanBuilder(dataset)
// Get an iterator to evaluate the query
const iterator = builder.build(query)
// Read results
iterator.subscribe(bindings => {
console.log('Find solutions:', bindings.toObject())
}, err => {
console.error('error', err)
}, () => {
console.log('Query evaluation complete!')
})
})

BIN
wallet/.DS_Store vendored Normal file

Binary file not shown.

67
wallet/CoinTransaction.js Normal file
View file

@ -0,0 +1,67 @@
const ChainUtil = require('../chain-util');
const { MINING_REWARD } = require('../config');
class CoinTransaction {
constructor() {
this.id = ChainUtil.id();
this.input = null;
this.outputs = [];
}
update(senderWallet, recipient, amount) {
const senderOutput = this.outputs.find(output => output.address === senderWallet.publicKey);
if (amount > senderOutput.amount) {
console.log(`Amount: ${amount} exceeds balance.`);
return;
}
senderOutput.amount = senderOutput.amount - amount;
this.outputs.push({ amount, address: recipient });
CoinTransaction.signCoinTransaction(this, senderWallet);
return this;
}
static CoinTransactionWithOutputs(senderWallet, outputs) {
const cointransaction = new this();
cointransaction.outputs.push(...outputs);
CoinTransaction.signCoinTransaction(cointransaction, senderWallet);
return cointransaction;
}
static newCoinTransaction(senderWallet, recipient, amount) {
if (amount > senderWallet.balance) {
console.log(`Amount: ${amount} exceeds balance.`);
return;
}
return CoinTransaction.CoinTransactionWithOutputs(senderWallet, [
{ amount: senderWallet.balance - amount, address: senderWallet.publicKey},
{ amount, address: recipient }]);
}
static rewardCoinTransaction(minerWallet, blockchainWallet) {
return CoinTransaction.CoinTransactionWithOutputs(blockchainWallet, [{
amount: MINING_REWARD, address: minerWallet.publicKey
}]);
}
static signCoinTransaction(cointransaction, senderWallet) {
cointransaction.input = {
timestamp: Date.now(),
amount: senderWallet.balance,
address: senderWallet.publicKey,
signature: senderWallet.sign(ChainUtil.hash(cointransaction.outputs))
}
}
static verifyCoinTransaction(cointransaction) {
return ChainUtil.verifySignature(
cointransaction.input.address,
cointransaction.input.signature,
ChainUtil.hash(cointransaction.outputs)
);
}
}
module.exports = CoinTransaction;

View file

@ -0,0 +1,100 @@
const ChainUtil = require('../chain-util');
class MetaDataTransaction {
constructor() {
this.id = null; // if there is a problem in the metadata transaction, change null with ChainUtil.id();
this.Signiture = null;
this.Name = null;
this.Geo = [];
this.IP_URL = null;
this.Topic_Token = null;
this.Permission = null;
this.RequestDetail = null;
this.OrgOwner = null;
this.DepOwner = null;
this.PrsnOwner = null;
this.MetaHash = null;
this.PaymentPerKbyte = null;
this.PaymentPerMinute = null;
this.Protocol = null;
this.MessageAttributes= {};
this.Interval = null;
this.FurtherDetails = null;
this.SSNmetadata = null;
// this.Geo = null;
// this.Std = null;
// this.name= null;
// this.MetaHash= null;
// this.file=null;
}
// update(senderWallet, Geo, URI, Name,Permission, OrgOwner, SSNmetadata) {
// this.Geo = Geo;
// this.URI = URI;
// this.Name = Name;
// this.Permission = Permission;
// this.OrgOwner = OrgOwner;
// this.PrsnOwner = senderWallet.publicKey;
// this.MetaHash = ChainUtil.hash(SSNmetadata);
// this.SSNmetadata = SSNmetadata;
// MetaDatatransaction.signMetaDataTransaction(this, senderWallet);
// return this;
// }
static MetaDataTransactionWithIoT(senderWallet, Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail, OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte, PaymentPerMinute, Protocol, MessageAttributes, Intrval, FurtherDetails, SSNmetadata) {
const metaDataTransaction = new this();
metaDataTransaction.id = ChainUtil.id();
metaDataTransaction.Name = Name;
metaDataTransaction.Geo = Geo;
metaDataTransaction.IP_URL = IP_URL;
metaDataTransaction.Topic_Token = Topic_Token;
metaDataTransaction.Permission = Permission;
metaDataTransaction.RequestDetail = RequestDetail
metaDataTransaction.OrgOwner = OrgOwner;
metaDataTransaction.DepOwner = DepOwner;
metaDataTransaction.PrsnOwner = PrsnOwner;
metaDataTransaction.PaymentPerKbyte = PaymentPerKbyte ;
metaDataTransaction.PaymentPerMinute = PaymentPerMinute;
metaDataTransaction.Protocol = Protocol;
metaDataTransaction.MessageAttributes = MessageAttributes;
metaDataTransaction.MessageAttributes['DeviceID'] = metaDataTransaction.id;
metaDataTransaction.MessageAttributes['DeviceName'] = Name;
metaDataTransaction.MessageAttributes['Sensors'] =[{"SensorName":"","Value":"" , "Unit":""}];
metaDataTransaction.MessageAttributes['TimeStamp'] = "";
metaDataTransaction.Interval = Intrval;
metaDataTransaction.FurtherDetails = FurtherDetails;
metaDataTransaction.SSNmetadata = SSNmetadata;
metaDataTransaction.MetaHash = ChainUtil.hash(SSNmetadata);
MetaDataTransaction.signMetaDataTransaction(metaDataTransaction, senderWallet);
return metaDataTransaction;
}
static newMetaDataTransaction(senderWallet,Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail, OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte, PaymentPerMinute, Protocol, MessageAttributes, Interval, FurtherDetails, SSNmetadata){
return MetaDataTransaction.MetaDataTransactionWithIoT(senderWallet, Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail, OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte, PaymentPerMinute, Protocol, MessageAttributes, Interval, FurtherDetails, SSNmetadata
);
}
static signMetaDataTransaction (metaDataTransaction, senderWallet) {
metaDataTransaction.Signiture = {
timestamp: Date.now(),
address: senderWallet.publicKey,
signature: senderWallet.sign(ChainUtil.hash(metaDataTransaction.SSNmetadata))
}
}
static verifyMetaDataTransaction(metaDataTransaction) {
return ChainUtil.verifySignature(
metaDataTransaction.Signiture.address,
metaDataTransaction.Signiture.signature,
ChainUtil.hash(metaDataTransaction.SSNmetadata)
);
}
}
module.exports = MetaDataTransaction;

99
wallet/index.js Normal file
View file

@ -0,0 +1,99 @@
const ChainUtil = require('../chain-util');
const CoinTransaction = require('./CoinTransaction');
const { INITIAL_BALANCE } = require('../config');
const MetaDataTransaction = require('./MetaDataTransaction');
const transactionPool = require('./transaction-pool');
class Wallet {
constructor() {
this.balance = INITIAL_BALANCE;
this.keyPair = ChainUtil.genKeyPair();
this.publicKey = this.keyPair.getPublic().encode('hex');
}
toString() {
return `Wallet -
publicKey: ${this.publicKey.toString()}
balance : ${this.balance}`
}
sign(dataHash) {
return this.keyPair.sign(dataHash);
}
createCoinTransaction(recipient, amount, blockchain, transactionPool) {
// this.balance = this.calculateBalance(blockchain);
if (amount > this.balance) {
console.log(`Amount: ${amount} exceceds current balance: ${this.balance}`);
return;
}
let cointransaction = transactionPool.existingPaymentTransaction(this.publicKey);
if (cointransaction) {
cointransaction.update(this, recipient, amount);
} else { //this should be the original one
//just for test i make the transaction not to update if the sender is the same
cointransaction = CoinTransaction.newCoinTransaction(this, recipient, amount);
transactionPool.updateOrAddPaymentTransaction(cointransaction);
}
return cointransaction;
}
createMetaDataTransaction(Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail, OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte, PaymentPerMinute, Protocol, MessageAttributes, Interval, FurtherDetails, SSNmetadata, transactionPool){
/* let metaData = metaDataPool.existingMetaData(this.publicKey);
if (metaData) {
metaData.update(this, Geo, Std, Name,MetaHash,file);
} else {*/
const metaDataTransaction= MetaDataTransaction.newMetaDataTransaction(this, Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail, OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte, PaymentPerMinute, Protocol, MessageAttributes,Interval, FurtherDetails, SSNmetadata);
transactionPool.updateOrAddMetaDataTransaction(metaDataTransaction);
//}
return metaDataTransaction;
}
calculateBalance(blockchain) {
let balance = this.balance;
let cointransactions = [];
blockchain.chain.forEach(block => block.data.forEach(cointransaction => {
cointransactions.push(cointransaction);
}));
const walletInputTs = cointransactions
.filter(cointransaction => cointransaction.input.address === this.publicKey);
let startTime = 0;
if (walletInputTs.length > 0) {
const recentInputT = walletInputTs.reduce(
(prev, current) => prev.input.timestamp > current.input.timestamp ? prev : current
);
balance = recentInputT.outputs.find(output => output.address === this.publicKey).amount;
startTime = recentInputT.input.timestamp;
}
cointransactions.forEach(cointransaction => {
if (cointransaction.input.timestamp > startTime) {
cointransaction.outputs.find(output => {
if (output.address === this.publicKey) {
balance += output.amount;
}
});
}
});
return balance;
}
static blockchainWallet() {
const blockchainWallet = new this();
blockchainWallet.address = 'blockchain-wallet';
return blockchainWallet;
}
}
module.exports = Wallet;

90
wallet/index.test.js Normal file
View file

@ -0,0 +1,90 @@
const Wallet = require('./index');
const TransactionPool = require('./transaction-pool');
const Blockchain = require('../blockchain');
const { INITIAL_BALANCE } = require('../config');
describe('Wallet', () => {
let wallet, tp, bc;
beforeEach(() => {
wallet = new Wallet();
tp = new TransactionPool();
bc = new Blockchain();
});
describe('creating a transaction', () => {
let transaction, sendAmount, recipient;
beforeEach(() => {
sendAmount = 50;
recipient = 'r4nd0m-4ddr355';
Geo = 20;
Std = 9014;
Name = 'temp';
MetaHash = '123abcd';
transaction = wallet.createCoinTransaction(recipient, sendAmount,Geo, Std, Name,MetaHash, bc, tp);
});
describe('and doing the same transaction', () => {
beforeEach(() => {
wallet.createCoinTransaction(recipient, sendAmount,Geo, Std, Name,MetaHash, bc, tp);
});
it('doubles the `sendAmount` subtracted from the wallet balance', () => {
expect(transaction.outputs.find(output => output.address === wallet.publicKey).amount)
.toEqual(wallet.balance - sendAmount * 2);
});
it('clones the `sendAmount` output for the recipient', () => {
expect(transaction.outputs.filter(output => output.address === recipient)
.map(output => output.amount)).toEqual([sendAmount, sendAmount]);
});
});
});
describe('calculating a balance', () => {
let addBalance, repeatAdd, senderWallet;
beforeEach(() => {
senderWallet = new Wallet();
addBalance = 100;
repeatAdd = 3;
for (let i=0; i<repeatAdd; i++) {
senderWallet.createCoinTransaction(wallet.publicKey, addBalance,Geo, Std, Name,MetaHash, bc, tp);
}
bc.addBlock(tp.paymenttransactions);
});
it('calculates the balance for blockchain transactions matching the recipient', () => {
expect(wallet.calculateBalance(bc)).toEqual(INITIAL_BALANCE + (addBalance * repeatAdd));
});
it('calculates the balance for blockchain transactions matching the sender', () => {
expect(senderWallet.calculateBalance(bc)).toEqual(INITIAL_BALANCE - (addBalance * repeatAdd));
});
describe('and the recipient conducts a transaction', () => {
let subtractBalance, recipientBalance;
beforeEach(() => {
tp.clear();
subtractBalance = 60;
recipientBalance = wallet.calculateBalance(bc);
wallet.createCoinTransaction(senderWallet.publicKey, subtractBalance,20,9014,'temp','123abc', bc, tp);
bc.addBlock(tp.paymenttransactions);
});
describe('and the sender sends another transaction to the recipient', () => {
beforeEach(() => {
tp.clear();
senderWallet.createCoinTransaction(wallet.publicKey, addBalance, 20,9014,'temp','123abc',bc, tp);
bc.addBlock(tp.paymenttransactions);
});
it('calculate the recipient balance only using transactions since its most recent one', () => {
expect(wallet.calculateBalance(bc)).toEqual(recipientBalance - subtractBalance + addBalance);
});
});
});
});
});

118
wallet/transaction-pool.js Normal file
View file

@ -0,0 +1,118 @@
const PaymntTransaction = require('./CoinTransaction');
const MetaDataTransaction = require('./MetaDataTransaction');
//const CompTransaction = require('./CompTransaction');
//const IntegrationTransaction = require('./IntegrationTransaction');
const { MaxNumOfPaymentTransactions, MaxNumOfMetadataTransactions,
MaxNumOfCompTransactions, MaxNumOfIntegrationTransactions}
= require('../config');
class TransactionPool {
constructor() {
this.paymenttransactions = [];
this.metaDataTransactions =[];
this.comptransactions = [];
this.integrationTransactions =[];
}
updateOrAddPaymentTransaction(paymenttransaction) {
let paymenttransactionWithId = this.paymenttransactions.find(t =>
t.id === paymenttransaction.id);
if (paymenttransactionWithId) {
this.paymenttransactions[this.paymenttransactions.indexOf
(paymenttransactionWithId)] = paymenttransaction;
} else {
this.paymenttransactions.push(paymenttransaction);
}
}
updateOrAddMetaDataTransaction(metaDataTransaction) {
let metaDataTransactionWithId = this.metaDataTransactions.find(t =>
t.id === metaDataTransaction.id);
if (metaDataTransactionWithId) {
this.metaDataTransactions[this.metaDataTransactions.indexOf
(metaDataTransactionWithId)] = metaDataTransaction;
} else {
this.metaDataTransactions.push(metaDataTransaction);
}
}
updateOrAddCompTransaction(comptransaction) {
let comptransactionWithId = this.comptransactions.find(t =>
t.id === comptransaction.id);
if (comptransactionWithId) {
this.comptransactions[this.comptransactions.indexOf
(comptransactionWithId)] = comptransaction;
} else {
this.comptransactions.push(comptransaction);
} }
updateOrAddIntegrationTransaction(integrationTransaction) {
let integrationTransactionWithId = this.integrationTransaction.find(
t => t.id === integrationTransaction.id);
if (integrationTransactionWithId) {
this.integrationTransactions[this.integrationTransactions.indexOf
(integrationTransactionWithId)] = integrationTransaction;
} else {
this.integrationTransactions.push(integrationTransaction);
}
}
existingPaymentTransaction(address) {
return this.paymenttransactions.find(t =>
t.input.address === address); }
existingMetaDataTransaction(address) {
return this.metaDataTransactions.find(t =>
t.Signiture.address === address);}
existingCompTransaction(address) {
return this.comptransactions.find(t =>
t.input.address === address); }
existingIntegrationTransaction(address) {
return this.integrationTransactions.find(t =>
t.Signiture.address === address);}
validPaymentTransactions() {
return this.paymenttransactions.filter(paymenttransaction => {
const outputTotal = paymenttransaction.outputs.reduce(
(total, output) => {
return total + output.amount;
}, 0);
if (paymenttransaction.input.amount !== outputTotal) {
console.log(`Invalid transaction from
${paymenttransaction.input.address}.`);
return;}
if (!PaymentTransaction.verifyPaymentTransaction(
paymenttransaction)) {
console.log(`Invalid signature from
${paymenttransaction.input.address}.`);
return;}
return paymenttransaction;
});
}
validMetaDataTransactions(){
if (!MetaDataTransaction.verifyMetaDataTransaction(
metaDataTransaction)) {
console.log(`Invalid signature from
${metaDataTransaction.Signiture.address}.`);
return;
}
return metaDataTransaction;
}
validCompTransactions(){
if (!CompTransaction.verifyCompTransaction(
CompTransaction)) {
console.log(`Invalid signature from
${CompTransaction.Signiture.address}.`);
return;
}
return compTransaction;
}
validIntegrationTransactions(){
if (!IntegrationTransaction.verifyIntegrationTransaction(
integrationTransaction)) {
console.log(`Invalid signature from
${integrationTransaction.Signiture.address}.`);
return;
}
return integrationTransaction;
}
clearAll() {
this.cointransactions = [];
this.metaDataTransactions = [];
this.comptransactions = [];
this.integrationTransactions = [];
}
}
module.exports = TransactionPool;

View file

@ -0,0 +1,58 @@
const TransactionPool = require('./transaction-pool');
const Transaction = require('./Cointransaction');
const Wallet = require('./index');
const Blockchain = require('../blockchain');
describe('TransactionPool', () => {
let tp, wallet, transaction, bc;
beforeEach(() => {
tp = new TransactionPool();
wallet = new Wallet();
bc = new Blockchain();
transaction = wallet.createCoinTransaction('r4nd-4dr355', 30,20,9014,'temp','123abc', bc, tp);
});
it('adds a transaction to the pool', () => {
expect(tp.paymenttransactions.find(t => t.id === transaction.id)).toEqual(transaction);
});
it('updates a transaction in the pool', () => {
const oldTransaction = JSON.stringify(transaction);
const newTransaction = transaction.update(wallet, 'foo-4ddr355', 40,20,9014,'temp','123abc');
tp.updateOrAddPaymentTransaction(newTransaction);
expect(JSON.stringify(tp.paymenttransactions.find(t => t.id === newTransaction.id)))
.not.toEqual(oldTransaction);
});
it('clears transactions', () => {
tp.clear();
expect(tp.paymenttransactions).toEqual([]);
});
describe('mixing valid and corrupt transactions', () => {
let validTransactions;
beforeEach(() => {
validTransactions = [...tp.paymenttransactions];
for (let i=0; i<6; i++) {
wallet = new Wallet();
transaction = wallet.createCoinTransaction('r4nd-4dr355', 30,20,9014,'temp','123abc', bc, tp);
if (i%2==0) {
transaction.input.amount = 99999;
} else {
validTransactions.push(transaction);
}
}
});
it('shows a difference between valid and corrupt transactions', () => {
expect(JSON.stringify(tp.paymenttransactions)).not.toEqual(JSON.stringify(validTransactions));
});
it('grabs valid transactions', () => {
expect(tp.validTransactions()).toEqual(validTransactions);
});
});
});

View file

@ -0,0 +1,79 @@
const Transaction = require('./Cointransaction');
const Wallet = require('./index');
const { MINING_REWARD } = require('../config');
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);
});
});
});