-
-
Save vekexasia/6c3d466d6a0bef6efd180c19f4509055 to your computer and use it in GitHub Desktop.
ark-bug-poc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const configContent = require('./test/config.json'); | |
const constants = require('./helpers/constants'); | |
configContent.forging.secret = []; | |
fs.writeFileSync('./config.vekexasiabug.json', JSON.stringify(configContent, null, 2)); | |
if (!fs.existsSync('./genesisBlock.backup.json')) { | |
fs.renameSync('./genesisBlock.json', './genesisBlock.backup.json'); | |
fs.writeFileSync('./genesisBlock.json', fs.readFileSync('./test/genesisBlock.json')); | |
} | |
// const app = spawn('/home/abaccega/.nvm/versions/node/v6.11.0/bin/node', ['./app.js', '-c', './config.vekexasiabug.json' ]) | |
// | |
// app.stdout.on('data', data => console.log(data.toString())); | |
// app.on('exit', data => console.log('exit', data)); | |
// app.stderr.on('data', (data) => { | |
// console.log(data.toString()); | |
// }); | |
var appConfig = require('./config.vekexasiabug.json'); | |
'use strict'; | |
// var appConfig = require('./config.json'); | |
var networks = require('./networks.json'); | |
var async = require('async'); | |
var checkIpInList = require('./helpers/checkIpInList.js'); | |
var extend = require('extend'); | |
// var fs = require('fs'); | |
var genesisblock = require('./genesisBlock.json'); | |
var arkjs = require('arkjs'); | |
var https = require('https'); | |
var Logger = require('./logger.js'); | |
var packageJson = require('./package.json'); | |
var path = require('path'); | |
var program = require('commander'); | |
var Sequence = require('./helpers/sequence.js'); | |
var util = require('util'); | |
var z_schema = require('./helpers/z_schema.js'); | |
var colors = require('colors'); | |
var vorpal = require('vorpal')(); | |
var spawn = require('child_process').spawn; | |
process.stdin.resume(); | |
var versionBuild = fs.readFileSync(path.join(__dirname, 'build'), 'utf8'); | |
program | |
.version(packageJson.version) | |
.option('-c, --config <path>', 'config file path') | |
.option('-g, --genesis <path>', 'genesis block') | |
.option('-n, --networks <path>', 'networks definition file') | |
.option('-p, --port <port>', 'listening port number') | |
.option('-a, --address <ip>', 'listening host name or ip') | |
.option('-x, --peers [peers...]', 'peers list') | |
.option('-l, --log <level>', 'log level') | |
.option('-i, --interactive', 'launch cli') | |
.parse(process.argv); | |
if (program.config) { | |
appConfig = require(path.resolve(process.cwd(), program.config)); | |
} | |
if (program.genesis) { | |
genesisblock = require(path.resolve(process.cwd(), program.genesis)); | |
} | |
if (program.networks) { | |
networks = require(path.resolve(process.cwd(), program.networks)); | |
} | |
if (program.port) { | |
appConfig.port = program.port; | |
} | |
if (program.address) { | |
appConfig.address = program.address; | |
} | |
if (program.peers) { | |
if (typeof program.peers === 'string') { | |
appConfig.peers.list = program.peers.split(',').map(function (peer) { | |
peer = peer.split(':'); | |
return { | |
ip: peer.shift(), | |
port: peer.shift() || appConfig.port | |
}; | |
}); | |
} else { | |
appConfig.peers.list = []; | |
} | |
} | |
if (program.log) { | |
appConfig.consoleLogLevel = program.log; | |
} | |
if (program.interactive) { | |
appConfig.consoleLogLevel = "none"; | |
} | |
var config = { | |
db: appConfig.db, | |
modules: { | |
accounts: './modules/accounts.js', | |
transactions: './modules/transactions.js', | |
blocks: './modules/blocks.js', | |
signatures: './modules/signatures.js', | |
transport: './modules/transport.js', | |
loader: './modules/loader.js', | |
system: './modules/system.js', | |
peers: './modules/peers.js', | |
delegates: './modules/delegates.js', | |
rounds: './modules/rounds.js', | |
multisignatures: './modules/multisignatures.js', | |
transactionPool: './modules/transactionPool.js', | |
blockchain: './modules/blockchain.js', | |
nodeManager: './modules/nodeManager.js' | |
} | |
}; | |
if(appConfig.network){ | |
appConfig.network = networks[appConfig.network]; | |
} | |
else { | |
appConfig.network = networks.ark; | |
} | |
if(appConfig.modules){ | |
for(var name in appConfig.modules){ | |
config.modules[name]=appConfig.modules[name]; | |
} | |
} | |
var logger = new Logger({ echo: appConfig.consoleLogLevel, errorLevel: appConfig.fileLogLevel, filename: appConfig.logFileName }); | |
var d = require('domain').create(); | |
d.on('error', function (err) { | |
logger.fatal('Domain master', { message: err.message, stack: err.stack }); | |
process.exit(0); | |
}); | |
d.run(function () { | |
var modules = []; | |
console.log(colors.cyan("\n\ | |
{_ {_______ {__ {__ {___ {__ {____ {_____ {________\n\ | |
{_ __ {__ {__ {__ {__ {_ {__ {__ {__ {__ {__ {__ {__\n\ | |
{_ {__ {__ {__ {__ {__ {__ {__ {__{__ {__{__ {__{__\n\ | |
{__ {__ {_ {__ {_ {_ {__ {__ {__{__ {__{__ {__{______\n\ | |
{______ {__ {__ {__ {__ {__ {__ {_ {__{__ {__{__ {__{__\n\ | |
{__ {__ {__ {__ {__ {__ {__ {_ __ {__ {__ {__ {__ {__\n\ | |
{__ {__{__ {__{__ {__ {__ {__ {____ {_____ {________\n\ | |
\n\n\ | |
W E L C O M E A B O A R D !\n\ | |
\n\ | |
")); | |
async.auto({ | |
config: function (cb) { | |
try { | |
appConfig.nethash = new Buffer(genesisblock.payloadHash, 'hex').toString('hex'); | |
} catch (e) { | |
logger.error('Failed to assign nethash from genesis block'); | |
throw Error(e); | |
} | |
cb(null, appConfig); | |
}, | |
logger: function (cb) { | |
cb(null, logger); | |
}, | |
build: function (cb) { | |
cb(null, versionBuild); | |
}, | |
genesisblock: function (cb) { | |
cb(null, { | |
block: genesisblock | |
}); | |
}, | |
schema: function (cb) { | |
var schema = new z_schema(appConfig.network).z_schema | |
cb(null, new schema()); | |
}, | |
network: ['config', function (scope, cb) { | |
var express = require('express'); | |
var compression = require('compression'); | |
var cors = require('cors'); | |
var app = express(); | |
require('./helpers/request-limiter')(app, appConfig); | |
app.use(compression({ level: 6 })); | |
app.use(cors()); | |
app.options('*', cors()); | |
var server = require('http').createServer(app); | |
var io = require('socket.io')(server); | |
var privateKey, certificate, https, https_io; | |
if (scope.config.ssl.enabled) { | |
privateKey = fs.readFileSync(scope.config.ssl.options.key); | |
certificate = fs.readFileSync(scope.config.ssl.options.cert); | |
https = require('https').createServer({ | |
key: privateKey, | |
cert: certificate, | |
ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:' + 'ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:' + '!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA' | |
}, app); | |
https_io = require('socket.io')(https); | |
} | |
cb(null, { | |
express: express, | |
app: app, | |
server: server, | |
io: io, | |
https: https, | |
https_io: https_io | |
}); | |
}], | |
//TODO: to move to modules/transactions.js ? | |
//To be deprecated in favor of blocksequence, encapsulating unconfirmed tx application in a blocksequence. | |
//To balance transaction application (unconfirmed and confirmed) | |
transactionSequence: ['logger', function (scope, cb) { | |
var sequence = new Sequence({ | |
onWarning: function (current, limit) { | |
scope.logger.warn('Transaction queue', current); | |
} | |
}); | |
cb(null, sequence); | |
}], | |
// To balance block processing | |
blockSequence: ['logger', function (scope, cb) { | |
var sequence = new Sequence({ | |
onWarning: function (current, limit) { | |
scope.logger.warn('Block queue', current); | |
} | |
}); | |
cb(null, sequence); | |
}], | |
// To balance logic (rebuilding, syncing, downloading blocks, swapping blocks, etc...) | |
managementSequence: ['logger', function (scope, cb) { | |
var sequence = new Sequence({ | |
onWarning: function (current, limit) { | |
scope.logger.warn('Block queue', current); | |
} | |
}); | |
cb(null, sequence); | |
}], | |
//To balance db write | |
dbSequence: ['logger', function (scope, cb) { | |
var sequence = new Sequence({ | |
onWarning: function (current, limit) { | |
scope.logger.warn('DB queue', current); | |
} | |
}); | |
cb(null, sequence); | |
}], | |
//To balance block reception via API | |
receiveBlockSequence: ['logger', function (scope, cb) { | |
var sequence = new Sequence({ | |
onWarning: function (current, limit) { | |
scope.logger.warn('Receive Block queue', current); | |
} | |
}); | |
cb(null, sequence); | |
}], | |
//To balance API calls | |
balancesSequence: ['logger', function (scope, cb) { | |
var sequence = new Sequence({ | |
onWarning: function (current, limit) { | |
scope.logger.warn('Balance queue', current); | |
} | |
}); | |
cb(null, sequence); | |
}], | |
connect: ['config', 'genesisblock', 'logger', 'build', 'network', function (scope, cb) { | |
var path = require('path'); | |
var bodyParser = require('body-parser'); | |
var methodOverride = require('method-override'); | |
var requestSanitizer = require('./helpers/request-sanitizer'); | |
var queryParser = require('express-query-int'); | |
scope.network.app.engine('html', require('ejs').renderFile); | |
scope.network.app.use(bodyParser.raw({limit: '4mb'})); | |
scope.network.app.use(bodyParser.urlencoded({extended: true, limit: '2mb', parameterLimit: 5000})); | |
scope.network.app.use(bodyParser.json({limit: '4mb'})); | |
scope.network.app.use(methodOverride()); | |
var ignore = ['id', 'name', 'lastBlockId', 'blockId', 'transactionId', 'address', 'recipientId', 'senderId', 'previousBlock']; | |
scope.network.app.use(queryParser({ | |
parser: function (value, radix, name) { | |
if (ignore.indexOf(name) >= 0) { | |
return value; | |
} | |
/*jslint eqeq: true*/ | |
if (isNaN(value) || parseInt(value) != value || isNaN(parseInt(value, radix))) { | |
return value; | |
} | |
return parseInt(value); | |
} | |
})); | |
scope.network.app.use(require('./helpers/z_schema-express.js')(scope.schema)); | |
scope.network.app.use(function (req, res, next) { | |
var parts = req.url.split('/'); | |
var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress; | |
// Log client connections | |
logger.trace(req.method + ' ' + req.url + ' from ' + ip + ":" + req.headers.port); | |
/* Instruct browser to deny display of <frame>, <iframe> regardless of origin. | |
* | |
* RFC -> https://tools.ietf.org/html/rfc7034 | |
*/ | |
res.setHeader('X-Frame-Options', 'DENY'); | |
/* Set Content-Security-Policy headers. | |
* | |
* frame-ancestors - Defines valid sources for <frame>, <iframe>, <object>, <embed> or <applet>. | |
* | |
* W3C Candidate Recommendation -> https://www.w3.org/TR/CSP/ | |
*/ | |
res.setHeader('Content-Security-Policy', 'frame-ancestors \'none\''); | |
if (parts.length > 1) { | |
if (parts[1] === 'api') { | |
if (!checkIpInList(scope.config.api.access.whiteList, ip, true)) { | |
res.sendStatus(403); | |
} else { | |
next(); | |
} | |
} else if (parts[1] === 'peer') { | |
if (checkIpInList(scope.config.peers.blackList, ip, false)) { | |
res.sendStatus(403); | |
} else { | |
next(); | |
} | |
} else { | |
next(); | |
} | |
} else { | |
next(); | |
} | |
}); | |
scope.network.server.listen(scope.config.port, scope.config.address, function (err) { | |
scope.logger.info('# Ark node server started on: ' + scope.config.address + ':' + scope.config.port); | |
if (!err) { | |
if (scope.config.ssl.enabled) { | |
scope.network.https.listen(scope.config.ssl.options.port, scope.config.ssl.options.address, function (err) { | |
scope.logger.info('Ark https started: ' + scope.config.ssl.options.address + ':' + scope.config.ssl.options.port); | |
cb(err, scope.network); | |
}); | |
} else { | |
cb(null, scope.network); | |
} | |
} else { | |
cb(err, scope.network); | |
} | |
}); | |
if(program.interactive){ | |
startInteractiveMode(scope); | |
} | |
}], | |
crypto: ['config', function (scope, cb) { | |
var crypto = require('./helpers/crypto.js') | |
cb(null, new crypto(scope)); | |
}], | |
bus: ['crypto', function (scope, cb) { | |
var changeCase = require('change-case'); | |
var bus = function () { | |
this.message = function () { | |
var args = []; | |
Array.prototype.push.apply(args, arguments); | |
var topic = args.shift(); | |
modules.forEach(function (module) { | |
var eventName = 'on' + changeCase.pascalCase(topic); | |
if (typeof(module[eventName]) === 'function') { | |
module[eventName].apply(module[eventName], args); | |
} | |
}); | |
}; | |
}; | |
cb(null, new bus()); | |
}], | |
db: function (cb) { | |
var db = require('./helpers/database.js'); | |
db.connect(config.db, logger, cb); | |
}, | |
logic: ['db', 'bus', 'schema', 'genesisblock', function (scope, cb) { | |
var Transaction = require('./logic/transaction.js'); | |
var Block = require('./logic/block.js'); | |
var Account = require('./logic/account.js'); | |
async.auto({ | |
bus: function (cb) { | |
cb(null, scope.bus); | |
}, | |
db: function (cb) { | |
cb(null, scope.db); | |
}, | |
crypto: function (cb) { | |
cb(null, scope.crypto); | |
}, | |
logger: function (cb) { | |
cb(null, logger); | |
}, | |
schema: function (cb) { | |
cb(null, scope.schema); | |
}, | |
genesisblock: function (cb) { | |
cb(null, { | |
block: genesisblock | |
}); | |
}, | |
account: ['db', 'bus', 'crypto', 'schema', 'genesisblock', function (scope, cb) { | |
new Account(scope, cb); | |
}], | |
transaction: ['db', 'bus', 'crypto', 'schema', 'genesisblock', 'account', function (scope, cb) { | |
new Transaction(scope, cb); | |
}], | |
block: ['db', 'bus', 'crypto', 'schema', 'genesisblock', 'account', 'transaction', function (scope, cb) { | |
new Block(scope, cb); | |
}] | |
}, cb); | |
}], | |
modules: ['network', 'connect', 'config', 'logger', 'bus', 'managementSequence', 'blockSequence', 'transactionSequence', 'dbSequence', 'balancesSequence', 'db', 'logic', function (scope, cb) { | |
var tasks = {}; | |
Object.keys(config.modules).forEach(function (name) { | |
tasks[name] = function (cb) { | |
var d = require('domain').create(); | |
d.on('error', function (err) { | |
scope.logger.fatal('Domain ' + name, {message: err.message, stack: err.stack}); | |
}); | |
d.run(function () { | |
logger.debug('Loading module', name); | |
var Klass = require(config.modules[name]); | |
var obj = new Klass(cb, scope); | |
modules.push(obj); | |
}); | |
}; | |
}); | |
async.parallel(tasks, function (err, results) { | |
cb(err, results); | |
}); | |
}], | |
ready: ['modules', 'bus', function (scope, cb) { | |
scope.bus.message('bind', scope.modules); | |
cb(); | |
}] | |
}, function (err, scope) { | |
if (err) { | |
scope.logger.fatal(err); | |
} else { | |
scope.logger.info('Modules ready and launched'); | |
scope.modules.nodeManager.startApp(); | |
onInit(scope, modules); | |
process.once('cleanup', function () { | |
scope.logger.info('Cleaning up...'); | |
async.eachSeries(modules, function (module, cb) { | |
if (typeof(module.cleanup) === 'function') { | |
module.cleanup(cb); | |
} else { | |
cb(); | |
} | |
}, function (err) { | |
if (err) { | |
scope.logger.error(err); | |
} else { | |
scope.logger.info('Cleaned up successfully'); | |
} | |
process.exit(1); | |
}); | |
}); | |
process.once('SIGTERM', function () { | |
scope.logger.info('caught SIGTERM'); | |
process.emit('cleanup'); | |
}); | |
process.once('exit', function () { | |
scope.logger.info('caught internal exit'); | |
process.emit('cleanup'); | |
}); | |
process.once('SIGINT', function () { | |
scope.logger.info('caught SIGINT'); | |
process.emit('cleanup'); | |
}); | |
} | |
}); | |
}); | |
process.on('uncaughtException', function (err) { | |
// Handle error safely | |
logger.fatal('System error', { message: err.message, stack: err.stack }); | |
process.emit('cleanup'); | |
}); | |
function startInteractiveMode(scope){ | |
vorpal | |
.command('rebuild', 'Rebuild node from scratch') | |
.action(function(args, callback) { | |
this.log('Not Implemented'); | |
callback(); | |
}); | |
vorpal | |
.command('status', 'Send status of the node') | |
.action(function(args, callback) { | |
var self = this; | |
scope.modules.loader.getNetwork(true, function(err, network){ | |
var lastBlock = scope.modules.blockchain.getLastBlock(); | |
self.log("Network Height:", network.height); | |
self.log("Node Height:", lastBlock.height, network.height>lastBlock.height?colors.red("(not sync)"):colors.green("(in sync)")); | |
callback(); | |
}); | |
self.log("Forging:", scope.modules.delegates.isForging()); | |
self.log("Active Delegate:", scope.modules.delegates.isActiveDelegate()); | |
var peers = scope.modules.peers.listBroadcastPeers(); | |
self.log("Connected Peers:", peers.length); | |
self.log("Mempool size:", scope.modules.transactionPool.getMempoolSize()); | |
}); | |
var tail; | |
vorpal | |
.command('log start', 'Start output logs') | |
.action(function(args, callback) { | |
var self=this; | |
if(tail){ | |
self.log("Already listening to logs"); | |
return callback(); | |
} | |
tail = spawn('tail', ['-f', appConfig.logFileName]); | |
tail.stdout.on('data', function(data) { | |
self.log(data.toString("UTF-8")); | |
}); | |
callback(); | |
}); | |
vorpal | |
.command('log stop', 'Stop output logs') | |
.action(function(args, callback) { | |
var self=this; | |
if(tail){ | |
tail.kill(); | |
tail=null; | |
} | |
callback(); | |
}); | |
vorpal | |
.command('log grep <query>', 'Grep logs with <query>') | |
.action(function(args, callback) { | |
var self=this; | |
var grep = spawn('grep', ['-e', args.query, appConfig.logFileName]); | |
grep.stdout.on('data', function(data) { | |
self.log(data.toString("UTF-8")); | |
}); | |
callback(); | |
}); | |
vorpal | |
.command('update node', 'force update from network') | |
.action(function(args, callback) { | |
var self = this; | |
scope.bus.message("updatePeers"); | |
callback(); | |
}); | |
vorpal | |
.command('sql <query>', 'query database') | |
.action(function(args, callback) { | |
var self = this; | |
scope.db.query(args.query).then(function(rows){ | |
self.log(rows.map(function(row){return JSON.stringify(row)}).join("\n")); | |
callback(); | |
}).catch(function(error){ | |
self.log(error); | |
callback(); | |
}); | |
}); | |
vorpal | |
.command('create account', 'generate a new random account') | |
.action(function(args, callback) { | |
var self = this; | |
var passphrase = require("bip39").generateMnemonic(); | |
self.log("Seed - private:",passphrase); | |
self.log("WIF - private:",require("arkjs").crypto.getKeys(passphrase).toWIF()); | |
self.log("Address - public :",require("arkjs").crypto.getAddress(require("arkjs").crypto.getKeys(passphrase).publicKey)); | |
callback(); | |
}); | |
var account=null; | |
vorpal | |
.mode('account <address>', 'get info of account (balance, vote, username, publicKey etc...)') | |
.delimiter('account>') | |
.init(function(args, callback){ | |
var self=this; | |
scope.db.query("select * from mem_accounts where address ='"+args.address+"'").then(function(rows){ | |
account=rows[0]; | |
self.log('Managing account '+args.address+'. Commands: '+Object.keys(account).join(", ")+'. To exit, type `exit`.'); | |
callback(); | |
}).catch(function(error){ | |
account={}; | |
self.log('Account not found '+args.address+'. To exit, type `exit`.'); | |
callback(); | |
}); | |
}) | |
.action(function(command, callback) { | |
var self = this; | |
this.log(account[command]); | |
callback(); | |
}); | |
vorpal | |
.command('spv fix', 'fix database using SPV on all accounts') | |
.action(function(args, callback) { | |
var self = this; | |
scope.modules.nodeManager.fixDatabase(function(err, results){ | |
if(err) self.log(colors.red(err)); | |
else self.log("Fixed "+results[3].length+" accounts"); | |
callback(); | |
}); | |
}); | |
vorpal | |
.command('spv <address>', 'Perform Simple Payment Verification against the blockchain') | |
.action(function(args, callback) { | |
scope.db.query("select * from mem_accounts where address ='"+args.address+"'").then(function(rows){ | |
var publicKey=rows[0].publicKey.toString("hex"); | |
var receivedSQL='select sum(amount) as total, count(amount) as count from transactions where amount > 0 and "recipientId" = \''+args.address+'\';' | |
var spentSQL='select sum(amount+fee) as total, count(amount) as count from transactions where "senderPublicKey" = \'\\x'+publicKey+'\';' | |
var rewardsSQL='select sum(reward+"totalFee") as total, count(reward) as count from blocks where "generatorPublicKey" = \'\\x'+publicKey+'\';' | |
async.series({ | |
received: function(cb){ | |
scope.db.query(receivedSQL).then(function(rows){ | |
cb(null, rows[0]); | |
}); | |
}, | |
spent: function(cb){ | |
scope.db.query(spentSQL).then(function(rows){ | |
cb(null, rows[0]); | |
}); | |
}, | |
rewards: function(cb){ | |
scope.db.query(rewardsSQL).then(function(rows){ | |
cb(null, rows[0]); | |
}); | |
} | |
}, function(err, result){ | |
result.balance = parseInt(result.received.total||0) - parseInt(result.spent.total||0) + parseInt(result.rewards.total||0); | |
result.numberOfTransactions = parseInt(result.received.count||0) + parseInt(result.spent.count||0) | |
result.forgedBlocks =parseInt(result.rewards.count||0); | |
self.log(JSON.stringify(result)); | |
}); | |
callback(); | |
}).catch(function(error){ | |
self.log('Account not found '+args.address); | |
callback(); | |
}); | |
var self = this; | |
}); | |
vorpal.history('ark-node'); | |
vorpal | |
.delimiter('ark-node>') | |
.show(); | |
} | |
/** VEKEXASIA CODE START **/ | |
const genesisDelegates = require('./test/delegatesPassphrases.json'); | |
const crypto = require('crypto'); | |
const slots = require('./helpers/slots'); | |
const BigNumber = require('bignumber.js'); | |
const blockTime = 10; | |
async function genRandomSendTx(scope) { | |
// Create tx | |
const { delegates } = await toPromise(cback => scope.modules.delegates.getDelegates({}, cback)); | |
const choosenOne = delegates[Math.random() * delegates.length | 0]; | |
const keypair = delegateKeypair(choosenOne.publicKey, scope.crypto) | |
const votes = []; | |
const tx = scope.logic.transaction.create({ | |
type : 0, // Vote | |
sender : { | |
publicKey: choosenOne.publicKey, | |
address : choosenOne.address | |
}, | |
amount : Math.random() * 100 | 0, | |
recipientId: choosenOne.address, | |
timestamp : scope.modules.blocks.getLastBlock().timestamp, | |
keypair, | |
votes | |
}); | |
return tx; | |
} | |
async function processTx(tx, scope) { | |
await toPromise(cback => scope.modules.nodeManager.onTransactionsReceived([tx], 'api', cback)); | |
await toPromise(cback => scope.modules.transactionPool.fillPool(constants.maxTxsPerBlock, cback)); | |
} | |
async function onInit(scope, modules) { | |
console.log('onInit'); | |
try { | |
await wait(5000); | |
// I manually forge N blocks till endOfRound - 2 | |
// await genBlocksTill2BeforeEndOfRound(scope); | |
for (let i = 0; i < 1000; i++) { | |
const tx = await genRandomSendTx(scope); | |
// Process tx | |
await processTx(tx, scope); | |
const previousBlock = scope.modules.blocks.getLastBlock(); | |
// Gen 1 block | |
await genBlocks(1, scope); | |
const lastBlock = scope.modules.blocks.getLastBlock(); | |
// Verify last block have such tx | |
if (lastBlock.transactions.length !== 1) { | |
console.error('Something is going on last block; should contain only 1 tx', lastBlock.transactions.length); | |
process.exit(1); | |
} | |
// craft a new block simulating a double forger. | |
// and hope it has a lower id. | |
let keypair = delegateKeypair(lastBlock.generatorPublicKey, scope.crypto); | |
let block = scope.logic.block.create({ | |
keypair, | |
timestamp : lastBlock.timestamp, | |
previousBlock, | |
transactions: [], | |
height : lastBlock.height, | |
}); | |
// block.blockSignature = scope.logic.block.sign(block, keypair); | |
// block = scope.logic.block.objectNormalize(block); | |
if (block.id < lastBlock.id) { | |
// We've found a valid block simulating a double forging that has lower id. | |
await toPromise(cb => scope.bus.message('blockReceived', block, { string: '127.0.0.1' }, cb)); | |
// Verify that new block is now picked as lastblock | |
if (scope.modules.blocks.getLastBlock().id !== block.id) { | |
console.log('Didnt replace old block with new one'); | |
process.exit(1); | |
} | |
// verify that a new generated block does not pick up tx. | |
await genBlocks(1, scope); | |
if (scope.modules.blocks.getLastBlock().transactions.length === 0) { | |
console.log('BUG: Transaction has disappeared!'); | |
// We may want to check if it's in the pool just | |
if (scope.modules.transactionPool.transactionInPool(tx.id)) { | |
console.log('BUT tx is in pool'); | |
} else { | |
console.log('yup. It disappeared.', tx.id) | |
} | |
process.exit(1); | |
} | |
} | |
} | |
process.exit(1); | |
} catch (e) { | |
console.log(e); | |
console.log(e.stack); | |
} | |
} | |
async function getDelegateInFuture(scope, blocksInFuture = 1) { | |
const lastBlock = scope.modules.blocks.getLastBlock(); | |
const slot = slots.getSlotNumber(lastBlock.timestamp + constants.blocktime * blocksInFuture); | |
const height = lastBlock.height + blocksInFuture; | |
const d = await toPromise(cb => scope.modules.rounds.getActiveDelegates(cb)); | |
// const delegates = await toPromise(cb => scope.modules.delegates.generateDelegateList(height, cb)); | |
return d[slot % slots.delegates]; | |
} | |
/** | |
* Generates n blocks as they would've been forged from this node. | |
* @param n | |
* @returns {Promise.<*>} | |
*/ | |
async function genBlocks(n, scope) { | |
for (let i = 0; i < n; i++) { | |
const lastBlock = scope.modules.blocks.getLastBlock(); | |
const slot = slots.getSlotNumber(lastBlock.timestamp + constants.blocktime); | |
const delegate = await getDelegateInFuture(scope, 1); | |
// console.log('DElegate', delegate, await getDelegateInFuture(scope)); | |
const keyPair = delegateKeypair(delegate, scope.crypto); | |
const block = await toPromise(cback => scope.modules.blocks.generateBlock( | |
keyPair, | |
slots.getSlotTime(slot), | |
cback | |
) | |
); | |
await toPromise(cb => scope.bus.message('blockForged', block, cb)); | |
} | |
return scope.modules.blocks.getLastBlock(); | |
} | |
/** | |
* Gets delegate secret from a pubKEy | |
* @param pubKey | |
*/ | |
function delegateSecret(pubKey) { | |
const delegate = genesisDelegates.filter(d => d.publicKey === pubKey)[0]; | |
return delegate.passphrase; | |
} | |
/** | |
* get Delegate keypair from pub/priv Key | |
* @param pubKey | |
* @param ed | |
* @returns {{publicKey, privateKey}} | |
*/ | |
function delegateKeypair(pubKey, crypto) { | |
return crypto.makeKeypair(delegateSecret(pubKey)); | |
} | |
/** | |
* Just waits for some time. | |
* @param ms | |
* @returns {Promise} | |
*/ | |
function wait(ms) { | |
return new Promise(resolve => setTimeout(resolve, ms)); | |
} | |
/** | |
* A simple (stupid) way to change callback-style fns to promise style. | |
* @param callbackkable | |
* @returns {Promise} | |
*/ | |
function toPromise(callbackkable) { | |
return new Promise((resolve, reject) => { | |
callbackkable((err, res) => { | |
if (err) { | |
return reject(err); | |
} | |
return resolve(res); | |
}); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment