smarter store update after replacing chains
concurrent mining validity checks for new transactions/blocks/metadata rdf store stores quads smarter clearing of transactions from pool when a new block is mined change querying to be readonly and generic moved some things around
This commit is contained in:
parent
ea81105df6
commit
b4c2a0c88d
7 changed files with 373 additions and 177 deletions
146
app/index.js
146
app/index.js
|
@ -33,8 +33,8 @@ const Blockchain = require('../blockchain');
|
||||||
const P2pServer = require('./p2p-server');
|
const P2pServer = require('./p2p-server');
|
||||||
const Wallet = require('../wallet');
|
const Wallet = require('../wallet');
|
||||||
const TransactionPool = require('../wallet/transaction-pool');
|
const TransactionPool = require('../wallet/transaction-pool');
|
||||||
const Miner = require('./miner');
|
|
||||||
const QueryEngine = require('@comunica/query-sparql').QueryEngine;
|
const QueryEngine = require('@comunica/query-sparql').QueryEngine;
|
||||||
|
const ChainUtil = require('../chain-util');
|
||||||
|
|
||||||
const N3 = require('n3');
|
const N3 = require('n3');
|
||||||
const jsonld = require('jsonld');
|
const jsonld = require('jsonld');
|
||||||
|
@ -51,10 +51,10 @@ const multer = require('multer');/* Multer is a node.js middleware for
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const bc = new Blockchain();
|
const bc = new Blockchain();
|
||||||
const wallet = new Wallet();
|
//currently gen a new keypair per run, we probably want to load this from something else in the future
|
||||||
|
const wallet = new Wallet(ChainUtil.genKeyPair());
|
||||||
const tp = new TransactionPool();
|
const tp = new TransactionPool();
|
||||||
const p2pServer = new P2pServer(bc, tp,'./persist_block_chain.json');
|
const p2pServer = new P2pServer(bc, tp, wallet, './persist_block_chain.json');
|
||||||
const miner = new Miner(bc, tp, wallet, p2pServer);
|
|
||||||
|
|
||||||
const parser = new N3.Parser(); //({format: 'application/n-quads'});
|
const parser = new N3.Parser(); //({format: 'application/n-quads'});
|
||||||
const myEngine = new QueryEngine();
|
const myEngine = new QueryEngine();
|
||||||
|
@ -119,12 +119,12 @@ app.get('/Transactions', (req, res) => {
|
||||||
res.json(tp);
|
res.json(tp);
|
||||||
});
|
});
|
||||||
///////////////
|
///////////////
|
||||||
app.get('/mine-transactions', (req, res) => {
|
//app.get('/mine-transactions', (req, res) => {
|
||||||
const block = miner.mine();
|
// const block = miner.mine();
|
||||||
console.log(`New block added: ${block.toString()}`);
|
// console.log(`New block added: ${block.toString()}`);
|
||||||
res.redirect('/blocks');
|
// res.redirect('/blocks');
|
||||||
// res.json("Block mined");
|
// // res.json("Block mined");
|
||||||
});
|
//});
|
||||||
///////////////
|
///////////////
|
||||||
app.get('/public-key', (req, res) => {
|
app.get('/public-key', (req, res) => {
|
||||||
res.json({ publicKey: wallet.publicKey });
|
res.json({ publicKey: wallet.publicKey });
|
||||||
|
@ -137,60 +137,66 @@ app.get('/Balance', (req, res) => {
|
||||||
///////////////
|
///////////////
|
||||||
//this API prints all the quads stored in the RDF store and returns the entire store
|
//this API prints all the quads stored in the RDF store and returns the entire store
|
||||||
app.get('/quads', (req, res) => {
|
app.get('/quads', (req, res) => {
|
||||||
for (const quad of store)
|
//for (const quad of store)
|
||||||
console.log(quad);
|
//console.log(quad);
|
||||||
res.json(store);
|
res.json(store);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/IoTdeviceRegistration', (req, res)=> {
|
app.get('/IoTdeviceRegistration', (req, res)=> {
|
||||||
fs.readdir('./uploads', function(err, files) {
|
fs.readdir('./uploads', function(err, files) {
|
||||||
console.log(files[files.length-2]);
|
//console.log(files[files.length-2]);
|
||||||
var FileName = files[files.length-2];
|
var FileName = files[files.length-2];
|
||||||
let rawdata = fs.readFileSync(`./uploads/${FileName}`);
|
let rawdata = fs.readFileSync(`./uploads/${FileName}`);
|
||||||
let SenShaMartDesc = JSON.parse(rawdata);
|
let SenShaMartDesc = JSON.parse(rawdata);
|
||||||
/* the following piece of code is used to genrate JSON object out of name-value pairs submitted
|
/* the following piece of code is used to genrate JSON object out of name-value pairs submitted
|
||||||
let SenShaMartExtNames = ['Name','Geo' ,'IP_URL' , 'Topic_Token', 'Permission', 'RequestDetail',
|
let SenShaMartExtNames = ['Name','Geo' ,'IP_URL' , 'Topic_Token', 'Permission', 'RequestDetail',
|
||||||
'OrgOwner', 'DepOwner','PrsnOwner', 'PaymentPerKbyte',
|
'OrgOwner', 'DepOwner','PrsnOwner', 'PaymentPerKbyte',
|
||||||
'PaymentPerMinute','Protocol', 'MessageAttributes', 'Interval',
|
'PaymentPerMinute','Protocol', 'MessageAttributes', 'Interval',
|
||||||
'FurtherDetails']
|
'FurtherDetails']
|
||||||
let SenShaMartExtValues = [Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail,
|
let SenShaMartExtValues = [Name,Geo ,IP_URL , Topic_Token, Permission, RequestDetail,
|
||||||
OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte,
|
OrgOwner, DepOwner,PrsnOwner, PaymentPerKbyte,
|
||||||
PaymentPerMinute,Protocol, MessageAttributes, Interval,
|
PaymentPerMinute,Protocol, MessageAttributes, Interval,
|
||||||
FurtherDetails]
|
FurtherDetails]
|
||||||
let SenSHaMArtExt = {};
|
let SenSHaMArtExt = {};
|
||||||
for (let i =0; i <SenShaMartExtNames.length; i++){
|
for (let i =0; i <SenShaMartExtNames.length; i++){
|
||||||
SenSHaMArtExt[`${SenShaMartExtNames[i]}`]= SenShaMartExtValues[i]
|
SenSHaMArtExt[`${SenShaMartExtNames[i]}`]= SenShaMartExtValues[i]
|
||||||
|
|
||||||
}
|
}
|
||||||
//let SenShaMartOnt = SSNmetadata;
|
//let SenShaMartOnt = SSNmetadata;
|
||||||
//SenShaMartOnt.push(SenSHaMArtExt); */
|
//SenShaMartOnt.push(SenSHaMArtExt); */
|
||||||
console.log(SenShaMartDesc)
|
//console.log(SenShaMartDesc);
|
||||||
jsonld.toRDF(SenShaMartDesc, {format: 'application/n-quads'},
|
jsonld.toRDF(SenShaMartDesc, {format: 'application/n-quads'},
|
||||||
(err, nquads) => {
|
(err, nquads) => {
|
||||||
console.log(nquads)
|
//console.log(nquads)
|
||||||
var metaDataTransaction = wallet.createMetadata(
|
var metadata = wallet.createMetadata(
|
||||||
nquads, tp);
|
nquads, tp);
|
||||||
|
p2pServer.newMetadata(metadata);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
res.json("MetadataTransactionCreated");
|
||||||
});
|
});
|
||||||
res.json("MetadataTransactionCreated");
|
|
||||||
});
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////
|
//////////////////////////////////////////////////
|
||||||
// POST APIs
|
// POST APIs
|
||||||
app.post('/mine', (req, res) => {
|
//this doesn't work well with the continious miner
|
||||||
const block = bc.addBlock(req.body.data);
|
//app.post('/mine', (req, res) => {
|
||||||
console.log(`New block added: ${block.toString()}`);
|
// const block = bc.addBlock(req.body.data);
|
||||||
|
// console.log(`New block added: ${block.toString()}`);
|
||||||
|
|
||||||
p2pServer.newBlock(block);
|
// p2pServer.newBlock(block);
|
||||||
|
|
||||||
res.redirect('/blocks');
|
// res.redirect('/blocks');
|
||||||
});
|
//});
|
||||||
///////////////
|
///////////////
|
||||||
app.post('/PaymentTransaction', (req, res) => {
|
app.post('/PaymentTransaction', (req, res) => {
|
||||||
const { recipient, amount } = req.body;
|
const { recipient, amount } = req.body;
|
||||||
const transaction = wallet.createTransaction(recipient, amount, bc, tp);
|
const transaction = wallet.createTransaction(recipient, amount, bc, tp);
|
||||||
//p2pServer.broadcastTransaction(transaction);
|
if (transaction === null) {
|
||||||
|
res.json("Couldn't create transaction");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p2pServer.newTransaction(transaction);
|
||||||
res.redirect('/transactions');
|
res.redirect('/transactions');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -198,11 +204,12 @@ app.post('/PaymentTransaction', (req, res) => {
|
||||||
app.post('/IoTdevicePaymentTransaction', (req, res) => {
|
app.post('/IoTdevicePaymentTransaction', (req, res) => {
|
||||||
const { Recipient_payment_address, Amount_of_money, Payment_method,
|
const { Recipient_payment_address, Amount_of_money, Payment_method,
|
||||||
Further_details} = req.body;
|
Further_details} = req.body;
|
||||||
if (Payment_method == "SensorCoin"){
|
if (Payment_method == "SensorCoin") {
|
||||||
const PaymentTransaction = wallet.createCoinTransaction(
|
//create coin transaction doesn't exist yet
|
||||||
Recipient_payment_address, Amount_of_money, bc, tp);
|
const PaymentTransaction = wallet.createCoinTransaction(
|
||||||
p2pServer.broadcastCoinTransaction(PaymentTransaction);
|
Recipient_payment_address, Amount_of_money, bc, tp);
|
||||||
res.json("PaymentTransactionCreated");
|
p2pServer.broadcastCoinTransaction(PaymentTransaction);
|
||||||
|
res.json("PaymentTransactionCreated");
|
||||||
}
|
}
|
||||||
else if (Payment_method == "Bitcoin") {
|
else if (Payment_method == "Bitcoin") {
|
||||||
res.redirect('/BitcoinTransaction')
|
res.redirect('/BitcoinTransaction')
|
||||||
|
@ -233,21 +240,30 @@ app.post("/UploadMetafile", upload.single('file'), (req, res) => {
|
||||||
//Start of comunica sparql query code
|
//Start of comunica sparql query code
|
||||||
app.post('/sparql', (req, res) => {
|
app.post('/sparql', (req, res) => {
|
||||||
console.log(req.body);
|
console.log(req.body);
|
||||||
const {Select,subject,predicate,object,Limit}= req.body;
|
const start = async function () {
|
||||||
const start = async function (a,b){
|
let result = [];
|
||||||
const bindingsStream = await myEngine.queryBindings(`SELECT ${Select} WHERE
|
const bindingsStream = await myEngine.queryBindings(
|
||||||
{${subject} ${predicate} ${object}} LIMIT
|
req.body,
|
||||||
${Limit}`, { sources: [{ type: 'rdfjsSource',
|
{
|
||||||
value: p2pServer.store}]
|
readOnly: true,
|
||||||
});
|
sources: [{
|
||||||
bindingsStream.on('data', (binding) => {
|
type: 'rdfjsSource',
|
||||||
console.log(binding.toString());
|
value: p2pServer.store
|
||||||
queryResult= binding;
|
}]
|
||||||
});
|
});
|
||||||
};
|
bindingsStream.on('data', (binding) => {
|
||||||
start()
|
console.log(binding.toString());
|
||||||
res.json("Query succsessful");
|
result.push(binding);
|
||||||
});
|
});
|
||||||
|
bindingsStream.on('end', () => {
|
||||||
|
res.json(JSON.stringify(result));
|
||||||
|
});
|
||||||
|
bindingsStream.on('error', (err) => {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
start()
|
||||||
|
});
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////Integration///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////Integration///////////////////////////////////////////////////////////
|
||||||
DistributedBrokers = ["mqtt.eclipse.org", "test.mosquitto.org","broker.hivemq.com"];
|
DistributedBrokers = ["mqtt.eclipse.org", "test.mosquitto.org","broker.hivemq.com"];
|
||||||
|
|
84
app/miner.js
84
app/miner.js
|
@ -1,34 +1,88 @@
|
||||||
const Wallet = require('../wallet');
|
const Wallet = require('../wallet');
|
||||||
const Transaction = require('../wallet/transaction');
|
const Transaction = require('../wallet/transaction');
|
||||||
|
const Block = require('../blockchain/block');
|
||||||
|
|
||||||
|
|
||||||
class Miner {
|
class Miner {
|
||||||
|
static STATE_WAITING = 0;
|
||||||
|
static STATE_RUNNING = 1;
|
||||||
|
static STATE_INTERRUPTED = 2;
|
||||||
|
static STATE_RESTARTING = 3;
|
||||||
|
|
||||||
constructor(blockchain, transactionPool, wallet, p2pServer) {
|
constructor(blockchain, transactionPool, wallet, p2pServer) {
|
||||||
this.blockchain = blockchain;
|
this.blockchain = blockchain;
|
||||||
this.transactionPool = transactionPool;
|
this.transactionPool = transactionPool;
|
||||||
this.wallet = wallet;
|
this.wallet = wallet;
|
||||||
this.p2pServer = p2pServer;
|
this.p2pServer = p2pServer;
|
||||||
}
|
this.state = Miner.STATE_WAITING;
|
||||||
|
this.mining = [[], []];
|
||||||
|
this.lastBlock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interrupt() {
|
||||||
|
if (this.state === Miner.STATE_RUNNING) {
|
||||||
|
this.state = Miner.STATE_INTERRUPTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interruptIfContainsTransaction(transaction) {
|
||||||
|
if (this.state === Miner.STATE_RUNNING && this.mining[0].find(t => t.id === transaction.id)) {
|
||||||
|
this.state = Miner.STATE_INTERRUPTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interruptIfContainsMetadata(metadata) {
|
||||||
|
if (this.state === Miner.STATE_RUNNING && this.mining[1].find(t => t.id === metadata.id)) {
|
||||||
|
this.state = Miner.STATE_INTERRUPTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startMine() {
|
||||||
|
//only continue if state is waiting or restarting
|
||||||
|
if (this.state !== Miner.STATE_WAITING && this.state !== Miner.STATE_RESTARTING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mine() {
|
|
||||||
const validTransactions = this.transactionPool.validTransactions();
|
const validTransactions = this.transactionPool.validTransactions();
|
||||||
|
const validMetadataS = this.transactionPool.validMetadataS();
|
||||||
|
|
||||||
|
if (validTransactions.length === 0 && validMetadataS.length === 0) {
|
||||||
|
this.state = Miner.STATE_WAITING;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
validTransactions.push(
|
validTransactions.push(
|
||||||
Transaction.rewardTransaction(this.wallet, Wallet.blockchainWallet())
|
Transaction.rewardTransaction(this.wallet, Wallet.blockchainWallet())
|
||||||
);
|
);
|
||||||
console.log(validTransactions);
|
|
||||||
console.log("//////");
|
|
||||||
const validMetadataS = this.transactionPool.validMetadataS();
|
|
||||||
// for (let i =0; i <validMetadataS.length; i++){
|
|
||||||
// validTransactions.push(validMetadataS[i]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
console.log(validTransactions);
|
this.lastBlock = this.blockchain.chain[this.blockchain.chain.length - 1];
|
||||||
// const validMetadataS = this.transactionPool.metadataS;
|
|
||||||
const block = this.blockchain.addBlock([validTransactions, validMetadataS]);
|
this.state = Miner.STATE_RUNNING;
|
||||||
this.p2pServer.newBlock(block);
|
|
||||||
this.transactionPool.clear();
|
this.mining = [validTransactions, validMetadataS];
|
||||||
|
this.nonce = 0;
|
||||||
return block;
|
this.mine();
|
||||||
|
}
|
||||||
|
|
||||||
|
mine() {
|
||||||
|
if (this.state !== Miner.STATE_RUNNING) {
|
||||||
|
this.state = Miner.STATE_RESTARTING;
|
||||||
|
startMine();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const difficulty = Block.adjustDifficulty(this.lastBlock, timestamp);
|
||||||
|
const hash = Block.hash(timestamp, this.lastBlock.hash, this.mining, this.nonce, difficulty);
|
||||||
|
|
||||||
|
if (hash.substring(0, difficulty) === '0'.repeat(difficulty)) {
|
||||||
|
//success
|
||||||
|
this.p2pServer.newBlock(new Block(timestamp, this.lastBlock.hash, hash, this.mining, this.nonce, difficulty));
|
||||||
|
this.state = Miner.STATE_RESTARTING;
|
||||||
|
setImmediate(() => { this.startMine() });
|
||||||
|
} else {
|
||||||
|
//failure
|
||||||
|
this.nonce++;
|
||||||
|
setImmediate(() => { this.mine() });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
const Websocket = require('ws');
|
const Websocket = require('ws');
|
||||||
const N3 = require('n3');
|
const N3 = require('n3');
|
||||||
const parser = new N3.Parser(); //({format: 'application/n-quads'});
|
|
||||||
const DataFactory = require('n3').DataFactory;
|
const DataFactory = require('n3').DataFactory;
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
|
const Miner = require('./miner');
|
||||||
|
const Transaction = require('../wallet/transaction');
|
||||||
|
const TransactionPool = require('../wallet/transaction-pool');
|
||||||
|
const Metadata = require('../wallet/metadata');
|
||||||
|
|
||||||
const P2P_PORT = process.env.P2P_PORT || 5000;
|
const P2P_PORT = process.env.P2P_PORT || 5000;
|
||||||
const peers = process.env.PEERS ? process.env.PEERS.split(',') : [];
|
const peers = process.env.PEERS ? process.env.PEERS.split(',') : [];
|
||||||
|
@ -15,12 +18,13 @@ const MESSAGE_TYPES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class P2pServer {
|
class P2pServer {
|
||||||
constructor(blockchain, transactionPool,chainStorageLocation) {
|
constructor(blockchain, transactionPool, wallet, chainStorageLocation) {
|
||||||
this.blockchain = blockchain;
|
this.blockchain = blockchain;
|
||||||
this.transactionPool = transactionPool;
|
this.transactionPool = transactionPool;
|
||||||
this.sockets = [];
|
this.sockets = [];
|
||||||
this.store = new N3.Store();
|
this.store = new N3.Store();
|
||||||
this.chainStorageLocation = chainStorageLocation;
|
this.chainStorageLocation = chainStorageLocation;
|
||||||
|
this.miner = new Miner(this.blockchain, this.transactionPool, wallet, this);
|
||||||
|
|
||||||
//possible race if deleted after check, but we live with it I guess
|
//possible race if deleted after check, but we live with it I guess
|
||||||
if (fs.existsSync(this.chainStorageLocation)) {
|
if (fs.existsSync(this.chainStorageLocation)) {
|
||||||
|
@ -63,39 +67,93 @@ class P2pServer {
|
||||||
const data = JSON.parse(message);
|
const data = JSON.parse(message);
|
||||||
switch(data.type) {
|
switch(data.type) {
|
||||||
case MESSAGE_TYPES.chain:
|
case MESSAGE_TYPES.chain:
|
||||||
newChain(data.chain);
|
this.newChain(data.chain);
|
||||||
break;
|
break;
|
||||||
case MESSAGE_TYPES.transaction:
|
case MESSAGE_TYPES.transaction:
|
||||||
this.transactionPool.updateOrAddTransaction(data.transaction);
|
this.newTransaction(data.transaction, false);
|
||||||
break;
|
break;
|
||||||
case MESSAGE_TYPES.metadata:
|
case MESSAGE_TYPES.metadata:
|
||||||
this.transactionPool.updateOrAddMetadata(data.metadata);
|
this.newMetadata(data.metadata, false);
|
||||||
break;
|
|
||||||
case MESSAGE_TYPES.clear_transactions:
|
|
||||||
this.transactionPool.clear();
|
|
||||||
break;
|
break;
|
||||||
|
//case MESSAGE_TYPES.clear_transactions:
|
||||||
|
// this.transactionPool.clear();
|
||||||
|
// break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
newBlock(block) {
|
newMetadata(metadata, broadcast) {
|
||||||
this.onNewBlock(block.data);
|
if (!Metadata.verifyMetadata(metadata)) {
|
||||||
this.syncChains();
|
console.log("Couldn't add metadata to p2pServer, couldn't verify");
|
||||||
this.persistChain(this.blockchain.chain);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.transactionPool.updateOrAddMetadata(metadata)) {
|
||||||
|
case TransactionPool.Return.add:
|
||||||
|
this.miner.startMine();
|
||||||
|
break;
|
||||||
|
case TransactionPool.Return.update:
|
||||||
|
this.miner.interruptIfContainsMetadata(metadata);
|
||||||
|
break;
|
||||||
|
case TransactionPool.Return.error:
|
||||||
|
console.log("Couldn't add metadata to p2pServer, couldn't updateOrAdd");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (broadcast === undefined || broadcast) {
|
||||||
|
this.broadcastMetadata(metadata);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newChain(chain,persist) {
|
newTransaction(transaction, broadcast) {
|
||||||
if (!this.blockchain.replaceChain(chain)) {
|
if (!Transaction.verifyTransaction(transaction)) {
|
||||||
|
console.log("Couldn't add transaction to p2pServer, couldn't verify");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this.transactionPool.updateOrAddTransaction(transaction)) {
|
||||||
|
case TransactionPool.Return.add:
|
||||||
|
this.miner.startMine();
|
||||||
|
break;
|
||||||
|
case TransactionPool.Return.update:
|
||||||
|
this.miner.interruptIfContainsTransaction(transaction);
|
||||||
|
break;
|
||||||
|
case TransactionPool.Return.error:
|
||||||
|
console.log("Couldn't add transaction to p2pServer, couldn't updateOrAdd");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (broadcast === undefined || broadcast) {
|
||||||
|
this.broadcastTransaction(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newBlock(block) {
|
||||||
|
if (!this.blockchain.addBlock(block)) {
|
||||||
|
//invalid block, return
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onNewBlock(block);
|
||||||
|
this.persistChain(this.blockchain.chain);
|
||||||
|
this.syncChains();
|
||||||
|
}
|
||||||
|
|
||||||
|
newChain(chain, persist) {
|
||||||
|
const oldChain = this.blockchain.chain;
|
||||||
|
const divergence = this.blockchain.replaceChain(chain);
|
||||||
|
|
||||||
|
if (divergence === null) {
|
||||||
//failed to replace
|
//failed to replace
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof persist === "undefined" || persist) {
|
if (typeof persist === "undefined" || persist) {
|
||||||
this.persistChain(chain);
|
this.persistChain(chain);
|
||||||
}
|
}
|
||||||
//dirty clear
|
for (let i = divergence; i < oldChain.length; i++) {
|
||||||
this.store = new N3.Store();
|
this.store.deleteGraph(oldChain[i].hash);
|
||||||
for (var block in this.blockchain.chain) {
|
}
|
||||||
this.onNewBlock(block);
|
for (let i = divergence; i < this.blockchain.chain.length; i++) {
|
||||||
|
this.onNewBlock(this.blockchain.chain[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,28 +170,36 @@ class P2pServer {
|
||||||
|
|
||||||
onNewBlock(block) {
|
onNewBlock(block) {
|
||||||
//block data is of form [transactions,metadatas]
|
//block data is of form [transactions,metadatas]
|
||||||
if (block.length != 2) {
|
if (block.data.length != 2) {
|
||||||
//assert?
|
//assert?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const metadatas = block[1];
|
|
||||||
|
|
||||||
for (var metadata in metadatas) {
|
this.transactionPool.clearFromBlock(block);
|
||||||
if (!(SSNmetadata in metadata)) {
|
|
||||||
|
this.miner.interrupt();
|
||||||
|
|
||||||
|
const metadatas = block.data[1];
|
||||||
|
|
||||||
|
for (const metadata of metadatas) {
|
||||||
|
if (!("SSNmetadata" in metadata)) {
|
||||||
//assert?
|
//assert?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ssn = metadata.SSNmetadata;
|
var ssn = metadata.SSNmetadata;
|
||||||
|
|
||||||
|
const parser = new N3.Parser();
|
||||||
|
|
||||||
parser.parse(
|
parser.parse(
|
||||||
ssn,
|
ssn,
|
||||||
(error, quadN, prefixes) => {
|
(error, quadN, prefixes) => {
|
||||||
if (quadN) {
|
if (quadN) {
|
||||||
store.addQuad(DataFactory.quad(
|
this.store.addQuad(DataFactory.quad(
|
||||||
DataFactory.namedNode(quadN.subject.id),
|
DataFactory.namedNode(quadN.subject.id),
|
||||||
DataFactory.namedNode(quadN.predicate.id),
|
DataFactory.namedNode(quadN.predicate.id),
|
||||||
DataFactory.namedNode(quadN.object.id)));
|
DataFactory.namedNode(quadN.object.id),
|
||||||
|
DataFactory.namedNode(block.hash)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -163,13 +229,13 @@ class P2pServer {
|
||||||
this.sockets.forEach(socket => this.sendChain(socket));
|
this.sockets.forEach(socket => this.sendChain(socket));
|
||||||
}
|
}
|
||||||
|
|
||||||
//broadcastTransaction(transaction) {
|
broadcastTransaction(transaction) {
|
||||||
// this.sockets.forEach(socket => this.sendTransaction(socket, transaction));
|
this.sockets.forEach(socket => this.sendTransaction(socket, transaction));
|
||||||
//}
|
}
|
||||||
|
|
||||||
//broadcastMetadata(metadata) {
|
broadcastMetadata(metadata) {
|
||||||
// this.sockets.forEach(socket => this.sendMetadata(socket, metadata));
|
this.sockets.forEach(socket => this.sendMetadata(socket, metadata));
|
||||||
//}
|
}
|
||||||
|
|
||||||
//broadcastClearTransactions() {
|
//broadcastClearTransactions() {
|
||||||
// this.sockets.forEach(socket => socket.send(JSON.stringify({
|
// this.sockets.forEach(socket => socket.send(JSON.stringify({
|
||||||
|
|
|
@ -8,7 +8,11 @@ class Block {
|
||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.nonce = nonce;
|
this.nonce = nonce;
|
||||||
this.difficulty = difficulty || DIFFICULTY;
|
if (difficulty === undefined) {
|
||||||
|
this.difficulty = DIFFICULTY;
|
||||||
|
} else {
|
||||||
|
this.difficulty = difficulty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
|
@ -24,26 +28,20 @@ class Block {
|
||||||
static genesis() {
|
static genesis() {
|
||||||
return new this('Genesis time', '-----', 'f1r57-h45h', [], 0, DIFFICULTY);
|
return new this('Genesis time', '-----', 'f1r57-h45h', [], 0, DIFFICULTY);
|
||||||
}
|
}
|
||||||
//we want this to eventually be continously running where there are things in the pool,
|
|
||||||
//however as node is single threaded, this almost has to be a fiber, and yield after every
|
|
||||||
//other iteration to allow for meaningful forward progress
|
|
||||||
|
|
||||||
//we can either add all new transactions into the block as we see them, or stay with the starting list, idk which
|
//returns false if hash doesn't match
|
||||||
//to be done later
|
static checkHash(hash, timestamp, lastHash, data, nonce, difficulty) {
|
||||||
static mineBlock(lastBlock, data) {
|
const computedHash = Block.hash(timestamp, lastHash, data, nonce, difficulty);
|
||||||
let hash, timestamp;
|
|
||||||
const lastHash = lastBlock.hash;
|
|
||||||
let { difficulty } = lastBlock;
|
|
||||||
let nonce = 0;
|
|
||||||
|
|
||||||
do {
|
if (computedHash !== hash) {
|
||||||
nonce++;
|
return false;
|
||||||
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, lastHash, hash, data, nonce, difficulty);
|
if (hash.substring(0, difficulty) !== '0'.repeat(difficulty)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static hash(timestamp, lastHash, data, nonce, difficulty) {
|
static hash(timestamp, lastHash, data, nonce, difficulty) {
|
||||||
|
@ -55,11 +53,16 @@ class Block {
|
||||||
return Block.hash(timestamp, lastHash, data, nonce, difficulty);
|
return Block.hash(timestamp, lastHash, data, nonce, difficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//returns false if block's hash doesn't match internals
|
||||||
|
static checkBlock(block) {
|
||||||
|
return Block.checkHash(block.hash, block.timestamp, block.lastHash, block.data, block.nonce, block.difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
static adjustDifficulty(lastBlock, currentTime) {
|
static adjustDifficulty(lastBlock, currentTime) {
|
||||||
let { difficulty } = lastBlock;
|
let { difficulty } = lastBlock;
|
||||||
difficulty = lastBlock.timestamp + MINE_RATE > currentTime ?
|
difficulty = lastBlock.timestamp + MINE_RATE > currentTime ?
|
||||||
difficulty + 1 : difficulty - 1;
|
difficulty + 1 : difficulty - 1;
|
||||||
return difficulty;
|
return Math.max(0, difficulty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,15 +5,37 @@ class Blockchain {
|
||||||
this.chain = [Block.genesis()];
|
this.chain = [Block.genesis()];
|
||||||
}
|
}
|
||||||
|
|
||||||
addBlock(data) {
|
//adds an existing block to the blockchain, returns false if the block can't be added, true if it was added
|
||||||
const block = Block.mineBlock(this.chain[this.chain.length-1], data);
|
addBlock(newBlock) {
|
||||||
this.chain.push(block);
|
if (newBlock.lastHash !== this.chain[this.chain.length - 1].hash) {
|
||||||
|
console.log("Tried to add invalid block, last hash didn't match our last hash");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//how to check if new block's timestamp is believable
|
||||||
|
if (newBlock.difficulty !== Block.adjustDifficulty(this.chain[this.chain.length - 1], newBlock.timestamp)) {
|
||||||
|
console.log("Tried to add invalid block, difficulty is incorrect");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Block.checkBlock(newBlock)) {
|
||||||
|
console.log("Tried to add invalid block, block's hash doesn't match its contents");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return block;
|
this.chain.push(newBlock);
|
||||||
|
|
||||||
|
console.log("Added new block: ");
|
||||||
|
//console.log(newBlock);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
isValidChain(chain) {
|
isValidChain(chain) {
|
||||||
if(JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis())) return false;
|
if (chain.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (JSON.stringify(chain[0]) !== JSON.stringify(Block.genesis())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (let i=1; i<chain.length; i++) {
|
for (let i=1; i<chain.length; i++) {
|
||||||
const block = chain[i];
|
const block = chain[i];
|
||||||
|
@ -23,12 +45,15 @@ class Blockchain {
|
||||||
block.hash !== Block.blockHash(block)) {
|
block.hash !== Block.blockHash(block)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!Block.checkBlock(block)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//return false on failure, true on success
|
//return null on fail, returns the index of where they differ
|
||||||
replaceChain(newChain) {
|
replaceChain(newChain) {
|
||||||
if (newChain.length <= this.chain.length) {
|
if (newChain.length <= this.chain.length) {
|
||||||
console.log('Received chain is not longer than the current chain.');
|
console.log('Received chain is not longer than the current chain.');
|
||||||
|
@ -39,8 +64,18 @@ class Blockchain {
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Replacing blockchain with the new chain.');
|
console.log('Replacing blockchain with the new chain.');
|
||||||
|
|
||||||
|
const oldChain = this.chain;
|
||||||
this.chain = newChain;
|
this.chain = newChain;
|
||||||
return true;
|
|
||||||
|
//find where they differ
|
||||||
|
for (let i = 1; i < oldChain.length; ++i) {
|
||||||
|
if (oldChain[i].hash !== newChain[i].hash) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if they didn't differ in the length of the old chain, must be one after
|
||||||
|
return oldChain.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
const ChainUtil = require('../chain-util');
|
|
||||||
const Transaction = require('./transaction');
|
const Transaction = require('./transaction');
|
||||||
const { INITIAL_BALANCE } = require('../config');
|
const { INITIAL_BALANCE } = require('../config');
|
||||||
const Metadata = require('./metadata');
|
const Metadata = require('./metadata');
|
||||||
|
const ChainUtil = require('../chain-util');
|
||||||
|
|
||||||
class Wallet {
|
class Wallet {
|
||||||
constructor() {
|
constructor(keyPair) {
|
||||||
this.balance = INITIAL_BALANCE;
|
this.balance = INITIAL_BALANCE;
|
||||||
this.keyPair = ChainUtil.genKeyPair();
|
this.keyPair = keyPair;
|
||||||
this.publicKey = this.keyPair.getPublic().encode('hex');
|
this.publicKey = this.keyPair.getPublic().encode('hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,36 +25,16 @@ class Wallet {
|
||||||
|
|
||||||
if (amount > this.balance) {
|
if (amount > this.balance) {
|
||||||
console.log(`Amount: ${amount} exceceds current balance: ${this.balance}`);
|
console.log(`Amount: ${amount} exceceds current balance: ${this.balance}`);
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let transaction = transactionPool.existingTransaction(this.publicKey);
|
return Transaction.newTransaction(this, recipient, amount);
|
||||||
|
|
||||||
if (transaction) {
|
|
||||||
transaction.update(this, recipient, amount);
|
|
||||||
} else {
|
|
||||||
transaction = Transaction.newTransaction(this, recipient, amount);
|
|
||||||
transactionPool.updateOrAddTransaction(transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
return transaction;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createMetadata(SSNmetadata, transactionPool){
|
createMetadata(SSNmetadata) {
|
||||||
//let metadata = transactionPool.existingMetadata(this.publicKey);
|
return Metadata.newMetadata(this, SSNmetadata);
|
||||||
|
}
|
||||||
|
|
||||||
// if (metaData) {
|
|
||||||
// metadata.update(this, Geo, Std, Name,MetaHash,file);
|
|
||||||
// } else {*/
|
|
||||||
|
|
||||||
let metadata= Metadata.newMetadata(this, SSNmetadata);
|
|
||||||
transactionPool.AddMetadata(metadata);
|
|
||||||
|
|
||||||
//}
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
calculateBalance(blockchain) {
|
calculateBalance(blockchain) {
|
||||||
let balance = this.balance;
|
let balance = this.balance;
|
||||||
let transactions = [];
|
let transactions = [];
|
||||||
|
@ -93,7 +73,7 @@ class Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
static blockchainWallet() {
|
static blockchainWallet() {
|
||||||
const blockchainWallet = new this();
|
const blockchainWallet = new this(ChainUtil.genKeyPair());
|
||||||
blockchainWallet.address = 'blockchain-wallet';
|
blockchainWallet.address = 'blockchain-wallet';
|
||||||
return blockchainWallet;
|
return blockchainWallet;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,50 @@
|
||||||
const Transaction = require('../wallet/transaction');
|
const Transaction = require('../wallet/transaction');
|
||||||
const Metadata = require('../wallet/metadata')
|
const Metadata = require('../wallet/metadata')
|
||||||
|
|
||||||
|
const Return = {
|
||||||
|
add: 1,
|
||||||
|
update: 2,
|
||||||
|
error: 3
|
||||||
|
};
|
||||||
|
|
||||||
class TransactionPool {
|
class TransactionPool {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.transactions = [];
|
this.transactions = [];
|
||||||
this.metadataS =[];
|
this.metadataS =[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//returns true on update, false on add
|
||||||
updateOrAddTransaction(transaction) {
|
updateOrAddTransaction(transaction) {
|
||||||
let transactionWithId = this.transactions.find(t => t.id === transaction.id);
|
if (!Transaction.verifyTransaction(transaction)) {
|
||||||
|
console.log("Couldn't update or add transaction, transaction couldn't be verified");
|
||||||
|
return Return.error;
|
||||||
|
}
|
||||||
|
const foundIndex = this.transactions.findIndex(t => t.id === transaction.id);
|
||||||
|
|
||||||
if (transactionWithId) {
|
if (foundIndex !== -1) {
|
||||||
this.transactions[this.transactions.indexOf(transactionWithId)] = transaction;
|
this.transactions[foundIndex] = transaction;
|
||||||
|
return Return.update;
|
||||||
} else {
|
} else {
|
||||||
this.transactions.push(transaction);
|
this.transactions.push(transaction);
|
||||||
|
return Return.add;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddMetadata(metadata) {
|
updateOrAddMetadata(metadata) {
|
||||||
// let metadataWithId = this.metadataS.find(t => t.id === metadata.id);
|
if (!Metadata.verifyMetadata(metadata)) {
|
||||||
|
console.log("Couldn't update metdata, metadata couldn't be verified");
|
||||||
|
return Return.error;
|
||||||
|
}
|
||||||
|
|
||||||
// if (metadataWithId) {
|
const foundIndex = this.metadataS.findIndex(t => t.id === metadata.id);
|
||||||
// this.metaDataS[this.metadataS.indexOf(metadataWithId)] = metadata;
|
|
||||||
// } else {
|
if (foundIndex !== -1) {
|
||||||
|
this.metadataS[foundIndex] = metadata;
|
||||||
|
return Return.update;
|
||||||
|
} else {
|
||||||
this.metadataS.push(metadata);
|
this.metadataS.push(metadata);
|
||||||
// }
|
return Return.add;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
existingTransaction(address) {
|
existingTransaction(address) {
|
||||||
|
@ -64,10 +85,31 @@ class TransactionPool {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
clearFromBlock(block) {
|
||||||
|
const transactions = block.data[0];
|
||||||
|
const metadatas = block.data[1];
|
||||||
|
for (const transaction of transactions) {
|
||||||
|
const foundTransaction = this.transactions.findIndex(t => t.id === transaction.id);
|
||||||
|
|
||||||
|
if (foundTransaction !== -1) {
|
||||||
|
this.transactions.splice(foundTransaction, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const metadata of metadatas) {
|
||||||
|
const foundMetadata = this.metadataS.findIndex(m => m.id === metadata.id);
|
||||||
|
|
||||||
|
if (foundMetadata !== -1) {
|
||||||
|
this.metadataS.splice(foundMetadata, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clearAll() {
|
||||||
this.transactions = [];
|
this.transactions = [];
|
||||||
this.metadataS = [];
|
this.metadataS = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TransactionPool;
|
module.exports = TransactionPool;
|
||||||
|
module.exports.Return = Return;
|
Loading…
Add table
Add a link
Reference in a new issue