miner mines continuously,
moved to an account model, transactions working, querying working, more checking of valid data
This commit is contained in:
parent
59bb42be11
commit
9847b2056b
12 changed files with 663 additions and 365 deletions
101
app/index.js
101
app/index.js
|
@ -27,16 +27,17 @@
|
|||
* to monitor the node
|
||||
*
|
||||
*/
|
||||
|
||||
const LoggerPretty = require("@comunica/logger-pretty").LoggerPretty;
|
||||
|
||||
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 QueryEngine = require('@comunica/query-sparql').QueryEngine;
|
||||
const ChainUtil = require('../chain-util');
|
||||
|
||||
const N3 = require('n3');
|
||||
const jsonld = require('jsonld');
|
||||
var mqtt = require('mqtt');
|
||||
var aedes = require('aedes')(); /* aedes is a stream-based MQTT broker */
|
||||
|
@ -48,17 +49,46 @@ const multer = require('multer');/* Multer is a node.js middleware for
|
|||
'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 SETTINGS_STORAGE_LOCATION = "./settings.json";
|
||||
const SETTING_MINER_PUBLIC_KEY = "miner-public-key";
|
||||
const SETTING_WALLET_PRIVATE_KEY = "wallet-private-key";
|
||||
|
||||
var settings = {};
|
||||
|
||||
//possible race if deleted after check, but we live with it I guess
|
||||
if (fs.existsSync(SETTINGS_STORAGE_LOCATION)) {
|
||||
const rawSettings = fs.readFileSync(SETTINGS_STORAGE_LOCATION, 'utf8');
|
||||
settings = JSON.parse(rawSettings);
|
||||
}
|
||||
|
||||
const app = express();
|
||||
const bc = new Blockchain();
|
||||
//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 p2pServer = new P2pServer(bc, tp, wallet, './persist_block_chain.json');
|
||||
|
||||
const parser = new N3.Parser(); //({format: 'application/n-quads'});
|
||||
//wallet init
|
||||
var wallet = null;
|
||||
|
||||
if (settings.hasOwnProperty(SETTING_WALLET_PRIVATE_KEY)) {
|
||||
wallet = new Wallet(ChainUtil.deserializeKeyPair(settings[SETTING_WALLET_PRIVATE_KEY]));
|
||||
} else {
|
||||
wallet = new Wallet(ChainUtil.genKeyPair());
|
||||
}
|
||||
|
||||
//miner public key init
|
||||
var minerPublicKey = null;
|
||||
|
||||
if (settings.hasOwnProperty(SETTING_MINER_PUBLIC_KEY)) {
|
||||
minerPublicKey = settings[SETTING_MINER_PUBLIC_KEY];
|
||||
} else {
|
||||
minerPublicKey = wallet.publicKey;
|
||||
}
|
||||
|
||||
const tp = new TransactionPool();
|
||||
const p2pServer = new P2pServer(tp, minerPublicKey, './persist_block_chain.json');
|
||||
const myEngine = new QueryEngine();
|
||||
|
||||
function getBlockchain() {
|
||||
return p2pServer.blockchain;
|
||||
}
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
//initialising a local storage for storing metadata file initially before storing it in the tripple store
|
||||
|
@ -73,7 +103,7 @@ const storage = multer.diskStorage({
|
|||
//filtering the type of uploaded Metadata files
|
||||
const fileFilter = (req, file, cb) => {
|
||||
// reject a file
|
||||
if (file.mimetype === 'application/json' || file.mimetype === 'text/plain' || file.mimettype === 'turtle') {
|
||||
if (file.mimetype === 'application/json' || file.mimetype === 'text/plain' || file.mimetype === 'turtle') {
|
||||
cb(null, true);
|
||||
} else {
|
||||
cb(null, false);
|
||||
|
@ -102,6 +132,9 @@ MQTTserver.listen(MQTTport, function () {
|
|||
app.use('/uploads', express.static('uploads')); // to store uploaded metadata to '/uploads' folder
|
||||
app.use(bodyParser.json()); //
|
||||
|
||||
//API HELPERS
|
||||
function
|
||||
|
||||
// GET APIs
|
||||
app.get('/blocks', (req, res) => {
|
||||
res.json(bc.chain);
|
||||
|
@ -131,7 +164,12 @@ app.get('/public-key', (req, res) => {
|
|||
});
|
||||
///////////////
|
||||
app.get('/Balance', (req, res) => {
|
||||
res.json({ Balance: wallet.balance });
|
||||
const balance = getBlockchain().getBalanceCopy(wallet.publicKey);
|
||||
res.json({ Balance: balance.balance });
|
||||
});
|
||||
app.get('/Balances', (req, res) => {
|
||||
const balances = getBlockchain().balances;
|
||||
res.json(balances);
|
||||
});
|
||||
|
||||
///////////////
|
||||
|
@ -170,15 +208,22 @@ app.get('/IoTdeviceRegistration', (req, res)=> {
|
|||
(err, nquads) => {
|
||||
//console.log(nquads)
|
||||
var metadata = wallet.createMetadata(
|
||||
nquads, tp);
|
||||
nquads);
|
||||
p2pServer.newMetadata(metadata);
|
||||
});
|
||||
});
|
||||
res.json("MetadataTransactionCreated");
|
||||
});
|
||||
|
||||
app.get('/storeSize', (req, res) => {
|
||||
res.json({
|
||||
size: getBlockchain().store.size
|
||||
});
|
||||
});
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// POST APIs
|
||||
|
||||
//this doesn't work well with the continious miner
|
||||
//app.post('/mine', (req, res) => {
|
||||
// const block = bc.addBlock(req.body.data);
|
||||
|
@ -190,18 +235,38 @@ app.get('/IoTdeviceRegistration', (req, res)=> {
|
|||
//});
|
||||
///////////////
|
||||
app.post('/PaymentTransaction', (req, res) => {
|
||||
if (!req.body.hasOwnProperty('recpient')) {
|
||||
res.json({
|
||||
result: false,
|
||||
reason: "Missing \"recipient\" in body"
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!req.body.hasOwnProperty('amount')) {
|
||||
res.json({
|
||||
result: false,
|
||||
reason: "Missing \"amount\" in body"
|
||||
});
|
||||
return;
|
||||
}
|
||||
const { recipient, amount } = req.body;
|
||||
const transaction = wallet.createTransaction(recipient, amount, bc, tp);
|
||||
const transaction = wallet.createTransaction(recipient, amount, getBlockchain());
|
||||
if (transaction === null) {
|
||||
res.json("Couldn't create transaction");
|
||||
return;
|
||||
}
|
||||
p2pServer.newTransaction(transaction);
|
||||
res.redirect('/transactions');
|
||||
res.json(transaction);
|
||||
});
|
||||
|
||||
///////////////
|
||||
app.post('/IoTdevicePaymentTransaction', (req, res) => {
|
||||
if (!req.body.hasOwnProperty("Recipient_payment_address")) {
|
||||
req.json({
|
||||
result: false,
|
||||
reason: "Missing \"Recipient_
|
||||
}
|
||||
}
|
||||
const { Recipient_payment_address, Amount_of_money, Payment_method,
|
||||
Further_details} = req.body;
|
||||
if (Payment_method == "SensorCoin") {
|
||||
|
@ -239,24 +304,22 @@ app.post("/UploadMetafile", upload.single('file'), (req, res) => {
|
|||
/////////////////////
|
||||
//Start of comunica sparql query code
|
||||
app.post('/sparql', (req, res) => {
|
||||
console.log(req.body);
|
||||
const start = async function () {
|
||||
try {
|
||||
let result = [];
|
||||
const bindingsStream = await myEngine.queryBindings(
|
||||
req.body,
|
||||
req.body.query,
|
||||
{
|
||||
log: new LoggerPretty({ level: 'trace' }),
|
||||
readOnly: true,
|
||||
sources: [{
|
||||
type: 'rdfjsSource',
|
||||
value: p2pServer.store
|
||||
}]
|
||||
sources: [getBlockchain().store]
|
||||
});
|
||||
bindingsStream.on('data', (binding) => {
|
||||
console.log(binding.toString());
|
||||
result.push(binding);
|
||||
});
|
||||
bindingsStream.on('end', () => {
|
||||
console.log('end');
|
||||
res.json(JSON.stringify(result));
|
||||
});
|
||||
bindingsStream.on('error', (err) => {
|
||||
|
|
90
app/miner.js
90
app/miner.js
|
@ -1,22 +1,26 @@
|
|||
const Wallet = require('../wallet');
|
||||
const Transaction = require('../wallet/transaction');
|
||||
const Block = require('../blockchain/block');
|
||||
|
||||
const ITERATIONS = 1;
|
||||
|
||||
class Miner {
|
||||
static STATE_WAITING = 0;
|
||||
static STATE_RUNNING = 1;
|
||||
static STATE_INTERRUPTED = 2;
|
||||
static STATE_RESTARTING = 3;
|
||||
static STATE_RUNNING = 0;
|
||||
static STATE_INTERRUPTED = 1;
|
||||
|
||||
constructor(blockchain, transactionPool, wallet, p2pServer) {
|
||||
constructor(blockchain, transactionPool, reward, p2pServer) {
|
||||
this.blockchain = blockchain;
|
||||
this.transactionPool = transactionPool;
|
||||
this.wallet = wallet;
|
||||
this.p2pServer = p2pServer;
|
||||
this.state = Miner.STATE_WAITING;
|
||||
this.mining = [[], []];
|
||||
this.state = Miner.STATE_INTERRUPTED;
|
||||
this.lastBlock = null;
|
||||
|
||||
this.minedStartTime = null;
|
||||
|
||||
this.mining = {};
|
||||
this.mining.transactions = [];
|
||||
this.mining.reward = reward;
|
||||
this.mining.metadatas = [];
|
||||
|
||||
this.startMine();
|
||||
}
|
||||
|
||||
interrupt() {
|
||||
|
@ -26,65 +30,77 @@ class Miner {
|
|||
}
|
||||
|
||||
interruptIfContainsTransaction(transaction) {
|
||||
if (this.state === Miner.STATE_RUNNING && this.mining[0].find(t => t.id === transaction.id)) {
|
||||
if (this.state === Miner.STATE_RUNNING && this.mining.metadatas.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)) {
|
||||
if (this.state === Miner.STATE_RUNNING && this.mining.transactions.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) {
|
||||
if (this.state !== Miner.STATE_INTERRUPTED && this.state !== Miner.STATE_RESTARTING) {
|
||||
return;
|
||||
}
|
||||
|
||||
const validTransactions = this.transactionPool.validTransactions();
|
||||
const validMetadataS = this.transactionPool.validMetadataS();
|
||||
this.minedStartTime = process.hrtime.bigint();
|
||||
|
||||
if (validTransactions.length === 0 && validMetadataS.length === 0) {
|
||||
this.state = Miner.STATE_WAITING;
|
||||
return;
|
||||
}
|
||||
|
||||
validTransactions.push(
|
||||
Transaction.rewardTransaction(this.wallet, Wallet.blockchainWallet())
|
||||
);
|
||||
this.mining.transactions = this.transactionPool.validTransactionsCopy();
|
||||
this.mining.metadatas = this.transactionPool.validMetadatasCopy();
|
||||
|
||||
this.lastBlock = this.blockchain.chain[this.blockchain.chain.length - 1];
|
||||
|
||||
this.nonce = 0;
|
||||
this.state = Miner.STATE_RUNNING;
|
||||
|
||||
this.mining = [validTransactions, validMetadataS];
|
||||
this.nonce = 0;
|
||||
this.mine();
|
||||
}
|
||||
|
||||
mine() {
|
||||
if (this.state !== Miner.STATE_RUNNING) {
|
||||
this.state = Miner.STATE_RESTARTING;
|
||||
startMine();
|
||||
this.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() });
|
||||
for (let i = 0; i < ITERATIONS; ++i) {
|
||||
const hash = Block.hash(
|
||||
timestamp,
|
||||
this.lastBlock.hash,
|
||||
this.mining.reward,
|
||||
this.mining.transactions,
|
||||
this.mining.metadatas,
|
||||
this.nonce,
|
||||
difficulty);
|
||||
|
||||
if (hash.substring(0, difficulty) === '0'.repeat(difficulty)) {
|
||||
//success
|
||||
const endTime = process.hrtime.bigint();
|
||||
console.log(`Mined a block of difficulty ${difficulty} in ${Number(endTime - this.minedStartTime) / 1000000}ms`);
|
||||
this.p2pServer.blockMined(new Block(
|
||||
timestamp,
|
||||
this.lastBlock.hash,
|
||||
hash,
|
||||
this.mining.reward,
|
||||
this.mining.transactions,
|
||||
this.mining.metadatas,
|
||||
this.nonce,
|
||||
difficulty));
|
||||
this.state = Miner.STATE_RESTARTING;
|
||||
setImmediate(() => { this.startMine() });
|
||||
} else {
|
||||
//failure
|
||||
this.nonce++;
|
||||
}
|
||||
}
|
||||
setImmediate(() => { this.mine() });
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Miner;
|
||||
module.exports = Miner;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
const Websocket = require('ws');
|
||||
const N3 = require('n3');
|
||||
const DataFactory = require('n3').DataFactory;
|
||||
|
||||
const fs = require('fs');
|
||||
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 Blockchain = require('../blockchain');
|
||||
|
||||
const P2P_PORT = process.env.P2P_PORT || 5000;
|
||||
const peers = process.env.PEERS ? process.env.PEERS.split(',') : [];
|
||||
|
@ -18,22 +18,26 @@ const MESSAGE_TYPES = {
|
|||
};
|
||||
|
||||
class P2pServer {
|
||||
constructor(blockchain, transactionPool, wallet, chainStorageLocation) {
|
||||
this.blockchain = blockchain;
|
||||
constructor(transactionPool, rewardPublicKey, chainStorageLocation) {
|
||||
this.blockchain = new Blockchain();
|
||||
this.transactionPool = transactionPool;
|
||||
this.sockets = [];
|
||||
this.store = new N3.Store();
|
||||
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
|
||||
if (fs.existsSync(this.chainStorageLocation)) {
|
||||
const rawPersistedChain = fs.readFileSync(this.chainStorageLocation, 'utf8');
|
||||
const chain = JSON.parse(rawPersistedChain);
|
||||
this.newChain(chain, false);
|
||||
const deserialized = Blockchain.deserialize(rawPersistedChain);
|
||||
if (deserialized === null) {
|
||||
console.log(`Couldn't deserialize chain at '${this.chainStorageLocation}', starting from genesis`);
|
||||
} else {
|
||||
this.blockchain = deserialized;
|
||||
}
|
||||
} else {
|
||||
console.log("Didn't find a persisted chain, starting from genesis");
|
||||
}
|
||||
|
||||
this.miner = new Miner(this.blockchain, this.transactionPool, rewardPublicKey, this);
|
||||
}
|
||||
|
||||
listen() {
|
||||
|
@ -106,7 +110,7 @@ class P2pServer {
|
|||
}
|
||||
|
||||
newTransaction(transaction, broadcast) {
|
||||
if (!Transaction.verifyTransaction(transaction)) {
|
||||
if (!Transaction.verify(transaction)) {
|
||||
console.log("Couldn't add transaction to p2pServer, couldn't verify");
|
||||
return false;
|
||||
}
|
||||
|
@ -128,32 +132,32 @@ class P2pServer {
|
|||
}
|
||||
}
|
||||
|
||||
newBlock(block) {
|
||||
blockMined(block) {
|
||||
if (!this.blockchain.addBlock(block)) {
|
||||
//invalid block, return
|
||||
return;
|
||||
}
|
||||
this.onNewBlock(block);
|
||||
this.persistChain(this.blockchain.chain);
|
||||
this.transactionPool.clearFromBlock(block);
|
||||
this.miner.interrupt();
|
||||
this.persistChain(this.blockchain);
|
||||
this.syncChains();
|
||||
}
|
||||
|
||||
newChain(chain, persist) {
|
||||
const oldChain = this.blockchain.chain;
|
||||
const divergence = this.blockchain.replaceChain(chain);
|
||||
|
||||
if (divergence === null) {
|
||||
const replaceResult = this.blockchain.replaceChain(chain);
|
||||
if (!replaceResult.result) {
|
||||
//failed to replace
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < replaceResult.chainDifference; i++) {
|
||||
this.transactionPool.clearFromBlock(this.blockchain.chain[i]);
|
||||
}
|
||||
|
||||
this.miner.interrupt();
|
||||
|
||||
if (typeof persist === "undefined" || persist) {
|
||||
this.persistChain(chain);
|
||||
}
|
||||
for (let i = divergence; i < oldChain.length; i++) {
|
||||
this.store.deleteGraph(oldChain[i].hash);
|
||||
}
|
||||
for (let i = divergence; i < this.blockchain.chain.length; i++) {
|
||||
this.onNewBlock(this.blockchain.chain[i]);
|
||||
this.persistChain(this.blockchain);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,70 +165,33 @@ class P2pServer {
|
|||
try {
|
||||
fs.writeFileSync(
|
||||
this.chainStorageLocation,
|
||||
JSON.stringify(chain));
|
||||
chain.serialize());
|
||||
} catch (err) {
|
||||
console.error("Couldn't persist chain, aborting");
|
||||
console.error(`Couldn't persist chain, aborting: ${err}`);
|
||||
process.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
onNewBlock(block) {
|
||||
//block data is of form [transactions,metadatas]
|
||||
if (block.data.length != 2) {
|
||||
//assert?
|
||||
return;
|
||||
}
|
||||
|
||||
this.transactionPool.clearFromBlock(block);
|
||||
|
||||
this.miner.interrupt();
|
||||
|
||||
const metadatas = block.data[1];
|
||||
|
||||
for (const metadata of metadatas) {
|
||||
if (!("SSNmetadata" in metadata)) {
|
||||
//assert?
|
||||
return;
|
||||
}
|
||||
|
||||
var ssn = metadata.SSNmetadata;
|
||||
|
||||
const parser = new N3.Parser();
|
||||
|
||||
parser.parse(
|
||||
ssn,
|
||||
(error, quadN, prefixes) => {
|
||||
if (quadN) {
|
||||
this.store.addQuad(DataFactory.quad(
|
||||
DataFactory.namedNode(quadN.subject.id),
|
||||
DataFactory.namedNode(quadN.predicate.id),
|
||||
DataFactory.namedNode(quadN.object.id),
|
||||
DataFactory.namedNode(block.hash)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sendChain(socket) {
|
||||
socket.send(JSON.stringify({
|
||||
type: MESSAGE_TYPES.chain,
|
||||
chain: this.blockchain.chain
|
||||
chain: this.blockchain.serialize()
|
||||
}));
|
||||
}
|
||||
|
||||
//sendTransaction(socket, transaction) {
|
||||
// socket.send(JSON.stringify({
|
||||
// type: MESSAGE_TYPES.transaction,
|
||||
// transaction
|
||||
// }));
|
||||
//}
|
||||
sendTransaction(socket, transaction) {
|
||||
socket.send(JSON.stringify({
|
||||
type: MESSAGE_TYPES.transaction,
|
||||
transaction
|
||||
}));
|
||||
}
|
||||
|
||||
//sendMetadata(socket, metadata) {
|
||||
// socket.send(JSON.stringify({
|
||||
// type: MESSAGE_TYPES.metadata,
|
||||
// metadata
|
||||
// }));
|
||||
//}
|
||||
sendMetadata(socket, metadata) {
|
||||
socket.send(JSON.stringify({
|
||||
type: MESSAGE_TYPES.metadata,
|
||||
metadata
|
||||
}));
|
||||
}
|
||||
syncChains() {
|
||||
this.sockets.forEach(socket => this.sendChain(socket));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue