From 59a4f28196ad6e93fe5d63c4ccc40e8e606bc527 Mon Sep 17 00:00:00 2001 From: "James D. Wilson" Date: Thu, 11 Jan 2018 21:16:12 -0600 Subject: [PATCH] Upgraded Byteball to es6 using lebab --- archiving.js | 35 +- balances.js | 137 ++-- bots.js | 31 +- breadcrumbs.js | 8 +- catchup.js | 211 +++--- chash.js | 105 ++- chat_storage.js | 7 +- check_daemon.js | 29 +- composer.js | 468 ++++++------ conf.js | 23 +- constants.js | 2 - db.js | 19 +- definition.js | 489 ++++++------ desktop_app.js | 25 +- device.js | 344 +++++---- divisible_asset.js | 234 +++--- enforce_singleton.js | 2 - event_bus.js | 5 +- graph.js | 155 ++-- headers_commission.js | 175 +++-- indivisible_asset.js | 547 +++++++------- joint_storage.js | 203 +++-- light.js | 383 +++++----- light_wallet.js | 87 ++- mail.js | 49 +- main_chain.js | 452 ++++++------ mc_outputs.js | 97 ++- merkle.js | 43 +- mutex.js | 41 +- my_witnesses.js | 37 +- mysql_pool.js | 91 +-- network.js | 1273 ++++++++++++++++---------------- object_hash.js | 33 +- object_length.js | 23 +- paid_witnessing.js | 106 +-- parent_composer.js | 191 ++--- private_payment.js | 65 +- profiler.js | 62 +- signature.js | 13 +- sqlite_migrations.js | 29 +- sqlite_pool.js | 245 +++--- storage.js | 564 +++++++------- string_utils.js | 22 +- test/string_utils.test.js | 4 +- test/validation_utils.test.js | 2 +- uri.js | 67 +- validation.js | 783 ++++++++++---------- validation_utils.js | 7 +- wallet.js | 938 +++++++++++------------ wallet_defined_by_addresses.js | 320 ++++---- wallet_defined_by_keys.js | 417 ++++++----- wallet_general.js | 27 +- witness_proof.js | 161 ++-- writer.js | 257 ++++--- 54 files changed, 5058 insertions(+), 5085 deletions(-) diff --git a/archiving.js b/archiving.js index 3ee2b2f5..5f0fee12 100644 --- a/archiving.js +++ b/archiving.js @@ -1,19 +1,18 @@ /*jslint node: true */ -"use strict"; -var db = require('./db.js'); +const db = require('./db.js'); function generateQueriesToArchiveJoint(conn, objJoint, reason, arrQueries, cb){ - var func = (reason === 'uncovered') ? generateQueriesToRemoveJoint : generateQueriesToVoidJoint; - func(conn, objJoint.unit.unit, arrQueries, function(){ - conn.addQuery(arrQueries, "INSERT "+conn.getIgnore()+" INTO archived_joints (unit, reason, json) VALUES (?,?,?)", + const func = (reason === 'uncovered') ? generateQueriesToRemoveJoint : generateQueriesToVoidJoint; + func(conn, objJoint.unit.unit, arrQueries, () => { + conn.addQuery(arrQueries, `INSERT ${conn.getIgnore()} INTO archived_joints (unit, reason, json) VALUES (?,?,?)`, [objJoint.unit.unit, reason, JSON.stringify(objJoint)]); cb(); }); } function generateQueriesToRemoveJoint(conn, unit, arrQueries, cb){ - generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){ + generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, () => { conn.addQuery(arrQueries, "DELETE FROM sent_mnemonics WHERE unit=?", [unit]); conn.addQuery(arrQueries, "DELETE FROM witness_list_hashes WHERE witness_list_unit=?", [unit]); conn.addQuery(arrQueries, "DELETE FROM earned_headers_commission_recipients WHERE unit=?", [unit]); @@ -41,7 +40,7 @@ function generateQueriesToRemoveJoint(conn, unit, arrQueries, cb){ } function generateQueriesToVoidJoint(conn, unit, arrQueries, cb){ - generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){ + generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, () => { // we keep witnesses, author addresses, and the unit itself conn.addQuery(arrQueries, "DELETE FROM witness_list_hashes WHERE witness_list_unit=?", [unit]); conn.addQuery(arrQueries, "DELETE FROM earned_headers_commission_recipients WHERE unit=?", [unit]); @@ -65,8 +64,8 @@ function generateQueriesToVoidJoint(conn, unit, arrQueries, cb){ } function generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){ - generateQueriesToUnspendTransferOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){ - generateQueriesToUnspendHeadersCommissionOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){ + generateQueriesToUnspendTransferOutputsSpentInArchivedUnit(conn, unit, arrQueries, () => { + generateQueriesToUnspendHeadersCommissionOutputsSpentInArchivedUnit(conn, unit, arrQueries, () => { generateQueriesToUnspendWitnessingOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb); }); }); @@ -87,12 +86,12 @@ function generateQueriesToUnspendTransferOutputsSpentInArchivedUnit(conn, unit, AND inputs.unit!=alt_inputs.unit \n\ )", [unit], - function(rows){ - rows.forEach(function(row){ + rows => { + rows.forEach(({src_unit, src_message_index, src_output_index}) => { conn.addQuery( arrQueries, "UPDATE outputs SET is_spent=0 WHERE unit=? AND message_index=? AND output_index=?", - [row.src_unit, row.src_message_index, row.src_output_index] + [src_unit, src_message_index, src_output_index] ); }); cb(); @@ -119,12 +118,12 @@ function generateQueriesToUnspendHeadersCommissionOutputsSpentInArchivedUnit(con AND inputs.unit!=alt_inputs.unit \n\ )", [unit], - function(rows){ - rows.forEach(function(row){ + rows => { + rows.forEach(({address, main_chain_index}) => { conn.addQuery( arrQueries, "UPDATE headers_commission_outputs SET is_spent=0 WHERE address=? AND main_chain_index=?", - [row.address, row.main_chain_index] + [address, main_chain_index] ); }); cb(); @@ -151,12 +150,12 @@ function generateQueriesToUnspendWitnessingOutputsSpentInArchivedUnit(conn, unit AND inputs.unit!=alt_inputs.unit \n\ )", [unit], - function(rows){ - rows.forEach(function(row){ + rows => { + rows.forEach(({address, main_chain_index}) => { conn.addQuery( arrQueries, "UPDATE witnessing_outputs SET is_spent=0 WHERE address=? AND main_chain_index=?", - [row.address, row.main_chain_index] + [address, main_chain_index] ); }); cb(); diff --git a/balances.js b/balances.js index d8b2b8f6..45ae35eb 100644 --- a/balances.js +++ b/balances.js @@ -1,53 +1,52 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var constants = require('./constants.js'); -var db = require('./db'); +const _ = require('lodash'); +const constants = require('./constants.js'); +const db = require('./db'); function readBalance(wallet, handleBalance){ - var walletIsAddress = typeof wallet === 'string' && wallet.length === 32; // ValidationUtils.isValidAddress - var join_my_addresses = walletIsAddress ? "" : "JOIN my_addresses USING(address)"; - var where_condition = walletIsAddress ? "address=?" : "wallet=?"; - var assocBalances = {base: {stable: 0, pending: 0}}; + const walletIsAddress = typeof wallet === 'string' && wallet.length === 32; // ValidationUtils.isValidAddress + const join_my_addresses = walletIsAddress ? "" : "JOIN my_addresses USING(address)"; + const where_condition = walletIsAddress ? "address=?" : "wallet=?"; + const assocBalances = {base: {stable: 0, pending: 0}}; assocBalances[constants.BLACKBYTES_ASSET] = {is_private: 1, stable: 0, pending: 0}; db.query( - "SELECT asset, is_stable, SUM(amount) AS balance \n\ - FROM outputs "+join_my_addresses+" CROSS JOIN units USING(unit) \n\ - WHERE is_spent=0 AND "+where_condition+" AND sequence='good' \n\ - GROUP BY asset, is_stable", + `SELECT asset, is_stable, SUM(amount) AS balance \n\ + FROM outputs ${join_my_addresses} CROSS JOIN units USING(unit) \n\ + WHERE is_spent=0 AND ${where_condition} AND sequence='good' \n\ + GROUP BY asset, is_stable`, [wallet], - function(rows){ - for (var i=0; i { + for (let i=0; i { if(rows.length){ assocBalances["base"]["stable"] += rows[0].total; } // add 0-balance assets db.query( - "SELECT DISTINCT outputs.asset, is_private \n\ - FROM outputs "+join_my_addresses+" \n\ - CROSS JOIN units USING(unit) \n\ - LEFT JOIN assets ON outputs.asset=assets.unit \n\ - WHERE "+where_condition+" AND sequence='good'", + `SELECT DISTINCT outputs.asset, is_private \n\ + FROM outputs ${join_my_addresses} \n\ + CROSS JOIN units USING(unit) \n\ + LEFT JOIN assets ON outputs.asset=assets.unit \n\ + WHERE ${where_condition} AND sequence='good'`, [wallet], - function(rows){ - for (var i=0; i { + for (let i=0; i { + for (let i=0; i { + const arrSharedAddresses = rows.map(({shared_address}) => shared_address); if (arrSharedAddresses.length === 0) return handleSharedAddresses([]); - readSharedAddressesDependingOnAddresses(arrSharedAddresses, function(arrNewSharedAddresses){ + readSharedAddressesDependingOnAddresses(arrSharedAddresses, arrNewSharedAddresses => { handleSharedAddresses(arrSharedAddresses.concat(arrNewSharedAddresses)); }); }); } function readSharedAddressesDependingOnAddresses(arrMemberAddresses, handleSharedAddresses){ - var strAddressList = arrMemberAddresses.map(db.escape).join(', '); - db.query("SELECT DISTINCT shared_address FROM shared_address_signing_paths WHERE address IN("+strAddressList+")", function(rows){ - var arrSharedAddresses = rows.map(function(row){ return row.shared_address; }); + const strAddressList = arrMemberAddresses.map(db.escape).join(', '); + db.query(`SELECT DISTINCT shared_address FROM shared_address_signing_paths WHERE address IN(${strAddressList})`, rows => { + const arrSharedAddresses = rows.map(({shared_address}) => shared_address); if (arrSharedAddresses.length === 0) return handleSharedAddresses([]); - var arrNewMemberAddresses = _.difference(arrSharedAddresses, arrMemberAddresses); + const arrNewMemberAddresses = _.difference(arrSharedAddresses, arrMemberAddresses); if (arrNewMemberAddresses.length === 0) return handleSharedAddresses([]); - readSharedAddressesDependingOnAddresses(arrNewMemberAddresses, function(arrNewSharedAddresses){ + readSharedAddressesDependingOnAddresses(arrNewMemberAddresses, arrNewSharedAddresses => { handleSharedAddresses(arrNewMemberAddresses.concat(arrNewSharedAddresses)); }); }); } function readSharedBalance(wallet, handleBalance){ - var assocBalances = {}; - readSharedAddressesOnWallet(wallet, function(arrSharedAddresses){ + const assocBalances = {}; + readSharedAddressesOnWallet(wallet, arrSharedAddresses => { if (arrSharedAddresses.length === 0) return handleBalance(assocBalances); - var strAddressList = arrSharedAddresses.map(db.escape).join(', '); + const strAddressList = arrSharedAddresses.map(db.escape).join(', '); db.query( - "SELECT asset, address, is_stable, SUM(amount) AS balance \n\ - FROM outputs CROSS JOIN units USING(unit) \n\ - WHERE is_spent=0 AND sequence='good' AND address IN("+strAddressList+") \n\ - GROUP BY asset, address, is_stable \n\ - UNION ALL \n\ - SELECT NULL AS asset, address, 1 AS is_stable, SUM(amount) AS balance FROM witnessing_outputs \n\ - WHERE is_spent=0 AND address IN("+strAddressList+") GROUP BY address \n\ - UNION ALL \n\ - SELECT NULL AS asset, address, 1 AS is_stable, SUM(amount) AS balance FROM headers_commission_outputs \n\ - WHERE is_spent=0 AND address IN("+strAddressList+") GROUP BY address", - function(rows){ - for (var i=0; i { + for (let i=0; i { if (err != null) { return cb(err, null); } async.eachSeries(bots, - function(bot, cb) { - setPairingStatus(bot, function(handled_bot){ - bot.isPaired = handled_bot.isPaired; + (bot, cb) => { + setPairingStatus(bot, ({isPaired}) => { + bot.isPaired = isPaired; cb(); }) }, - function(){ + () => { bots_cache = bots; cb(err, bots); } @@ -37,10 +36,10 @@ function load(cb) { } function setPairingStatus(bot, cb) { - var pubkey = bot.pairing_code.substr(0, bot.pairing_code.indexOf('@')); + const pubkey = bot.pairing_code.substr(0, bot.pairing_code.indexOf('@')); bot.device_address = objectHash.getDeviceAddress(pubkey); - db.query("SELECT 1 FROM correspondent_devices WHERE device_address = ?", [bot.device_address], function(rows){ - bot.isPaired = (rows.length == 1); + db.query("SELECT 1 FROM correspondent_devices WHERE device_address = ?", [bot.device_address], ({length}) => { + bot.isPaired = (length == 1); cb(bot); }); } diff --git a/breadcrumbs.js b/breadcrumbs.js index 9801bca5..70d18f4b 100644 --- a/breadcrumbs.js +++ b/breadcrumbs.js @@ -1,18 +1,16 @@ /*jslint node: true */ -'use strict'; - /* Used for debugging long sequences of calls not captured by stack traces. Should be included with bug reports. */ -var MAX_LENGTH = 200; -var arrBreadcrumbs = []; +const MAX_LENGTH = 200; +const arrBreadcrumbs = []; function add(breadcrumb){ if (arrBreadcrumbs.length > MAX_LENGTH) arrBreadcrumbs.shift(); // forget the oldest breadcrumbs - arrBreadcrumbs.push(Date().toString() + ': ' + breadcrumb); + arrBreadcrumbs.push(`${Date().toString()}: ${breadcrumb}`); console.log(breadcrumb); } diff --git a/catchup.js b/catchup.js index 43e09b2c..a29bd9e5 100644 --- a/catchup.js +++ b/catchup.js @@ -1,22 +1,21 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var _ = require('lodash'); -var storage = require('./storage.js'); -var objectHash = require("./object_hash.js"); -var db = require('./db.js'); -var mutex = require('./mutex.js'); -var validation = require('./validation.js'); -var witnessProof = require('./witness_proof.js'); +const async = require('async'); +const _ = require('lodash'); +const storage = require('./storage.js'); +const objectHash = require("./object_hash.js"); +const db = require('./db.js'); +const mutex = require('./mutex.js'); +const validation = require('./validation.js'); +const witnessProof = require('./witness_proof.js'); function prepareCatchupChain(catchupRequest, callbacks){ if (!catchupRequest) return callbacks.ifError("no catchup request"); - var last_stable_mci = catchupRequest.last_stable_mci; - var last_known_mci = catchupRequest.last_known_mci; - var arrWitnesses = catchupRequest.witnesses; + const last_stable_mci = catchupRequest.last_stable_mci; + const last_known_mci = catchupRequest.last_known_mci; + const arrWitnesses = catchupRequest.witnesses; if (typeof last_stable_mci !== "number") return callbacks.ifError("no last_stable_mci"); @@ -27,17 +26,17 @@ function prepareCatchupChain(catchupRequest, callbacks){ if (!Array.isArray(arrWitnesses)) return callbacks.ifError("no witnesses"); - mutex.lock(['prepareCatchupChain'], function(unlock){ - var start_ts = Date.now(); - var objCatchupChain = { + mutex.lock(['prepareCatchupChain'], unlock => { + const start_ts = Date.now(); + const objCatchupChain = { unstable_mc_joints: [], stable_last_ball_joints: [], witness_change_and_definition_joints: [] }; - var last_ball_unit = null; + let last_ball_unit = null; async.series([ - function(cb){ // check if the peer really needs hash trees - db.query("SELECT is_stable FROM units WHERE is_on_main_chain=1 AND main_chain_index=?", [last_known_mci], function(rows){ + cb => { // check if the peer really needs hash trees + db.query("SELECT is_stable FROM units WHERE is_on_main_chain=1 AND main_chain_index=?", [last_known_mci], rows => { if (rows.length === 0) return cb("already_current"); if (rows[0].is_stable === 0) @@ -45,10 +44,16 @@ function prepareCatchupChain(catchupRequest, callbacks){ cb(); }); }, - function(cb){ + cb => { witnessProof.prepareWitnessProof( arrWitnesses, last_stable_mci, - function(err, arrUnstableMcJoints, arrWitnessChangeAndDefinitionJoints, _last_ball_unit, _last_ball_mci){ + ( + err, + arrUnstableMcJoints, + arrWitnessChangeAndDefinitionJoints, + _last_ball_unit, + _last_ball_mci + ) => { if (err) return cb(err); objCatchupChain.unstable_mc_joints = arrUnstableMcJoints; @@ -59,28 +64,28 @@ function prepareCatchupChain(catchupRequest, callbacks){ } ); }, - function(cb){ // jump by last_ball references until we land on or behind last_stable_mci + cb => { // jump by last_ball references until we land on or behind last_stable_mci if (!last_ball_unit) return cb(); goUp(last_ball_unit); function goUp(unit){ - storage.readJointWithBall(db, unit, function(objJoint){ + storage.readJointWithBall(db, unit, objJoint => { objCatchupChain.stable_last_ball_joints.push(objJoint); - storage.readUnitProps(db, unit, function(objUnitProps){ - (objUnitProps.main_chain_index <= last_stable_mci) ? cb() : goUp(objJoint.unit.last_ball_unit); + storage.readUnitProps(db, unit, ({main_chain_index}) => { + (main_chain_index <= last_stable_mci) ? cb() : goUp(objJoint.unit.last_ball_unit); }); }); } } - ], function(err){ + ], err => { if (err === "already_current") callbacks.ifOk({status: "current"}); else if (err) callbacks.ifError(err); else callbacks.ifOk(objCatchupChain); - console.log("prepareCatchupChain since mci "+last_stable_mci+" took "+(Date.now()-start_ts)+'ms'); + console.log(`prepareCatchupChain since mci ${last_stable_mci} took ${Date.now()-start_ts}ms`); unlock(); }); }); @@ -104,25 +109,25 @@ function processCatchupChain(catchupChain, peer, callbacks){ witnessProof.processWitnessProof( catchupChain.unstable_mc_joints, catchupChain.witness_change_and_definition_joints, true, - function(err, arrLastBallUnits, assocLastBallByLastBallUnit){ + (err, arrLastBallUnits, assocLastBallByLastBallUnit) => { if (err) return callbacks.ifError(err); - var objFirstStableJoint = catchupChain.stable_last_ball_joints[0]; - var objFirstStableUnit = objFirstStableJoint.unit; + const objFirstStableJoint = catchupChain.stable_last_ball_joints[0]; + const objFirstStableUnit = objFirstStableJoint.unit; if (arrLastBallUnits.indexOf(objFirstStableUnit.unit) === -1) return callbacks.ifError("first stable unit is not last ball unit of any unstable unit"); - var last_ball_unit = objFirstStableUnit.unit; - var last_ball = assocLastBallByLastBallUnit[last_ball_unit]; + let last_ball_unit = objFirstStableUnit.unit; + let last_ball = assocLastBallByLastBallUnit[last_ball_unit]; if (objFirstStableJoint.ball !== last_ball) - return callbacks.ifError("last ball and last ball unit do not match: "+objFirstStableJoint.ball+"!=="+last_ball); + return callbacks.ifError(`last ball and last ball unit do not match: ${objFirstStableJoint.ball}!==${last_ball}`); // stable joints - var arrChainBalls = []; - for (var i=0; i { + mutex.lock(["catchup_chain"], _unlock => { unlock = _unlock; - db.query("SELECT 1 FROM catchup_chain_balls LIMIT 1", function(rows){ - (rows.length > 0) ? cb("duplicate") : cb(); + db.query("SELECT 1 FROM catchup_chain_balls LIMIT 1", ({length}) => { + (length > 0) ? cb("duplicate") : cb(); }); }); }, - function(cb){ // adjust first chain ball if necessary and make sure it is the only stable unit in the entire chain + cb => { // adjust first chain ball if necessary and make sure it is the only stable unit in the entire chain db.query( "SELECT is_stable, is_on_main_chain, main_chain_index FROM balls JOIN units USING(unit) WHERE ball=?", [arrChainBalls[0]], - function(rows){ + rows => { if (rows.length === 0){ if (storage.isGenesisBall(arrChainBalls[0])) return cb(); - return cb("first chain ball "+arrChainBalls[0]+" is not known"); + return cb(`first chain ball ${arrChainBalls[0]} is not known`); } - var objFirstChainBallProps = rows[0]; + const objFirstChainBallProps = rows[0]; if (objFirstChainBallProps.is_stable !== 1) - return cb("first chain ball "+arrChainBalls[0]+" is not stable"); + return cb(`first chain ball ${arrChainBalls[0]} is not stable`); if (objFirstChainBallProps.is_on_main_chain !== 1) - return cb("first chain ball "+arrChainBalls[0]+" is not on mc"); - storage.readLastStableMcUnitProps(db, function(objLastStableMcUnitProps){ - var last_stable_mci = objLastStableMcUnitProps.main_chain_index; + return cb(`first chain ball ${arrChainBalls[0]} is not on mc`); + storage.readLastStableMcUnitProps(db, ({main_chain_index, ball}) => { + const last_stable_mci = main_chain_index; if (objFirstChainBallProps.main_chain_index > last_stable_mci) // duplicate check - return cb("first chain ball "+arrChainBalls[0]+" mci is too large"); + return cb(`first chain ball ${arrChainBalls[0]} mci is too large`); if (objFirstChainBallProps.main_chain_index === last_stable_mci) // exact match return cb(); - arrChainBalls[0] = objLastStableMcUnitProps.ball; // replace to avoid receiving duplicates + arrChainBalls[0] = ball; // replace to avoid receiving duplicates if (!arrChainBalls[1]) return cb(); - db.query("SELECT is_stable FROM balls JOIN units USING(unit) WHERE ball=?", [arrChainBalls[1]], function(rows2){ + db.query("SELECT is_stable FROM balls JOIN units USING(unit) WHERE ball=?", [arrChainBalls[1]], rows2 => { if (rows2.length === 0) return cb(); - var objSecondChainBallProps = rows2[0]; + const objSecondChainBallProps = rows2[0]; if (objSecondChainBallProps.is_stable === 1) - return cb("second chain ball "+arrChainBalls[1]+" must not be stable"); + return cb(`second chain ball ${arrChainBalls[1]} must not be stable`); cb(); }); }); } ); }, - function(cb){ // validation complete, now write the chain for future downloading of hash trees - var arrValues = arrChainBalls.map(function(ball){ return "("+db.escape(ball)+")"; }); - db.query("INSERT INTO catchup_chain_balls (ball) VALUES "+arrValues.join(', '), function(){ + cb => { // validation complete, now write the chain for future downloading of hash trees + const arrValues = arrChainBalls.map(ball => `(${db.escape(ball)})`); + db.query(`INSERT INTO catchup_chain_balls (ball) VALUES ${arrValues.join(', ')}`, () => { cb(); }); } - ], function(err){ + ], err => { unlock(); err ? callbacks.ifError(err) : callbacks.ifOk(); }); @@ -204,22 +209,22 @@ function processCatchupChain(catchupChain, peer, callbacks){ function readHashTree(hashTreeRequest, callbacks){ if (!hashTreeRequest) return callbacks.ifError("no hash tree request"); - var from_ball = hashTreeRequest.from_ball; - var to_ball = hashTreeRequest.to_ball; + const from_ball = hashTreeRequest.from_ball; + const to_ball = hashTreeRequest.to_ball; if (typeof from_ball !== 'string') return callbacks.ifError("no from_ball"); if (typeof to_ball !== 'string') return callbacks.ifError("no to_ball"); - var from_mci; - var to_mci; + let from_mci; + let to_mci; db.query( "SELECT is_stable, is_on_main_chain, main_chain_index, ball FROM balls JOIN units USING(unit) WHERE ball IN(?,?)", [from_ball, to_ball], - function(rows){ + rows => { if (rows.length !== 2) return callbacks.ifError("some balls not found"); - for (var i=0; i= to_mci) return callbacks.ifError("from is after to"); - var arrBalls = []; - var op = (from_mci === 0) ? ">=" : ">"; // if starting from 0, add genesis itself + const arrBalls = []; + const op = (from_mci === 0) ? ">=" : ">"; // if starting from 0, add genesis itself db.query( - "SELECT unit, ball, content_hash FROM units LEFT JOIN balls USING(unit) \n\ - WHERE main_chain_index "+op+" ? AND main_chain_index<=? ORDER BY main_chain_index, `level`", + `SELECT unit, ball, content_hash FROM units LEFT JOIN balls USING(unit) \n\ + WHERE main_chain_index ${op} ? AND main_chain_index<=? ORDER BY main_chain_index, \`level\``, [from_mci, to_mci], - function(ball_rows){ + ball_rows => { async.eachSeries( ball_rows, - function(objBall, cb){ + (objBall, cb) => { if (!objBall.ball) - throw Error("no ball for unit "+objBall.unit); + throw Error(`no ball for unit ${objBall.unit}`); if (objBall.content_hash) objBall.is_nonserial = true; delete objBall.content_hash; db.query( "SELECT ball FROM parenthoods LEFT JOIN balls ON parent_unit=balls.unit WHERE child_unit=? ORDER BY ball", [objBall.unit], - function(parent_rows){ - if (parent_rows.some(function(parent_row){ return !parent_row.ball; })) + parent_rows => { + if (parent_rows.some(({ball}) => !ball)) throw Error("some parents have no balls"); if (parent_rows.length > 0) - objBall.parent_balls = parent_rows.map(function(parent_row){ return parent_row.ball; }); + objBall.parent_balls = parent_rows.map(({ball}) => ball); db.query( "SELECT ball FROM skiplist_units LEFT JOIN balls ON skiplist_unit=balls.unit WHERE skiplist_units.unit=? ORDER BY ball", [objBall.unit], - function(srows){ - if (srows.some(function(srow){ return !srow.ball; })) + srows => { + if (srows.some(({ball}) => !ball)) throw Error("some skiplist units have no balls"); if (srows.length > 0) - objBall.skiplist_balls = srows.map(function(srow){ return srow.ball; }); + objBall.skiplist_balls = srows.map(({ball}) => ball); arrBalls.push(objBall); cb(); } @@ -269,7 +274,7 @@ function readHashTree(hashTreeRequest, callbacks){ } ); }, - function(){ + () => { callbacks.ifOk(arrBalls); } ); @@ -282,20 +287,20 @@ function readHashTree(hashTreeRequest, callbacks){ function processHashTree(arrBalls, callbacks){ if (!Array.isArray(arrBalls)) return callbacks.ifError("no balls array"); - mutex.lock(["hash_tree"], function(unlock){ + mutex.lock(["hash_tree"], unlock => { - db.query("SELECT 1 FROM hash_tree_balls LIMIT 1", function(ht_rows){ + db.query("SELECT 1 FROM hash_tree_balls LIMIT 1", ht_rows => { //if (ht_rows.length > 0) // duplicate // return unlock(); - db.takeConnectionFromPool(function(conn){ + db.takeConnectionFromPool(conn => { - conn.query("BEGIN", function(){ + conn.query("BEGIN", () => { - var max_mci = null; + let max_mci = null; async.eachSeries( arrBalls, - function(objBall, cb){ + (objBall, cb) => { if (typeof objBall.ball !== "string") return cb("no ball"); if (typeof objBall.unit !== "string") @@ -307,11 +312,11 @@ function processHashTree(arrBalls, callbacks){ else if (objBall.parent_balls) return cb("genesis with parents?"); if (objBall.ball !== objectHash.getBallHash(objBall.unit, objBall.parent_balls, objBall.skiplist_balls, objBall.is_nonserial)) - return cb("wrong ball hash, ball "+objBall.ball+", unit "+objBall.unit); + return cb(`wrong ball hash, ball ${objBall.ball}, unit ${objBall.unit}`); function addBall(){ // insert even if it already exists in balls, because we need to define max_mci by looking outside this hash tree - conn.query("INSERT "+conn.getIgnore()+" INTO hash_tree_balls (ball, unit) VALUES(?,?)", [objBall.ball, objBall.unit], function(){ + conn.query(`INSERT ${conn.getIgnore()} INTO hash_tree_balls (ball, unit) VALUES(?,?)`, [objBall.ball, objBall.unit], () => { cb(); //console.log("inserted unit "+objBall.unit, objBall.ball); }); @@ -323,8 +328,8 @@ function processHashTree(arrBalls, callbacks){ conn.query( "SELECT ball FROM hash_tree_balls WHERE ball IN(?) UNION SELECT ball FROM balls WHERE ball IN(?)", [objBall.skiplist_balls, objBall.skiplist_balls], - function(rows){ - if (rows.length !== objBall.skiplist_balls.length) + ({length}) => { + if (length !== objBall.skiplist_balls.length) return cb("some skiplist balls not found"); addBall(); } @@ -333,20 +338,20 @@ function processHashTree(arrBalls, callbacks){ if (!objBall.parent_balls) return checkSkiplistBallsExist(); - conn.query("SELECT ball FROM hash_tree_balls WHERE ball IN(?)", [objBall.parent_balls], function(rows){ + conn.query("SELECT ball FROM hash_tree_balls WHERE ball IN(?)", [objBall.parent_balls], rows => { //console.log(rows.length+" rows", objBall.parent_balls); if (rows.length === objBall.parent_balls.length) return checkSkiplistBallsExist(); - var arrFoundBalls = rows.map(function(row) { return row.ball; }); - var arrMissingBalls = _.difference(objBall.parent_balls, arrFoundBalls); + const arrFoundBalls = rows.map(({ball}) => ball); + const arrMissingBalls = _.difference(objBall.parent_balls, arrFoundBalls); conn.query( "SELECT ball, main_chain_index, is_on_main_chain FROM balls JOIN units USING(unit) WHERE ball IN(?)", [arrMissingBalls], - function(rows2){ + rows2 => { if (rows2.length !== arrMissingBalls.length) - return cb("some parents not found, unit "+objBall.unit); - for (var i=0; i max_mci || max_mci === null)) max_mci = props.main_chain_index; } @@ -355,10 +360,10 @@ function processHashTree(arrBalls, callbacks){ ); }); }, - function(error){ + error => { function finish(err){ - conn.query(err ? "ROLLBACK" : "COMMIT", function(){ + conn.query(err ? "ROLLBACK" : "COMMIT", () => { conn.release(); unlock(); err ? callbacks.ifError(err) : callbacks.ifOk(); @@ -377,7 +382,7 @@ function processHashTree(arrBalls, callbacks){ "SELECT ball, main_chain_index \n\ FROM catchup_chain_balls LEFT JOIN balls USING(ball) LEFT JOIN units USING(unit) \n\ ORDER BY member_index LIMIT 2", - function(rows){ + rows => { if (rows.length !== 2) return finish("expecting to have 2 elements in the chain"); @@ -387,7 +392,7 @@ function processHashTree(arrBalls, callbacks){ if (rows[1].ball !== arrBalls[arrBalls.length-1].ball) return finish("tree root doesn't match second chain element"); // remove the last chain element, we now have hash tree instead - conn.query("DELETE FROM catchup_chain_balls WHERE ball=?", [rows[0].ball], function(){ + conn.query("DELETE FROM catchup_chain_balls WHERE ball=?", [rows[0].ball], () => { purgeHandledBallsFromHashTree(conn, finish); }); @@ -402,11 +407,11 @@ function processHashTree(arrBalls, callbacks){ } function purgeHandledBallsFromHashTree(conn, onDone){ - conn.query("SELECT ball FROM hash_tree_balls CROSS JOIN balls USING(ball)", function(rows){ + conn.query("SELECT ball FROM hash_tree_balls CROSS JOIN balls USING(ball)", rows => { if (rows.length === 0) return onDone(); - var arrHandledBalls = rows.map(function(row){ return row.ball; }); - conn.query("DELETE FROM hash_tree_balls WHERE ball IN(?)", [arrHandledBalls], function(){ + const arrHandledBalls = rows.map(({ball}) => ball); + conn.query("DELETE FROM hash_tree_balls WHERE ball IN(?)", [arrHandledBalls], () => { onDone(); }); }); diff --git a/chash.js b/chash.js index d592002a..bf2d3800 100644 --- a/chash.js +++ b/chash.js @@ -1,26 +1,25 @@ /*jslint node: true */ -"use strict"; -var crypto = require('crypto'); -var base32 = require('thirty-two'); +const crypto = require('crypto'); +const base32 = require('thirty-two'); -var PI = "14159265358979323846264338327950288419716939937510"; -var zeroString = "00000000"; +const PI = "14159265358979323846264338327950288419716939937510"; +const zeroString = "00000000"; -var arrRelativeOffsets = PI.split(""); +const arrRelativeOffsets = PI.split(""); function checkLength(chash_length){ if (chash_length !== 160 && chash_length !== 288) - throw Error("unsupported c-hash length: "+chash_length); + throw Error(`unsupported c-hash length: ${chash_length}`); } function calcOffsets(chash_length){ checkLength(chash_length); - var arrOffsets = []; - var offset = 0; - var index = 0; + const arrOffsets = []; + let offset = 0; + let index = 0; - for (var i=0; offset { cb(rows); }); } diff --git a/check_daemon.js b/check_daemon.js index 16956a3a..660c7dd8 100644 --- a/check_daemon.js +++ b/check_daemon.js @@ -1,17 +1,16 @@ /*jslint node: true */ -"use strict"; -var child_process = require('child_process'); -var conf = require('./conf.js'); -var mail = require('./mail.js'); +const child_process = require('child_process'); +const conf = require('./conf.js'); +const mail = require('./mail.js'); function checkDaemon(daemon_name, handleResult){ - child_process.exec('ps x', function(err, stdout, stderr){ + child_process.exec('ps x', (err, stdout, stderr) => { if (err) - throw Error('ps x failed: '+err); + throw Error(`ps x failed: ${err}`); if (stderr) - throw Error('ps x stderr: '+stderr); - var bFound = false; - stdout.split('\n').forEach(function(line){ + throw Error(`ps x stderr: ${stderr}`); + let bFound = false; + stdout.split('\n').forEach(line => { if (line.indexOf(daemon_name) >= 0){ bFound = true; write(line); @@ -22,17 +21,17 @@ function checkDaemon(daemon_name, handleResult){ } function checkDaemonAndNotify(daemon_name){ - checkDaemon(daemon_name, function(bFound){ + checkDaemon(daemon_name, bFound => { if (!bFound) - notifyAdmin('daemon '+daemon_name+' is down'); + notifyAdmin(`daemon ${daemon_name} is down`); }); } function checkDaemonAndRestart(daemon_name, start_command){ - checkDaemon(daemon_name, function(bFound){ + checkDaemon(daemon_name, bFound => { if (bFound) return; - notifyAdmin('daemon '+daemon_name+' is down, trying to restart '+start_command); + notifyAdmin(`daemon ${daemon_name} is down, trying to restart ${start_command}`); child_process.exec(start_command).unref(); process.exit(); }); @@ -46,12 +45,12 @@ function notifyAdmin(message){ to: conf.admin_email, from: conf.from_email, subject: message, - body: 'Check daemon:\n'+message + body: `Check daemon:\n${message}` }); } function write(str){ - console.log(Date().toString()+': '+str); + console.log(`${Date().toString()}: ${str}`); } exports.checkDaemon = checkDaemon; diff --git a/composer.js b/composer.js index 298753ac..01d73106 100644 --- a/composer.js +++ b/composer.js @@ -1,46 +1,45 @@ /*jslint node: true */ -"use strict"; -var crypto = require('crypto'); -var async = require('async'); -var db = require('./db.js'); -var constants = require('./constants.js'); -var objectHash = require('./object_hash.js'); -var objectLength = require("./object_length.js"); -var ecdsaSig = require('./signature.js'); -var mutex = require('./mutex.js'); -var _ = require('lodash'); -var storage = require('./storage.js'); -var myWitnesses = require('./my_witnesses.js'); -var parentComposer = require('./parent_composer.js'); -var paid_witnessing = require("./paid_witnessing.js"); -var headers_commission = require("./headers_commission.js"); -var mc_outputs = require("./mc_outputs.js"); -var validation = require('./validation.js'); -var writer = require('./writer.js'); -var conf = require('./conf.js'); -var profiler = require('./profiler.js'); - -var TRANSFER_INPUT_SIZE = 0 // type: "transfer" omitted +const crypto = require('crypto'); +const async = require('async'); +const db = require('./db.js'); +const constants = require('./constants.js'); +const objectHash = require('./object_hash.js'); +const objectLength = require("./object_length.js"); +const ecdsaSig = require('./signature.js'); +const mutex = require('./mutex.js'); +const _ = require('lodash'); +const storage = require('./storage.js'); +const myWitnesses = require('./my_witnesses.js'); +const parentComposer = require('./parent_composer.js'); +const paid_witnessing = require("./paid_witnessing.js"); +const headers_commission = require("./headers_commission.js"); +const mc_outputs = require("./mc_outputs.js"); +const validation = require('./validation.js'); +const writer = require('./writer.js'); +const conf = require('./conf.js'); +const profiler = require('./profiler.js'); + +const TRANSFER_INPUT_SIZE = 0 // type: "transfer" omitted + 44 // unit + 8 // message_index + 8; // output_index -var HEADERS_COMMISSION_INPUT_SIZE = 18 // type: "headers_commission" +const HEADERS_COMMISSION_INPUT_SIZE = 18 // type: "headers_commission" + 8 // from_main_chain_index + 8; // to_main_chain_index -var WITNESSING_INPUT_SIZE = 10 // type: "witnessing" +const WITNESSING_INPUT_SIZE = 10 // type: "witnessing" + 8 // from_main_chain_index + 8; // to_main_chain_index -var ADDRESS_SIZE = 32; +const ADDRESS_SIZE = 32; -var hash_placeholder = "--------------------------------------------"; // 256 bits (32 bytes) base64: 44 bytes -var sig_placeholder = "----------------------------------------------------------------------------------------"; // 88 bytes +const hash_placeholder = "--------------------------------------------"; // 256 bits (32 bytes) base64: 44 bytes +const sig_placeholder = "----------------------------------------------------------------------------------------"; // 88 bytes -var bGenesis = false; -exports.setGenesis = function(_bGenesis){ bGenesis = _bGenesis; }; +let bGenesis = false; +exports.setGenesis = _bGenesis => { bGenesis = _bGenesis; }; function repeatString(str, times){ @@ -49,28 +48,28 @@ function repeatString(str, times){ return (new Array(times+1)).join(str); } -function sortOutputs(a,b){ - var addr_comparison = a.address.localeCompare(b.address); - return addr_comparison ? addr_comparison : (a.amount - b.amount); +function sortOutputs({address, amount}, {address, amount}) { + const addr_comparison = address.localeCompare(address); + return addr_comparison ? addr_comparison : (amount - amount); } // bMultiAuthored includes all addresses, not just those that pay // arrAddresses is paying addresses function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci, amount, bMultiAuthored, onDone){ - var asset = objAsset ? objAsset.asset : null; - console.log("pick coins "+asset+" amount "+amount); - var is_base = objAsset ? 0 : 1; - var arrInputsWithProofs = []; - var total_amount = 0; - var required_amount = amount; + const asset = objAsset ? objAsset.asset : null; + console.log(`pick coins ${asset} amount ${amount}`); + const is_base = objAsset ? 0 : 1; + const arrInputsWithProofs = []; + let total_amount = 0; + let required_amount = amount; // adds element to arrInputsWithProofs function addInput(input){ total_amount += input.amount; - var objInputWithProof = {input: input}; + const objInputWithProof = {input}; if (objAsset && objAsset.is_private){ // for type=payment only - var spend_proof = objectHash.getBase64Hash({ - asset: asset, + const spend_proof = objectHash.getBase64Hash({ + asset, amount: input.amount, address: input.address, unit: input.unit, @@ -78,7 +77,7 @@ function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci output_index: input.output_index, blinding: input.blinding }); - var objSpendProof = {spend_proof: spend_proof}; + const objSpendProof = {spend_proof}; if (bMultiAuthored) objSpendProof.address = input.address; objInputWithProof.spend_proof = objSpendProof; @@ -94,18 +93,18 @@ function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci function pickOneCoinJustBiggerAndContinue(){ if (amount === Infinity) return pickMultipleCoinsAndContinue(); - var more = is_base ? '>' : '>='; + const more = is_base ? '>' : '>='; conn.query( - "SELECT unit, message_index, output_index, amount, blinding, address \n\ - FROM outputs \n\ - CROSS JOIN units USING(unit) \n\ - WHERE address IN(?) AND asset"+(asset ? "="+conn.escape(asset) : " IS NULL")+" AND is_spent=0 AND amount "+more+" ? \n\ - AND is_stable=1 AND sequence='good' AND main_chain_index<=? \n\ - ORDER BY amount LIMIT 1", + `SELECT unit, message_index, output_index, amount, blinding, address \n\ + FROM outputs \n\ + CROSS JOIN units USING(unit) \n\ + WHERE address IN(?) AND asset${asset ? `=${conn.escape(asset)}` : " IS NULL"} AND is_spent=0 AND amount ${more} ? \n\ + AND is_stable=1 AND sequence='good' AND main_chain_index<=? \n\ + ORDER BY amount LIMIT 1`, [arrSpendableAddresses, amount+is_base*TRANSFER_INPUT_SIZE, last_ball_mci], - function(rows){ + rows => { if (rows.length === 1){ - var input = rows[0]; + const input = rows[0]; // default type is "transfer" addInput(input); onDone(arrInputsWithProofs, total_amount); @@ -119,26 +118,26 @@ function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci // then, try to add smaller coins until we accumulate the target amount function pickMultipleCoinsAndContinue(){ conn.query( - "SELECT unit, message_index, output_index, amount, address, blinding \n\ - FROM outputs \n\ - CROSS JOIN units USING(unit) \n\ - WHERE address IN(?) AND asset"+(asset ? "="+conn.escape(asset) : " IS NULL")+" AND is_spent=0 \n\ - AND is_stable=1 AND sequence='good' AND main_chain_index<=? \n\ - ORDER BY amount DESC LIMIT ?", + `SELECT unit, message_index, output_index, amount, address, blinding \n\ + FROM outputs \n\ + CROSS JOIN units USING(unit) \n\ + WHERE address IN(?) AND asset${asset ? `=${conn.escape(asset)}` : " IS NULL"} AND is_spent=0 \n\ + AND is_stable=1 AND sequence='good' AND main_chain_index<=? \n\ + ORDER BY amount DESC LIMIT ?`, [arrSpendableAddresses, last_ball_mci, constants.MAX_INPUTS_PER_PAYMENT_MESSAGE-2], - function(rows){ + rows => { async.eachSeries( rows, - function(row, cb){ - var input = row; + (row, cb) => { + const input = row; objectHash.cleanNulls(input); required_amount += is_base*TRANSFER_INPUT_SIZE; addInput(input); // if we allow equality, we might get 0 amount for change which is invalid - var bFound = is_base ? (total_amount > required_amount) : (total_amount >= required_amount); + const bFound = is_base ? (total_amount > required_amount) : (total_amount >= required_amount); bFound ? cb('found') : cb(); }, - function(err){ + err => { if (err === 'found') onDone(arrInputsWithProofs, total_amount); else if (asset) @@ -163,35 +162,35 @@ function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci function addMcInputs(type, input_size, max_mci, onStillNotEnough){ async.eachSeries( arrAddresses, - function(address, cb){ - var target_amount = required_amount + input_size + (bMultiAuthored ? ADDRESS_SIZE : 0) - total_amount; + (address, cb) => { + const target_amount = required_amount + input_size + (bMultiAuthored ? ADDRESS_SIZE : 0) - total_amount; mc_outputs.findMcIndexIntervalToTargetAmount(conn, type, address, max_mci, target_amount, { ifNothing: cb, - ifFound: function(from_mc_index, to_mc_index, earnings, bSufficient){ + ifFound(from_mc_index, to_mc_index, earnings, bSufficient) { if (earnings === 0) throw Error("earnings === 0"); total_amount += earnings; - var input = { - type: type, + const input = { + type, from_main_chain_index: from_mc_index, to_main_chain_index: to_mc_index }; - var full_input_size = input_size; + let full_input_size = input_size; if (bMultiAuthored){ full_input_size += ADDRESS_SIZE; // address length input.address = address; } required_amount += full_input_size; - arrInputsWithProofs.push({input: input}); + arrInputsWithProofs.push({input}); (total_amount > required_amount) ? cb("found") // break eachSeries : cb(); // try next address } }); }, - function(err){ + err => { if (!err) - console.log(arrAddresses+" "+type+": got only "+total_amount+" out of required "+required_amount); + console.log(`${arrAddresses} ${type}: got only ${total_amount} out of required ${required_amount}`); (err === "found") ? onDone(arrInputsWithProofs, total_amount) : onStillNotEnough(); } ); @@ -204,44 +203,44 @@ function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci if (amount === Infinity && !objAsset.cap) // don't try to create infinite issue return onDone(null); } - console.log("will try to issue asset "+asset); + console.log(`will try to issue asset ${asset}`); // for issue, we use full list of addresses rather than spendable addresses if (objAsset.issued_by_definer_only && arrAddresses.indexOf(objAsset.definer_address) === -1) return finish(); - var issuer_address = objAsset.issued_by_definer_only ? objAsset.definer_address : arrAddresses[0]; - var issue_amount = objAsset.cap || (required_amount - total_amount) || 1; // 1 currency unit in case required_amount = total_amount + const issuer_address = objAsset.issued_by_definer_only ? objAsset.definer_address : arrAddresses[0]; + const issue_amount = objAsset.cap || (required_amount - total_amount) || 1; // 1 currency unit in case required_amount = total_amount function addIssueInput(serial_number){ total_amount += issue_amount; - var input = { + const input = { type: "issue", amount: issue_amount, - serial_number: serial_number + serial_number }; if (bMultiAuthored) input.address = issuer_address; - var objInputWithProof = {input: input}; + const objInputWithProof = {input}; if (objAsset && objAsset.is_private){ - var spend_proof = objectHash.getBase64Hash({ - asset: asset, + const spend_proof = objectHash.getBase64Hash({ + asset, amount: issue_amount, denomination: 1, address: issuer_address, - serial_number: serial_number + serial_number }); - var objSpendProof = {spend_proof: spend_proof}; + const objSpendProof = {spend_proof}; if (bMultiAuthored) objSpendProof.address = input.address; objInputWithProof.spend_proof = objSpendProof; } arrInputsWithProofs.push(objInputWithProof); - var bFound = is_base ? (total_amount > required_amount) : (total_amount >= required_amount); + const bFound = is_base ? (total_amount > required_amount) : (total_amount >= required_amount); bFound ? onDone(arrInputsWithProofs, total_amount) : finish(); } if (objAsset.cap){ - conn.query("SELECT 1 FROM inputs WHERE type='issue' AND asset=?", [asset], function(rows){ - if (rows.length > 0) // already issued + conn.query("SELECT 1 FROM inputs WHERE type='issue' AND asset=?", [asset], ({length}) => { + if (length > 0) // already issued return finish(); addIssueInput(1); }); @@ -250,8 +249,8 @@ function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci conn.query( "SELECT MAX(serial_number) AS max_serial_number FROM inputs WHERE type='issue' AND asset=? AND address=?", [asset, issuer_address], - function(rows){ - var max_serial_number = (rows.length === 0) ? 0 : rows[0].max_serial_number; + rows => { + const max_serial_number = (rows.length === 0) ? 0 : rows[0].max_serial_number; addIssueInput(max_serial_number+1); } ); @@ -267,7 +266,7 @@ function pickDivisibleCoinsForAmount(conn, objAsset, arrAddresses, last_ball_mci var arrSpendableAddresses = arrAddresses.concat(); // cloning if (objAsset && objAsset.auto_destroy){ - var i = arrAddresses.indexOf(objAsset.definer_address); + const i = arrAddresses.indexOf(objAsset.definer_address); if (i>=0) arrSpendableAddresses.splice(i, 1); } @@ -293,38 +292,38 @@ function composeTextJoint(arrSigningAddresses, arrPayingAddresses, text, signer, } function composePaymentJoint(arrFromAddresses, arrOutputs, signer, callbacks){ - composeJoint({paying_addresses: arrFromAddresses, outputs: arrOutputs, signer: signer, callbacks: callbacks}); + composeJoint({paying_addresses: arrFromAddresses, outputs: arrOutputs, signer, callbacks}); } - + function composePaymentAndTextJoint(arrSigningAddresses, arrPayingAddresses, arrOutputs, text, signer, callbacks){ composeJoint({ signing_addresses: arrSigningAddresses, paying_addresses: arrPayingAddresses, outputs: arrOutputs, messages: [createTextMessage(text)], - signer: signer, - callbacks: callbacks + signer, + callbacks }); } function composeContentJoint(from_address, app, payload, signer, callbacks){ - var objMessage = { - app: app, + const objMessage = { + app, payload_location: "inline", payload_hash: objectHash.getBase64Hash(payload), - payload: payload + payload }; composeJoint({ paying_addresses: [from_address], outputs: [{address: from_address, amount: 0}], messages: [objMessage], - signer: signer, - callbacks: callbacks + signer, + callbacks }); } function composeDefinitionChangeJoint(from_address, definition_chash, signer, callbacks){ - composeContentJoint(from_address, "address_definition_change", {definition_chash: definition_chash}, signer, callbacks); + composeContentJoint(from_address, "address_definition_change", {definition_chash}, signer, callbacks); } function composeDataFeedJoint(from_address, data, signer, callbacks){ @@ -340,12 +339,12 @@ function composeDedinitionTemplateJoint(from_address, arrDefinitionTemplate, sig } function composePollJoint(from_address, question, arrChoices, signer, callbacks){ - var poll_data = {question: question, choices: arrChoices}; + const poll_data = {question, choices: arrChoices}; composeContentJoint(from_address, "poll", poll_data, signer, callbacks); } function composeVoteJoint(from_address, poll_unit, choice, signer, callbacks){ - var vote_data = {unit: poll_unit, choice: choice}; + const vote_data = {unit: poll_unit, choice}; composeContentJoint(from_address, "vote", vote_data, signer, callbacks); } @@ -362,7 +361,7 @@ function composeAssetDefinitionJoint(from_address, asset_definition, signer, cal } function composeAssetAttestorsJoint(from_address, asset, arrNewAttestors, signer, callbacks){ - composeContentJoint(from_address, "asset_attestors", {asset: asset, attestors: arrNewAttestors}, signer, callbacks); + composeContentJoint(from_address, "asset_attestors", {asset, attestors: arrNewAttestors}, signer, callbacks); } /* @@ -371,9 +370,9 @@ function composeAssetAttestorsJoint(from_address, asset, arrNewAttestors, signer */ function composeJoint(params){ - var arrWitnesses = params.witnesses; + const arrWitnesses = params.witnesses; if (!arrWitnesses){ - myWitnesses.readMyWitnesses(function(_arrWitnesses){ + myWitnesses.readMyWitnesses(_arrWitnesses => { params.witnesses = _arrWitnesses; composeJoint(params); }); @@ -381,11 +380,11 @@ function composeJoint(params){ } if (conf.bLight && !params.lightProps){ - var network = require('./network.js'); + const network = require('./network.js'); network.requestFromLightVendor( 'light/get_parents_and_last_ball_and_witness_list_unit', {witnesses: arrWitnesses}, - function(ws, request, response){ + (ws, request, response) => { if (response.error) return params.callbacks.ifError(response.error); if (!response.parent_units || !response.last_stable_mc_ball || !response.last_stable_mc_ball_unit || typeof response.last_stable_mc_ball_mci !== 'number') @@ -400,18 +399,18 @@ function composeJoint(params){ // try to use as few paying_addresses as possible. Assuming paying_addresses are sorted such that the most well-funded addresses come first if (params.minimal && !params.send_all){ var callbacks = params.callbacks; - var arrCandidatePayingAddresses = params.paying_addresses; + const arrCandidatePayingAddresses = params.paying_addresses; - var trySubset = function(count){ + const trySubset = count => { if (count > constants.MAX_AUTHORS_PER_UNIT) return callbacks.ifNotEnoughFunds("Too many authors. Consider splitting the payment into two units."); - var try_params = _.clone(params); + const try_params = _.clone(params); delete try_params.minimal; try_params.paying_addresses = arrCandidatePayingAddresses.slice(0, count); try_params.callbacks = { ifOk: callbacks.ifOk, ifError: callbacks.ifError, - ifNotEnoughFunds: function(error_message){ + ifNotEnoughFunds(error_message) { if (count === arrCandidatePayingAddresses.length) return callbacks.ifNotEnoughFunds(error_message); trySubset(count+1); // add one more paying address @@ -423,14 +422,14 @@ function composeJoint(params){ return trySubset(1); } - var arrSigningAddresses = params.signing_addresses || []; - var arrPayingAddresses = params.paying_addresses || []; - var arrOutputs = params.outputs || []; - var arrMessages = _.clone(params.messages || []); - var assocPrivatePayloads = params.private_payloads || {}; // those that correspond to a subset of params.messages - var fnRetrieveMessages = params.retrieveMessages; - var lightProps = params.lightProps; - var signer = params.signer; + const arrSigningAddresses = params.signing_addresses || []; + const arrPayingAddresses = params.paying_addresses || []; + const arrOutputs = params.outputs || []; + const arrMessages = _.clone(params.messages || []); + let assocPrivatePayloads = params.private_payloads || {}; // those that correspond to a subset of params.messages + const fnRetrieveMessages = params.retrieveMessages; + const lightProps = params.lightProps; + const signer = params.signer; var callbacks = params.callbacks; if (conf.bLight && !lightProps) @@ -438,8 +437,8 @@ function composeJoint(params){ //profiler.start(); - var arrChangeOutputs = arrOutputs.filter(function(output) { return (output.amount === 0); }); - var arrExternalOutputs = arrOutputs.filter(function(output) { return (output.amount > 0); }); + const arrChangeOutputs = arrOutputs.filter(({amount}) => amount === 0); + const arrExternalOutputs = arrOutputs.filter(({amount}) => amount > 0); if (arrChangeOutputs.length > 1) throw Error("more than one change output"); if (arrChangeOutputs.length === 0) @@ -447,9 +446,9 @@ function composeJoint(params){ if (arrPayingAddresses.length === 0) throw Error("no payers?"); - var arrFromAddresses = _.union(arrSigningAddresses, arrPayingAddresses).sort(); + const arrFromAddresses = _.union(arrSigningAddresses, arrPayingAddresses).sort(); - var objPaymentMessage = { + const objPaymentMessage = { app: "payment", payload_location: "inline", payload_hash: hash_placeholder, @@ -460,60 +459,58 @@ function composeJoint(params){ // we'll add more outputs below } }; - var total_amount = 0; - arrExternalOutputs.forEach(function(output){ + let total_amount = 0; + arrExternalOutputs.forEach(output => { objPaymentMessage.payload.outputs.push(output); total_amount += output.amount; }); arrMessages.push(objPaymentMessage); - var bMultiAuthored = (arrFromAddresses.length > 1); - var objUnit = { + const bMultiAuthored = (arrFromAddresses.length > 1); + const objUnit = { version: constants.version, alt: constants.alt, //timestamp: Date.now(), messages: arrMessages, authors: [] }; - var objJoint = {unit: objUnit}; + const objJoint = {unit: objUnit}; if (params.earned_headers_commission_recipients) // it needn't be already sorted by address, we'll sort it now - objUnit.earned_headers_commission_recipients = params.earned_headers_commission_recipients.concat().sort(function(a,b){ - return ((a.address < b.address) ? -1 : 1); - }); + objUnit.earned_headers_commission_recipients = params.earned_headers_commission_recipients.concat().sort(({address}, {address}) => (address < address) ? -1 : 1); else if (bMultiAuthored) // by default, the entire earned hc goes to the change address objUnit.earned_headers_commission_recipients = [{address: arrChangeOutputs[0].address, earned_headers_commission_share: 100}]; - var total_input; - var last_ball_mci; - var assocSigningPaths = {}; - var unlock_callback; - var conn; + let total_input; + let last_ball_mci; + const assocSigningPaths = {}; + let unlock_callback; + let conn; - var handleError = function(err){ + const handleError = err => { //profiler.stop('compose'); unlock_callback(); if (typeof err === "object"){ if (err.error_code === "NOT_ENOUGH_FUNDS") return callbacks.ifNotEnoughFunds(err.error); - throw Error("unknown error code in: "+JSON.stringify(err)); + throw Error(`unknown error code in: ${JSON.stringify(err)}`); } callbacks.ifError(err); }; async.series([ - function(cb){ // lock - mutex.lock(arrFromAddresses.map(function(from_address){ return 'c-'+from_address; }), function(unlock){ + cb => { // lock + mutex.lock(arrFromAddresses.map(from_address => `c-${from_address}`), unlock => { unlock_callback = unlock; cb(); }); }, - function(cb){ // start transaction - db.takeConnectionFromPool(function(new_conn){ + cb => { // start transaction + db.takeConnectionFromPool(new_conn => { conn = new_conn; - conn.query("BEGIN", function(){cb();}); + conn.query("BEGIN", () => {cb();}); }); }, - function(cb){ // parent units + cb => { // parent units if (bGenesis) return cb(); @@ -529,8 +526,8 @@ function composeJoint(params){ SELECT 1 FROM units CROSS JOIN unit_authors USING(unit) \n\ WHERE (main_chain_index>? OR main_chain_index IS NULL) AND address IN(?) AND sequence!='good'", [last_ball_mci, arrFromAddresses, last_ball_mci, arrFromAddresses, last_ball_mci, arrFromAddresses], - function(rows){ - if (rows.length > 0) + ({length}) => { + if (length > 0) return cb("some definition changes or definitions or nonserials are not stable yet"); cb(); } @@ -547,9 +544,15 @@ function composeJoint(params){ parentComposer.pickParentUnitsAndLastBall( conn, arrWitnesses, - function(err, arrParentUnits, last_stable_mc_ball, last_stable_mc_ball_unit, last_stable_mc_ball_mci){ + ( + err, + arrParentUnits, + last_stable_mc_ball, + last_stable_mc_ball_unit, + last_stable_mc_ball_mci + ) => { if (err) - return cb("unable to find parents: "+err); + return cb(`unable to find parents: ${err}`); objUnit.parent_units = arrParentUnits; objUnit.last_ball = last_stable_mc_ball; objUnit.last_ball_unit = last_stable_mc_ball_unit; @@ -558,11 +561,11 @@ function composeJoint(params){ } ); }, - function(cb){ // authors - async.eachSeries(arrFromAddresses, function(from_address, cb2){ + cb => { // authors + async.eachSeries(arrFromAddresses, (from_address, cb2) => { function setDefinition(){ - signer.readDefinition(conn, from_address, function(err, arrDefinition){ + signer.readDefinition(conn, from_address, (err, arrDefinition) => { if (err) return cb2(err); objAuthor.definition = arrDefinition; @@ -574,10 +577,10 @@ function composeJoint(params){ address: from_address, authentifiers: {} }; - signer.readSigningPaths(conn, from_address, function(assocLengthsBySigningPaths){ - var arrSigningPaths = Object.keys(assocLengthsBySigningPaths); + signer.readSigningPaths(conn, from_address, assocLengthsBySigningPaths => { + const arrSigningPaths = Object.keys(assocLengthsBySigningPaths); assocSigningPaths[from_address] = arrSigningPaths; - for (var j=0; j { + if (length === 0) // first message from this address return setDefinition(); // try to find last stable change of definition, then check if the definition was already disclosed conn.query( @@ -595,10 +598,10 @@ function composeJoint(params){ WHERE address=? AND is_stable=1 AND sequence='good' AND main_chain_index<=? \n\ ORDER BY level DESC LIMIT 1", [from_address, last_ball_mci], - function(rows){ + rows => { if (rows.length === 0) // no definition changes at all return cb2(); - var row = rows[0]; + const row = rows[0]; row.definition ? cb2() : setDefinition(); // if definition not found in the db, add it into the json } ); @@ -607,7 +610,7 @@ function composeJoint(params){ }); }, cb); }, - function(cb){ // witnesses + cb => { // witnesses if (bGenesis){ objUnit.witnesses = arrWitnesses; return cb(); @@ -620,10 +623,10 @@ function composeJoint(params){ return cb(); } // witness addresses must not have references - storage.determineIfWitnessAddressDefinitionsHaveReferences(conn, arrWitnesses, function(bWithReferences){ + storage.determineIfWitnessAddressDefinitionsHaveReferences(conn, arrWitnesses, bWithReferences => { if (bWithReferences) return cb("some witnesses have references in their addresses"); - storage.findWitnessListUnit(conn, arrWitnesses, last_ball_mci, function(witness_list_unit){ + storage.findWitnessListUnit(conn, arrWitnesses, last_ball_mci, witness_list_unit => { if (witness_list_unit) objUnit.witness_list_unit = witness_list_unit; else @@ -632,28 +635,27 @@ function composeJoint(params){ }); }); }, - // messages retrieved via callback - function(cb){ + cb => { if (!fnRetrieveMessages) return cb(); console.log("will retrieve messages"); - fnRetrieveMessages(conn, last_ball_mci, bMultiAuthored, arrPayingAddresses, function(err, arrMoreMessages, assocMorePrivatePayloads){ - console.log("fnRetrieveMessages callback: err code = "+(err ? err.error_code : "")); + fnRetrieveMessages(conn, last_ball_mci, bMultiAuthored, arrPayingAddresses, (err, arrMoreMessages, assocMorePrivatePayloads) => { + console.log(`fnRetrieveMessages callback: err code = ${err ? err.error_code : ""}`); if (err) - return cb((typeof err === "string") ? ("unable to add additional messages: "+err) : err); + return cb((typeof err === "string") ? (`unable to add additional messages: ${err}`) : err); Array.prototype.push.apply(objUnit.messages, arrMoreMessages); if (assocMorePrivatePayloads && Object.keys(assocMorePrivatePayloads).length > 0) - for (var payload_hash in assocMorePrivatePayloads) + for (const payload_hash in assocMorePrivatePayloads) assocPrivatePayloads[payload_hash] = assocMorePrivatePayloads[payload_hash]; cb(); }); }, - function(cb){ // input coins + cb => { // input coins objUnit.headers_commission = objectLength.getHeadersSize(objUnit); - var naked_payload_commission = objectLength.getTotalPayloadSize(objUnit); // without input coins + const naked_payload_commission = objectLength.getTotalPayloadSize(objUnit); // without input coins if (bGenesis){ - var issueInput = {type: "issue", serial_number: 1, amount: constants.TOTAL_WHITEBYTES}; + const issueInput = {type: "issue", serial_number: 1, amount: constants.TOTAL_WHITEBYTES}; if (objUnit.authors.length > 1) { issueInput.address = arrWitnesses[0]; } @@ -672,54 +674,54 @@ function composeJoint(params){ } // all inputs must appear before last_ball - var target_amount = params.send_all ? Infinity : (total_amount + objUnit.headers_commission + naked_payload_commission); + const target_amount = params.send_all ? Infinity : (total_amount + objUnit.headers_commission + naked_payload_commission); pickDivisibleCoinsForAmount( conn, null, arrPayingAddresses, last_ball_mci, target_amount, bMultiAuthored, - function(arrInputsWithProofs, _total_input){ + (arrInputsWithProofs, _total_input) => { if (!arrInputsWithProofs) return cb({ error_code: "NOT_ENOUGH_FUNDS", - error: "not enough spendable funds from "+arrPayingAddresses+" for "+target_amount + error: `not enough spendable funds from ${arrPayingAddresses} for ${target_amount}` }); total_input = _total_input; - objPaymentMessage.payload.inputs = arrInputsWithProofs.map(function(objInputWithProof){ return objInputWithProof.input; }); + objPaymentMessage.payload.inputs = arrInputsWithProofs.map(({input}) => input); objUnit.payload_commission = objectLength.getTotalPayloadSize(objUnit); console.log("inputs increased payload by", objUnit.payload_commission - naked_payload_commission); cb(); } ); } - ], function(err){ + ], err => { // we close the transaction and release the connection before signing as multisig signing may take very very long // however we still keep c-ADDRESS lock to avoid creating accidental doublespends - conn.query(err ? "ROLLBACK" : "COMMIT", function(){ + conn.query(err ? "ROLLBACK" : "COMMIT", () => { conn.release(); if (err) return handleError(err); // change, payload hash, signature, and unit hash - var change = total_input - total_amount - objUnit.headers_commission - objUnit.payload_commission; + const change = total_input - total_amount - objUnit.headers_commission - objUnit.payload_commission; if (change <= 0){ if (!params.send_all) - throw Error("change="+change+", params="+JSON.stringify(params)); + throw Error(`change=${change}, params=${JSON.stringify(params)}`); return handleError({ error_code: "NOT_ENOUGH_FUNDS", - error: "not enough spendable funds from "+arrPayingAddresses+" for fees" + error: `not enough spendable funds from ${arrPayingAddresses} for fees` }); } objPaymentMessage.payload.outputs[0].amount = change; objPaymentMessage.payload.outputs.sort(sortOutputs); objPaymentMessage.payload_hash = objectHash.getBase64Hash(objPaymentMessage.payload); - var text_to_sign = objectHash.getUnitHashToSign(objUnit); + const text_to_sign = objectHash.getUnitHashToSign(objUnit); async.each( objUnit.authors, - function(author, cb2){ - var address = author.address; + (author, cb2) => { + const address = author.address; async.each( // different keys sign in parallel (if multisig) assocSigningPaths[address], - function(path, cb3){ + (path, cb3) => { if (signer.sign){ - signer.sign(objUnit, assocPrivatePayloads, address, path, function(err, signature){ + signer.sign(objUnit, assocPrivatePayloads, address, path, (err, signature) => { if (err) return cb3(err); // it can't be accidentally confused with real signature as there are no [ and ] in base64 alphabet @@ -730,7 +732,7 @@ function composeJoint(params){ }); } else{ - signer.readPrivateKey(address, path, function(err, privKey){ + signer.readPrivateKey(address, path, (err, privKey) => { if (err) return cb3(err); author.authentifiers[path] = ecdsaSig.sign(text_to_sign, privKey); @@ -738,12 +740,12 @@ function composeJoint(params){ }); } }, - function(err){ + err => { cb2(err); } ); }, - function(err){ + err => { if (err) return handleError(err); objUnit.unit = objectHash.getUnitHash(objUnit); @@ -761,15 +763,15 @@ function composeJoint(params){ }); } -var TYPICAL_FEE = 1000; -var MAX_FEE = 20000; +const TYPICAL_FEE = 1000; +const MAX_FEE = 20000; function filterMostFundedAddresses(rows, estimated_amount){ if (!estimated_amount) - return rows.map(function(row){ return row.address; }); - var arrFundedAddresses = []; - var accumulated_amount = 0; - for (var i=0; i address); + const arrFundedAddresses = []; + let accumulated_amount = 0; + for (let i=0; i estimated_amount + MAX_FEE) @@ -782,22 +784,22 @@ function readSortedFundedAddresses(asset, arrAvailableAddresses, estimated_amoun if (arrAvailableAddresses.length === 0) return handleFundedAddresses([]); if (estimated_amount && typeof estimated_amount !== 'number') - throw Error('invalid estimated amount: '+estimated_amount); + throw Error(`invalid estimated amount: ${estimated_amount}`); // addresses closest to estimated amount come first - var order_by = estimated_amount ? "(SUM(amount)>"+estimated_amount+") DESC, ABS(SUM(amount)-"+estimated_amount+") ASC" : "SUM(amount) DESC"; + const order_by = estimated_amount ? `(SUM(amount)>${estimated_amount}) DESC, ABS(SUM(amount)-${estimated_amount}) ASC` : "SUM(amount) DESC"; db.query( - "SELECT address, SUM(amount) AS total \n\ - FROM outputs \n\ - CROSS JOIN units USING(unit) \n\ - WHERE address IN(?) AND is_stable=1 AND sequence='good' AND is_spent=0 AND asset"+(asset ? "=?" : " IS NULL")+" \n\ - AND NOT EXISTS ( \n\ - SELECT * FROM unit_authors JOIN units USING(unit) \n\ - WHERE is_stable=0 AND unit_authors.address=outputs.address AND definition_chash IS NOT NULL \n\ - ) \n\ - GROUP BY address ORDER BY "+order_by, + `SELECT address, SUM(amount) AS total \n\ + FROM outputs \n\ + CROSS JOIN units USING(unit) \n\ + WHERE address IN(?) AND is_stable=1 AND sequence='good' AND is_spent=0 AND asset${asset ? "=?" : " IS NULL"} \n\ + AND NOT EXISTS ( \n\ + SELECT * FROM unit_authors JOIN units USING(unit) \n\ + WHERE is_stable=0 AND unit_authors.address=outputs.address AND definition_chash IS NOT NULL \n\ + ) \n\ + GROUP BY address ORDER BY ${order_by}`, asset ? [arrAvailableAddresses, asset] : [arrAvailableAddresses], - function(rows){ - var arrFundedAddresses = filterMostFundedAddresses(rows, estimated_amount); + rows => { + const arrFundedAddresses = filterMostFundedAddresses(rows, estimated_amount); handleFundedAddresses(arrFundedAddresses); /* if (arrFundedAddresses.length === 0) return handleFundedAddresses([]); @@ -818,11 +820,11 @@ function readSortedFundedAddresses(asset, arrAvailableAddresses, estimated_amoun // tries to use as few of the params.available_paying_addresses as possible. // note: it doesn't select addresses that have _only_ witnessing or headers commissions outputs function composeMinimalJoint(params){ - var estimated_amount = (params.send_all || params.retrieveMessages) ? 0 : params.outputs.reduce(function(acc, output){ return acc+output.amount; }, 0) + TYPICAL_FEE; - readSortedFundedAddresses(null, params.available_paying_addresses, estimated_amount, function(arrFundedPayingAddresses){ + const estimated_amount = (params.send_all || params.retrieveMessages) ? 0 : params.outputs.reduce((acc, {amount}) => acc+amount, 0) + TYPICAL_FEE; + readSortedFundedAddresses(null, params.available_paying_addresses, estimated_amount, arrFundedPayingAddresses => { if (arrFundedPayingAddresses.length === 0) return params.callbacks.ifNotEnoughFunds("all paying addresses are unfunded"); - var minimal_params = _.clone(params); + const minimal_params = _.clone(params); delete minimal_params.available_paying_addresses; minimal_params.minimal = true; minimal_params.paying_addresses = arrFundedPayingAddresses; @@ -831,7 +833,7 @@ function composeMinimalJoint(params){ } function composeAndSaveMinimalJoint(params){ - var params_with_save = _.clone(params); + const params_with_save = _.clone(params); params_with_save.callbacks = getSavingCallbacks(params.callbacks); composeMinimalJoint(params_with_save); } @@ -840,41 +842,41 @@ function getSavingCallbacks(callbacks){ return { ifError: callbacks.ifError, ifNotEnoughFunds: callbacks.ifNotEnoughFunds, - ifOk: function(objJoint, assocPrivatePayloads, composer_unlock){ - var objUnit = objJoint.unit; - var unit = objUnit.unit; + ifOk(objJoint, assocPrivatePayloads, composer_unlock) { + const objUnit = objJoint.unit; + const unit = objUnit.unit; validation.validate(objJoint, { - ifUnitError: function(err){ + ifUnitError(err) { composer_unlock(); - callbacks.ifError("Validation error: "+err); + callbacks.ifError(`Validation error: ${err}`); // throw Error("unexpected validation error: "+err); }, - ifJointError: function(err){ - throw Error("unexpected validation joint error: "+err); + ifJointError(err) { + throw Error(`unexpected validation joint error: ${err}`); }, - ifTransientError: function(err){ - throw Error("unexpected validation transient error: "+err); + ifTransientError(err) { + throw Error(`unexpected validation transient error: ${err}`); }, - ifNeedHashTree: function(){ + ifNeedHashTree() { throw Error("unexpected need hash tree"); }, - ifNeedParentUnits: function(arrMissingUnits){ - throw Error("unexpected dependencies: "+arrMissingUnits.join(", ")); + ifNeedParentUnits(arrMissingUnits) { + throw Error(`unexpected dependencies: ${arrMissingUnits.join(", ")}`); }, - ifOk: function(objValidationState, validation_unlock){ - console.log("base asset OK "+objValidationState.sequence); + ifOk(objValidationState, validation_unlock) { + console.log(`base asset OK ${objValidationState.sequence}`); if (objValidationState.sequence !== 'good'){ validation_unlock(); composer_unlock(); - return callbacks.ifError("Bad sequence "+objValidationState.sequence); + return callbacks.ifError(`Bad sequence ${objValidationState.sequence}`); } postJointToLightVendorIfNecessaryAndSave( objJoint, function onLightError(err){ // light only - console.log("failed to post base payment "+unit); - var eventBus = require('./event_bus.js'); + console.log(`failed to post base payment ${unit}`); + const eventBus = require('./event_bus.js'); if (err.match(/signature/)) - eventBus.emit('nonfatal_error', "failed to post unit "+unit+": "+err+"; "+JSON.stringify(objUnit), new Error()); + eventBus.emit('nonfatal_error', `failed to post unit ${unit}: ${err}; ${JSON.stringify(objUnit)}`, new Error()); validation_unlock(); composer_unlock(); callbacks.ifError(err); @@ -882,7 +884,7 @@ function getSavingCallbacks(callbacks){ function save(){ writer.saveJoint( objJoint, objValidationState, - function(conn, cb){ + (conn, cb) => { if (typeof callbacks.preCommitCb === "function") callbacks.preCommitCb(conn, objJoint, cb); else @@ -893,7 +895,7 @@ function getSavingCallbacks(callbacks){ composer_unlock(); if (err) return callbacks.ifError(err); - console.log("saved unit "+unit); + console.log(`saved unit ${unit}`); callbacks.ifOk(objJoint, assocPrivatePayloads); } ); @@ -907,8 +909,8 @@ function getSavingCallbacks(callbacks){ function postJointToLightVendorIfNecessaryAndSave(objJoint, onLightError, save){ if (conf.bLight){ // light clients cannot save before receiving OK from light vendor - var network = require('./network.js'); - network.postJointToLightVendor(objJoint, function(response){ + const network = require('./network.js'); + network.postJointToLightVendor(objJoint, response => { if (response === 'accepted') save(); else @@ -924,11 +926,11 @@ function composeAndSavePaymentJoint(arrFromAddresses, arrOutputs, signer, callba } -function getMessageIndexByPayloadHash(objUnit, payload_hash){ - for (var i=0; i { + conn.query("BEGIN", () => { + doWork(conn, err => { + conn.query(err ? "ROLLBACK" : "COMMIT", () => { conn.release(); onDone(err); }); diff --git a/definition.js b/definition.js index 8b44a85e..360776dd 100644 --- a/definition.js +++ b/definition.js @@ -1,29 +1,36 @@ /*jslint node: true */ -"use strict"; -var crypto = require('crypto'); -var _ = require('lodash'); -var async = require('async'); -var constants = require('./constants.js'); -var storage = require('./storage.js'); -var db = require('./db.js'); -var ecdsaSig = require('./signature.js'); -var merkle = require('./merkle.js'); -var ValidationUtils = require("./validation_utils.js"); -var objectHash = require("./object_hash.js"); +const crypto = require('crypto'); +const _ = require('lodash'); +const async = require('async'); +const constants = require('./constants.js'); +const storage = require('./storage.js'); +const db = require('./db.js'); +const ecdsaSig = require('./signature.js'); +const merkle = require('./merkle.js'); +const ValidationUtils = require("./validation_utils.js"); +const objectHash = require("./object_hash.js"); -var hasFieldsExcept = ValidationUtils.hasFieldsExcept; -var isStringOfLength = ValidationUtils.isStringOfLength; -var isNonemptyString = ValidationUtils.isNonemptyString; -var isInteger = ValidationUtils.isInteger; -var isNonnegativeInteger = ValidationUtils.isNonnegativeInteger; -var isPositiveInteger = ValidationUtils.isPositiveInteger; -var isNonemptyArray = ValidationUtils.isNonemptyArray; -var isArrayOfLength = ValidationUtils.isArrayOfLength; -var isValidAddress = ValidationUtils.isValidAddress; +const hasFieldsExcept = ValidationUtils.hasFieldsExcept; +const isStringOfLength = ValidationUtils.isStringOfLength; +const isNonemptyString = ValidationUtils.isNonemptyString; +const isInteger = ValidationUtils.isInteger; +const isNonnegativeInteger = ValidationUtils.isNonnegativeInteger; +const isPositiveInteger = ValidationUtils.isPositiveInteger; +const isNonemptyArray = ValidationUtils.isNonemptyArray; +const isArrayOfLength = ValidationUtils.isArrayOfLength; +const isValidAddress = ValidationUtils.isValidAddress; // validate definition of address or asset spending conditions -function validateDefinition(conn, arrDefinition, objUnit, objValidationState, arrAuthentifierPaths, bAssetCondition, handleResult){ +function validateDefinition( + conn, + arrDefinition, + {authors}, + {bDefiningPrivateAsset, last_ball_mci, bNoReferences}, + arrAuthentifierPaths, + bAssetCondition, + handleResult +) { function getFilterError(filter){ if (!filter) @@ -31,11 +38,11 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar if (hasFieldsExcept(filter, ["what", "asset", "type", "own_funds", "address", "amount", "amount_at_least", "amount_at_most"])) return "unknown fields in filter"; if (filter.what !== "input" && filter.what !== "output") - return "invalid what="+filter.what; - if (bAssetCondition && filter.asset === "this asset" && objValidationState.bDefiningPrivateAsset) + return `invalid what=${filter.what}`; + if (bAssetCondition && filter.asset === "this asset" && bDefiningPrivateAsset) return "private asset cannot reference itself"; if ("asset" in filter && !(filter.asset === "base" || isStringOfLength(filter.asset, constants.HASH_LENGTH) || bAssetCondition && filter.asset === "this asset")) - return "invalid asset: "+filter.asset; + return `invalid asset: ${filter.asset}`; if (filter.what === "output"){ if ("type" in filter) return "output canot have type"; @@ -45,13 +52,13 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar if (bAssetCondition && "own_funds" in filter) return "asset condition cannot be filtered by own_funds"; if ("type" in filter && filter.type !== "issue" && filter.type !== "transfer") - return "invalid type: "+filter.type; + return `invalid type: ${filter.type}`; if ("own_funds" in filter && typeof filter.own_funds !== "boolean") return "own_funds must be boolean"; if (bAssetCondition && filter.address === 'this address') return "asset condition cannot reference this address"; if ("address" in filter && !isValidAddress(filter.address) && filter.address !== 'this address') // it is ok if the address was never used yet - return "invalid address: "+filter.address; + return `invalid address: ${filter.address}`; if ("amount" in filter && !isPositiveInteger(filter.amount)) return "amount must be positive int"; if ("amount_at_least" in filter && !isPositiveInteger(filter.amount_at_least)) @@ -69,8 +76,8 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar function determineIfAnyOfAssetsIsPrivate(arrAssets, cb){ if (arrAssets.length === 0) return cb(false); - conn.query("SELECT 1 FROM assets WHERE unit IN(?) AND is_private=1 LIMIT 1", [arrAssets], function(rows){ - cb(rows.length > 0); + conn.query("SELECT 1 FROM assets WHERE unit IN(?) AND is_private=1 LIMIT 1", [arrAssets], ({length}) => { + cb(length > 0); }); } @@ -78,8 +85,8 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar function pathIncludesOneOfAuthentifiers(path){ if (bAssetCondition) throw Error('pathIncludesOneOfAuthentifiers called in asset condition'); - for (var i=0; i constants.MAX_COMPLEXITY) - return cb("complexity exceeded at "+path); + return cb(`complexity exceeded at ${path}`); if (!isArrayOfLength(arr, 2)) return cb("expression must be 2-element array"); - var op = arr[0]; - var args = arr[1]; + const op = arr[0]; + const args = arr[1]; switch(op){ case 'or': case 'and': if (!Array.isArray(args)) - return cb(op+" args must be array"); + return cb(`${op} args must be array`); if (args.length < 2) - return cb(op+" must have at least 2 options"); + return cb(`${op} must have at least 2 options`); var count_options_with_sig = 0; var index = -1; async.eachSeries( args, - function(arg, cb2){ + (arg, cb2) => { index++; - evaluate(arg, path+'.'+index, bInNegation, function(err, bHasSig){ + evaluate(arg, `${path}.${index}`, bInNegation, (err, bHasSig) => { if (err) return cb2(err); if (bHasSig) @@ -124,7 +131,7 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar cb2(); }); }, - function(err){ + err => { if (err) return cb(err); cb(null, op === "and" && count_options_with_sig > 0 || op === "or" && count_options_with_sig === args.length); @@ -134,7 +141,7 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'r of set': if (hasFieldsExcept(args, ["required", "set"])) - return cb("unknown fields in "+op); + return cb(`unknown fields in ${op}`); if (!isPositiveInteger(args.required)) return cb("required must be positive"); if (!Array.isArray(args.set)) @@ -151,9 +158,9 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar var index = -1; async.eachSeries( args.set, - function(arg, cb2){ + (arg, cb2) => { index++; - evaluate(arg, path+'.'+index, bInNegation, function(err, bHasSig){ + evaluate(arg, `${path}.${index}`, bInNegation, (err, bHasSig) => { if (err) return cb2(err); if (bHasSig) @@ -161,10 +168,10 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar cb2(); }); }, - function(err){ + err => { if (err) return cb(err); - var count_options_without_sig = args.set.length - count_options_with_sig; + const count_options_without_sig = args.set.length - count_options_with_sig; cb(null, args.required > count_options_without_sig); } ); @@ -172,26 +179,26 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'weighted and': if (hasFieldsExcept(args, ["required", "set"])) - return cb("unknown fields in "+op); + return cb(`unknown fields in ${op}`); if (!isPositiveInteger(args.required)) return cb("required must be positive"); if (!Array.isArray(args.set)) return cb("set must be array"); if (args.set.length < 2) return cb("set must have at least 2 options"); - var weight_of_options_with_sig = 0; - var total_weight = 0; + let weight_of_options_with_sig = 0; + let total_weight = 0; var index = -1; async.eachSeries( args.set, - function(arg, cb2){ + (arg, cb2) => { index++; if (hasFieldsExcept(arg, ["value", "weight"])) return cb2("unknown fields in weighted set element"); if (!isPositiveInteger(arg.weight)) return cb2("weight must be positive int"); total_weight += arg.weight; - evaluate(arg.value, path+'.'+index, bInNegation, function(err, bHasSig){ + evaluate(arg.value, `${path}.${index}`, bInNegation, (err, bHasSig) => { if (err) return cb2(err); if (bHasSig) @@ -199,12 +206,12 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar cb2(); }); }, - function(err){ + err => { if (err) return cb(err); if (args.required > total_weight) return cb("required must be <= than total weight"); - var weight_of_options_without_sig = total_weight - weight_of_options_with_sig; + const weight_of_options_without_sig = total_weight - weight_of_options_with_sig; cb(null, args.required > weight_of_options_without_sig); } ); @@ -212,11 +219,11 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'sig': if (bInNegation) - return cb(op+" cannot be negated"); + return cb(`${op} cannot be negated`); if (bAssetCondition) - return cb("asset condition cannot have "+op); + return cb(`asset condition cannot have ${op}`); if (hasFieldsExcept(args, ["algo", "pubkey"])) - return cb("unknown fields in "+op); + return cb(`unknown fields in ${op}`); if (args.algo === "secp256k1") return cb("default algo must not be explicitly specified"); if ("algo" in args && args.algo !== "secp256k1") @@ -227,11 +234,11 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'hash': if (bInNegation) - return cb(op+" cannot be negated"); + return cb(`${op} cannot be negated`); if (bAssetCondition) - return cb("asset condition cannot have "+op); + return cb(`asset condition cannot have ${op}`); if (hasFieldsExcept(args, ["algo", "hash"])) - return cb("unknown fields in "+op); + return cb(`unknown fields in ${op}`); if (args.algo === "sha256") return cb("default algo must not be explicitly specified"); if ("algo" in args && args.algo !== "sha256") @@ -241,32 +248,30 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar return cb(); case 'address': - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); if (bInNegation) - return cb(op+" cannot be negated"); + return cb(`${op} cannot be negated`); if (bAssetCondition) - return cb("asset condition cannot have "+op); - var other_address = args; + return cb(`asset condition cannot have ${op}`); + const other_address = args; if (!isValidAddress(other_address)) return cb("invalid address"); - storage.readDefinitionByAddress(conn, other_address, objValidationState.last_ball_mci, { - ifFound: function(arrInnerAddressDefinition){ + storage.readDefinitionByAddress(conn, other_address, last_ball_mci, { + ifFound(arrInnerAddressDefinition) { console.log("inner address:", arrInnerAddressDefinition); needToEvaluateNestedAddress(path) ? evaluate(arrInnerAddressDefinition, path, bInNegation, cb) : cb(null, true); }, - ifDefinitionNotFound: function(definition_chash){ + ifDefinitionNotFound(definition_chash) { // if (objValidationState.bAllowUnresolvedInnerDefinitions) // return cb(null, true); - var bAllowUnresolvedInnerDefinitions = true; - var arrDefiningAuthors = objUnit.authors.filter(function(author){ - return (author.address === other_address && author.definition && objectHash.getChash160(author.definition) === definition_chash); - }); + const bAllowUnresolvedInnerDefinitions = true; + const arrDefiningAuthors = authors.filter(({address, definition}) => address === other_address && definition && objectHash.getChash160(definition) === definition_chash); if (arrDefiningAuthors.length === 0) // no address definition in the current unit - return bAllowUnresolvedInnerDefinitions ? cb(null, true) : cb("definition of inner address "+other_address+" not found"); + return bAllowUnresolvedInnerDefinitions ? cb(null, true) : cb(`definition of inner address ${other_address} not found`); if (arrDefiningAuthors.length > 1) throw Error("more than 1 address definition"); - var arrInnerAddressDefinition = arrDefiningAuthors[0].definition; + const arrInnerAddressDefinition = arrDefiningAuthors[0].definition; needToEvaluateNestedAddress(path) ? evaluate(arrInnerAddressDefinition, path, bInNegation, cb) : cb(null, true); } }); @@ -274,28 +279,28 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'definition template': // ['definition template', ['unit', {param1: 'value1'}]] - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); if (!isArrayOfLength(args, 2)) - return cb("2-element array expected in "+op); - var unit = args[0]; - var params = args[1]; + return cb(`2-element array expected in ${op}`); + const unit = args[0]; + const params = args[1]; if (!isStringOfLength(unit, constants.HASH_LENGTH)) return cb("unit must be 44 bytes long"); if (!ValidationUtils.isNonemptyObject(params)) return cb("params must be non-empty object"); - for (var key in params) + for (const key in params) if (typeof params[key] !== "string" && typeof params[key] !== "number") return cb("each param must be string or number"); conn.query( "SELECT payload FROM messages JOIN units USING(unit) \n\ WHERE unit=? AND app='definition_template' AND main_chain_index<=? AND +sequence='good' AND is_stable=1", - [unit, objValidationState.last_ball_mci], - function(rows){ + [unit, last_ball_mci], + rows => { if (rows.length !== 1) return cb("template not found or too many"); - var template = rows[0].payload; - var arrTemplate = JSON.parse(template); + const template = rows[0].payload; + const arrTemplate = JSON.parse(template); try{ var arrFilledTemplate = replaceInTemplate(arrTemplate, params); console.log(require('util').inspect(arrFilledTemplate, {depth: null})); @@ -312,7 +317,7 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar break; case 'seen address': - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); if (!isValidAddress(args)) // it is ok if the address was never used yet return cb("invalid seen address"); @@ -320,14 +325,14 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'seen definition change': case 'has definition change': - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); if (!isArrayOfLength(args, 2)) - return cb(op+" must have 2 args"); - var changed_address = args[0]; - var new_definition_chash = args[1]; + return cb(`${op} must have 2 args`); + const changed_address = args[0]; + const new_definition_chash = args[1]; if (bAssetCondition && (changed_address === 'this address' || new_definition_chash === 'this address')) - return cb("asset condition cannot reference this address in "+op); + return cb(`asset condition cannot reference this address in ${op}`); if (!isValidAddress(changed_address) && changed_address !== 'this address') // it is ok if the address was never used yet return cb("invalid changed address"); if (!isValidAddress(new_definition_chash) && new_definition_chash !== 'this address') @@ -336,7 +341,7 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'cosigned by': if (bInNegation) - return cb(op+" cannot be negated"); + return cb(`${op} cannot be negated`); if (!isValidAddress(args)) // it is ok if the address was never used yet return cb("invalid cosigner address"); return cb(); @@ -346,27 +351,27 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar break; case 'in data feed': - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); if (!Array.isArray(args)) - return cb(op+" arg must be array"); + return cb(`${op} arg must be array`); if (args.length !== 4 && args.length !== 5) - return cb(op+" must have 4 or 5 args"); + return cb(`${op} must have 4 or 5 args`); var arrAddresses = args[0]; var feed_name = args[1]; var relation = args[2]; var value = args[3]; var min_mci = args[4]; if (!isNonemptyArray(arrAddresses)) - return cb("no addresses in "+op); + return cb(`no addresses in ${op}`); for (var i=0; i", "<", ">=", "<=", "!="].indexOf(relation) === -1) - return cb("invalid relation: "+relation); + return cb(`invalid relation: ${relation}`); if (!isNonemptyString(feed_name)) return cb("no feed_name"); if (feed_name.length > constants.MAX_DATA_FEED_NAME_LENGTH) @@ -384,29 +389,29 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar else return cb("invalid value"); if (typeof min_mci !== 'undefined' && !isNonnegativeInteger(min_mci)) - return cb(op+": invalid min_mci"); + return cb(`${op}: invalid min_mci`); return cb(); case 'in merkle': if (bInNegation) - return cb(op+" cannot be negated"); - if (objValidationState.bNoReferences) + return cb(`${op} cannot be negated`); + if (bNoReferences) return cb("no references allowed in address definition"); if (bAssetCondition) - return cb("asset condition cannot have "+op); + return cb(`asset condition cannot have ${op}`); if (!Array.isArray(args)) - return cb(op+" arg must be array"); + return cb(`${op} arg must be array`); if (args.length !== 3 && args.length !== 4) - return cb(op+" must have 3 or 4 args"); + return cb(`${op} must have 3 or 4 args`); var arrAddresses = args[0]; var feed_name = args[1]; - var element = args[2]; + const element = args[2]; var min_mci = args[3]; if (!isNonemptyArray(arrAddresses)) - return cb("no addresses in "+op); + return cb(`no addresses in ${op}`); for (var i=0; i\?|-]{1,100}/)) return cb("incorrect format of merkled element"); if (typeof min_mci !== 'undefined' && !isNonnegativeInteger(min_mci)) - return cb(op+": invalid min_mci"); + return cb(`${op}: invalid min_mci`); return cb(); case 'mci': @@ -427,15 +432,15 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar if (!isNonemptyString(relation)) return cb("no relation"); if (["=", ">", "<", ">=", "<=", "!="].indexOf(relation) === -1) - return cb("invalid relation: "+relation); + return cb(`invalid relation: ${relation}`); if (!isNonnegativeInteger(value)) - return cb(op+" must be a non-neg number"); + return cb(`${op} must be a non-neg number`); break; case 'has': case 'has one': case 'seen': - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); var err = getFilterError(args); if (err) @@ -450,7 +455,7 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar } if (!args.asset || args.asset === 'base' || bAssetCondition && args.asset === "this asset") return cb(); - determineIfAnyOfAssetsIsPrivate([args.asset], function(bPrivate){ + determineIfAnyOfAssetsIsPrivate([args.asset], bPrivate => { if (bPrivate) return cb("asset must be public"); cb(); @@ -459,28 +464,28 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar case 'has equal': case 'has one equal': - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); if (hasFieldsExcept(args, ["equal_fields", "search_criteria"])) - return cb("unknown fields in "+op); + return cb(`unknown fields in ${op}`); if (!isNonemptyArray(args.equal_fields)) return cb("no equal_fields"); - var assocUsedFields = {}; + const assocUsedFields = {}; for (var i=0; i { bPrivate ? cb("all assets must be public") : cb(); }); break; case 'sum': - if (objValidationState.bNoReferences) + if (bNoReferences) return cb("no references allowed in address definition"); if (hasFieldsExcept(args, ["filter", "equals", "at_least", "at_most"])) - return cb("unknown fields in "+op); + return cb(`unknown fields in ${op}`); var err = getFilterError(args.filter); if (err) return cb(err); @@ -518,18 +523,18 @@ function validateDefinition(conn, arrDefinition, objUnit, objValidationState, ar return cb("at least one of equals, at_least, at_most must be specified"); if (!args.filter.asset || args.filter.asset === 'base' || bAssetCondition && args.filter.asset === "this asset") return cb(); - determineIfAnyOfAssetsIsPrivate([args.filter.asset], function(bPrivate){ + determineIfAnyOfAssetsIsPrivate([args.filter.asset], bPrivate => { bPrivate ? cb("asset must be public") : cb(); }); break; default: - return cb("unknown op: "+op); + return cb(`unknown op: ${op}`); } } var complexity = 0; - evaluate(arrDefinition, 'r', false, function(err, bHasSig){ + evaluate(arrDefinition, 'r', false, (err, bHasSig) => { if (err) return handleResult(err); if (!bHasSig && !bAssetCondition) @@ -548,8 +553,8 @@ function evaluateAssetCondition(conn, asset, arrDefinition, objUnit, objValidati function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit, objValidationState, assocAuthentifiers, cb){ function evaluate(arr, path, cb2){ - var op = arr[0]; - var args = arr[1]; + const op = arr[0]; + const args = arr[1]; switch(op){ case 'or': // ['or', [list of options]] @@ -557,15 +562,15 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit var index = -1; async.eachSeries( args, - function(arg, cb3){ + (arg, cb3) => { index++; - evaluate(arg, path+'.'+index, function(arg_res){ + evaluate(arg, `${path}.${index}`, arg_res => { res = res || arg_res; cb3(); // check all members, even if required minimum already found //res ? cb3("found") : cb3(); }); }, - function(){ + () => { cb2(res); } ); @@ -577,15 +582,15 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit var index = -1; async.eachSeries( args, - function(arg, cb3){ + (arg, cb3) => { index++; - evaluate(arg, path+'.'+index, function(arg_res){ + evaluate(arg, `${path}.${index}`, arg_res => { res = res && arg_res; cb3(); // check all members, even if required minimum already found //res ? cb3() : cb3("found"); }); }, - function(){ + () => { cb2(res); } ); @@ -593,20 +598,20 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'r of set': // ['r of set', {required: 2, set: [list of options]}] - var count = 0; + let count = 0; var index = -1; async.eachSeries( args.set, - function(arg, cb3){ + (arg, cb3) => { index++; - evaluate(arg, path+'.'+index, function(arg_res){ + evaluate(arg, `${path}.${index}`, arg_res => { if (arg_res) count++; cb3(); // check all members, even if required minimum already found, so that we don't allow invalid sig on unchecked path //(count < args.required) ? cb3() : cb3("found"); }); }, - function(){ + () => { cb2(count >= args.required); } ); @@ -614,20 +619,20 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'weighted and': // ['weighted and', {required: 15, set: [{value: boolean_expr, weight: 10}, {value: boolean_expr, weight: 20}]}] - var weight = 0; + let weight = 0; var index = -1; async.eachSeries( args.set, - function(arg, cb3){ + (arg, cb3) => { index++; - evaluate(arg.value, path+'.'+index, function(arg_res){ + evaluate(arg.value, `${path}.${index}`, arg_res => { if (arg_res) weight += arg.weight; cb3(); // check all members, even if required minimum already found //(weight < args.required) ? cb3() : cb3("found"); }); }, - function(){ + () => { cb2(weight >= args.required); } ); @@ -636,7 +641,7 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'sig': // ['sig', {algo: 'secp256k1', pubkey: 'base64'}] //console.log(op, path); - var signature = assocAuthentifiers[path]; + const signature = assocAuthentifiers[path]; if (!signature) return cb2(false); arrUsedPaths.push(path); @@ -646,7 +651,7 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit return cb2(true); var res = ecdsaSig.verify(objValidationState.unit_hash_to_sign, signature, args.pubkey); if (!res) - fatal_error = "bad signature at path "+path; + fatal_error = `bad signature at path ${path}`; cb2(res); } break; @@ -660,27 +665,25 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit if (algo === 'sha256'){ var res = (args.hash === crypto.createHash("sha256").update(assocAuthentifiers[path], "utf8").digest("base64")); if (!res) - fatal_error = "bad hash at path "+path; + fatal_error = `bad hash at path ${path}`; cb2(res); } break; case 'address': // ['address', 'BASE32'] - var other_address = args; + const other_address = args; storage.readDefinitionByAddress(conn, other_address, objValidationState.last_ball_mci, { - ifFound: function(arrInnerAddressDefinition){ + ifFound(arrInnerAddressDefinition) { evaluate(arrInnerAddressDefinition, path, cb2); }, - ifDefinitionNotFound: function(definition_chash){ - var arrDefiningAuthors = objUnit.authors.filter(function(author){ - return (author.address === other_address && author.definition && objectHash.getChash160(author.definition) === definition_chash); - }); + ifDefinitionNotFound(definition_chash) { + const arrDefiningAuthors = objUnit.authors.filter(author => author.address === other_address && author.definition && objectHash.getChash160(author.definition) === definition_chash); if (arrDefiningAuthors.length === 0) // no definition in the current unit return cb2(false); if (arrDefiningAuthors.length > 1) throw Error("more than 1 address definition"); - var arrInnerAddressDefinition = arrDefiningAuthors[0].definition; + const arrInnerAddressDefinition = arrDefiningAuthors[0].definition; evaluate(arrInnerAddressDefinition, path, cb2); } }); @@ -688,18 +691,18 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'definition template': // ['definition template', ['unit', {param1: 'value1'}]] - var unit = args[0]; + const unit = args[0]; var params = args[1]; conn.query( "SELECT payload FROM messages JOIN units USING(unit) \n\ WHERE unit=? AND app='definition_template' AND main_chain_index<=? AND +sequence='good' AND is_stable=1", [unit, objValidationState.last_ball_mci], - function(rows){ + rows => { if (rows.length !== 1) throw Error("not 1 template"); - var template = rows[0].payload; - var arrTemplate = JSON.parse(template); - var arrFilledTemplate = replaceInTemplate(arrTemplate, params); + const template = rows[0].payload; + const arrTemplate = JSON.parse(template); + const arrFilledTemplate = replaceInTemplate(arrTemplate, params); evaluate(arrFilledTemplate, path, cb2); } ); @@ -707,14 +710,14 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'seen address': // ['seen address', 'BASE32'] - var seen_address = args; + const seen_address = args; conn.query( "SELECT 1 FROM unit_authors CROSS JOIN units USING(unit) \n\ WHERE address=? AND main_chain_index<=? AND sequence='good' AND is_stable=1 \n\ LIMIT 1", [seen_address, objValidationState.last_ball_mci], - function(rows){ - cb2(rows.length > 0); + ({length}) => { + cb2(length > 0); } ); break; @@ -732,18 +735,18 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit WHERE address=? AND definition_chash=? AND main_chain_index<=? AND sequence='good' AND is_stable=1 \n\ LIMIT 1", [changed_address, new_definition_chash, objValidationState.last_ball_mci], - function(rows){ - cb2(rows.length > 0); + ({length}) => { + cb2(length > 0); } ); break; case 'seen': // ['seen', {what: 'input', asset: 'asset or base', type: 'transfer'|'issue', own_funds: true, amount_at_least: 123, amount_at_most: 123, amount: 123, address: 'BASE32'}] - var filter = args; - var sql = "SELECT 1 FROM "+filter.what+"s CROSS JOIN units USING(unit) \n\ - LEFT JOIN assets ON asset=assets.unit \n\ - WHERE main_chain_index<=? AND sequence='good' AND is_stable=1 AND (asset IS NULL OR is_private=0) "; + const filter = args; + let sql = `SELECT 1 FROM ${filter.what}s CROSS JOIN units USING(unit) \n\ + LEFT JOIN assets ON asset=assets.unit \n\ + WHERE main_chain_index<=? AND sequence='good' AND is_stable=1 AND (asset IS NULL OR is_private=0) `; var params = [objValidationState.last_ball_mci]; if (filter.asset){ if (filter.asset === 'base') @@ -776,23 +779,23 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit } } sql += " LIMIT 1"; - conn.query(sql, params, function(rows){ - cb2(rows.length > 0); + conn.query(sql, params, ({length}) => { + cb2(length > 0); }); break; case 'cosigned by': // ['cosigned by', 'BASE32'] - var cosigner_address = args; - var arrAuthorAddresses = objUnit.authors.map(function(author){ return author.address; }); - console.log(op+" "+arrAuthorAddresses.indexOf(cosigner_address)); + const cosigner_address = args; + const arrAuthorAddresses = objUnit.authors.map(author => author.address); + console.log(`${op} ${arrAuthorAddresses.indexOf(cosigner_address)}`); cb2(arrAuthorAddresses.indexOf(cosigner_address) >= 0); break; case 'not': // useful for conditions such as: after timestamp but there's still no searched value in datafeed // sig, hash, and address cannot be negated - evaluate(args, path, function(not_res){ + evaluate(args, path, not_res => { cb2(!not_res); }); break; @@ -802,39 +805,39 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit var arrAddresses = args[0]; var feed_name = args[1]; var relation = args[2]; - var value = args[3]; + const value = args[3]; var min_mci = args[4] || 0; - var value_condition; + let value_condition; var index; var params = [arrAddresses, feed_name]; if (typeof value === "string"){ index = 'byNameStringValue'; - var isNumber = /^-?\d+\.?\d*$/.test(value); + const isNumber = /^-?\d+\.?\d*$/.test(value); if (isNumber){ - var bForceNumericComparison = (['>','>=','<','<='].indexOf(relation) >= 0); - var plus_0 = bForceNumericComparison ? '+0' : ''; - value_condition = '(value'+plus_0+relation+value+' OR int_value'+relation+value+')'; + const bForceNumericComparison = (['>','>=','<','<='].indexOf(relation) >= 0); + const plus_0 = bForceNumericComparison ? '+0' : ''; + value_condition = `(value${plus_0}${relation}${value} OR int_value${relation}${value})`; // params.push(value, value); } else{ - value_condition = 'value'+relation+'?'; + value_condition = `value${relation}?`; params.push(value); } } else{ index = 'byNameIntValue'; - value_condition = 'int_value'+relation+'?'; + value_condition = `int_value${relation}?`; params.push(value); } params.push(objValidationState.last_ball_mci, min_mci); conn.query( - "SELECT 1 FROM data_feeds "+db.forceIndex(index)+" CROSS JOIN units USING(unit) CROSS JOIN unit_authors USING(unit) \n\ - WHERE address IN(?) AND feed_name=? AND "+value_condition+" \n\ - AND main_chain_index<=? AND main_chain_index>=? AND sequence='good' AND is_stable=1 LIMIT 1", + `SELECT 1 FROM data_feeds ${db.forceIndex(index)} CROSS JOIN units USING(unit) CROSS JOIN unit_authors USING(unit) \n\ + WHERE address IN(?) AND feed_name=? AND ${value_condition} \n\ + AND main_chain_index<=? AND main_chain_index>=? AND sequence='good' AND is_stable=1 LIMIT 1`, params, - function(rows){ - console.log(op+" "+feed_name+" "+rows.length); - cb2(rows.length > 0); + ({length}) => { + console.log(`${op} ${feed_name} ${length}`); + cb2(length > 0); } ); break; @@ -846,12 +849,12 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit arrUsedPaths.push(path); var arrAddresses = args[0]; var feed_name = args[1]; - var element = args[2]; + const element = args[2]; var min_mci = args[3] || 0; - var serialized_proof = assocAuthentifiers[path]; - var proof = merkle.deserializeMerkleProof(serialized_proof); + const serialized_proof = assocAuthentifiers[path]; + const proof = merkle.deserializeMerkleProof(serialized_proof); if (!merkle.verifyMerkleProof(element, proof)){ - fatal_error = "bad merkle proof at path "+path; + fatal_error = `bad merkle proof at path ${path}`; return cb2(false); } conn.query( @@ -859,38 +862,38 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit WHERE address IN(?) AND feed_name=? AND value=? AND main_chain_index<=? AND main_chain_index>=? AND sequence='good' AND is_stable=1 \n\ LIMIT 1", [arrAddresses, feed_name, proof.root, objValidationState.last_ball_mci, min_mci], - function(rows){ - if (rows.length === 0) - fatal_error = "merkle proof at path "+path+" not found"; - cb2(rows.length > 0); + ({length}) => { + if (length === 0) + fatal_error = `merkle proof at path ${path} not found`; + cb2(length > 0); } ); break; case 'mci': var relation = args[0]; - var mci = args[1]; + const mci = args[1]; switch(relation){ case '>': return cb2(objValidationState.last_ball_mci > mci); case '>=': return cb2(objValidationState.last_ball_mci >= mci); case '<': return cb2(objValidationState.last_ball_mci < mci); case '<=': return cb2(objValidationState.last_ball_mci <= mci); case '=': return cb2(objValidationState.last_ball_mci === mci); - default: throw Error('unknown relation in mci: '+relation); + default: throw Error(`unknown relation in mci: ${relation}`); } break; case 'age': var relation = args[0]; - var age = args[1]; - var arrSrcUnits = []; - for (var i=0; i { + const bSatisfies = (length === arrSrcUnits.length); + console.log(`${op} ${bSatisfies}`); cb2(bSatisfies); } ); @@ -915,8 +918,8 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'has one': // ['has', {what: 'input', asset: 'asset or base', type: 'transfer'|'issue', own_funds: true, amount_at_least: 123, amount_at_most: 123, amount: 123, address: 'BASE32'}] // when an address is included (referenced from another address), own_funds refers to outer address' funds - augmentMessagesAndEvaluateFilter(op, args, function(res){ - console.log(op+" "+res, args); + augmentMessagesAndEvaluateFilter(op, args, res => { + console.log(`${op} ${res}`, args); cb2(res); }); break; @@ -924,16 +927,16 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'has equal': case 'has one equal': // ['has equal', {equal_fields: ['address', 'amount'], search_criteria: [{what: 'output', asset: 'asset1', address: 'BASE32'}, {what: 'input', asset: 'asset2', type: 'issue', own_funds: true, address: 'ANOTHERBASE32'}]}] - augmentMessagesAndEvaluateFilter("has", args.search_criteria[0], function(res1, arrFirstObjects){ + augmentMessagesAndEvaluateFilter("has", args.search_criteria[0], (res1, arrFirstObjects) => { if (!res1) return cb2(false); - augmentMessagesAndEvaluateFilter("has", args.search_criteria[1], function(res2, arrSecondObjects){ + augmentMessagesAndEvaluateFilter("has", args.search_criteria[1], (res2, arrSecondObjects) => { if (!res2) return cb2(false); - var count_equal_pairs = 0; - for (var i=0; i arrFirstObjects[i][field] !== arrSecondObjects[j][field])) count_equal_pairs++; if (count_equal_pairs === 0) return cb2(false); @@ -948,12 +951,12 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit case 'sum': // ['sum', {filter: {what: 'input', asset: 'asset or base', type: 'transfer'|'issue', own_funds: true, address: 'BASE32'}, at_least: 123, at_most: 123, equals: 123}] - augmentMessagesAndEvaluateFilter("has", args.filter, function(res, arrFoundObjects){ - var sum = 0; + augmentMessagesAndEvaluateFilter("has", args.filter, (res, arrFoundObjects) => { + let sum = 0; if (res) - for (var i=0; i= args.at_least) @@ -972,14 +975,14 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit changed_address = address; if (new_definition_chash === 'this address') new_definition_chash = address; - cb2(objUnit.messages.some(function(message){ - if (message.app !== 'address_definition_change') + cb2(objUnit.messages.some(({app, payload}) => { + if (app !== 'address_definition_change') return false; - if (!message.payload) + if (!payload) return false; - if (message.payload.definition_chash !== new_definition_chash) + if (payload.definition_chash !== new_definition_chash) return false; - var address = message.payload.address || objUnit.authors[0].address; + const address = payload.address || objUnit.authors[0].address; return (address === changed_address); })); break; @@ -1001,15 +1004,15 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit function evaluateFilter(op, filter, handleResult){ - var filter_address = filter.address; + let filter_address = filter.address; if (filter_address === 'this address') filter_address = address; - var arrFoundObjects = []; - for (var i=0; i author.address); objValidationState.arrAugmentedMessages = _.cloneDeep(objUnit.messages); async.eachSeries( objValidationState.arrAugmentedMessages, - function(message, cb3){ + (message, cb3) => { if (message.app !== 'payment' || !message.payload) // we are looking only for public payments return cb3(); - var payload = message.payload; + const payload = message.payload; if (!payload.inputs) // skip now, will choke when checking the message return cb3(); console.log("augmenting inputs"); async.eachSeries( payload.inputs, - function(input, cb4){ + (input, cb4) => { console.log("input", input); if (input.type === "issue"){ if (!input.address) @@ -1101,7 +1104,7 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit conn.query( "SELECT amount, address FROM outputs WHERE unit=? AND message_index=? AND output_index=?", [input.unit, input.message_index, input.output_index], - function(rows){ + rows => { if (rows.length === 1){ console.log("src", rows[0]); input.amount = rows[0].amount; @@ -1121,7 +1124,7 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit ); } - var bAssetCondition = (assocAuthentifiers === null); + const bAssetCondition = (assocAuthentifiers === null); if (bAssetCondition && address || !bAssetCondition && this_asset) throw Error("incompatible params"); var fatal_error = null; @@ -1132,15 +1135,15 @@ function validateAuthentifiers(conn, address, this_asset, arrDefinition, objUnit // 2. redefinition of a referenced address might introduce loops that will drive complexity to infinity // 3. if an inner address was redefined by keychange but the definition for the new keyset not supplied before last ball, the address // becomes temporarily unusable - validateDefinition(conn, arrDefinition, objUnit, objValidationState, Object.keys(assocAuthentifiers), bAssetCondition, function(err){ + validateDefinition(conn, arrDefinition, objUnit, objValidationState, Object.keys(assocAuthentifiers), bAssetCondition, err => { if (err) return cb(err); //console.log("eval def"); - evaluate(arrDefinition, 'r', function(res){ + evaluate(arrDefinition, 'r', res => { if (fatal_error) return cb(fatal_error); if (!bAssetCondition && arrUsedPaths.length !== Object.keys(assocAuthentifiers).length) - return cb("some authentifiers are not used, res="+res+", used="+arrUsedPaths+", passed="+JSON.stringify(assocAuthentifiers)); + return cb(`some authentifiers are not used, res=${res}, used=${arrUsedPaths}, passed=${JSON.stringify(assocAuthentifiers)}`); cb(null, res); }); }); @@ -1156,16 +1159,16 @@ function replaceInTemplate(arrTemplate, params){ // searching for pattern "$name" if (x.charAt(0) !== '$') return x; - var name = x.substring(1); + const name = x.substring(1); if (!(name in params)) - throw NoVarException("variable "+name+" not specified"); + throw NoVarException(`variable ${name} not specified`); return params[name]; // may change type if params[name] is not a string case 'object': if (Array.isArray(x)) - for (var i=0; i { if (response === 'updated') return callbacks.ifOk(); - var error = response.error || ("unrecognized response: "+JSON.stringify(response)); + const error = response.error || (`unrecognized response: ${JSON.stringify(response)}`); callbacks.ifError(error); }); } function createTempPubkeyPackage(temp_pubkey){ - var objTempPubkey = { - temp_pubkey: temp_pubkey, + const objTempPubkey = { + temp_pubkey, pubkey: objMyPermanentDeviceKey.pub_b64 }; objTempPubkey.signature = ecdsaSig.sign(objectHash.getDeviceMessageHashToSign(objTempPubkey), objMyPermanentDeviceKey.priv); @@ -195,7 +194,7 @@ function createTempPubkeyPackage(temp_pubkey){ function genPrivKey(){ - var privKey; + let privKey; do { console.log("generating new priv key"); privKey = crypto.randomBytes(32); @@ -204,10 +203,10 @@ function genPrivKey(){ return privKey; } -var last_rotate_wake_ts = Date.now(); +let last_rotate_wake_ts = Date.now(); function rotateTempDeviceKeyIfCouldBeAlreadyUsed(){ - var actual_interval = Date.now() - last_rotate_wake_ts; + const actual_interval = Date.now() - last_rotate_wake_ts; last_rotate_wake_ts = Date.now(); if (actual_interval > TEMP_DEVICE_KEY_ROTATION_PERIOD + 1000) return console.log("woke up after sleep or high load, will skip rotation"); @@ -223,20 +222,20 @@ function rotateTempDeviceKey(){ console.log("will rotate temp device key"); network.findOutboundPeerOrConnect(conf.WS_PROTOCOL+my_device_hub, function onLocatedHubForRotation(err, ws){ if (err) - return console.log('will not rotate because: '+err); + return console.log(`will not rotate because: ${err}`); if (ws.readyState !== ws.OPEN) return console.log('will not rotate because connection is not open'); if (!ws.bLoggedIn) return console.log('will not rotate because not logged in'); // reconnected and not logged in yet - var new_priv_key = genPrivKey(); - var objNewMyTempDeviceKey = { + const new_priv_key = genPrivKey(); + const objNewMyTempDeviceKey = { use_count: 0, priv: new_priv_key, pub_b64: ecdsa.publicKeyCreate(new_priv_key, true).toString('base64') }; - saveTempKeys(new_priv_key, objMyTempDeviceKey.priv, function(err){ + saveTempKeys(new_priv_key, objMyTempDeviceKey.priv, err => { if (err){ - console.log('failed to save new temp keys, canceling: '+err); + console.log(`failed to save new temp keys, canceling: ${err}`); return; } objMyPrevTempDeviceKey = objMyTempDeviceKey; @@ -252,9 +251,9 @@ function scheduleTempDeviceKeyRotation(){ return; bScheduledTempDeviceKeyRotation = true; console.log('will schedule rotation in 1 minute'); - setTimeout(function(){ + setTimeout(() => { // due to timeout, we are probably last to request (and receive) this lock - mutex.lock(["from_hub"], function(unlock){ + mutex.lock(["from_hub"], unlock => { console.log("will schedule rotation"); rotateTempDeviceKeyIfCouldBeAlreadyUsed(); last_rotate_wake_ts = Date.now(); @@ -269,13 +268,13 @@ function scheduleTempDeviceKeyRotation(){ // sending/receiving messages function deriveSharedSecret(ecdh, peer_b64_pubkey){ - var shared_secret_src = ecdh.computeSecret(peer_b64_pubkey, "base64"); - var shared_secret = crypto.createHash("sha256").update(shared_secret_src).digest().slice(0, 16); + const shared_secret_src = ecdh.computeSecret(peer_b64_pubkey, "base64"); + const shared_secret = crypto.createHash("sha256").update(shared_secret_src).digest().slice(0, 16); return shared_secret; } function decryptPackage(objEncryptedPackage){ - var priv_key; + let priv_key; if (objEncryptedPackage.dh.recipient_ephemeral_pubkey === objMyTempDeviceKey.pub_b64){ priv_key = objMyTempDeviceKey.priv; if (objMyTempDeviceKey.use_count) @@ -297,39 +296,39 @@ function decryptPackage(objEncryptedPackage){ } else{ console.log("message encrypted to unknown key"); - setTimeout(function(){ - throw Error("message encrypted to unknown key, device "+my_device_address+", len="+objEncryptedPackage.encrypted_message.length+". The error might be caused by restoring from an old backup or using the same keys on another device."); + setTimeout(() => { + throw Error(`message encrypted to unknown key, device ${my_device_address}, len=${objEncryptedPackage.encrypted_message.length}. The error might be caused by restoring from an old backup or using the same keys on another device.`); }, 100); // eventBus.emit('nonfatal_error', "message encrypted to unknown key, device "+my_device_address+", len="+objEncryptedPackage.encrypted_message.length, new Error('unknown key')); return null; } - var ecdh = crypto.createECDH('secp256k1'); + const ecdh = crypto.createECDH('secp256k1'); if (process.browser) // workaround bug in crypto-browserify https://github.com/crypto-browserify/createECDH/issues/9 ecdh.generateKeys("base64", "compressed"); ecdh.setPrivateKey(priv_key); - var shared_secret = deriveSharedSecret(ecdh, objEncryptedPackage.dh.sender_ephemeral_pubkey); - var iv = new Buffer(objEncryptedPackage.iv, 'base64'); - var decipher = crypto.createDecipheriv('aes-128-gcm', shared_secret, iv); - var authtag = new Buffer(objEncryptedPackage.authtag, 'base64'); + const shared_secret = deriveSharedSecret(ecdh, objEncryptedPackage.dh.sender_ephemeral_pubkey); + const iv = new Buffer(objEncryptedPackage.iv, 'base64'); + const decipher = crypto.createDecipheriv('aes-128-gcm', shared_secret, iv); + const authtag = new Buffer(objEncryptedPackage.authtag, 'base64'); decipher.setAuthTag(authtag); - var enc_buf = Buffer(objEncryptedPackage.encrypted_message, "base64"); + const enc_buf = Buffer(objEncryptedPackage.encrypted_message, "base64"); // var decrypted1 = decipher.update(enc_buf); // under browserify, decryption of long buffers fails with Array buffer allocation errors, have to split the buffer into chunks - var arrChunks = []; - var CHUNK_LENGTH = 4096; - for (var offset = 0; offset < enc_buf.length; offset += CHUNK_LENGTH){ + let arrChunks = []; + const CHUNK_LENGTH = 4096; + for (let offset = 0; offset < enc_buf.length; offset += CHUNK_LENGTH){ // console.log('offset '+offset); arrChunks.push(decipher.update(enc_buf.slice(offset, Math.min(offset+CHUNK_LENGTH, enc_buf.length)))); } - var decrypted1 = Buffer.concat(arrChunks); + const decrypted1 = Buffer.concat(arrChunks); arrChunks = null; - var decrypted2 = decipher.final(); - breadcrumbs.add("decrypted lengths: "+decrypted1.length+" + "+decrypted2.length); - var decrypted_message_buf = Buffer.concat([decrypted1, decrypted2]); - var decrypted_message = decrypted_message_buf.toString("utf8"); - console.log("decrypted: "+decrypted_message); - var json = JSON.parse(decrypted_message); + const decrypted2 = decipher.final(); + breadcrumbs.add(`decrypted lengths: ${decrypted1.length} + ${decrypted2.length}`); + const decrypted_message_buf = Buffer.concat([decrypted1, decrypted2]); + const decrypted_message = decrypted_message_buf.toString("utf8"); + console.log(`decrypted: ${decrypted_message}`); + const json = JSON.parse(decrypted_message); if (json.encrypted_package){ // strip another layer of encryption console.log("inner encryption"); return decryptPackage(json.encrypted_package); @@ -340,15 +339,15 @@ function decryptPackage(objEncryptedPackage){ // a hack to read large text from cordova sqlite function readMessageInChunksFromOutbox(message_hash, len, handleMessage){ - var CHUNK_LEN = 1000000; - var start = 1; - var message = ''; + const CHUNK_LEN = 1000000; + let start = 1; + let message = ''; function readChunk(){ - db.query("SELECT SUBSTR(message, ?, ?) AS chunk FROM outbox WHERE message_hash=?", [start, CHUNK_LEN, message_hash], function(rows){ + db.query("SELECT SUBSTR(message, ?, ?) AS chunk FROM outbox WHERE message_hash=?", [start, CHUNK_LEN, message_hash], rows => { if (rows.length === 0) return handleMessage(); if (rows.length > 1) - throw Error(rows.length+' msgs by hash in outbox, start='+start+', length='+len); + throw Error(`${rows.length} msgs by hash in outbox, start=${start}, length=${len}`); message += rows[0].chunk; start += CHUNK_LEN; (start > len) ? handleMessage(message) : readChunk(); @@ -359,33 +358,33 @@ function readMessageInChunksFromOutbox(message_hash, len, handleMessage){ function resendStalledMessages(delay){ var delay = delay || 0; - console.log("resending stalled messages delayed by "+delay+" minute"); + console.log(`resending stalled messages delayed by ${delay} minute`); if (!objMyPermanentDeviceKey) return console.log("objMyPermanentDeviceKey not set yet, can't resend stalled messages"); - mutex.lockOrSkip(['stalled'], function(unlock){ - var bCordova = (typeof window !== 'undefined' && window && window.cordova); + mutex.lockOrSkip(['stalled'], unlock => { + const bCordova = (typeof window !== 'undefined' && window && window.cordova); db.query( - "SELECT "+(bCordova ? "LENGTH(message) AS len" : "message")+", message_hash, `to`, pubkey, hub \n\ - FROM outbox JOIN correspondent_devices ON `to`=device_address \n\ - WHERE outbox.creation_date<="+db.addTime("-"+delay+" MINUTE")+" ORDER BY outbox.creation_date", - function(rows){ - console.log(rows.length+" stalled messages"); + `SELECT ${bCordova ? "LENGTH(message) AS len" : "message"}, message_hash, \`to\`, pubkey, hub \n\ + FROM outbox JOIN correspondent_devices ON \`to\`=device_address \n\ + WHERE outbox.creation_date<=${db.addTime(`-${delay} MINUTE`)} ORDER BY outbox.creation_date`, + rows => { + console.log(`${rows.length} stalled messages`); async.eachSeries( rows, - function(row, cb){ + (row, cb) => { if (!row.hub){ // weird error - eventBus.emit('nonfatal_error', "no hub in resendStalledMessages: "+JSON.stringify(row)+", l="+rows.length, new Error('no hub')); + eventBus.emit('nonfatal_error', `no hub in resendStalledMessages: ${JSON.stringify(row)}, l=${rows.length}`, new Error('no hub')); return cb(); } // throw Error("no hub in resendStalledMessages: "+JSON.stringify(row)); - var send = function(message){ + const send = message => { if (!message) // the message is already gone return cb(); - var objDeviceMessage = JSON.parse(message); + const objDeviceMessage = JSON.parse(message); //if (objDeviceMessage.to !== row.to) // throw "to mismatch"; - console.log('sending stalled '+row.message_hash); - sendPreparedMessageToHub(row.hub, row.pubkey, row.message_hash, objDeviceMessage, {ifOk: cb, ifError: function(err){ cb(); }}); + console.log(`sending stalled ${row.message_hash}`); + sendPreparedMessageToHub(row.hub, row.pubkey, row.message_hash, objDeviceMessage, {ifOk: cb, ifError(err) { cb(); }}); }; bCordova ? readMessageInChunksFromOutbox(row.message_hash, row.len, send) : send(row.message); }, @@ -396,25 +395,25 @@ function resendStalledMessages(delay){ }); } -setInterval(function(){ resendStalledMessages(1); }, SEND_RETRY_PERIOD); +setInterval(() => { resendStalledMessages(1); }, SEND_RETRY_PERIOD); // reliable delivery // first param is either WebSocket or hostname of the hub function reliablySendPreparedMessageToHub(ws, recipient_device_pubkey, json, callbacks, conn){ - var recipient_device_address = objectHash.getDeviceAddress(recipient_device_pubkey); - console.log('will encrypt and send to '+recipient_device_address+': '+JSON.stringify(json)); + const recipient_device_address = objectHash.getDeviceAddress(recipient_device_pubkey); + console.log(`will encrypt and send to ${recipient_device_address}: ${JSON.stringify(json)}`); // encrypt to recipient's permanent pubkey before storing the message into outbox - var objEncryptedPackage = createEncryptedPackage(json, recipient_device_pubkey); + const objEncryptedPackage = createEncryptedPackage(json, recipient_device_pubkey); // if the first attempt fails, this will be the inner message - var objDeviceMessage = { + const objDeviceMessage = { encrypted_package: objEncryptedPackage }; - var message_hash = objectHash.getBase64Hash(objDeviceMessage); + const message_hash = objectHash.getBase64Hash(objDeviceMessage); conn = conn || db; conn.query( "INSERT INTO outbox (message_hash, `to`, message) VALUES (?,?,?)", [message_hash, recipient_device_address, JSON.stringify(objDeviceMessage)], - function(){ + () => { if (callbacks && callbacks.onSaved){ callbacks.onSaved(); // db in resendStalledMessages will block until the transaction commits, assuming only 1 db connection @@ -431,12 +430,12 @@ function reliablySendPreparedMessageToHub(ws, recipient_device_pubkey, json, cal // first param is either WebSocket or hostname of the hub function sendPreparedMessageToHub(ws, recipient_device_pubkey, message_hash, json, callbacks){ if (!callbacks) - callbacks = {ifOk: function(){}, ifError: function(){}}; + callbacks = {ifOk() {}, ifError() {}}; if (typeof ws === "string"){ - var hub_host = ws; + const hub_host = ws; network.findOutboundPeerOrConnect(conf.WS_PROTOCOL+hub_host, function onLocatedHubForSend(err, ws){ if (err){ - db.query("UPDATE outbox SET last_error=? WHERE message_hash=?", [err, message_hash], function(){}); + db.query("UPDATE outbox SET last_error=? WHERE message_hash=?", [err, message_hash], () => {}); return callbacks.ifError(err); } sendPreparedMessageToConnectedHub(ws, recipient_device_pubkey, message_hash, json, callbacks); @@ -448,72 +447,72 @@ function sendPreparedMessageToHub(ws, recipient_device_pubkey, message_hash, jso // first param is WebSocket only function sendPreparedMessageToConnectedHub(ws, recipient_device_pubkey, message_hash, json, callbacks){ - network.sendRequest(ws, 'hub/get_temp_pubkey', recipient_device_pubkey, false, function(ws, request, response){ + network.sendRequest(ws, 'hub/get_temp_pubkey', recipient_device_pubkey, false, (ws, request, response) => { function handleError(error){ callbacks.ifError(error); - db.query("UPDATE outbox SET last_error=? WHERE message_hash=?", [error, message_hash], function(){}); + db.query("UPDATE outbox SET last_error=? WHERE message_hash=?", [error, message_hash], () => {}); } if (response.error) return handleError(response.error); - var objTempPubkey = response; + const objTempPubkey = response; if (!objTempPubkey.temp_pubkey || !objTempPubkey.pubkey || !objTempPubkey.signature) return handleError("missing fields in hub response"); if (objTempPubkey.pubkey !== recipient_device_pubkey) return handleError("temp pubkey signed by wrong permanent pubkey"); if (!ecdsaSig.verify(objectHash.getDeviceMessageHashToSign(objTempPubkey), objTempPubkey.signature, objTempPubkey.pubkey)) return handleError("wrong sig under temp pubkey"); - var objEncryptedPackage = createEncryptedPackage(json, objTempPubkey.temp_pubkey); - var recipient_device_address = objectHash.getDeviceAddress(recipient_device_pubkey); - var objDeviceMessage = { + const objEncryptedPackage = createEncryptedPackage(json, objTempPubkey.temp_pubkey); + const recipient_device_address = objectHash.getDeviceAddress(recipient_device_pubkey); + const objDeviceMessage = { encrypted_package: objEncryptedPackage, to: recipient_device_address, pubkey: objMyPermanentDeviceKey.pub_b64 // who signs. Essentially, the from again. }; objDeviceMessage.signature = ecdsaSig.sign(objectHash.getDeviceMessageHashToSign(objDeviceMessage), objMyPermanentDeviceKey.priv); - network.sendRequest(ws, 'hub/deliver', objDeviceMessage, false, function(ws, request, response){ + network.sendRequest(ws, 'hub/deliver', objDeviceMessage, false, (ws, request, response) => { if (response === "accepted"){ - db.query("DELETE FROM outbox WHERE message_hash=?", [message_hash], function(){ + db.query("DELETE FROM outbox WHERE message_hash=?", [message_hash], () => { callbacks.ifOk(); }); } else - handleError( response.error || ("unrecognized response: "+JSON.stringify(response)) ); + handleError( response.error || (`unrecognized response: ${JSON.stringify(response)}`) ); }); }); } function createEncryptedPackage(json, recipient_device_pubkey){ - var text = JSON.stringify(json); + const text = JSON.stringify(json); // console.log("will encrypt and send: "+text); - var ecdh = crypto.createECDH('secp256k1'); - var sender_ephemeral_pubkey = ecdh.generateKeys("base64", "compressed"); - var shared_secret = deriveSharedSecret(ecdh, recipient_device_pubkey); // Buffer + const ecdh = crypto.createECDH('secp256k1'); + const sender_ephemeral_pubkey = ecdh.generateKeys("base64", "compressed"); + const shared_secret = deriveSharedSecret(ecdh, recipient_device_pubkey); // Buffer console.log(shared_secret.length); // we could also derive iv from the unused bits of ecdh.computeSecret() and save some bandwidth - var iv = crypto.randomBytes(12); // 128 bits (16 bytes) total, we take 12 bytes for random iv and leave 4 bytes for the counter - var cipher = crypto.createCipheriv("aes-128-gcm", shared_secret, iv); + const iv = crypto.randomBytes(12); // 128 bits (16 bytes) total, we take 12 bytes for random iv and leave 4 bytes for the counter + const cipher = crypto.createCipheriv("aes-128-gcm", shared_secret, iv); // under browserify, encryption of long strings fails with Array buffer allocation errors, have to split the string into chunks - var arrChunks = []; - var CHUNK_LENGTH = 2003; - for (var offset = 0; offset < text.length; offset += CHUNK_LENGTH){ + let arrChunks = []; + const CHUNK_LENGTH = 2003; + for (let offset = 0; offset < text.length; offset += CHUNK_LENGTH){ // console.log('offset '+offset); arrChunks.push(cipher.update(text.slice(offset, Math.min(offset+CHUNK_LENGTH, text.length)), 'utf8')); } arrChunks.push(cipher.final()); - var encrypted_message_buf = Buffer.concat(arrChunks); + const encrypted_message_buf = Buffer.concat(arrChunks); arrChunks = null; // var encrypted_message_buf = Buffer.concat([cipher.update(text, "utf8"), cipher.final()]); //console.log(encrypted_message_buf); - var encrypted_message = encrypted_message_buf.toString("base64"); + const encrypted_message = encrypted_message_buf.toString("base64"); //console.log(encrypted_message); - var authtag = cipher.getAuthTag(); + const authtag = cipher.getAuthTag(); // this is visible and verifiable by the hub - var encrypted_package = { - encrypted_message: encrypted_message, + const encrypted_package = { + encrypted_message, iv: iv.toString('base64'), authtag: authtag.toString('base64'), dh: { - sender_ephemeral_pubkey: sender_ephemeral_pubkey, + sender_ephemeral_pubkey, recipient_ephemeral_pubkey: recipient_device_pubkey } }; @@ -523,26 +522,25 @@ function createEncryptedPackage(json, recipient_device_pubkey){ // first param is either WebSocket or hostname of the hub or null function sendMessageToHub(ws, recipient_device_pubkey, subject, body, callbacks, conn){ // this content is hidden from the hub by encryption - var json = { + const json = { from: my_device_address, // presence of this field guarantees that you cannot strip off the signature and add your own signature instead device_hub: my_device_hub, - subject: subject, - body: body + subject, + body }; conn = conn || db; if (ws) return reliablySendPreparedMessageToHub(ws, recipient_device_pubkey, json, callbacks, conn); - var recipient_device_address = objectHash.getDeviceAddress(recipient_device_pubkey); - conn.query("SELECT hub FROM correspondent_devices WHERE device_address=?", [recipient_device_address], function(rows){ + const recipient_device_address = objectHash.getDeviceAddress(recipient_device_pubkey); + conn.query("SELECT hub FROM correspondent_devices WHERE device_address=?", [recipient_device_address], rows => { if (rows.length !== 1) throw Error("no hub in correspondents"); reliablySendPreparedMessageToHub(rows[0].hub, recipient_device_pubkey, json, callbacks, conn); }); } -function sendMessageToDevice(device_address, subject, body, callbacks, conn){ - conn = conn || db; - conn.query("SELECT hub, pubkey FROM correspondent_devices WHERE device_address=?", [device_address], function(rows){ +function sendMessageToDevice(device_address, subject, body, callbacks, conn = db) { + conn.query("SELECT hub, pubkey FROM correspondent_devices WHERE device_address=?", [device_address], rows => { if (rows.length !== 1) throw Error("correspondent not found"); sendMessageToHub(rows[0].hub, rows[0].pubkey, subject, body, callbacks, conn); @@ -556,29 +554,29 @@ function sendMessageToDevice(device_address, subject, body, callbacks, conn){ function sendPairingMessage(hub_host, recipient_device_pubkey, pairing_secret, reverse_pairing_secret, callbacks){ - var body = {pairing_secret: pairing_secret, device_name: my_device_name}; + const body = {pairing_secret, device_name: my_device_name}; if (reverse_pairing_secret) body.reverse_pairing_secret = reverse_pairing_secret; sendMessageToHub(hub_host, recipient_device_pubkey, "pairing", body, callbacks); } function startWaitingForPairing(handlePairingInfo){ - var pairing_secret = crypto.randomBytes(9).toString("base64"); - var pairingInfo = { - pairing_secret: pairing_secret, + const pairing_secret = crypto.randomBytes(9).toString("base64"); + const pairingInfo = { + pairing_secret, device_pubkey: objMyPermanentDeviceKey.pub_b64, device_address: my_device_address, hub: my_device_hub }; - db.query("INSERT INTO pairing_secrets (pairing_secret, expiry_date) VALUES(?, "+db.addTime("+1 MONTH")+")", [pairing_secret], function(){ + db.query(`INSERT INTO pairing_secrets (pairing_secret, expiry_date) VALUES(?, ${db.addTime("+1 MONTH")})`, [pairing_secret], () => { handlePairingInfo(pairingInfo); }); } // {pairing_secret: "random string", device_name: "Bob's MacBook Pro", reverse_pairing_secret: "random string"} function handlePairingMessage(json, device_pubkey, callbacks){ - var body = json.body; - var from_address = objectHash.getDeviceAddress(device_pubkey); + const body = json.body; + const from_address = objectHash.getDeviceAddress(device_pubkey); if (!ValidationUtils.isNonemptyString(body.pairing_secret)) return callbacks.ifError("correspondent not known and no pairing secret"); if (!ValidationUtils.isNonemptyString(json.device_hub)) // home hub of the sender @@ -588,24 +586,24 @@ function handlePairingMessage(json, device_pubkey, callbacks){ if ("reverse_pairing_secret" in body && !ValidationUtils.isNonemptyString(body.reverse_pairing_secret)) return callbacks.ifError("bad reverse pairing secret"); db.query( - "SELECT is_permanent FROM pairing_secrets WHERE pairing_secret=? AND expiry_date > "+db.getNow(), + `SELECT is_permanent FROM pairing_secrets WHERE pairing_secret=? AND expiry_date > ${db.getNow()}`, [body.pairing_secret], - function(pairing_rows){ + pairing_rows => { if (pairing_rows.length === 0) return callbacks.ifError("pairing secret not found or expired"); // add new correspondent and delete pending pairing db.query( - "INSERT "+db.getIgnore()+" INTO correspondent_devices (device_address, pubkey, hub, name, is_confirmed) VALUES (?,?,?,?,1)", + `INSERT ${db.getIgnore()} INTO correspondent_devices (device_address, pubkey, hub, name, is_confirmed) VALUES (?,?,?,?,1)`, [from_address, device_pubkey, json.device_hub, body.device_name], - function(){ + () => { db.query( // don't update name if already confirmed "UPDATE correspondent_devices SET is_confirmed=1, name=? WHERE device_address=? AND is_confirmed=0", [body.device_name, from_address], - function(){ + () => { eventBus.emit("paired", from_address, body.pairing_secret); if (pairing_rows[0].is_permanent === 0){ // multiple peers can pair through permanent secret - db.query("DELETE FROM pairing_secrets WHERE pairing_secret=?", [body.pairing_secret], function(){}); - eventBus.emit('paired_by_secret-'+body.pairing_secret, from_address); + db.query("DELETE FROM pairing_secrets WHERE pairing_secret=?", [body.pairing_secret], () => {}); + eventBus.emit(`paired_by_secret-${body.pairing_secret}`, from_address); } if (body.reverse_pairing_secret) sendPairingMessage(json.device_hub, device_pubkey, body.reverse_pairing_secret, null); @@ -625,11 +623,11 @@ function handlePairingMessage(json, device_pubkey, callbacks){ function addUnconfirmedCorrespondent(device_pubkey, device_hub, device_name, onDone){ console.log("addUnconfirmedCorrespondent"); - var device_address = objectHash.getDeviceAddress(device_pubkey); + const device_address = objectHash.getDeviceAddress(device_pubkey); db.query( - "INSERT "+db.getIgnore()+" INTO correspondent_devices (device_address, pubkey, hub, name, is_confirmed) VALUES (?,?,?,?,0)", + `INSERT ${db.getIgnore()} INTO correspondent_devices (device_address, pubkey, hub, name, is_confirmed) VALUES (?,?,?,?,0)`, [device_address, device_pubkey, device_hub, device_name], - function(){ + () => { if (onDone) onDone(device_address); } @@ -637,13 +635,13 @@ function addUnconfirmedCorrespondent(device_pubkey, device_hub, device_name, onD } function readCorrespondents(handleCorrespondents){ - db.query("SELECT device_address, hub, name, my_record_pref, peer_record_pref FROM correspondent_devices ORDER BY name", function(rows){ + db.query("SELECT device_address, hub, name, my_record_pref, peer_record_pref FROM correspondent_devices ORDER BY name", rows => { handleCorrespondents(rows); }); } function readCorrespondent(device_address, handleCorrespondent){ - db.query("SELECT device_address, hub, name, my_record_pref, peer_record_pref FROM correspondent_devices WHERE device_address=?", [device_address], function(rows){ + db.query("SELECT device_address, hub, name, my_record_pref, peer_record_pref FROM correspondent_devices WHERE device_address=?", [device_address], rows => { handleCorrespondent(rows.length ? rows[0] : null); }); } @@ -652,7 +650,7 @@ function readCorrespondentsByDeviceAddresses(arrDeviceAddresses, handleCorrespon db.query( "SELECT device_address, hub, name, pubkey, my_record_pref, peer_record_pref FROM correspondent_devices WHERE device_address IN(?) ORDER BY name", [arrDeviceAddresses], - function(rows){ + rows => { handleCorrespondents(rows); } ); @@ -662,20 +660,20 @@ function updateCorrespondentProps(correspondent, onDone){ db.query( "UPDATE correspondent_devices SET hub=?, name=?, my_record_pref=?, peer_record_pref=? WHERE device_address=?", [correspondent.hub, correspondent.name, correspondent.my_record_pref, correspondent.peer_record_pref, correspondent.device_address], - function(){ + () => { if (onDone) onDone(); } ); } function addIndirectCorrespondents(arrOtherCosigners, onDone){ - async.eachSeries(arrOtherCosigners, function(correspondent, cb){ - if (correspondent.device_address === my_device_address) + async.eachSeries(arrOtherCosigners, ({device_address, hub, name, pubkey}, cb) => { + if (device_address === my_device_address) return cb(); db.query( - "INSERT "+db.getIgnore()+" INTO correspondent_devices (device_address, hub, name, pubkey, is_indirect) VALUES(?,?,?,?,1)", - [correspondent.device_address, correspondent.hub, correspondent.name, correspondent.pubkey], - function(){ + `INSERT ${db.getIgnore()} INTO correspondent_devices (device_address, hub, name, pubkey, is_indirect) VALUES(?,?,?,?,1)`, + [device_address, hub, name, pubkey], + () => { cb(); } ); @@ -683,8 +681,8 @@ function addIndirectCorrespondents(arrOtherCosigners, onDone){ } function removeCorrespondentDevice(device_address, onDone){ - breadcrumbs.add('correspondent removed: '+device_address); - var arrQueries = []; + breadcrumbs.add(`correspondent removed: ${device_address}`); + const arrQueries = []; db.addQuery(arrQueries, "DELETE FROM outbox WHERE `to`=?", [device_address]); db.addQuery(arrQueries, "DELETE FROM correspondent_devices WHERE device_address=?", [device_address]); async.series(arrQueries, onDone); @@ -698,17 +696,17 @@ function getWitnessesFromHub(cb){ console.log('getWitnessesFromHub'); if (!my_device_hub){ console.log('getWitnessesFromHub: no hub yet'); - return setTimeout(function(){ + return setTimeout(() => { getWitnessesFromHub(cb); }, 2000); } - network.findOutboundPeerOrConnect(conf.WS_PROTOCOL+my_device_hub, function(err, ws){ + network.findOutboundPeerOrConnect(conf.WS_PROTOCOL+my_device_hub, (err, ws) => { if (err) return cb(err); - network.sendRequest(ws, 'get_witnesses', null, false, function(ws, request, response){ + network.sendRequest(ws, 'get_witnesses', null, false, (ws, request, response) => { if (response.error) return cb(response.error); - var arrWitnessesFromHub = response; + const arrWitnessesFromHub = response; cb(null, arrWitnessesFromHub); }); }); @@ -717,11 +715,11 @@ function getWitnessesFromHub(cb){ // responseHandler(error, response) callback function requestFromHub(command, params, responseHandler){ if (!my_device_hub) - return setTimeout(function(){ requestFromHub(command, params, responseHandler); }, 2000); - network.findOutboundPeerOrConnect(conf.WS_PROTOCOL+my_device_hub, function(err, ws){ + return setTimeout(() => { requestFromHub(command, params, responseHandler); }, 2000); + network.findOutboundPeerOrConnect(conf.WS_PROTOCOL+my_device_hub, (err, ws) => { if (err) return responseHandler(err); - network.sendRequest(ws, command, params, false, function(ws, request, response){ + network.sendRequest(ws, command, params, false, (ws, request, response) => { if (response.error) return responseHandler(response.error); responseHandler(null, response); diff --git a/divisible_asset.js b/divisible_asset.js index 89950e8e..1ae92624 100644 --- a/divisible_asset.js +++ b/divisible_asset.js @@ -1,15 +1,14 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var _ = require('lodash'); -var constants = require('./constants.js'); -var storage = require('./storage.js'); -var objectHash = require("./object_hash.js"); -var db = require('./db.js'); -var composer = require("./composer.js"); -var ValidationUtils = require('./validation_utils.js'); -var validation = require('./validation.js'); -var writer = require('./writer.js'); +const async = require('async'); +const _ = require('lodash'); +const constants = require('./constants.js'); +const storage = require('./storage.js'); +const objectHash = require("./object_hash.js"); +const db = require('./db.js'); +const composer = require("./composer.js"); +const ValidationUtils = require('./validation_utils.js'); +const validation = require('./validation.js'); +const writer = require('./writer.js'); function validateAndSavePrivatePaymentChain(conn, arrPrivateElements, callbacks){ @@ -18,56 +17,57 @@ function validateAndSavePrivatePaymentChain(conn, arrPrivateElements, callbacks) } -function validateAndSaveDivisiblePrivatePayment(conn, objPrivateElement, callbacks){ +function validateAndSaveDivisiblePrivatePayment(conn, objPrivateElement, {ifError, ifOk}) { validateDivisiblePrivatePayment(conn, objPrivateElement, { - ifError: callbacks.ifError, - ifOk: function(bStable, arrAuthorAddresses){ - console.log("private validation OK "+bStable); - var unit = objPrivateElement.unit; - var message_index = objPrivateElement.message_index; - var payload = objPrivateElement.payload; - var arrQueries = []; + ifError: ifError, + ifOk(bStable, arrAuthorAddresses) { + console.log(`private validation OK ${bStable}`); + const unit = objPrivateElement.unit; + const message_index = objPrivateElement.message_index; + const payload = objPrivateElement.payload; + const arrQueries = []; for (var j=0; j { - var arrAuthorAddresses = objPartialUnit.authors.map(function(author) { return author.address; } ); + const arrAuthorAddresses = objPartialUnit.authors.map(({address}) => address ); function validateSpendProofs(sp_cb){ - var arrSpendProofs = []; + const arrSpendProofs = []; async.eachSeries( payload.inputs, - function(input, cb){ + (input, cb) => { if (input.type === "issue"){ - var address = input.address || arrAuthorAddresses[0]; - var spend_proof = objectHash.getBase64Hash({ + const address = input.address || arrAuthorAddresses[0]; + const spend_proof = objectHash.getBase64Hash({ asset: payload.asset, amount: input.amount, - address: address, + address, serial_number: input.serial_number }); - arrSpendProofs.push({address: address, spend_proof: spend_proof}); + arrSpendProofs.push({address, spend_proof}); cb(); } else if (!input.type){ conn.query( "SELECT address, amount, blinding FROM outputs WHERE unit=? AND message_index=? AND output_index=? AND asset=?", [input.unit, input.message_index, input.output_index, payload.asset], - function(rows){ + rows => { if (rows.length !== 1) return cb("not 1 row when selecting src output"); - var src_output = rows[0]; - var spend_proof = objectHash.getBase64Hash({ + const src_output = rows[0]; + const spend_proof = objectHash.getBase64Hash({ asset: payload.asset, unit: input.unit, message_index: input.message_index, @@ -124,25 +124,25 @@ function validateDivisiblePrivatePayment(conn, objPrivateElement, callbacks){ amount: src_output.amount, blinding: src_output.blinding }); - arrSpendProofs.push({address: src_output.address, spend_proof: spend_proof}); + arrSpendProofs.push({address: src_output.address, spend_proof}); cb(); } ); } else - cb("unknown input type: "+input.type); + cb(`unknown input type: ${input.type}`); }, - function(err){ + err => { if (err) return sp_cb(err); //arrSpendProofs.sort(function(a,b){ return a.spend_proof.localeCompare(b.spend_proof); }); conn.query( "SELECT address, spend_proof FROM spend_proofs WHERE unit=? AND message_index=? ORDER BY spend_proof", [unit, message_index], - function(rows){ + rows => { if (rows.length !== arrSpendProofs.length) return sp_cb("incorrect number of spend proofs"); - for (var i=0; i { validation.validatePayment(conn, payload, message_index, objPartialUnit, objValidationState, cb); }); - async.series(arrFuncs, function(err){ - console.log("162: "+err); + async.series(arrFuncs, err => { + console.log(`162: ${err}`); err ? callbacks.ifError(err) : callbacks.ifOk(bStable, arrAuthorAddresses); }); } @@ -168,13 +168,13 @@ function validateDivisiblePrivatePayment(conn, objPrivateElement, callbacks){ // {asset: asset, paying_addresses: arrPayingAddresses, fee_paying_addresses: arrFeePayingAddresses, change_address: change_address, to_address: to_address, amount: amount, signer: signer, callbacks: callbacks} function composeDivisibleAssetPaymentJoint(params){ - console.log("asset payment from "+params.paying_addresses); + console.log(`asset payment from ${params.paying_addresses}`); if ((params.to_address || params.amount) && params.asset_outputs) throw Error("to_address and asset_outputs at the same time"); if (!ValidationUtils.isNonemptyArray(params.fee_paying_addresses)) throw Error('no fee_paying_addresses'); - var private_payload; - var arrBaseOutputs = [{address: params.fee_paying_addresses[0], amount: 0}]; // public outputs: the change only + let private_payload; + let arrBaseOutputs = [{address: params.fee_paying_addresses[0], amount: 0}]; // public outputs: the change only if (params.base_outputs) arrBaseOutputs = arrBaseOutputs.concat(params.base_outputs); composer.composeJoint({ @@ -184,9 +184,9 @@ function composeDivisibleAssetPaymentJoint(params){ outputs: arrBaseOutputs, messages:params.messages, // function that creates additional messages to be added to the joint - retrieveMessages: function(conn, last_ball_mci, bMultiAuthored, arrPayingAddresses, onDone){ - var arrAssetPayingAddresses = _.intersection(arrPayingAddresses, params.paying_addresses); - storage.loadAssetWithListOfAttestedAuthors(conn, params.asset, last_ball_mci, arrAssetPayingAddresses, function(err, objAsset){ + retrieveMessages(conn, last_ball_mci, bMultiAuthored, arrPayingAddresses, onDone) { + const arrAssetPayingAddresses = _.intersection(arrPayingAddresses, params.paying_addresses); + storage.loadAssetWithListOfAttestedAuthors(conn, params.asset, last_ball_mci, arrAssetPayingAddresses, (err, objAsset) => { if (err) return onDone(err); if (objAsset.fixed_denominations) @@ -199,37 +199,37 @@ function composeDivisibleAssetPaymentJoint(params){ if (objAsset.spender_attested && objAsset.arrAttestedAddresses.length === 0) return onDone("none of the authors is attested"); - var target_amount = params.to_address + const target_amount = params.to_address ? params.amount - : params.asset_outputs.reduce(function(accumulator, output){ return accumulator + output.amount; }, 0); + : params.asset_outputs.reduce((accumulator, {amount}) => accumulator + amount, 0); composer.pickDivisibleCoinsForAmount( conn, objAsset, arrAssetPayingAddresses, last_ball_mci, target_amount, bMultiAuthored, - function(arrInputsWithProofs, total_input){ - console.log("pick coins callback "+arrInputsWithProofs); + (arrInputsWithProofs, total_input) => { + console.log(`pick coins callback ${arrInputsWithProofs}`); if (!arrInputsWithProofs) return onDone({error_code: "NOT_ENOUGH_FUNDS", error: "not enough asset coins"}); - var arrOutputs = params.to_address ? [{address: params.to_address, amount: params.amount}] : params.asset_outputs; - var change = total_input - target_amount; + const arrOutputs = params.to_address ? [{address: params.to_address, amount: params.amount}] : params.asset_outputs; + const change = total_input - target_amount; if (change > 0){ - var objChangeOutput = {address: params.change_address, amount: change}; + const objChangeOutput = {address: params.change_address, amount: change}; arrOutputs.push(objChangeOutput); } if (objAsset.is_private) - arrOutputs.forEach(function(output){ output.blinding = composer.generateBlinding(); }); + arrOutputs.forEach(output => { output.blinding = composer.generateBlinding(); }); arrOutputs.sort(composer.sortOutputs); - var payload = { + const payload = { asset: params.asset, - inputs: arrInputsWithProofs.map(function(objInputWithProof){ return objInputWithProof.input; }), + inputs: arrInputsWithProofs.map(({input}) => input), outputs: arrOutputs }; - var objMessage = { + const objMessage = { app: "payment", payload_location: objAsset.is_private ? "none" : "inline", payload_hash: objectHash.getBase64Hash(payload) }; - var assocPrivatePayloads; + let assocPrivatePayloads; if (objAsset.is_private){ - objMessage.spend_proofs = arrInputsWithProofs.map(function(objInputWithProof){ return objInputWithProof.spend_proof; }); + objMessage.spend_proofs = arrInputsWithProofs.map(({spend_proof}) => spend_proof); private_payload = payload; assocPrivatePayloads[objMessage.payload_hash] = private_payload; } @@ -246,7 +246,7 @@ function composeDivisibleAssetPaymentJoint(params){ callbacks: { ifError: params.callbacks.ifError, ifNotEnoughFunds: params.callbacks.ifNotEnoughFunds, - ifOk: function(objJoint, assocPrivatePayloads, composer_unlock_callback){ + ifOk(objJoint, assocPrivatePayloads, composer_unlock_callback) { // adding private_payload params.callbacks.ifOk(objJoint, private_payload, composer_unlock_callback); } @@ -259,59 +259,59 @@ function getSavingCallbacks(callbacks){ return { ifError: callbacks.ifError, ifNotEnoughFunds: callbacks.ifNotEnoughFunds, - ifOk: function(objJoint, private_payload, composer_unlock){ - var objUnit = objJoint.unit; - var unit = objUnit.unit; + ifOk(objJoint, private_payload, composer_unlock) { + const objUnit = objJoint.unit; + const unit = objUnit.unit; validation.validate(objJoint, { - ifUnitError: function(err){ + ifUnitError(err) { composer_unlock(); - callbacks.ifError("Validation error: "+err); + callbacks.ifError(`Validation error: ${err}`); // throw Error("unexpected validation error: "+err); }, - ifJointError: function(err){ - throw Error("unexpected validation joint error: "+err); + ifJointError(err) { + throw Error(`unexpected validation joint error: ${err}`); }, - ifTransientError: function(err){ - throw Error("unexpected validation transient error: "+err); + ifTransientError(err) { + throw Error(`unexpected validation transient error: ${err}`); }, - ifNeedHashTree: function(){ + ifNeedHashTree() { throw Error("unexpected need hash tree"); }, - ifNeedParentUnits: function(arrMissingUnits){ - throw Error("unexpected dependencies: "+arrMissingUnits.join(", ")); + ifNeedParentUnits(arrMissingUnits) { + throw Error(`unexpected dependencies: ${arrMissingUnits.join(", ")}`); }, - ifOk: function(objValidationState, validation_unlock){ - console.log("divisible asset OK "+objValidationState.sequence); + ifOk(objValidationState, validation_unlock) { + console.log(`divisible asset OK ${objValidationState.sequence}`); if (objValidationState.sequence !== 'good'){ validation_unlock(); composer_unlock(); - return callbacks.ifError("Divisible asset bad sequence "+objValidationState.sequence); + return callbacks.ifError(`Divisible asset bad sequence ${objValidationState.sequence}`); } - var bPrivate = !!private_payload; - var objPrivateElement; - var preCommitCallback = null; + const bPrivate = !!private_payload; + let objPrivateElement; + let preCommitCallback = null; if (bPrivate){ - preCommitCallback = function(conn, cb){ - var payload_hash = objectHash.getBase64Hash(private_payload); - var message_index = composer.getMessageIndexByPayloadHash(objUnit, payload_hash); + preCommitCallback = (conn, cb) => { + const payload_hash = objectHash.getBase64Hash(private_payload); + const message_index = composer.getMessageIndexByPayloadHash(objUnit, payload_hash); objPrivateElement = { - unit: unit, - message_index: message_index, + unit, + message_index, payload: private_payload }; validateAndSaveDivisiblePrivatePayment(conn, objPrivateElement, { - ifError: function(err){ + ifError(err) { cb(err); }, - ifOk: function(){ + ifOk() { cb(); } }); }; } else { if (typeof callbacks.preCommitCb === "function") { - preCommitCallback = function(conn, cb){ + preCommitCallback = (conn, cb) => { callbacks.preCommitCb(conn, objJoint, cb); } } @@ -320,7 +320,7 @@ function getSavingCallbacks(callbacks){ composer.postJointToLightVendorIfNecessaryAndSave( objJoint, function onLightError(err){ // light only - console.log("failed to post divisible payment "+unit); + console.log(`failed to post divisible payment ${unit}`); validation_unlock(); composer_unlock(); callbacks.ifError(err); @@ -330,10 +330,10 @@ function getSavingCallbacks(callbacks){ objJoint, objValidationState, preCommitCallback, function onDone(err){ - console.log("saved unit "+unit, objPrivateElement); + console.log(`saved unit ${unit}`, objPrivateElement); validation_unlock(); composer_unlock(); - var arrChains = objPrivateElement ? [[objPrivateElement]] : null; // only one chain that consists of one element + const arrChains = objPrivateElement ? [[objPrivateElement]] : null; // only one chain that consists of one element callbacks.ifOk(objJoint, arrChains, arrChains); } ); @@ -347,12 +347,12 @@ function getSavingCallbacks(callbacks){ // {asset: asset, paying_addresses: arrPayingAddresses, fee_paying_addresses: arrFeePayingAddresses, change_address: change_address, to_address: to_address, amount: amount, signer: signer, callbacks: callbacks} function composeAndSaveDivisibleAssetPaymentJoint(params){ - var params_with_save = _.clone(params); + const params_with_save = _.clone(params); params_with_save.callbacks = getSavingCallbacks(params.callbacks); composeDivisibleAssetPaymentJoint(params_with_save); } -var TYPICAL_FEE = 1000; +const TYPICAL_FEE = 1000; // {asset: asset, available_paying_addresses: arrAvailablePayingAddresses, available_fee_paying_addresses: arrAvailableFeePayingAddresses, change_address: change_address, to_address: to_address, amount: amount, signer: signer, callbacks: callbacks} function composeMinimalDivisibleAssetPaymentJoint(params){ @@ -361,13 +361,13 @@ function composeMinimalDivisibleAssetPaymentJoint(params){ throw Error('no available_paying_addresses'); if (!ValidationUtils.isNonemptyArray(params.available_fee_paying_addresses)) throw Error('no available_fee_paying_addresses'); - composer.readSortedFundedAddresses(params.asset, params.available_paying_addresses, params.amount, function(arrFundedPayingAddresses){ + composer.readSortedFundedAddresses(params.asset, params.available_paying_addresses, params.amount, arrFundedPayingAddresses => { if (arrFundedPayingAddresses.length === 0) return params.callbacks.ifNotEnoughFunds("all paying addresses are unfunded in asset, make sure all your funds are confirmed"); - composer.readSortedFundedAddresses(null, params.available_fee_paying_addresses, TYPICAL_FEE, function(arrFundedFeePayingAddresses){ + composer.readSortedFundedAddresses(null, params.available_fee_paying_addresses, TYPICAL_FEE, arrFundedFeePayingAddresses => { if (arrFundedPayingAddresses.length === 0) return params.callbacks.ifNotEnoughFunds("all paying addresses are unfunded in asset, make sure all your funds are confirmed"); - var minimal_params = _.clone(params); + const minimal_params = _.clone(params); delete minimal_params.available_paying_addresses; delete minimal_params.available_fee_paying_addresses; minimal_params.minimal = true; @@ -380,7 +380,7 @@ function composeMinimalDivisibleAssetPaymentJoint(params){ // {asset: asset, available_paying_addresses: arrAvailablePayingAddresses, available_fee_paying_addresses: arrAvailableFeePayingAddresses, change_address: change_address, to_address: to_address, amount: amount, signer: signer, callbacks: callbacks} function composeAndSaveMinimalDivisibleAssetPaymentJoint(params){ - var params_with_save = _.clone(params); + const params_with_save = _.clone(params); params_with_save.callbacks = getSavingCallbacks(params.callbacks); composeMinimalDivisibleAssetPaymentJoint(params_with_save); } diff --git a/enforce_singleton.js b/enforce_singleton.js index ca9b5b37..1cc50462 100644 --- a/enforce_singleton.js +++ b/enforce_singleton.js @@ -1,6 +1,4 @@ /*jslint node: true */ -"use strict"; - if (global._bByteballCoreLoaded) throw Error("Looks like you are loading multiple copies of byteballcore, which is not supported.\nRunnung 'npm dedupe' might help."); diff --git a/event_bus.js b/event_bus.js index b5785bd4..84076b21 100644 --- a/event_bus.js +++ b/event_bus.js @@ -1,10 +1,9 @@ /*jslint node: true */ -"use strict"; require('./enforce_singleton.js'); -var EventEmitter = require('events').EventEmitter; +const EventEmitter = require('events').EventEmitter; -var eventEmitter = new EventEmitter(); +const eventEmitter = new EventEmitter(); eventEmitter.setMaxListeners(20); module.exports = eventEmitter; diff --git a/graph.js b/graph.js index 4f590617..4e93d638 100644 --- a/graph.js +++ b/graph.js @@ -1,10 +1,9 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var async = require('async'); -var storage = require('./storage.js'); -var db = require('./db.js'); -var profiler = require('./profiler.js'); +const _ = require('lodash'); +const async = require('async'); +const storage = require('./storage.js'); +const db = require('./db.js'); +const profiler = require('./profiler.js'); @@ -14,11 +13,11 @@ function compareUnits(conn, unit1, unit2, handleResult){ conn.query( "SELECT unit, level, latest_included_mc_index, main_chain_index, is_on_main_chain, is_free FROM units WHERE unit IN(?)", [[unit1, unit2]], - function(rows){ + rows => { if (rows.length !== 2) throw Error("not 2 rows"); - var objUnitProps1 = (rows[0].unit === unit1) ? rows[0] : rows[1]; - var objUnitProps2 = (rows[0].unit === unit2) ? rows[0] : rows[1]; + const objUnitProps1 = (rows[0].unit === unit1) ? rows[0] : rows[1]; + const objUnitProps2 = (rows[0].unit === unit2) ? rows[0] : rows[1]; compareUnitsByProps(conn, objUnitProps1, objUnitProps2, handleResult); } ); @@ -60,13 +59,13 @@ function compareUnitsByProps(conn, objUnitProps1, objUnitProps2, handleResult){ else return handleResult(null); - var objEarlierUnit = (objUnitProps1.level < objUnitProps2.level) ? objUnitProps1 : objUnitProps2; - var objLaterUnit = (objUnitProps1.level < objUnitProps2.level) ? objUnitProps2 : objUnitProps1; - var resultIfFound = (objUnitProps1.level < objUnitProps2.level) ? -1 : 1; + const objEarlierUnit = (objUnitProps1.level < objUnitProps2.level) ? objUnitProps1 : objUnitProps2; + const objLaterUnit = (objUnitProps1.level < objUnitProps2.level) ? objUnitProps2 : objUnitProps1; + const resultIfFound = (objUnitProps1.level < objUnitProps2.level) ? -1 : 1; // can be negative if main_chain_index === null but that doesn't matter - var earlier_unit_delta = objEarlierUnit.main_chain_index - objEarlierUnit.latest_included_mc_index; - var later_unit_delta = objLaterUnit.main_chain_index - objLaterUnit.latest_included_mc_index; + const earlier_unit_delta = objEarlierUnit.main_chain_index - objEarlierUnit.latest_included_mc_index; + const later_unit_delta = objLaterUnit.main_chain_index - objLaterUnit.latest_included_mc_index; function goUp(arrStartUnits){ //console.log('compare', arrStartUnits); @@ -75,10 +74,10 @@ function compareUnitsByProps(conn, objUnitProps1, objUnitProps2, handleResult){ FROM parenthoods JOIN units ON parent_unit=unit \n\ WHERE child_unit IN(?)", [arrStartUnits], - function(rows){ - var arrNewStartUnits = []; - for (var i=0; i { + const arrNewStartUnits = []; + for (let i=0; i objEarlierUnit.level) @@ -95,10 +94,10 @@ function compareUnitsByProps(conn, objUnitProps1, objUnitProps2, handleResult){ FROM parenthoods JOIN units ON child_unit=unit \n\ WHERE parent_unit IN(?)", [arrStartUnits], - function(rows){ - var arrNewStartUnits = []; - for (var i=0; i { + const arrNewStartUnits = []; + for (let i=0; i { + if (is_free === 1) return handleResult(false); - var max_later_limci = Math.max.apply( - null, arrLaterUnitProps.map(function(objLaterUnitProps){ return objLaterUnitProps.latest_included_mc_index; })); + const max_later_limci = Math.max.apply( + null, arrLaterUnitProps.map(({latest_included_mc_index}) => latest_included_mc_index)); //console.log("max limci "+max_later_limci+", earlier mci "+objEarlierUnitProps.main_chain_index); - if (objEarlierUnitProps.main_chain_index !== null && max_later_limci >= objEarlierUnitProps.main_chain_index) + if (main_chain_index !== null && max_later_limci >= main_chain_index) return handleResult(true); - var max_later_level = Math.max.apply( - null, arrLaterUnitProps.map(function(objLaterUnitProps){ return objLaterUnitProps.level; })); - if (max_later_level < objEarlierUnitProps.level) + const max_later_level = Math.max.apply( + null, arrLaterUnitProps.map(({level}) => level)); + if (max_later_level < level) return handleResult(false); function goUp(arrStartUnits){ @@ -141,13 +140,13 @@ function determineIfIncluded(conn, earlier_unit, arrLaterUnits, handleResult){ FROM parenthoods JOIN units ON parent_unit=unit \n\ WHERE child_unit IN(?)", [arrStartUnits], - function(rows){ - var arrNewStartUnits = []; - for (var i=0; i { + const arrNewStartUnits = []; + for (let i=0; i objEarlierUnitProps.level) + if (objUnitProps.is_on_main_chain === 0 && objUnitProps.level > level) arrNewStartUnits.push(objUnitProps.unit); } (arrNewStartUnits.length > 0) ? goUp(_.uniq(arrNewStartUnits)) : handleResult(false); @@ -168,9 +167,15 @@ function determineIfIncludedOrEqual(conn, earlier_unit, arrLaterUnits, handleRes // excludes earlier unit -function readDescendantUnitsByAuthorsBeforeMcIndex(conn, objEarlierUnitProps, arrAuthorAddresses, to_main_chain_index, handleUnits){ +function readDescendantUnitsByAuthorsBeforeMcIndex( + conn, + {main_chain_index, unit}, + arrAuthorAddresses, + to_main_chain_index, + handleUnits +) { - var arrUnits = []; + let arrUnits = []; function goDown(arrStartUnits){ profiler.start(); @@ -180,11 +185,11 @@ function readDescendantUnitsByAuthorsBeforeMcIndex(conn, objEarlierUnitProps, ar JOIN units ON child_unit=units.unit \n\ LEFT JOIN unit_authors ON unit_authors.unit=units.unit AND address IN(?) \n\ WHERE parent_unit IN(?) AND latest_included_mc_index { + const arrNewStartUnits = []; + for (let i=0; i=? AND main_chain_index>? AND main_chain_index<=? AND latest_included_mc_index=? AND main_chain_index<=?", -// [objEarlierUnitProps.main_chain_index, to_main_chain_index], - function(rows){ - arrUnits = rows.map(function(row) { return row.unit; }); + `SELECT unit FROM units ${db.forceIndex("byMcIndex")} LEFT JOIN unit_authors USING(unit) \n\ + WHERE latest_included_mc_index>=? AND main_chain_index>? AND main_chain_index<=? AND latest_included_mc_index { + arrUnits = rows.map(({unit}) => unit); profiler.stop('mc-wc-descendants-initial'); - goDown([objEarlierUnitProps.unit]); + goDown([unit]); } ); } @@ -214,23 +217,23 @@ function readDescendantUnitsByAuthorsBeforeMcIndex(conn, objEarlierUnitProps, ar // excludes earlier unit -function readDescendantUnitsBeforeLandingOnMc(conn, objEarlierUnitProps, arrLaterUnitProps, handleUnits){ +function readDescendantUnitsBeforeLandingOnMc(conn, {main_chain_index, unit}, arrLaterUnitProps, handleUnits) { - var max_later_limci = Math.max.apply(null, arrLaterUnitProps.map(function(objLaterUnitProps){ return objLaterUnitProps.latest_included_mc_index; })); - var max_later_level = Math.max.apply(null, arrLaterUnitProps.map(function(objLaterUnitProps){ return objLaterUnitProps.level; })); - var arrLandedUnits = []; // units that landed on MC before max_later_limci, they are already included in at least one of later units - var arrUnlandedUnits = []; // direct shoots to later units, without touching the MC + const max_later_limci = Math.max.apply(null, arrLaterUnitProps.map(({latest_included_mc_index}) => latest_included_mc_index)); + const max_later_level = Math.max.apply(null, arrLaterUnitProps.map(({level}) => level)); + const arrLandedUnits = []; // units that landed on MC before max_later_limci, they are already included in at least one of later units + const arrUnlandedUnits = []; // direct shoots to later units, without touching the MC function goDown(arrStartUnits){ conn.query( "SELECT unit, level, latest_included_mc_index, main_chain_index, is_on_main_chain \n\ FROM parenthoods JOIN units ON child_unit=unit \n\ WHERE parent_unit IN(?) AND latest_included_mc_index { + const arrNewStartUnits = []; + for (let i=0; i= objEarlierUnitProps.main_chain_index) // continue; //if (objUnitProps.level > max_later_level) @@ -246,21 +249,21 @@ function readDescendantUnitsBeforeLandingOnMc(conn, objEarlierUnitProps, arrLate ); } - goDown([objEarlierUnitProps.unit]); + goDown([unit]); } // includes later units -function readAscendantUnitsAfterTakingOffMc(conn, objEarlierUnitProps, arrLaterUnitProps, handleUnits){ - var arrLaterUnits = arrLaterUnitProps.map(function(objLaterUnitProps){ return objLaterUnitProps.unit; }); - var max_later_limci = Math.max.apply(null, arrLaterUnitProps.map(function(objLaterUnitProps){ return objLaterUnitProps.latest_included_mc_index; })); - var arrLandedUnits = []; // units that took off MC after earlier unit's MCI, they already include the earlier unit - var arrUnlandedUnits = []; // direct shoots from earlier units, without touching the MC +function readAscendantUnitsAfterTakingOffMc(conn, {main_chain_index, level}, arrLaterUnitProps, handleUnits) { + const arrLaterUnits = arrLaterUnitProps.map(({unit}) => unit); + const max_later_limci = Math.max.apply(null, arrLaterUnitProps.map(({latest_included_mc_index}) => latest_included_mc_index)); + const arrLandedUnits = []; // units that took off MC after earlier unit's MCI, they already include the earlier unit + const arrUnlandedUnits = []; // direct shoots from earlier units, without touching the MC - arrLaterUnitProps.forEach(function(objUnitProps){ - if (objUnitProps.latest_included_mc_index >= objEarlierUnitProps.main_chain_index) - arrLandedUnits.push(objUnitProps.unit); + arrLaterUnitProps.forEach(({latest_included_mc_index, unit}) => { + if (latest_included_mc_index >= main_chain_index) + arrLandedUnits.push(unit); else - arrUnlandedUnits.push(objUnitProps.unit); + arrUnlandedUnits.push(unit); }); function goUp(arrStartUnits){ @@ -268,17 +271,17 @@ function readAscendantUnitsAfterTakingOffMc(conn, objEarlierUnitProps, arrLaterU "SELECT unit, level, latest_included_mc_index, main_chain_index, is_on_main_chain \n\ FROM parenthoods JOIN units ON parent_unit=unit \n\ WHERE child_unit IN(?) AND (main_chain_index>? OR main_chain_index IS NULL) AND level>=?", - [arrStartUnits, max_later_limci, objEarlierUnitProps.level], - function(rows){ - var arrNewStartUnits = []; - for (var i=0; i { + const arrNewStartUnits = []; + for (let i=0; i= objEarlierUnitProps.main_chain_index) + if (objUnitProps.latest_included_mc_index >= main_chain_index) arrLandedUnits.push(objUnitProps.unit); else arrUnlandedUnits.push(objUnitProps.unit); diff --git a/headers_commission.js b/headers_commission.js index 249475f5..b58444e8 100644 --- a/headers_commission.js +++ b/headers_commission.js @@ -1,25 +1,24 @@ /*jslint node: true */ -"use strict"; -var crypto = require('crypto'); -var async = require('async'); -var db = require('./db.js'); -var conf = require('./conf.js'); +const crypto = require('crypto'); +const async = require('async'); +const db = require('./db.js'); +const conf = require('./conf.js'); -var max_spendable_mci = null; +let max_spendable_mci = null; function calcHeadersCommissions(conn, onDone){ // we don't require neither source nor recipient to be majority witnessed -- we don't want to return many times to the same MC index. console.log("will calc h-comm"); if (max_spendable_mci === null) // first calc after restart only - return initMaxSpendableMci(conn, function(){ calcHeadersCommissions(conn, onDone); }); + return initMaxSpendableMci(conn, () => { calcHeadersCommissions(conn, onDone); }); // max_spendable_mci is old, it was last updated after previous calc - var since_mc_index = max_spendable_mci; + const since_mc_index = max_spendable_mci; async.series([ - function(cb){ + cb => { if (conf.storage === 'mysql'){ - var best_child_sql = "SELECT unit \n\ + const best_child_sql = "SELECT unit \n\ FROM parenthoods \n\ JOIN units AS alt_child_units ON parenthoods.child_unit=alt_child_units.unit \n\ WHERE parent_unit=punits.unit AND alt_child_units.main_chain_index-punits.main_chain_index<=1 AND +alt_child_units.sequence='good' \n\ @@ -27,41 +26,41 @@ function calcHeadersCommissions(conn, onDone){ LIMIT 1"; // headers commissions to single unit author conn.query( - "INSERT INTO headers_commission_contributions (unit, address, amount) \n\ - SELECT punits.unit, address, punits.headers_commission AS hc \n\ - FROM units AS chunits \n\ - JOIN unit_authors USING(unit) \n\ - JOIN parenthoods ON chunits.unit=parenthoods.child_unit \n\ - JOIN units AS punits ON parenthoods.parent_unit=punits.unit \n\ - JOIN units AS next_mc_units ON next_mc_units.is_on_main_chain=1 AND next_mc_units.main_chain_index=punits.main_chain_index+1 \n\ - WHERE chunits.is_stable=1 \n\ - AND +chunits.sequence='good' \n\ - AND punits.main_chain_index>? \n\ - AND chunits.main_chain_index-punits.main_chain_index<=1 \n\ - AND +punits.sequence='good' \n\ - AND punits.is_stable=1 \n\ - AND next_mc_units.is_stable=1 \n\ - AND chunits.unit=( "+best_child_sql+" ) \n\ - AND (SELECT COUNT(*) FROM unit_authors WHERE unit=chunits.unit)=1 \n\ - AND (SELECT COUNT(*) FROM earned_headers_commission_recipients WHERE unit=chunits.unit)=0 \n\ - UNION ALL \n\ - SELECT punits.unit, earned_headers_commission_recipients.address, \n\ - ROUND(punits.headers_commission*earned_headers_commission_share/100.0) AS hc \n\ - FROM units AS chunits \n\ - JOIN earned_headers_commission_recipients USING(unit) \n\ - JOIN parenthoods ON chunits.unit=parenthoods.child_unit \n\ - JOIN units AS punits ON parenthoods.parent_unit=punits.unit \n\ - JOIN units AS next_mc_units ON next_mc_units.is_on_main_chain=1 AND next_mc_units.main_chain_index=punits.main_chain_index+1 \n\ - WHERE chunits.is_stable=1 \n\ - AND +chunits.sequence='good' \n\ - AND punits.main_chain_index>? \n\ - AND chunits.main_chain_index-punits.main_chain_index<=1 \n\ - AND +punits.sequence='good' \n\ - AND punits.is_stable=1 \n\ - AND next_mc_units.is_stable=1 \n\ - AND chunits.unit=( "+best_child_sql+" )", + `INSERT INTO headers_commission_contributions (unit, address, amount) \n\ + SELECT punits.unit, address, punits.headers_commission AS hc \n\ + FROM units AS chunits \n\ + JOIN unit_authors USING(unit) \n\ + JOIN parenthoods ON chunits.unit=parenthoods.child_unit \n\ + JOIN units AS punits ON parenthoods.parent_unit=punits.unit \n\ + JOIN units AS next_mc_units ON next_mc_units.is_on_main_chain=1 AND next_mc_units.main_chain_index=punits.main_chain_index+1 \n\ + WHERE chunits.is_stable=1 \n\ + AND +chunits.sequence='good' \n\ + AND punits.main_chain_index>? \n\ + AND chunits.main_chain_index-punits.main_chain_index<=1 \n\ + AND +punits.sequence='good' \n\ + AND punits.is_stable=1 \n\ + AND next_mc_units.is_stable=1 \n\ + AND chunits.unit=( ${best_child_sql} ) \n\ + AND (SELECT COUNT(*) FROM unit_authors WHERE unit=chunits.unit)=1 \n\ + AND (SELECT COUNT(*) FROM earned_headers_commission_recipients WHERE unit=chunits.unit)=0 \n\ + UNION ALL \n\ + SELECT punits.unit, earned_headers_commission_recipients.address, \n\ + ROUND(punits.headers_commission*earned_headers_commission_share/100.0) AS hc \n\ + FROM units AS chunits \n\ + JOIN earned_headers_commission_recipients USING(unit) \n\ + JOIN parenthoods ON chunits.unit=parenthoods.child_unit \n\ + JOIN units AS punits ON parenthoods.parent_unit=punits.unit \n\ + JOIN units AS next_mc_units ON next_mc_units.is_on_main_chain=1 AND next_mc_units.main_chain_index=punits.main_chain_index+1 \n\ + WHERE chunits.is_stable=1 \n\ + AND +chunits.sequence='good' \n\ + AND punits.main_chain_index>? \n\ + AND chunits.main_chain_index-punits.main_chain_index<=1 \n\ + AND +punits.sequence='good' \n\ + AND punits.is_stable=1 \n\ + AND next_mc_units.is_stable=1 \n\ + AND chunits.unit=( ${best_child_sql} )`, [since_mc_index, since_mc_index], - function(){ cb(); } + () => { cb(); } ); } else{ // there is no SHA1 in sqlite, have to do it in js @@ -80,11 +79,11 @@ function calcHeadersCommissions(conn, onDone){ AND chunits.main_chain_index-punits.main_chain_index<=1 \n\ AND next_mc_units.is_stable=1", [since_mc_index], - function(rows){ - var assocChildrenInfos = {}; - rows.forEach(function(row){ - var payer_unit = row.payer_unit; - var child_unit = row.child_unit; + rows => { + const assocChildrenInfos = {}; + rows.forEach(row => { + const payer_unit = row.payer_unit; + const child_unit = row.child_unit; if (!assocChildrenInfos[payer_unit]) assocChildrenInfos[payer_unit] = {headers_commission: row.headers_commission, children: []}; else if (assocChildrenInfos[payer_unit].headers_commission !== row.headers_commission) @@ -93,52 +92,52 @@ function calcHeadersCommissions(conn, onDone){ delete row.payer_unit; assocChildrenInfos[payer_unit].children.push(row); }); - var assocWonAmounts = {}; // amounts won, indexed by child unit who won the hc, and payer unit - for (var payer_unit in assocChildrenInfos){ - var headers_commission = assocChildrenInfos[payer_unit].headers_commission; - var winnerChildInfo = getWinnerInfo(assocChildrenInfos[payer_unit].children); - var child_unit = winnerChildInfo.child_unit; + const assocWonAmounts = {}; // amounts won, indexed by child unit who won the hc, and payer unit + for (const payer_unit in assocChildrenInfos){ + const headers_commission = assocChildrenInfos[payer_unit].headers_commission; + const winnerChildInfo = getWinnerInfo(assocChildrenInfos[payer_unit].children); + const child_unit = winnerChildInfo.child_unit; if (!assocWonAmounts[child_unit]) assocWonAmounts[child_unit] = {}; assocWonAmounts[child_unit][payer_unit] = headers_commission; } //console.log(assocWonAmounts); - var arrWinnerUnits = Object.keys(assocWonAmounts); + const arrWinnerUnits = Object.keys(assocWonAmounts); if (arrWinnerUnits.length === 0) return cb(); - var strWinnerUnitsList = arrWinnerUnits.map(db.escape).join(', '); + const strWinnerUnitsList = arrWinnerUnits.map(db.escape).join(', '); conn.query( - "SELECT \n\ - unit_authors.unit, \n\ - unit_authors.address, \n\ - 100 AS earned_headers_commission_share \n\ - FROM unit_authors \n\ - LEFT JOIN earned_headers_commission_recipients USING(unit) \n\ - WHERE unit_authors.unit IN("+strWinnerUnitsList+") AND earned_headers_commission_recipients.unit IS NULL \n\ - UNION ALL \n\ - SELECT \n\ - unit, \n\ - address, \n\ - earned_headers_commission_share \n\ - FROM earned_headers_commission_recipients \n\ - WHERE unit IN("+strWinnerUnitsList+")", - function(profit_distribution_rows){ - var arrValues = []; - profit_distribution_rows.forEach(function(row){ - var child_unit = row.unit; - for (var payer_unit in assocWonAmounts[child_unit]){ - var full_amount = assocWonAmounts[child_unit][payer_unit]; + `SELECT \n\ + unit_authors.unit, \n\ + unit_authors.address, \n\ + 100 AS earned_headers_commission_share \n\ + FROM unit_authors \n\ + LEFT JOIN earned_headers_commission_recipients USING(unit) \n\ + WHERE unit_authors.unit IN(${strWinnerUnitsList}) AND earned_headers_commission_recipients.unit IS NULL \n\ + UNION ALL \n\ + SELECT \n\ + unit, \n\ + address, \n\ + earned_headers_commission_share \n\ + FROM earned_headers_commission_recipients \n\ + WHERE unit IN(${strWinnerUnitsList})`, + profit_distribution_rows => { + const arrValues = []; + profit_distribution_rows.forEach(({unit, earned_headers_commission_share, address}) => { + const child_unit = unit; + for (const payer_unit in assocWonAmounts[child_unit]){ + const full_amount = assocWonAmounts[child_unit][payer_unit]; if (!full_amount) - throw Error("no amount for child unit "+child_unit+", payer unit "+payer_unit); + throw Error(`no amount for child unit ${child_unit}, payer unit ${payer_unit}`); // note that we round _before_ summing up header commissions won from several parent units - var amount = (row.earned_headers_commission_share === 100) + const amount = (earned_headers_commission_share === 100) ? full_amount - : Math.round(full_amount * row.earned_headers_commission_share / 100.0); + : Math.round(full_amount * earned_headers_commission_share / 100.0); // hc outputs will be indexed by mci of _payer_ unit - arrValues.push("('"+payer_unit+"', '"+row.address+"', "+amount+")"); + arrValues.push(`('${payer_unit}', '${address}', ${amount})`); } }); - conn.query("INSERT INTO headers_commission_contributions (unit, address, amount) VALUES "+arrValues.join(", "), function(){ + conn.query(`INSERT INTO headers_commission_contributions (unit, address, amount) VALUES ${arrValues.join(", ")}`, () => { cb(); }); } @@ -147,18 +146,18 @@ function calcHeadersCommissions(conn, onDone){ ); } // sqlite }, - function(cb){ + cb => { conn.query( "INSERT INTO headers_commission_outputs (main_chain_index, address, amount) \n\ SELECT main_chain_index, address, SUM(amount) FROM headers_commission_contributions JOIN units USING(unit) \n\ WHERE main_chain_index>? \n\ GROUP BY main_chain_index, address", [since_mc_index], - function(){ cb(); } + () => { cb(); } ); }, - function(cb){ - conn.query("SELECT MAX(main_chain_index) AS max_spendable_mci FROM headers_commission_outputs", function(rows){ + cb => { + conn.query("SELECT MAX(main_chain_index) AS max_spendable_mci FROM headers_commission_outputs", rows => { max_spendable_mci = rows[0].max_spendable_mci; cb(); }); @@ -170,15 +169,15 @@ function calcHeadersCommissions(conn, onDone){ function getWinnerInfo(arrChildren){ if (arrChildren.length === 1) return arrChildren[0]; - arrChildren.forEach(function(child){ + arrChildren.forEach(child => { child.hash = crypto.createHash("sha1").update(child.child_unit + child.next_mc_unit, "utf8").digest("hex"); }); - arrChildren.sort(function(a, b){ return ((a.hash < b.hash) ? -1 : 1); }); + arrChildren.sort(({hash}, {hash}) => (hash < hash) ? -1 : 1); return arrChildren[0]; } function initMaxSpendableMci(conn, onDone){ - conn.query("SELECT MAX(main_chain_index) AS max_spendable_mci FROM headers_commission_outputs", function(rows){ + conn.query("SELECT MAX(main_chain_index) AS max_spendable_mci FROM headers_commission_outputs", rows => { max_spendable_mci = rows[0].max_spendable_mci || 0; if (onDone) onDone(); diff --git a/indivisible_asset.js b/indivisible_asset.js index 728be14b..7d35ccc1 100644 --- a/indivisible_asset.js +++ b/indivisible_asset.js @@ -1,20 +1,19 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var _ = require('lodash'); -var conf = require('./conf.js'); -var storage = require('./storage.js'); -var objectHash = require("./object_hash.js"); -var db = require('./db.js'); -var constants = require("./constants.js"); -var composer = require("./composer.js"); -var validation = require('./validation.js'); -var ValidationUtils = require("./validation_utils.js"); -var writer = require('./writer.js'); -var graph = require('./graph.js'); -var profiler = require('./profiler.js'); +const async = require('async'); +const _ = require('lodash'); +const conf = require('./conf.js'); +const storage = require('./storage.js'); +const objectHash = require("./object_hash.js"); +const db = require('./db.js'); +const constants = require("./constants.js"); +const composer = require("./composer.js"); +const validation = require('./validation.js'); +const ValidationUtils = require("./validation_utils.js"); +const writer = require('./writer.js'); +const graph = require('./graph.js'); +const profiler = require('./profiler.js'); -var NOT_ENOUGH_FUNDS_ERROR_MESSAGE = "not enough indivisible asset coins that fit the desired amount within the specified tolerances, make sure all your funds are confirmed"; +const NOT_ENOUGH_FUNDS_ERROR_MESSAGE = "not enough indivisible asset coins that fit the desired amount within the specified tolerances, make sure all your funds are confirmed"; function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, callbacks){ @@ -23,12 +22,12 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, conn.query( "SELECT spend_proof, address FROM spend_proofs WHERE unit=? AND message_index=?", [objPrivateElement.unit, objPrivateElement.message_index], - function(rows){ + rows => { profiler.stop('spend_proof'); if (rows.length !== 1) - return cb("expected 1 spend proof, found "+rows.length); - var stored_spend_proof = rows[0].spend_proof; - var spend_proof_address = rows[0].address; + return cb(`expected 1 spend proof, found ${rows.length}`); + const stored_spend_proof = rows[0].spend_proof; + const spend_proof_address = rows[0].address; if (stored_spend_proof !== spend_proof) return cb("spend proof doesn't match"); if (objPrevPrivateElement && objPrevPrivateElement.output.address !== spend_proof_address) @@ -44,13 +43,13 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, if (conf.bLight) return cb(); // already validated the linkproof profiler.start(); - graph.determineIfIncluded(conn, input.unit, [objPrivateElement.unit], function(bIncluded){ + graph.determineIfIncluded(conn, input.unit, [objPrivateElement.unit], bIncluded => { profiler.stop('determineIfIncluded'); bIncluded ? cb() : cb("input unit not included"); }); } - var payload = objPrivateElement.payload; + const payload = objPrivateElement.payload; if (!ValidationUtils.isStringOfLength(payload.asset, constants.HASH_LENGTH)) return callbacks.ifError("invalid asset in private payment"); if (!ValidationUtils.isPositiveInteger(payload.denomination)) @@ -61,7 +60,7 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, return callbacks.ifError("invalid output index"); if (!ValidationUtils.isNonemptyArray(payload.outputs)) return callbacks.ifError("invalid outputs"); - var our_hidden_output = payload.outputs[objPrivateElement.output_index]; + const our_hidden_output = payload.outputs[objPrivateElement.output_index]; if (!ValidationUtils.isNonemptyObject(payload.outputs[objPrivateElement.output_index])) return callbacks.ifError("no output at output_index"); if (!ValidationUtils.isValidAddress(objPrivateElement.output.address)) @@ -69,7 +68,7 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, if (!ValidationUtils.isNonemptyString(objPrivateElement.output.blinding)) return callbacks.ifError("bad blinding in output"); if (objectHash.getBase64Hash(objPrivateElement.output) !== our_hidden_output.output_hash) - return callbacks.ifError("output hash doesn't match, output="+JSON.stringify(objPrivateElement.output)+", hash="+our_hidden_output.output_hash); + return callbacks.ifError(`output hash doesn't match, output=${JSON.stringify(objPrivateElement.output)}, hash=${our_hidden_output.output_hash}`); if (!ValidationUtils.isArrayOfLength(payload.inputs, 1)) return callbacks.ifError("inputs array must be 1 element long"); var input = payload.inputs[0]; @@ -79,12 +78,12 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, profiler.start(); validation.initPrivatePaymentValidationState( conn, objPrivateElement.unit, objPrivateElement.message_index, payload, callbacks.ifError, - function(bStable, objPartialUnit, objValidationState){ + (bStable, objPartialUnit, objValidationState) => { profiler.stop('initPrivatePaymentValidationState'); - var arrFuncs = []; - var spend_proof; - var input_address; // from which address the money is sent + const arrFuncs = []; + let spend_proof; + let input_address; // from which address the money is sent if (!input.type){ // transfer if (typeof input.unit !== 'string') return callbacks.ifError("invalid unit in private payment"); @@ -96,8 +95,8 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, return callbacks.ifError("no prev output blinding"); if (!objPrevPrivateElement.payload || !objPrevPrivateElement.payload.outputs) return callbacks.ifError("no prev outputs"); - var src_output = objPrevPrivateElement.output; - var prev_hidden_output = objPrevPrivateElement.payload.outputs[input.output_index]; + const src_output = objPrevPrivateElement.output; + const prev_hidden_output = objPrevPrivateElement.payload.outputs[input.output_index]; if (!prev_hidden_output) return callbacks.ifError("no prev hidden output"); input_address = src_output.address; @@ -110,18 +109,18 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, amount: prev_hidden_output.amount, blinding: src_output.blinding }); - console.log("validation spend proof: "+JSON.stringify({ - asset: payload.asset, - unit: input.unit, - message_index: input.message_index, - output_index: input.output_index, - address: src_output.address, - amount: prev_hidden_output.amount, - blinding: src_output.blinding - })); + console.log(`validation spend proof: ${JSON.stringify({ + asset: payload.asset, + unit: input.unit, + message_index: input.message_index, + output_index: input.output_index, + address: src_output.address, + amount: prev_hidden_output.amount, + blinding: src_output.blinding +})}`); arrFuncs.push(validateSourceOutput); objValidationState.src_coin = { - src_output: src_output, + src_output, denomination: payload.denomination, amount: prev_hidden_output.amount }; @@ -142,21 +141,21 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, else return callbacks.ifError("neither transfer nor issue in private input"); - if (!objPartialUnit.authors.some(function(author){ return (author.address === input_address); })) + if (!objPartialUnit.authors.some(({address}) => address === input_address)) return callbacks.ifError("input address not found among unit authors"); - arrFuncs.push(function(cb){ + arrFuncs.push(cb => { validateSpendProof(spend_proof, cb); }); - arrFuncs.push(function(cb){ + arrFuncs.push(cb => { // we need to unhide the single output we are interested in, other outputs stay partially hidden like {amount: 300, output_hash: "base64"} - var partially_revealed_payload = _.cloneDeep(payload); - var our_output = partially_revealed_payload.outputs[objPrivateElement.output_index]; + const partially_revealed_payload = _.cloneDeep(payload); + const our_output = partially_revealed_payload.outputs[objPrivateElement.output_index]; our_output.address = objPrivateElement.output.address; our_output.blinding = objPrivateElement.output.blinding; validation.validatePayment(conn, partially_revealed_payload, objPrivateElement.message_index, objPartialUnit, objValidationState, cb); }); - async.series(arrFuncs, function(err){ + async.series(arrFuncs, err => { // profiler.stop('validatePayment'); err ? callbacks.ifError(err) : callbacks.ifOk(bStable, input_address); }); @@ -168,19 +167,19 @@ function validatePrivatePayment(conn, objPrivateElement, objPrevPrivateElement, // arrPrivateElements is ordered in reverse chronological order function parsePrivatePaymentChain(conn, arrPrivateElements, callbacks){ - var bAllStable = true; - var issuePrivateElement = arrPrivateElements[arrPrivateElements.length-1]; + let bAllStable = true; + const issuePrivateElement = arrPrivateElements[arrPrivateElements.length-1]; if (!issuePrivateElement.payload || !issuePrivateElement.payload.inputs || !issuePrivateElement.payload.inputs[0]) return callbacks.ifError("invalid issue private element"); - var asset = issuePrivateElement.payload.asset; + const asset = issuePrivateElement.payload.asset; if (!asset) return callbacks.ifError("no asset in issue private element"); - var denomination = issuePrivateElement.payload.denomination; + const denomination = issuePrivateElement.payload.denomination; if (!denomination) return callbacks.ifError("no denomination in issue private element"); async.forEachOfSeries( arrPrivateElements, - function(objPrivateElement, i, cb){ + (objPrivateElement, i, cb) => { if (!objPrivateElement.payload || !objPrivateElement.payload.inputs || !objPrivateElement.payload.inputs[0]) return cb("invalid payload"); if (!objPrivateElement.output) @@ -201,7 +200,7 @@ function parsePrivatePaymentChain(conn, arrPrivateElements, callbacks){ } validatePrivatePayment(conn, objPrivateElement, prevElement, { ifError: cb, - ifOk: function(bStable, input_address){ + ifOk(bStable, input_address) { objPrivateElement.bStable = bStable; objPrivateElement.input_address = input_address; if (!bStable) @@ -210,7 +209,7 @@ function parsePrivatePaymentChain(conn, arrPrivateElements, callbacks){ } }); }, - function(err){ + err => { if (err) return callbacks.ifError(err); callbacks.ifOk(bAllStable); @@ -222,56 +221,56 @@ function parsePrivatePaymentChain(conn, arrPrivateElements, callbacks){ function validateAndSavePrivatePaymentChain(conn, arrPrivateElements, callbacks){ parsePrivatePaymentChain(conn, arrPrivateElements, { ifError: callbacks.ifError, - ifOk: function(bAllStable){ - console.log("saving private chain "+JSON.stringify(arrPrivateElements)); + ifOk(bAllStable) { + console.log(`saving private chain ${JSON.stringify(arrPrivateElements)}`); profiler.start(); - var arrQueries = []; - for (var i=0; i { profiler.stop('save'); callbacks.ifOk(); }); @@ -288,7 +287,7 @@ function updateIndivisibleOutputsThatWereReceivedUnstable(conn, onDone){ conn.query( "UPDATE outputs SET is_serial=? WHERE unit=?", [is_serial, unit], - function(){ + () => { is_serial ? updateInputUniqueness(unit, onUpdated) : onUpdated(); } ); @@ -296,25 +295,25 @@ function updateIndivisibleOutputsThatWereReceivedUnstable(conn, onDone){ function updateInputUniqueness(unit, onUpdated){ // may update several inputs - conn.query("UPDATE inputs SET is_unique=1 WHERE unit=?", [unit], function(){ + conn.query("UPDATE inputs SET is_unique=1 WHERE unit=?", [unit], () => { onUpdated(); }); } console.log("updatePrivateIndivisibleOutputsThatWereReceivedUnstable starting"); conn.query( - "SELECT unit, message_index, sequence FROM outputs "+(conf.storage === 'sqlite' ? "INDEXED BY outputsIsSerial" : "")+" \n\ - JOIN units USING(unit) \n\ - WHERE outputs.is_serial IS NULL AND units.is_stable=1 AND is_spent=0", // is_spent=0 selects the final output in the chain - function(rows){ + `SELECT unit, message_index, sequence FROM outputs ${conf.storage === 'sqlite' ? "INDEXED BY outputsIsSerial" : ""} \n\ + JOIN units USING(unit) \n\ + WHERE outputs.is_serial IS NULL AND units.is_stable=1 AND is_spent=0`, // is_spent=0 selects the final output in the chain + rows => { if (rows.length === 0) return onDone(); async.eachSeries( rows, - function(row, cb){ + ({unit, sequence, message_index}, cb) => { function updateFinalOutputProps(is_serial){ - updateOutputProps(row.unit, is_serial, cb); + updateOutputProps(unit, is_serial, cb); } function goUp(unit, message_index){ @@ -324,22 +323,22 @@ function updateIndivisibleOutputsThatWereReceivedUnstable(conn, onDone){ FROM inputs \n\ WHERE unit=? AND message_index=?", [unit, message_index], - function(src_rows){ + src_rows => { if (src_rows.length === 0) throw Error("updating unstable: blackbyte input not found"); if (src_rows.length > 1) throw Error("updating unstable: more than one input found"); - var src_row = src_rows[0]; + const src_row = src_rows[0]; if (src_row.src_unit === null) // reached root of the chain (issue) return cb(); conn.query( "SELECT sequence, is_stable, is_serial FROM outputs JOIN units USING(unit) \n\ WHERE unit=? AND message_index=? AND output_index=?", [src_row.src_unit, src_row.src_message_index, src_row.src_output_index], - function(prev_rows){ + prev_rows => { if (prev_rows.length === 0) throw Error("src unit not found"); - var prev_output = prev_rows[0]; + const prev_output = prev_rows[0]; if (prev_output.is_serial === 0) throw Error("prev is already nonserial"); if (prev_output.is_stable === 0) @@ -348,8 +347,8 @@ function updateIndivisibleOutputsThatWereReceivedUnstable(conn, onDone){ throw Error("prev is_serial=1 but seq!=good"); if (prev_output.is_serial === 1) // already was stable when initially received return cb(); - var is_serial = (prev_output.sequence === 'good') ? 1 : 0; - updateOutputProps(src_row.src_unit, is_serial, function(){ + const is_serial = (prev_output.sequence === 'good') ? 1 : 0; + updateOutputProps(src_row.src_unit, is_serial, () => { if (!is_serial) // overwrite the tip of the chain return updateFinalOutputProps(0); goUp(src_row.src_unit, src_row.src_message_index); @@ -360,9 +359,9 @@ function updateIndivisibleOutputsThatWereReceivedUnstable(conn, onDone){ ); } - var is_serial = (row.sequence === 'good') ? 1 : 0; - updateOutputProps(row.unit, is_serial, function(){ - goUp(row.unit, row.message_index); + const is_serial = (sequence === 'good') ? 1 : 0; + updateOutputProps(unit, is_serial, () => { + goUp(unit, message_index); }); }, onDone @@ -375,39 +374,39 @@ function pickIndivisibleCoinsForAmount( conn, objAsset, arrAddresses, last_ball_mci, to_address, change_address, amount, tolerance_plus, tolerance_minus, bMultiAuthored, onDone) { if (!ValidationUtils.isPositiveInteger(amount)) - throw Error("bad amount: "+amount); - updateIndivisibleOutputsThatWereReceivedUnstable(conn, function(){ + throw Error(`bad amount: ${amount}`); + updateIndivisibleOutputsThatWereReceivedUnstable(conn, () => { console.log("updatePrivateIndivisibleOutputsThatWereReceivedUnstable done"); - var arrPayloadsWithProofs = []; - var arrOutputIds = []; - var accumulated_amount = 0; - var asset = objAsset.asset; + const arrPayloadsWithProofs = []; + const arrOutputIds = []; + let accumulated_amount = 0; + const asset = objAsset.asset; function createOutputs(amount_to_use, change_amount){ - var output = { + const output = { address: to_address, amount: amount_to_use }; if (objAsset.is_private) output.blinding = composer.generateBlinding(); - var outputs = [output]; + const outputs = [output]; if (change_amount){ - var change_output = { + const change_output = { address: change_address, amount: change_amount }; if (objAsset.is_private) change_output.blinding = composer.generateBlinding(); outputs.push(change_output); - outputs.sort(function(o1, o2){ return (o1.address < o2.address) ? -1 : 1; }); + outputs.sort(({address}, {address}) => (address < address) ? -1 : 1); } return outputs; } function pickNextCoin(remaining_amount){ - console.log("looking for output for "+remaining_amount); + console.log(`looking for output for ${remaining_amount}`); if (remaining_amount <= 0) - throw Error("remaining amount is "+remaining_amount); + throw Error(`remaining amount is ${remaining_amount}`); conn.query( "SELECT output_id, unit, message_index, output_index, amount, denomination, address, blinding, is_stable \n\ FROM outputs CROSS JOIN units USING(unit) \n\ @@ -417,19 +416,19 @@ function pickIndivisibleCoinsForAmount( [asset, arrAddresses, last_ball_mci, remaining_amount, (arrOutputIds.length > 0) ? arrOutputIds : -1, remaining_amount + tolerance_plus, remaining_amount], - function(rows){ + rows => { if (rows.length === 0) return issueNextCoinIfAllowed(remaining_amount); - var row = rows[0]; + const row = rows[0]; if (row.is_stable === 0) // contradicts to main_chain_index<=last_ball_mci throw Error("unstable or nonserial unit"); - var input = { + const input = { unit: row.unit, message_index: row.message_index, output_index: row.output_index }; - var amount_to_use; - var change_amount; + let amount_to_use; + let change_amount; if (row.amount > remaining_amount + tolerance_plus){ // take the maximum that the denomination allows amount_to_use = Math.floor((remaining_amount + tolerance_plus)/row.denomination) * row.denomination; @@ -437,16 +436,16 @@ function pickIndivisibleCoinsForAmount( } else amount_to_use = row.amount; - var payload = { - asset: asset, + const payload = { + asset, denomination: row.denomination, inputs: [input], outputs: createOutputs(amount_to_use, change_amount) }; - var objPayloadWithProof = {payload: payload, input_address: row.address}; + const objPayloadWithProof = {payload, input_address: row.address}; if (objAsset.is_private){ - var spend_proof = objectHash.getBase64Hash({ - asset: asset, + const spend_proof = objectHash.getBase64Hash({ + asset, unit: row.unit, message_index: row.message_index, output_index: row.output_index, @@ -454,8 +453,8 @@ function pickIndivisibleCoinsForAmount( amount: row.amount, blinding: row.blinding }); - var objSpendProof = { - spend_proof: spend_proof + const objSpendProof = { + spend_proof }; if (bMultiAuthored) objSpendProof.address = row.address; @@ -482,60 +481,60 @@ function pickIndivisibleCoinsForAmount( function issueNextCoin(remaining_amount){ console.log("issuing a new coin"); if (remaining_amount <= 0) - throw Error("remaining amount is "+remaining_amount); - var issuer_address = objAsset.issued_by_definer_only ? objAsset.definer_address : arrAddresses[0]; - var can_issue_condition = objAsset.cap ? "max_issued_serial_number=0" : "1"; + throw Error(`remaining amount is ${remaining_amount}`); + const issuer_address = objAsset.issued_by_definer_only ? objAsset.definer_address : arrAddresses[0]; + const can_issue_condition = objAsset.cap ? "max_issued_serial_number=0" : "1"; conn.query( - "SELECT denomination, count_coins, max_issued_serial_number FROM asset_denominations \n\ - WHERE asset=? AND "+can_issue_condition+" AND denomination<=? \n\ - ORDER BY denomination DESC LIMIT 1", + `SELECT denomination, count_coins, max_issued_serial_number FROM asset_denominations \n\ + WHERE asset=? AND ${can_issue_condition} AND denomination<=? \n\ + ORDER BY denomination DESC LIMIT 1`, [asset, remaining_amount+tolerance_plus], - function(rows){ + rows => { if (rows.length === 0) return onDone(NOT_ENOUGH_FUNDS_ERROR_MESSAGE); - var row = rows[0]; + const row = rows[0]; if (!!row.count_coins !== !!objAsset.cap) throw Error("invalid asset cap and count_coins"); - var denomination = row.denomination; - var serial_number = row.max_issued_serial_number+1; - var count_coins_to_issue = row.count_coins || Math.floor((remaining_amount+tolerance_plus)/denomination); - var issue_amount = count_coins_to_issue * denomination; + const denomination = row.denomination; + const serial_number = row.max_issued_serial_number+1; + const count_coins_to_issue = row.count_coins || Math.floor((remaining_amount+tolerance_plus)/denomination); + const issue_amount = count_coins_to_issue * denomination; conn.query( "UPDATE asset_denominations SET max_issued_serial_number=max_issued_serial_number+1 WHERE denomination=? AND asset=?", [denomination, asset], - function(){ - var input = { + () => { + const input = { type: 'issue', - serial_number: serial_number, + serial_number, amount: issue_amount }; if (bMultiAuthored) input.address = issuer_address; - var amount_to_use; - var change_amount; + let amount_to_use; + let change_amount; if (issue_amount > remaining_amount + tolerance_plus){ amount_to_use = Math.floor((remaining_amount + tolerance_plus)/denomination) * denomination; change_amount = issue_amount - amount_to_use; } else amount_to_use = issue_amount; - var payload = { - asset: asset, - denomination: denomination, + const payload = { + asset, + denomination, inputs: [input], outputs: createOutputs(amount_to_use, change_amount) }; - var objPayloadWithProof = {payload: payload, input_address: issuer_address}; + const objPayloadWithProof = {payload, input_address: issuer_address}; if (objAsset.is_private){ - var spend_proof = objectHash.getBase64Hash({ - asset: asset, + const spend_proof = objectHash.getBase64Hash({ + asset, address: issuer_address, - serial_number: serial_number, // need to avoid duplicate spend proofs when issuing uncapped coins - denomination: denomination, + serial_number, // need to avoid duplicate spend proofs when issuing uncapped coins + denomination, amount: input.amount }); - var objSpendProof = { - spend_proof: spend_proof + const objSpendProof = { + spend_proof }; if (bMultiAuthored) objSpendProof.address = issuer_address; @@ -543,7 +542,7 @@ function pickIndivisibleCoinsForAmount( } arrPayloadsWithProofs.push(objPayloadWithProof); accumulated_amount += amount_to_use; - console.log("payloads with proofs: "+JSON.stringify(arrPayloadsWithProofs)); + console.log(`payloads with proofs: ${JSON.stringify(arrPayloadsWithProofs)}`); if (accumulated_amount >= amount - tolerance_minus && accumulated_amount <= amount + tolerance_plus) return onDone(null, arrPayloadsWithProofs); pickNextCoin(amount - accumulated_amount); @@ -553,9 +552,9 @@ function pickIndivisibleCoinsForAmount( ); } - var arrSpendableAddresses = arrAddresses.concat(); // cloning + const arrSpendableAddresses = arrAddresses.concat(); // cloning if (objAsset && objAsset.auto_destroy){ - var i = arrAddresses.indexOf(objAsset.definer_address); + const i = arrAddresses.indexOf(objAsset.definer_address); if (i>=0) arrSpendableAddresses.splice(i, 1); } @@ -572,9 +571,9 @@ function pickIndivisibleCoinsForAmount( * Using Durstenfeld shuffle algorithm. */ function shuffleArray(array) { - for (var i = array.length - 1; i > 0; i--) { - var j = Math.floor(Math.random() * (i + 1)); - var temp = array[i]; + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + const temp = array[i]; array[i] = array[j]; array[j] = temp; } @@ -583,20 +582,20 @@ function shuffleArray(array) { // this function receives fully open payload function buildPrivateElementsChain(conn, unit, message_index, output_index, payload, handlePrivateElements){ - var asset = payload.asset; - var denomination = payload.denomination; - var output = payload.outputs[output_index]; - var hidden_payload = _.cloneDeep(payload); - hidden_payload.outputs.forEach(function(o){ - delete o.address; - delete o.blinding; + const asset = payload.asset; + const denomination = payload.denomination; + const output = payload.outputs[output_index]; + const hidden_payload = _.cloneDeep(payload); + hidden_payload.outputs.forEach(({address, blinding}) => { + delete address; + delete blinding; // output_hash was already added }); - var arrPrivateElements = [{ - unit: unit, - message_index: message_index, + const arrPrivateElements = [{ + unit, + message_index, payload: hidden_payload, - output_index: output_index, + output_index, output: { address: output.address, blinding: output.blinding @@ -609,19 +608,19 @@ function buildPrivateElementsChain(conn, unit, message_index, output_index, payl (SELECT COUNT(*) FROM unit_authors WHERE unit=?) AS count_authors \n\ FROM inputs WHERE unit=? AND message_index=?", [_unit, _unit, _message_index], - function(in_rows){ + in_rows => { if (in_rows.length === 0) throw Error("building chain: blackbyte input not found"); if (in_rows.length > 1) throw Error("building chain: more than 1 input found"); - var in_row = in_rows[0]; + const in_row = in_rows[0]; if (!in_row.address) throw Error("readPayloadAndGoUp: input address is NULL"); if (in_row.asset !== asset) throw Error("building chain: asset mismatch"); if (in_row.denomination !== denomination) throw Error("building chain: denomination mismatch"); - var input = {}; + const input = {}; if (in_row.src_unit){ // transfer input.unit = in_row.src_unit; input.message_index = in_row.src_message_index; @@ -638,11 +637,11 @@ function buildPrivateElementsChain(conn, unit, message_index, output_index, payl "SELECT address, blinding, output_hash, amount, output_index, asset, denomination FROM outputs \n\ WHERE unit=? AND message_index=? ORDER BY output_index", [_unit, _message_index], - function(out_rows){ + out_rows => { if (out_rows.length === 0) throw Error("blackbyte output not found"); - var output = {}; - var outputs = out_rows.map(function(o){ + const output = {}; + const outputs = out_rows.map(o => { if (o.asset !== asset) throw Error("outputs asset mismatch"); if (o.denomination !== denomination) @@ -658,17 +657,17 @@ function buildPrivateElementsChain(conn, unit, message_index, output_index, payl }); if (!output.address) throw Error("output not filled"); - var objPrivateElement = { + const objPrivateElement = { unit: _unit, message_index: _message_index, payload: { - asset: asset, - denomination: denomination, + asset, + denomination, inputs: [input], - outputs: outputs + outputs }, output_index: _output_index, - output: output + output }; arrPrivateElements.push(objPrivateElement); (input.type === 'issue') @@ -680,14 +679,14 @@ function buildPrivateElementsChain(conn, unit, message_index, output_index, payl ); } - var input = payload.inputs[0]; + const input = payload.inputs[0]; (input.type === 'issue') ? handlePrivateElements(arrPrivateElements) : readPayloadAndGoUp(input.unit, input.message_index, input.output_index); } function composeIndivisibleAssetPaymentJoint(params){ - console.log("indivisible payment from "+params.paying_addresses, params); + console.log(`indivisible payment from ${params.paying_addresses}`, params); if ((params.to_address || params.amount) && params.asset_outputs) throw Error("to_address and asset_outputs at the same time"); if (params.asset_outputs){ @@ -706,8 +705,8 @@ function composeIndivisibleAssetPaymentJoint(params){ // function that creates additional messages to be added to the joint retrieveMessages: function createAdditionalMessages(conn, last_ball_mci, bMultiAuthored, arrPayingAddresses, onDone){ - var arrAssetPayingAddresses = _.intersection(arrPayingAddresses, params.paying_addresses); - storage.loadAssetWithListOfAttestedAuthors(conn, params.asset, last_ball_mci, arrAssetPayingAddresses, function(err, objAsset){ + const arrAssetPayingAddresses = _.intersection(arrPayingAddresses, params.paying_addresses); + storage.loadAssetWithListOfAttestedAuthors(conn, params.asset, last_ball_mci, arrAssetPayingAddresses, (err, objAsset) => { if (err) return onDone(err); if (!objAsset.fixed_denominations) @@ -724,34 +723,34 @@ function composeIndivisibleAssetPaymentJoint(params){ params.to_address, params.change_address, params.amount, params.tolerance_plus || 0, params.tolerance_minus || 0, bMultiAuthored, - function(err, arrPayloadsWithProofs){ + (err, arrPayloadsWithProofs) => { if (!arrPayloadsWithProofs) return onDone({ error_code: "NOT_ENOUGH_FUNDS", error: err }); - var arrMessages = []; - var assocPrivatePayloads = {}; - for (var i=0; i { o.output_hash = objectHash.getBase64Hash({address: o.address, blinding: o.blinding}); }); - var hidden_payload = _.cloneDeep(payload); - hidden_payload.outputs.forEach(function(o){ - delete o.address; - delete o.blinding; + const hidden_payload = _.cloneDeep(payload); + hidden_payload.outputs.forEach(({address, blinding}) => { + delete address; + delete blinding; }); payload_hash = objectHash.getBase64Hash(hidden_payload); } else payload_hash = objectHash.getBase64Hash(payload); - var objMessage = { + const objMessage = { app: "payment", payload_location: objAsset.is_private ? "none" : "inline", - payload_hash: payload_hash + payload_hash }; if (objAsset.is_private){ assocPrivatePayloads[payload_hash] = payload; @@ -763,7 +762,7 @@ function composeIndivisibleAssetPaymentJoint(params){ } // messages are sorted in descending order by denomination of the coin, so shuffle them to avoid giving any clues shuffleArray(arrMessages); - console.log("composed messages "+JSON.stringify(arrMessages)); + console.log(`composed messages ${JSON.stringify(arrMessages)}`); onDone(null, arrMessages, assocPrivatePayloads); } ); @@ -775,7 +774,7 @@ function composeIndivisibleAssetPaymentJoint(params){ callbacks: { ifError: params.callbacks.ifError, ifNotEnoughFunds: params.callbacks.ifNotEnoughFunds, - ifOk: function(objJoint, assocPrivatePayloads, composer_unlock_callback){ + ifOk(objJoint, assocPrivatePayloads, composer_unlock_callback) { params.callbacks.ifOk(objJoint, assocPrivatePayloads, composer_unlock_callback); } } @@ -787,62 +786,62 @@ function getSavingCallbacks(to_address, callbacks){ return { ifError: callbacks.ifError, ifNotEnoughFunds: callbacks.ifNotEnoughFunds, - ifOk: function(objJoint, assocPrivatePayloads, composer_unlock){ - var objUnit = objJoint.unit; - var unit = objUnit.unit; + ifOk(objJoint, assocPrivatePayloads, composer_unlock) { + const objUnit = objJoint.unit; + const unit = objUnit.unit; validation.validate(objJoint, { - ifUnitError: function(err){ + ifUnitError(err) { composer_unlock(); - callbacks.ifError("Validation error: "+err); + callbacks.ifError(`Validation error: ${err}`); // throw Error("unexpected validation error: "+err); }, - ifJointError: function(err){ - throw Error("unexpected validation joint error: "+err); + ifJointError(err) { + throw Error(`unexpected validation joint error: ${err}`); }, - ifTransientError: function(err){ - throw Error("unexpected validation transient error: "+err); + ifTransientError(err) { + throw Error(`unexpected validation transient error: ${err}`); }, - ifNeedHashTree: function(){ + ifNeedHashTree() { throw Error("unexpected need hash tree"); }, - ifNeedParentUnits: function(arrMissingUnits){ - throw Error("unexpected dependencies: "+arrMissingUnits.join(", ")); + ifNeedParentUnits(arrMissingUnits) { + throw Error(`unexpected dependencies: ${arrMissingUnits.join(", ")}`); }, - ifOk: function(objValidationState, validation_unlock){ - console.log("Private OK "+objValidationState.sequence); + ifOk(objValidationState, validation_unlock) { + console.log(`Private OK ${objValidationState.sequence}`); if (objValidationState.sequence !== 'good'){ validation_unlock(); composer_unlock(); - return callbacks.ifError("Indivisible asset bad sequence "+objValidationState.sequence); + return callbacks.ifError(`Indivisible asset bad sequence ${objValidationState.sequence}`); } - var bPrivate = !!assocPrivatePayloads; - var arrRecipientChains = bPrivate ? [] : null; // chains for to_address - var arrCosignerChains = bPrivate ? [] : null; // chains for all output addresses, including change, to be shared with cosigners (if any) - var preCommitCallback = null; - var bPreCommitCallbackFailed = false; + const bPrivate = !!assocPrivatePayloads; + const arrRecipientChains = bPrivate ? [] : null; // chains for to_address + const arrCosignerChains = bPrivate ? [] : null; // chains for all output addresses, including change, to be shared with cosigners (if any) + let preCommitCallback = null; + let bPreCommitCallbackFailed = false; if (bPrivate){ - preCommitCallback = function(conn, cb){ + preCommitCallback = (conn, cb) => { async.eachSeries( Object.keys(assocPrivatePayloads), - function(payload_hash, cb2){ - var message_index = composer.getMessageIndexByPayloadHash(objUnit, payload_hash); - var payload = assocPrivatePayloads[payload_hash]; + (payload_hash, cb2) => { + const message_index = composer.getMessageIndexByPayloadHash(objUnit, payload_hash); + const payload = assocPrivatePayloads[payload_hash]; // We build, validate, and save two chains: one for the payee, the other for oneself (the change). // They differ only in the last element async.forEachOfSeries( payload.outputs, - function(output, output_index, cb3){ + ({address}, output_index, cb3) => { // we have only heads of the chains so far. Now add the tails. buildPrivateElementsChain( conn, unit, message_index, output_index, payload, - function(arrPrivateElements){ + arrPrivateElements => { validateAndSavePrivatePaymentChain(conn, _.cloneDeep(arrPrivateElements), { - ifError: function(err){ + ifError(err) { cb3(err); }, - ifOk: function(){ - if (output.address === to_address) + ifOk() { + if (address === to_address) arrRecipientChains.push(arrPrivateElements); arrCosignerChains.push(arrPrivateElements); cb3(); @@ -854,17 +853,17 @@ function getSavingCallbacks(to_address, callbacks){ cb2 ); }, - function(err){ + err => { if (err){ - console.log("===== error in precommit callback: "+err); + console.log(`===== error in precommit callback: ${err}`); bPreCommitCallbackFailed = true; return cb(err); } - var onSuccessfulPrecommit = !conf.bLight ? cb : function(){ + const onSuccessfulPrecommit = !conf.bLight ? cb : () => { composer.postJointToLightVendorIfNecessaryAndSave( objJoint, function onLightError(err){ // light only - console.log("failed to post indivisible payment "+unit); + console.log(`failed to post indivisible payment ${unit}`); bPreCommitCallbackFailed = true; cb(err); // will rollback }, @@ -881,22 +880,22 @@ function getSavingCallbacks(to_address, callbacks){ }; } else { if (typeof callbacks.preCommitCb === "function") { - preCommitCallback = function(conn, cb){ + preCommitCallback = (conn, cb) => { callbacks.preCommitCb(conn, objJoint, cb); } } } - var saveAndUnlock = function(){ + const saveAndUnlock = () => { writer.saveJoint( objJoint, objValidationState, preCommitCallback, function onDone(err){ - console.log("saved unit "+unit); + console.log(`saved unit ${unit}`); validation_unlock(); composer_unlock(); if (bPreCommitCallbackFailed) - callbacks.ifError("precommit callback failed: "+err); + callbacks.ifError(`precommit callback failed: ${err}`); else callbacks.ifOk(objJoint, arrRecipientChains, arrCosignerChains); } @@ -911,7 +910,7 @@ function getSavingCallbacks(to_address, callbacks){ composer.postJointToLightVendorIfNecessaryAndSave( objJoint, function onLightError(err){ // light only - console.log("failed to post indivisible payment "+unit); + console.log(`failed to post indivisible payment ${unit}`); validation_unlock(); composer_unlock(); callbacks.ifError(err); @@ -925,31 +924,31 @@ function getSavingCallbacks(to_address, callbacks){ } function restorePrivateChains(asset, unit, to_address, handleChains){ - var arrRecipientChains = []; - var arrCosignerChains = []; + const arrRecipientChains = []; + const arrCosignerChains = []; db.query( "SELECT DISTINCT message_index, denomination, payload_hash FROM outputs JOIN messages USING(unit, message_index) WHERE unit=? AND asset=?", [unit, asset], - function(rows){ + rows => { async.eachSeries( rows, - function(row, cb){ - var payload = {asset: asset, denomination: row.denomination}; - var message_index = row.message_index; + (row, cb) => { + const payload = {asset, denomination: row.denomination}; + const message_index = row.message_index; db.query( "SELECT src_unit, src_message_index, src_output_index, denomination, asset FROM inputs WHERE unit=? AND message_index=?", [unit, message_index], - function(input_rows){ + input_rows => { if (input_rows.length !== 1) throw Error("not 1 input"); - var input_row = input_rows[0]; + const input_row = input_rows[0]; if (input_row.asset !== asset) throw Error("assets don't match"); if (input_row.denomination !== row.denomination) throw Error("denominations don't match"); if (input_row.src_message_index === null || input_row.src_output_index === null) throw Error("only transfers supported"); - var input = { + const input = { unit: input_row.src_unit, message_index: input_row.src_message_index, output_index: input_row.src_output_index @@ -959,30 +958,30 @@ function restorePrivateChains(asset, unit, to_address, handleChains){ "SELECT address, amount, blinding, output_hash FROM outputs \n\ WHERE unit=? AND asset=? AND message_index=? ORDER BY output_index", [unit, asset, message_index], - function(outputs){ + outputs => { if (outputs.length === 0) - throw Error("outputs not found for mi "+message_index); - if (!outputs.some(function(output){ return (output.address && output.blinding); })) + throw Error(`outputs not found for mi ${message_index}`); + if (!outputs.some(({address, blinding}) => address && blinding)) throw Error("all outputs are hidden"); payload.outputs = outputs; - var hidden_payload = _.cloneDeep(payload); - hidden_payload.outputs.forEach(function(o){ - delete o.address; - delete o.blinding; + const hidden_payload = _.cloneDeep(payload); + hidden_payload.outputs.forEach(({address, blinding}) => { + delete address; + delete blinding; }); - var payload_hash = objectHash.getBase64Hash(hidden_payload); + const payload_hash = objectHash.getBase64Hash(hidden_payload); if (payload_hash !== row.payload_hash) throw Error("wrong payload hash"); async.forEachOfSeries( payload.outputs, - function(output, output_index, cb3){ - if (!output.address || !output.blinding) // skip + ({address, blinding}, output_index, cb3) => { + if (!address || !blinding) // skip return cb3(); // we have only heads of the chains so far. Now add the tails. buildPrivateElementsChain( db, unit, message_index, output_index, payload, - function(arrPrivateElements){ - if (output.address === to_address) + arrPrivateElements => { + if (address === to_address) arrRecipientChains.push(arrPrivateElements); arrCosignerChains.push(arrPrivateElements); cb3(); @@ -996,7 +995,7 @@ function restorePrivateChains(asset, unit, to_address, handleChains){ } ); }, - function(){ + () => { handleChains(arrRecipientChains, arrCosignerChains); } ); @@ -1006,14 +1005,14 @@ function restorePrivateChains(asset, unit, to_address, handleChains){ // {asset: asset, paying_addresses: arrPayingAddresses, fee_paying_addresses: arrFeePayingAddresses, to_address: to_address, change_address: change_address, amount: amount, tolerance_plus: tolerance_plus, tolerance_minus: tolerance_minus, signer: signer, callbacks: callbacks} function composeAndSaveIndivisibleAssetPaymentJoint(params){ - var params_with_save = _.clone(params); + const params_with_save = _.clone(params); params_with_save.callbacks = getSavingCallbacks(getToAddress(params), params.callbacks); composeIndivisibleAssetPaymentJoint(params_with_save); } function readAddressesFundedInAsset(asset, amount, arrAvailablePayingAddresses, handleFundedAddresses){ - var remaining_amount = amount; - var assocAddresses = {}; + let remaining_amount = amount; + const assocAddresses = {}; db.query( "SELECT amount, denomination, address FROM outputs CROSS JOIN units USING(unit) \n\ WHERE is_spent=0 AND address IN(?) AND is_stable=1 AND sequence='good' AND asset=? \n\ @@ -1023,33 +1022,33 @@ function readAddressesFundedInAsset(asset, amount, arrAvailablePayingAddresses, ) \n\ ORDER BY denomination DESC, amount DESC", [arrAvailablePayingAddresses, asset], - function(rows){ - for (var i=0; i { + for (let i=0; i remaining_amount) continue; assocAddresses[row.address] = true; - var used_amount = (row.amount <= remaining_amount) ? row.amount : row.denomination * Math.floor(remaining_amount/row.denomination); + const used_amount = (row.amount <= remaining_amount) ? row.amount : row.denomination * Math.floor(remaining_amount/row.denomination); remaining_amount -= used_amount; if (remaining_amount === 0) break; }; - var arrAddresses = Object.keys(assocAddresses); + const arrAddresses = Object.keys(assocAddresses); handleFundedAddresses(arrAddresses); } ); } -var TYPICAL_FEE = 3000; +const TYPICAL_FEE = 3000; // reads addresses funded in asset plus addresses for paying commissions function readFundedAddresses(asset, amount, arrAvailablePayingAddresses, arrAvailableFeePayingAddresses, handleFundedAddresses){ - readAddressesFundedInAsset(asset, amount, arrAvailablePayingAddresses, function(arrAddressesFundedInAsset){ + readAddressesFundedInAsset(asset, amount, arrAvailablePayingAddresses, arrAddressesFundedInAsset => { // add other addresses to pay for commissions (in case arrAddressesFundedInAsset don't have enough bytes to pay commissions) // var arrOtherAddresses = _.difference(arrAvailablePayingAddresses, arrAddressesFundedInAsset); // if (arrOtherAddresses.length === 0) // return handleFundedAddresses(arrAddressesFundedInAsset); - composer.readSortedFundedAddresses(null, arrAvailableFeePayingAddresses, TYPICAL_FEE, function(arrFundedFeePayingAddresses){ + composer.readSortedFundedAddresses(null, arrAvailableFeePayingAddresses, TYPICAL_FEE, arrFundedFeePayingAddresses => { // if (arrFundedOtherAddresses.length === 0) // return handleFundedAddresses(arrAddressesFundedInAsset); // handleFundedAddresses(arrAddressesFundedInAsset.concat(arrFundedOtherAddresses)); @@ -1066,10 +1065,10 @@ function composeMinimalIndivisibleAssetPaymentJoint(params){ throw Error('no available_fee_paying_addresses'); readFundedAddresses( params.asset, params.amount, params.available_paying_addresses, params.available_fee_paying_addresses, - function(arrFundedPayingAddresses, arrFundedFeePayingAddresses){ + (arrFundedPayingAddresses, arrFundedFeePayingAddresses) => { if (arrFundedPayingAddresses.length === 0) return params.callbacks.ifNotEnoughFunds("all paying addresses are unfunded in asset, make sure all your funds are confirmed"); - var minimal_params = _.clone(params); + const minimal_params = _.clone(params); delete minimal_params.available_paying_addresses; delete minimal_params.available_fee_paying_addresses; minimal_params.minimal = true; @@ -1082,16 +1081,16 @@ function composeMinimalIndivisibleAssetPaymentJoint(params){ // {asset: asset, available_paying_addresses: arrAvailablePayingAddresses, available_fee_paying_addresses: arrAvailableFeePayingAddresses, to_address: to_address, amount: amount, tolerance_plus: tolerance_plus, tolerance_minus: tolerance_minus, signer: signer, callbacks: callbacks} function composeAndSaveMinimalIndivisibleAssetPaymentJoint(params){ - var params_with_save = _.clone(params); + const params_with_save = _.clone(params); params_with_save.callbacks = getSavingCallbacks(getToAddress(params), params.callbacks); composeMinimalIndivisibleAssetPaymentJoint(params_with_save); } -function getToAddress(params){ - if (params.to_address) - return params.to_address; - if (params.asset_outputs) - return params.asset_outputs[0].address; +function getToAddress({to_address, asset_outputs}) { + if (to_address) + return to_address; + if (asset_outputs) + return asset_outputs[0].address; throw Error("unable to determine to_address"); } diff --git a/joint_storage.js b/joint_storage.js index 7ab8cb6c..cda601ac 100644 --- a/joint_storage.js +++ b/joint_storage.js @@ -1,30 +1,29 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var async = require('async'); -var storage = require('./storage.js'); -var archiving = require('./archiving.js'); -var db = require('./db.js'); -var constants = require("./constants.js"); -var objectHash = require("./object_hash.js"); -var mutex = require('./mutex.js'); -var conf = require('./conf.js'); -var breadcrumbs = require('./breadcrumbs.js'); +const _ = require('lodash'); +const async = require('async'); +const storage = require('./storage.js'); +const archiving = require('./archiving.js'); +const db = require('./db.js'); +const constants = require("./constants.js"); +const objectHash = require("./object_hash.js"); +const mutex = require('./mutex.js'); +const conf = require('./conf.js'); +const breadcrumbs = require('./breadcrumbs.js'); function checkIfNewUnit(unit, callbacks) { if (storage.isKnownUnit(unit)) return callbacks.ifKnown(); - db.query("SELECT 1 FROM units WHERE unit=?", [unit], function(rows){ - if (rows.length > 0){ + db.query("SELECT 1 FROM units WHERE unit=?", [unit], ({length}) => { + if (length > 0){ storage.setUnitIsKnown(unit); return callbacks.ifKnown(); } - db.query("SELECT 1 FROM unhandled_joints WHERE unit=?", [unit], function(unhandled_rows){ - if (unhandled_rows.length > 0) + db.query("SELECT 1 FROM unhandled_joints WHERE unit=?", [unit], ({length}) => { + if (length > 0) return callbacks.ifKnownUnverified(); - db.query("SELECT error FROM known_bad_joints WHERE unit=?", [unit], function(bad_rows){ + db.query("SELECT error FROM known_bad_joints WHERE unit=?", [unit], bad_rows => { (bad_rows.length === 0) ? callbacks.ifNew() : callbacks.ifKnownBad(bad_rows[0].error); }); }); @@ -36,8 +35,8 @@ function checkIfNewJoint(objJoint, callbacks) { ifKnown: callbacks.ifKnown, ifKnownUnverified: callbacks.ifKnownUnverified, ifKnownBad: callbacks.ifKnownBad, - ifNew: function(){ - db.query("SELECT error FROM known_bad_joints WHERE joint=?", [objectHash.getJointHash(objJoint)], function(bad_rows){ + ifNew() { + db.query("SELECT error FROM known_bad_joints WHERE joint=?", [objectHash.getJointHash(objJoint)], bad_rows => { (bad_rows.length === 0) ? callbacks.ifNew() : callbacks.ifKnownBad(bad_rows[0].error); }); } @@ -46,13 +45,13 @@ function checkIfNewJoint(objJoint, callbacks) { function removeUnhandledJointAndDependencies(unit, onDone){ - db.takeConnectionFromPool(function(conn){ - var arrQueries = []; + db.takeConnectionFromPool(conn => { + const arrQueries = []; conn.addQuery(arrQueries, "BEGIN"); conn.addQuery(arrQueries, "DELETE FROM unhandled_joints WHERE unit=?", [unit]); conn.addQuery(arrQueries, "DELETE FROM dependencies WHERE unit=?", [unit]); conn.addQuery(arrQueries, "COMMIT"); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { conn.release(); if (onDone) onDone(); @@ -61,17 +60,15 @@ function removeUnhandledJointAndDependencies(unit, onDone){ } function saveUnhandledJointAndDependencies(objJoint, arrMissingParentUnits, peer, onDone){ - db.takeConnectionFromPool(function(conn){ - var unit = objJoint.unit.unit; - var sql = "INSERT "+conn.getIgnore()+" INTO dependencies (unit, depends_on_unit) VALUES " + arrMissingParentUnits.map(function(missing_unit){ - return "("+conn.escape(unit)+", "+conn.escape(missing_unit)+")"; - }).join(", "); - var arrQueries = []; + db.takeConnectionFromPool(conn => { + const unit = objJoint.unit.unit; + const sql = `INSERT ${conn.getIgnore()} INTO dependencies (unit, depends_on_unit) VALUES ${arrMissingParentUnits.map(missing_unit => `(${conn.escape(unit)}, ${conn.escape(missing_unit)})`).join(", ")}`; + const arrQueries = []; conn.addQuery(arrQueries, "BEGIN"); conn.addQuery(arrQueries, "INSERT INTO unhandled_joints (unit, json, peer) VALUES (?, ?, ?)", [unit, JSON.stringify(objJoint), peer]); conn.addQuery(arrQueries, sql); conn.addQuery(arrQueries, "COMMIT"); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { conn.release(); if (onDone) onDone(); @@ -83,27 +80,27 @@ function saveUnhandledJointAndDependencies(objJoint, arrMissingParentUnits, peer // handleDependentJoint called for each dependent unit function readDependentJointsThatAreReady(unit, handleDependentJoint){ //console.log("readDependentJointsThatAreReady "+unit); - var t=Date.now(); - var from = unit ? "FROM dependencies AS src_deps JOIN dependencies USING(unit)" : "FROM dependencies"; - var where = unit ? "WHERE src_deps.depends_on_unit="+db.escape(unit) : ""; - mutex.lock(["dependencies"], function(unlock){ + const t=Date.now(); + const from = unit ? "FROM dependencies AS src_deps JOIN dependencies USING(unit)" : "FROM dependencies"; + const where = unit ? `WHERE src_deps.depends_on_unit=${db.escape(unit)}` : ""; + mutex.lock(["dependencies"], unlock => { db.query( - "SELECT dependencies.unit, unhandled_joints.unit AS unit_for_json, \n\ - SUM(CASE WHEN units.unit IS NULL THEN 1 ELSE 0 END) AS count_missing_parents \n\ - "+from+" \n\ - JOIN unhandled_joints ON dependencies.unit=unhandled_joints.unit \n\ - LEFT JOIN units ON dependencies.depends_on_unit=units.unit \n\ - "+where+" \n\ - GROUP BY dependencies.unit \n\ - HAVING count_missing_parents=0 \n\ - ORDER BY NULL", - function(rows){ + `SELECT dependencies.unit, unhandled_joints.unit AS unit_for_json, \n\ + SUM(CASE WHEN units.unit IS NULL THEN 1 ELSE 0 END) AS count_missing_parents \n\ + ${from} \n\ + JOIN unhandled_joints ON dependencies.unit=unhandled_joints.unit \n\ + LEFT JOIN units ON dependencies.depends_on_unit=units.unit \n\ + ${where} \n\ + GROUP BY dependencies.unit \n\ + HAVING count_missing_parents=0 \n\ + ORDER BY NULL`, + rows => { //console.log(rows.length+" joints are ready"); //console.log("deps: "+(Date.now()-t)); - rows.forEach(function(row) { - db.query("SELECT json, peer, "+db.getUnixTimestamp("creation_date")+" AS creation_ts FROM unhandled_joints WHERE unit=?", [row.unit_for_json], function(internal_rows){ - internal_rows.forEach(function(internal_row) { - handleDependentJoint(JSON.parse(internal_row.json), parseInt(internal_row.creation_ts), internal_row.peer); + rows.forEach(({unit_for_json}) => { + db.query(`SELECT json, peer, ${db.getUnixTimestamp("creation_date")} AS creation_ts FROM unhandled_joints WHERE unit=?`, [unit_for_json], internal_rows => { + internal_rows.forEach(({json, creation_ts, peer}) => { + handleDependentJoint(JSON.parse(json), parseInt(creation_ts), peer); }); }); }); @@ -116,32 +113,32 @@ function readDependentJointsThatAreReady(unit, handleDependentJoint){ function findLostJoints(handleLostJoints){ //console.log("findLostJoints"); db.query( - "SELECT DISTINCT depends_on_unit \n\ - FROM dependencies \n\ - LEFT JOIN unhandled_joints ON depends_on_unit=unhandled_joints.unit \n\ - LEFT JOIN units ON depends_on_unit=units.unit \n\ - WHERE unhandled_joints.unit IS NULL AND units.unit IS NULL AND dependencies.creation_date < " + db.addTime("-8 SECOND"), - function(rows){ + `SELECT DISTINCT depends_on_unit \n\ + FROM dependencies \n\ + LEFT JOIN unhandled_joints ON depends_on_unit=unhandled_joints.unit \n\ + LEFT JOIN units ON depends_on_unit=units.unit \n\ + WHERE unhandled_joints.unit IS NULL AND units.unit IS NULL AND dependencies.creation_date < ${db.addTime("-8 SECOND")}`, + rows => { //console.log(rows.length+" lost joints"); if (rows.length === 0) return; - handleLostJoints(rows.map(function(row){ return row.depends_on_unit; })); + handleLostJoints(rows.map(({depends_on_unit}) => depends_on_unit)); } ); } // onPurgedDependentJoint called for each purged dependent unit function purgeJointAndDependencies(objJoint, error, onPurgedDependentJoint, onDone){ - db.takeConnectionFromPool(function(conn){ - var unit = objJoint.unit.unit; - var arrQueries = []; + db.takeConnectionFromPool(conn => { + const unit = objJoint.unit.unit; + const arrQueries = []; conn.addQuery(arrQueries, "BEGIN"); conn.addQuery(arrQueries, "INSERT INTO known_bad_joints (unit, json, error) VALUES (?,?,?)", [unit, JSON.stringify(objJoint), error]); conn.addQuery(arrQueries, "DELETE FROM unhandled_joints WHERE unit=?", [unit]); // if any conn.addQuery(arrQueries, "DELETE FROM dependencies WHERE unit=?", [unit]); - collectQueriesToPurgeDependentJoints(conn, arrQueries, unit, error, onPurgedDependentJoint, function(){ + collectQueriesToPurgeDependentJoints(conn, arrQueries, unit, error, onPurgedDependentJoint, () => { conn.addQuery(arrQueries, "COMMIT"); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { conn.release(); if (onDone) onDone(); @@ -152,12 +149,12 @@ function purgeJointAndDependencies(objJoint, error, onPurgedDependentJoint, onDo // onPurgedDependentJoint called for each purged dependent unit function purgeDependencies(unit, error, onPurgedDependentJoint, onDone){ - db.takeConnectionFromPool(function(conn){ - var arrQueries = []; + db.takeConnectionFromPool(conn => { + const arrQueries = []; conn.addQuery(arrQueries, "BEGIN"); - collectQueriesToPurgeDependentJoints(conn, arrQueries, unit, error, onPurgedDependentJoint, function(){ + collectQueriesToPurgeDependentJoints(conn, arrQueries, unit, error, onPurgedDependentJoint, () => { conn.addQuery(arrQueries, "COMMIT"); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { conn.release(); if (onDone) onDone(); @@ -168,18 +165,18 @@ function purgeDependencies(unit, error, onPurgedDependentJoint, onDone){ // onPurgedDependentJoint called for each purged dependent unit function collectQueriesToPurgeDependentJoints(conn, arrQueries, unit, error, onPurgedDependentJoint, onDone){ - conn.query("SELECT unit, peer FROM dependencies JOIN unhandled_joints USING(unit) WHERE depends_on_unit=?", [unit], function(rows){ + conn.query("SELECT unit, peer FROM dependencies JOIN unhandled_joints USING(unit) WHERE depends_on_unit=?", [unit], rows => { if (rows.length === 0) return onDone(); //conn.addQuery(arrQueries, "DELETE FROM dependencies WHERE depends_on_unit=?", [unit]); - var arrUnits = rows.map(function(row) { return row.unit; }); - conn.addQuery(arrQueries, "INSERT "+conn.getIgnore()+" INTO known_bad_joints (unit, json, error) \n\ - SELECT unit, json, ? FROM unhandled_joints WHERE unit IN(?)", [error, arrUnits]); + const arrUnits = rows.map(row => row.unit); + conn.addQuery(arrQueries, `INSERT ${conn.getIgnore()} INTO known_bad_joints (unit, json, error) \n\ + SELECT unit, json, ? FROM unhandled_joints WHERE unit IN(?)`, [error, arrUnits]); conn.addQuery(arrQueries, "DELETE FROM unhandled_joints WHERE unit IN(?)", [arrUnits]); conn.addQuery(arrQueries, "DELETE FROM dependencies WHERE unit IN(?)", [arrUnits]); async.eachSeries( rows, - function(row, cb){ + (row, cb) => { if (onPurgedDependentJoint) onPurgedDependentJoint(row.unit, row.peer); collectQueriesToPurgeDependentJoints(conn, arrQueries, row.unit, error, onPurgedDependentJoint, cb); @@ -190,50 +187,48 @@ function collectQueriesToPurgeDependentJoints(conn, arrQueries, unit, error, onP } function purgeUncoveredNonserialJointsUnderLock(){ - mutex.lockOrSkip(["purge_uncovered"], function(unlock){ + mutex.lockOrSkip(["purge_uncovered"], unlock => { purgeUncoveredNonserialJoints(false, unlock); }); } function purgeUncoveredNonserialJoints(bByExistenceOfChildren, onDone){ - var cond = bByExistenceOfChildren ? "(SELECT 1 FROM parenthoods WHERE parent_unit=unit LIMIT 1) IS NULL" : "is_free=1"; - var order_column = (conf.storage === 'mysql') ? 'creation_date' : 'rowid'; // this column must be indexed! - var byIndex = (bByExistenceOfChildren && conf.storage === 'sqlite') ? 'INDEXED BY bySequence' : ''; + const cond = bByExistenceOfChildren ? "(SELECT 1 FROM parenthoods WHERE parent_unit=unit LIMIT 1) IS NULL" : "is_free=1"; + const order_column = (conf.storage === 'mysql') ? 'creation_date' : 'rowid'; // this column must be indexed! + const byIndex = (bByExistenceOfChildren && conf.storage === 'sqlite') ? 'INDEXED BY bySequence' : ''; // the purged units can arrive again, no problem db.query( // purge the bad ball if we've already received at least 7 witnesses after receiving the bad ball - "SELECT unit FROM units "+byIndex+" \n\ - WHERE "+cond+" AND sequence IN('final-bad','temp-bad') AND content_hash IS NULL \n\ - AND NOT EXISTS (SELECT * FROM dependencies WHERE depends_on_unit=units.unit) \n\ - AND NOT EXISTS (SELECT * FROM balls WHERE balls.unit=units.unit) \n\ - AND (units.creation_date < "+db.addTime('-10 SECOND')+" OR EXISTS ( \n\ - SELECT DISTINCT address FROM units AS wunits CROSS JOIN unit_authors USING(unit) CROSS JOIN my_witnesses USING(address) \n\ - WHERE wunits."+order_column+" > units."+order_column+" \n\ - LIMIT 0,1 \n\ - )) \n\ - /* AND NOT EXISTS (SELECT * FROM unhandled_joints) */", - // some unhandled joints may depend on the unit to be archived but it is not in dependencies because it was known when its child was received - // [constants.MAJORITY_OF_WITNESSES - 1], - function(rows){ + `SELECT unit FROM units ${byIndex} \n\ + WHERE ${cond} AND sequence IN('final-bad','temp-bad') AND content_hash IS NULL \n\ + AND NOT EXISTS (SELECT * FROM dependencies WHERE depends_on_unit=units.unit) \n\ + AND NOT EXISTS (SELECT * FROM balls WHERE balls.unit=units.unit) \n\ + AND (units.creation_date < ${db.addTime('-10 SECOND')} OR EXISTS ( \n\ + SELECT DISTINCT address FROM units AS wunits CROSS JOIN unit_authors USING(unit) CROSS JOIN my_witnesses USING(address) \n\ + WHERE wunits.${order_column} > units.${order_column} \n\ + LIMIT 0,1 \n\ + )) \n\ + /* AND NOT EXISTS (SELECT * FROM unhandled_joints) */`, + rows => { async.eachSeries( rows, - function(row, cb){ - breadcrumbs.add("--------------- archiving uncovered unit "+row.unit); - storage.readJoint(db, row.unit, { - ifNotFound: function(){ + ({unit}, cb) => { + breadcrumbs.add(`--------------- archiving uncovered unit ${unit}`); + storage.readJoint(db, unit, { + ifNotFound() { throw Error("nonserial unit not found?"); }, - ifFound: function(objJoint){ - db.takeConnectionFromPool(function(conn){ - mutex.lock(["write"], function(unlock){ - var arrQueries = []; + ifFound(objJoint) { + db.takeConnectionFromPool(conn => { + mutex.lock(["write"], unlock => { + const arrQueries = []; conn.addQuery(arrQueries, "BEGIN"); - archiving.generateQueriesToArchiveJoint(conn, objJoint, 'uncovered', arrQueries, function(){ + archiving.generateQueriesToArchiveJoint(conn, objJoint, 'uncovered', arrQueries, () => { conn.addQuery(arrQueries, "COMMIT"); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { unlock(); conn.release(); - breadcrumbs.add("------- done archiving "+row.unit); - storage.forgetUnit(row.unit); + breadcrumbs.add(`------- done archiving ${unit}`); + storage.forgetUnit(unit); cb(); }); }); @@ -242,7 +237,7 @@ function purgeUncoveredNonserialJoints(bByExistenceOfChildren, onDone){ } }); }, - function(){ + () => { if (rows.length > 0) return purgeUncoveredNonserialJoints(true, onDone); // to clean chains of bad units if (!bByExistenceOfChildren) @@ -251,7 +246,7 @@ function purgeUncoveredNonserialJoints(bByExistenceOfChildren, onDone){ db.query( "UPDATE units SET is_free=1 WHERE is_free=0 AND is_stable=0 \n\ AND (SELECT 1 FROM parenthoods WHERE parent_unit=unit LIMIT 1) IS NULL", - function(){ + () => { onDone(); } ); @@ -268,17 +263,17 @@ function readJointsSinceMci(mci, handleJoint, onDone){ WHERE (is_stable=0 AND main_chain_index>=? OR main_chain_index IS NULL OR is_free=1) AND archived_joints.unit IS NULL \n\ ORDER BY +level", [mci], - function(rows){ + rows => { async.eachSeries( rows, - function(row, cb){ - storage.readJoint(db, row.unit, { - ifNotFound: function(){ + ({unit}, cb) => { + storage.readJoint(db, unit, { + ifNotFound() { // throw Error("unit "+row.unit+" not found"); - breadcrumbs.add("unit "+row.unit+" not found"); + breadcrumbs.add(`unit ${unit} not found`); cb(); }, - ifFound: function(objJoint){ + ifFound(objJoint) { handleJoint(objJoint); cb(); } diff --git a/light.js b/light.js index 2e730cde..9b8815ca 100644 --- a/light.js +++ b/light.js @@ -1,29 +1,28 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var storage = require('./storage.js'); -var objectHash = require("./object_hash.js"); -var db = require('./db.js'); -var mutex = require('./mutex.js'); -var constants = require("./constants.js"); -var graph = require('./graph.js'); -var writer = require('./writer.js'); -var validation = require('./validation.js'); -var witnessProof = require('./witness_proof.js'); -var ValidationUtils = require("./validation_utils.js"); -var parentComposer = require('./parent_composer.js'); -var breadcrumbs = require('./breadcrumbs.js'); -var eventBus = require('./event_bus.js'); - -var MAX_HISTORY_ITEMS = 1000; +const async = require('async'); +const storage = require('./storage.js'); +const objectHash = require("./object_hash.js"); +const db = require('./db.js'); +const mutex = require('./mutex.js'); +const constants = require("./constants.js"); +const graph = require('./graph.js'); +const writer = require('./writer.js'); +const validation = require('./validation.js'); +const witnessProof = require('./witness_proof.js'); +const ValidationUtils = require("./validation_utils.js"); +const parentComposer = require('./parent_composer.js'); +const breadcrumbs = require('./breadcrumbs.js'); +const eventBus = require('./event_bus.js'); + +const MAX_HISTORY_ITEMS = 1000; // unit's MC index is earlier_mci function buildProofChain(later_mci, earlier_mci, unit, arrBalls, onDone){ if (earlier_mci === null) - throw Error("earlier_mci=null, unit="+unit); + throw Error(`earlier_mci=null, unit=${unit}`); if (later_mci === earlier_mci) return buildLastMileOfProofChain(earlier_mci, unit, arrBalls, onDone); - buildProofChainOnMc(later_mci, earlier_mci, arrBalls, function(){ + buildProofChainOnMc(later_mci, earlier_mci, arrBalls, () => { buildLastMileOfProofChain(earlier_mci, unit, arrBalls, onDone); }); } @@ -33,40 +32,40 @@ function buildProofChainOnMc(later_mci, earlier_mci, arrBalls, onDone){ function addBall(mci){ if (mci < 0) - throw Error("mci<0, later_mci="+later_mci+", earlier_mci="+earlier_mci); - db.query("SELECT unit, ball, content_hash FROM units JOIN balls USING(unit) WHERE main_chain_index=? AND is_on_main_chain=1", [mci], function(rows){ + throw Error(`mci<0, later_mci=${later_mci}, earlier_mci=${earlier_mci}`); + db.query("SELECT unit, ball, content_hash FROM units JOIN balls USING(unit) WHERE main_chain_index=? AND is_on_main_chain=1", [mci], rows => { if (rows.length !== 1) - throw Error("no prev chain element? mci="+mci+", later_mci="+later_mci+", earlier_mci="+earlier_mci); - var objBall = rows[0]; + throw Error(`no prev chain element? mci=${mci}, later_mci=${later_mci}, earlier_mci=${earlier_mci}`); + const objBall = rows[0]; if (objBall.content_hash) objBall.is_nonserial = true; delete objBall.content_hash; db.query( "SELECT ball FROM parenthoods LEFT JOIN balls ON parent_unit=balls.unit WHERE child_unit=? ORDER BY ball", [objBall.unit], - function(parent_rows){ - if (parent_rows.some(function(parent_row){ return !parent_row.ball; })) + parent_rows => { + if (parent_rows.some(({ball}) => !ball)) throw Error("some parents have no balls"); if (parent_rows.length > 0) - objBall.parent_balls = parent_rows.map(function(parent_row){ return parent_row.ball; }); + objBall.parent_balls = parent_rows.map(({ball}) => ball); db.query( "SELECT ball, main_chain_index \n\ FROM skiplist_units JOIN units ON skiplist_unit=units.unit LEFT JOIN balls ON units.unit=balls.unit \n\ WHERE skiplist_units.unit=? ORDER BY ball", [objBall.unit], - function(srows){ - if (srows.some(function(srow){ return !srow.ball; })) + srows => { + if (srows.some(({ball}) => !ball)) throw Error("some skiplist units have no balls"); if (srows.length > 0) - objBall.skiplist_balls = srows.map(function(srow){ return srow.ball; }); + objBall.skiplist_balls = srows.map(({ball}) => ball); arrBalls.push(objBall); if (mci === earlier_mci) return onDone(); if (srows.length === 0) // no skiplist return addBall(mci-1); - var next_mci = mci - 1; - for (var i=0; i= earlier_mci) next_mci = next_skiplist_mci; } @@ -88,31 +87,31 @@ function buildProofChainOnMc(later_mci, earlier_mci, arrBalls, onDone){ // unit's MC index is mci, find a path from mci unit to this unit function buildLastMileOfProofChain(mci, unit, arrBalls, onDone){ function addBall(_unit){ - db.query("SELECT unit, ball, content_hash FROM units JOIN balls USING(unit) WHERE unit=?", [_unit], function(rows){ + db.query("SELECT unit, ball, content_hash FROM units JOIN balls USING(unit) WHERE unit=?", [_unit], rows => { if (rows.length !== 1) throw Error("no unit?"); - var objBall = rows[0]; + const objBall = rows[0]; if (objBall.content_hash) objBall.is_nonserial = true; delete objBall.content_hash; db.query( "SELECT ball FROM parenthoods LEFT JOIN balls ON parent_unit=balls.unit WHERE child_unit=? ORDER BY ball", [objBall.unit], - function(parent_rows){ - if (parent_rows.some(function(parent_row){ return !parent_row.ball; })) + parent_rows => { + if (parent_rows.some(({ball}) => !ball)) throw Error("some parents have no balls"); if (parent_rows.length > 0) - objBall.parent_balls = parent_rows.map(function(parent_row){ return parent_row.ball; }); + objBall.parent_balls = parent_rows.map(({ball}) => ball); db.query( "SELECT ball \n\ FROM skiplist_units JOIN units ON skiplist_unit=units.unit LEFT JOIN balls ON units.unit=balls.unit \n\ WHERE skiplist_units.unit=? ORDER BY ball", [objBall.unit], - function(srows){ - if (srows.some(function(srow){ return !srow.ball; })) + srows => { + if (srows.some(({ball}) => !ball)) throw Error("last mile: some skiplist units have no balls"); if (srows.length > 0) - objBall.skiplist_balls = srows.map(function(srow){ return srow.ball; }); + objBall.skiplist_balls = srows.map(({ball}) => ball); arrBalls.push(objBall); if (_unit === unit) return onDone(); @@ -128,18 +127,18 @@ function buildLastMileOfProofChain(mci, unit, arrBalls, onDone){ db.query( "SELECT parent_unit FROM parenthoods JOIN units ON parent_unit=unit WHERE child_unit=? AND main_chain_index=?", [interim_unit, mci], - function(parent_rows){ - var arrParents = parent_rows.map(function(parent_row){ return parent_row.parent_unit; }); + parent_rows => { + const arrParents = parent_rows.map(({parent_unit}) => parent_unit); if (arrParents.indexOf(unit) >= 0) return addBall(unit); async.eachSeries( arrParents, - function(parent_unit, cb){ - graph.determineIfIncluded(db, unit, [parent_unit], function(bIncluded){ + (parent_unit, cb) => { + graph.determineIfIncluded(db, unit, [parent_unit], bIncluded => { bIncluded ? cb(parent_unit) : cb(); }); }, - function(parent_unit){ + parent_unit => { if (!parent_unit) throw Error("no parent that includes target unit"); addBall(parent_unit); @@ -150,10 +149,10 @@ function buildLastMileOfProofChain(mci, unit, arrBalls, onDone){ } // start from MC unit and go back in history - db.query("SELECT unit FROM units WHERE main_chain_index=? AND is_on_main_chain=1", [mci], function(rows){ + db.query("SELECT unit FROM units WHERE main_chain_index=? AND is_on_main_chain=1", [mci], rows => { if (rows.length !== 1) throw Error("no mc unit?"); - var mc_unit = rows[0].unit; + const mc_unit = rows[0].unit; if (mc_unit === unit) return onDone(); findParent(mc_unit); @@ -165,10 +164,10 @@ function buildLastMileOfProofChain(mci, unit, arrBalls, onDone){ function prepareHistory(historyRequest, callbacks){ if (!historyRequest) return callbacks.ifError("no history request"); - var arrKnownStableUnits = historyRequest.known_stable_units; - var arrWitnesses = historyRequest.witnesses; - var arrAddresses = historyRequest.addresses; - var arrRequestedJoints = historyRequest.requested_joints; + const arrKnownStableUnits = historyRequest.known_stable_units; + const arrWitnesses = historyRequest.witnesses; + const arrAddresses = historyRequest.addresses; + const arrRequestedJoints = historyRequest.requested_joints; if (!arrAddresses && !arrRequestedJoints) return callbacks.ifError("neither addresses nor joints requested"); @@ -183,43 +182,49 @@ function prepareHistory(historyRequest, callbacks){ if (!ValidationUtils.isArrayOfLength(arrWitnesses, constants.COUNT_WITNESSES)) return callbacks.ifError("wrong number of witnesses"); - var assocKnownStableUnits = {}; + const assocKnownStableUnits = {}; if (arrKnownStableUnits) - arrKnownStableUnits.forEach(function(unit){ + arrKnownStableUnits.forEach(unit => { assocKnownStableUnits[unit] = true; }); - var objResponse = {}; + const objResponse = {}; // add my joints and proofchain to these joints - var arrSelects = []; + let arrSelects = []; if (arrAddresses){ // we don't filter sequence='good' after the unit is stable, so the client will see final doublespends too - var strAddressList = arrAddresses.map(db.escape).join(', '); - arrSelects = ["SELECT DISTINCT unit, main_chain_index, level FROM outputs JOIN units USING(unit) \n\ - WHERE address IN("+strAddressList+") AND (+sequence='good' OR is_stable=1) \n\ - UNION \n\ - SELECT DISTINCT unit, main_chain_index, level FROM unit_authors JOIN units USING(unit) \n\ - WHERE address IN("+strAddressList+") AND (+sequence='good' OR is_stable=1) \n"]; + const strAddressList = arrAddresses.map(db.escape).join(', '); + arrSelects = [`SELECT DISTINCT unit, main_chain_index, level FROM outputs JOIN units USING(unit) \n\ + WHERE address IN(${strAddressList}) AND (+sequence='good' OR is_stable=1) \n\ + UNION \n\ + SELECT DISTINCT unit, main_chain_index, level FROM unit_authors JOIN units USING(unit) \n\ + WHERE address IN(${strAddressList}) AND (+sequence='good' OR is_stable=1) \n`]; } if (arrRequestedJoints){ - var strUnitList = arrRequestedJoints.map(db.escape).join(', '); - arrSelects.push("SELECT unit, main_chain_index, level FROM units WHERE unit IN("+strUnitList+") AND (+sequence='good' OR is_stable=1) \n"); + const strUnitList = arrRequestedJoints.map(db.escape).join(', '); + arrSelects.push(`SELECT unit, main_chain_index, level FROM units WHERE unit IN(${strUnitList}) AND (+sequence='good' OR is_stable=1) \n`); } - var sql = arrSelects.join("UNION \n") + "ORDER BY main_chain_index DESC, level DESC"; - db.query(sql, function(rows){ + const sql = `${arrSelects.join("UNION \n")}ORDER BY main_chain_index DESC, level DESC`; + db.query(sql, rows => { // if no matching units, don't build witness proofs - rows = rows.filter(function(row){ return !assocKnownStableUnits[row.unit]; }); + rows = rows.filter(({unit}) => !assocKnownStableUnits[unit]); if (rows.length === 0) return callbacks.ifOk(objResponse); if (rows.length > MAX_HISTORY_ITEMS) return callbacks.ifError("your history is too large, consider switching to a full client"); - mutex.lock(['prepareHistory'], function(unlock){ - var start_ts = Date.now(); + mutex.lock(['prepareHistory'], unlock => { + const start_ts = Date.now(); witnessProof.prepareWitnessProof( arrWitnesses, 0, - function(err, arrUnstableMcJoints, arrWitnessChangeAndDefinitionJoints, last_ball_unit, last_ball_mci){ + ( + err, + arrUnstableMcJoints, + arrWitnessChangeAndDefinitionJoints, + last_ball_unit, + last_ball_mci + ) => { if (err){ callbacks.ifError(err); return unlock(); @@ -231,32 +236,32 @@ function prepareHistory(historyRequest, callbacks){ // add my joints and proofchain to those joints objResponse.joints = []; objResponse.proofchain_balls = []; - var later_mci = last_ball_mci+1; // +1 so that last ball itself is included in the chain + let later_mci = last_ball_mci+1; // +1 so that last ball itself is included in the chain async.eachSeries( rows, - function(row, cb2){ - storage.readJoint(db, row.unit, { - ifNotFound: function(){ - throw Error("prepareJointsWithProofs unit not found "+row.unit); + ({unit, main_chain_index}, cb2) => { + storage.readJoint(db, unit, { + ifNotFound() { + throw Error(`prepareJointsWithProofs unit not found ${unit}`); }, - ifFound: function(objJoint){ + ifFound(objJoint) { objResponse.joints.push(objJoint); - if (row.main_chain_index > last_ball_mci || row.main_chain_index === null) // unconfirmed, no proofchain + if (main_chain_index > last_ball_mci || main_chain_index === null) // unconfirmed, no proofchain return cb2(); - buildProofChain(later_mci, row.main_chain_index, row.unit, objResponse.proofchain_balls, function(){ - later_mci = row.main_chain_index; + buildProofChain(later_mci, main_chain_index, unit, objResponse.proofchain_balls, () => { + later_mci = main_chain_index; cb2(); }); } }); }, - function(){ + () => { //if (objResponse.joints.length > 0 && objResponse.proofchain_balls.length === 0) // throw "no proofs"; if (objResponse.proofchain_balls.length === 0) delete objResponse.proofchain_balls; callbacks.ifOk(objResponse); - console.log("prepareHistory for addresses "+(arrAddresses || []).join(', ')+" and joints "+(arrRequestedJoints || []).join(', ')+" took "+(Date.now()-start_ts)+'ms'); + console.log(`prepareHistory for addresses ${(arrAddresses || []).join(', ')} and joints ${(arrRequestedJoints || []).join(', ')} took ${Date.now()-start_ts}ms`); unlock(); } ); @@ -283,30 +288,30 @@ function processHistory(objResponse, callbacks){ witnessProof.processWitnessProof( objResponse.unstable_mc_joints, objResponse.witness_change_and_definition_joints, false, - function(err, arrLastBallUnits, assocLastBallByLastBallUnit){ + (err, arrLastBallUnits, assocLastBallByLastBallUnit) => { if (err) return callbacks.ifError(err); - var assocKnownBalls = {}; - for (var unit in assocLastBallByLastBallUnit){ - var ball = assocLastBallByLastBallUnit[unit]; + let assocKnownBalls = {}; + for (const unit in assocLastBallByLastBallUnit){ + const ball = assocLastBallByLastBallUnit[unit]; assocKnownBalls[ball] = true; } // proofchain - var assocProvenUnitsNonserialness = {}; + const assocProvenUnitsNonserialness = {}; for (var i=0; i { assocKnownBalls[parent_ball] = true; }); if (objBall.skiplist_balls) - objBall.skiplist_balls.forEach(function(skiplist_ball){ + objBall.skiplist_balls.forEach(skiplist_ball => { assocKnownBalls[skiplist_ball] = true; }); assocProvenUnitsNonserialness[objBall.unit] = objBall.is_nonserial; @@ -315,8 +320,8 @@ function processHistory(objResponse, callbacks){ // joints that pay to/from me and joints that I explicitly requested for (var i=0; i { + const arrUnits = objResponse.joints.map(({unit}) => unit.unit); + breadcrumbs.add(`got light_joints for processHistory ${arrUnits.join(', ')}`); + db.query(`SELECT unit, is_stable FROM units WHERE unit IN(${arrUnits.map(db.escape).join(', ')})`, rows => { + const assocExistingUnits = {}; + rows.forEach(({unit}) => { + assocExistingUnits[unit] = true; }); - var arrProvenUnits = []; + let arrProvenUnits = []; async.eachSeries( objResponse.joints.reverse(), // have them in forward chronological order so that we correctly mark is_spent flag - function(objJoint, cb2){ - var objUnit = objJoint.unit; - var unit = objUnit.unit; + (objJoint, cb2) => { + const objUnit = objJoint.unit; + const unit = objUnit.unit; // assocProvenUnitsNonserialness[unit] is true for non-serials, false for serials, undefined for unstable - var sequence = assocProvenUnitsNonserialness[unit] ? 'final-bad' : 'good'; + const sequence = assocProvenUnitsNonserialness[unit] ? 'final-bad' : 'good'; if (unit in assocProvenUnitsNonserialness) arrProvenUnits.push(unit); if (assocExistingUnits[unit]){ @@ -356,31 +361,31 @@ function processHistory(objResponse, callbacks){ db.query( "UPDATE units SET main_chain_index=?, sequence=? WHERE unit=?", [objUnit.main_chain_index, sequence, unit], - function(){ + () => { cb2(); } ); } else - writer.saveJoint(objJoint, {sequence: sequence, arrDoubleSpendInputs: [], arrAdditionalQueries: []}, null, cb2); + writer.saveJoint(objJoint, {sequence, arrDoubleSpendInputs: [], arrAdditionalQueries: []}, null, cb2); }, - function(err){ + err => { breadcrumbs.add('processHistory almost done'); if (err){ unlock(); return callbacks.ifError(err); } - fixIsSpentFlagAndInputAddress(function(){ + fixIsSpentFlagAndInputAddress(() => { if (arrProvenUnits.length === 0){ unlock(); return callbacks.ifOk(true); } - db.query("UPDATE units SET is_stable=1, is_free=0 WHERE unit IN(?)", [arrProvenUnits], function(){ + db.query("UPDATE units SET is_stable=1, is_free=0 WHERE unit IN(?)", [arrProvenUnits], () => { unlock(); - arrProvenUnits = arrProvenUnits.filter(function(unit){ return !assocProvenUnitsNonserialness[unit]; }); + arrProvenUnits = arrProvenUnits.filter(unit => !assocProvenUnitsNonserialness[unit]); if (arrProvenUnits.length === 0) return callbacks.ifOk(true); - emitStability(arrProvenUnits, function(bEmitted){ + emitStability(arrProvenUnits, bEmitted => { callbacks.ifOk(!bEmitted); }); }); @@ -402,12 +407,12 @@ function fixIsSpentFlag(onDone){ FROM outputs \n\ JOIN inputs ON outputs.unit=inputs.src_unit AND outputs.message_index=inputs.src_message_index AND outputs.output_index=inputs.src_output_index \n\ WHERE is_spent=0 AND type='transfer'", - function(rows){ - console.log(rows.length+" previous outputs appear to be spent"); + rows => { + console.log(`${rows.length} previous outputs appear to be spent`); if (rows.length === 0) return onDone(); - var arrQueries = []; - rows.forEach(function(row){ + const arrQueries = []; + rows.forEach(row => { console.log('fixing is_spent for output', row); db.addQuery(arrQueries, "UPDATE outputs SET is_spent=1 WHERE unit=? AND message_index=? AND output_index=?", [row.unit, row.message_index, row.output_index]); @@ -423,12 +428,12 @@ function fixInputAddress(onDone){ FROM outputs \n\ JOIN inputs ON outputs.unit=inputs.src_unit AND outputs.message_index=inputs.src_message_index AND outputs.output_index=inputs.src_output_index \n\ WHERE inputs.address IS NULL AND type='transfer'", - function(rows){ - console.log(rows.length+" previous inputs appear to be without address"); + rows => { + console.log(`${rows.length} previous inputs appear to be without address`); if (rows.length === 0) return onDone(); - var arrQueries = []; - rows.forEach(function(row){ + const arrQueries = []; + rows.forEach(row => { console.log('fixing input address for output', row); db.addQuery(arrQueries, "UPDATE inputs SET address=? WHERE src_unit=? AND src_message_index=? AND src_output_index=?", @@ -440,7 +445,7 @@ function fixInputAddress(onDone){ } function fixIsSpentFlagAndInputAddress(onDone){ - fixIsSpentFlag(function(){ + fixIsSpentFlag(() => { fixInputAddress(onDone); }); } @@ -456,28 +461,28 @@ function determineIfHaveUnstableJoints(arrAddresses, handleResult){ WHERE address IN(?) AND +sequence='good' AND is_stable=0 \n\ LIMIT 1", [arrAddresses, arrAddresses], - function(rows){ - handleResult(rows.length > 0); + ({length}) => { + handleResult(length > 0); } ); } function emitStability(arrProvenUnits, onDone){ - var strUnitList = arrProvenUnits.map(db.escape).join(', '); + const strUnitList = arrProvenUnits.map(db.escape).join(', '); db.query( - "SELECT unit FROM unit_authors JOIN my_addresses USING(address) WHERE unit IN("+strUnitList+") \n\ - UNION \n\ - SELECT unit FROM outputs JOIN my_addresses USING(address) WHERE unit IN("+strUnitList+") \n\ - UNION \n\ - SELECT unit FROM unit_authors JOIN shared_addresses ON address=shared_address WHERE unit IN("+strUnitList+") \n\ - UNION \n\ - SELECT unit FROM outputs JOIN shared_addresses ON address=shared_address WHERE unit IN("+strUnitList+")", - function(rows){ + `SELECT unit FROM unit_authors JOIN my_addresses USING(address) WHERE unit IN(${strUnitList}) \n\ + UNION \n\ + SELECT unit FROM outputs JOIN my_addresses USING(address) WHERE unit IN(${strUnitList}) \n\ + UNION \n\ + SELECT unit FROM unit_authors JOIN shared_addresses ON address=shared_address WHERE unit IN(${strUnitList}) \n\ + UNION \n\ + SELECT unit FROM outputs JOIN shared_addresses ON address=shared_address WHERE unit IN(${strUnitList})`, + rows => { onDone(rows.length > 0); if (rows.length > 0){ - eventBus.emit('my_transactions_became_stable', rows.map(function(row){ return row.unit; })); - rows.forEach(function(row){ - eventBus.emit('my_stable-'+row.unit); + eventBus.emit('my_transactions_became_stable', rows.map(({unit}) => unit)); + rows.forEach(({unit}) => { + eventBus.emit(`my_stable-${unit}`); }); } } @@ -488,22 +493,28 @@ function emitStability(arrProvenUnits, onDone){ function prepareParentsAndLastBallAndWitnessListUnit(arrWitnesses, callbacks){ if (!ValidationUtils.isArrayOfLength(arrWitnesses, constants.COUNT_WITNESSES)) return callbacks.ifError("wrong number of witnesses"); - storage.determineIfWitnessAddressDefinitionsHaveReferences(db, arrWitnesses, function(bWithReferences){ + storage.determineIfWitnessAddressDefinitionsHaveReferences(db, arrWitnesses, bWithReferences => { if (bWithReferences) return callbacks.ifError("some witnesses have references in their addresses"); parentComposer.pickParentUnitsAndLastBall( db, arrWitnesses, - function(err, arrParentUnits, last_stable_mc_ball, last_stable_mc_ball_unit, last_stable_mc_ball_mci){ + ( + err, + arrParentUnits, + last_stable_mc_ball, + last_stable_mc_ball_unit, + last_stable_mc_ball_mci + ) => { if (err) - return callbacks.ifError("unable to find parents: "+err); - var objResponse = { + return callbacks.ifError(`unable to find parents: ${err}`); + const objResponse = { parent_units: arrParentUnits, - last_stable_mc_ball: last_stable_mc_ball, - last_stable_mc_ball_unit: last_stable_mc_ball_unit, - last_stable_mc_ball_mci: last_stable_mc_ball_mci + last_stable_mc_ball, + last_stable_mc_ball_unit, + last_stable_mc_ball_mci }; - storage.findWitnessListUnit(db, arrWitnesses, last_stable_mc_ball_mci, function(witness_list_unit){ + storage.findWitnessListUnit(db, arrWitnesses, last_stable_mc_ball_mci, witness_list_unit => { if (witness_list_unit) objResponse.witness_list_unit = witness_list_unit; callbacks.ifOk(objResponse); @@ -519,18 +530,18 @@ function prepareLinkProofs(arrUnits, callbacks){ return callbacks.ifError("no units array"); if (arrUnits.length === 1) return callbacks.ifError("chain of one element"); - mutex.lock(['prepareLinkProofs'], function(unlock){ - var start_ts = Date.now(); - var arrChain = []; + mutex.lock(['prepareLinkProofs'], unlock => { + const start_ts = Date.now(); + const arrChain = []; async.forEachOfSeries( arrUnits, - function(unit, i, cb){ + (unit, i, cb) => { if (i === 0) return cb(); createLinkProof(arrUnits[i-1], arrUnits[i], arrChain, cb); }, - function(err){ - console.log("prepareLinkProofs for units "+arrUnits.join(', ')+" took "+(Date.now()-start_ts)+'ms, err='+err); + err => { + console.log(`prepareLinkProofs for units ${arrUnits.join(', ')} took ${Date.now()-start_ts}ms, err=${err}`); err ? callbacks.ifError(err) : callbacks.ifOk(arrChain); unlock(); } @@ -542,34 +553,34 @@ function prepareLinkProofs(arrUnits, callbacks){ // earlier unit is not included in the chain function createLinkProof(later_unit, earlier_unit, arrChain, cb){ storage.readJoint(db, later_unit, { - ifNotFound: function(){ + ifNotFound() { cb("later unit not found"); }, - ifFound: function(objLaterJoint){ - var later_mci = objLaterJoint.unit.main_chain_index; + ifFound(objLaterJoint) { + const later_mci = objLaterJoint.unit.main_chain_index; arrChain.push(objLaterJoint); - storage.readUnitProps(db, objLaterJoint.unit.last_ball_unit, function(objLaterLastBallUnitProps){ - var later_lb_mci = objLaterLastBallUnitProps.main_chain_index; + storage.readUnitProps(db, objLaterJoint.unit.last_ball_unit, ({main_chain_index}) => { + const later_lb_mci = main_chain_index; storage.readJoint(db, earlier_unit, { - ifNotFound: function(){ + ifNotFound() { cb("earlier unit not found"); }, - ifFound: function(objEarlierJoint){ - var earlier_mci = objEarlierJoint.unit.main_chain_index; - var earlier_unit = objEarlierJoint.unit.unit; + ifFound(objEarlierJoint) { + const earlier_mci = objEarlierJoint.unit.main_chain_index; + const earlier_unit = objEarlierJoint.unit.unit; if (later_mci < earlier_mci) return cb("not included"); if (later_lb_mci >= earlier_mci){ // was spent when confirmed // includes the ball of earlier unit - buildProofChain(later_lb_mci + 1, earlier_mci, earlier_unit, arrChain, function(){ + buildProofChain(later_lb_mci + 1, earlier_mci, earlier_unit, arrChain, () => { cb(); }); } else{ // the output was unconfirmed when spent - graph.determineIfIncluded(db, earlier_unit, [later_unit], function(bIncluded){ + graph.determineIfIncluded(db, earlier_unit, [later_unit], bIncluded => { if (!bIncluded) return cb("not included"); - buildPath(objLaterJoint, objEarlierJoint, arrChain, function(){ + buildPath(objLaterJoint, objEarlierJoint, arrChain, () => { cb(); }); }); @@ -588,10 +599,10 @@ function buildPath(objLaterJoint, objEarlierJoint, arrChain, onDone){ function addJoint(unit, onAdded){ storage.readJoint(db, unit, { - ifNotFound: function(){ + ifNotFound() { throw Error("unit not found?"); }, - ifFound: function(objJoint){ + ifFound(objJoint) { arrChain.push(objJoint); onAdded(objJoint); } @@ -603,27 +614,27 @@ function buildPath(objLaterJoint, objEarlierJoint, arrChain, onDone){ "SELECT parent.unit, parent.main_chain_index FROM units AS child JOIN units AS parent ON child.best_parent_unit=parent.unit \n\ WHERE child.unit=?", [objChildJoint.unit.unit], - function(rows){ + rows => { if (rows.length !== 1) throw Error("goUp not 1 parent"); if (rows[0].main_chain_index < objEarlierJoint.unit.main_chain_index) // jumped over the target return buildPathToEarlierUnit(objChildJoint); - addJoint(rows[0].unit, function(objJoint){ + addJoint(rows[0].unit, objJoint => { (objJoint.unit.main_chain_index === objEarlierJoint.unit.main_chain_index) ? buildPathToEarlierUnit(objJoint) : goUp(objJoint); }); } ); } - function buildPathToEarlierUnit(objJoint){ + function buildPathToEarlierUnit({unit}) { db.query( "SELECT unit FROM parenthoods JOIN units ON parent_unit=unit \n\ WHERE child_unit=? AND main_chain_index=?", - [objJoint.unit.unit, objJoint.unit.main_chain_index], - function(rows){ + [unit.unit, unit.main_chain_index], + rows => { if (rows.length === 0) throw Error("no parents with same mci?"); - var arrParentUnits = rows.map(function(row){ return row.unit }); + const arrParentUnits = rows.map(({unit}) => unit); if (arrParentUnits.indexOf(objEarlierJoint.unit.unit) >= 0) return onDone(); if (arrParentUnits.length === 1) @@ -631,14 +642,14 @@ function buildPath(objLaterJoint, objEarlierJoint, arrChain, onDone){ // find any parent that includes earlier unit async.eachSeries( arrParentUnits, - function(unit, cb){ - graph.determineIfIncluded(db, objEarlierJoint.unit.unit, [unit], function(bIncluded){ + (unit, cb) => { + graph.determineIfIncluded(db, objEarlierJoint.unit.unit, [unit], bIncluded => { if (!bIncluded) return cb(); // try next cb(unit); // abort the eachSeries }); }, - function(unit){ + unit => { if (!unit) throw Error("none of the parents includes earlier unit"); addJoint(unit, buildPathToEarlierUnit); @@ -655,39 +666,39 @@ function buildPath(objLaterJoint, objEarlierJoint, arrChain, onDone){ function processLinkProofs(arrUnits, arrChain, callbacks){ // check first element - var objFirstJoint = arrChain[0]; + const objFirstJoint = arrChain[0]; if (!objFirstJoint || !objFirstJoint.unit || objFirstJoint.unit.unit !== arrUnits[0]) return callbacks.ifError("unexpected 1st element"); - var assocKnownUnits = {}; - var assocKnownBalls = {}; + const assocKnownUnits = {}; + const assocKnownBalls = {}; assocKnownUnits[arrUnits[0]] = true; for (var i=0; i { assocKnownUnits[parent_unit] = true; }); } else if (objElement.unit && objElement.ball){ - var objBall = objElement; + const objBall = objElement; if (!assocKnownBalls[objBall.ball]) - return callbacks.ifError("unknown ball "+objBall.ball); + return callbacks.ifError(`unknown ball ${objBall.ball}`); if (objBall.ball !== objectHash.getBallHash(objBall.unit, objBall.parent_balls, objBall.skiplist_balls, objBall.is_nonserial)) return callbacks.ifError("invalid ball hash"); - objBall.parent_balls.forEach(function(parent_ball){ + objBall.parent_balls.forEach(parent_ball => { assocKnownBalls[parent_ball] = true; }); if (objBall.skiplist_balls) - objBall.skiplist_balls.forEach(function(skiplist_ball){ + objBall.skiplist_balls.forEach(skiplist_ball => { assocKnownBalls[skiplist_ball] = true; }); assocKnownUnits[objBall.unit] = true; @@ -698,7 +709,7 @@ function processLinkProofs(arrUnits, arrChain, callbacks){ // so, the chain is valid, now check that we can find the requested units in the chain for (var i=1; i { if (err) - return console.log("reconnectToLightVendor: "+err); - if (ws.bLightVendor) + return console.log(`reconnectToLightVendor: ${err}`); + if (bLightVendor) return console.log("already connected to light vendor"); - if (ws.bRefreshingHistory) + if (bRefreshingHistory) return console.log("already refreshing history"); refreshLightClientHistory(); }); } function readListOfUnstableUnits(handleUnits){ - db.query("SELECT unit FROM units WHERE is_stable=0", function(rows){ - var arrUnits = rows.map(function(row){ return row.unit; }); + db.query("SELECT unit FROM units WHERE is_stable=0", rows => { + const arrUnits = rows.map(({unit}) => unit); handleUnits(arrUnits); }); } function prepareRequestForHistory(handleResult){ - myWitnesses.readMyWitnesses(function(arrWitnesses){ + myWitnesses.readMyWitnesses(arrWitnesses => { if (arrWitnesses.length === 0) // first start, witnesses not set yet return handleResult(null); - var objHistoryRequest = {witnesses: arrWitnesses}; - walletGeneral.readMyAddresses(function(arrAddresses){ + const objHistoryRequest = {witnesses: arrWitnesses}; + walletGeneral.readMyAddresses(arrAddresses => { if (arrAddresses.length > 0) objHistoryRequest.addresses = arrAddresses; - readListOfUnstableUnits(function(arrUnits){ + readListOfUnstableUnits(arrUnits => { if (arrUnits.length > 0) objHistoryRequest.requested_joints = arrUnits; if (!objHistoryRequest.addresses && !objHistoryRequest.requested_joints) @@ -58,14 +57,14 @@ function prepareRequestForHistory(handleResult){ if (!objHistoryRequest.addresses) return handleResult(objHistoryRequest); objHistoryRequest.last_stable_mci = 0; - var strAddressList = arrAddresses.map(db.escape).join(', '); + const strAddressList = arrAddresses.map(db.escape).join(', '); db.query( - "SELECT unit FROM unit_authors JOIN units USING(unit) WHERE is_stable=1 AND address IN("+strAddressList+") \n\ - UNION \n\ - SELECT unit FROM outputs JOIN units USING(unit) WHERE is_stable=1 AND address IN("+strAddressList+")", - function(rows){ + `SELECT unit FROM unit_authors JOIN units USING(unit) WHERE is_stable=1 AND address IN(${strAddressList}) \n\ + UNION \n\ + SELECT unit FROM outputs JOIN units USING(unit) WHERE is_stable=1 AND address IN(${strAddressList})`, + rows => { if (rows.length) - objHistoryRequest.known_stable_units = rows.map(function(row){ return row.unit; }); + objHistoryRequest.known_stable_units = rows.map(({unit}) => unit); handleResult(objHistoryRequest); } ); @@ -82,7 +81,7 @@ function prepareRequestForHistory(handleResult){ }, 'wait'); } -var bFirstRefreshStarted = false; +let bFirstRefreshStarted = false; function refreshLightClientHistory(){ if (!conf.bLight) @@ -95,7 +94,7 @@ function refreshLightClientHistory(){ bFirstRefreshStarted = true; } network.findOutboundPeerOrConnect(network.light_vendor_url, function onLocatedLightVendor(err, ws){ - var finish = function(msg){ + const finish = msg => { if (msg) console.log(msg); if (ws) @@ -103,32 +102,32 @@ function refreshLightClientHistory(){ eventBus.emit('refresh_light_done'); }; if (err) - return finish("refreshLightClientHistory: "+err); + return finish(`refreshLightClientHistory: ${err}`); console.log('refreshLightClientHistory connected'); // handling the response may take some time, don't send new requests if (ws.bRefreshingHistory) return console.log("previous refresh not finished yet"); ws.bRefreshingHistory = true; - prepareRequestForHistory(function(objRequest){ + prepareRequestForHistory(objRequest => { if (!objRequest) return finish(); - network.sendRequest(ws, 'light/get_history', objRequest, false, function(ws, request, response){ + network.sendRequest(ws, 'light/get_history', objRequest, false, (ws, request, response) => { if (response.error){ if (response.error.indexOf('your history is too large') >= 0) throw Error(response.error); return finish(response.error); } ws.bLightVendor = true; - var interval = setInterval(function(){ // refresh UI periodically while we are processing history + const interval = setInterval(() => { // refresh UI periodically while we are processing history eventBus.emit('maybe_new_transactions'); }, 500); light.processHistory(response, { - ifError: function(err){ + ifError(err) { clearInterval(interval); network.sendError(ws, err); finish(); }, - ifOk: function(bRefreshUI){ + ifOk(bRefreshUI) { clearInterval(interval); finish(); if (bRefreshUI) @@ -141,15 +140,15 @@ function refreshLightClientHistory(){ } function archiveDoublespendUnits(){ - db.query("SELECT unit FROM units WHERE is_stable=0 AND is_free=1 AND creation_date<"+db.addTime('-1 DAY'), function(rows){ - var arrUnits = rows.map(function(row){ return row.unit; }); - breadcrumbs.add("units still unstable after 1 day: "+arrUnits.join(', ')); - arrUnits.forEach(function(unit){ - network.requestFromLightVendor('get_joint', unit, function(ws, request, response){ - if (response.error) - return breadcrumbs.add("get_joint "+unit+": "+response.error); - if (response.joint_not_found === unit){ - breadcrumbs.add("light vendor doesn't know about unit "+unit+" any more, will archive"); + db.query(`SELECT unit FROM units WHERE is_stable=0 AND is_free=1 AND creation_date<${db.addTime('-1 DAY')}`, rows => { + const arrUnits = rows.map(({unit}) => unit); + breadcrumbs.add(`units still unstable after 1 day: ${arrUnits.join(', ')}`); + arrUnits.forEach(unit => { + network.requestFromLightVendor('get_joint', unit, (ws, request, {error, joint_not_found}) => { + if (error) + return breadcrumbs.add(`get_joint ${unit}: ${error}`); + if (joint_not_found === unit){ + breadcrumbs.add(`light vendor doesn't know about unit ${unit} any more, will archive`); storage.archiveJointAndDescendantsIfExists(unit); } }); diff --git a/mail.js b/mail.js index be5344d1..cd350ca8 100644 --- a/mail.js +++ b/mail.js @@ -1,7 +1,6 @@ -'use strict'; -var child_process = require('child_process'); -var conf = require('./conf.js'); -var DNS; +const child_process = require('child_process'); +const conf = require('./conf.js'); +let DNS; if (conf.smtpTransport === 'direct' && !(typeof window !== 'undefined' && window && window.cordova)) { DNS = require('dns'); @@ -10,11 +9,11 @@ if (conf.smtpTransport === 'direct' && !(typeof window !== 'undefined' && window if (conf.smtpTransport === 'relay' && !conf.smtpRelay) throw Error("please set smtpRelay in conf"); - + function sendmail(params, cb){ if (!cb) - cb = function(){}; + cb = () => {}; switch (conf.smtpTransport){ case 'relay': return sendMailThroughRelay(params, cb); @@ -26,24 +25,24 @@ function sendmail(params, cb){ } } -function sendMailThroughUnixSendmail(params, cb){ - var child = child_process.spawn('/usr/sbin/sendmail', ['-t', params.to]); +function sendMailThroughUnixSendmail({to, from, subject, body}, cb) { + const child = child_process.spawn('/usr/sbin/sendmail', ['-t', to]); child.stdout.pipe(process.stdout); child.stderr.pipe(process.stderr); - child.stdin.write("Return-Path: <"+params.from+">\r\nTo: "+params.to+"\r\nFrom: "+params.from+"\r\nSubject: "+params.subject+"\r\n\r\n"+params.body); + child.stdin.write(`Return-Path: <${from}>\r\nTo: ${to}\r\nFrom: ${from}\r\nSubject: ${subject}\r\n\r\n${body}`); child.stdin.end(); cb(); } function sendMailDirectly(params, cb) { - var nodemailer = require('node4mailer'+''); - var hostname = params.to.slice(params.to.indexOf("@")+1); - DNS.resolveMx(hostname, function(err, exchanges){ - var exchange = hostname; + const nodemailer = require('node4mailer'+''); + const hostname = params.to.slice(params.to.indexOf("@")+1); + DNS.resolveMx(hostname, (err, exchanges) => { + let exchange = hostname; if (exchanges && exchanges.length) exchange = exchanges[0].exchange; - var transporter = nodemailer.createTransport({ + const transporter = nodemailer.createTransport({ host: exchange, // port: 25, secure: false, @@ -53,7 +52,7 @@ function sendMailDirectly(params, cb) { } }); - var mailOptions = { + const mailOptions = { from: params.from, to: params.to, subject: params.subject, @@ -61,9 +60,9 @@ function sendMailDirectly(params, cb) { html: params.htmlBody // html body }; - transporter.sendMail(mailOptions, function(error, info) { + transporter.sendMail(mailOptions, (error, info) => { if (error) { - console.error("failed to send mail to "+params.to+": "+error); + console.error(`failed to send mail to ${params.to}: ${error}`); return cb(error); } console.log('Message sent: %s', info.messageId); @@ -73,8 +72,8 @@ function sendMailDirectly(params, cb) { } function sendMailThroughRelay(params, cb){ - var nodemailer = require('node4mailer'+''); - var transportOpts = { + const nodemailer = require('node4mailer'+''); + const transportOpts = { host: conf.smtpRelay, // port: 25, secure: false, @@ -88,8 +87,8 @@ function sendMailThroughRelay(params, cb){ user: conf.smtpUser, pass: conf.smtpPassword } - var transporter = nodemailer.createTransport(transportOpts); - var mailOptions = { + const transporter = nodemailer.createTransport(transportOpts); + const mailOptions = { from: params.from, to: params.to, subject: params.subject, @@ -97,9 +96,9 @@ function sendMailThroughRelay(params, cb){ html: params.htmlBody // html body }; - transporter.sendMail(mailOptions, function(error, info) { + transporter.sendMail(mailOptions, (error, info) => { if (error) { - console.error("failed to send mail to "+params.to+": "+error+"\n", error); + console.error(`failed to send mail to ${params.to}: ${error}\n`, error); return cb(error); } console.log('Message sent: %s', info.messageId); @@ -111,8 +110,8 @@ function sendBugEmail(error_message, exception){ sendmail({ to: conf.bug_sink_email, from: conf.bugs_from_email, - subject: 'BUG '+error_message.substr(0, 200).replace(/\s/g, ' '), - body: error_message + "\n\n" + ((typeof exception === 'string') ? exception : JSON.stringify(exception, null, '\t')) + subject: `BUG ${error_message.substr(0, 200).replace(/\s/g, ' ')}`, + body: `${error_message}\n\n${(typeof exception === 'string') ? exception : JSON.stringify(exception, null, '\t')}` }); } diff --git a/main_chain.js b/main_chain.js index d5733c8b..cd314d9e 100644 --- a/main_chain.js +++ b/main_chain.js @@ -1,21 +1,20 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var async = require('async'); -var db = require('./db.js'); -var constants = require("./constants.js"); -var storage = require('./storage.js'); -var graph = require('./graph.js'); -var objectHash = require("./object_hash.js"); -var paid_witnessing = require("./paid_witnessing.js"); -var headers_commission = require("./headers_commission.js"); -var mutex = require('./mutex.js'); -var eventBus = require('./event_bus.js'); -var profiler = require('./profiler.js'); -var breadcrumbs = require('./breadcrumbs.js'); +const _ = require('lodash'); +const async = require('async'); +const db = require('./db.js'); +const constants = require("./constants.js"); +const storage = require('./storage.js'); +const graph = require('./graph.js'); +const objectHash = require("./object_hash.js"); +const paid_witnessing = require("./paid_witnessing.js"); +const headers_commission = require("./headers_commission.js"); +const mutex = require('./mutex.js'); +const eventBus = require('./event_bus.js'); +const profiler = require('./profiler.js'); +const breadcrumbs = require('./breadcrumbs.js'); // override when adding units which caused witnessed level to significantly retreat -var arrRetreatingUnits = [ +const arrRetreatingUnits = [ '+5ntioHT58jcFb8oVc+Ff4UvO5UvYGRcrGfYIofGUW8=', 'C/aPdM0sODPLC3NqJPWdZlqmV8B4xxf2N/+HSEi0sKU=', 'sSev6hvQU86SZBemy9CW2lJIko2jZDoY55Lm3zf2QU4=', @@ -27,16 +26,16 @@ var arrRetreatingUnits = [ function updateMainChain(conn, from_unit, last_added_unit, onDone){ - var arrAllParents = []; - var arrNewMcUnits = []; + let arrAllParents = []; + const arrNewMcUnits = []; // if unit === null, read free balls function findNextUpMainChainUnit(unit, handleUnit){ - function handleProps(props){ - if (props.best_parent_unit === null) + function handleProps({best_parent_unit, witnessed_level}) { + if (best_parent_unit === null) throw Error("best parent is null"); - console.log("unit "+unit+", best parent "+props.best_parent_unit+", wlevel "+props.witnessed_level); - handleUnit(props.best_parent_unit); + console.log(`unit ${unit}, best parent ${best_parent_unit}, wlevel ${witnessed_level}`); + handleUnit(best_parent_unit); } function readLastUnitProps(handleLastUnitProps){ conn.query("SELECT unit AS best_parent_unit, witnessed_level \n\ @@ -45,14 +44,14 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ level-witnessed_level ASC, \n\ unit ASC \n\ LIMIT 5", - function(rows){ + rows => { if (rows.length === 0) throw Error("no free units?"); if (rows.length > 1){ - var arrParents = rows.map(function(row){ return row.best_parent_unit; }); + const arrParents = rows.map(({best_parent_unit}) => best_parent_unit); arrAllParents = arrParents; - for (var i=0; i= 0) return handleLastUnitProps(rows[n]); } @@ -75,17 +74,17 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ return checkNotRebuildingStableMainChainAndGoDown(0, unit); profiler.start(); - findNextUpMainChainUnit(unit, function(best_parent_unit){ - storage.readUnitProps(conn, best_parent_unit, function(objBestParentUnitProps){ - var objBestParentUnitProps2 = storage.assocUnstableUnits[best_parent_unit]; + findNextUpMainChainUnit(unit, best_parent_unit => { + storage.readUnitProps(conn, best_parent_unit, objBestParentUnitProps => { + const objBestParentUnitProps2 = storage.assocUnstableUnits[best_parent_unit]; if (!objBestParentUnitProps2) - throw Error("unstable unit not found: "+best_parent_unit); - var objBestParentUnitPropsForCheck = _.cloneDeep(objBestParentUnitProps2); + throw Error(`unstable unit not found: ${best_parent_unit}`); + const objBestParentUnitPropsForCheck = _.cloneDeep(objBestParentUnitProps2); delete objBestParentUnitPropsForCheck.parent_units; if (!_.isEqual(objBestParentUnitPropsForCheck, objBestParentUnitProps)) - throwError("different props, db: "+JSON.stringify(objBestParentUnitProps)+", unstable: "+JSON.stringify(objBestParentUnitProps2)); + throwError(`different props, db: ${JSON.stringify(objBestParentUnitProps)}, unstable: ${JSON.stringify(objBestParentUnitProps2)}`); if (!objBestParentUnitProps.is_on_main_chain) - conn.query("UPDATE units SET is_on_main_chain=1, main_chain_index=NULL WHERE unit=?", [best_parent_unit], function(){ + conn.query("UPDATE units SET is_on_main_chain=1, main_chain_index=NULL WHERE unit=?", [best_parent_unit], () => { objBestParentUnitProps2.is_on_main_chain = 1; objBestParentUnitProps2.main_chain_index = null; arrNewMcUnits.push(best_parent_unit); @@ -104,15 +103,15 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ } function checkNotRebuildingStableMainChainAndGoDown(last_main_chain_index, last_main_chain_unit){ - console.log("checkNotRebuildingStableMainChainAndGoDown "+from_unit); + console.log(`checkNotRebuildingStableMainChainAndGoDown ${from_unit}`); profiler.start(); conn.query( "SELECT unit FROM units WHERE is_on_main_chain=1 AND main_chain_index>? AND is_stable=1", [last_main_chain_index], - function(rows){ + rows => { profiler.stop('mc-checkNotRebuilding'); if (rows.length > 0) - throw Error("removing stable units "+rows.map(function(row){return row.unit}).join(', ')+" from MC after adding "+last_added_unit+" with all parents "+arrAllParents.join(', ')); + throw Error(`removing stable units ${rows.map(({unit}) => unit).join(', ')} from MC after adding ${last_added_unit} with all parents ${arrAllParents.join(', ')}`); goDownAndUpdateMainChainIndex(last_main_chain_index, last_main_chain_unit); } ); @@ -124,36 +123,36 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ //"UPDATE units SET is_on_main_chain=0, main_chain_index=NULL WHERE is_on_main_chain=1 AND main_chain_index>?", "UPDATE units SET is_on_main_chain=0, main_chain_index=NULL WHERE main_chain_index>?", [last_main_chain_index], - function(){ - for (var unit in storage.assocUnstableUnits){ - var o = storage.assocUnstableUnits[unit]; + () => { + for (const unit in storage.assocUnstableUnits){ + const o = storage.assocUnstableUnits[unit]; if (o.main_chain_index > last_main_chain_index){ o.is_on_main_chain = 0; o.main_chain_index = null; } } - var main_chain_index = last_main_chain_index; - var main_chain_unit = last_main_chain_unit; + let main_chain_index = last_main_chain_index; + const main_chain_unit = last_main_chain_unit; conn.query( "SELECT unit FROM units WHERE is_on_main_chain=1 AND main_chain_index IS NULL ORDER BY level", - function(rows){ + rows => { if (rows.length === 0){ //if (last_main_chain_index > 0) - throw Error("no unindexed MC units after adding "+last_added_unit); + throw Error(`no unindexed MC units after adding ${last_added_unit}`); //else{ // console.log("last MC=0, no unindexed MC units"); // return updateLatestIncludedMcIndex(last_main_chain_index, true); //} } - var arrDbNewMcUnits = rows.map(function(row){ return row.unit; }); + const arrDbNewMcUnits = rows.map(({unit}) => unit); arrNewMcUnits.reverse(); if (!_.isEqual(arrNewMcUnits, arrDbNewMcUnits)) - throwError("different new MC units, arr: "+JSON.stringify(arrNewMcUnits)+", db: "+JSON.stringify(arrDbNewMcUnits)); + throwError(`different new MC units, arr: ${JSON.stringify(arrNewMcUnits)}, db: ${JSON.stringify(arrDbNewMcUnits)}`); async.eachSeries( rows, - function(row, cb){ + ({unit}, cb) => { main_chain_index++; - var arrUnits = [row.unit]; + let arrUnits = [unit]; function goUp(arrStartUnits){ conn.query( @@ -161,17 +160,17 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ FROM parenthoods JOIN units ON parent_unit=unit \n\ WHERE child_unit IN(?) AND main_chain_index IS NULL", [arrStartUnits], - function(rows){ - var arrNewStartUnits = rows.map(function(row){ return row.unit; }); - var arrNewStartUnits2 = []; - arrStartUnits.forEach(function(start_unit){ - storage.assocUnstableUnits[start_unit].parent_units.forEach(function(parent_unit){ + rows => { + const arrNewStartUnits = rows.map(({unit}) => unit); + const arrNewStartUnits2 = []; + arrStartUnits.forEach(start_unit => { + storage.assocUnstableUnits[start_unit].parent_units.forEach(parent_unit => { if (storage.assocUnstableUnits[parent_unit] && storage.assocUnstableUnits[parent_unit].main_chain_index === null && arrNewStartUnits2.indexOf(parent_unit) === -1) arrNewStartUnits2.push(parent_unit); }); }); if (!_.isEqual(arrNewStartUnits.sort(), arrNewStartUnits2.sort())) - throwError("different new start units, arr: "+JSON.stringify(arrNewStartUnits2)+", db: "+JSON.stringify(arrNewStartUnits)); + throwError(`different new start units, arr: ${JSON.stringify(arrNewStartUnits2)}, db: ${JSON.stringify(arrNewStartUnits)}`); if (arrNewStartUnits.length === 0) return updateMc(); arrUnits = arrUnits.concat(arrNewStartUnits); @@ -181,12 +180,12 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ } function updateMc(){ - arrUnits.forEach(function(unit){ + arrUnits.forEach(unit => { storage.assocUnstableUnits[unit].main_chain_index = main_chain_index; }); - var strUnitList = arrUnits.map(db.escape).join(', '); - conn.query("UPDATE units SET main_chain_index=? WHERE unit IN("+strUnitList+")", [main_chain_index], function(){ - conn.query("UPDATE unit_authors SET _mci=? WHERE unit IN("+strUnitList+")", [main_chain_index], function(){ + const strUnitList = arrUnits.map(db.escape).join(', '); + conn.query(`UPDATE units SET main_chain_index=? WHERE unit IN(${strUnitList})`, [main_chain_index], () => { + conn.query(`UPDATE unit_authors SET _mci=? WHERE unit IN(${strUnitList})`, [main_chain_index], () => { cb(); }); }); @@ -195,13 +194,13 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ goUp(arrUnits); }, - function(err){ + err => { console.log("goDownAndUpdateMainChainIndex done"); if (err) throw Error("goDownAndUpdateMainChainIndex eachSeries failed"); conn.query( "UPDATE unit_authors SET _mci=NULL WHERE unit IN(SELECT unit FROM units WHERE main_chain_index IS NULL)", - function(){ + () => { profiler.stop('mc-goDown'); updateLatestIncludedMcIndex(last_main_chain_index, true); } @@ -219,17 +218,17 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ function checkAllLatestIncludedMcIndexesAreSet(){ profiler.start(); if (!_.isEqual(assocDbLimcisByUnit, assocLimcisByUnit)) - throwError("different LIMCIs, mem: "+JSON.stringify(assocLimcisByUnit)+", db: "+JSON.stringify(assocDbLimcisByUnit)); - conn.query("SELECT unit FROM units WHERE latest_included_mc_index IS NULL AND level!=0", function(rows){ + throwError(`different LIMCIs, mem: ${JSON.stringify(assocLimcisByUnit)}, db: ${JSON.stringify(assocDbLimcisByUnit)}`); + conn.query("SELECT unit FROM units WHERE latest_included_mc_index IS NULL AND level!=0", rows => { if (rows.length > 0) - throw Error(rows.length+" units have latest_included_mc_index=NULL, e.g. unit "+rows[0].unit); + throw Error(`${rows.length} units have latest_included_mc_index=NULL, e.g. unit ${rows[0].unit}`); profiler.stop('mc-limci-check'); updateStableMcFlag(); }); } function propagateLIMCI(){ - console.log("propagateLIMCI "+last_main_chain_index); + console.log(`propagateLIMCI ${last_main_chain_index}`); profiler.start(); // the 1st condition in WHERE is the same that was used 2 queries ago to NULL limcis conn.query( @@ -252,18 +251,18 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ WHERE (chunits.main_chain_index > ? OR chunits.main_chain_index IS NULL) \n\ AND (chunits.latest_included_mc_index IS NULL OR chunits.latest_included_mc_index < punits.latest_included_mc_index)", [last_main_chain_index], - function(rows){ + rows => { profiler.stop('mc-limci-select-propagate'); if (rows.length === 0) return checkAllLatestIncludedMcIndexesAreSet(); profiler.start(); async.eachSeries( rows, - function(row, cb){ - assocDbLimcisByUnit[row.unit] = row.latest_included_mc_index; - conn.query("UPDATE units SET latest_included_mc_index=? WHERE unit=?", [row.latest_included_mc_index, row.unit], function(){cb();}); + ({unit, latest_included_mc_index}, cb) => { + assocDbLimcisByUnit[unit] = latest_included_mc_index; + conn.query("UPDATE units SET latest_included_mc_index=? WHERE unit=?", [latest_included_mc_index, unit], () => {cb();}); }, - function(){ + () => { profiler.stop('mc-limci-update-propagate'); propagateLIMCI(); } @@ -279,33 +278,33 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ } function calcLIMCIs(onUpdated){ - var arrFilledUnits = []; + const arrFilledUnits = []; async.forEachOfSeries( assocChangedUnits, - function(props, unit, cb){ - var max_limci = -1; + (props, unit, cb) => { + let max_limci = -1; async.eachSeries( props.parent_units, - function(parent_unit, cb2){ - loadUnitProps(parent_unit, function(parent_props){ - if (parent_props.is_on_main_chain){ - props.latest_included_mc_index = parent_props.main_chain_index; + (parent_unit, cb2) => { + loadUnitProps(parent_unit, ({is_on_main_chain, main_chain_index, latest_included_mc_index}) => { + if (is_on_main_chain){ + props.latest_included_mc_index = main_chain_index; assocLimcisByUnit[unit] = props.latest_included_mc_index; arrFilledUnits.push(unit); return cb2('done'); } - if (parent_props.latest_included_mc_index === null) + if (latest_included_mc_index === null) return cb2('parent limci not known yet'); - if (parent_props.latest_included_mc_index > max_limci) - max_limci = parent_props.latest_included_mc_index; + if (latest_included_mc_index > max_limci) + max_limci = latest_included_mc_index; cb2(); }); }, - function(err){ + err => { if (err) return cb(); if (max_limci < 0) - throw Error("max limci < 0 for unit "+unit); + throw Error(`max limci < 0 for unit ${unit}`); props.latest_included_mc_index = max_limci; assocLimcisByUnit[unit] = props.latest_included_mc_index; arrFilledUnits.push(unit); @@ -313,8 +312,8 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ } ); }, - function(){ - arrFilledUnits.forEach(function(unit){ + () => { + arrFilledUnits.forEach(unit => { delete assocChangedUnits[unit]; }); if (Object.keys(assocChangedUnits).length > 0) @@ -325,21 +324,21 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ ); } - console.log("updateLatestIncludedMcIndex "+last_main_chain_index); + console.log(`updateLatestIncludedMcIndex ${last_main_chain_index}`); profiler.start(); var assocChangedUnits = {}; var assocLimcisByUnit = {}; var assocDbLimcisByUnit = {}; - for (var unit in storage.assocUnstableUnits){ - var o = storage.assocUnstableUnits[unit]; + for (const unit in storage.assocUnstableUnits){ + const o = storage.assocUnstableUnits[unit]; if (o.main_chain_index > last_main_chain_index || o.main_chain_index === null){ o.latest_included_mc_index = null; assocChangedUnits[unit] = o; } } - calcLIMCIs(function(){ - conn.query("UPDATE units SET latest_included_mc_index=NULL WHERE main_chain_index>? OR main_chain_index IS NULL", [last_main_chain_index], function(res){ - console.log("update LIMCI=NULL done, matched rows: "+res.affectedRows); + calcLIMCIs(() => { + conn.query("UPDATE units SET latest_included_mc_index=NULL WHERE main_chain_index>? OR main_chain_index IS NULL", [last_main_chain_index], ({affectedRows}) => { + console.log(`update LIMCI=NULL done, matched rows: ${affectedRows}`); profiler.stop('mc-limci-set-null'); profiler.start(); conn.query( @@ -370,20 +369,20 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ AND (chunits.main_chain_index > ? OR chunits.main_chain_index IS NULL) \n\ AND chunits.latest_included_mc_index IS NULL", [last_main_chain_index], - function(rows){ - console.log(rows.length+" rows"); + rows => { + console.log(`${rows.length} rows`); profiler.stop('mc-limci-select-initial'); profiler.start(); if (rows.length === 0 && bRebuiltMc) - throw Error("no latest_included_mc_index updated, last_mci="+last_main_chain_index+", affected="+res.affectedRows); + throw Error(`no latest_included_mc_index updated, last_mci=${last_main_chain_index}, affected=${affectedRows}`); async.eachSeries( rows, - function(row, cb){ - console.log(row.main_chain_index, row.unit); - assocDbLimcisByUnit[row.unit] = row.main_chain_index; - conn.query("UPDATE units SET latest_included_mc_index=? WHERE unit=?", [row.main_chain_index, row.unit], function(){ cb(); }); + ({main_chain_index, unit}, cb) => { + console.log(main_chain_index, unit); + assocDbLimcisByUnit[unit] = main_chain_index; + conn.query("UPDATE units SET latest_included_mc_index=? WHERE unit=?", [main_chain_index, unit], () => { cb(); }); }, - function(){ + () => { profiler.stop('mc-limci-update-initial'); propagateLIMCI(); } @@ -395,7 +394,7 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ } function readLastStableMcUnit(handleLastStableMcUnit){ - conn.query("SELECT unit FROM units WHERE is_on_main_chain=1 AND is_stable=1 ORDER BY main_chain_index DESC LIMIT 1", function(rows){ + conn.query("SELECT unit FROM units WHERE is_on_main_chain=1 AND is_stable=1 ORDER BY main_chain_index DESC LIMIT 1", rows => { if (rows.length === 0) throw Error("no units on stable MC?"); handleLastStableMcUnit(rows[0].unit); @@ -406,43 +405,43 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ function updateStableMcFlag(){ console.log("updateStableMcFlag"); profiler.start(); - readLastStableMcUnit(function(last_stable_mc_unit){ - console.log("last stable mc unit "+last_stable_mc_unit); - storage.readWitnesses(conn, last_stable_mc_unit, function(arrWitnesses){ - conn.query("SELECT unit, is_on_main_chain, main_chain_index, level FROM units WHERE best_parent_unit=?", [last_stable_mc_unit], function(rows){ + readLastStableMcUnit(last_stable_mc_unit => { + console.log(`last stable mc unit ${last_stable_mc_unit}`); + storage.readWitnesses(conn, last_stable_mc_unit, arrWitnesses => { + conn.query("SELECT unit, is_on_main_chain, main_chain_index, level FROM units WHERE best_parent_unit=?", [last_stable_mc_unit], rows => { if (rows.length === 0){ //if (isGenesisUnit(last_stable_mc_unit)) // return finish(); - throw Error("no best children of last stable MC unit "+last_stable_mc_unit+"?"); + throw Error(`no best children of last stable MC unit ${last_stable_mc_unit}?`); } - var arrMcRows = rows.filter(function(row){ return (row.is_on_main_chain === 1); }); // only one element - var arrAltRows = rows.filter(function(row){ return (row.is_on_main_chain === 0); }); + const arrMcRows = rows.filter(({is_on_main_chain}) => is_on_main_chain === 1); // only one element + const arrAltRows = rows.filter(({is_on_main_chain}) => is_on_main_chain === 0); if (arrMcRows.length !== 1) throw Error("not a single MC child?"); - var first_unstable_mc_unit = arrMcRows[0].unit; - var first_unstable_mc_index = arrMcRows[0].main_chain_index; - var first_unstable_mc_level = arrMcRows[0].level; - var arrAltBranchRootUnits = arrAltRows.map(function(row){ return row.unit; }); + const first_unstable_mc_unit = arrMcRows[0].unit; + const first_unstable_mc_index = arrMcRows[0].main_chain_index; + const first_unstable_mc_level = arrMcRows[0].level; + const arrAltBranchRootUnits = arrAltRows.map(({unit}) => unit); function advanceLastStableMcUnitAndTryNext(){ profiler.stop('mc-stableFlag'); markMcIndexStable(conn, first_unstable_mc_index, updateStableMcFlag); } - conn.query("SELECT witnessed_level FROM units WHERE is_free=1 AND is_on_main_chain=1", function(wl_rows){ + conn.query("SELECT witnessed_level FROM units WHERE is_free=1 AND is_on_main_chain=1", wl_rows => { if (wl_rows.length !== 1) throw Error("not a single mc wl"); // this is the level when we colect 7 witnesses if walking up the MC from its end - var mc_end_witnessed_level = wl_rows[0].witnessed_level; + const mc_end_witnessed_level = wl_rows[0].witnessed_level; conn.query( // among these 7 witnesses, find min wl "SELECT MIN(witnessed_level) AS min_mc_wl FROM units LEFT JOIN unit_authors USING(unit) \n\ WHERE is_on_main_chain=1 AND level>=? AND address IN(?)", // _left_ join enforces the best query plan in sqlite [mc_end_witnessed_level, arrWitnesses], - function(min_wl_rows){ + min_wl_rows => { if (min_wl_rows.length !== 1) throw Error("not a single min mc wl"); - var min_mc_wl = min_wl_rows[0].min_mc_wl; + const min_mc_wl = min_wl_rows[0].min_mc_wl; if (arrAltBranchRootUnits.length === 0){ // no alt branches if (min_mc_wl >= first_unstable_mc_level) return advanceLastStableMcUnitAndTryNext(); @@ -463,7 +462,7 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ return; */ } - createListOfBestChildren(arrAltBranchRootUnits, function(arrAltBestChildren){ + createListOfBestChildren(arrAltBranchRootUnits, arrAltBestChildren => { // Compose a set S of units that increase WL, that is their own WL is greater than that of every parent. // In this set, find max L. Alt WL will never reach it. If min_mc_wl > L, next MC unit is stable. // Also filter the set S to include only those units that are conformant with the last stable MC unit. @@ -478,10 +477,10 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ WHERE unit_witnesses.unit IN(units.unit, units.witness_list_unit) AND unit_witnesses.address IN(?) \n\ )>=?", [arrAltBestChildren, arrWitnesses, constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS], - function(max_alt_rows){ + max_alt_rows => { if (max_alt_rows.length !== 1) throw Error("not a single max alt level"); - var max_alt_level = max_alt_rows[0].max_alt_level; + const max_alt_level = max_alt_rows[0].max_alt_level; (min_mc_wl > max_alt_level) ? advanceLastStableMcUnitAndTryNext() : finish(); } ); @@ -498,28 +497,28 @@ function updateMainChain(conn, from_unit, last_added_unit, onDone){ function createListOfBestChildren(arrParentUnits, handleBestChildrenList){ if (arrParentUnits.length === 0) return handleBestChildrenList([]); - var arrBestChildren = arrParentUnits.slice(); + const arrBestChildren = arrParentUnits.slice(); function goDownAndCollectBestChildren(arrStartUnits, cb){ - conn.query("SELECT unit, is_free FROM units WHERE best_parent_unit IN(?)", [arrStartUnits], function(rows){ + conn.query("SELECT unit, is_free FROM units WHERE best_parent_unit IN(?)", [arrStartUnits], rows => { if (rows.length === 0) return cb(); //console.log("unit", arrStartUnits, "best children:", rows.map(function(row){ return row.unit; }), "free units:", rows.reduce(function(sum, row){ return sum+row.is_free; }, 0)); async.eachSeries( rows, - function(row, cb2){ - arrBestChildren.push(row.unit); - if (row.is_free === 1) + ({unit, is_free}, cb2) => { + arrBestChildren.push(unit); + if (is_free === 1) cb2(); else - goDownAndCollectBestChildren([row.unit], cb2); + goDownAndCollectBestChildren([unit], cb2); }, cb ); }); } - goDownAndCollectBestChildren(arrParentUnits, function(){ + goDownAndCollectBestChildren(arrParentUnits, () => { handleBestChildrenList(arrBestChildren); }); } @@ -582,42 +581,42 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle // hack to workaround past validation error if (earlier_unit === 'LGFzduLJNQNzEqJqUXdkXr58wDYx77V8WurDF3+GIws=' && arrLaterUnits.join(',') === '6O4t3j8kW0/Lo7n2nuS8ITDv2UbOhlL9fF1M6j/PrJ4=') return handleResult(true); - storage.readPropsOfUnits(conn, earlier_unit, arrLaterUnits, function(objEarlierUnitProps, arrLaterUnitProps){ - if (objEarlierUnitProps.is_free === 1) + storage.readPropsOfUnits(conn, earlier_unit, arrLaterUnits, ({is_free}, arrLaterUnitProps) => { + if (is_free === 1) return handleResult(false); - var max_later_limci = Math.max.apply( - null, arrLaterUnitProps.map(function(objLaterUnitProps){ return objLaterUnitProps.latest_included_mc_index; })); - readBestParentAndItsWitnesses(conn, earlier_unit, function(best_parent_unit, arrWitnesses){ - conn.query("SELECT unit, is_on_main_chain, main_chain_index, level FROM units WHERE best_parent_unit=?", [best_parent_unit], function(rows){ + const max_later_limci = Math.max.apply( + null, arrLaterUnitProps.map(({latest_included_mc_index}) => latest_included_mc_index)); + readBestParentAndItsWitnesses(conn, earlier_unit, (best_parent_unit, arrWitnesses) => { + conn.query("SELECT unit, is_on_main_chain, main_chain_index, level FROM units WHERE best_parent_unit=?", [best_parent_unit], rows => { if (rows.length === 0) - throw Error("no best children of "+best_parent_unit+"?"); - var arrMcRows = rows.filter(function(row){ return (row.is_on_main_chain === 1); }); // only one element - var arrAltRows = rows.filter(function(row){ return (row.is_on_main_chain === 0); }); + throw Error(`no best children of ${best_parent_unit}?`); + const arrMcRows = rows.filter(({is_on_main_chain}) => is_on_main_chain === 1); // only one element + const arrAltRows = rows.filter(({is_on_main_chain}) => is_on_main_chain === 0); if (arrMcRows.length !== 1) throw Error("not a single MC child?"); - var first_unstable_mc_unit = arrMcRows[0].unit; + const first_unstable_mc_unit = arrMcRows[0].unit; if (first_unstable_mc_unit !== earlier_unit) throw Error("first unstable MC unit is not our input unit"); - var first_unstable_mc_index = arrMcRows[0].main_chain_index; - var first_unstable_mc_level = arrMcRows[0].level; - var arrAltBranchRootUnits = arrAltRows.map(function(row){ return row.unit; }); + const first_unstable_mc_index = arrMcRows[0].main_chain_index; + const first_unstable_mc_level = arrMcRows[0].level; + const arrAltBranchRootUnits = arrAltRows.map(({unit}) => unit); //console.log("first_unstable_mc_index", first_unstable_mc_index); //console.log("first_unstable_mc_level", first_unstable_mc_level); //console.log("alt", arrAltBranchRootUnits); function findMinMcWitnessedLevel(handleMinMcWl){ - var min_mc_wl = Number.MAX_VALUE; - var count = 0; + let min_mc_wl = Number.MAX_VALUE; + let count = 0; function goUp(start_unit){ conn.query( "SELECT best_parent_unit, witnessed_level, \n\ (SELECT COUNT(*) FROM unit_authors WHERE unit_authors.unit=units.unit AND address IN(?)) AS count \n\ FROM units WHERE unit=?", [arrWitnesses, start_unit], - function(rows){ + rows => { if (rows.length !== 1) throw Error("findMinMcWitnessedLevel: not 1 row"); - var row = rows[0]; + const row = rows[0]; if (row.count > 0 && row.witnessed_level < min_mc_wl) min_mc_wl = row.witnessed_level; count += row.count; @@ -636,8 +635,8 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle unit ASC \n\ LIMIT 1", [arrWitnesses, arrLaterUnits], - function(rows){ - var row = rows[0]; + rows => { + const row = rows[0]; if (row.count > 0) min_mc_wl = row.witnessed_level; count += row.count; @@ -652,12 +651,12 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle // check if alt branches are included by later units async.eachSeries( arrAltBranchRootUnits, - function(alt_root_unit, cb){ - graph.determineIfIncludedOrEqual(conn, alt_root_unit, arrLaterUnits, function(bIncluded){ + (alt_root_unit, cb) => { + graph.determineIfIncludedOrEqual(conn, alt_root_unit, arrLaterUnits, bIncluded => { bIncluded ? cb("included") : cb(); }); }, - function(err){ + err => { handleHasAltBranchesResult(err ? true : false); } ); @@ -667,28 +666,28 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle function createListOfBestChildrenIncludedByLaterUnits(arrAltBranchRootUnits, handleBestChildrenList){ if (arrAltBranchRootUnits.length === 0) return handleBestChildrenList([]); - var arrBestChildren = []; + const arrBestChildren = []; function goDownAndCollectBestChildren(arrStartUnits, cb){ - conn.query("SELECT unit, is_free, main_chain_index FROM units WHERE best_parent_unit IN(?)", [arrStartUnits], function(rows){ + conn.query("SELECT unit, is_free, main_chain_index FROM units WHERE best_parent_unit IN(?)", [arrStartUnits], rows => { if (rows.length === 0) return cb(); async.eachSeries( rows, - function(row, cb2){ + ({unit, is_free, main_chain_index}, cb2) => { function addUnit(){ - arrBestChildren.push(row.unit); - if (row.is_free === 1) + arrBestChildren.push(unit); + if (is_free === 1) cb2(); else - goDownAndCollectBestChildren([row.unit], cb2); + goDownAndCollectBestChildren([unit], cb2); } - if (row.main_chain_index !== null && row.main_chain_index <= max_later_limci) + if (main_chain_index !== null && main_chain_index <= max_later_limci) addUnit(); else - graph.determineIfIncludedOrEqual(conn, row.unit, arrLaterUnits, function(bIncluded){ + graph.determineIfIncludedOrEqual(conn, unit, arrLaterUnits, bIncluded => { bIncluded ? addUnit() : cb2(); }); }, @@ -699,29 +698,29 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle // leaves only those roots that are included by later units function filterAltBranchRootUnits(cb){ - var arrFilteredAltBranchRootUnits = []; - conn.query("SELECT unit, is_free, main_chain_index FROM units WHERE unit IN(?)", [arrAltBranchRootUnits], function(rows){ + const arrFilteredAltBranchRootUnits = []; + conn.query("SELECT unit, is_free, main_chain_index FROM units WHERE unit IN(?)", [arrAltBranchRootUnits], rows => { if (rows.length === 0) throw Error("no alt branch root units?"); async.eachSeries( rows, - function(row, cb2){ + ({unit, main_chain_index}, cb2) => { function addUnit(){ - arrBestChildren.push(row.unit); + arrBestChildren.push(unit); // if (row.is_free === 0) // seems no reason to exclude - arrFilteredAltBranchRootUnits.push(row.unit); + arrFilteredAltBranchRootUnits.push(unit); cb2(); } - if (row.main_chain_index !== null && row.main_chain_index <= max_later_limci) + if (main_chain_index !== null && main_chain_index <= max_later_limci) addUnit(); else - graph.determineIfIncludedOrEqual(conn, row.unit, arrLaterUnits, function(bIncluded){ + graph.determineIfIncludedOrEqual(conn, unit, arrLaterUnits, bIncluded => { bIncluded ? addUnit() : cb2(); }); }, - function(){ + () => { //console.log('filtered:', arrFilteredAltBranchRootUnits); goDownAndCollectBestChildren(arrFilteredAltBranchRootUnits, cb); } @@ -729,15 +728,15 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle }); } - filterAltBranchRootUnits(function(){ + filterAltBranchRootUnits(() => { //console.log('best children:', arrBestChildren); handleBestChildrenList(arrBestChildren); }); } - findMinMcWitnessedLevel(function(min_mc_wl){ + findMinMcWitnessedLevel(min_mc_wl => { //console.log("min mc wl", min_mc_wl); - determineIfHasAltBranches(function(bHasAltBranches){ + determineIfHasAltBranches(bHasAltBranches => { if (!bHasAltBranches){ //console.log("no alt"); if (min_mc_wl >= first_unstable_mc_level) @@ -759,7 +758,7 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle */ } // has alt branches - createListOfBestChildrenIncludedByLaterUnits(arrAltBranchRootUnits, function(arrAltBestChildren){ + createListOfBestChildrenIncludedByLaterUnits(arrAltBranchRootUnits, arrAltBestChildren => { //throw arrAltBestChildren; // Compose a set S of units that increase WL, that is their own WL is greater than that of every parent. // In this set, find max L. Alt WL will never reach it. If min_mc_wl > L, next MC unit is stable. @@ -775,10 +774,10 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle WHERE unit_witnesses.unit IN(units.unit, units.witness_list_unit) AND unit_witnesses.address IN(?) \n\ )>=?", [arrAltBestChildren, arrWitnesses, constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS], - function(max_alt_rows){ + max_alt_rows => { if (max_alt_rows.length !== 1) throw Error("not a single max alt level"); - var max_alt_level = max_alt_rows[0].max_alt_level; + const max_alt_level = max_alt_rows[0].max_alt_level; // allow '=' since alt WL will *never* reach max_alt_level. // The comparison when moving the stability point above is still strict for compatibility handleResult(min_mc_wl >= max_alt_level); @@ -799,21 +798,21 @@ function determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, handle // It is assumed earlier_unit is not marked as stable yet // If it appears to be stable, its MC index will be marked as stable, as well as all preceeding MC indexes function determineIfStableInLaterUnitsAndUpdateStableMcFlag(conn, earlier_unit, arrLaterUnits, bStableInDb, handleResult){ - determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, function(bStable){ + determineIfStableInLaterUnits(conn, earlier_unit, arrLaterUnits, bStable => { console.log("determineIfStableInLaterUnits", earlier_unit, arrLaterUnits, bStable); if (!bStable) return handleResult(bStable); if (bStable && bStableInDb) return handleResult(bStable); breadcrumbs.add('stable in parents, will wait for write lock'); - mutex.lock(["write"], function(unlock){ + mutex.lock(["write"], unlock => { breadcrumbs.add('stable in parents, got write lock'); - storage.readLastStableMcIndex(conn, function(last_stable_mci){ - storage.readUnitProps(conn, earlier_unit, function(objEarlierUnitProps){ - var new_last_stable_mci = objEarlierUnitProps.main_chain_index; + storage.readLastStableMcIndex(conn, last_stable_mci => { + storage.readUnitProps(conn, earlier_unit, ({main_chain_index}) => { + const new_last_stable_mci = main_chain_index; if (new_last_stable_mci <= last_stable_mci) // fix: it could've been changed by parallel tasks - No, our SQL transaction doesn't see the changes throw Error("new last stable mci expected to be higher than existing"); - var mci = last_stable_mci; + let mci = last_stable_mci; advanceLastStableMcUnitAndStepForward(); function advanceLastStableMcUnitAndStepForward(){ @@ -835,9 +834,9 @@ function determineIfStableInLaterUnitsAndUpdateStableMcFlag(conn, earlier_unit, function readBestParentAndItsWitnesses(conn, unit, handleBestParentAndItsWitnesses){ - storage.readStaticUnitProps(conn, unit, function(props){ - storage.readWitnesses(conn, props.best_parent_unit, function(arrWitnesses){ - handleBestParentAndItsWitnesses(props.best_parent_unit, arrWitnesses); + storage.readStaticUnitProps(conn, unit, ({best_parent_unit}) => { + storage.readWitnesses(conn, best_parent_unit, arrWitnesses => { + handleBestParentAndItsWitnesses(best_parent_unit, arrWitnesses); }); }); } @@ -845,22 +844,22 @@ function readBestParentAndItsWitnesses(conn, unit, handleBestParentAndItsWitness function markMcIndexStable(conn, mci, onDone){ profiler.start(); - var arrStabilizedUnits = []; - for (var unit in storage.assocUnstableUnits){ - var o = storage.assocUnstableUnits[unit]; + const arrStabilizedUnits = []; + for (const unit in storage.assocUnstableUnits){ + const o = storage.assocUnstableUnits[unit]; if (o.main_chain_index === mci && o.is_stable === 0){ o.is_stable = 1; storage.assocStableUnits[unit] = o; arrStabilizedUnits.push(unit); } } - arrStabilizedUnits.forEach(function(unit){ + arrStabilizedUnits.forEach(unit => { delete storage.assocUnstableUnits[unit]; }); conn.query( "UPDATE units SET is_stable=1 WHERE is_stable=0 AND main_chain_index=?", [mci], - function(){ + () => { // next op handleNonserialUnits(); } @@ -870,27 +869,27 @@ function markMcIndexStable(conn, mci, onDone){ function handleNonserialUnits(){ conn.query( "SELECT * FROM units WHERE main_chain_index=? AND sequence!='good' ORDER BY unit", [mci], - function(rows){ + rows => { async.eachSeries( rows, - function(row, cb){ + (row, cb) => { if (row.sequence === 'final-bad') return row.content_hash ? cb() : setContentHash(row.unit, cb); // temp-bad if (row.content_hash) throw Error("temp-bad and with content_hash?"); - findStableConflictingUnits(row, function(arrConflictingUnits){ - var sequence = (arrConflictingUnits.length > 0) ? 'final-bad' : 'good'; - console.log("unit "+row.unit+" has competitors "+arrConflictingUnits+", it becomes "+sequence); - conn.query("UPDATE units SET sequence=? WHERE unit=?", [sequence, row.unit], function(){ + findStableConflictingUnits(row, arrConflictingUnits => { + const sequence = (arrConflictingUnits.length > 0) ? 'final-bad' : 'good'; + console.log(`unit ${row.unit} has competitors ${arrConflictingUnits}, it becomes ${sequence}`); + conn.query("UPDATE units SET sequence=? WHERE unit=?", [sequence, row.unit], () => { if (sequence === 'good') - conn.query("UPDATE inputs SET is_unique=1 WHERE unit=?", [row.unit], function(){ cb(); }); + conn.query("UPDATE inputs SET is_unique=1 WHERE unit=?", [row.unit], () => { cb(); }); else setContentHash(row.unit, cb); }); }); }, - function(){ + () => { //if (rows.length > 0) // throw "stop"; // next op @@ -903,12 +902,12 @@ function markMcIndexStable(conn, mci, onDone){ function setContentHash(unit, onSet){ storage.readJoint(conn, unit, { - ifNotFound: function(){ - throw Error("bad unit not found: "+unit); + ifNotFound() { + throw Error(`bad unit not found: ${unit}`); }, - ifFound: function(objJoint){ - var content_hash = objectHash.getUnitContentHash(objJoint.unit); - conn.query("UPDATE units SET content_hash=? WHERE unit=?", [content_hash, unit], function(){ + ifFound(objJoint) { + const content_hash = objectHash.getUnitContentHash(objJoint.unit); + conn.query("UPDATE units SET content_hash=? WHERE unit=?", [content_hash, unit], () => { onSet(); }); } @@ -945,18 +944,18 @@ function markMcIndexStable(conn, mci, onDone){ AND (competitor_units.main_chain_index <= this_unit.main_chain_index)", // if on the same mci, the smallest unit wins becuse it got selected earlier and was assigned sequence=good [objUnitProps.unit], - function(rows){ - var arrConflictingUnits = []; + rows => { + const arrConflictingUnits = []; async.eachSeries( rows, - function(row, cb){ - graph.compareUnitsByProps(conn, row, objUnitProps, function(result){ + (row, cb) => { + graph.compareUnitsByProps(conn, row, objUnitProps, result => { if (result === null) arrConflictingUnits.push(row.unit); cb(); }); }, - function(){ + () => { handleConflictingUnits(arrConflictingUnits); } ); @@ -969,30 +968,30 @@ function markMcIndexStable(conn, mci, onDone){ conn.query( "SELECT units.*, ball FROM units LEFT JOIN balls USING(unit) \n\ WHERE main_chain_index=? ORDER BY level", [mci], - function(unit_rows){ + unit_rows => { async.eachSeries( unit_rows, - function(objUnitProps, cb){ - var unit = objUnitProps.unit; + (objUnitProps, cb) => { + const unit = objUnitProps.unit; conn.query( "SELECT ball FROM parenthoods LEFT JOIN balls ON parent_unit=unit WHERE child_unit=? ORDER BY ball", [unit], - function(parent_ball_rows){ - if (parent_ball_rows.some(function(parent_ball_row){ return (parent_ball_row.ball === null); })) - throw Error("some parent balls not found for unit "+unit); - var arrParentBalls = parent_ball_rows.map(function(parent_ball_row){ return parent_ball_row.ball; }); - var arrSimilarMcis = getSimilarMcis(mci); - var arrSkiplistUnits = []; - var arrSkiplistBalls = []; + parent_ball_rows => { + if (parent_ball_rows.some(({ball}) => ball === null)) + throw Error(`some parent balls not found for unit ${unit}`); + const arrParentBalls = parent_ball_rows.map(({ball}) => ball); + const arrSimilarMcis = getSimilarMcis(mci); + const arrSkiplistUnits = []; + const arrSkiplistBalls = []; if (objUnitProps.is_on_main_chain === 1 && arrSimilarMcis.length > 0){ conn.query( "SELECT units.unit, ball FROM units LEFT JOIN balls USING(unit) \n\ WHERE is_on_main_chain=1 AND main_chain_index IN(?)", [arrSimilarMcis], - function(rows){ - rows.forEach(function(row){ - var skiplist_unit = row.unit; - var skiplist_ball = row.ball; + rows => { + rows.forEach(row => { + const skiplist_unit = row.unit; + const skiplist_ball = row.ball; if (!skiplist_ball) throw Error("no skiplist ball"); arrSkiplistUnits.push(skiplist_unit); @@ -1006,22 +1005,19 @@ function markMcIndexStable(conn, mci, onDone){ addBall(); function addBall(){ - var ball = objectHash.getBallHash(unit, arrParentBalls, arrSkiplistBalls.sort(), objUnitProps.sequence === 'final-bad'); + const ball = objectHash.getBallHash(unit, arrParentBalls, arrSkiplistBalls.sort(), objUnitProps.sequence === 'final-bad'); if (objUnitProps.ball){ // already inserted if (objUnitProps.ball !== ball) - throw Error("stored and calculated ball hashes do not match, ball="+ball+", objUnitProps="+JSON.stringify(objUnitProps)); + throw Error(`stored and calculated ball hashes do not match, ball=${ball}, objUnitProps=${JSON.stringify(objUnitProps)}`); return cb(); } - conn.query("INSERT INTO balls (ball, unit) VALUES(?,?)", [ball, unit], function(){ - conn.query("DELETE FROM hash_tree_balls WHERE ball=?", [ball], function(){ + conn.query("INSERT INTO balls (ball, unit) VALUES(?,?)", [ball, unit], () => { + conn.query("DELETE FROM hash_tree_balls WHERE ball=?", [ball], () => { if (arrSkiplistUnits.length === 0) return cb(); conn.query( - "INSERT INTO skiplist_units (unit, skiplist_unit) VALUES " - +arrSkiplistUnits.map(function(skiplist_unit){ - return "("+conn.escape(unit)+", "+conn.escape(skiplist_unit)+")"; - }), - function(){ cb(); } + `INSERT INTO skiplist_units (unit, skiplist_unit) VALUES ${arrSkiplistUnits.map(skiplist_unit => `(${conn.escape(unit)}, ${conn.escape(skiplist_unit)})`)}`, + () => { cb(); } ); }); }); @@ -1029,7 +1025,7 @@ function markMcIndexStable(conn, mci, onDone){ } ); }, - function(){ + () => { // next op updateRetrievable(); } @@ -1039,7 +1035,7 @@ function markMcIndexStable(conn, mci, onDone){ } function updateRetrievable(){ - storage.updateMinRetrievableMciAfterStabilizingMci(conn, mci, function(min_retrievable_mci){ + storage.updateMinRetrievableMciAfterStabilizingMci(conn, mci, min_retrievable_mci => { profiler.stop('mc-mark-stable'); calcCommissions(); }); @@ -1047,16 +1043,16 @@ function markMcIndexStable(conn, mci, onDone){ function calcCommissions(){ async.series([ - function(cb){ + cb => { profiler.start(); headers_commission.calcHeadersCommissions(conn, cb); }, - function(cb){ + cb => { profiler.stop('mc-headers-commissions'); paid_witnessing.updatePaidWitnesses(conn, cb); } - ], function(){ - process.nextTick(function(){ // don't call it synchronously with event emitter + ], () => { + process.nextTick(() => { // don't call it synchronously with event emitter eventBus.emit("mci_became_stable", mci); }); onDone(); @@ -1067,8 +1063,8 @@ function markMcIndexStable(conn, mci, onDone){ // returns list of past MC indices for skiplist function getSimilarMcis(mci){ - var arrSimilarMcis = []; - var divisor = 10; + const arrSimilarMcis = []; + let divisor = 10; while (true){ if (mci % divisor === 0){ arrSimilarMcis.push(mci - divisor); diff --git a/mc_outputs.js b/mc_outputs.js index 59710fae..346cd731 100644 --- a/mc_outputs.js +++ b/mc_outputs.js @@ -1,9 +1,8 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var async = require('async'); -var db = require('./db.js'); -var conf = require('./conf.js'); +const _ = require('lodash'); +const async = require('async'); +const db = require('./db.js'); +const conf = require('./conf.js'); // Functions for reading headers commissions and witnessing outputs. @@ -12,16 +11,14 @@ var conf = require('./conf.js'); function readNextSpendableMcIndex(conn, type, address, arrConflictingUnits, handleNextSpendableMcIndex){ conn.query( - "SELECT to_main_chain_index FROM inputs CROSS JOIN units USING(unit) \n\ - WHERE type=? AND address=? AND sequence='good' "+( - (arrConflictingUnits && arrConflictingUnits.length > 0) - ? " AND unit NOT IN("+arrConflictingUnits.map(function(unit){ return db.escape(unit); }).join(", ")+") " - : "" - )+" \n\ - ORDER BY to_main_chain_index DESC LIMIT 1", + `SELECT to_main_chain_index FROM inputs CROSS JOIN units USING(unit) \n\ + WHERE type=? AND address=? AND sequence='good' ${(arrConflictingUnits && arrConflictingUnits.length > 0) +? ` AND unit NOT IN(${arrConflictingUnits.map(unit => db.escape(unit)).join(", ")}) ` +: ""} \n\ + ORDER BY to_main_chain_index DESC LIMIT 1`, [type, address], - function(rows){ - var mci = (rows.length > 0) ? (rows[0].to_main_chain_index+1) : 0; + rows => { + const mci = (rows.length > 0) ? (rows[0].to_main_chain_index+1) : 0; // readNextUnspentMcIndex(conn, type, address, function(next_unspent_mci){ // if (next_unspent_mci !== mci) // throw Error("next unspent mci !== next spendable mci: "+next_unspent_mci+" !== "+mci+", address "+address); @@ -45,9 +42,9 @@ function readNextUnspentMcIndex(conn, type, address, handleNextUnspentMcIndex){ */ function readMaxSpendableMcIndex(conn, type, handleMaxSpendableMcIndex){ - var table = type + '_outputs'; - conn.query("SELECT MAX(main_chain_index) AS max_mc_index FROM "+table, function(rows){ - var max_mc_index = rows[0].max_mc_index || 0; + const table = `${type}_outputs`; + conn.query(`SELECT MAX(main_chain_index) AS max_mc_index FROM ${table}`, rows => { + const max_mc_index = rows[0].max_mc_index || 0; handleMaxSpendableMcIndex(max_mc_index); }); } @@ -55,11 +52,11 @@ function readMaxSpendableMcIndex(conn, type, handleMaxSpendableMcIndex){ function findMcIndexIntervalToTargetAmount(conn, type, address, max_mci, target_amount, callbacks){ - var table = type + '_outputs'; - readNextSpendableMcIndex(conn, type, address, null, function(from_mci){ + const table = `${type}_outputs`; + readNextSpendableMcIndex(conn, type, address, null, from_mci => { if (from_mci > max_mci) return callbacks.ifNothing(); - readMaxSpendableMcIndex(conn, type, function(max_spendable_mci){ + readMaxSpendableMcIndex(conn, type, max_spendable_mci => { if (max_spendable_mci <= 0) return callbacks.ifNothing(); if (max_spendable_mci > max_mci) @@ -68,42 +65,42 @@ function findMcIndexIntervalToTargetAmount(conn, type, address, max_mci, target_ target_amount = 1e15; if (conf.storage === 'mysql'){ conn.query( - "SELECT main_chain_index, accumulated, has_sufficient \n\ - FROM ( \n\ - SELECT main_chain_index, @sum:=@sum+amount AS accumulated, @has_sufficient:=(@sum>?) AS has_sufficient \n\ - FROM "+table+", (SELECT @sum:=0, @has_sufficient:=0) AS unused \n\ - WHERE is_spent=0 AND address=? AND main_chain_index>=? AND main_chain_index<=? \n\ - ORDER BY main_chain_index \n\ - ) AS t \n\ - WHERE IF(@has_sufficient, has_sufficient, 1) \n\ - ORDER BY IF(@has_sufficient, accumulated, -accumulated) LIMIT 1", + `SELECT main_chain_index, accumulated, has_sufficient \n\ + FROM ( \n\ + SELECT main_chain_index, @sum:=@sum+amount AS accumulated, @has_sufficient:=(@sum>?) AS has_sufficient \n\ + FROM ${table}, (SELECT @sum:=0, @has_sufficient:=0) AS unused \n\ + WHERE is_spent=0 AND address=? AND main_chain_index>=? AND main_chain_index<=? \n\ + ORDER BY main_chain_index \n\ + ) AS t \n\ + WHERE IF(@has_sufficient, has_sufficient, 1) \n\ + ORDER BY IF(@has_sufficient, accumulated, -accumulated) LIMIT 1`, [target_amount, address, from_mci, max_spendable_mci], - function(rows){ + rows => { if (rows.length === 0) return callbacks.ifNothing(); - var bHasSufficient = rows[0].has_sufficient; - var accumulated = rows[0].accumulated; - var to_mci = rows[0].main_chain_index; + const bHasSufficient = rows[0].has_sufficient; + const accumulated = rows[0].accumulated; + const to_mci = rows[0].main_chain_index; callbacks.ifFound(from_mci, to_mci, accumulated, bHasSufficient); } ); } else{ - var MIN_MC_OUTPUT = (type === 'witnessing') ? 11 : 344; - var max_count_outputs = Math.ceil(target_amount/MIN_MC_OUTPUT); + const MIN_MC_OUTPUT = (type === 'witnessing') ? 11 : 344; + const max_count_outputs = Math.ceil(target_amount/MIN_MC_OUTPUT); conn.query( - "SELECT main_chain_index, amount \n\ - FROM "+table+" \n\ - WHERE is_spent=0 AND address=? AND main_chain_index>=? AND main_chain_index<=? \n\ - ORDER BY main_chain_index LIMIT ?", + `SELECT main_chain_index, amount \n\ + FROM ${table} \n\ + WHERE is_spent=0 AND address=? AND main_chain_index>=? AND main_chain_index<=? \n\ + ORDER BY main_chain_index LIMIT ?`, [address, from_mci, max_spendable_mci, max_count_outputs], - function(rows){ + rows => { if (rows.length === 0) return callbacks.ifNothing(); - var accumulated = 0; - var to_mci; - var bHasSufficient = false; - for (var i=0; i target_amount){ @@ -120,14 +117,14 @@ function findMcIndexIntervalToTargetAmount(conn, type, address, max_mci, target_ } function calcEarnings(conn, type, from_main_chain_index, to_main_chain_index, address, callbacks){ - var table = type + '_outputs'; + const table = `${type}_outputs`; conn.query( - "SELECT SUM(amount) AS total \n\ - FROM "+table+" \n\ - WHERE main_chain_index>=? AND main_chain_index<=? AND address=?", + `SELECT SUM(amount) AS total \n\ + FROM ${table} \n\ + WHERE main_chain_index>=? AND main_chain_index<=? AND address=?`, [from_main_chain_index, to_main_chain_index, address], - function(rows){ - var total = rows[0].total; + rows => { + let total = rows[0].total; if (total === null) total = 0; if (typeof total !== 'number') diff --git a/merkle.js b/merkle.js index 0b9c18b3..aa554359 100644 --- a/merkle.js +++ b/merkle.js @@ -1,17 +1,16 @@ /*jslint node: true */ -"use strict"; -var crypto = require('crypto'); +const crypto = require('crypto'); function hash(str){ return crypto.createHash("sha256").update(str, "utf8").digest("base64"); } function getMerkleRoot(arrElements){ - var arrHashes = arrElements.map(hash); + let arrHashes = arrElements.map(hash); while (arrHashes.length > 1){ - var arrOverHashes = []; // hashes over hashes - for (var i=0; i= arrElements.length) throw Error("invalid index"); - var arrHashes = arrElements.map(hash); + let arrHashes = arrElements.map(hash); var index = element_index; - var arrSiblings = []; + const arrSiblings = []; while (arrHashes.length > 1){ - var arrOverHashes = []; // hashes over hashes - var overIndex = null; - for (var i=0; i 0) - serialized_proof += "-"+proof.siblings.join("-"); - serialized_proof += "-"+proof.root; +function serializeMerkleProof({index, siblings, root}) { + let serialized_proof = index; + if (siblings.length > 0) + serialized_proof += `-${siblings.join("-")}`; + serialized_proof += `-${root}`; return serialized_proof; } function deserializeMerkleProof(serialized_proof){ - var arr = serialized_proof.split("-"); - var proof = {}; + const arr = serialized_proof.split("-"); + const proof = {}; proof.root = arr.pop(); proof.index = arr.shift(); proof.siblings = arr; @@ -82,9 +81,9 @@ function deserializeMerkleProof(serialized_proof){ } function verifyMerkleProof(element, proof){ - var index = proof.index; - var the_other_sibling = hash(element); - for (var i=0; i 30*1000) - throw Error("possible deadlock on job "+require('util').inspect(job)+",\nproc:"+job.proc.toString()+" \nall jobs: "+require('util').inspect(arrQueuedJobs, {depth: null})); + throw Error(`possible deadlock on job ${require('util').inspect(job)},\nproc:${job.proc.toString()} \nall jobs: ${require('util').inspect(arrQueuedJobs, {depth: null})}`); } } // long running locks are normal in multisig scenarios //setInterval(checkForDeadlocks, 1000); -setInterval(function(){ - console.log("queued jobs: "+JSON.stringify(arrQueuedJobs.map(function(job){ return job.arrKeys; }))+", locked keys: "+JSON.stringify(arrLockedKeyArrays)); +setInterval(() => { + console.log(`queued jobs: ${JSON.stringify(arrQueuedJobs.map(({arrKeys}) => arrKeys))}, locked keys: ${JSON.stringify(arrLockedKeyArrays)}`); }, 10000); exports.lock = lock; diff --git a/my_witnesses.js b/my_witnesses.js index 6855c08e..833292fd 100644 --- a/my_witnesses.js +++ b/my_witnesses.js @@ -1,14 +1,13 @@ /*jslint node: true */ -"use strict"; -var db = require('./db.js'); -var conf = require('./conf.js'); -var constants = require('./constants.js'); -var storage = require('./storage.js'); -var ValidationUtils = require("./validation_utils.js"); +const db = require('./db.js'); +const conf = require('./conf.js'); +const constants = require('./constants.js'); +const storage = require('./storage.js'); +const ValidationUtils = require("./validation_utils.js"); function readMyWitnesses(handleWitnesses, actionIfEmpty){ - db.query("SELECT address FROM my_witnesses ORDER BY address", function(rows){ - var arrWitnesses = rows.map(function(row){ return row.address; }); + db.query("SELECT address FROM my_witnesses ORDER BY address", rows => { + let arrWitnesses = rows.map(({address}) => address); // reset witness list if old witnesses found if (constants.alt === '2' && arrWitnesses.indexOf('5K7CSLTRPC5LFLOS3D34GBHG7RFD4TPO') >= 0 || constants.version === '1.0' && arrWitnesses.indexOf('2FF7PSL7FYXVU5UIQHCVDTTPUOOG75GX') >= 0 @@ -22,14 +21,14 @@ function readMyWitnesses(handleWitnesses, actionIfEmpty){ return handleWitnesses([]); if (actionIfEmpty === 'wait'){ console.log('no witnesses yet, will retry later'); - setTimeout(function(){ + setTimeout(() => { readMyWitnesses(handleWitnesses, actionIfEmpty); }, 1000); return; } } if (arrWitnesses.length !== constants.COUNT_WITNESSES) - throw Error("wrong number of my witnesses: "+arrWitnesses.length); + throw Error(`wrong number of my witnesses: ${arrWitnesses.length}`); handleWitnesses(arrWitnesses); }); } @@ -38,13 +37,13 @@ function readMyWitnesses(handleWitnesses, actionIfEmpty){ function replaceWitness(old_witness, new_witness, handleResult){ if (!ValidationUtils.isValidAddress(new_witness)) return handleResult("new witness address is invalid"); - readMyWitnesses(function(arrWitnesses){ + readMyWitnesses(arrWitnesses => { if (arrWitnesses.indexOf(old_witness) === -1) return handleResult("old witness not known"); if (arrWitnesses.indexOf(new_witness) >= 0) return handleResult("new witness already present"); - var doReplace = function(){ - db.query("UPDATE my_witnesses SET address=? WHERE address=?", [new_witness, old_witness], function(){ + const doReplace = () => { + db.query("UPDATE my_witnesses SET address=? WHERE address=?", [new_witness, old_witness], () => { handleResult(); }); }; @@ -53,10 +52,10 @@ function replaceWitness(old_witness, new_witness, handleResult){ db.query( "SELECT 1 FROM unit_authors CROSS JOIN units USING(unit) WHERE address=? AND sequence='good' AND is_stable=1 LIMIT 1", [new_witness], - function(rows){ - if (rows.length === 0) + ({length}) => { + if (length === 0) return handleResult("no stable messages from the new witness yet"); - storage.determineIfWitnessAddressDefinitionsHaveReferences(db, [new_witness], function(bHasReferences){ + storage.determineIfWitnessAddressDefinitionsHaveReferences(db, [new_witness], bHasReferences => { if (bHasReferences) return handleResult("address definition of the new witness has or had references"); doReplace(); @@ -68,10 +67,10 @@ function replaceWitness(old_witness, new_witness, handleResult){ function insertWitnesses(arrWitnesses, onDone){ if (arrWitnesses.length !== constants.COUNT_WITNESSES) - throw Error("attempting to insert wrong number of witnesses: "+arrWitnesses.length); - var placeholders = Array.apply(null, Array(arrWitnesses.length)).map(function(){ return '(?)'; }).join(','); + throw Error(`attempting to insert wrong number of witnesses: ${arrWitnesses.length}`); + const placeholders = Array(...Array(arrWitnesses.length)).map(() => '(?)').join(','); console.log('will insert witnesses', arrWitnesses); - db.query("INSERT INTO my_witnesses (address) VALUES "+placeholders, arrWitnesses, function(){ + db.query(`INSERT INTO my_witnesses (address) VALUES ${placeholders}`, arrWitnesses, () => { console.log('inserted witnesses'); if (onDone) onDone(); diff --git a/mysql_pool.js b/mysql_pool.js index ce545c93..fc16e9f1 100644 --- a/mysql_pool.js +++ b/mysql_pool.js @@ -1,33 +1,32 @@ /*jslint node: true */ -"use strict"; -var mysql = require('mysql'); +const mysql = require('mysql'); -module.exports = function(connection_or_pool){ +module.exports = connection_or_pool => { console.log("constructor"); - var safe_connection = connection_or_pool; + const safe_connection = connection_or_pool; safe_connection.original_query = safe_connection.query; safe_connection.original_release = safe_connection.release; // this is a hack to make all errors throw exception that would kill the program - safe_connection.query = function () { - var last_arg = arguments[arguments.length - 1]; - var bHasCallback = (typeof last_arg === 'function'); + safe_connection.query = function(...args) { + let last_arg = args[args.length - 1]; + const bHasCallback = (typeof last_arg === 'function'); if (!bHasCallback){ // no callback - last_arg = function(){}; + last_arg = () => {}; //return connection_or_pool.original_query.apply(connection_or_pool, arguments); } - var count_arguments_without_callback = bHasCallback ? (arguments.length-1) : arguments.length; - var new_args = []; - var q; + const count_arguments_without_callback = bHasCallback ? (args.length-1) : args.length; + const new_args = []; + let q; - for (var i=0; i { if (err){ - console.error("\nfailed query: "+q.sql); + console.error(`\nfailed query: ${q.sql}`); /* //console.error("code: "+(typeof err.code)); if (false && err.code === 'ER_LOCK_DEADLOCK'){ @@ -43,39 +42,39 @@ module.exports = function(connection_or_pool){ last_arg(results, fields); }); //console.log(new_args); - q = connection_or_pool.original_query.apply(connection_or_pool, new_args); + q = connection_or_pool.original_query(...new_args); //console.log(q.sql); return q; }; //safe_connection.escape = connection_or_pool.escape; - safe_connection.release = function(){ + safe_connection.release = () => { //console.log("releasing connection"); connection_or_pool.original_release(); }; safe_connection.addQuery = function (arr) { - var query_args = []; - for (var i=1; i { // add callback for async.series() member tasks if (typeof query_args[query_args.length-1] !== 'function') - query_args.push(function(){callback();}); // add mysql callback + query_args.push(() => {callback();}); // add mysql callback else{ - var f = query_args[query_args.length-1]; - query_args[query_args.length-1] = function(){ - f.apply(f, arguments); + const f = query_args[query_args.length-1]; + query_args[query_args.length-1] = function(...args) { + f.apply(f, args); callback(); } } - safe_connection.query.apply(safe_connection, query_args); + safe_connection.query(...query_args); }); }; // this is for pool only - safe_connection.takeConnectionFromPool = function(handleConnection){ - connection_or_pool.getConnection(function(err, new_connection) { + safe_connection.takeConnectionFromPool = handleConnection => { + connection_or_pool.getConnection((err, new_connection) => { if (err) throw err; console.log("got connection from pool"); @@ -83,45 +82,27 @@ module.exports = function(connection_or_pool){ }); }; - safe_connection.getCountUsedConnections = function(){ - return (safe_connection._allConnections.length - safe_connection._freeConnections.length); - }; + safe_connection.getCountUsedConnections = () => safe_connection._allConnections.length - safe_connection._freeConnections.length; - safe_connection.close = function(cb){ + safe_connection.close = cb => { connection_or_pool.end(cb); }; - safe_connection.addTime = function(interval){ - return "NOW() + INTERVAL "+interval; - }; + safe_connection.addTime = interval => `NOW() + INTERVAL ${interval}`; - safe_connection.getNow = function(){ - return "NOW()"; - }; + safe_connection.getNow = () => "NOW()"; - safe_connection.getFromUnixTime = function(ts){ - return "FROM_UNIXTIME("+ts+")"; - }; + safe_connection.getFromUnixTime = ts => `FROM_UNIXTIME(${ts})`; - safe_connection.getRandom = function(){ - return "RAND()"; - }; + safe_connection.getRandom = () => "RAND()"; - safe_connection.forceIndex = function(index){ - return "FORCE INDEX ("+ index +")"; - }; + safe_connection.forceIndex = index => `FORCE INDEX (${index})`; - safe_connection.dropTemporaryTable = function(table){ - return "DROP TEMPORARY TABLE IF EXISTS " + table; - }; + safe_connection.dropTemporaryTable = table => `DROP TEMPORARY TABLE IF EXISTS ${table}`; - safe_connection.getIgnore = function(){ - return "IGNORE"; - }; + safe_connection.getIgnore = () => "IGNORE"; - safe_connection.getUnixTimestamp = function(date){ - return "UNIX_TIMESTAMP("+date+")"; - }; + safe_connection.getUnixTimestamp = date => `UNIX_TIMESTAMP(${date})`; return safe_connection; }; diff --git a/network.js b/network.js index 3399f7ab..a8747902 100644 --- a/network.js +++ b/network.js @@ -1,84 +1,83 @@ /*jslint node: true */ -"use strict"; -var WebSocket = process.browser ? global.WebSocket : require('ws'); -var socks = process.browser ? null : require('socks'+''); -var WebSocketServer = WebSocket.Server; -var crypto = require('crypto'); -var _ = require('lodash'); -var async = require('async'); -var db = require('./db.js'); -var constants = require('./constants.js'); -var storage = require('./storage.js'); -var myWitnesses = require('./my_witnesses.js'); -var joint_storage = require('./joint_storage.js'); -var validation = require('./validation.js'); -var ValidationUtils = require("./validation_utils.js"); -var writer = require('./writer.js'); -var conf = require('./conf.js'); -var mutex = require('./mutex.js'); -var catchup = require('./catchup.js'); -var privatePayment = require('./private_payment.js'); -var objectHash = require('./object_hash.js'); -var ecdsaSig = require('./signature.js'); -var eventBus = require('./event_bus.js'); -var light = require('./light.js'); -var breadcrumbs = require('./breadcrumbs.js'); -var mail = process.browser ? null : require('./mail.js'+''); - -var FORWARDING_TIMEOUT = 10*1000; // don't forward if the joint was received more than FORWARDING_TIMEOUT ms ago -var STALLED_TIMEOUT = 5000; // a request is treated as stalled if no response received within STALLED_TIMEOUT ms -var RESPONSE_TIMEOUT = 300*1000; // after this timeout, the request is abandoned -var HEARTBEAT_TIMEOUT = conf.HEARTBEAT_TIMEOUT || 10*1000; -var HEARTBEAT_RESPONSE_TIMEOUT = 60*1000; -var PAUSE_TIMEOUT = 2*HEARTBEAT_TIMEOUT; - -var wss; -var arrOutboundPeers = []; -var assocConnectingOutboundWebsockets = {}; -var assocUnitsInWork = {}; -var assocRequestedUnits = {}; -var bCatchingUp = false; -var bWaitingForCatchupChain = false; -var bWaitingTillIdle = false; -var coming_online_time = Date.now(); -var assocReroutedConnectionsByTag = {}; -var arrWatchedAddresses = []; // does not include my addresses, therefore always empty -var last_hearbeat_wake_ts = Date.now(); -var peer_events_buffer = []; -var assocKnownPeers = {}; -var exchangeRates = {}; +const WebSocket = process.browser ? global.WebSocket : require('ws'); +const socks = process.browser ? null : require('socks'+''); +const WebSocketServer = WebSocket.Server; +const crypto = require('crypto'); +const _ = require('lodash'); +const async = require('async'); +const db = require('./db.js'); +const constants = require('./constants.js'); +const storage = require('./storage.js'); +const myWitnesses = require('./my_witnesses.js'); +const joint_storage = require('./joint_storage.js'); +const validation = require('./validation.js'); +const ValidationUtils = require("./validation_utils.js"); +const writer = require('./writer.js'); +const conf = require('./conf.js'); +const mutex = require('./mutex.js'); +const catchup = require('./catchup.js'); +const privatePayment = require('./private_payment.js'); +const objectHash = require('./object_hash.js'); +const ecdsaSig = require('./signature.js'); +const eventBus = require('./event_bus.js'); +const light = require('./light.js'); +const breadcrumbs = require('./breadcrumbs.js'); +const mail = process.browser ? null : require('./mail.js'+''); + +const FORWARDING_TIMEOUT = 10*1000; // don't forward if the joint was received more than FORWARDING_TIMEOUT ms ago +const STALLED_TIMEOUT = 5000; // a request is treated as stalled if no response received within STALLED_TIMEOUT ms +const RESPONSE_TIMEOUT = 300*1000; // after this timeout, the request is abandoned +const HEARTBEAT_TIMEOUT = conf.HEARTBEAT_TIMEOUT || 10*1000; +const HEARTBEAT_RESPONSE_TIMEOUT = 60*1000; +const PAUSE_TIMEOUT = 2*HEARTBEAT_TIMEOUT; + +let wss; +const arrOutboundPeers = []; +const assocConnectingOutboundWebsockets = {}; +const assocUnitsInWork = {}; +const assocRequestedUnits = {}; +let bCatchingUp = false; +let bWaitingForCatchupChain = false; +let bWaitingTillIdle = false; +let coming_online_time = Date.now(); +const assocReroutedConnectionsByTag = {}; +let arrWatchedAddresses = []; // does not include my addresses, therefore always empty +let last_hearbeat_wake_ts = Date.now(); +let peer_events_buffer = []; +const assocKnownPeers = {}; +const exchangeRates = {}; if (process.browser){ // browser console.log("defining .on() on ws"); WebSocket.prototype.on = function(event, callback) { - var self = this; + const self = this; if (event === 'message'){ - this['on'+event] = function(event){ - callback.call(self, event.data); + this[`on${event}`] = ({data}) => { + callback.call(self, data); }; return; } if (event !== 'open'){ - this['on'+event] = callback; + this[`on${event}`] = callback; return; } // allow several handlers for 'open' event if (!this['open_handlers']) this['open_handlers'] = []; this['open_handlers'].push(callback); - this['on'+event] = function(){ - self['open_handlers'].forEach(function(cb){ + this[`on${event}`] = () => { + self['open_handlers'].forEach(cb => { cb(); }); }; }; WebSocket.prototype.once = WebSocket.prototype.on; - WebSocket.prototype.setMaxListeners = function(){}; + WebSocket.prototype.setMaxListeners = () => {}; } // if not using a hub and accepting messages directly (be your own hub) -var my_device_address; -var objMyTempPubkeyPackage; +let my_device_address; +let objMyTempPubkeyPackage; function setMyDeviceProps(device_address, objTempPubkey){ my_device_address = device_address; @@ -90,20 +89,20 @@ exports.light_vendor_url = null; // general network functions function sendMessage(ws, type, content) { - var message = JSON.stringify([type, content]); + const message = JSON.stringify([type, content]); if (ws.readyState !== ws.OPEN) - return console.log("readyState="+ws.readyState+' on peer '+ws.peer+', will not send '+message); - console.log("SENDING "+message+" to "+ws.peer); + return console.log(`readyState=${ws.readyState} on peer ${ws.peer}, will not send ${message}`); + console.log(`SENDING ${message} to ${ws.peer}`); ws.send(message); } function sendJustsaying(ws, subject, body){ - sendMessage(ws, 'justsaying', {subject: subject, body: body}); + sendMessage(ws, 'justsaying', {subject, body}); } function sendAllInboundJustsaying(subject, body){ - wss.clients.forEach(function(ws){ - sendMessage(ws, 'justsaying', {subject: subject, body: body}); + wss.clients.forEach(ws => { + sendMessage(ws, 'justsaying', {subject, body}); }); } @@ -120,11 +119,11 @@ function sendResult(ws, content) { } function sendErrorResult(ws, unit, error) { - sendResult(ws, {unit: unit, result: 'error', error: error}); + sendResult(ws, {unit, result: 'error', error}); } function sendVersion(ws){ - var libraryPackageJson = require('./package.json'); + const libraryPackageJson = require('./package.json'); sendJustsaying(ws, 'version', { protocol_version: constants.version, alt: constants.alt, @@ -137,50 +136,50 @@ function sendVersion(ws){ function sendResponse(ws, tag, response){ delete ws.assocInPreparingResponse[tag]; - sendMessage(ws, 'response', {tag: tag, response: response}); + sendMessage(ws, 'response', {tag, response}); } function sendErrorResponse(ws, tag, error) { - sendResponse(ws, tag, {error: error}); + sendResponse(ws, tag, {error}); } // if a 2nd identical request is issued before we receive a response to the 1st request, then: // 1. its responseHandler will be called too but no second request will be sent to the wire // 2. bReroutable flag must be the same function sendRequest(ws, command, params, bReroutable, responseHandler){ - var request = {command: command}; + const request = {command}; if (params) request.params = params; - var content = _.clone(request); - var tag = objectHash.getBase64Hash(request); + const content = _.clone(request); + const tag = objectHash.getBase64Hash(request); //if (ws.assocPendingRequests[tag]) // ignore duplicate requests while still waiting for response from the same peer // return console.log("will not send identical "+command+" request"); if (ws.assocPendingRequests[tag]){ - console.log('already sent a '+command+' request to '+ws.peer+', will add one more response handler rather than sending a duplicate request to the wire'); + console.log(`already sent a ${command} request to ${ws.peer}, will add one more response handler rather than sending a duplicate request to the wire`); ws.assocPendingRequests[tag].responseHandlers.push(responseHandler); } else{ content.tag = tag; // after STALLED_TIMEOUT, reroute the request to another peer // it'll work correctly even if the current peer is already disconnected when the timeout fires - var reroute = !bReroutable ? null : function(){ - console.log('will try to reroute a '+command+' request stalled at '+ws.peer); + const reroute = !bReroutable ? null : () => { + console.log(`will try to reroute a ${command} request stalled at ${ws.peer}`); if (!ws.assocPendingRequests[tag]) return console.log('will not reroute - the request was already handled by another peer'); ws.assocPendingRequests[tag].bRerouted = true; - findNextPeer(ws, function(next_ws){ // the callback may be called much later if findNextPeer has to wait for connection + findNextPeer(ws, next_ws => { // the callback may be called much later if findNextPeer has to wait for connection if (!ws.assocPendingRequests[tag]) return console.log('will not reroute after findNextPeer - the request was already handled by another peer'); if (next_ws === ws || assocReroutedConnectionsByTag[tag] && assocReroutedConnectionsByTag[tag].indexOf(next_ws) >= 0){ - console.log('will not reroute '+command+' to the same peer, will rather wait for a new connection'); - eventBus.once('connected_to_source', function(){ // try again - console.log('got new connection, retrying reroute '+command); + console.log(`will not reroute ${command} to the same peer, will rather wait for a new connection`); + eventBus.once('connected_to_source', () => { // try again + console.log(`got new connection, retrying reroute ${command}`); reroute(); }); return; } - console.log('rerouting '+command+' from '+ws.peer+' to '+next_ws.peer); - ws.assocPendingRequests[tag].responseHandlers.forEach(function(rh){ + console.log(`rerouting ${command} from ${ws.peer} to ${next_ws.peer}`); + ws.assocPendingRequests[tag].responseHandlers.forEach(rh => { sendRequest(next_ws, command, params, bReroutable, rh); }); if (!assocReroutedConnectionsByTag[tag]) @@ -188,31 +187,31 @@ function sendRequest(ws, command, params, bReroutable, responseHandler){ assocReroutedConnectionsByTag[tag].push(next_ws); }); }; - var reroute_timer = !bReroutable ? null : setTimeout(reroute, STALLED_TIMEOUT); - var cancel_timer = bReroutable ? null : setTimeout(function(){ - ws.assocPendingRequests[tag].responseHandlers.forEach(function(rh){ + const reroute_timer = !bReroutable ? null : setTimeout(reroute, STALLED_TIMEOUT); + const cancel_timer = bReroutable ? null : setTimeout(() => { + ws.assocPendingRequests[tag].responseHandlers.forEach(rh => { rh(ws, request, {error: "[internal] response timeout"}); }); delete ws.assocPendingRequests[tag]; }, RESPONSE_TIMEOUT); ws.assocPendingRequests[tag] = { - request: request, + request, responseHandlers: [responseHandler], - reroute: reroute, - reroute_timer: reroute_timer, - cancel_timer: cancel_timer + reroute, + reroute_timer, + cancel_timer }; sendMessage(ws, 'request', content); } } function handleResponse(ws, tag, response){ - var pendingRequest = ws.assocPendingRequests[tag]; + const pendingRequest = ws.assocPendingRequests[tag]; if (!pendingRequest) // was canceled due to timeout or rerouted and answered by another peer //throw "no req by tag "+tag; - return console.log("no req by tag "+tag); - pendingRequest.responseHandlers.forEach(function(responseHandler){ - process.nextTick(function(){ + return console.log(`no req by tag ${tag}`); + pendingRequest.responseHandlers.forEach(responseHandler => { + process.nextTick(() => { responseHandler(ws, pendingRequest.request, response); }); }); @@ -223,11 +222,11 @@ function handleResponse(ws, tag, response){ // if the request was rerouted, cancel all other pending requests if (assocReroutedConnectionsByTag[tag]){ - assocReroutedConnectionsByTag[tag].forEach(function(client){ - if (client.assocPendingRequests[tag]){ - clearTimeout(client.assocPendingRequests[tag].reroute_timer); - clearTimeout(client.assocPendingRequests[tag].cancel_timer); - delete client.assocPendingRequests[tag]; + assocReroutedConnectionsByTag[tag].forEach(({assocPendingRequests}) => { + if (assocPendingRequests[tag]){ + clearTimeout(assocPendingRequests[tag].reroute_timer); + clearTimeout(assocPendingRequests[tag].cancel_timer); + delete assocPendingRequests[tag]; } }); delete assocReroutedConnectionsByTag[tag]; @@ -236,8 +235,8 @@ function handleResponse(ws, tag, response){ function cancelRequestsOnClosedConnection(ws){ console.log("websocket closed, will complete all outstanding requests"); - for (var tag in ws.assocPendingRequests){ - var pendingRequest = ws.assocPendingRequests[tag]; + for (const tag in ws.assocPendingRequests){ + const pendingRequest = ws.assocPendingRequests[tag]; clearTimeout(pendingRequest.reroute_timer); clearTimeout(pendingRequest.cancel_timer); if (pendingRequest.reroute){ // reroute immediately, not waiting for STALLED_TIMEOUT @@ -246,7 +245,7 @@ function cancelRequestsOnClosedConnection(ws){ // we still keep ws.assocPendingRequests[tag] because we'll need it when we find a peer to reroute to } else{ - pendingRequest.responseHandlers.forEach(function(rh){ + pendingRequest.responseHandlers.forEach(rh => { rh(ws, pendingRequest.request, {error: "[internal] connection closed"}); }); delete ws.assocPendingRequests[tag]; @@ -260,24 +259,24 @@ function cancelRequestsOnClosedConnection(ws){ // peers function findNextPeer(ws, handleNextPeer){ - tryFindNextPeer(ws, function(next_ws){ + tryFindNextPeer(ws, next_ws => { if (next_ws) return handleNextPeer(next_ws); - var peer = ws ? ws.peer : '[none]'; - console.log('findNextPeer after '+peer+' found no appropriate peer, will wait for a new connection'); - eventBus.once('connected_to_source', function(new_ws){ - console.log('got new connection, retrying findNextPeer after '+peer); + const peer = ws ? ws.peer : '[none]'; + console.log(`findNextPeer after ${peer} found no appropriate peer, will wait for a new connection`); + eventBus.once('connected_to_source', new_ws => { + console.log(`got new connection, retrying findNextPeer after ${peer}`); findNextPeer(ws, handleNextPeer); }); }); } function tryFindNextPeer(ws, handleNextPeer){ - var arrOutboundSources = arrOutboundPeers.filter(function(outbound_ws){ return outbound_ws.bSource; }); - var len = arrOutboundSources.length; + const arrOutboundSources = arrOutboundPeers.filter(({bSource}) => bSource); + const len = arrOutboundSources.length; if (len > 0){ - var peer_index = arrOutboundSources.indexOf(ws); // -1 if it is already disconnected by now, or if it is inbound peer, or if it is null - var next_peer_index = (peer_index === -1) ? getRandomInt(0, len-1) : ((peer_index+1)%len); + const peer_index = arrOutboundSources.indexOf(ws); // -1 if it is already disconnected by now, or if it is inbound peer, or if it is null + const next_peer_index = (peer_index === -1) ? getRandomInt(0, len-1) : ((peer_index+1)%len); handleNextPeer(arrOutboundSources[next_peer_index]); } else @@ -289,25 +288,25 @@ function getRandomInt(min, max) { } function findRandomInboundPeer(handleInboundPeer){ - var arrInboundSources = wss.clients.filter(function(inbound_ws){ return inbound_ws.bSource; }); + const arrInboundSources = wss.clients.filter(({bSource}) => bSource); if (arrInboundSources.length === 0) return handleInboundPeer(null); - var arrInboundHosts = arrInboundSources.map(function(ws){ return ws.host; }); + const arrInboundHosts = arrInboundSources.map(({host}) => host); // filter only those inbound peers that are reversible db.query( - "SELECT peer_host FROM peer_host_urls JOIN peer_hosts USING(peer_host) \n\ - WHERE is_active=1 AND peer_host IN(?) \n\ - AND (count_invalid_joints/count_new_good_joints { + console.log(`${rows.length} inbound peers`); if (rows.length === 0) return handleInboundPeer(null); - var host = rows[0].peer_host; - console.log("selected inbound peer "+host); - var ws = arrInboundSources.filter(function(ws){ return (ws.host === host); })[0]; + const host = rows[0].peer_host; + console.log(`selected inbound peer ${host}`); + const ws = arrInboundSources.filter(ws => ws.host === host)[0]; if (!ws) throw Error("inbound ws not found"); handleInboundPeer(ws); @@ -316,20 +315,20 @@ function findRandomInboundPeer(handleInboundPeer){ } function checkIfHaveEnoughOutboundPeersAndAdd(){ - var arrOutboundPeerUrls = arrOutboundPeers.map(function(ws){ return ws.peer; }); + const arrOutboundPeerUrls = arrOutboundPeers.map(({peer}) => peer); db.query( "SELECT peer FROM peers JOIN peer_hosts USING(peer_host) \n\ WHERE count_new_good_joints>0 AND count_invalid_joints/count_new_good_joints 0) ? arrOutboundPeerUrls : null], - function(rows){ - var count_good_peers = rows.length; + rows => { + const count_good_peers = rows.length; if (count_good_peers >= conf.MIN_COUNT_GOOD_PEERS) return; if (count_good_peers === 0) // nobody trusted enough to ask for new peers, can't do anything return; - var arrGoodPeerUrls = rows.map(function(row){ return row.peer; }); - for (var i=0; i peer); + for (let i=0; i { if (assocConnectingOutboundWebsockets[url]){ - console.log('abandoning connection to '+url+' due to timeout'); + console.log(`abandoning connection to ${url} due to timeout`); delete assocConnectingOutboundWebsockets[url]; // after this, new connection attempts will be allowed to the wire, but this one can still succeed. See the check for duplicates below. } }, 5000); ws.setMaxListeners(20); // avoid warning ws.once('open', function onWsOpen() { - breadcrumbs.add('connected to '+url); + breadcrumbs.add(`connected to ${url}`); delete assocConnectingOutboundWebsockets[url]; ws.assocPendingRequests = {}; ws.assocInPreparingResponse = {}; if (!ws.url) throw Error("no url on ws"); - if (ws.url !== url && ws.url !== url + "/") // browser implementatin of Websocket might add / - throw Error("url is different: "+ws.url); - var another_ws_to_same_peer = getOutboundPeerWsByUrl(url); + if (ws.url !== url && ws.url !== `${url}/`) // browser implementatin of Websocket might add / + throw Error(`url is different: ${ws.url}`); + const another_ws_to_same_peer = getOutboundPeerWsByUrl(url); if (another_ws_to_same_peer){ // duplicate connection. May happen if we abondoned a connection attempt after timeout but it still succeeded while we opened another connection - console.log('already have a connection to '+url+', will keep the old one and close the duplicate'); + console.log(`already have a connection to ${url}, will keep the old one and close the duplicate`); ws.close(1000, 'duplicate connection'); if (onOpen) onOpen(null, another_ws_to_same_peer); @@ -381,7 +380,7 @@ function connectToPeer(url, onOpen) { ws.host = getHostByPeer(ws.peer); ws.bOutbound = true; ws.last_ts = Date.now(); - console.log('connected to '+url+", host "+ws.host); + console.log(`connected to ${url}, host ${ws.host}`); arrOutboundPeers.push(ws); sendVersion(ws); if (conf.myUrl) // I can listen too, this is my url to connect to @@ -391,11 +390,11 @@ function connectToPeer(url, onOpen) { if (onOpen) onOpen(null, ws); eventBus.emit('connected', ws); - eventBus.emit('open-'+url); + eventBus.emit(`open-${url}`); }); ws.on('close', function onWsClose() { - var i = arrOutboundPeers.indexOf(ws); - console.log('close event, removing '+i+': '+url); + const i = arrOutboundPeers.indexOf(ws); + console.log(`close event, removing ${i}: ${url}`); if (i !== -1) arrOutboundPeers.splice(i, 1); cancelRequestsOnClosedConnection(ws); @@ -404,13 +403,13 @@ function connectToPeer(url, onOpen) { }); ws.on('error', function onWsError(e){ delete assocConnectingOutboundWebsockets[url]; - console.log("error from server "+url+": "+e); - var err = e.toString(); + console.log(`error from server ${url}: ${e}`); + const err = e.toString(); // !ws.bOutbound means not connected yet. This is to distinguish connection errors from later errors that occur on open connection if (!ws.bOutbound && onOpen) onOpen(err); if (!ws.bOutbound) - eventBus.emit('open-'+url, err); + eventBus.emit(`open-${url}`, err); }); ws.on('message', onWebsocketMessage); console.log('connectToPeer done'); @@ -421,26 +420,26 @@ function addOutboundPeers(multiplier){ multiplier = 1; if (multiplier >= 32) // limit recursion return; - var order_by = (multiplier <= 4) ? "count_new_good_joints DESC" : db.getRandom(); // don't stick to old peers with most accumulated good joints - var arrOutboundPeerUrls = arrOutboundPeers.map(function(ws){ return ws.peer; }); - var arrInboundHosts = wss.clients.map(function(ws){ return ws.host; }); - var max_new_outbound_peers = Math.min(conf.MAX_OUTBOUND_CONNECTIONS-arrOutboundPeerUrls.length, 5); // having too many connections being opened creates odd delays in db functions + const order_by = (multiplier <= 4) ? "count_new_good_joints DESC" : db.getRandom(); // don't stick to old peers with most accumulated good joints + const arrOutboundPeerUrls = arrOutboundPeers.map(({peer}) => peer); + const arrInboundHosts = wss.clients.map(({host}) => host); + const max_new_outbound_peers = Math.min(conf.MAX_OUTBOUND_CONNECTIONS-arrOutboundPeerUrls.length, 5); // having too many connections being opened creates odd delays in db functions if (max_new_outbound_peers <= 0) return; db.query( - "SELECT peer \n\ - FROM peers \n\ - JOIN peer_hosts USING(peer_host) \n\ - LEFT JOIN peer_host_urls ON peer=url AND is_active=1 \n\ - WHERE (count_invalid_joints/count_new_good_joints 0) ? "AND peer NOT IN("+db.escape(arrOutboundPeerUrls)+") \n" : "")+"\n\ - "+((arrInboundHosts.length > 0) ? "AND (peer_host_urls.peer_host IS NULL OR peer_host_urls.peer_host NOT IN("+db.escape(arrInboundHosts)+")) \n": "")+"\n\ - AND is_self=0 \n\ - ORDER BY "+order_by+" LIMIT ?", + `SELECT peer \n\ + FROM peers \n\ + JOIN peer_hosts USING(peer_host) \n\ + LEFT JOIN peer_host_urls ON peer=url AND is_active=1 \n\ + WHERE (count_invalid_joints/count_new_good_joints 0) ? `AND peer NOT IN(${db.escape(arrOutboundPeerUrls)}) \n` : ""}\n\ + ${(arrInboundHosts.length > 0) ? `AND (peer_host_urls.peer_host IS NULL OR peer_host_urls.peer_host NOT IN(${db.escape(arrInboundHosts)})) \n`: ""}\n\ + AND is_self=0 \n\ + ORDER BY ${order_by} LIMIT ?`, [conf.MAX_TOLERATED_INVALID_RATIO*multiplier, max_new_outbound_peers], - function(rows){ - for (var i=0; i { + for (let i=0; i { if (onDone) onDone(); }); @@ -469,16 +468,16 @@ function addPeer(peer){ if (assocKnownPeers[peer]) return; assocKnownPeers[peer] = true; - var host = getHostByPeer(peer); - addPeerHost(host, function(){ - console.log("will insert peer "+peer); - db.query("INSERT "+db.getIgnore()+" INTO peers (peer_host, peer) VALUES (?,?)", [host, peer]); + const host = getHostByPeer(peer); + addPeerHost(host, () => { + console.log(`will insert peer ${peer}`); + db.query(`INSERT ${db.getIgnore()} INTO peers (peer_host, peer) VALUES (?,?)`, [host, peer]); }); } function getOutboundPeerWsByUrl(url){ - console.log("outbound peers: "+arrOutboundPeers.map(function(o){ return o.peer; }).join(", ")); - for (var i=0; i peer).join(", ")}`); + for (let i=0; i {}; url = url.toLowerCase(); - var ws = getOutboundPeerWsByUrl(url); + let ws = getOutboundPeerWsByUrl(url); if (ws) return onOpen(null, ws); // check if we are already connecting to the peer ws = assocConnectingOutboundWebsockets[url]; if (ws){ // add second event handler - breadcrumbs.add('already connecting to '+url); - return eventBus.once('open-'+url, function secondOnOpen(err){ - console.log('second open '+url+", err="+err); + breadcrumbs.add(`already connecting to ${url}`); + return eventBus.once(`open-${url}`, function secondOnOpen(err){ + console.log(`second open ${url}, err=${err}`); if (err) return onOpen(err); if (ws.readyState === ws.OPEN) @@ -521,7 +520,7 @@ function findOutboundPeerOrConnect(url, onOpen){ } }); } - console.log("will connect to "+url); + console.log(`will connect to ${url}`); connectToPeer(url, onOpen); } @@ -530,7 +529,7 @@ function purgePeerEvents(){ return; } console.log('will purge peer events'); - db.query("DELETE FROM peer_events WHERE event_date <= datetime('now', '-3 day')", function() { + db.query("DELETE FROM peer_events WHERE event_date <= datetime('now', '-3 day')", () => { console.log("deleted some old peer_events"); }); } @@ -539,29 +538,29 @@ function purgeDeadPeers(){ if (conf.storage !== 'sqlite') return; console.log('will purge dead peers'); - var arrOutboundPeerUrls = arrOutboundPeers.map(function(ws){ return ws.peer; }); - db.query("SELECT rowid, "+db.getUnixTimestamp('event_date')+" AS ts FROM peer_events ORDER BY rowid DESC LIMIT 1", function(lrows){ + const arrOutboundPeerUrls = arrOutboundPeers.map(({peer}) => peer); + db.query(`SELECT rowid, ${db.getUnixTimestamp('event_date')} AS ts FROM peer_events ORDER BY rowid DESC LIMIT 1`, lrows => { if (lrows.length === 0) return; - var last_rowid = lrows[0].rowid; - var last_event_ts = lrows[0].ts; - db.query("SELECT peer, peer_host FROM peers", function(rows){ - async.eachSeries(rows, function(row, cb){ - if (arrOutboundPeerUrls.indexOf(row.peer) >= 0) + const last_rowid = lrows[0].rowid; + const last_event_ts = lrows[0].ts; + db.query("SELECT peer, peer_host FROM peers", rows => { + async.eachSeries(rows, ({peer, peer_host}, cb) => { + if (arrOutboundPeerUrls.indexOf(peer) >= 0) return cb(); db.query( - "SELECT MAX(rowid) AS max_rowid, MAX("+db.getUnixTimestamp('event_date')+") AS max_event_ts FROM peer_events WHERE peer_host=?", - [row.peer_host], - function(mrows){ - var max_rowid = mrows[0].max_rowid || 0; - var max_event_ts = mrows[0].max_event_ts || 0; - var count_other_events = last_rowid - max_rowid; - var days_since_last_event = (last_event_ts - max_event_ts)/24/3600; + `SELECT MAX(rowid) AS max_rowid, MAX(${db.getUnixTimestamp('event_date')}) AS max_event_ts FROM peer_events WHERE peer_host=?`, + [peer_host], + mrows => { + const max_rowid = mrows[0].max_rowid || 0; + const max_event_ts = mrows[0].max_event_ts || 0; + const count_other_events = last_rowid - max_rowid; + const days_since_last_event = (last_event_ts - max_event_ts)/24/3600; if (count_other_events < 20000 || days_since_last_event < 7) return cb(); - console.log('peer '+row.peer+' is dead, will delete'); - db.query("DELETE FROM peers WHERE peer=?", [row.peer], function(){ - delete assocKnownPeers[row.peer]; + console.log(`peer ${peer} is dead, will delete`); + db.query("DELETE FROM peers WHERE peer=?", [peer], () => { + delete assocKnownPeers[peer]; cb(); }); } @@ -577,44 +576,44 @@ function requestPeers(ws){ function handleNewPeers(ws, request, arrPeerUrls){ if (arrPeerUrls.error) - return console.log('get_peers failed: '+arrPeerUrls.error); + return console.log(`get_peers failed: ${arrPeerUrls.error}`); if (!Array.isArray(arrPeerUrls)) return sendError(ws, "peer urls is not an array"); - var arrQueries = []; - for (var i=0; i 2*HEARTBEAT_TIMEOUT); + const bJustResumed = (typeof window !== 'undefined' && window && window.cordova && Date.now() - last_hearbeat_wake_ts > 2*HEARTBEAT_TIMEOUT); last_hearbeat_wake_ts = Date.now(); - wss.clients.concat(arrOutboundPeers).forEach(function(ws){ + wss.clients.concat(arrOutboundPeers).forEach(ws => { if (ws.bSleeping) return; - var elapsed_since_last_received = Date.now() - ws.last_ts; + const elapsed_since_last_received = Date.now() - ws.last_ts; if (elapsed_since_last_received < HEARTBEAT_TIMEOUT) return; if (!ws.last_sent_heartbeat_ts || bJustResumed){ ws.last_sent_heartbeat_ts = Date.now(); return sendRequest(ws, 'heartbeat', null, false, handleHeartbeatResponse); } - var elapsed_since_last_sent_heartbeat = Date.now() - ws.last_sent_heartbeat_ts; + const elapsed_since_last_sent_heartbeat = Date.now() - ws.last_sent_heartbeat_ts; if (elapsed_since_last_sent_heartbeat < HEARTBEAT_RESPONSE_TIMEOUT) return; - console.log('will disconnect peer '+ws.peer+' who was silent for '+elapsed_since_last_received+'ms'); + console.log(`will disconnect peer ${ws.peer} who was silent for ${elapsed_since_last_received}ms`); ws.close(1000, "lost connection"); }); } @@ -629,28 +628,27 @@ function handleHeartbeatResponse(ws, request, response){ function requestFromLightVendor(command, params, responseHandler){ if (!exports.light_vendor_url){ console.log("light_vendor_url not set yet"); - return setTimeout(function(){ + return setTimeout(() => { requestFromLightVendor(command, params, responseHandler); }, 1000); } - findOutboundPeerOrConnect(exports.light_vendor_url, function(err, ws){ + findOutboundPeerOrConnect(exports.light_vendor_url, (err, ws) => { if (err) - return responseHandler(null, null, {error: "[connect to light vendor failed]: "+err}); + return responseHandler(null, null, {error: `[connect to light vendor failed]: ${err}`}); sendRequest(ws, command, params, false, responseHandler); }); } function printConnectionStatus(){ - console.log(wss.clients.length+" incoming connections, "+arrOutboundPeers.length+" outgoing connections, "+ - Object.keys(assocConnectingOutboundWebsockets).length+" outgoing connections being opened"); + console.log(`${wss.clients.length} incoming connections, ${arrOutboundPeers.length} outgoing connections, ${Object.keys(assocConnectingOutboundWebsockets).length} outgoing connections being opened`); } function subscribe(ws){ ws.subscription_id = crypto.randomBytes(30).toString("base64"); // this is to detect self-connect - storage.readLastMainChainIndex(function(last_mci){ - sendRequest(ws, 'subscribe', {subscription_id: ws.subscription_id, last_mci: last_mci}, false, function(ws, request, response){ + storage.readLastMainChainIndex(last_mci => { + sendRequest(ws, 'subscribe', {subscription_id: ws.subscription_id, last_mci}, false, (ws, request, {error}) => { delete ws.subscription_id; - if (response.error) + if (error) return; ws.bSource = true; eventBus.emit('connected_to_source', ws); @@ -662,22 +660,22 @@ function subscribe(ws){ // sent as justsaying or as response to a request function sendJoint(ws, objJoint, tag) { - console.log('sending joint identified by unit ' + objJoint.unit.unit + ' to', ws.peer); + console.log(`sending joint identified by unit ${objJoint.unit.unit} to`, ws.peer); tag ? sendResponse(ws, tag, {joint: objJoint}) : sendJustsaying(ws, 'joint', objJoint); } // sent by light clients to their vendors function postJointToLightVendor(objJoint, handleResponse) { - console.log('posing joint identified by unit ' + objJoint.unit.unit + ' to light vendor'); - requestFromLightVendor('post_joint', objJoint, function(ws, request, response){ + console.log(`posing joint identified by unit ${objJoint.unit.unit} to light vendor`); + requestFromLightVendor('post_joint', objJoint, (ws, request, response) => { handleResponse(response); }); } function sendFreeJoints(ws) { - storage.readFreeJoints(function(objJoint){ + storage.readFreeJoints(objJoint => { sendJoint(ws, objJoint); - }, function(){ + }, () => { sendJustsaying(ws, 'free_joints_end', null); }); } @@ -685,22 +683,22 @@ function sendFreeJoints(ws) { function sendJointsSinceMci(ws, mci) { joint_storage.readJointsSinceMci( mci, - function(objJoint){ + objJoint => { sendJoint(ws, objJoint); }, - function(){ + () => { sendJustsaying(ws, 'free_joints_end', null); } ); } function requestFreeJointsFromAllOutboundPeers(){ - for (var i=0; i { sendJustsaying(ws, 'refresh', last_mci); }); } @@ -709,44 +707,44 @@ function rerequestLostJoints(){ //console.log("rerequestLostJoints"); if (bCatchingUp) return; - joint_storage.findLostJoints(function(arrUnits){ + joint_storage.findLostJoints(arrUnits => { console.log("lost units", arrUnits); - tryFindNextPeer(null, function(ws){ + tryFindNextPeer(null, ws => { if (!ws) return; - console.log("found next peer "+ws.peer); - requestJoints(ws, arrUnits.filter(function(unit){ return (!assocUnitsInWork[unit] && !havePendingJointRequest(unit)); })); + console.log(`found next peer ${ws.peer}`); + requestJoints(ws, arrUnits.filter(unit => !assocUnitsInWork[unit] && !havePendingJointRequest(unit))); }); }); } function requestNewMissingJoints(ws, arrUnits){ - var arrNewUnits = []; + let arrNewUnits = []; async.eachSeries( arrUnits, - function(unit, cb){ + (unit, cb) => { if (assocUnitsInWork[unit]) return cb(); if (havePendingJointRequest(unit)){ - console.log("unit "+unit+" was already requested"); + console.log(`unit ${unit} was already requested`); return cb(); } joint_storage.checkIfNewUnit(unit, { - ifNew: function(){ + ifNew() { arrNewUnits.push(unit); cb(); }, - ifKnown: function(){console.log("known"); cb();}, // it has just been handled - ifKnownUnverified: function(){console.log("known unverified"); cb();}, // I was already waiting for it - ifKnownBad: function(error){ - throw Error("known bad "+unit+": "+error); + ifKnown() {console.log("known"); cb();}, // it has just been handled + ifKnownUnverified() {console.log("known unverified"); cb();}, // I was already waiting for it + ifKnownBad(error) { + throw Error(`known bad ${unit}: ${error}`); } }); }, - function(){ + () => { //console.log(arrNewUnits.length+" of "+arrUnits.length+" left", assocUnitsInWork); // filter again as something could have changed each time we were paused in checkIfNewUnit - arrNewUnits = arrNewUnits.filter(function(unit){ return (!assocUnitsInWork[unit] && !havePendingJointRequest(unit)); }); + arrNewUnits = arrNewUnits.filter(unit => !assocUnitsInWork[unit] && !havePendingJointRequest(unit)); if (arrNewUnits.length > 0) requestJoints(ws, arrNewUnits); } @@ -756,13 +754,13 @@ function requestNewMissingJoints(ws, arrUnits){ function requestJoints(ws, arrUnits) { if (arrUnits.length === 0) return; - arrUnits.forEach(function(unit){ + arrUnits.forEach(unit => { if (assocRequestedUnits[unit]){ - var diff = Date.now() - assocRequestedUnits[unit]; + const diff = Date.now() - assocRequestedUnits[unit]; // since response handlers are called in nextTick(), there is a period when the pending request is already cleared but the response // handler is not yet called, hence assocRequestedUnits[unit] not yet cleared if (diff <= STALLED_TIMEOUT) - return console.log("unit "+unit+" already requested "+diff+" ms ago, assocUnitsInWork="+assocUnitsInWork[unit]); + return console.log(`unit ${unit} already requested ${diff} ms ago, assocUnitsInWork=${assocUnitsInWork[unit]}`); // throw new Error("unit "+unit+" already requested "+diff+" ms ago, assocUnitsInWork="+assocUnitsInWork[unit]); } if (ws.readyState === ws.OPEN) @@ -772,22 +770,22 @@ function requestJoints(ws, arrUnits) { }); } -function handleResponseToJointRequest(ws, request, response){ - delete assocRequestedUnits[request.params]; - if (!response.joint){ - var unit = request.params; - if (response.joint_not_found === unit){ +function handleResponseToJointRequest(ws, {params}, {joint, joint_not_found}) { + delete assocRequestedUnits[params]; + if (!joint){ + var unit = params; + if (joint_not_found === unit){ if (conf.bLight) // we trust the light vendor that if it doesn't know about the unit after 1 day, it doesn't exist - db.query("DELETE FROM unhandled_private_payments WHERE unit=? AND creation_date<"+db.addTime('-1 DAY'), [unit]); + db.query(`DELETE FROM unhandled_private_payments WHERE unit=? AND creation_date<${db.addTime('-1 DAY')}`, [unit]); if (!bCatchingUp) - return console.log("unit "+unit+" does not exist"); // if it is in unhandled_joints, it'll be deleted in 1 hour + return console.log(`unit ${unit} does not exist`); // if it is in unhandled_joints, it'll be deleted in 1 hour // return purgeDependenciesAndNotifyPeers(unit, "unit "+unit+" does not exist"); - db.query("SELECT 1 FROM hash_tree_balls WHERE unit=?", [unit], function(rows){ - if (rows.length === 0) - return console.log("unit "+unit+" does not exist (catching up)"); + db.query("SELECT 1 FROM hash_tree_balls WHERE unit=?", [unit], ({length}) => { + if (length === 0) + return console.log(`unit ${unit} does not exist (catching up)`); // return purgeDependenciesAndNotifyPeers(unit, "unit "+unit+" does not exist (catching up)"); - findNextPeer(ws, function(next_ws){ - breadcrumbs.add("found next peer to reroute joint_not_found "+unit+": "+next_ws.peer); + findNextPeer(ws, next_ws => { + breadcrumbs.add(`found next peer to reroute joint_not_found ${unit}: ${next_ws.peer}`); requestJoints(next_ws, [unit]); }); }); @@ -799,12 +797,12 @@ function handleResponseToJointRequest(ws, request, response){ // - when catching up and requesting old joints from random peers, in this case we are pretty sure it should exist return; } - var objJoint = response.joint; + const objJoint = joint; if (!objJoint.unit || !objJoint.unit.unit) return sendError(ws, 'no unit'); var unit = objJoint.unit.unit; - if (request.params !== unit) - return sendError(ws, "I didn't request this unit from you: "+unit); + if (params !== unit) + return sendError(ws, `I didn't request this unit from you: ${unit}`); if (conf.bLight && objJoint.ball && !objJoint.unit.content_hash){ // accept it as unfinished (otherwise we would have to require a proof) delete objJoint.ball; @@ -814,10 +812,10 @@ function handleResponseToJointRequest(ws, request, response){ } function havePendingRequest(command){ - var arrPeers = wss.clients.concat(arrOutboundPeers); - for (var i=0; i { db.query("DELETE FROM dependencies WHERE NOT EXISTS (SELECT * FROM unhandled_joints WHERE unhandled_joints.unit=dependencies.unit)"); }); } function purgeJointAndDependenciesAndNotifyPeers(objJoint, error, onDone){ if (error.indexOf('is not stable in view of your parents') >= 0){ // give it a chance to be retried after adding other units - eventBus.emit('nonfatal_error', "error on unit "+objJoint.unit.unit+": "+error+"; "+JSON.stringify(objJoint), new Error()); + eventBus.emit('nonfatal_error', `error on unit ${objJoint.unit.unit}: ${error}; ${JSON.stringify(objJoint)}`, new Error()); return onDone(); } joint_storage.purgeJointAndDependencies( objJoint, error, - // this callback is called for each dependent unit - function(purged_unit, peer){ - var ws = getPeerWebSocket(peer); + (purged_unit, peer) => { + const ws = getPeerWebSocket(peer); if (ws) - sendErrorResult(ws, purged_unit, "error on (indirect) parent unit "+objJoint.unit.unit+": "+error); + sendErrorResult(ws, purged_unit, `error on (indirect) parent unit ${objJoint.unit.unit}: ${error}`); }, onDone ); @@ -868,66 +865,65 @@ function purgeDependenciesAndNotifyPeers(unit, error, onDone){ joint_storage.purgeDependencies( unit, error, - // this callback is called for each dependent unit - function(purged_unit, peer){ - var ws = getPeerWebSocket(peer); + (purged_unit, peer) => { + const ws = getPeerWebSocket(peer); if (ws) - sendErrorResult(ws, purged_unit, "error on (indirect) parent unit "+unit+": "+error); + sendErrorResult(ws, purged_unit, `error on (indirect) parent unit ${unit}: ${error}`); }, onDone ); } function forwardJoint(ws, objJoint){ - wss.clients.concat(arrOutboundPeers).forEach(function(client) { + wss.clients.concat(arrOutboundPeers).forEach(client => { if (client != ws && client.bSubscribed) sendJoint(client, objJoint); }); } function handleJoint(ws, objJoint, bSaved, callbacks){ - var unit = objJoint.unit.unit; + const unit = objJoint.unit.unit; if (assocUnitsInWork[unit]) return callbacks.ifUnitInWork(); assocUnitsInWork[unit] = true; - var validate = function(){ + const validate = () => { validation.validate(objJoint, { - ifUnitError: function(error){ - console.log(objJoint.unit.unit+" validation failed: "+error); + ifUnitError(error) { + console.log(`${objJoint.unit.unit} validation failed: ${error}`); callbacks.ifUnitError(error); // throw Error(error); - purgeJointAndDependenciesAndNotifyPeers(objJoint, error, function(){ + purgeJointAndDependenciesAndNotifyPeers(objJoint, error, () => { delete assocUnitsInWork[unit]; }); if (ws && error !== 'authentifier verification failed' && !error.match(/bad merkle proof at path/)) writeEvent('invalid', ws.host); if (objJoint.unsigned) - eventBus.emit("validated-"+unit, false); + eventBus.emit(`validated-${unit}`, false); }, - ifJointError: function(error){ + ifJointError(error) { callbacks.ifJointError(error); // throw Error(error); db.query( "INSERT INTO known_bad_joints (joint, json, error) VALUES (?,?,?)", [objectHash.getJointHash(objJoint), JSON.stringify(objJoint), error], - function(){ + () => { delete assocUnitsInWork[unit]; } ); if (ws) writeEvent('invalid', ws.host); if (objJoint.unsigned) - eventBus.emit("validated-"+unit, false); + eventBus.emit(`validated-${unit}`, false); }, - ifTransientError: function(error){ + ifTransientError(error) { throw Error(error); - console.log("############################## transient error "+error); + console.log(`############################## transient error ${error}`); delete assocUnitsInWork[unit]; }, - ifNeedHashTree: function(){ - console.log('need hash tree for unit '+unit); + ifNeedHashTree() { + console.log(`need hash tree for unit ${unit}`); if (objJoint.unsigned) throw Error("ifNeedHashTree() unsigned"); callbacks.ifNeedHashTree(); @@ -935,10 +931,10 @@ function handleJoint(ws, objJoint, bSaved, callbacks){ delete assocUnitsInWork[unit]; }, ifNeedParentUnits: callbacks.ifNeedParentUnits, - ifOk: function(objValidationState, validation_unlock){ + ifOk(objValidationState, validation_unlock) { if (objJoint.unsigned) throw Error("ifOk() unsigned"); - writer.saveJoint(objJoint, objValidationState, null, function(){ + writer.saveJoint(objJoint, objValidationState, null, () => { validation_unlock(); callbacks.ifOk(); if (ws) @@ -948,28 +944,28 @@ function handleJoint(ws, objJoint, bSaved, callbacks){ eventBus.emit('new_joint', objJoint); }); }, - ifOkUnsigned: function(bSerial){ + ifOkUnsigned(bSerial) { if (!objJoint.unsigned) throw Error("ifOkUnsigned() signed"); callbacks.ifOkUnsigned(); - eventBus.emit("validated-"+unit, bSerial); + eventBus.emit(`validated-${unit}`, bSerial); } }); }; joint_storage.checkIfNewJoint(objJoint, { - ifNew: function(){ + ifNew() { bSaved ? callbacks.ifNew() : validate(); }, - ifKnown: function(){ + ifKnown() { callbacks.ifKnown(); delete assocUnitsInWork[unit]; }, - ifKnownBad: function(){ + ifKnownBad() { callbacks.ifKnownBad(); delete assocUnitsInWork[unit]; }, - ifKnownUnverified: function(){ + ifKnownUnverified() { bSaved ? validate() : callbacks.ifKnownUnverified(); } }); @@ -981,26 +977,26 @@ function handlePostedJoint(ws, objJoint, onDone){ if (!objJoint || !objJoint.unit || !objJoint.unit.unit) return onDone('no unit'); - var unit = objJoint.unit.unit; + const unit = objJoint.unit.unit; delete objJoint.unit.main_chain_index; handleJoint(ws, objJoint, false, { - ifUnitInWork: function(){ + ifUnitInWork() { onDone("already handling this unit"); }, - ifUnitError: function(error){ + ifUnitError(error) { onDone(error); }, - ifJointError: function(error){ + ifJointError(error) { onDone(error); }, - ifNeedHashTree: function(){ + ifNeedHashTree() { onDone("need hash tree"); }, - ifNeedParentUnits: function(arrMissingUnits){ + ifNeedParentUnits(arrMissingUnits) { onDone("unknown parents"); }, - ifOk: function(){ + ifOk() { onDone(); // forward to other peers @@ -1009,21 +1005,21 @@ function handlePostedJoint(ws, objJoint, onDone){ delete assocUnitsInWork[unit]; }, - ifOkUnsigned: function(){ + ifOkUnsigned() { delete assocUnitsInWork[unit]; onDone("you can't send unsigned units"); }, - ifKnown: function(){ + ifKnown() { if (objJoint.unsigned) throw Error("known unsigned"); onDone("known"); writeEvent('known_good', ws.host); }, - ifKnownBad: function(){ + ifKnownBad() { onDone("known bad"); writeEvent('known_bad', ws.host); }, - ifKnownUnverified: function(){ // impossible unless the peer also sends this joint by 'joint' justsaying + ifKnownUnverified() { // impossible unless the peer also sends this joint by 'joint' justsaying onDone("known unverified"); delete assocUnitsInWork[unit]; } @@ -1032,37 +1028,37 @@ function handlePostedJoint(ws, objJoint, onDone){ function handleOnlineJoint(ws, objJoint, onDone){ if (!onDone) - onDone = function(){}; - var unit = objJoint.unit.unit; + onDone = () => {}; + const unit = objJoint.unit.unit; delete objJoint.unit.main_chain_index; handleJoint(ws, objJoint, false, { ifUnitInWork: onDone, - ifUnitError: function(error){ + ifUnitError(error) { sendErrorResult(ws, unit, error); onDone(); }, - ifJointError: function(error){ + ifJointError(error) { sendErrorResult(ws, unit, error); onDone(); }, - ifNeedHashTree: function(){ + ifNeedHashTree() { if (!bCatchingUp && !bWaitingForCatchupChain) requestCatchup(ws); // we are not saving the joint so that in case requestCatchup() fails, the joint will be requested again via findLostJoints, // which will trigger another attempt to request catchup onDone(); }, - ifNeedParentUnits: function(arrMissingUnits){ - sendInfo(ws, {unit: unit, info: "unresolved dependencies: "+arrMissingUnits.join(", ")}); - joint_storage.saveUnhandledJointAndDependencies(objJoint, arrMissingUnits, ws.peer, function(){ + ifNeedParentUnits(arrMissingUnits) { + sendInfo(ws, {unit, info: `unresolved dependencies: ${arrMissingUnits.join(", ")}`}); + joint_storage.saveUnhandledJointAndDependencies(objJoint, arrMissingUnits, ws.peer, () => { delete assocUnitsInWork[unit]; }); requestNewMissingJoints(ws, arrMissingUnits); onDone(); }, - ifOk: function(){ - sendResult(ws, {unit: unit, result: 'accepted'}); + ifOk() { + sendResult(ws, {unit, result: 'accepted'}); // forward to other peers if (!bCatchingUp && !conf.bLight) @@ -1074,26 +1070,26 @@ function handleOnlineJoint(ws, objJoint, onDone){ findAndHandleJointsThatAreReady(unit); onDone(); }, - ifOkUnsigned: function(){ + ifOkUnsigned() { delete assocUnitsInWork[unit]; onDone(); }, - ifKnown: function(){ + ifKnown() { if (objJoint.unsigned) throw Error("known unsigned"); - sendResult(ws, {unit: unit, result: 'known'}); + sendResult(ws, {unit, result: 'known'}); writeEvent('known_good', ws.host); onDone(); }, - ifKnownBad: function(){ - sendResult(ws, {unit: unit, result: 'known_bad'}); + ifKnownBad() { + sendResult(ws, {unit, result: 'known_bad'}); writeEvent('known_bad', ws.host); if (objJoint.unsigned) - eventBus.emit("validated-"+unit, false); + eventBus.emit(`validated-${unit}`, false); onDone(); }, - ifKnownUnverified: function(){ - sendResult(ws, {unit: unit, result: 'known_unverified'}); + ifKnownUnverified() { + sendResult(ws, {unit, result: 'known_unverified'}); delete assocUnitsInWork[unit]; onDone(); } @@ -1103,64 +1099,64 @@ function handleOnlineJoint(ws, objJoint, onDone){ function handleSavedJoint(objJoint, creation_ts, peer){ - var unit = objJoint.unit.unit; - var ws = getPeerWebSocket(peer); + const unit = objJoint.unit.unit; + let ws = getPeerWebSocket(peer); if (ws && ws.readyState !== ws.OPEN) ws = null; handleJoint(ws, objJoint, true, { - ifUnitInWork: function(){}, - ifUnitError: function(error){ + ifUnitInWork() {}, + ifUnitError(error) { if (ws) sendErrorResult(ws, unit, error); }, - ifJointError: function(error){ + ifJointError(error) { if (ws) sendErrorResult(ws, unit, error); }, - ifNeedHashTree: function(){ + ifNeedHashTree() { throw Error("handleSavedJoint: need hash tree"); }, - ifNeedParentUnits: function(arrMissingUnits){ - db.query("SELECT 1 FROM archived_joints WHERE unit IN(?) LIMIT 1", [arrMissingUnits], function(rows){ - if (rows.length === 0) - throw Error("unit "+unit+" still has unresolved dependencies: "+arrMissingUnits.join(", ")); - breadcrumbs.add("unit "+unit+" has unresolved dependencies that were archived: "+arrMissingUnits.join(", ")) + ifNeedParentUnits(arrMissingUnits) { + db.query("SELECT 1 FROM archived_joints WHERE unit IN(?) LIMIT 1", [arrMissingUnits], ({length}) => { + if (length === 0) + throw Error(`unit ${unit} still has unresolved dependencies: ${arrMissingUnits.join(", ")}`); + breadcrumbs.add(`unit ${unit} has unresolved dependencies that were archived: ${arrMissingUnits.join(", ")}`) if (ws) requestNewMissingJoints(ws, arrMissingUnits); else - findNextPeer(null, function(next_ws){ + findNextPeer(null, next_ws => { requestNewMissingJoints(next_ws, arrMissingUnits); }); delete assocUnitsInWork[unit]; }); }, - ifOk: function(){ + ifOk() { if (ws) - sendResult(ws, {unit: unit, result: 'accepted'}); + sendResult(ws, {unit, result: 'accepted'}); // forward to other peers if (!bCatchingUp && !conf.bLight && creation_ts > Date.now() - FORWARDING_TIMEOUT) forwardJoint(ws, objJoint); - joint_storage.removeUnhandledJointAndDependencies(unit, function(){ + joint_storage.removeUnhandledJointAndDependencies(unit, () => { delete assocUnitsInWork[unit]; // wake up other saved joints that depend on me findAndHandleJointsThatAreReady(unit); }); }, - ifOkUnsigned: function(){ - joint_storage.removeUnhandledJointAndDependencies(unit, function(){ + ifOkUnsigned() { + joint_storage.removeUnhandledJointAndDependencies(unit, () => { delete assocUnitsInWork[unit]; }); }, // readDependentJointsThatAreReady can read the same joint twice before it's handled. If not new, just ignore (we've already responded to peer). - ifKnown: function(){}, - ifKnownBad: function(){}, - ifNew: function(){ + ifKnown() {}, + ifKnownBad() {}, + ifNew() { // that's ok: may be simultaneously selected by readDependentJointsThatAreReady and deleted by purgeJunkUnhandledJoints when we wake up after sleep delete assocUnitsInWork[unit]; - console.log("new in handleSavedJoint: "+unit); + console.log(`new in handleSavedJoint: ${unit}`); // throw Error("new in handleSavedJoint: "+unit); } }); @@ -1168,9 +1164,9 @@ function handleSavedJoint(objJoint, creation_ts, peer){ function handleLightOnlineJoint(ws, objJoint){ // the lock ensures that we do not overlap with history processing which might also write new joints - mutex.lock(["light_joints"], function(unlock){ - breadcrumbs.add('got light_joints for handleLightOnlineJoint '+objJoint.unit.unit); - handleOnlineJoint(ws, objJoint, function(){ + mutex.lock(["light_joints"], unlock => { + breadcrumbs.add(`got light_joints for handleLightOnlineJoint ${objJoint.unit.unit}`); + handleOnlineJoint(ws, objJoint, () => { breadcrumbs.add('handleLightOnlineJoint done'); unlock(); }); @@ -1187,33 +1183,33 @@ function addWatchedAddress(address){ // if any of the watched addresses are affected, notifies: 1. own UI 2. light clients function notifyWatchers(objJoint, source_ws){ - var objUnit = objJoint.unit; - var arrAddresses = objUnit.authors.map(function(author){ return author.address; }); + const objUnit = objJoint.unit; + const arrAddresses = objUnit.authors.map(({address}) => address); if (!objUnit.messages) // voided unit return; - for (var i=0; i 0){ eventBus.emit("new_my_transactions", [objJoint.unit.unit]); - eventBus.emit("new_my_unit-"+objJoint.unit.unit, objJoint); + eventBus.emit(`new_my_unit-${objJoint.unit.unit}`, objJoint); } else db.query( "SELECT 1 FROM my_addresses WHERE address IN(?) UNION SELECT 1 FROM shared_addresses WHERE shared_address IN(?)", [arrAddresses, arrAddresses], - function(rows){ - if (rows.length > 0){ + ({length}) => { + if (length > 0){ eventBus.emit("new_my_transactions", [objJoint.unit.unit]); - eventBus.emit("new_my_unit-"+objJoint.unit.unit, objJoint); + eventBus.emit(`new_my_unit-${objJoint.unit.unit}`, objJoint); } } ); @@ -1223,12 +1219,12 @@ function notifyWatchers(objJoint, source_ws){ if (objJoint.ball) // already stable, light clients will require a proof return; // this is a new unstable joint, light clients will accept it without proof - db.query("SELECT peer FROM watched_light_addresses WHERE address IN(?)", [arrAddresses], function(rows){ + db.query("SELECT peer FROM watched_light_addresses WHERE address IN(?)", [arrAddresses], rows => { if (rows.length === 0) return; objUnit.timestamp = Math.round(Date.now()/1000); // light clients need timestamp - rows.forEach(function(row){ - var ws = getPeerWebSocket(row.peer); + rows.forEach(({peer}) => { + const ws = getPeerWebSocket(peer); if (ws && ws.readyState === ws.OPEN && ws !== source_ws) sendJoint(ws, objJoint); }); @@ -1241,14 +1237,14 @@ function notifyWatchersAboutStableJoints(mci){ // the event was emitted from inside mysql transaction, make sure it completes so that the changes are visible // If the mci became stable in determineIfStableInLaterUnitsAndUpdateStableMcFlag (rare), write lock is released before the validation commits, // so we might not see this mci as stable yet. Hopefully, it'll complete before light/have_updates roundtrip - mutex.lock(["write"], function(unlock){ + mutex.lock(["write"], unlock => { unlock(); // we don't need to block writes, we requested the lock just to wait that the current write completes notifyLocalWatchedAddressesAboutStableJoints(mci); - console.log("notifyWatchersAboutStableJoints "+mci); + console.log(`notifyWatchersAboutStableJoints ${mci}`); if (mci <= 1) return; - storage.findLastBallMciOfMci(db, mci, function(last_ball_mci){ - storage.findLastBallMciOfMci(db, mci-1, function(prev_last_ball_mci){ + storage.findLastBallMciOfMci(db, mci, last_ball_mci => { + storage.findLastBallMciOfMci(db, mci-1, prev_last_ball_mci => { if (prev_last_ball_mci === last_ball_mci) return; notifyLightClientsAboutStableJoints(prev_last_ball_mci, last_ball_mci); @@ -1269,14 +1265,14 @@ function notifyLightClientsAboutStableJoints(from_mci, to_mci){ SELECT peer FROM units JOIN watched_light_units USING(unit) \n\ WHERE main_chain_index>? AND main_chain_index<=?", [from_mci, to_mci, from_mci, to_mci, from_mci, to_mci], - function(rows){ - rows.forEach(function(row){ - var ws = getPeerWebSocket(row.peer); + rows => { + rows.forEach(({peer}) => { + const ws = getPeerWebSocket(peer); if (ws && ws.readyState === ws.OPEN) sendJustsaying(ws, 'light/have_updates'); }); db.query("DELETE FROM watched_light_units \n\ - WHERE unit IN (SELECT unit FROM units WHERE main_chain_index>? AND main_chain_index<=?)", [from_mci, to_mci], function() { + WHERE unit IN (SELECT unit FROM units WHERE main_chain_index>? AND main_chain_index<=?)", [from_mci, to_mci], () => { }); } @@ -1286,9 +1282,9 @@ function notifyLightClientsAboutStableJoints(from_mci, to_mci){ function notifyLocalWatchedAddressesAboutStableJoints(mci){ function handleRows(rows){ if (rows.length > 0){ - eventBus.emit('my_transactions_became_stable', rows.map(function(row){ return row.unit; })); - rows.forEach(function(row){ - eventBus.emit('my_stable-'+row.unit); + eventBus.emit('my_transactions_became_stable', rows.map(({unit}) => unit)); + rows.forEach(({unit}) => { + eventBus.emit(`my_stable-${unit}`); }); } } @@ -1316,7 +1312,7 @@ function notifyLocalWatchedAddressesAboutStableJoints(mci){ function addLightWatchedAddress(address){ if (!conf.bLight || !exports.light_vendor_url) return; - findOutboundPeerOrConnect(exports.light_vendor_url, function(err, ws){ + findOutboundPeerOrConnect(exports.light_vendor_url, (err, ws) => { if (err) return; sendJustsaying(ws, 'light/new_address_to_watch', address); @@ -1328,46 +1324,46 @@ function flushEvents(forceFlushing) { return; } - var arrQueryParams = []; - var objUpdatedHosts = {}; - peer_events_buffer.forEach(function(event_row){ - var host = event_row.host; - var event = event_row.event; - var event_date = event_row.event_date; + const arrQueryParams = []; + let objUpdatedHosts = {}; + peer_events_buffer.forEach(event_row => { + const host = event_row.host; + const event = event_row.event; + const event_date = event_row.event_date; if (event === 'new_good'){ - var column = "count_"+event+"_joints"; + const column = `count_${event}_joints`; _.set(objUpdatedHosts, [host, column], _.get(objUpdatedHosts, [host, column], 0)+1); } - arrQueryParams.push("(" + db.escape(host) +"," + db.escape(event) + "," + db.getFromUnixTime(event_date) + ")"); + arrQueryParams.push(`(${db.escape(host)},${db.escape(event)},${db.getFromUnixTime(event_date)})`); }); - for (var host in objUpdatedHosts) { - var columns_obj = objUpdatedHosts[host]; - var sql_columns_updates = []; - for (var column in columns_obj) { - sql_columns_updates.push(column + "=" + column + "+" + columns_obj[column]); + for (const host in objUpdatedHosts) { + const columns_obj = objUpdatedHosts[host]; + const sql_columns_updates = []; + for (const column in columns_obj) { + sql_columns_updates.push(`${column}=${column}+${columns_obj[column]}`); } - db.query("UPDATE peer_hosts SET "+sql_columns_updates.join()+" WHERE peer_host=?", [host]); + db.query(`UPDATE peer_hosts SET ${sql_columns_updates.join()} WHERE peer_host=?`, [host]); } - db.query("INSERT INTO peer_events (peer_host, event, event_date) VALUES "+ arrQueryParams.join()); + db.query(`INSERT INTO peer_events (peer_host, event, event_date) VALUES ${arrQueryParams.join()}`); peer_events_buffer = []; objUpdatedHosts = {}; } function writeEvent(event, host){ if (event === 'invalid' || event === 'nonserial'){ - var column = "count_"+event+"_joints"; - db.query("UPDATE peer_hosts SET "+column+"="+column+"+1 WHERE peer_host=?", [host]); + const column = `count_${event}_joints`; + db.query(`UPDATE peer_hosts SET ${column}=${column}+1 WHERE peer_host=?`, [host]); db.query("INSERT INTO peer_events (peer_host, event) VALUES (?,?)", [host, event]); return; } - var event_date = Math.floor(Date.now() / 1000); - peer_events_buffer.push({host: host, event: event, event_date: event_date}); + const event_date = Math.floor(Date.now() / 1000); + peer_events_buffer.push({host, event, event_date}); flushEvents(); } -setInterval(function(){flushEvents(true)}, 1000 * 60); +setInterval(() => {flushEvents(true)}, 1000 * 60); function findAndHandleJointsThatAreReady(unit){ @@ -1378,7 +1374,7 @@ function findAndHandleJointsThatAreReady(unit){ function comeOnline(){ bCatchingUp = false; coming_online_time = Date.now(); - waitTillIdle(function(){ + waitTillIdle(() => { requestFreeJointsFromAllOutboundPeers(); setTimeout(cleanBadSavedPrivatePayments, 300*1000); }); @@ -1397,7 +1393,7 @@ function waitTillIdle(onIdle){ } else{ bWaitingTillIdle = true; - setTimeout(function(){ + setTimeout(() => { waitTillIdle(onIdle); }, 100); } @@ -1406,7 +1402,7 @@ function waitTillIdle(onIdle){ function broadcastJoint(objJoint){ if (conf.bLight) // the joint was already posted to light vendor before saving return; - wss.clients.concat(arrOutboundPeers).forEach(function(client) { + wss.clients.concat(arrOutboundPeers).forEach(client => { if (client.bSubscribed) sendJoint(client, objJoint); }); @@ -1423,12 +1419,12 @@ function checkCatchupLeftovers(){ UNION \n\ SELECT 1 FROM catchup_chain_balls \n\ LIMIT 1", - function(rows){ - if (rows.length === 0) + ({length}) => { + if (length === 0) return console.log('no leftovers'); console.log('have catchup leftovers from the previous run'); - findNextPeer(null, function(ws){ - console.log('will request leftovers from '+ws.peer); + findNextPeer(null, ws => { + console.log(`will request leftovers from ${ws.peer}`); if (!bCatchingUp && !bWaitingForCatchupChain) requestCatchup(ws); }); @@ -1437,21 +1433,21 @@ function checkCatchupLeftovers(){ } function requestCatchup(ws){ - console.log("will request catchup from "+ws.peer); + console.log(`will request catchup from ${ws.peer}`); eventBus.emit('catching_up_started'); - catchup.purgeHandledBallsFromHashTree(db, function(){ + catchup.purgeHandledBallsFromHashTree(db, () => { db.query( "SELECT hash_tree_balls.unit FROM hash_tree_balls LEFT JOIN units USING(unit) WHERE units.unit IS NULL ORDER BY ball_index", - function(tree_rows){ // leftovers from previous run + tree_rows => { // leftovers from previous run if (tree_rows.length > 0){ bCatchingUp = true; console.log("will request balls found in hash tree"); - requestNewMissingJoints(ws, tree_rows.map(function(tree_row){ return tree_row.unit; })); + requestNewMissingJoints(ws, tree_rows.map(({unit}) => unit)); waitTillHashTreeFullyProcessedAndRequestNext(ws); return; } - db.query("SELECT 1 FROM catchup_chain_balls LIMIT 1", function(chain_rows){ // leftovers from previous run - if (chain_rows.length > 0){ + db.query("SELECT 1 FROM catchup_chain_balls LIMIT 1", ({length}) => { // leftovers from previous run + if (length > 0){ bCatchingUp = true; requestNextHashTree(ws); return; @@ -1463,10 +1459,10 @@ function requestCatchup(ws){ // (will also reset the flag only after the response is fully processed) bWaitingForCatchupChain = true; - storage.readLastStableMcIndex(db, function(last_stable_mci){ - storage.readLastMainChainIndex(function(last_known_mci){ - myWitnesses.readMyWitnesses(function(arrWitnesses){ - var params = {witnesses: arrWitnesses, last_stable_mci: last_stable_mci, last_known_mci: last_known_mci}; + storage.readLastStableMcIndex(db, last_stable_mci => { + storage.readLastMainChainIndex(last_known_mci => { + myWitnesses.readMyWitnesses(arrWitnesses => { + const params = {witnesses: arrWitnesses, last_stable_mci, last_known_mci}; sendRequest(ws, 'catchup', params, true, handleCatchupChain); }, 'wait'); }); @@ -1480,22 +1476,22 @@ function requestCatchup(ws){ function handleCatchupChain(ws, request, response){ if (response.error){ bWaitingForCatchupChain = false; - console.log('catchup request got error response: '+response.error); + console.log(`catchup request got error response: ${response.error}`); // findLostJoints will wake up and trigger another attempt to request catchup return; } - var catchupChain = response; + const catchupChain = response; catchup.processCatchupChain(catchupChain, ws.peer, { - ifError: function(error){ + ifError(error) { bWaitingForCatchupChain = false; sendError(ws, error); }, - ifOk: function(){ + ifOk() { bWaitingForCatchupChain = false; bCatchingUp = true; requestNextHashTree(ws); }, - ifCurrent: function(){ + ifCurrent() { bWaitingForCatchupChain = false; } }); @@ -1507,52 +1503,52 @@ function handleCatchupChain(ws, request, response){ function requestNextHashTree(ws){ eventBus.emit('catchup_next_hash_tree'); - db.query("SELECT ball FROM catchup_chain_balls ORDER BY member_index LIMIT 2", function(rows){ + db.query("SELECT ball FROM catchup_chain_balls ORDER BY member_index LIMIT 2", rows => { if (rows.length === 0) return comeOnline(); if (rows.length === 1){ - db.query("DELETE FROM catchup_chain_balls WHERE ball=?", [rows[0].ball], function(){ + db.query("DELETE FROM catchup_chain_balls WHERE ball=?", [rows[0].ball], () => { comeOnline(); }); return; } - var from_ball = rows[0].ball; - var to_ball = rows[1].ball; + const from_ball = rows[0].ball; + const to_ball = rows[1].ball; // don't send duplicate requests - for (var tag in ws.assocPendingRequests) + for (const tag in ws.assocPendingRequests) if (ws.assocPendingRequests[tag].request.command === 'get_hash_tree'){ console.log("already requested hash tree from this peer"); return; } - sendRequest(ws, 'get_hash_tree', {from_ball: from_ball, to_ball: to_ball}, true, handleHashTree); + sendRequest(ws, 'get_hash_tree', {from_ball, to_ball}, true, handleHashTree); }); } function handleHashTree(ws, request, response){ if (response.error){ - console.log('get_hash_tree got error response: '+response.error); + console.log(`get_hash_tree got error response: ${response.error}`); waitTillHashTreeFullyProcessedAndRequestNext(ws); // after 1 sec, it'll request the same hash tree, likely from another peer return; } - var hashTree = response; + const hashTree = response; catchup.processHashTree(hashTree.balls, { - ifError: function(error){ + ifError(error) { sendError(ws, error); waitTillHashTreeFullyProcessedAndRequestNext(ws); // after 1 sec, it'll request the same hash tree, likely from another peer }, - ifOk: function(){ - requestNewMissingJoints(ws, hashTree.balls.map(function(objBall){ return objBall.unit; })); + ifOk() { + requestNewMissingJoints(ws, hashTree.balls.map(({unit}) => unit)); waitTillHashTreeFullyProcessedAndRequestNext(ws); } }); } function waitTillHashTreeFullyProcessedAndRequestNext(ws){ - setTimeout(function(){ - db.query("SELECT 1 FROM hash_tree_balls LEFT JOIN units USING(unit) WHERE units.unit IS NULL LIMIT 1", function(rows){ - if (rows.length === 0){ - findNextPeer(ws, function(next_ws){ + setTimeout(() => { + db.query("SELECT 1 FROM hash_tree_balls LEFT JOIN units USING(unit) WHERE units.unit IS NULL LIMIT 1", ({length}) => { + if (length === 0){ + findNextPeer(ws, next_ws => { requestNextHashTree(next_ws); }); } @@ -1569,17 +1565,17 @@ function waitTillHashTreeFullyProcessedAndRequestNext(ws){ function sendPrivatePaymentToWs(ws, arrChains){ // each chain is sent as separate ws message - arrChains.forEach(function(arrPrivateElements){ + arrChains.forEach(arrPrivateElements => { sendJustsaying(ws, 'private_payment', arrPrivateElements); }); } // sends multiple private payloads and their corresponding chains function sendPrivatePayment(peer, arrChains){ - var ws = getPeerWebSocket(peer); + const ws = getPeerWebSocket(peer); if (ws) return sendPrivatePaymentToWs(ws, arrChains); - findOutboundPeerOrConnect(peer, function(err, ws){ + findOutboundPeerOrConnect(peer, (err, ws) => { if (!err) sendPrivatePaymentToWs(ws, arrChains); }); @@ -1590,17 +1586,17 @@ function handleOnlinePrivatePayment(ws, arrPrivateElements, bViaHub, callbacks){ if (!ValidationUtils.isNonemptyArray(arrPrivateElements)) return callbacks.ifError("private_payment content must be non-empty array"); - var unit = arrPrivateElements[0].unit; - var message_index = arrPrivateElements[0].message_index; - var output_index = arrPrivateElements[0].payload.denomination ? arrPrivateElements[0].output_index : -1; + const unit = arrPrivateElements[0].unit; + const message_index = arrPrivateElements[0].message_index; + const output_index = arrPrivateElements[0].payload.denomination ? arrPrivateElements[0].output_index : -1; - var savePrivatePayment = function(cb){ + const savePrivatePayment = cb => { // we may receive the same unit and message index but different output indexes if recipient and cosigner are on the same device. // in this case, we also receive the same (unit, message_index, output_index) twice - as cosigner and as recipient. That's why IGNORE. db.query( - "INSERT "+db.getIgnore()+" INTO unhandled_private_payments (unit, message_index, output_index, json, peer) VALUES (?,?,?,?,?)", + `INSERT ${db.getIgnore()} INTO unhandled_private_payments (unit, message_index, output_index, json, peer) VALUES (?,?,?,?,?)`, [unit, message_index, output_index, JSON.stringify(arrPrivateElements), bViaHub ? '' : ws.peer], // forget peer if received via hub - function(){ + () => { callbacks.ifQueued(); if (cb) cb(); @@ -1609,7 +1605,7 @@ function handleOnlinePrivatePayment(ws, arrPrivateElements, bViaHub, callbacks){ }; if (conf.bLight && arrPrivateElements.length > 1){ - savePrivatePayment(function(){ + savePrivatePayment(() => { updateLinkProofsOfPrivateChain(arrPrivateElements, unit, message_index, output_index); rerequestLostJointsOfPrivatePayments(); // will request the head element }); @@ -1617,81 +1613,81 @@ function handleOnlinePrivatePayment(ws, arrPrivateElements, bViaHub, callbacks){ } joint_storage.checkIfNewUnit(unit, { - ifKnown: function(){ + ifKnown() { //assocUnitsInWork[unit] = true; privatePayment.validateAndSavePrivatePaymentChain(arrPrivateElements, { - ifOk: function(){ + ifOk() { //delete assocUnitsInWork[unit]; callbacks.ifAccepted(unit); eventBus.emit("new_my_transactions", [unit]); }, - ifError: function(error){ + ifError(error) { //delete assocUnitsInWork[unit]; callbacks.ifValidationError(unit, error); }, - ifWaitingForChain: function(){ + ifWaitingForChain() { savePrivatePayment(); } }); }, - ifNew: function(){ + ifNew() { savePrivatePayment(); // if received via hub, I'm requesting from the same hub, thus telling the hub that this unit contains a private payment for me. // It would be better to request missing joints from somebody else requestNewMissingJoints(ws, [unit]); }, ifKnownUnverified: savePrivatePayment, - ifKnownBad: function(){ + ifKnownBad() { callbacks.ifValidationError(unit, "known bad"); } }); } - + // if unit is undefined, find units that are ready function handleSavedPrivatePayments(unit){ //if (unit && assocUnitsInWork[unit]) // return; - mutex.lock(["saved_private"], function(unlock){ - var sql = unit - ? "SELECT json, peer, unit, message_index, output_index, linked FROM unhandled_private_payments WHERE unit="+db.escape(unit) + mutex.lock(["saved_private"], unlock => { + const sql = unit + ? `SELECT json, peer, unit, message_index, output_index, linked FROM unhandled_private_payments WHERE unit=${db.escape(unit)}` : "SELECT json, peer, unit, message_index, output_index, linked FROM unhandled_private_payments CROSS JOIN units USING(unit)"; - db.query(sql, function(rows){ + db.query(sql, rows => { if (rows.length === 0) return unlock(); - var assocNewUnits = {}; + const assocNewUnits = {}; async.each( // handle different chains in parallel rows, - function(row, cb){ - var arrPrivateElements = JSON.parse(row.json); - var ws = getPeerWebSocket(row.peer); + (row, cb) => { + const arrPrivateElements = JSON.parse(row.json); + let ws = getPeerWebSocket(row.peer); if (ws && ws.readyState !== ws.OPEN) ws = null; - var validateAndSave = function(){ - var objHeadPrivateElement = arrPrivateElements[0]; - var payload_hash = objectHash.getBase64Hash(objHeadPrivateElement.payload); - var key = 'private_payment_validated-'+objHeadPrivateElement.unit+'-'+payload_hash+'-'+row.output_index; + const validateAndSave = () => { + const objHeadPrivateElement = arrPrivateElements[0]; + const payload_hash = objectHash.getBase64Hash(objHeadPrivateElement.payload); + const key = `private_payment_validated-${objHeadPrivateElement.unit}-${payload_hash}-${row.output_index}`; privatePayment.validateAndSavePrivatePaymentChain(arrPrivateElements, { - ifOk: function(){ + ifOk() { if (ws) sendResult(ws, {private_payment_in_unit: row.unit, result: 'accepted'}); if (row.peer) // received directly from a peer, not through the hub eventBus.emit("new_direct_private_chains", [arrPrivateElements]); assocNewUnits[row.unit] = true; deleteHandledPrivateChain(row.unit, row.message_index, row.output_index, cb); - console.log('emit '+key); + console.log(`emit ${key}`); eventBus.emit(key, true); }, - ifError: function(error){ - console.log("validation of priv: "+error); + ifError(error) { + console.log(`validation of priv: ${error}`); // throw Error(error); if (ws) - sendResult(ws, {private_payment_in_unit: row.unit, result: 'error', error: error}); + sendResult(ws, {private_payment_in_unit: row.unit, result: 'error', error}); deleteHandledPrivateChain(row.unit, row.message_index, row.output_index, cb); eventBus.emit(key, false); }, // light only. Means that chain joints (excluding the head) not downloaded yet or not stable yet - ifWaitingForChain: function(){ + ifWaitingForChain() { cb(); } }); @@ -1703,9 +1699,9 @@ function handleSavedPrivatePayments(unit){ validateAndSave(); }, - function(){ + () => { unlock(); - var arrNewUnits = Object.keys(assocNewUnits); + const arrNewUnits = Object.keys(assocNewUnits); if (arrNewUnits.length > 0) eventBus.emit("new_my_transactions", arrNewUnits); } @@ -1715,7 +1711,7 @@ function handleSavedPrivatePayments(unit){ } function deleteHandledPrivateChain(unit, message_index, output_index, cb){ - db.query("DELETE FROM unhandled_private_payments WHERE unit=? AND message_index=? AND output_index=?", [unit, message_index, output_index], function(){ + db.query("DELETE FROM unhandled_private_payments WHERE unit=? AND message_index=? AND output_index=?", [unit, message_index, output_index], () => { cb(); }); } @@ -1725,12 +1721,12 @@ function cleanBadSavedPrivatePayments(){ if (conf.bLight || bCatchingUp) return; db.query( - "SELECT DISTINCT unhandled_private_payments.unit FROM unhandled_private_payments LEFT JOIN units USING(unit) \n\ - WHERE units.unit IS NULL AND unhandled_private_payments.creation_date<"+db.addTime('-1 DAY'), - function(rows){ - rows.forEach(function(row){ - breadcrumbs.add('deleting bad saved private payment '+row.unit); - db.query("DELETE FROM unhandled_private_payments WHERE unit=?", [row.unit]); + `SELECT DISTINCT unhandled_private_payments.unit FROM unhandled_private_payments LEFT JOIN units USING(unit) \n\ + WHERE units.unit IS NULL AND unhandled_private_payments.creation_date<${db.addTime('-1 DAY')}`, + rows => { + rows.forEach(({unit}) => { + breadcrumbs.add(`deleting bad saved private payment ${unit}`); + db.query("DELETE FROM unhandled_private_payments WHERE unit=?", [unit]); }); } ); @@ -1743,11 +1739,11 @@ function rerequestLostJointsOfPrivatePayments(){ return; db.query( "SELECT DISTINCT unhandled_private_payments.unit FROM unhandled_private_payments LEFT JOIN units USING(unit) WHERE units.unit IS NULL", - function(rows){ + rows => { if (rows.length === 0) return; - var arrUnits = rows.map(function(row){ return row.unit; }); - findOutboundPeerOrConnect(exports.light_vendor_url, function(err, ws){ + const arrUnits = rows.map(({unit}) => unit); + findOutboundPeerOrConnect(exports.light_vendor_url, (err, ws) => { if (err) return; requestNewMissingJoints(ws, arrUnits); @@ -1759,35 +1755,35 @@ function rerequestLostJointsOfPrivatePayments(){ // light only function requestUnfinishedPastUnitsOfPrivateChains(arrChains, onDone){ if (!onDone) - onDone = function(){}; - privatePayment.findUnfinishedPastUnitsOfPrivateChains(arrChains, true, function(arrUnits){ + onDone = () => {}; + privatePayment.findUnfinishedPastUnitsOfPrivateChains(arrChains, true, arrUnits => { if (arrUnits.length === 0) return onDone(); - breadcrumbs.add(arrUnits.length+" unfinished past units of private chains"); + breadcrumbs.add(`${arrUnits.length} unfinished past units of private chains`); requestHistoryFor(arrUnits, [], onDone); }); } function requestHistoryFor(arrUnits, arrAddresses, onDone){ if (!onDone) - onDone = function(){}; - myWitnesses.readMyWitnesses(function(arrWitnesses){ - var objHistoryRequest = {witnesses: arrWitnesses}; + onDone = () => {}; + myWitnesses.readMyWitnesses(arrWitnesses => { + const objHistoryRequest = {witnesses: arrWitnesses}; if (arrUnits.length) objHistoryRequest.requested_joints = arrUnits; if (arrAddresses.length) objHistoryRequest.addresses = arrAddresses; - requestFromLightVendor('light/get_history', objHistoryRequest, function(ws, request, response){ + requestFromLightVendor('light/get_history', objHistoryRequest, (ws, request, response) => { if (response.error){ console.log(response.error); return onDone(response.error); } light.processHistory(response, { - ifError: function(err){ + ifError(err) { sendError(ws, err); onDone(err); }, - ifOk: function(){ + ifOk() { onDone(); } }); @@ -1797,9 +1793,9 @@ function requestHistoryFor(arrUnits, arrAddresses, onDone){ function requestProofsOfJointsIfNewOrUnstable(arrUnits, onDone){ if (!onDone) - onDone = function(){}; - storage.filterNewOrUnstableUnits(arrUnits, function(arrNewOrUnstableUnits){ - if (arrNewOrUnstableUnits.length === 0) + onDone = () => {}; + storage.filterNewOrUnstableUnits(arrUnits, ({length}) => { + if (length === 0) return onDone(); requestHistoryFor(arrUnits, [], onDone); }); @@ -1807,15 +1803,15 @@ function requestProofsOfJointsIfNewOrUnstable(arrUnits, onDone){ // light only function requestUnfinishedPastUnitsOfSavedPrivateElements(){ - mutex.lock(['private_chains'], function(unlock){ - db.query("SELECT json FROM unhandled_private_payments", function(rows){ + mutex.lock(['private_chains'], unlock => { + db.query("SELECT json FROM unhandled_private_payments", rows => { eventBus.emit('unhandled_private_payments_left', rows.length); if (rows.length === 0) return unlock(); - breadcrumbs.add(rows.length+" unhandled private payments"); - var arrChains = []; - rows.forEach(function(row){ - var arrPrivateElements = JSON.parse(row.json); + breadcrumbs.add(`${rows.length} unhandled private payments`); + const arrChains = []; + rows.forEach(({json}) => { + const arrPrivateElements = JSON.parse(json); arrChains.push(arrPrivateElements); }); requestUnfinishedPastUnitsOfPrivateChains(arrChains, function onPrivateChainsReceived(err){ @@ -1834,20 +1830,20 @@ function requestUnfinishedPastUnitsOfSavedPrivateElements(){ function checkThatEachChainElementIncludesThePrevious(arrPrivateElements, handleResult){ if (arrPrivateElements.length === 1) // an issue return handleResult(true); - var arrUnits = arrPrivateElements.map(function(objPrivateElement){ return objPrivateElement.unit; }); - requestFromLightVendor('light/get_link_proofs', arrUnits, function(ws, request, response){ + const arrUnits = arrPrivateElements.map(({unit}) => unit); + requestFromLightVendor('light/get_link_proofs', arrUnits, (ws, request, response) => { if (response.error) return handleResult(null); // undefined result - var arrChain = response; + const arrChain = response; if (!ValidationUtils.isNonemptyArray(arrChain)) return handleResult(null); // undefined result light.processLinkProofs(arrUnits, arrChain, { - ifError: function(err){ - console.log("linkproof validation failed: "+err); + ifError(err) { + console.log(`linkproof validation failed: ${err}`); throw Error(err); handleResult(false); }, - ifOk: function(){ + ifOk() { console.log("linkproof validated ok"); handleResult(true); } @@ -1860,29 +1856,28 @@ function updateLinkProofsOfPrivateChain(arrPrivateElements, unit, message_index, if (!conf.bLight) throw Error("not light but updateLinkProofsOfPrivateChain"); if (!onFailure) - onFailure = function(){}; + onFailure = () => {}; if (!onSuccess) - onSuccess = function(){}; - checkThatEachChainElementIncludesThePrevious(arrPrivateElements, function(bLinked){ + onSuccess = () => {}; + checkThatEachChainElementIncludesThePrevious(arrPrivateElements, bLinked => { if (bLinked === null) return onFailure(); if (!bLinked) return deleteHandledPrivateChain(unit, message_index, output_index, onFailure); // the result cannot depend on output_index - db.query("UPDATE unhandled_private_payments SET linked=1 WHERE unit=? AND message_index=?", [unit, message_index], function(){ + db.query("UPDATE unhandled_private_payments SET linked=1 WHERE unit=? AND message_index=?", [unit, message_index], () => { onSuccess(); }); }); } -function initWitnessesIfNecessary(ws, onDone){ - onDone = onDone || function(){}; - myWitnesses.readMyWitnesses(function(arrWitnesses){ - if (arrWitnesses.length > 0) // already have witnesses +function initWitnessesIfNecessary(ws, onDone = () => {}) { + myWitnesses.readMyWitnesses(({length}) => { + if (length > 0) // already have witnesses return onDone(); - sendRequest(ws, 'get_witnesses', null, false, function(ws, request, arrWitnesses){ + sendRequest(ws, 'get_witnesses', null, false, (ws, request, arrWitnesses) => { if (arrWitnesses.error){ - console.log('get_witnesses returned error: '+arrWitnesses.error); + console.log(`get_witnesses returned error: ${arrWitnesses.error}`); return onDone(); } myWitnesses.insertWitnesses(arrWitnesses, onDone); @@ -1894,17 +1889,17 @@ function initWitnessesIfNecessary(ws, onDone){ // hub function sendStoredDeviceMessages(ws, device_address){ - db.query("SELECT message_hash, message FROM device_messages WHERE device_address=? ORDER BY creation_date LIMIT 100", [device_address], function(rows){ - rows.forEach(function(row){ - sendJustsaying(ws, 'hub/message', {message_hash: row.message_hash, message: JSON.parse(row.message)}); + db.query("SELECT message_hash, message FROM device_messages WHERE device_address=? ORDER BY creation_date LIMIT 100", [device_address], rows => { + rows.forEach(({message_hash, message}) => { + sendJustsaying(ws, 'hub/message', {message_hash: message_hash, message: JSON.parse(message)}); }); - sendInfo(ws, rows.length+" messages sent"); + sendInfo(ws, `${rows.length} messages sent`); sendJustsaying(ws, 'hub/message_box_status', (rows.length === 100) ? 'has_more' : 'empty'); }); } function version2int(version){ - var arr = version.split('.'); + const arr = version.split('.'); return arr[0]*10000 + arr[1]*100 + arr[2]*1; } @@ -1916,7 +1911,7 @@ function handleJustsaying(ws, subject, body){ case 'refresh': if (bCatchingUp) return; - var mci = body; + const mci = body; if (ValidationUtils.isNonnegativeInteger(mci)) return sendJointsSinceMci(ws, mci); else @@ -1926,12 +1921,12 @@ function handleJustsaying(ws, subject, body){ if (!body) return; if (body.protocol_version !== constants.version){ - sendError(ws, 'Incompatible versions, mine '+constants.version+', yours '+body.protocol_version); + sendError(ws, `Incompatible versions, mine ${constants.version}, yours ${body.protocol_version}`); ws.close(1000, 'incompatible versions'); return; } if (body.alt !== constants.alt){ - sendError(ws, 'Incompatible alts, mine '+constants.alt+', yours '+body.alt); + sendError(ws, `Incompatible alts, mine ${constants.alt}, yours ${body.alt}`); ws.close(1000, 'incompatible alts'); return; } @@ -1958,21 +1953,21 @@ function handleJustsaying(ws, subject, body){ case 'bugreport': if (!body) return; - if (conf.ignoreBugreportRegexp && new RegExp(conf.ignoreBugreportRegexp).test(body.message+' '+body.exception.toString())) + if (conf.ignoreBugreportRegexp && new RegExp(conf.ignoreBugreportRegexp).test(`${body.message} ${body.exception.toString()}`)) return console.log('ignoring bugreport'); mail.sendBugEmail(body.message, body.exception); break; case 'joint': - var objJoint = body; + const objJoint = body; if (!objJoint || !objJoint.unit || !objJoint.unit.unit) return sendError(ws, 'no unit'); if (objJoint.ball && !storage.isGenesisUnit(objJoint.unit.unit)) return sendError(ws, 'only requested joint can contain a ball'); if (conf.bLight && !ws.bLightVendor) return sendError(ws, "I'm a light client and you are not my vendor"); - db.query("SELECT 1 FROM archived_joints WHERE unit=? AND reason='uncovered'", [objJoint.unit.unit], function(rows){ - if (rows.length > 0) // ignore it as long is it was unsolicited + db.query("SELECT 1 FROM archived_joints WHERE unit=? AND reason='uncovered'", [objJoint.unit.unit], ({length}) => { + if (length > 0) // ignore it as long is it was unsolicited return sendError(ws, "this unit is already known and archived"); // light clients accept the joint without proof, it'll be saved as unconfirmed (non-stable) return conf.bLight ? handleLightOnlineJoint(ws, objJoint) : handleOnlineJoint(ws, objJoint); @@ -1987,19 +1982,19 @@ function handleJustsaying(ws, subject, body){ case 'private_payment': if (!body) return; - var arrPrivateElements = body; + const arrPrivateElements = body; handleOnlinePrivatePayment(ws, arrPrivateElements, false, { - ifError: function(error){ + ifError(error) { sendError(ws, error); }, - ifAccepted: function(unit){ + ifAccepted(unit) { sendResult(ws, {private_payment_in_unit: unit, result: 'accepted'}); eventBus.emit("new_direct_private_chains", [arrPrivateElements]); }, - ifValidationError: function(unit, error){ - sendResult(ws, {private_payment_in_unit: unit, result: 'error', error: error}); + ifValidationError(unit, error) { + sendResult(ws, {private_payment_in_unit: unit, result: 'error', error}); }, - ifQueued: function(){ + ifQueued() { } }); break; @@ -2007,7 +2002,7 @@ function handleJustsaying(ws, subject, body){ case 'my_url': if (!body) return; - var url = body; + const url = body; if (ws.bOutbound) // ignore: if you are outbound, I already know your url break; // inbound only @@ -2017,8 +2012,8 @@ function handleJustsaying(ws, subject, body){ if (url.indexOf('ws://') !== 0 && url.indexOf('wss://') !== 0) // invalid url break; ws.claimed_url = url; - db.query("SELECT creation_date AS latest_url_change_date, url FROM peer_host_urls WHERE peer_host=? ORDER BY creation_date DESC LIMIT 1", [ws.host], function(rows){ - var latest_change = rows[0]; + db.query("SELECT creation_date AS latest_url_change_date, url FROM peer_host_urls WHERE peer_host=? ORDER BY creation_date DESC LIMIT 1", [ws.host], rows => { + const latest_change = rows[0]; if (latest_change && latest_change.url === url) // advertises the same url return; //var elapsed_time = Date.now() - Date.parse(latest_change.latest_url_change_date); @@ -2028,7 +2023,7 @@ function handleJustsaying(ws, subject, body){ // verify it is really your url by connecting to this url, sending a random string through this new connection, // and expecting this same string over existing inbound connection ws.sent_echo_string = crypto.randomBytes(30).toString("base64"); - findOutboundPeerOrConnect(url, function(err, reverse_ws){ + findOutboundPeerOrConnect(url, (err, reverse_ws) => { if (!err) sendJustsaying(reverse_ws, 'want_echo', ws.sent_echo_string); }); @@ -2042,7 +2037,7 @@ function handleJustsaying(ws, subject, body){ // inbound only if (!ws.claimed_url) break; - var reverse_ws = getOutboundPeerWsByUrl(ws.claimed_url); + const reverse_ws = getOutboundPeerWsByUrl(ws.claimed_url); if (!reverse_ws) // no reverse outbound connection break; sendJustsaying(reverse_ws, 'your_echo', echo_string); @@ -2057,12 +2052,12 @@ function handleJustsaying(ws, subject, body){ break; if (ws.sent_echo_string !== echo_string) break; - var outbound_host = getHostByPeer(ws.claimed_url); - var arrQueries = []; - db.addQuery(arrQueries, "INSERT "+db.getIgnore()+" INTO peer_hosts (peer_host) VALUES (?)", [outbound_host]); - db.addQuery(arrQueries, "INSERT "+db.getIgnore()+" INTO peers (peer_host, peer, learnt_from_peer_host) VALUES (?,?,?)", + const outbound_host = getHostByPeer(ws.claimed_url); + const arrQueries = []; + db.addQuery(arrQueries, `INSERT ${db.getIgnore()} INTO peer_hosts (peer_host) VALUES (?)`, [outbound_host]); + db.addQuery(arrQueries, `INSERT ${db.getIgnore()} INTO peers (peer_host, peer, learnt_from_peer_host) VALUES (?,?,?)`, [outbound_host, ws.claimed_url, ws.host]); - db.addQuery(arrQueries, "UPDATE peer_host_urls SET is_active=NULL, revocation_date="+db.getNow()+" WHERE peer_host=?", [ws.host]); + db.addQuery(arrQueries, `UPDATE peer_host_urls SET is_active=NULL, revocation_date=${db.getNow()} WHERE peer_host=?`, [ws.host]); db.addQuery(arrQueries, "INSERT INTO peer_host_urls (peer_host, url) VALUES (?,?)", [ws.host, ws.claimed_url]); async.series(arrQueries); ws.sent_echo_string = null; @@ -2075,7 +2070,7 @@ function handleJustsaying(ws, subject, body){ return; if (!conf.bServeAsHub) return sendError(ws, "I'm not a hub"); - var objLogin = body; + const objLogin = body; if (objLogin.challenge !== ws.challenge) return sendError(ws, "wrong challenge"); if (!objLogin.pubkey || !objLogin.signature) @@ -2088,16 +2083,16 @@ function handleJustsaying(ws, subject, body){ return sendError(ws, "wrong signature"); ws.device_address = objectHash.getDeviceAddress(objLogin.pubkey); // after this point the device is authenticated and can send further commands - var finishLogin = function(){ + const finishLogin = () => { ws.bLoginComplete = true; if (ws.onLoginComplete){ ws.onLoginComplete(); delete ws.onLoginComplete; } }; - db.query("SELECT 1 FROM devices WHERE device_address=?", [ws.device_address], function(rows){ - if (rows.length === 0) - db.query("INSERT INTO devices (device_address, pubkey) VALUES (?,?)", [ws.device_address, objLogin.pubkey], function(){ + db.query("SELECT 1 FROM devices WHERE device_address=?", [ws.device_address], ({length}) => { + if (length === 0) + db.query("INSERT INTO devices (device_address, pubkey) VALUES (?,?)", [ws.device_address, objLogin.pubkey], () => { sendInfo(ws, "address created"); finishLogin(); }); @@ -2126,13 +2121,13 @@ function handleJustsaying(ws, subject, body){ case 'hub/delete': if (!conf.bServeAsHub) return sendError(ws, "I'm not a hub"); - var message_hash = body; + const message_hash = body; if (!message_hash) return sendError(ws, "no message hash"); if (!ws.device_address) return sendError(ws, "please log in first"); - db.query("DELETE FROM device_messages WHERE device_address=? AND message_hash=?", [ws.device_address, message_hash], function(){ - sendInfo(ws, "deleted message "+message_hash); + db.query("DELETE FROM device_messages WHERE device_address=? AND message_hash=?", [ws.device_address, message_hash], () => { + sendInfo(ws, `deleted message ${message_hash}`); }); break; @@ -2160,11 +2155,11 @@ function handleJustsaying(ws, subject, body){ return sendError(ws, "I'm light myself, can't serve you"); if (ws.bOutbound) return sendError(ws, "light clients have to be inbound"); - var address = body; + const address = body; if (!ValidationUtils.isValidAddress(address)) return sendError(ws, "address not valid"); - db.query("INSERT "+db.getIgnore()+" INTO watched_light_addresses (peer, address) VALUES (?,?)", [ws.peer, address], function(){ - sendInfo(ws, "now watching "+address); + db.query(`INSERT ${db.getIgnore()} INTO watched_light_addresses (peer, address) VALUES (?,?)`, [ws.peer, address], () => { + sendInfo(ws, `now watching ${address}`); // check if we already have something on this address db.query( "SELECT unit, is_stable FROM unit_authors JOIN units USING(unit) WHERE address=? \n\ @@ -2172,20 +2167,20 @@ function handleJustsaying(ws, subject, body){ SELECT unit, is_stable FROM outputs JOIN units USING(unit) WHERE address=? \n\ ORDER BY is_stable LIMIT 10", [address, address], - function(rows){ + rows => { if (rows.length === 0) return; - if (rows.length === 10 || rows.some(function(row){ return row.is_stable; })) + if (rows.length === 10 || rows.some(({is_stable}) => is_stable)) sendJustsaying(ws, 'light/have_updates'); - rows.forEach(function(row){ - if (row.is_stable) + rows.forEach(({is_stable, unit}) => { + if (is_stable) return; - storage.readJoint(db, row.unit, { - ifFound: function(objJoint){ + storage.readJoint(db, unit, { + ifFound(objJoint) { sendJoint(ws, objJoint); }, - ifNotFound: function(){ - throw Error("watched unit "+row.unit+" not found"); + ifNotFound() { + throw Error(`watched unit ${unit} not found`); } }); }); @@ -2204,7 +2199,7 @@ function handleJustsaying(ws, subject, body){ function handleRequest(ws, tag, command, params){ if (ws.assocInPreparingResponse[tag]) // ignore repeated request while still preparing response to a previous identical request - return console.log("ignoring identical "+command+" request"); + return console.log(`ignoring identical ${command} request`); ws.assocInPreparingResponse[tag] = true; switch (command){ case 'heartbeat': @@ -2213,7 +2208,7 @@ function handleRequest(ws, tag, command, params){ // true if our timers were paused // Happens only on android, which suspends timers when the app becomes paused but still keeps network connections // Handling 'pause' event would've been more straightforward but with preference KeepRunning=false, the event is delayed till resume - var bPaused = (typeof window !== 'undefined' && window && window.cordova && Date.now() - last_hearbeat_wake_ts > PAUSE_TIMEOUT); + const bPaused = (typeof window !== 'undefined' && window && window.cordova && Date.now() - last_hearbeat_wake_ts > PAUSE_TIMEOUT); if (bPaused) return sendResponse(ws, tag, 'sleep'); // opt out of receiving heartbeats and move the connection into a sleeping state sendResponse(ws, tag); @@ -2222,10 +2217,10 @@ function handleRequest(ws, tag, command, params){ case 'subscribe': if (!ValidationUtils.isNonemptyObject(params)) return sendErrorResponse(ws, tag, 'no params'); - var subscription_id = params.subscription_id; + const subscription_id = params.subscription_id; if (typeof subscription_id !== 'string') return sendErrorResponse(ws, tag, 'no subscription_id'); - if (wss.clients.concat(arrOutboundPeers).some(function(other_ws) { return (other_ws.subscription_id === subscription_id); })){ + if (wss.clients.concat(arrOutboundPeers).some(other_ws => other_ws.subscription_id === subscription_id)){ if (ws.bOutbound) db.query("UPDATE peers SET is_self=1 WHERE peer=?", [ws.peer]); sendErrorResponse(ws, tag, "self-connect"); @@ -2255,20 +2250,20 @@ function handleRequest(ws, tag, command, params){ // return; if (ws.old_core) return sendErrorResponse(ws, tag, "old core, will not serve get_joint"); - var unit = params; + const unit = params; storage.readJoint(db, unit, { - ifFound: function(objJoint){ + ifFound(objJoint) { sendJoint(ws, objJoint, tag); }, - ifNotFound: function(){ + ifNotFound() { sendResponse(ws, tag, {joint_not_found: unit}); } }); break; case 'post_joint': // only light clients use this command to post joints they created - var objJoint = params; - handlePostedJoint(ws, objJoint, function(error){ + const objJoint = params; + handlePostedJoint(ws, objJoint, error => { error ? sendErrorResponse(ws, tag, error) : sendResponse(ws, tag, 'accepted'); }); break; @@ -2276,16 +2271,16 @@ function handleRequest(ws, tag, command, params){ case 'catchup': if (!ws.bSubscribed) return sendErrorResponse(ws, tag, "not subscribed, will not serve catchup"); - var catchupRequest = params; - mutex.lock(['catchup_request'], function(unlock){ + const catchupRequest = params; + mutex.lock(['catchup_request'], unlock => { if (!ws || ws.readyState !== ws.OPEN) // may be already gone when we receive the lock return process.nextTick(unlock); catchup.prepareCatchupChain(catchupRequest, { - ifError: function(error){ + ifError(error) { sendErrorResponse(ws, tag, error); unlock(); }, - ifOk: function(objCatchupChain){ + ifOk(objCatchupChain) { sendResponse(ws, tag, objCatchupChain); unlock(); } @@ -2296,16 +2291,16 @@ function handleRequest(ws, tag, command, params){ case 'get_hash_tree': if (!ws.bSubscribed) return sendErrorResponse(ws, tag, "not subscribed, will not serve get_hash_tree"); - var hashTreeRequest = params; - mutex.lock(['get_hash_tree_request'], function(unlock){ + const hashTreeRequest = params; + mutex.lock(['get_hash_tree_request'], unlock => { if (!ws || ws.readyState !== ws.OPEN) // may be already gone when we receive the lock return process.nextTick(unlock); catchup.readHashTree(hashTreeRequest, { - ifError: function(error){ + ifError(error) { sendErrorResponse(ws, tag, error); unlock(); }, - ifOk: function(arrBalls){ + ifOk(arrBalls) { // we have to wrap arrBalls into an object because the peer will check .error property first sendResponse(ws, tag, {balls: arrBalls}); unlock(); @@ -2315,33 +2310,33 @@ function handleRequest(ws, tag, command, params){ break; case 'get_peers': - var arrPeerUrls = arrOutboundPeers.map(function(ws){ return ws.peer; }); + const arrPeerUrls = arrOutboundPeers.map(({peer}) => peer); // empty array is ok sendResponse(ws, tag, arrPeerUrls); break; case 'get_witnesses': - myWitnesses.readMyWitnesses(function(arrWitnesses){ + myWitnesses.readMyWitnesses(arrWitnesses => { sendResponse(ws, tag, arrWitnesses); }, 'wait'); break; case 'get_last_mci': - storage.readLastMainChainIndex(function(last_mci){ + storage.readLastMainChainIndex(last_mci => { sendResponse(ws, tag, last_mci); }); break; // I'm a hub, the peer wants to deliver a message to one of my clients case 'hub/deliver': - var objDeviceMessage = params; + const objDeviceMessage = params; if (!objDeviceMessage || !objDeviceMessage.signature || !objDeviceMessage.pubkey || !objDeviceMessage.to || !objDeviceMessage.encrypted_package || !objDeviceMessage.encrypted_package.dh || !objDeviceMessage.encrypted_package.dh.sender_ephemeral_pubkey || !objDeviceMessage.encrypted_package.encrypted_message || !objDeviceMessage.encrypted_package.iv || !objDeviceMessage.encrypted_package.authtag) return sendErrorResponse(ws, tag, "missing fields"); - var bToMe = (my_device_address && my_device_address === objDeviceMessage.to); + const bToMe = (my_device_address && my_device_address === objDeviceMessage.to); if (!conf.bServeAsHub && !bToMe) return sendErrorResponse(ws, tag, "I'm not a hub"); if (!ecdsaSig.verify(objectHash.getDeviceMessageHashToSign(objDeviceMessage), objDeviceMessage.signature, objDeviceMessage.pubkey)) @@ -2357,19 +2352,19 @@ function handleRequest(ws, tag, command, params){ return; } - db.query("SELECT 1 FROM devices WHERE device_address=?", [objDeviceMessage.to], function(rows){ - if (rows.length === 0) - return sendErrorResponse(ws, tag, "address "+objDeviceMessage.to+" not registered here"); - var message_hash = objectHash.getBase64Hash(objDeviceMessage); + db.query("SELECT 1 FROM devices WHERE device_address=?", [objDeviceMessage.to], ({length}) => { + if (length === 0) + return sendErrorResponse(ws, tag, `address ${objDeviceMessage.to} not registered here`); + const message_hash = objectHash.getBase64Hash(objDeviceMessage); db.query( - "INSERT "+db.getIgnore()+" INTO device_messages (message_hash, message, device_address) VALUES (?,?,?)", + `INSERT ${db.getIgnore()} INTO device_messages (message_hash, message, device_address) VALUES (?,?,?)`, [message_hash, JSON.stringify(objDeviceMessage), objDeviceMessage.to], - function(){ + () => { // if the addressee is connected, deliver immediately - wss.clients.forEach(function(client){ + wss.clients.forEach(client => { if (client.device_address === objDeviceMessage.to) { sendJustsaying(client, 'hub/message', { - message_hash: message_hash, + message_hash, message: objDeviceMessage }); } @@ -2383,22 +2378,22 @@ function handleRequest(ws, tag, command, params){ // I'm a hub, the peer wants to get a correspondent's temporary pubkey case 'hub/get_temp_pubkey': - var permanent_pubkey = params; + const permanent_pubkey = params; if (!permanent_pubkey) return sendErrorResponse(ws, tag, "no permanent_pubkey"); if (permanent_pubkey.length !== constants.PUBKEY_LENGTH) return sendErrorResponse(ws, tag, "wrong permanent_pubkey length"); - var device_address = objectHash.getDeviceAddress(permanent_pubkey); + const device_address = objectHash.getDeviceAddress(permanent_pubkey); if (device_address === my_device_address) // to me return sendResponse(ws, tag, objMyTempPubkeyPackage); // this package signs my permanent key if (!conf.bServeAsHub) return sendErrorResponse(ws, tag, "I'm not a hub"); - db.query("SELECT temp_pubkey_package FROM devices WHERE device_address=?", [device_address], function(rows){ + db.query("SELECT temp_pubkey_package FROM devices WHERE device_address=?", [device_address], rows => { if (rows.length === 0) return sendErrorResponse(ws, tag, "device with this pubkey is not registered here"); if (!rows[0].temp_pubkey_package) return sendErrorResponse(ws, tag, "temp pub key not set yet"); - var objTempPubkey = JSON.parse(rows[0].temp_pubkey_package); + const objTempPubkey = JSON.parse(rows[0].temp_pubkey_package); sendResponse(ws, tag, objTempPubkey); }); break; @@ -2409,7 +2404,7 @@ function handleRequest(ws, tag, command, params){ return sendErrorResponse(ws, tag, "I'm not a hub"); if (!ws.device_address) return sendErrorResponse(ws, tag, "please log in first"); - var objTempPubkey = params; + const objTempPubkey = params; if (!objTempPubkey || !objTempPubkey.temp_pubkey || !objTempPubkey.pubkey || !objTempPubkey.signature) return sendErrorResponse(ws, tag, "no temp_pubkey params"); if (objTempPubkey.temp_pubkey.length !== constants.PUBKEY_LENGTH) @@ -2418,13 +2413,13 @@ function handleRequest(ws, tag, command, params){ return sendErrorResponse(ws, tag, "signed by another pubkey"); if (!ecdsaSig.verify(objectHash.getDeviceMessageHashToSign(objTempPubkey), objTempPubkey.signature, objTempPubkey.pubkey)) return sendErrorResponse(ws, tag, "wrong signature"); - var fnUpdate = function(onDone){ - db.query("UPDATE devices SET temp_pubkey_package=? WHERE device_address=?", [JSON.stringify(objTempPubkey), ws.device_address], function(){ + const fnUpdate = onDone => { + db.query("UPDATE devices SET temp_pubkey_package=? WHERE device_address=?", [JSON.stringify(objTempPubkey), ws.device_address], () => { if (onDone) onDone(); }); }; - fnUpdate(function(){ + fnUpdate(() => { sendResponse(ws, tag, "updated"); }); if (!ws.bLoginComplete) @@ -2436,30 +2431,26 @@ function handleRequest(ws, tag, command, params){ return sendErrorResponse(ws, tag, "I'm light myself, can't serve you"); if (ws.bOutbound) return sendErrorResponse(ws, tag, "light clients have to be inbound"); - mutex.lock(['get_history_request'], function(unlock){ + mutex.lock(['get_history_request'], unlock => { if (!ws || ws.readyState !== ws.OPEN) // may be already gone when we receive the lock return process.nextTick(unlock); light.prepareHistory(params, { - ifError: function(err){ + ifError(err) { sendErrorResponse(ws, tag, err); unlock(); }, - ifOk: function(objResponse){ + ifOk(objResponse) { sendResponse(ws, tag, objResponse); if (params.addresses) db.query( - "INSERT "+db.getIgnore()+" INTO watched_light_addresses (peer, address) VALUES "+ - params.addresses.map(function(address){ return "("+db.escape(ws.peer)+", "+db.escape(address)+")"; }).join(", ") + `INSERT ${db.getIgnore()} INTO watched_light_addresses (peer, address) VALUES ${params.addresses.map(address => `(${db.escape(ws.peer)}, ${db.escape(address)})`).join(", ")}` ); if (params.requested_joints) { storage.sliceAndExecuteQuery("SELECT unit FROM units WHERE main_chain_index >= ? AND unit IN(?)", - [storage.getMinRetrievableMci(), params.requested_joints], params.requested_joints, function(rows) { + [storage.getMinRetrievableMci(), params.requested_joints], params.requested_joints, rows => { if(rows.length) { db.query( - "INSERT " + db.getIgnore() + " INTO watched_light_units (peer, unit) VALUES " + - rows.map(function(row) { - return "(" + db.escape(ws.peer) + ", " + db.escape(row.unit) + ")"; - }).join(", ") + `INSERT ${db.getIgnore()} INTO watched_light_units (peer, unit) VALUES ${rows.map(row => `(${db.escape(ws.peer)}, ${db.escape(row.unit)})`).join(", ")}` ); } }); @@ -2477,15 +2468,15 @@ function handleRequest(ws, tag, command, params){ return sendErrorResponse(ws, tag, "I'm light myself, can't serve you"); if (ws.bOutbound) return sendErrorResponse(ws, tag, "light clients have to be inbound"); - mutex.lock(['get_link_proofs_request'], function(unlock){ + mutex.lock(['get_link_proofs_request'], unlock => { if (!ws || ws.readyState !== ws.OPEN) // may be already gone when we receive the lock return process.nextTick(unlock); light.prepareLinkProofs(params, { - ifError: function(err){ + ifError(err) { sendErrorResponse(ws, tag, err); unlock(); }, - ifOk: function(objResponse){ + ifOk(objResponse) { sendResponse(ws, tag, objResponse); unlock(); } @@ -2501,10 +2492,10 @@ function handleRequest(ws, tag, command, params){ if (!params) return sendErrorResponse(ws, tag, "no params in get_parents_and_last_ball_and_witness_list_unit"); light.prepareParentsAndLastBallAndWitnessListUnit(params.witnesses, { - ifError: function(err){ + ifError(err) { sendErrorResponse(ws, tag, err); }, - ifOk: function(objResponse){ + ifOk(objResponse) { sendResponse(ws, tag, objResponse); } }); @@ -2525,16 +2516,16 @@ function handleRequest(ws, tag, command, params){ break; case 'hub/get_bots': - db.query("SELECT id, name, pairing_code, description FROM bots ORDER BY rank DESC, id", [], function(rows){ + db.query("SELECT id, name, pairing_code, description FROM bots ORDER BY rank DESC, id", [], rows => { sendResponse(ws, tag, rows); }); break; case 'hub/get_asset_metadata': - var asset = params; + const asset = params; if (!ValidationUtils.isStringOfLength(asset, constants.HASH_LENGTH)) - return sendErrorResponse(ws, tag, "bad asset: "+asset); - db.query("SELECT metadata_unit, registry_address, suffix FROM asset_metadata WHERE asset=?", [asset], function(rows){ + return sendErrorResponse(ws, tag, `bad asset: ${asset}`); + db.query("SELECT metadata_unit, registry_address, suffix FROM asset_metadata WHERE asset=?", [asset], rows => { if (rows.length === 0) return sendErrorResponse(ws, tag, "no metadata"); sendResponse(ws, tag, rows[0]); @@ -2545,22 +2536,22 @@ function handleRequest(ws, tag, command, params){ function onWebsocketMessage(message) { - var ws = this; + const ws = this; if (ws.readyState !== ws.OPEN) return; - console.log('RECEIVED '+(message.length > 1000 ? message.substr(0,1000)+'... ('+message.length+' chars)' : message)+' from '+ws.peer); + console.log(`RECEIVED ${message.length > 1000 ? `${message.substr(0,1000)}... (${message.length} chars)` : message} from ${ws.peer}`); ws.last_ts = Date.now(); try{ var arrMessage = JSON.parse(message); } catch(e){ - return console.log('failed to json.parse message '+message); + return console.log(`failed to json.parse message ${message}`); } - var message_type = arrMessage[0]; - var content = arrMessage[1]; + const message_type = arrMessage[0]; + const content = arrMessage[1]; switch (message_type){ case 'justsaying': @@ -2573,7 +2564,7 @@ function onWebsocketMessage(message) { return handleResponse(ws, content.tag, content.response); default: - console.log("unknown type: "+message_type); + console.log(`unknown type: ${message_type}`); // throw Error("unknown type: "+message_type); } } @@ -2584,8 +2575,8 @@ function startAcceptingConnections(){ //db.query("DELETE FROM light_peer_witnesses"); // listen for new connections wss = new WebSocketServer({ port: conf.port }); - wss.on('connection', function(ws) { - var ip = ws.upgradeReq.connection.remoteAddress; + wss.on('connection', ws => { + let ip = ws.upgradeReq.connection.remoteAddress; if (!ip){ console.log("no ip in accepted connection"); ws.terminate(); @@ -2593,29 +2584,29 @@ function startAcceptingConnections(){ } if (ws.upgradeReq.headers['x-real-ip'] && (ip === '127.0.0.1' || ip.match(/^192\.168\./))) // we are behind a proxy ip = ws.upgradeReq.headers['x-real-ip']; - ws.peer = ip + ":" + ws.upgradeReq.connection.remotePort; + ws.peer = `${ip}:${ws.upgradeReq.connection.remotePort}`; ws.host = ip; ws.assocPendingRequests = {}; ws.assocInPreparingResponse = {}; ws.bInbound = true; ws.last_ts = Date.now(); - console.log('got connection from '+ws.peer+", host "+ws.host); + console.log(`got connection from ${ws.peer}, host ${ws.host}`); if (wss.clients.length >= conf.MAX_INBOUND_CONNECTIONS){ - console.log("inbound connections maxed out, rejecting new client "+ip); + console.log(`inbound connections maxed out, rejecting new client ${ip}`); ws.close(1000, "inbound connections maxed out"); // 1001 doesn't work in cordova return; } - var bStatsCheckUnderWay = true; + let bStatsCheckUnderWay = true; db.query( - "SELECT \n\ - SUM(CASE WHEN event='invalid' THEN 1 ELSE 0 END) AS count_invalid, \n\ - SUM(CASE WHEN event='new_good' THEN 1 ELSE 0 END) AS count_new_good \n\ - FROM peer_events WHERE peer_host=? AND event_date>"+db.addTime("-1 HOUR"), [ws.host], - function(rows){ + `SELECT \n\ + SUM(CASE WHEN event='invalid' THEN 1 ELSE 0 END) AS count_invalid, \n\ + SUM(CASE WHEN event='new_good' THEN 1 ELSE 0 END) AS count_new_good \n\ + FROM peer_events WHERE peer_host=? AND event_date>${db.addTime("-1 HOUR")}`, [ws.host], + rows => { bStatsCheckUnderWay = false; - var stats = rows[0]; + const stats = rows[0]; if (stats.count_invalid){ - console.log("rejecting new client "+ws.host+" because of bad stats"); + console.log(`rejecting new client ${ws.host} because of bad stats`); return ws.terminate(); } @@ -2635,7 +2626,7 @@ function startAcceptingConnections(){ eventBus.emit('connected', ws); } ); - ws.on('message', function(message){ // might come earlier than stats check completes + ws.on('message', message => { // might come earlier than stats check completes function tryHandleMessage(){ if (bStatsCheckUnderWay) setTimeout(tryHandleMessage, 100); @@ -2644,20 +2635,20 @@ function startAcceptingConnections(){ } tryHandleMessage(); }); - ws.on('close', function(){ + ws.on('close', () => { db.query("DELETE FROM watched_light_addresses WHERE peer=?", [ws.peer]); db.query("DELETE FROM watched_light_units WHERE peer=?", [ws.peer]); //db.query("DELETE FROM light_peer_witnesses WHERE peer=?", [ws.peer]); - console.log("client "+ws.peer+" disconnected"); + console.log(`client ${ws.peer} disconnected`); cancelRequestsOnClosedConnection(ws); }); - ws.on('error', function(e){ - console.log("error on client "+ws.peer+": "+e); + ws.on('error', e => { + console.log(`error on client ${ws.peer}: ${e}`); ws.close(1000, "received error"); }); addPeerHost(ws.host); }); - console.log('WSS running at port ' + conf.port); + console.log(`WSS running at port ${conf.port}`); } function startRelay(){ @@ -2705,7 +2696,7 @@ function start(){ } function closeAllWsConnections() { - arrOutboundPeers.forEach(function(ws) { + arrOutboundPeers.forEach(ws => { ws.close(1000,'Re-connect'); }); } diff --git a/object_hash.js b/object_hash.js index 669b2a9b..a0e7efcb 100644 --- a/object_hash.js +++ b/object_hash.js @@ -1,9 +1,8 @@ /*jslint node: true */ -"use strict"; -var crypto = require('crypto'); -var _ = require('lodash'); -var chash = require('./chash.js'); -var getSourceString = require('./string_utils').getSourceString; +const crypto = require('crypto'); +const _ = require('lodash'); +const chash = require('./chash.js'); +const getSourceString = require('./string_utils').getSourceString; function getChash160(obj) { return chash.getChash160(getSourceString(obj)); @@ -23,7 +22,7 @@ function getBase64Hash(obj) { function getNakedUnit(objUnit){ - var objNakedUnit = _.cloneDeep(objUnit); + const objNakedUnit = _.cloneDeep(objUnit); delete objNakedUnit.unit; delete objNakedUnit.headers_commission; delete objNakedUnit.payload_commission; @@ -31,7 +30,7 @@ function getNakedUnit(objUnit){ delete objNakedUnit.timestamp; //delete objNakedUnit.last_ball_unit; if (objNakedUnit.messages){ - for (var i=0; i ({ + address: address + })) // already sorted }; if (objUnit.witness_list_unit) objStrippedUnit.witness_list_unit = objUnit.witness_list_unit; @@ -67,15 +68,15 @@ function getUnitHash(objUnit) { } function getUnitHashToSign(objUnit) { - var objNakedUnit = getNakedUnit(objUnit); - for (var i=0; i 0) objBall.parent_balls = arrParentBalls; @@ -92,7 +93,7 @@ function getJointHash(objJoint) { } function cleanNulls(obj){ - Object.keys(obj).forEach(function(key){ + Object.keys(obj).forEach(key => { if (obj[key] === null) delete obj[key]; }); @@ -105,11 +106,11 @@ function cleanNulls(obj){ // but still selectable by double-click. Stripping the leading 0 will not produce a payment address that the device owner knows a private key for, // because payment address is derived by c-hashing the definition object, while device address is produced from raw public key. function getDeviceAddress(b64_pubkey){ - return ('0' + getChash160(b64_pubkey)); + return `0${getChash160(b64_pubkey)}`; } function getDeviceMessageHashToSign(objDeviceMessage) { - var objNakedDeviceMessage = _.clone(objDeviceMessage); + const objNakedDeviceMessage = _.clone(objDeviceMessage); delete objNakedDeviceMessage.signature; return crypto.createHash("sha256").update(getSourceString(objNakedDeviceMessage), "utf8").digest(); } diff --git a/object_length.js b/object_length.js index 17ea9e78..56fd9e7d 100644 --- a/object_length.js +++ b/object_length.js @@ -1,8 +1,7 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); +const _ = require('lodash'); -var PARENT_UNITS_SIZE = 2*44; +const PARENT_UNITS_SIZE = 2*44; function getLength(value) { if (value === null) @@ -14,29 +13,29 @@ function getLength(value) { return 8; //return value.toString().length; case "object": - var len = 0; + let len = 0; if (Array.isArray(value)) - value.forEach(function(element){ + value.forEach(element => { len += getLength(element); }); else - for (var key in value){ + for (const key in value){ if (typeof value[key] === "undefined") - throw Error("undefined at "+key+" of "+JSON.stringify(value)); + throw Error(`undefined at ${key} of ${JSON.stringify(value)}`); len += getLength(value[key]); } return len; case "boolean": return 1; default: - throw Error("unknown type="+(typeof value)+" of "+value); + throw Error(`unknown type=${typeof value} of ${value}`); } } function getHeadersSize(objUnit) { if (objUnit.content_hash) throw Error("trying to get headers size of stripped unit"); - var objHeader = _.cloneDeep(objUnit); + const objHeader = _.cloneDeep(objUnit); delete objHeader.unit; delete objHeader.headers_commission; delete objHeader.payload_commission; @@ -47,10 +46,10 @@ function getHeadersSize(objUnit) { return getLength(objHeader) + PARENT_UNITS_SIZE; } -function getTotalPayloadSize(objUnit) { - if (objUnit.content_hash) +function getTotalPayloadSize({content_hash, messages}) { + if (content_hash) throw Error("trying to get payload size of stripped unit"); - return getLength(objUnit.messages); + return getLength(messages); } exports.getHeadersSize = getHeadersSize; diff --git a/paid_witnessing.js b/paid_witnessing.js index 01491c12..0edf29af 100644 --- a/paid_witnessing.js +++ b/paid_witnessing.js @@ -1,21 +1,20 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var async = require('async'); -var storage = require('./storage.js'); -var graph = require('./graph.js'); -var db = require('./db.js'); -var constants = require("./constants.js"); -var conf = require("./conf.js"); -var mc_outputs = require("./mc_outputs.js"); -var profiler = require("./profiler.js"); +const _ = require('lodash'); +const async = require('async'); +const storage = require('./storage.js'); +const graph = require('./graph.js'); +const db = require('./db.js'); +const constants = require("./constants.js"); +const conf = require("./conf.js"); +const mc_outputs = require("./mc_outputs.js"); +const profiler = require("./profiler.js"); function calcWitnessEarnings(conn, type, from_main_chain_index, to_main_chain_index, address, callbacks){ conn.query( "SELECT COUNT(*) AS count FROM units WHERE is_on_main_chain=1 AND is_stable=1 AND main_chain_index>=? AND main_chain_index<=?", [to_main_chain_index, to_main_chain_index+constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+1], - function(count_rows){ + count_rows => { if (count_rows[0].count !== constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+2) return callbacks.ifError("not enough stable MC units after to_main_chain_index"); mc_outputs.calcEarnings(conn, type, from_main_chain_index, to_main_chain_index, address, callbacks); @@ -51,9 +50,9 @@ function readMaxWitnessSpendableMcIndex(conn, handleMaxSpendableMcIndex){ */ function readUnitOnMcIndex(conn, main_chain_index, handleUnit){ - conn.query("SELECT unit FROM units WHERE is_on_main_chain=1 AND main_chain_index=?", [main_chain_index], function(rows){ + conn.query("SELECT unit FROM units WHERE is_on_main_chain=1 AND main_chain_index=?", [main_chain_index], rows => { if (rows.length !== 1) - throw Error("no units or more than one unit on MC index "+main_chain_index); + throw Error(`no units or more than one unit on MC index ${main_chain_index}`); handleUnit(rows[0].unit); }); } @@ -61,21 +60,21 @@ function readUnitOnMcIndex(conn, main_chain_index, handleUnit){ function updatePaidWitnesses(conn, cb){ console.log("updating paid witnesses"); profiler.start(); - storage.readLastStableMcIndex(conn, function(last_stable_mci){ + storage.readLastStableMcIndex(conn, last_stable_mci => { profiler.stop('mc-wc-readLastStableMCI'); - var max_spendable_mc_index = getMaxSpendableMciForLastBallMci(last_stable_mci); + const max_spendable_mc_index = getMaxSpendableMciForLastBallMci(last_stable_mci); (max_spendable_mc_index > 0) ? buildPaidWitnessesTillMainChainIndex(conn, max_spendable_mc_index, cb) : cb(); }); } function buildPaidWitnessesTillMainChainIndex(conn, to_main_chain_index, cb){ profiler.start(); - var cross = (conf.storage === 'sqlite') ? 'CROSS' : ''; // correct the query planner + const cross = (conf.storage === 'sqlite') ? 'CROSS' : ''; // correct the query planner conn.query( - "SELECT MIN(main_chain_index) AS min_main_chain_index FROM balls "+cross+" JOIN units USING(unit) WHERE count_paid_witnesses IS NULL", - function(rows){ + `SELECT MIN(main_chain_index) AS min_main_chain_index FROM balls ${cross} JOIN units USING(unit) WHERE count_paid_witnesses IS NULL`, + rows => { profiler.stop('mc-wc-minMCI'); - var main_chain_index = rows[0].min_main_chain_index; + let main_chain_index = rows[0].min_main_chain_index; if (main_chain_index > to_main_chain_index) return cb(); @@ -97,41 +96,41 @@ function buildPaidWitnessesTillMainChainIndex(conn, to_main_chain_index, cb){ } function buildPaidWitnessesForMainChainIndex(conn, main_chain_index, cb){ - console.log("updating paid witnesses mci "+main_chain_index); + console.log(`updating paid witnesses mci ${main_chain_index}`); profiler.start(); conn.query( "SELECT COUNT(*) AS count, SUM(CASE WHEN is_stable=1 THEN 1 ELSE 0 END) AS count_on_stable_mc \n\ FROM units WHERE is_on_main_chain=1 AND main_chain_index>=? AND main_chain_index<=?", [main_chain_index, main_chain_index+constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+1], - function(rows){ + rows => { profiler.stop('mc-wc-select-count'); - var count = rows[0].count; - var count_on_stable_mc = rows[0].count_on_stable_mc; + const count = rows[0].count; + const count_on_stable_mc = rows[0].count_on_stable_mc; if (count !== constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+2) - throw Error("main chain is not long enough yet for MC index "+main_chain_index); + throw Error(`main chain is not long enough yet for MC index ${main_chain_index}`); if (count_on_stable_mc !== count) - throw Error("not enough stable MC units yet after MC index "+main_chain_index+": count_on_stable_mc="+count_on_stable_mc+", count="+count); + throw Error(`not enough stable MC units yet after MC index ${main_chain_index}: count_on_stable_mc=${count_on_stable_mc}, count=${count}`); profiler.start(); // we read witnesses from MC unit (users can cheat with side-chains to flip the witness list and pay commissions to their own witnesses) - readMcUnitWitnesses(conn, main_chain_index, function(arrWitnesses){ + readMcUnitWitnesses(conn, main_chain_index, arrWitnesses => { conn.query("CREATE TEMPORARY TABLE paid_witness_events_tmp ( \n\ unit CHAR(44) NOT NULL, \n\ address CHAR(32) NOT NULL, \n\ - delay TINYINT NULL)", function(){ - conn.query("SELECT * FROM units WHERE main_chain_index=?", [main_chain_index], function(rows){ + delay TINYINT NULL)", () => { + conn.query("SELECT * FROM units WHERE main_chain_index=?", [main_chain_index], rows => { profiler.stop('mc-wc-select-units'); et=0; rt=0; async.eachSeries( rows, - function(row, cb2){ + (row, cb2) => { // the unit itself might be never majority witnessed by unit-designated witnesses (which might be far off), // but its payload commission still belongs to and is spendable by the MC-unit-designated witnesses. //if (row.is_stable !== 1) // throw "unit "+row.unit+" is not on stable MC yet"; buildPaidWitnesses(conn, row, arrWitnesses, cb2); }, - function(err){ + err => { console.log(rt, et); if (err) // impossible throw Error(err); @@ -147,9 +146,9 @@ function buildPaidWitnessesForMainChainIndex(conn, main_chain_index, cb){ WHERE main_chain_index=? \n\ GROUP BY address", [main_chain_index], - function(){ + () => { //console.log(Date.now()-t); - conn.query(conn.dropTemporaryTable("paid_witness_events_tmp"), function(){ + conn.query(conn.dropTemporaryTable("paid_witness_events_tmp"), () => { profiler.stop('mc-wc-aggregate-events'); cb(); }); @@ -167,56 +166,57 @@ function buildPaidWitnessesForMainChainIndex(conn, main_chain_index, cb){ function readMcUnitWitnesses(conn, main_chain_index, handleWitnesses){ - conn.query("SELECT witness_list_unit, unit FROM units WHERE main_chain_index=? AND is_on_main_chain=1", [main_chain_index], function(rows){ + conn.query("SELECT witness_list_unit, unit FROM units WHERE main_chain_index=? AND is_on_main_chain=1", [main_chain_index], rows => { if (rows.length !== 1) - throw Error("not 1 row on MC "+main_chain_index); - var witness_list_unit = rows[0].witness_list_unit ? rows[0].witness_list_unit : rows[0].unit; + throw Error(`not 1 row on MC ${main_chain_index}`); + const witness_list_unit = rows[0].witness_list_unit ? rows[0].witness_list_unit : rows[0].unit; storage.readWitnessList(conn, witness_list_unit, handleWitnesses); }); } -var et, rt; +var et; +var rt; function buildPaidWitnesses(conn, objUnitProps, arrWitnesses, onDone){ function updateCountPaidWitnesses(count_paid_witnesses){ - conn.query("UPDATE balls SET count_paid_witnesses=? WHERE unit=?", [count_paid_witnesses, objUnitProps.unit], function(){ + conn.query("UPDATE balls SET count_paid_witnesses=? WHERE unit=?", [count_paid_witnesses, objUnitProps.unit], () => { profiler.stop('mc-wc-insert-events'); onDone(); }); } - var unit = objUnitProps.unit; - var to_main_chain_index = objUnitProps.main_chain_index + constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING; + const unit = objUnitProps.unit; + const to_main_chain_index = objUnitProps.main_chain_index + constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING; - var t=Date.now(); - graph.readDescendantUnitsByAuthorsBeforeMcIndex(conn, objUnitProps, arrWitnesses, to_main_chain_index, function(arrUnits){ + let t=Date.now(); + graph.readDescendantUnitsByAuthorsBeforeMcIndex(conn, objUnitProps, arrWitnesses, to_main_chain_index, arrUnits => { rt+=Date.now()-t; t=Date.now(); - var strUnitsList = (arrUnits.length === 0) ? 'NULL' : arrUnits.map(function(unit){ return conn.escape(unit); }).join(', '); + const strUnitsList = (arrUnits.length === 0) ? 'NULL' : arrUnits.map(unit => conn.escape(unit)).join(', '); //throw "no witnesses before mc "+to_main_chain_index+" for unit "+objUnitProps.unit; profiler.start(); conn.query( // we don't care if the unit is majority witnessed by the unit-designated witnesses // _left_ join forces use of indexes in units // can't get rid of filtering by address because units can be co-authored by witness with somebody else - "SELECT address, MIN(main_chain_index-?) AS delay \n\ - FROM units \n\ - LEFT JOIN unit_authors USING(unit) \n\ - WHERE unit IN("+strUnitsList+") AND address IN(?) AND +sequence='good' \n\ - GROUP BY address", + `SELECT address, MIN(main_chain_index-?) AS delay \n\ + FROM units \n\ + LEFT JOIN unit_authors USING(unit) \n\ + WHERE unit IN(${strUnitsList}) AND address IN(?) AND +sequence='good' \n\ + GROUP BY address`, [objUnitProps.main_chain_index, arrWitnesses], - function(rows){ + rows => { et += Date.now()-t; - var count_paid_witnesses = rows.length; - var arrValues; + let count_paid_witnesses = rows.length; + let arrValues; if (count_paid_witnesses === 0){ // nobody witnessed, pay equally to all count_paid_witnesses = arrWitnesses.length; - arrValues = arrWitnesses.map(function(address){ return "("+conn.escape(unit)+", "+conn.escape(address)+", NULL)"; }); + arrValues = arrWitnesses.map(address => `(${conn.escape(unit)}, ${conn.escape(address)}, NULL)`); } else - arrValues = rows.map(function(row){ return "("+conn.escape(unit)+", "+conn.escape(row.address)+", "+row.delay+")"; }); + arrValues = rows.map(({address, delay}) => `(${conn.escape(unit)}, ${conn.escape(address)}, ${delay})`); profiler.stop('mc-wc-select-events'); profiler.start(); - conn.query("INSERT INTO paid_witness_events_tmp (unit, address, delay) VALUES "+arrValues.join(", "), function(){ + conn.query(`INSERT INTO paid_witness_events_tmp (unit, address, delay) VALUES ${arrValues.join(", ")}`, () => { updateCountPaidWitnesses(count_paid_witnesses); }); } diff --git a/parent_composer.js b/parent_composer.js index 376eeb48..c3593ddd 100644 --- a/parent_composer.js +++ b/parent_composer.js @@ -1,11 +1,10 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var db = require('./db.js'); -var constants = require("./constants.js"); -var conf = require("./conf.js"); -var storage = require("./storage.js"); -var main_chain = require("./main_chain.js"); +const _ = require('lodash'); +const db = require('./db.js'); +const constants = require("./constants.js"); +const conf = require("./conf.js"); +const storage = require("./storage.js"); +const main_chain = require("./main_chain.js"); function pickParentUnits(conn, arrWitnesses, onDone){ @@ -16,26 +15,26 @@ function pickParentUnits(conn, arrWitnesses, onDone){ //var order_and_limit = bDeep ? "ORDER BY main_chain_index DESC LIMIT 1" : "ORDER BY unit LIMIT 1"; conn.query( - "SELECT \n\ - unit, version, alt, ( \n\ - SELECT COUNT(*) \n\ - FROM unit_witnesses \n\ - WHERE unit_witnesses.unit IN(units.unit, units.witness_list_unit) AND address IN(?) \n\ - ) AS count_matching_witnesses \n\ - FROM units "+(conf.storage === 'sqlite' ? "INDEXED BY byFree" : "")+" \n\ - LEFT JOIN archived_joints USING(unit) \n\ - WHERE +sequence='good' AND is_free=1 AND archived_joints.unit IS NULL ORDER BY unit LIMIT ?", + `SELECT \n\ + unit, version, alt, ( \n\ + SELECT COUNT(*) \n\ + FROM unit_witnesses \n\ + WHERE unit_witnesses.unit IN(units.unit, units.witness_list_unit) AND address IN(?) \n\ + ) AS count_matching_witnesses \n\ + FROM units ${conf.storage === 'sqlite' ? "INDEXED BY byFree" : ""} \n\ + LEFT JOIN archived_joints USING(unit) \n\ + WHERE +sequence='good' AND is_free=1 AND archived_joints.unit IS NULL ORDER BY unit LIMIT ?`, // exclude potential parents that were archived and then received again [arrWitnesses, constants.MAX_PARENTS_PER_UNIT], - function(rows){ - if (rows.some(function(row){ return (row.version !== constants.version || row.alt !== constants.alt); })) + rows => { + if (rows.some(({version, alt}) => version !== constants.version || alt !== constants.alt)) throw Error('wrong network'); - var count_required_matches = constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS; + const count_required_matches = constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS; // we need at least one compatible parent, otherwise go deep - if (rows.filter(function(row){ return (row.count_matching_witnesses >= count_required_matches); }).length === 0) + if (rows.filter(({count_matching_witnesses}) => count_matching_witnesses >= count_required_matches).length === 0) return pickDeepParentUnits(conn, arrWitnesses, null, onDone); - var arrParentUnits = rows.map(function(row){ return row.unit; }); - adjustParentsToNotRetreatWitnessedLevel(conn, arrWitnesses, arrParentUnits, function(arrAdjustedParents){ + const arrParentUnits = rows.map(({unit}) => unit); + adjustParentsToNotRetreatWitnessedLevel(conn, arrWitnesses, arrParentUnits, arrAdjustedParents => { onDone(null, arrAdjustedParents); }); // checkWitnessedLevelNotRetreatingAndLookLower(conn, arrWitnesses, arrParentUnits, (arrParentUnits.length === 1), onDone); @@ -44,28 +43,28 @@ function pickParentUnits(conn, arrWitnesses, onDone){ } function adjustParentsToNotRetreatWitnessedLevel(conn, arrWitnesses, arrParentUnits, handleAdjustedParents){ - var arrExcludedUnits = []; - var iterations = 0; + let arrExcludedUnits = []; + let iterations = 0; function replaceExcludedParent(arrCurrentParentUnits, excluded_unit){ - console.log('replaceExcludedParent '+arrCurrentParentUnits.join(', ')+" excluding "+excluded_unit); - var arrNewExcludedUnits = [excluded_unit]; - console.log('excluded parents: '+arrNewExcludedUnits.join(', ')); + console.log(`replaceExcludedParent ${arrCurrentParentUnits.join(', ')} excluding ${excluded_unit}`); + const arrNewExcludedUnits = [excluded_unit]; + console.log(`excluded parents: ${arrNewExcludedUnits.join(', ')}`); arrExcludedUnits = arrExcludedUnits.concat(arrNewExcludedUnits); - var arrParentsToKeep = _.difference(arrCurrentParentUnits, arrNewExcludedUnits); - conn.query("SELECT DISTINCT parent_unit FROM parenthoods WHERE child_unit IN(?)", [arrNewExcludedUnits], function(rows){ - var arrCandidateReplacements = rows.map(function(row){ return row.parent_unit; }); - console.log('candidate replacements: '+arrCandidateReplacements.join(', ')); + const arrParentsToKeep = _.difference(arrCurrentParentUnits, arrNewExcludedUnits); + conn.query("SELECT DISTINCT parent_unit FROM parenthoods WHERE child_unit IN(?)", [arrNewExcludedUnits], rows => { + const arrCandidateReplacements = rows.map(({parent_unit}) => parent_unit); + console.log(`candidate replacements: ${arrCandidateReplacements.join(', ')}`); conn.query( "SELECT DISTINCT parent_unit FROM parenthoods WHERE parent_unit IN(?) AND child_unit NOT IN(?)", [arrCandidateReplacements, arrExcludedUnits], - function(rows){ - var arrCandidatesWithOtherChildren = rows.map(function(row){ return row.parent_unit; }); - console.log('candidates with other children: '+arrCandidatesWithOtherChildren.join(', ')); - var arrReplacementParents = _.difference(arrCandidateReplacements, arrCandidatesWithOtherChildren); - console.log('replacements for excluded parents: '+arrReplacementParents.join(', ')); - var arrNewParents = arrParentsToKeep.concat(arrReplacementParents); - console.log('new parents: '+arrNewParents.join(', ')); + rows => { + const arrCandidatesWithOtherChildren = rows.map(({parent_unit}) => parent_unit); + console.log(`candidates with other children: ${arrCandidatesWithOtherChildren.join(', ')}`); + const arrReplacementParents = _.difference(arrCandidateReplacements, arrCandidatesWithOtherChildren); + console.log(`replacements for excluded parents: ${arrReplacementParents.join(', ')}`); + const arrNewParents = arrParentsToKeep.concat(arrReplacementParents); + console.log(`new parents: ${arrNewParents.join(', ')}`); if (arrNewParents.length === 0) throw Error("no new parents"); checkWitnessedLevelAndReplace(arrNewParents); @@ -75,14 +74,14 @@ function adjustParentsToNotRetreatWitnessedLevel(conn, arrWitnesses, arrParentUn } function checkWitnessedLevelAndReplace(arrCurrentParentUnits){ - console.log('checkWitnessedLevelAndReplace '+arrCurrentParentUnits.join(', ')); + console.log(`checkWitnessedLevelAndReplace ${arrCurrentParentUnits.join(', ')}`); if (iterations > 0 && arrExcludedUnits.length === 0) throw Error("infinite cycle"); iterations++; - determineWitnessedLevels(conn, arrWitnesses, arrCurrentParentUnits, function(child_witnessed_level, best_parent_witnessed_level, best_parent_unit){ + determineWitnessedLevels(conn, arrWitnesses, arrCurrentParentUnits, (child_witnessed_level, best_parent_witnessed_level, best_parent_unit) => { if (child_witnessed_level >= best_parent_witnessed_level) return handleAdjustedParents(arrCurrentParentUnits.sort()); - console.log('wl would retreat from '+best_parent_witnessed_level+' to '+child_witnessed_level+', parents '+arrCurrentParentUnits.join(', ')); + console.log(`wl would retreat from ${best_parent_witnessed_level} to ${child_witnessed_level}, parents ${arrCurrentParentUnits.join(', ')}`); replaceExcludedParent(arrCurrentParentUnits, best_parent_unit); }); } @@ -91,22 +90,22 @@ function adjustParentsToNotRetreatWitnessedLevel(conn, arrWitnesses, arrParentUn } function pickParentUnitsUnderWitnessedLevel(conn, arrWitnesses, max_wl, onDone){ - console.log("looking for free parents under wl "+max_wl); + console.log(`looking for free parents under wl ${max_wl}`); conn.query( - "SELECT unit \n\ - FROM units "+(conf.storage === 'sqlite' ? "INDEXED BY byFree" : "")+" \n\ - WHERE +sequence='good' AND is_free=1 AND witnessed_level=? \n\ - ORDER BY witnessed_level DESC, level DESC LIMIT ?", + `SELECT unit \n\ + FROM units ${conf.storage === 'sqlite' ? "INDEXED BY byFree" : ""} \n\ + WHERE +sequence='good' AND is_free=1 AND witnessed_level=? \n\ + ORDER BY witnessed_level DESC, level DESC LIMIT ?`, [max_wl, arrWitnesses, constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS, constants.MAX_PARENTS_PER_UNIT], - function(rows){ + rows => { if (rows.length === 0) return pickDeepParentUnits(conn, arrWitnesses, max_wl, onDone); - var arrParentUnits = rows.map(function(row){ return row.unit; }).sort(); + const arrParentUnits = rows.map(({unit}) => unit).sort(); checkWitnessedLevelNotRetreatingAndLookLower(conn, arrWitnesses, arrParentUnits, true, onDone); } ); @@ -118,41 +117,41 @@ function pickDeepParentUnits(conn, arrWitnesses, max_wl, onDone){ // fixed: an attacker could cover all free compatible units with his own incompatible ones, then those that were not on MC will be never included //var cond = bDeep ? "is_on_main_chain=1" : "is_free=1"; - console.log("looking for deep parents, max_wl="+max_wl); - var and_wl = (max_wl === null) ? '' : "AND +is_on_main_chain=1 AND witnessed_level<"+max_wl; + console.log(`looking for deep parents, max_wl=${max_wl}`); + const and_wl = (max_wl === null) ? '' : `AND +is_on_main_chain=1 AND witnessed_level<${max_wl}`; conn.query( - "SELECT unit \n\ - FROM units \n\ - WHERE +sequence='good' "+and_wl+" \n\ - AND ( \n\ - SELECT COUNT(*) \n\ - FROM unit_witnesses \n\ - WHERE unit_witnesses.unit IN(units.unit, units.witness_list_unit) AND address IN(?) \n\ - )>=? \n\ - ORDER BY main_chain_index DESC LIMIT 1", + `SELECT unit \n\ + FROM units \n\ + WHERE +sequence='good' ${and_wl} \n\ + AND ( \n\ + SELECT COUNT(*) \n\ + FROM unit_witnesses \n\ + WHERE unit_witnesses.unit IN(units.unit, units.witness_list_unit) AND address IN(?) \n\ + )>=? \n\ + ORDER BY main_chain_index DESC LIMIT 1`, [arrWitnesses, constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS], - function(rows){ + rows => { if (rows.length === 0) return onDone("failed to find compatible parents: no deep units"); - var arrParentUnits = rows.map(function(row){ return row.unit; }); + const arrParentUnits = rows.map(({unit}) => unit); checkWitnessedLevelNotRetreatingAndLookLower(conn, arrWitnesses, arrParentUnits, true, onDone); } ); } function determineWitnessedLevels(conn, arrWitnesses, arrParentUnits, handleResult){ - storage.determineWitnessedLevelAndBestParent(conn, arrParentUnits, arrWitnesses, function(witnessed_level, best_parent_unit){ - storage.readStaticUnitProps(conn, best_parent_unit, function(bestParentProps){ + storage.determineWitnessedLevelAndBestParent(conn, arrParentUnits, arrWitnesses, (witnessed_level, best_parent_unit) => { + storage.readStaticUnitProps(conn, best_parent_unit, bestParentProps => { handleResult(witnessed_level, bestParentProps.witnessed_level, best_parent_unit); }); }); } function checkWitnessedLevelNotRetreatingAndLookLower(conn, arrWitnesses, arrParentUnits, bRetryDeeper, onDone){ - determineWitnessedLevels(conn, arrWitnesses, arrParentUnits, function(child_witnessed_level, best_parent_witnessed_level){ + determineWitnessedLevels(conn, arrWitnesses, arrParentUnits, (child_witnessed_level, best_parent_witnessed_level) => { if (child_witnessed_level >= best_parent_witnessed_level) return onDone(null, arrParentUnits); - console.log("witness level would retreat from "+best_parent_witnessed_level+" to "+child_witnessed_level+" if parents = "+arrParentUnits.join(', ')+", will look for older parents"); + console.log(`witness level would retreat from ${best_parent_witnessed_level} to ${child_witnessed_level} if parents = ${arrParentUnits.join(', ')}, will look for older parents`); bRetryDeeper ? pickDeepParentUnits(conn, arrWitnesses, best_parent_witnessed_level, onDone) : pickParentUnitsUnderWitnessedLevel(conn, arrWitnesses, best_parent_witnessed_level, onDone); @@ -169,7 +168,7 @@ function findLastStableMcBall(conn, arrWitnesses, onDone){ )>=? \n\ ORDER BY main_chain_index DESC LIMIT 1", [arrWitnesses, constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS], - function(rows){ + rows => { if (rows.length === 0) return onDone("failed to find last stable ball"); onDone(null, rows[0].ball, rows[0].unit, rows[0].main_chain_index); @@ -178,17 +177,17 @@ function findLastStableMcBall(conn, arrWitnesses, onDone){ } function adjustLastStableMcBallAndParents(conn, last_stable_mc_ball_unit, arrParentUnits, arrWitnesses, handleAdjustedLastStableUnit){ - main_chain.determineIfStableInLaterUnits(conn, last_stable_mc_ball_unit, arrParentUnits, function(bStable){ + main_chain.determineIfStableInLaterUnits(conn, last_stable_mc_ball_unit, arrParentUnits, bStable => { if (bStable){ - conn.query("SELECT ball, main_chain_index FROM units JOIN balls USING(unit) WHERE unit=?", [last_stable_mc_ball_unit], function(rows){ + conn.query("SELECT ball, main_chain_index FROM units JOIN balls USING(unit) WHERE unit=?", [last_stable_mc_ball_unit], rows => { if (rows.length !== 1) - throw Error("not 1 ball by unit "+last_stable_mc_ball_unit); - var row = rows[0]; + throw Error(`not 1 ball by unit ${last_stable_mc_ball_unit}`); + const row = rows[0]; handleAdjustedLastStableUnit(row.ball, last_stable_mc_ball_unit, row.main_chain_index, arrParentUnits); }); return; } - console.log('will adjust last stable ball because '+last_stable_mc_ball_unit+' is not stable in view of parents '+arrParentUnits.join(', ')); + console.log(`will adjust last stable ball because ${last_stable_mc_ball_unit} is not stable in view of parents ${arrParentUnits.join(', ')}`); /*if (arrParentUnits.length > 1){ // select only one parent pickDeepParentUnits(conn, arrWitnesses, null, function(err, arrAdjustedParentUnits){ if (err) @@ -197,10 +196,10 @@ function adjustLastStableMcBallAndParents(conn, last_stable_mc_ball_unit, arrPar }); return; }*/ - storage.readStaticUnitProps(conn, last_stable_mc_ball_unit, function(objUnitProps){ - if (!objUnitProps.best_parent_unit) - throw Error("no best parent of "+last_stable_mc_ball_unit); - adjustLastStableMcBallAndParents(conn, objUnitProps.best_parent_unit, arrParentUnits, arrWitnesses, handleAdjustedLastStableUnit); + storage.readStaticUnitProps(conn, last_stable_mc_ball_unit, ({best_parent_unit}) => { + if (!best_parent_unit) + throw Error(`no best parent of ${last_stable_mc_ball_unit}`); + adjustLastStableMcBallAndParents(conn, best_parent_unit, arrParentUnits, arrWitnesses, handleAdjustedLastStableUnit); }); }); } @@ -209,31 +208,41 @@ function trimParentList(conn, arrParentUnits, arrWitnesses, handleTrimmedList){ if (arrParentUnits.length <= constants.MAX_PARENTS_PER_UNIT) return handleTrimmedList(arrParentUnits); conn.query( - "SELECT unit, (SELECT 1 FROM unit_authors WHERE unit_authors.unit=units.unit AND address IN(?) LIMIT 1) AS is_witness \n\ - FROM units WHERE unit IN("+arrParentUnits.map(db.escape).join(', ')+") ORDER BY is_witness DESC, "+db.getRandom()+" LIMIT ?", + `SELECT unit, (SELECT 1 FROM unit_authors WHERE unit_authors.unit=units.unit AND address IN(?) LIMIT 1) AS is_witness \n\ + FROM units WHERE unit IN(${arrParentUnits.map(db.escape).join(', ')}) ORDER BY is_witness DESC, ${db.getRandom()} LIMIT ?`, [arrWitnesses, constants.MAX_PARENTS_PER_UNIT], - function(rows){ - handleTrimmedList(rows.map(function(row){ return row.unit; }).sort()); + rows => { + handleTrimmedList(rows.map(({unit}) => unit).sort()); } ); } function pickParentUnitsAndLastBall(conn, arrWitnesses, onDone){ - pickParentUnits(conn, arrWitnesses, function(err, arrParentUnits){ + pickParentUnits(conn, arrWitnesses, (err, arrParentUnits) => { if (err) return onDone(err); - findLastStableMcBall(conn, arrWitnesses, function(err, last_stable_mc_ball, last_stable_mc_ball_unit, last_stable_mc_ball_mci){ + findLastStableMcBall(conn, arrWitnesses, ( + err, + last_stable_mc_ball, + last_stable_mc_ball_unit, + last_stable_mc_ball_mci + ) => { if (err) return onDone(err); adjustLastStableMcBallAndParents( conn, last_stable_mc_ball_unit, arrParentUnits, arrWitnesses, - function(last_stable_ball, last_stable_unit, last_stable_mci, arrAdjustedParentUnits){ - trimParentList(conn, arrAdjustedParentUnits, arrWitnesses, function(arrTrimmedParentUnits){ - storage.findWitnessListUnit(conn, arrWitnesses, last_stable_mci, function(witness_list_unit){ - var objFakeUnit = {parent_units: arrTrimmedParentUnits}; + ( + last_stable_ball, + last_stable_unit, + last_stable_mci, + arrAdjustedParentUnits + ) => { + trimParentList(conn, arrAdjustedParentUnits, arrWitnesses, arrTrimmedParentUnits => { + storage.findWitnessListUnit(conn, arrWitnesses, last_stable_mci, witness_list_unit => { + const objFakeUnit = {parent_units: arrTrimmedParentUnits}; if (witness_list_unit) objFakeUnit.witness_list_unit = witness_list_unit; - storage.determineIfHasWitnessListMutationsAlongMc(conn, objFakeUnit, last_stable_unit, arrWitnesses, function(err){ + storage.determineIfHasWitnessListMutationsAlongMc(conn, objFakeUnit, last_stable_unit, arrWitnesses, err => { if (err) return onDone(err); // if first arg is not array, it is error onDone(null, arrTrimmedParentUnits, last_stable_ball, last_stable_unit, last_stable_mci); diff --git a/private_payment.js b/private_payment.js index cc44c73a..5757a3c4 100644 --- a/private_payment.js +++ b/private_payment.js @@ -1,21 +1,20 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var storage = require('./storage.js'); -var db = require('./db.js'); -var conf = require('./conf.js'); -var ValidationUtils = require("./validation_utils.js"); -var indivisibleAsset = require('./indivisible_asset.js'); -var divisibleAsset = require('./divisible_asset.js'); +const _ = require('lodash'); +const storage = require('./storage.js'); +const db = require('./db.js'); +const conf = require('./conf.js'); +const ValidationUtils = require("./validation_utils.js"); +const indivisibleAsset = require('./indivisible_asset.js'); +const divisibleAsset = require('./divisible_asset.js'); function findUnfinishedPastUnitsOfPrivateChains(arrChains, includeLatestElement, handleUnits){ - var assocUnits = {}; - arrChains.forEach(function(arrPrivateElements){ + const assocUnits = {}; + arrChains.forEach(arrPrivateElements => { assocUnits[arrPrivateElements[0].payload.asset] = true; // require asset definition - for (var i = includeLatestElement ? 0 : 1; i { + storage.readAsset(db, asset, null, (err, {fixed_denominations}) => { if (err) return callbacks.ifError(err); - if (!!objAsset.fixed_denominations !== !!headElement.payload.denomination) + if (!!fixed_denominations !== !!headElement.payload.denomination) return callbacks.ifError("presence of denomination field doesn't match the asset type"); - db.takeConnectionFromPool(function(conn){ - conn.query("BEGIN", function(){ - var transaction_callbacks = { - ifError: function(err){ - conn.query("ROLLBACK", function(){ + db.takeConnectionFromPool(conn => { + conn.query("BEGIN", () => { + const transaction_callbacks = { + ifError(err) { + conn.query("ROLLBACK", () => { conn.release(); callbacks.ifError(err); }); }, - ifOk: function(){ - conn.query("COMMIT", function(){ + ifOk() { + conn.query("COMMIT", () => { conn.release(); callbacks.ifOk(); }); } }; // check if duplicate - var sql = "SELECT address FROM outputs WHERE unit=? AND message_index=?"; - var params = [headElement.unit, headElement.message_index]; - if (objAsset.fixed_denominations){ + let sql = "SELECT address FROM outputs WHERE unit=? AND message_index=?"; + const params = [headElement.unit, headElement.message_index]; + if (fixed_denominations){ if (!ValidationUtils.isNonnegativeInteger(headElement.output_index)) return transaction_callbacks.ifError("no output index in head private element"); sql += " AND output_index=?"; @@ -66,14 +65,14 @@ function validateAndSavePrivatePaymentChain(arrPrivateElements, callbacks){ conn.query( sql, params, - function(rows){ + rows => { if (rows.length > 1) - throw Error("more than one output "+sql+' '+params.join(', ')); + throw Error(`more than one output ${sql} ${params.join(', ')}`); if (rows.length > 0 && rows[0].address){ // we could have this output already but the address is still hidden - console.log("duplicate private payment "+params.join(', ')); + console.log(`duplicate private payment ${params.join(', ')}`); return transaction_callbacks.ifOk(); } - var assetModule = objAsset.fixed_denominations ? indivisibleAsset : divisibleAsset; + const assetModule = fixed_denominations ? indivisibleAsset : divisibleAsset; assetModule.validateAndSavePrivatePaymentChain(conn, arrPrivateElements, transaction_callbacks); } ); @@ -83,8 +82,8 @@ function validateAndSavePrivatePaymentChain(arrPrivateElements, callbacks){ }; if (conf.bLight) - findUnfinishedPastUnitsOfPrivateChains([arrPrivateElements], false, function(arrUnfinishedUnits){ - (arrUnfinishedUnits.length > 0) ? callbacks.ifWaitingForChain() : validateAndSave(); + findUnfinishedPastUnitsOfPrivateChains([arrPrivateElements], false, ({length}) => { + (length > 0) ? callbacks.ifWaitingForChain() : validateAndSave(); }); else validateAndSave(); diff --git a/profiler.js b/profiler.js index 4998be13..68b5ae25 100644 --- a/profiler.js +++ b/profiler.js @@ -1,20 +1,18 @@ /*jslint node: true */ -"use strict"; +let count = 0; +const times = {}; +let start_ts = 0; -var count = 0; -var times = {}; -var start_ts = 0; - -var timers = {}; -var timers_results = {}; -var profiler_start_ts = Date.now(); +const timers = {}; +const timers_results = {}; +const profiler_start_ts = Date.now(); function mark_start(tag, id) { return; if (!id) id = 0; if (!timers[tag]) timers[tag] = {}; if (timers[tag][id]) - throw Error("multiple start marks for " + tag + "[" + id + "]"); + throw Error(`multiple start marks for ${tag}[${id}]`); timers[tag][id] = Date.now(); } @@ -45,35 +43,35 @@ function stop(tag){ function print(){ console.log("\nProfiling results:"); - var total = 0; + let total = 0; for (var tag in times) total += times[tag]; for (var tag in times){ console.log( - pad_right(tag+": ", 33) + - pad_left(times[tag], 5) + ', ' + - pad_left((times[tag]/count).toFixed(2), 5) + ' per unit, ' + - pad_left((100*times[tag]/total).toFixed(2), 5) + '%' + `${pad_right(`${tag}: `, 33) + +pad_left(times[tag], 5)}, ${pad_left((times[tag]/count).toFixed(2), 5)} per unit, ${pad_left((100*times[tag]/total).toFixed(2), 5)}%` ); } - console.log('total: '+total); - console.log(total/count+' per unit'); + console.log(`total: ${total}`); + console.log(`${total/count} per unit`); } function print_results() { console.log("\nBenchmarking results:"); - for (var tag in timers_results) { - var results = timers_results[tag]; - var sum = 0, max = 0, min = 999999999999; - for (var i = 0; i < results.length; i++) { - var v = results[i]; + for (const tag in timers_results) { + const results = timers_results[tag]; + let sum = 0; + let max = 0; + let min = 999999999999; + for (let i = 0; i < results.length; i++) { + const v = results[i]; sum += v; if (v > max) max = v; if (v < min) min = v; } - console.log(tag.padding(50) + ": avg:" + Math.round(sum / results.length).toString().padding(8) + "max:" + Math.round(max).toString().padding(8) + "min:" + Math.round(min).toString().padding(8) + "records:" + results.length); - } - console.log("\n\nStart time: " + profiler_start_ts + ", End time: " + Date.now() + " Elapsed ms:" + (Date.now() - profiler_start_ts)); + console.log(`${tag.padding(50)}: avg:${Math.round(sum / results.length).toString().padding(8)}max:${Math.round(max).toString().padding(8)}min:${Math.round(min).toString().padding(8)}records:${results.length}`); + } + console.log(`\n\nStart time: ${profiler_start_ts}, End time: ${Date.now()} Elapsed ms:${Date.now() - profiler_start_ts}`); } function pad_right(str, len){ @@ -83,7 +81,7 @@ function pad_right(str, len){ } function pad_left(str, len){ - str = str+''; + str = `${str}`; if (str.length >= len) return str; return ' '.repeat(len - str.length) + str; @@ -93,7 +91,7 @@ function increment(){ count++; } -process.on('SIGINT', function(){ +process.on('SIGINT', () => { console.log = clog; console.log("received sigint"); //print(); @@ -103,12 +101,12 @@ process.on('SIGINT', function(){ String.prototype.padding = function(n, c) { - var val = this.valueOf(); + const val = this.valueOf(); if ( Math.abs(n) <= val.length ) { return val; } - var m = Math.max((Math.abs(n) - this.length) || 0, 0); - var pad = Array(m + 1).join(String(c || ' ').charAt(0)); + const m = Math.max((Math.abs(n) - this.length) || 0, 0); + const pad = Array(m + 1).join(String(c || ' ').charAt(0)); // var pad = String(c || ' ').charAt(0).repeat(Math.abs(n) - this.length); return (n < 0) ? pad + val : val + pad; // return (n < 0) ? val + pad : pad + val; @@ -125,7 +123,7 @@ exports.mark_start = mark_start; exports.mark_end = mark_end; -exports.start = function(){}; -exports.stop = function(){}; -exports.increment = function(){}; +exports.start = () => {}; +exports.stop = () => {}; +exports.increment = () => {}; //exports.print = function(){}; \ No newline at end of file diff --git a/signature.js b/signature.js index beda0370..d657c44f 100644 --- a/signature.js +++ b/signature.js @@ -1,19 +1,18 @@ /*jslint node: true */ -"use strict"; -var ecdsa = require('secp256k1'); +const ecdsa = require('secp256k1'); -exports.sign = function(hash, priv_key){ - var res = ecdsa.sign(hash, priv_key); +exports.sign = (hash, priv_key) => { + const res = ecdsa.sign(hash, priv_key); return res.signature.toString("base64"); }; -exports.verify = function(hash, b64_sig, b64_pub_key){ +exports.verify = (hash, b64_sig, b64_pub_key) => { try{ - var signature = new Buffer(b64_sig, "base64"); // 64 bytes (32+32) + const signature = new Buffer(b64_sig, "base64"); // 64 bytes (32+32) return ecdsa.verify(hash, signature, new Buffer(b64_pub_key, "base64")); } catch(e){ - console.log('signature verification exception: '+e.toString()); + console.log(`signature verification exception: ${e.toString()}`); return false; } }; diff --git a/sqlite_migrations.js b/sqlite_migrations.js index 6b35ee1f..81a6547d 100644 --- a/sqlite_migrations.js +++ b/sqlite_migrations.js @@ -1,26 +1,25 @@ /*jslint node: true */ -"use strict"; -var eventBus = require('./event_bus.js'); +const eventBus = require('./event_bus.js'); -var VERSION = 17; +const VERSION = 17; -var async = require('async'); -var bCordova = (typeof window === 'object' && window.cordova); +const async = require('async'); +const bCordova = (typeof window === 'object' && window.cordova); function migrateDb(connection, onDone){ - connection.db[bCordova ? 'query' : 'all']("PRAGMA user_version", function(err, result){ + connection.db[bCordova ? 'query' : 'all']("PRAGMA user_version", (err, result) => { if (err) - throw Error("PRAGMA user_version failed: "+err); - var rows = bCordova ? result.rows : result; + throw Error(`PRAGMA user_version failed: ${err}`); + const rows = bCordova ? result.rows : result; if (rows.length !== 1) - throw Error("PRAGMA user_version returned "+rows.length+" rows"); - var version = rows[0].user_version; - console.log("db version "+version+", software version "+VERSION); + throw Error(`PRAGMA user_version returned ${rows.length} rows`); + const version = rows[0].user_version; + console.log(`db version ${version}, software version ${VERSION}`); if (version > VERSION) - throw Error("user version "+version+" > "+VERSION+": looks like you are using a new database with an old client"); + throw Error(`user version ${version} > ${VERSION}: looks like you are using a new database with an old client`); if (version === VERSION) return onDone(); - var arrQueries = []; + const arrQueries = []; if (version < 1){ connection.addQuery(arrQueries, "CREATE INDEX IF NOT EXISTS unitAuthorsIndexByAddressDefinitionChash ON unit_authors(address, definition_chash)"); connection.addQuery(arrQueries, "CREATE INDEX IF NOT EXISTS outputsIsSerial ON outputs(is_serial)"); @@ -148,11 +147,11 @@ function migrateDb(connection, onDone){ )"); connection.addQuery(arrQueries, "CREATE INDEX IF NOT EXISTS ppfByField ON private_profile_fields(`field`)"); } - connection.addQuery(arrQueries, "PRAGMA user_version="+VERSION); + connection.addQuery(arrQueries, `PRAGMA user_version=${VERSION}`); eventBus.emit('started_db_upgrade'); if (typeof window === 'undefined') console.error("=== will upgrade the database, it can take some time"); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { eventBus.emit('finished_db_upgrade'); if (typeof window === 'undefined') console.error("=== db upgrade finished"); diff --git a/sqlite_pool.js b/sqlite_pool.js index 8e78609b..5d587179 100644 --- a/sqlite_pool.js +++ b/sqlite_pool.js @@ -1,14 +1,13 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var async = require('async'); -var sqlite_migrations = require('./sqlite_migrations'); -var EventEmitter = require('events').EventEmitter; +const _ = require('lodash'); +const async = require('async'); +const sqlite_migrations = require('./sqlite_migrations'); +const EventEmitter = require('events').EventEmitter; -var bCordova = (typeof window === 'object' && window.cordova); -var sqlite3; -var path; -var cordovaSqlite; +const bCordova = (typeof window === 'object' && window.cordova); +let sqlite3; +let path; +let cordovaSqlite; if (bCordova){ // will error before deviceready @@ -16,15 +15,15 @@ if (bCordova){ } else{ sqlite3 = require('sqlite3');//.verbose(); - path = require('./desktop_app.js'+'').getAppDataDir() + '/'; - console.log("path="+path); + path = `${require('./desktop_app.js'+'').getAppDataDir()}/`; + console.log(`path=${path}`); } -module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ +module.exports = (db_name, MAX_CONNECTIONS, bReadOnly) => { function openDb(cb){ if (bCordova){ - var db = new cordovaSqlite(db_name); + const db = new cordovaSqlite(db_name); db.open(cb); return db; } @@ -32,23 +31,23 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ return new sqlite3.Database(path + db_name, bReadOnly ? sqlite3.OPEN_READONLY : sqlite3.OPEN_READWRITE, cb); } - var eventEmitter = new EventEmitter(); - var bReady = false; - var arrConnections = []; - var arrQueue = []; + const eventEmitter = new EventEmitter(); + let bReady = false; + const arrConnections = []; + const arrQueue = []; function connect(handleConnection){ console.log("opening new db connection"); - var db = openDb(function(err){ + const db = openDb(err => { if (err) throw Error(err); console.log("opened db"); - connection.query("PRAGMA foreign_keys = 1", function(){ - connection.query("PRAGMA busy_timeout=30000", function(){ - connection.query("PRAGMA journal_mode=WAL", function(){ - connection.query("PRAGMA synchronous=NORMAL", function(){ - connection.query("PRAGMA temp_store=MEMORY", function(){ - sqlite_migrations.migrateDb(connection, function(){ + connection.query("PRAGMA foreign_keys = 1", () => { + connection.query("PRAGMA busy_timeout=30000", () => { + connection.query("PRAGMA journal_mode=WAL", () => { + connection.query("PRAGMA synchronous=NORMAL", () => { + connection.query("PRAGMA temp_store=MEMORY", () => { + sqlite_migrations.migrateDb(connection, () => { handleConnection(connection); }); }); @@ -59,36 +58,36 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ }); var connection = { - db: db, + db, bInUse: true, - release: function(){ + release() { //console.log("released connection"); this.bInUse = false; if (arrQueue.length === 0) return; - var connectionHandler = arrQueue.shift(); + const connectionHandler = arrQueue.shift(); this.bInUse = true; connectionHandler(this); }, - query: function(){ + query(...args) { if (!this.bInUse) throw Error("this connection was returned to the pool"); - var last_arg = arguments[arguments.length - 1]; - var bHasCallback = (typeof last_arg === 'function'); + let last_arg = args[args.length - 1]; + const bHasCallback = (typeof last_arg === 'function'); if (!bHasCallback) // no callback - last_arg = function(){}; + last_arg = () => {}; - var sql = arguments[0]; + const sql = args[0]; //console.log("======= query: "+sql); - var bSelect = !!sql.match(/^SELECT/i); - var count_arguments_without_callback = bHasCallback ? (arguments.length-1) : arguments.length; - var new_args = []; - var self = this; + const bSelect = !!sql.match(/^SELECT/i); + const count_arguments_without_callback = bHasCallback ? (args.length-1) : args.length; + const new_args = []; + const self = this; - for (var i=0; i { if (param === null) return 'null'; if (param === undefined) return 'undefined'; return param;}).join(', ')}`); } // note that sqlite3 sets nonzero this.changes even when rows were matched but nothing actually changed (new values are same as old) // this.changes appears to be correct for INSERTs despite the documentation states the opposite @@ -107,29 +106,29 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ if (bSelect && bCordova) // note that on android, result.affectedRows is 1 even when inserted many rows result = result.rows || []; //console.log("changes="+this.changes+", affected="+result.affectedRows); - var consumed_time = Date.now() - start_ts; + const consumed_time = Date.now() - start_ts; if (consumed_time > 25) - console.log("long query took "+consumed_time+"ms:\n"+new_args.filter(function(a, i){ return (i i { // add callback for async.series() member tasks if (typeof query_args[query_args.length-1] !== 'function') - query_args.push(function(){callback();}); // add callback + query_args.push(() => {callback();}); // add callback else{ - var f = query_args[query_args.length-1]; - query_args[query_args.length-1] = function(){ // add callback() call to the end of the function - f.apply(f, arguments); + const f = query_args[query_args.length-1]; + query_args[query_args.length-1] = function(...args) { // add callback() call to the end of the function + f.apply(f, args); callback(); } } - self.query.apply(self, query_args); + self.query(...query_args); }); } @@ -160,7 +159,7 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ if (!bReady){ console.log("takeConnectionFromPool will wait for ready"); - eventEmitter.once('ready', function(){ + eventEmitter.once('ready', () => { console.log("db is now ready"); takeConnectionFromPool(handleConnection); }); @@ -168,7 +167,7 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ } // first, try to find a free connection - for (var i=0; i { + let last_arg = args[args.length - 1]; + const bHasCallback = (typeof last_arg === 'function'); if (!bHasCallback) // no callback - last_arg = function(){}; + last_arg = () => {}; - var count_arguments_without_callback = bHasCallback ? (args.length-1) : args.length; - var new_args = []; + const count_arguments_without_callback = bHasCallback ? (args.length-1) : args.length; + const new_args = []; - for (var i=0; i { connection.release(); last_arg(rows); }); - connection.query.apply(connection, new_args); + connection.query(...new_args); }); } function close(cb){ if (!cb) - cb = function(){}; + cb = () => {}; bReady = false; if (arrConnections.length === 0) return cb(); @@ -235,7 +234,7 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ // interval is string such as -8 SECOND function addTime(interval){ - return "datetime('now', '"+interval+"')"; + return `datetime('now', '${interval}')`; } function getNow(){ @@ -243,11 +242,11 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ } function getUnixTimestamp(date){ - return "strftime('%s', "+date+")"; + return `strftime('%s', ${date})`; } function getFromUnixTime(ts){ - return "datetime("+ts+", 'unixepoch')"; + return `datetime(${ts}, 'unixepoch')`; } function getRandom(){ @@ -255,11 +254,11 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ } function forceIndex(index){ - return "INDEXED BY " + index; + return `INDEXED BY ${index}`; } function dropTemporaryTable(table) { - return "DROP TABLE IF EXISTS " + table; + return `DROP TABLE IF EXISTS ${table}`; } // note that IGNORE behaves differently from mysql. In particular, if you insert and forget to specify a NOT NULL colum without DEFAULT value, @@ -270,17 +269,17 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ function escape(str){ if (typeof str === 'string') - return "'"+str.replace(/'/g, "''")+"'"; + return `'${str.replace(/'/g, "''")}'`; else if (Array.isArray(str)) - return str.map(function(member){ return escape(member); }).join(","); + return str.map(member => escape(member)).join(","); else - throw Error("escape: unknown type "+(typeof str)); + throw Error(`escape: unknown type ${typeof str}`); } createDatabaseIfNecessary(db_name, onDbReady); - var pool = {}; + const pool = {}; pool.query = query; pool.addQuery = addQuery; pool.takeConnectionFromPool = takeConnectionFromPool; @@ -303,11 +302,11 @@ module.exports = function(db_name, MAX_CONNECTIONS, bReadOnly){ // the function modifies first two memebers of the args array in place // will misbehave if there are ? in SQL comments function expandArrayPlaceholders(args){ - var sql = args[0]; - var params = args[1]; + const sql = args[0]; + const params = args[1]; if (!Array.isArray(params) || params.length === 0) return; - var assocLengthsOfArrayParams = {}; + const assocLengthsOfArrayParams = {}; for (var i=0; i { console.log("database file already exists"); onDbReady(); }, function onSqliteNotInited(err) { // file not found console.log("will copy initial database file"); - window.resolveLocalFileSystemURL(window.cordova.file.applicationDirectory + "/www/" + initial_db_filename, function(fileEntry) { + window.resolveLocalFileSystemURL(`${window.cordova.file.applicationDirectory}/www/${initial_db_filename}`, fileEntry => { console.log("got initial db fileentry"); // get parent dir - window.resolveLocalFileSystemURL(getParentDirPath(), function(parentDirEntry) { + window.resolveLocalFileSystemURL(getParentDirPath(), parentDirEntry => { console.log("resolved parent dir"); - parentDirEntry.getDirectory(getDatabaseDirName(), {create: true}, function(dbDirEntry){ + parentDirEntry.getDirectory(getDatabaseDirName(), {create: true}, dbDirEntry => { console.log("resolved db dir"); - fileEntry.copyTo(dbDirEntry, db_name, function(){ + fileEntry.copyTo(dbDirEntry, db_name, () => { console.log("copied initial cordova database"); onDbReady(); - }, function(err){ - throw Error("failed to copyTo: "+JSON.stringify(err)); + }, err => { + throw Error(`failed to copyTo: ${JSON.stringify(err)}`); }); - }, function(err){ - throw Error("failed to getDirectory databases: "+JSON.stringify(err)); + }, err => { + throw Error(`failed to getDirectory databases: ${JSON.stringify(err)}`); }); - }, function(err){ - throw Error("failed to resolveLocalFileSystemURL of parent dir: "+JSON.stringify(err)); + }, err => { + throw Error(`failed to resolveLocalFileSystemURL of parent dir: ${JSON.stringify(err)}`); }); - }, function(err){ - throw Error("failed to getFile: "+JSON.stringify(err)); + }, err => { + throw Error(`failed to getFile: ${JSON.stringify(err)}`); }); }); }, function onFailure(err){ - throw Error("failed to requestFileSystem: "+err); + throw Error(`failed to requestFileSystem: ${err}`); }); }, false); } else{ // copy initial db to app folder - var fs = require('fs'+''); - fs.stat(path + db_name, function(err, stats){ - console.log("stat "+err); + const fs = require('fs'+''); + fs.stat(path + db_name, (err, stats) => { + console.log(`stat ${err}`); if (!err) // already exists return onDbReady(); console.log("will copy initial db"); - var mode = parseInt('700', 8); - var parent_dir = require('path'+'').dirname(path); - fs.mkdir(parent_dir, mode, function(err){ - console.log('mkdir '+parent_dir+': '+err); - fs.mkdir(path, mode, function(err){ - console.log('mkdir '+path+': '+err); - fs.createReadStream(__dirname + '/' + initial_db_filename).pipe(fs.createWriteStream(path + db_name)).on('finish', onDbReady); + const mode = parseInt('700', 8); + const parent_dir = require('path'+'').dirname(path); + fs.mkdir(parent_dir, mode, err => { + console.log(`mkdir ${parent_dir}: ${err}`); + fs.mkdir(path, mode, err => { + console.log(`mkdir ${path}: ${err}`); + fs.createReadStream(`${__dirname}/${initial_db_filename}`).pipe(fs.createWriteStream(path + db_name)).on('finish', onDbReady); }); }); }); diff --git a/storage.js b/storage.js index 79b1580a..29b81b56 100644 --- a/storage.js +++ b/storage.js @@ -1,37 +1,36 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var _ = require('lodash'); -var db = require('./db.js'); -var conf = require('./conf.js'); -var objectHash = require("./object_hash.js"); -var constants = require("./constants.js"); -var mutex = require('./mutex.js'); -var archiving = require('./archiving.js'); -var profiler = require('./profiler.js'); - -var MAX_INT32 = Math.pow(2, 31) - 1; - -var genesis_ball = objectHash.getBallHash(constants.GENESIS_UNIT); - -var MAX_ITEMS_IN_CACHE = 300; -var assocKnownUnits = {}; -var assocCachedUnits = {}; -var assocCachedUnitAuthors = {}; -var assocCachedUnitWitnesses = {}; -var assocCachedAssetInfos = {}; - -var assocUnstableUnits = {}; -var assocStableUnits = {}; - -var min_retrievable_mci = null; +const async = require('async'); +const _ = require('lodash'); +const db = require('./db.js'); +const conf = require('./conf.js'); +const objectHash = require("./object_hash.js"); +const constants = require("./constants.js"); +const mutex = require('./mutex.js'); +const archiving = require('./archiving.js'); +const profiler = require('./profiler.js'); + +const MAX_INT32 = Math.pow(2, 31) - 1; + +const genesis_ball = objectHash.getBallHash(constants.GENESIS_UNIT); + +const MAX_ITEMS_IN_CACHE = 300; +const assocKnownUnits = {}; +const assocCachedUnits = {}; +const assocCachedUnitAuthors = {}; +const assocCachedUnitWitnesses = {}; +let assocCachedAssetInfos = {}; + +const assocUnstableUnits = {}; +const assocStableUnits = {}; + +let min_retrievable_mci = null; initializeMinRetrievableMci(); function readJoint(conn, unit, callbacks) { if (!conf.bSaveJointJson) return readJointDirectly(conn, unit, callbacks); - conn.query("SELECT json FROM joints WHERE unit=?", [unit], function(rows){ + conn.query("SELECT json FROM joints WHERE unit=?", [unit], rows => { if (rows.length === 0) return readJointDirectly(conn, unit, callbacks); callbacks.ifFound(JSON.parse(rows[0].json)); @@ -39,40 +38,40 @@ function readJoint(conn, unit, callbacks) { } function readJointDirectly(conn, unit, callbacks, bRetrying) { - console.log("\nreading unit "+unit); + console.log(`\nreading unit ${unit}`); if (min_retrievable_mci === null){ console.log("min_retrievable_mci not known yet"); - setTimeout(function(){ + setTimeout(() => { readJointDirectly(conn, unit, callbacks); }, 1000); return; } //profiler.start(); conn.query( - "SELECT units.unit, version, alt, witness_list_unit, last_ball_unit, balls.ball AS last_ball, is_stable, \n\ - content_hash, headers_commission, payload_commission, main_chain_index, "+conn.getUnixTimestamp("units.creation_date")+" AS timestamp \n\ - FROM units LEFT JOIN balls ON last_ball_unit=balls.unit WHERE units.unit=?", + `SELECT units.unit, version, alt, witness_list_unit, last_ball_unit, balls.ball AS last_ball, is_stable, \n\ + content_hash, headers_commission, payload_commission, main_chain_index, ${conn.getUnixTimestamp("units.creation_date")} AS timestamp \n\ + FROM units LEFT JOIN balls ON last_ball_unit=balls.unit WHERE units.unit=?`, [unit], - function(unit_rows){ + unit_rows => { if (unit_rows.length === 0){ //profiler.stop('read'); return callbacks.ifNotFound(); } - var objUnit = unit_rows[0]; - var objJoint = {unit: objUnit}; - var main_chain_index = objUnit.main_chain_index; + const objUnit = unit_rows[0]; + const objJoint = {unit: objUnit}; + const main_chain_index = objUnit.main_chain_index; //delete objUnit.main_chain_index; objUnit.timestamp = parseInt(objUnit.timestamp); - var bFinalBad = !!objUnit.content_hash; - var bStable = objUnit.is_stable; + const bFinalBad = !!objUnit.content_hash; + const bStable = objUnit.is_stable; delete objUnit.is_stable; objectHash.cleanNulls(objUnit); - var bVoided = (objUnit.content_hash && main_chain_index < min_retrievable_mci); - var bRetrievable = (main_chain_index >= min_retrievable_mci || main_chain_index === null); + const bVoided = (objUnit.content_hash && main_chain_index < min_retrievable_mci); + const bRetrievable = (main_chain_index >= min_retrievable_mci || main_chain_index === null); if (!conf.bLight && !objUnit.last_ball) - throw Error("no last ball in unit "+JSON.stringify(objUnit)); + throw Error(`no last ball in unit ${JSON.stringify(objUnit)}`); // unit hash verification below will fail if: // 1. the unit was received already voided, i.e. its messages are stripped and content_hash is set @@ -88,70 +87,70 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { delete objUnit.content_hash; async.series([ - function(callback){ // parents + callback => { // parents conn.query( "SELECT parent_unit \n\ FROM parenthoods \n\ WHERE child_unit=? \n\ ORDER BY parent_unit", [unit], - function(rows){ + rows => { if (rows.length === 0) return callback(); - objUnit.parent_units = rows.map(function(row){ return row.parent_unit; }); + objUnit.parent_units = rows.map(({parent_unit}) => parent_unit); callback(); } ); }, - function(callback){ // ball + callback => { // ball if (bRetrievable && !isGenesisUnit(unit)) return callback(); // include the .ball field even if it is not stable yet, because its parents might have been changed // and the receiver should not attempt to verify them - conn.query("SELECT ball FROM balls WHERE unit=?", [unit], function(rows){ + conn.query("SELECT ball FROM balls WHERE unit=?", [unit], rows => { if (rows.length === 0) return callback(); objJoint.ball = rows[0].ball; callback(); }); }, - function(callback){ // skiplist + callback => { // skiplist if (bRetrievable) return callback(); - conn.query("SELECT skiplist_unit FROM skiplist_units WHERE unit=? ORDER BY skiplist_unit", [unit], function(rows){ + conn.query("SELECT skiplist_unit FROM skiplist_units WHERE unit=? ORDER BY skiplist_unit", [unit], rows => { if (rows.length === 0) return callback(); - objJoint.skiplist_units = rows.map(function(row){ return row.skiplist_unit; }); + objJoint.skiplist_units = rows.map(({skiplist_unit}) => skiplist_unit); callback(); }); }, - function(callback){ // witnesses - conn.query("SELECT address FROM unit_witnesses WHERE unit=? ORDER BY address", [unit], function(rows){ + callback => { // witnesses + conn.query("SELECT address FROM unit_witnesses WHERE unit=? ORDER BY address", [unit], rows => { if (rows.length > 0) - objUnit.witnesses = rows.map(function(row){ return row.address; }); + objUnit.witnesses = rows.map(({address}) => address); callback(); }); }, - function(callback){ // earned_headers_commission_recipients + callback => { // earned_headers_commission_recipients if (bVoided) return callback(); conn.query("SELECT address, earned_headers_commission_share FROM earned_headers_commission_recipients \ WHERE unit=? ORDER BY address", [unit], - function(rows){ + rows => { if (rows.length > 0) objUnit.earned_headers_commission_recipients = rows; callback(); } ); }, - function(callback){ // authors - conn.query("SELECT address, definition_chash FROM unit_authors WHERE unit=? ORDER BY address", [unit], function(rows){ + callback => { // authors + conn.query("SELECT address, definition_chash FROM unit_authors WHERE unit=? ORDER BY address", [unit], rows => { objUnit.authors = []; async.eachSeries( rows, - function(row, cb){ - var author = {address: row.address}; + ({address, definition_chash}, cb) => { + const author = {address: address}; function onAuthorDone(){ objUnit.authors.push(author); @@ -164,19 +163,19 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { conn.query( "SELECT path, authentifier FROM authentifiers WHERE unit=? AND address=?", [unit, author.address], - function(sig_rows){ - for (var i=0; i { + for (let i=0; i { callback(); } ); }); }, - function(callback){ // messages + callback => { // messages if (bVoided) return callback(); conn.query( "SELECT app, payload_hash, payload_location, payload, payload_uri, payload_uri_hash, message_index \n\ FROM messages WHERE unit=? ORDER BY message_index", [unit], - function(rows){ + rows => { if (rows.length === 0){ if (conf.bLight) - throw new Error("no messages in unit "+unit); + throw new Error(`no messages in unit ${unit}`); return callback(); // any errors will be caught by verifying unit hash } objUnit.messages = []; async.eachSeries( rows, - function(row, cb){ - var objMessage = row; - var message_index = row.message_index; + (row, cb) => { + const objMessage = row; + const message_index = row.message_index; delete objMessage.message_index; objectHash.cleanNulls(objMessage); objUnit.messages.push(objMessage); @@ -217,12 +216,12 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { conn.query( "SELECT spend_proof, address FROM spend_proofs WHERE unit=? AND message_index=? ORDER BY spend_proof_index", [unit, message_index], - function(proof_rows){ + proof_rows => { if (proof_rows.length === 0) return cb(); objMessage.spend_proofs = []; - for (var i=0; i { if (dch_rows.length === 0) throw Error("no definition change?"); objMessage.payload = dch_rows[0]; @@ -253,14 +252,14 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { case "poll": conn.query( "SELECT question FROM polls WHERE unit=? AND message_index=?", [unit, message_index], - function(poll_rows){ + poll_rows => { if (poll_rows.length !== 1) throw Error("no poll question or too many?"); objMessage.payload = {question: poll_rows[0].question}; - conn.query("SELECT choice FROM poll_choices WHERE unit=? ORDER BY choice_index", [unit], function(ch_rows){ + conn.query("SELECT choice FROM poll_choices WHERE unit=? ORDER BY choice_index", [unit], ch_rows => { if (ch_rows.length === 0) throw Error("no choices?"); - objMessage.payload.choices = ch_rows.map(function(choice_row){ return choice_row.choice; }); + objMessage.payload.choices = ch_rows.map(({choice}) => choice); addSpendProofs(); }); } @@ -270,7 +269,7 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { case "vote": conn.query( "SELECT poll_unit, choice FROM votes WHERE unit=? AND message_index=?", [unit, message_index], - function(vote_rows){ + vote_rows => { if (vote_rows.length !== 1) throw Error("no vote choice or too many?"); objMessage.payload = {unit: vote_rows[0].poll_unit, choice: vote_rows[0].choice}; @@ -286,7 +285,7 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { issue_condition, transfer_condition \n\ FROM assets WHERE unit=? AND message_index=?", [unit, message_index], - function(asset_rows){ + asset_rows => { if (asset_rows.length !== 1) throw Error("no asset or too many?"); objMessage.payload = asset_rows[0]; @@ -303,38 +302,36 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { if (objMessage.payload.transfer_condition) objMessage.payload.transfer_condition = JSON.parse(objMessage.payload.transfer_condition); - var addAttestors = function(next){ + const addAttestors = next => { if (!objMessage.payload.spender_attested) return next(); conn.query( "SELECT attestor_address FROM asset_attestors \n\ WHERE unit=? AND message_index=? ORDER BY attestor_address", [unit, message_index], - function(att_rows){ + att_rows => { if (att_rows.length === 0) throw Error("no attestors?"); - objMessage.payload.attestors = att_rows.map(function(att_row){ - return att_row.attestor_address; - }); + objMessage.payload.attestors = att_rows.map(({attestor_address}) => attestor_address); next(); } ); }; - var addDenominations = function(next){ + const addDenominations = next => { if (!objMessage.payload.fixed_denominations) return next(); conn.query( "SELECT denomination, count_coins FROM asset_denominations \n\ WHERE asset=? ORDER BY denomination", [unit], - function(denom_rows){ + denom_rows => { if (denom_rows.length === 0) throw Error("no denominations?"); - objMessage.payload.denominations = denom_rows.map(function(denom_row){ - var denom = {denomination: denom_row.denomination}; - if (denom_row.count_coins) - denom.count_coins = denom_row.count_coins; + objMessage.payload.denominations = denom_rows.map(({denomination, count_coins}) => { + const denom = {denomination: denomination}; + if (count_coins) + denom.count_coins = count_coins; return denom; }); next(); @@ -352,14 +349,14 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { "SELECT attestor_address, asset FROM asset_attestors \n\ WHERE unit=? AND message_index=? ORDER BY attestor_address", [unit, message_index], - function(att_rows){ + att_rows => { if (att_rows.length === 0) throw Error("no attestors?"); objMessage.payload = {asset: att_rows[0].asset}; if (att_rows.length > 1 - && att_rows.some(function(att_row){ return (att_row.asset !== objMessage.payload.asset) })) + && att_rows.some(({asset}) => asset !== objMessage.payload.asset)) throw Error("different assets in attestor list"); - objMessage.payload.attestors = att_rows.map(function(att_row){ return att_row.attestor_address;}); + objMessage.payload.attestors = att_rows.map(({attestor_address}) => attestor_address); addSpendProofs(); } ); @@ -368,13 +365,13 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { case "data_feed": conn.query( "SELECT feed_name, `value`, int_value FROM data_feeds WHERE unit=? AND message_index=?", [unit, message_index], - function(df_rows){ + df_rows => { if (df_rows.length === 0) throw Error("no data feed?"); objMessage.payload = {}; - df_rows.forEach(function(df_row){ - objMessage.payload[df_row.feed_name] = - (typeof df_row.value === 'string') ? df_row.value : Number(df_row.int_value); + df_rows.forEach(({feed_name, value, int_value}) => { + objMessage.payload[feed_name] = + (typeof value === 'string') ? value : Number(int_value); }); addSpendProofs(); } @@ -391,10 +388,10 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { case "payment": objMessage.payload = {}; - var prev_asset; - var prev_denomination; + let prev_asset; + let prev_denomination; - var readInputs = function(cb2){ + const readInputs = cb2 => { conn.query( "SELECT type, denomination, assets.fixed_denominations, \n\ src_unit AS unit, src_message_index AS message_index, src_output_index AS output_index, \n\ @@ -404,14 +401,14 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { WHERE inputs.unit=? AND inputs.message_index=? \n\ ORDER BY input_index", [unit, message_index], - function(input_rows){ + input_rows => { objMessage.payload.inputs = []; - for (var i=0; i0){ if (asset !== prev_asset) throw Error("different assets in inputs?"); @@ -441,15 +438,15 @@ function readJointDirectly(conn, unit, callbacks, bRetrying) { } ); }; - var readOutputs = function(cb2){ + const readOutputs = cb2 => { objMessage.payload.outputs = []; conn.query( // we don't select blinding because it's absent on public payments "SELECT address, amount, asset, denomination \n\ FROM outputs WHERE unit=? AND message_index=? ORDER BY output_index", [unit, message_index], - function(output_rows){ - for (var i=0; i { + for (let i=0; i { //profiler.stop('read'); // verify unit hash. Might fail if the unit was archived while reading, in this case retry // light wallets don't have last_ball, don't verify their hashes if (!conf.bLight && !isCorrectHash(objUnit, unit)){ if (bRetrying) - throw Error("unit hash verification failed, unit: "+unit+", objUnit: "+JSON.stringify(objUnit)); + throw Error(`unit hash verification failed, unit: ${unit}, objUnit: ${JSON.stringify(objUnit)}`); console.log("unit hash verification failed, will retry"); - return setTimeout(function(){ + return setTimeout(() => { readJointDirectly(conn, unit, callbacks, true); }, 60*1000); } if (!conf.bSaveJointJson || !bStable || (bFinalBad && bRetrievable) || bRetrievable) return callbacks.ifFound(objJoint); - conn.query("INSERT "+db.getIgnore()+" INTO joints (unit, json) VALUES (?,?)", [unit, JSON.stringify(objJoint)], function(){ + conn.query(`INSERT ${db.getIgnore()} INTO joints (unit, json) VALUES (?,?)`, [unit, JSON.stringify(objJoint)], () => { callbacks.ifFound(objJoint); }); }); @@ -513,13 +510,13 @@ function isCorrectHash(objUnit, unit){ // add .ball even if it is not retrievable function readJointWithBall(conn, unit, handleJoint) { readJoint(conn, unit, { - ifNotFound: function(){ - throw Error("joint not found, unit "+unit); + ifNotFound() { + throw Error(`joint not found, unit ${unit}`); }, - ifFound: function(objJoint){ + ifFound(objJoint) { if (objJoint.ball) return handleJoint(objJoint); - conn.query("SELECT ball FROM balls WHERE unit=?", [unit], function(rows){ + conn.query("SELECT ball FROM balls WHERE unit=?", [unit], rows => { if (rows.length === 1) objJoint.ball = rows[0].ball; handleJoint(objJoint); @@ -531,15 +528,15 @@ function readJointWithBall(conn, unit, handleJoint) { function readWitnessList(conn, unit, handleWitnessList, bAllowEmptyList){ - var arrWitnesses = assocCachedUnitWitnesses[unit]; + let arrWitnesses = assocCachedUnitWitnesses[unit]; if (arrWitnesses) return handleWitnessList(arrWitnesses); - conn.query("SELECT address FROM unit_witnesses WHERE unit=? ORDER BY address", [unit], function(rows){ + conn.query("SELECT address FROM unit_witnesses WHERE unit=? ORDER BY address", [unit], rows => { if (!bAllowEmptyList && rows.length === 0) - throw Error("witness list of unit "+unit+" not found"); + throw Error(`witness list of unit ${unit} not found`); if (rows.length > 0 && rows.length !== constants.COUNT_WITNESSES) - throw Error("wrong number of witnesses in unit "+unit); - arrWitnesses = rows.map(function(row){ return row.address; }); + throw Error(`wrong number of witnesses in unit ${unit}`); + arrWitnesses = rows.map(({address}) => address); if (rows.length > 0) assocCachedUnitWitnesses[unit] = arrWitnesses; handleWitnessList(arrWitnesses); @@ -547,14 +544,14 @@ function readWitnessList(conn, unit, handleWitnessList, bAllowEmptyList){ } function readWitnesses(conn, unit, handleWitnessList){ - var arrWitnesses = assocCachedUnitWitnesses[unit]; + const arrWitnesses = assocCachedUnitWitnesses[unit]; if (arrWitnesses) return handleWitnessList(arrWitnesses); - conn.query("SELECT witness_list_unit FROM units WHERE unit=?", [unit], function(rows){ + conn.query("SELECT witness_list_unit FROM units WHERE unit=?", [unit], rows => { if (rows.length === 0) - throw Error("unit "+unit+" not found"); - var witness_list_unit = rows[0].witness_list_unit; - readWitnessList(conn, witness_list_unit ? witness_list_unit : unit, function(arrWitnesses){ + throw Error(`unit ${unit} not found`); + const witness_list_unit = rows[0].witness_list_unit; + readWitnessList(conn, witness_list_unit ? witness_list_unit : unit, arrWitnesses => { assocCachedUnitWitnesses[unit] = arrWitnesses; handleWitnessList(arrWitnesses); }); @@ -569,27 +566,27 @@ function determineIfWitnessAddressDefinitionsHaveReferences(conn, arrWitnesses, SELECT 1 FROM definitions WHERE definition_chash IN(?) AND has_references=1 \n\ LIMIT 1", [arrWitnesses, arrWitnesses], - function(rows){ - handleResult(rows.length > 0); + ({length}) => { + handleResult(length > 0); } ); } function determineWitnessedLevelAndBestParent(conn, arrParentUnits, arrWitnesses, handleWitnessedLevelAndBestParent){ - var arrCollectedWitnesses = []; - var my_best_parent_unit; + const arrCollectedWitnesses = []; + let my_best_parent_unit; function addWitnessesAndGoUp(start_unit){ - readStaticUnitProps(conn, start_unit, function(props){ - var best_parent_unit = props.best_parent_unit; - var level = props.level; + readStaticUnitProps(conn, start_unit, props => { + const best_parent_unit = props.best_parent_unit; + const level = props.level; if (level === null) throw Error("null level in updateWitnessedLevel"); if (level === 0) // genesis return handleWitnessedLevelAndBestParent(0, my_best_parent_unit); - readUnitAuthors(conn, start_unit, function(arrAuthors){ - for (var i=0; i { + for (let i=0; i { if (!best_parent_unit) - throw Error("no best parent of "+arrParentUnits.join(', ')); + throw Error(`no best parent of ${arrParentUnits.join(', ')}`); my_best_parent_unit = best_parent_unit; addWitnessesAndGoUp(best_parent_unit); }); @@ -637,8 +634,8 @@ function readDefinitionByAddress(conn, address, max_mci, callbacks){ "SELECT definition_chash FROM address_definition_changes CROSS JOIN units USING(unit) \n\ WHERE address=? AND is_stable=1 AND sequence='good' AND main_chain_index<=? ORDER BY level DESC LIMIT 1", [address, max_mci], - function(rows){ - var definition_chash = (rows.length > 0) ? rows[0].definition_chash : address; + rows => { + const definition_chash = (rows.length > 0) ? rows[0].definition_chash : address; readDefinitionAtMci(conn, definition_chash, max_mci, callbacks); } ); @@ -646,10 +643,10 @@ function readDefinitionByAddress(conn, address, max_mci, callbacks){ // max_mci must be stable function readDefinitionAtMci(conn, definition_chash, max_mci, callbacks){ - var sql = "SELECT definition FROM definitions CROSS JOIN unit_authors USING(definition_chash) CROSS JOIN units USING(unit) \n\ + const sql = "SELECT definition FROM definitions CROSS JOIN unit_authors USING(definition_chash) CROSS JOIN units USING(unit) \n\ WHERE definition_chash=? AND is_stable=1 AND sequence='good' AND main_chain_index<=?"; - var params = [definition_chash, max_mci]; - conn.query(sql, params, function(rows){ + const params = [definition_chash, max_mci]; + conn.query(sql, params, rows => { if (rows.length === 0) return callbacks.ifDefinitionNotFound(definition_chash); callbacks.ifFound(JSON.parse(rows[0].definition)); @@ -657,7 +654,7 @@ function readDefinitionAtMci(conn, definition_chash, max_mci, callbacks){ } function readDefinition(conn, definition_chash, callbacks){ - conn.query("SELECT definition FROM definitions WHERE definition_chash=?", [definition_chash], function(rows){ + conn.query("SELECT definition FROM definitions WHERE definition_chash=?", [definition_chash], rows => { if (rows.length === 0) return callbacks.ifDefinitionNotFound(definition_chash); callbacks.ifFound(JSON.parse(rows[0].definition)); @@ -665,13 +662,13 @@ function readDefinition(conn, definition_chash, callbacks){ } function readFreeJoints(ifFoundFreeBall, onDone){ - db.query("SELECT units.unit FROM units LEFT JOIN archived_joints USING(unit) WHERE is_free=1 AND archived_joints.unit IS NULL", function(rows){ - async.each(rows, function(row, cb){ - readJoint(db, row.unit, { - ifNotFound: function(){ + db.query("SELECT units.unit FROM units LEFT JOIN archived_joints USING(unit) WHERE is_free=1 AND archived_joints.unit IS NULL", rows => { + async.each(rows, ({unit}, cb) => { + readJoint(db, unit, { + ifNotFound() { throw Error("free ball lost"); }, - ifFound: function(objJoint){ + ifFound(objJoint) { ifFoundFreeBall(objJoint); cb(); } @@ -699,19 +696,19 @@ function readUnitProps(conn, unit, handleProps){ conn.query( "SELECT unit, level, latest_included_mc_index, main_chain_index, is_on_main_chain, is_free, is_stable, witnessed_level FROM units WHERE unit=?", [unit], - function(rows){ + rows => { if (rows.length !== 1) throw Error("not 1 row"); - var props = rows[0]; + const props = rows[0]; if (props.is_stable) assocStableUnits[unit] = props; else{ - var props2 = _.cloneDeep(assocUnstableUnits[unit]); + const props2 = _.cloneDeep(assocUnstableUnits[unit]); if (!props2) - throw Error("no unstable props of "+unit); + throw Error(`no unstable props of ${unit}`); delete props2.parent_units; if (!_.isEqual(props, props2)) - throw Error("different props of "+unit+", mem: "+JSON.stringify(props2)+", db: "+JSON.stringify(props)); + throw Error(`different props of ${unit}, mem: ${JSON.stringify(props2)}, db: ${JSON.stringify(props)}`); } handleProps(props); } @@ -719,24 +716,25 @@ function readUnitProps(conn, unit, handleProps){ } function readPropsOfUnits(conn, earlier_unit, arrLaterUnits, handleProps){ - var bEarlierInLaterUnits = (arrLaterUnits.indexOf(earlier_unit) !== -1); + const bEarlierInLaterUnits = (arrLaterUnits.indexOf(earlier_unit) !== -1); conn.query( "SELECT unit, level, latest_included_mc_index, main_chain_index, is_on_main_chain, is_free FROM units WHERE unit IN(?, ?)", [earlier_unit, arrLaterUnits], - function(rows){ - if (rows.length !== arrLaterUnits.length + (bEarlierInLaterUnits ? 0 : 1)) - throw Error("wrong number of rows for earlier "+earlier_unit+", later "+arrLaterUnits); - var objEarlierUnitProps, arrLaterUnitProps = []; - for (var i=0; i { + if (rows.length !== arrLaterUnits.length + (bEarlierInLaterUnits ? 0 : 1)) + throw Error(`wrong number of rows for earlier ${earlier_unit}, later ${arrLaterUnits}`); + let objEarlierUnitProps; + const arrLaterUnitProps = []; + for (let i=0; i { if (rows.length === 0) return handleLastStableMcUnitProps(null); // empty database //throw "readLastStableMcUnitProps: no units on stable MC?"; @@ -760,15 +758,15 @@ function readLastStableMcUnitProps(conn, handleLastStableMcUnitProps){ } function readLastStableMcIndex(conn, handleLastStableMcIndex){ - readLastStableMcUnitProps(conn, function(objLastStableMcUnitProps){ + readLastStableMcUnitProps(conn, objLastStableMcUnitProps => { handleLastStableMcIndex(objLastStableMcUnitProps ? objLastStableMcUnitProps.main_chain_index : 0); }); } function readLastMainChainIndex(handleLastMcIndex){ - db.query("SELECT MAX(main_chain_index) AS last_mc_index FROM units", function(rows){ - var last_mc_index = rows[0].last_mc_index; + db.query("SELECT MAX(main_chain_index) AS last_mc_index FROM units", rows => { + let last_mc_index = rows[0].last_mc_index; if (last_mc_index === null) // empty database last_mc_index = 0; handleLastMcIndex(last_mc_index); @@ -784,9 +782,9 @@ function findLastBallMciOfMci(conn, mci, handleLastBallMci){ FROM units JOIN units AS lb_units ON units.last_ball_unit=lb_units.unit \n\ WHERE units.is_on_main_chain=1 AND units.main_chain_index=?", [mci], - function(rows){ + rows => { if (rows.length !== 1) - throw Error("last ball's mci count "+rows.length+" !== 1, mci = "+mci); + throw Error(`last ball's mci count ${rows.length} !== 1, mci = ${mci}`); if (rows[0].is_on_main_chain !== 1) throw Error("lb is not on mc?"); handleLastBallMci(rows[0].main_chain_index); @@ -799,42 +797,42 @@ function getMinRetrievableMci(){ } function updateMinRetrievableMciAfterStabilizingMci(conn, last_stable_mci, handleMinRetrievableMci){ - console.log("updateMinRetrievableMciAfterStabilizingMci "+last_stable_mci); - findLastBallMciOfMci(conn, last_stable_mci, function(last_ball_mci){ + console.log(`updateMinRetrievableMciAfterStabilizingMci ${last_stable_mci}`); + findLastBallMciOfMci(conn, last_stable_mci, last_ball_mci => { if (last_ball_mci <= min_retrievable_mci) // nothing new return handleMinRetrievableMci(min_retrievable_mci); - var prev_min_retrievable_mci = min_retrievable_mci; + const prev_min_retrievable_mci = min_retrievable_mci; min_retrievable_mci = last_ball_mci; // strip content off units older than min_retrievable_mci conn.query( // 'JOIN messages' filters units that are not stripped yet - "SELECT DISTINCT unit, content_hash FROM units "+db.forceIndex('byMcIndex')+" CROSS JOIN messages USING(unit) \n\ - WHERE main_chain_index<=? AND main_chain_index>=? AND sequence='final-bad'", + `SELECT DISTINCT unit, content_hash FROM units ${db.forceIndex('byMcIndex')} CROSS JOIN messages USING(unit) \n\ + WHERE main_chain_index<=? AND main_chain_index>=? AND sequence='final-bad'`, [min_retrievable_mci, prev_min_retrievable_mci], - function(unit_rows){ - var arrQueries = []; + unit_rows => { + const arrQueries = []; async.eachSeries( unit_rows, - function(unit_row, cb){ - var unit = unit_row.unit; + (unit_row, cb) => { + const unit = unit_row.unit; if (!unit_row.content_hash) - throw Error("no content hash in bad unit "+unit); + throw Error(`no content hash in bad unit ${unit}`); readJoint(conn, unit, { - ifNotFound: function(){ - throw Error("bad unit not found: "+unit); + ifNotFound() { + throw Error(`bad unit not found: ${unit}`); }, - ifFound: function(objJoint){ + ifFound(objJoint) { archiving.generateQueriesToArchiveJoint(conn, objJoint, 'voided', arrQueries, cb); } }); }, - function(){ + () => { if (arrQueries.length === 0) return handleMinRetrievableMci(min_retrievable_mci); - async.series(arrQueries, function(){ - unit_rows.forEach(function(unit_row){ - forgetUnit(unit_row.unit); + async.series(arrQueries, () => { + unit_rows.forEach(({unit}) => { + forgetUnit(unit); }); handleMinRetrievableMci(min_retrievable_mci); }); @@ -850,7 +848,7 @@ function initializeMinRetrievableMci(){ "SELECT MAX(lb_units.main_chain_index) AS min_retrievable_mci \n\ FROM units JOIN units AS lb_units ON units.last_ball_unit=lb_units.unit \n\ WHERE units.is_on_main_chain=1 AND units.is_stable=1", - function(rows){ + rows => { if (rows.length !== 1) throw Error("MAX() no rows?"); min_retrievable_mci = rows[0].min_retrievable_mci; @@ -862,9 +860,9 @@ function initializeMinRetrievableMci(){ function archiveJointAndDescendantsIfExists(from_unit){ - console.log('will archive if exists from unit '+from_unit); - db.query("SELECT 1 FROM units WHERE unit=?", [from_unit], function(rows){ - if (rows.length > 0) + console.log(`will archive if exists from unit ${from_unit}`); + db.query("SELECT 1 FROM units WHERE unit=?", [from_unit], ({length}) => { + if (length > 0) archiveJointAndDescendants(from_unit); }); } @@ -873,10 +871,10 @@ function archiveJointAndDescendants(from_unit){ db.executeInTransaction(function doWork(conn, cb){ function addChildren(arrParentUnits){ - conn.query("SELECT DISTINCT child_unit FROM parenthoods WHERE parent_unit IN(?)", [arrParentUnits], function(rows){ + conn.query("SELECT DISTINCT child_unit FROM parenthoods WHERE parent_unit IN(?)", [arrParentUnits], rows => { if (rows.length === 0) return archive(); - var arrChildUnits = rows.map(function(row){ return row.child_unit; }); + const arrChildUnits = rows.map(({child_unit}) => child_unit); arrUnits = arrUnits.concat(arrChildUnits); addChildren(arrChildUnits); }); @@ -886,23 +884,23 @@ function archiveJointAndDescendants(from_unit){ arrUnits = _.uniq(arrUnits); // does not affect the order arrUnits.reverse(); console.log('will archive', arrUnits); - var arrQueries = []; + const arrQueries = []; async.eachSeries( arrUnits, - function(unit, cb2){ + (unit, cb2) => { readJoint(conn, unit, { - ifNotFound: function(){ - throw Error("unit to be archived not found: "+unit); + ifNotFound() { + throw Error(`unit to be archived not found: ${unit}`); }, - ifFound: function(objJoint){ + ifFound(objJoint) { archiving.generateQueriesToArchiveJoint(conn, objJoint, 'uncovered', arrQueries, cb2); } }); }, - function(){ + () => { conn.addQuery(arrQueries, "DELETE FROM known_bad_joints"); - console.log('will execute '+arrQueries.length+' queries to archive'); - async.series(arrQueries, function(){ + console.log(`will execute ${arrQueries.length} queries to archive`); + async.series(arrQueries, () => { arrUnits.forEach(forgetUnit); cb(); }); @@ -910,12 +908,12 @@ function archiveJointAndDescendants(from_unit){ ); } - console.log('will archive from unit '+from_unit); + console.log(`will archive from unit ${from_unit}`); var arrUnits = [from_unit]; addChildren([from_unit]); }, function onDone(){ - console.log('done archiving from unit '+from_unit); + console.log(`done archiving from unit ${from_unit}`); }); } @@ -924,19 +922,19 @@ function archiveJointAndDescendants(from_unit){ // Assets function readAssetInfo(conn, asset, handleAssetInfo){ - var objAsset = assocCachedAssetInfos[asset]; + const objAsset = assocCachedAssetInfos[asset]; if (objAsset) return handleAssetInfo(objAsset); conn.query( "SELECT assets.*, main_chain_index, sequence, is_stable, address AS definer_address, unit AS asset \n\ FROM assets JOIN units USING(unit) JOIN unit_authors USING(unit) WHERE unit=?", [asset], - function(rows){ + rows => { if (rows.length > 1) throw Error("more than one asset?"); if (rows.length === 0) return handleAssetInfo(null); - var objAsset = rows[0]; + const objAsset = rows[0]; if (objAsset.issue_condition) objAsset.issue_condition = JSON.parse(objAsset.issue_condition); if (objAsset.transfer_condition) @@ -953,13 +951,13 @@ function readAsset(conn, asset, last_ball_mci, handleAsset){ if (conf.bLight) last_ball_mci = MAX_INT32; else - return readLastStableMcIndex(conn, function(last_stable_mci){ + return readLastStableMcIndex(conn, last_stable_mci => { readAsset(conn, asset, last_stable_mci, handleAsset); }); } - readAssetInfo(conn, asset, function(objAsset){ + readAssetInfo(conn, asset, objAsset => { if (!objAsset) - return handleAsset("asset "+asset+" not found"); + return handleAsset(`asset ${asset} not found`); if (objAsset.main_chain_index > last_ball_mci) return handleAsset("asset definition must be before last ball"); if (objAsset.sequence !== "good") @@ -972,8 +970,8 @@ function readAsset(conn, asset, last_ball_mci, handleAsset){ "SELECT MAX(level) AS max_level FROM asset_attestors CROSS JOIN units USING(unit) \n\ WHERE asset=? AND main_chain_index<=? AND is_stable=1 AND sequence='good'", [asset, last_ball_mci], - function(latest_rows){ - var max_level = latest_rows[0].max_level; + latest_rows => { + const max_level = latest_rows[0].max_level; if (!max_level) throw Error("no max level of asset attestors"); @@ -982,10 +980,10 @@ function readAsset(conn, asset, last_ball_mci, handleAsset){ "SELECT attestor_address FROM asset_attestors CROSS JOIN units USING(unit) \n\ WHERE asset=? AND level=? AND main_chain_index<=? AND is_stable=1 AND sequence='good'", [asset, max_level, last_ball_mci], - function(att_rows){ + att_rows => { if (att_rows.length === 0) throw Error("no attestors?"); - objAsset.arrAttestorAddresses = att_rows.map(function(att_row){ return att_row.attestor_address; }); + objAsset.arrAttestorAddresses = att_rows.map(({attestor_address}) => attestor_address); handleAsset(null, objAsset); } ); @@ -995,7 +993,13 @@ function readAsset(conn, asset, last_ball_mci, handleAsset){ } // filter only those addresses that are attested (doesn't work for light clients) -function filterAttestedAddresses(conn, objAsset, last_ball_mci, arrAddresses, handleAttestedAddresses){ +function filterAttestedAddresses( + conn, + {arrAttestorAddresses}, + last_ball_mci, + arrAddresses, + handleAttestedAddresses +) { conn.query( "SELECT DISTINCT address FROM attestations CROSS JOIN units USING(unit) \n\ WHERE attestor_address IN(?) AND address IN(?) AND main_chain_index<=? AND is_stable=1 AND sequence='good' \n\ @@ -1003,9 +1007,9 @@ function filterAttestedAddresses(conn, objAsset, last_ball_mci, arrAddresses, ha (SELECT main_chain_index FROM address_definition_changes JOIN units USING(unit) \n\ WHERE address_definition_changes.address=attestations.address ORDER BY main_chain_index DESC LIMIT 1), \n\ 0)", - [objAsset.arrAttestorAddresses, arrAddresses, last_ball_mci], - function(addr_rows){ - var arrAttestedAddresses = addr_rows.map(function(addr_row){ return addr_row.address; }); + [arrAttestorAddresses, arrAddresses, last_ball_mci], + addr_rows => { + const arrAttestedAddresses = addr_rows.map(({address}) => address); handleAttestedAddresses(arrAttestedAddresses); } ); @@ -1013,12 +1017,12 @@ function filterAttestedAddresses(conn, objAsset, last_ball_mci, arrAddresses, ha // note that light clients cannot check attestations function loadAssetWithListOfAttestedAuthors(conn, asset, last_ball_mci, arrAuthorAddresses, handleAsset){ - readAsset(conn, asset, last_ball_mci, function(err, objAsset){ + readAsset(conn, asset, last_ball_mci, (err, objAsset) => { if (err) return handleAsset(err); if (!objAsset.spender_attested) return handleAsset(null, objAsset); - filterAttestedAddresses(conn, objAsset, last_ball_mci, arrAuthorAddresses, function(arrAttestedAddresses){ + filterAttestedAddresses(conn, objAsset, last_ball_mci, arrAuthorAddresses, arrAttestedAddresses => { objAsset.arrAttestedAddresses = arrAttestedAddresses; handleAsset(null, objAsset); }); @@ -1031,7 +1035,7 @@ function findWitnessListUnit(conn, arrWitnesses, last_ball_mci, handleWitnessLis FROM witness_list_hashes CROSS JOIN units ON witness_list_hashes.witness_list_unit=unit \n\ WHERE witness_list_hash=? AND sequence='good' AND is_stable=1 AND main_chain_index<=?", [objectHash.getBase64Hash(arrWitnesses), last_ball_mci], - function(rows){ + rows => { handleWitnessListUnit((rows.length === 0) ? null : rows[0].witness_list_unit); } ); @@ -1039,39 +1043,39 @@ function findWitnessListUnit(conn, arrWitnesses, last_ball_mci, handleWitnessLis function sliceAndExecuteQuery(query, params, largeParam, callback) { if (typeof largeParam !== 'object' || largeParam.length === 0) return callback([]); - var CHUNK_SIZE = 200; - var length = largeParam.length; - var arrParams = []; - var newParams; - var largeParamPosition = params.indexOf(largeParam); + const CHUNK_SIZE = 200; + const length = largeParam.length; + const arrParams = []; + let newParams; + const largeParamPosition = params.indexOf(largeParam); - for (var offset = 0; offset < length; offset += CHUNK_SIZE) { + for (let offset = 0; offset < length; offset += CHUNK_SIZE) { newParams = params.slice(0); newParams[largeParamPosition] = largeParam.slice(offset, offset + CHUNK_SIZE); arrParams.push(newParams); } - var result = []; - async.eachSeries(arrParams, function(params, cb) { - db.query(query, params, function(rows) { + let result = []; + async.eachSeries(arrParams, (params, cb) => { + db.query(query, params, rows => { result = result.concat(rows); cb(); }); - }, function() { + }, () => { callback(result); }); } function filterNewOrUnstableUnits(arrUnits, handleFilteredUnits){ - sliceAndExecuteQuery("SELECT unit FROM units WHERE unit IN(?) AND is_stable=1", [arrUnits], arrUnits, function(rows) { - var arrKnownStableUnits = rows.map(function(row){ return row.unit; }); - var arrNewOrUnstableUnits = _.difference(arrUnits, arrKnownStableUnits); + sliceAndExecuteQuery("SELECT unit FROM units WHERE unit IN(?) AND is_stable=1", [arrUnits], arrUnits, rows => { + const arrKnownStableUnits = rows.map(({unit}) => unit); + const arrNewOrUnstableUnits = _.difference(arrUnits, arrKnownStableUnits); handleFilteredUnits(arrNewOrUnstableUnits); }); } // for unit that is not saved to the db yet -function determineBestParent(conn, objUnit, arrWitnesses, handleBestParent){ +function determineBestParent(conn, {parent_units, witness_list_unit}, arrWitnesses, handleBestParent) { // choose best parent among compatible parents only conn.query( "SELECT unit \n\ @@ -1086,12 +1090,12 @@ function determineBestParent(conn, objUnit, arrWitnesses, handleBestParent){ level-witnessed_level ASC, \n\ unit ASC \n\ LIMIT 1", - [objUnit.parent_units, objUnit.witness_list_unit, + [parent_units, witness_list_unit, arrWitnesses, constants.COUNT_WITNESSES - constants.MAX_WITNESS_LIST_MUTATIONS], - function(rows){ + rows => { if (rows.length !== 1) return handleBestParent(null); - var best_parent_unit = rows[0].unit; + const best_parent_unit = rows[0].unit; handleBestParent(best_parent_unit); } ); @@ -1100,7 +1104,7 @@ function determineBestParent(conn, objUnit, arrWitnesses, handleBestParent){ function determineIfHasWitnessListMutationsAlongMc(conn, objUnit, last_ball_unit, arrWitnesses, handleResult){ if (!objUnit.parent_units) // genesis return handleResult(); - buildListOfMcUnitsWithPotentiallyDifferentWitnesslists(conn, objUnit, last_ball_unit, arrWitnesses, function(bHasBestParent, arrMcUnits){ + buildListOfMcUnitsWithPotentiallyDifferentWitnesslists(conn, objUnit, last_ball_unit, arrWitnesses, (bHasBestParent, arrMcUnits) => { if (!bHasBestParent) return handleResult("no compatible best parent"); console.log("###### MC units ", arrMcUnits); @@ -1113,10 +1117,10 @@ function determineIfHasWitnessListMutationsAlongMc(conn, objUnit, last_ball_unit GROUP BY units.unit \n\ HAVING count_matching_witnesses { console.log(rows); if (rows.length > 0) - return handleResult("too many ("+(constants.COUNT_WITNESSES - rows[0].count_matching_witnesses)+") witness list mutations relative to MC unit "+rows[0].unit); + return handleResult(`too many (${constants.COUNT_WITNESSES - rows[0].count_matching_witnesses}) witness list mutations relative to MC unit ${rows[0].unit}`); handleResult(); } ); @@ -1127,22 +1131,22 @@ function determineIfHasWitnessListMutationsAlongMc(conn, objUnit, last_ball_unit function buildListOfMcUnitsWithPotentiallyDifferentWitnesslists(conn, objUnit, last_ball_unit, arrWitnesses, handleList){ function addAndGoUp(unit){ - readStaticUnitProps(conn, unit, function(props){ + readStaticUnitProps(conn, unit, ({witness_list_unit, best_parent_unit}) => { // the parent has the same witness list and the parent has already passed the MC compatibility test - if (objUnit.witness_list_unit && objUnit.witness_list_unit === props.witness_list_unit) + if (objUnit.witness_list_unit && objUnit.witness_list_unit === witness_list_unit) return handleList(true, arrMcUnits); else arrMcUnits.push(unit); if (unit === last_ball_unit) return handleList(true, arrMcUnits); - if (!props.best_parent_unit) - throw Error("no best parent of unit "+unit+"?"); - addAndGoUp(props.best_parent_unit); + if (!best_parent_unit) + throw Error(`no best parent of unit ${unit}?`); + addAndGoUp(best_parent_unit); }); } var arrMcUnits = []; - determineBestParent(conn, objUnit, arrWitnesses, function(best_parent_unit){ + determineBestParent(conn, objUnit, arrWitnesses, best_parent_unit => { if (!best_parent_unit) return handleList(false); addAndGoUp(best_parent_unit); @@ -1151,10 +1155,10 @@ function buildListOfMcUnitsWithPotentiallyDifferentWitnesslists(conn, objUnit, l function readStaticUnitProps(conn, unit, handleProps){ - var props = assocCachedUnits[unit]; + let props = assocCachedUnits[unit]; if (props) return handleProps(props); - conn.query("SELECT level, witnessed_level, best_parent_unit, witness_list_unit FROM units WHERE unit=?", [unit], function(rows){ + conn.query("SELECT level, witnessed_level, best_parent_unit, witness_list_unit FROM units WHERE unit=?", [unit], rows => { if (rows.length !== 1) throw Error("not 1 unit"); props = rows[0]; @@ -1164,13 +1168,13 @@ function readStaticUnitProps(conn, unit, handleProps){ } function readUnitAuthors(conn, unit, handleAuthors){ - var arrAuthors = assocCachedUnitAuthors[unit]; + const arrAuthors = assocCachedUnitAuthors[unit]; if (arrAuthors) return handleAuthors(arrAuthors); - conn.query("SELECT address FROM unit_authors WHERE unit=?", [unit], function(rows){ + conn.query("SELECT address FROM unit_authors WHERE unit=?", [unit], rows => { if (rows.length === 0) throw Error("no authors"); - var arrAuthors2 = rows.map(function(row){ return row.address; }).sort(); + const arrAuthors2 = rows.map(({address}) => address).sort(); // if (arrAuthors && arrAuthors.join('-') !== arrAuthors2.join('-')) // throw Error('cache is corrupt'); assocCachedUnitAuthors[unit] = arrAuthors2; @@ -1198,31 +1202,31 @@ function forgetUnit(unit){ function shrinkCache(){ if (Object.keys(assocCachedAssetInfos).length > MAX_ITEMS_IN_CACHE) assocCachedAssetInfos = {}; - console.log(Object.keys(assocUnstableUnits).length+" unstable units"); - var arrKnownUnits = Object.keys(assocKnownUnits); - var arrPropsUnits = Object.keys(assocCachedUnits); - var arrStableUnits = Object.keys(assocStableUnits); - var arrAuthorsUnits = Object.keys(assocCachedUnitAuthors); - var arrWitnessesUnits = Object.keys(assocCachedUnitWitnesses); + console.log(`${Object.keys(assocUnstableUnits).length} unstable units`); + const arrKnownUnits = Object.keys(assocKnownUnits); + const arrPropsUnits = Object.keys(assocCachedUnits); + const arrStableUnits = Object.keys(assocStableUnits); + const arrAuthorsUnits = Object.keys(assocCachedUnitAuthors); + const arrWitnessesUnits = Object.keys(assocCachedUnitWitnesses); if (arrPropsUnits.length < MAX_ITEMS_IN_CACHE && arrAuthorsUnits.length < MAX_ITEMS_IN_CACHE && arrWitnessesUnits.length < MAX_ITEMS_IN_CACHE && arrKnownUnits.length < MAX_ITEMS_IN_CACHE && arrStableUnits.length < MAX_ITEMS_IN_CACHE) return console.log('cache is small, will not shrink'); - var arrUnits = _.union(arrPropsUnits, arrAuthorsUnits, arrWitnessesUnits, arrKnownUnits, arrStableUnits); - console.log('will shrink cache, total units: '+arrUnits.length); - readLastStableMcIndex(db, function(last_stable_mci){ - var CHUNK_SIZE = 500; // there is a limit on the number of query params - for (var offset=0; offset { + const CHUNK_SIZE = 500; // there is a limit on the number of query params + for (let offset=0; offset { + console.log(`will remove ${rows.length} units from cache`); + rows.forEach(({unit}) => { + delete assocKnownUnits[unit]; + delete assocCachedUnits[unit]; + delete assocStableUnits[unit]; + delete assocCachedUnitAuthors[unit]; + delete assocCachedUnitWitnesses[unit]; }); } ); @@ -1237,18 +1241,18 @@ function initUnstableUnits(onDone){ db.query( "SELECT unit, level, latest_included_mc_index, main_chain_index, is_on_main_chain, is_free, is_stable, witnessed_level \n\ FROM units WHERE is_stable=0 ORDER BY +level", - function(rows){ + rows => { // assocUnstableUnits = {}; - rows.forEach(function(row){ + rows.forEach(row => { row.parent_units = []; assocUnstableUnits[row.unit] = row; }); console.log('initUnstableUnits 1 done'); db.query( - "SELECT parent_unit, child_unit FROM parenthoods WHERE child_unit IN("+Object.keys(assocUnstableUnits).map(db.escape)+")", - function(prows){ - prows.forEach(function(prow){ - assocUnstableUnits[prow.child_unit].parent_units.push(prow.parent_unit); + `SELECT parent_unit, child_unit FROM parenthoods WHERE child_unit IN(${Object.keys(assocUnstableUnits).map(db.escape)})`, + prows => { + prows.forEach(({child_unit, parent_unit}) => { + assocUnstableUnits[child_unit].parent_units.push(parent_unit); }); console.log('initUnstableUnits done'); if (onDone) @@ -1260,7 +1264,7 @@ function initUnstableUnits(onDone){ } function resetUnstableUnits(onDone){ - Object.keys(assocUnstableUnits).forEach(function(unit){ + Object.keys(assocUnstableUnits).forEach(unit => { delete assocUnstableUnits[unit]; }); initUnstableUnits(onDone); diff --git a/string_utils.js b/string_utils.js index 8394dcc6..478a9a86 100644 --- a/string_utils.js +++ b/string_utils.js @@ -1,7 +1,5 @@ /*jslint node: true */ -"use strict"; - -var STRING_JOIN_CHAR = "\x00"; +const STRING_JOIN_CHAR = "\x00"; /** * Converts the argument into a string by mapping data types to a prefixed string and concatenating all fields together. @@ -9,10 +7,10 @@ var STRING_JOIN_CHAR = "\x00"; * @returns {string} the string version of the value */ function getSourceString(obj) { - var arrComponents = []; + const arrComponents = []; function extractComponents(variable){ if (variable === null) - throw Error("null value in "+JSON.stringify(obj)); + throw Error(`null value in ${JSON.stringify(obj)}`); switch (typeof variable){ case "string": arrComponents.push("s", variable); @@ -26,26 +24,26 @@ function getSourceString(obj) { case "object": if (Array.isArray(variable)){ if (variable.length === 0) - throw Error("empty array in "+JSON.stringify(obj)); + throw Error(`empty array in ${JSON.stringify(obj)}`); arrComponents.push('['); - for (var i=0; i { if (typeof variable[key] === "undefined") - throw Error("undefined at "+key+" of "+JSON.stringify(obj)); + throw Error(`undefined at ${key} of ${JSON.stringify(obj)}`); arrComponents.push(key); extractComponents(variable[key]); }); } break; default: - throw Error("hash: unknown type="+(typeof variable)+" of "+variable+", object: "+JSON.stringify(obj)); + throw Error(`hash: unknown type=${typeof variable} of ${variable}, object: ${JSON.stringify(obj)}`); } } diff --git a/test/string_utils.test.js b/test/string_utils.test.js index bba9001a..a25ab91b 100644 --- a/test/string_utils.test.js +++ b/test/string_utils.test.js @@ -34,9 +34,7 @@ test('Test a boolean', t => { }); const arrayInput = ['a', 81, 'b', 'c', 3.6903690369, true]; -const arrayInputResultPairs = ['s', 'n', 's', 's', 'n', 'b'].map(function(typ, idx) { - return [typ, arrayInput[idx]].join(STRING_JOIN_CHAR); -}).join(STRING_JOIN_CHAR); +const arrayInputResultPairs = ['s', 'n', 's', 's', 'n', 'b'].map((typ, idx) => [typ, arrayInput[idx]].join(STRING_JOIN_CHAR)).join(STRING_JOIN_CHAR); const arrayInputResult = ['[', arrayInputResultPairs, ']'].join(STRING_JOIN_CHAR); test('Test an array', t => { t.true(getSourceString(arrayInput) === arrayInputResult); diff --git a/test/validation_utils.test.js b/test/validation_utils.test.js index e173f073..138efc8e 100644 --- a/test/validation_utils.test.js +++ b/test/validation_utils.test.js @@ -1,7 +1,7 @@ const test = require('ava'); const { check, gen, property } = require('testcheck'); -var ValidationUtils = require("../validation_utils.js"); +const ValidationUtils = require("../validation_utils.js"); /** * hasFieldsExcept diff --git a/uri.js b/uri.js index 86a7ae8a..7b27026c 100644 --- a/uri.js +++ b/uri.js @@ -1,23 +1,22 @@ /*jslint node: true */ -"use strict"; -var ValidationUtils = require("./validation_utils.js"); -var constants = require("./constants.js"); -var conf = require('./conf.js'); -var Mnemonic = require('bitcore-mnemonic'); +const ValidationUtils = require("./validation_utils.js"); +const constants = require("./constants.js"); +const conf = require('./conf.js'); +const Mnemonic = require('bitcore-mnemonic'); function parseUri(uri, callbacks){ - var protocol = conf.program || 'byteball'; - var re = new RegExp('^'+protocol+':(.+)$', 'i'); - var arrMatches = uri.match(re); + const protocol = conf.program || 'byteball'; + const re = new RegExp(`^${protocol}:(.+)$`, 'i'); + const arrMatches = uri.match(re); if (!arrMatches) - return callbacks.ifError("no "+protocol+" prefix"); - var value = arrMatches[1]; - var objRequest = {}; + return callbacks.ifError(`no ${protocol} prefix`); + const value = arrMatches[1]; + const objRequest = {}; // pairing / start a chat // var arrPairingMatches = value.match(/^([\w\/+]{44})@([\w.:\/-]+)(?:#|%23)([\w\/+]+)$/); - var arrPairingMatches = value.replace('%23', '#').match(/^([\w\/+]{44})@([\w.:\/-]+)#([\w\/+-]+)$/); + const arrPairingMatches = value.replace('%23', '#').match(/^([\w\/+]{44})@([\w.:\/-]+)#([\w\/+-]+)$/); if (arrPairingMatches){ objRequest.type = "pairing"; objRequest.pubkey = arrPairingMatches[1]; @@ -29,7 +28,7 @@ function parseUri(uri, callbacks){ } // authentication/authorization - var arrAuthMatches = value.match(/^auth\?(.+)$/); + const arrAuthMatches = value.match(/^auth\?(.+)$/); if (arrAuthMatches){ objRequest.type = "auth"; var query_string = arrAuthMatches[1]; @@ -46,8 +45,8 @@ function parseUri(uri, callbacks){ var arrParts = assocParams.device.split('@'); if (arrParts.length !== 2) return callbacks.ifError("not 2 parts in full device address"); - var pubkey = arrParts[0]; - var hub = arrParts[1]; + const pubkey = arrParts[0]; + const hub = arrParts[1]; if (pubkey.length !== constants.PUBKEY_LENGTH) return callbacks.ifError("pubkey length is not 44"); if (hub.match(/[^\w\.:-]/)) @@ -60,10 +59,10 @@ function parseUri(uri, callbacks){ } // claim textcoin using mnemonic - var arrMnemonicMatches = value.match(/^textcoin\?(.+)$/); + const arrMnemonicMatches = value.match(/^textcoin\?(.+)$/); if (arrMnemonicMatches){ objRequest.type = "textcoin"; - var mnemonic = arrMnemonicMatches[1].split('-').join(' '); + const mnemonic = arrMnemonicMatches[1].split('-').join(' '); try { if (Mnemonic.isValid(mnemonic)) { objRequest.mnemonic = mnemonic; @@ -80,35 +79,35 @@ function parseUri(uri, callbacks){ var arrParts = value.split('?'); if (arrParts.length > 2) return callbacks.ifError("too many question marks"); - var address = arrParts[0]; + const address = arrParts[0]; var query_string = arrParts[1]; if (!ValidationUtils.isValidAddress(address)) - return callbacks.ifError("address "+address+" is invalid"); + return callbacks.ifError(`address ${address} is invalid`); objRequest.type = "address"; objRequest.address = address; if (query_string){ var assocParams = parseQueryString(query_string); - var strAmount = assocParams.amount; + const strAmount = assocParams.amount; if (typeof strAmount === 'string'){ - var amount = parseInt(strAmount); - if (amount + '' !== strAmount) - return callbacks.ifError("invalid amount: "+strAmount); + const amount = parseInt(strAmount); + if (`${amount}` !== strAmount) + return callbacks.ifError(`invalid amount: ${strAmount}`); if (!ValidationUtils.isPositiveInteger(amount)) - return callbacks.ifError("nonpositive amount: "+strAmount); + return callbacks.ifError(`nonpositive amount: ${strAmount}`); objRequest.amount = amount; } - var asset = assocParams.asset; + const asset = assocParams.asset; if (typeof asset === 'string'){ if (asset !== 'base' && !ValidationUtils.isValidBase64(asset, constants.HASH_LENGTH)) // invalid asset - return callbacks.ifError('invalid asset: '+asset); + return callbacks.ifError(`invalid asset: ${asset}`); objRequest.asset = asset; } if (!objRequest.asset && objRequest.amount) // when amount is set, asset must be also set objRequest.asset = 'base'; - var device_address = assocParams.device_address; + const device_address = assocParams.device_address; if (device_address){ if (!ValidationUtils.isValidDeviceAddress(device_address)) - return callbacks.ifError('invalid device address: '+device_address); + return callbacks.ifError(`invalid device address: ${device_address}`); objRequest.device_address = device_address; } } @@ -118,14 +117,14 @@ function parseUri(uri, callbacks){ function parseQueryString(str, delimiter){ if (!delimiter) delimiter = '&'; - var arrPairs = str.split(delimiter); - var assocParams = {}; - arrPairs.forEach(function(pair){ - var arrNameValue = pair.split('='); + const arrPairs = str.split(delimiter); + const assocParams = {}; + arrPairs.forEach(pair => { + const arrNameValue = pair.split('='); if (arrNameValue.length !== 2) return; - var name = decodeURIComponent(arrNameValue[0]); - var value = decodeURIComponent(arrNameValue[1]); + const name = decodeURIComponent(arrNameValue[0]); + const value = decodeURIComponent(arrNameValue[1]); assocParams[name] = value; }); return assocParams; diff --git a/validation.js b/validation.js index 6f03062c..480ceb0e 100644 --- a/validation.js +++ b/validation.js @@ -1,39 +1,38 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var storage = require('./storage.js'); -var graph = require('./graph.js'); -var main_chain = require('./main_chain.js'); -var paid_witnessing = require("./paid_witnessing.js"); -var headers_commission = require("./headers_commission.js"); -var mc_outputs = require("./mc_outputs.js"); -var objectHash = require("./object_hash.js"); -var objectLength = require("./object_length.js"); -var db = require('./db.js'); -var chash = require('./chash.js'); -var mutex = require('./mutex.js'); -var constants = require("./constants.js"); -var ValidationUtils = require("./validation_utils.js"); -var Definition = require("./definition.js"); -var conf = require('./conf.js'); -var profiler = require('./profiler.js'); -var breadcrumbs = require('./breadcrumbs.js'); - -var MAX_INT32 = Math.pow(2, 31) - 1; - -var hasFieldsExcept = ValidationUtils.hasFieldsExcept; -var isNonemptyString = ValidationUtils.isNonemptyString; -var isStringOfLength = ValidationUtils.isStringOfLength; -var isInteger = ValidationUtils.isInteger; -var isNonnegativeInteger = ValidationUtils.isNonnegativeInteger; -var isPositiveInteger = ValidationUtils.isPositiveInteger; -var isNonemptyArray = ValidationUtils.isNonemptyArray; -var isValidAddress = ValidationUtils.isValidAddress; -var isValidBase64 = ValidationUtils.isValidBase64; - - -function hasValidHashes(objJoint){ - var objUnit = objJoint.unit; +const async = require('async'); +const storage = require('./storage.js'); +const graph = require('./graph.js'); +const main_chain = require('./main_chain.js'); +const paid_witnessing = require("./paid_witnessing.js"); +const headers_commission = require("./headers_commission.js"); +const mc_outputs = require("./mc_outputs.js"); +const objectHash = require("./object_hash.js"); +const objectLength = require("./object_length.js"); +const db = require('./db.js'); +const chash = require('./chash.js'); +const mutex = require('./mutex.js'); +const constants = require("./constants.js"); +const ValidationUtils = require("./validation_utils.js"); +const Definition = require("./definition.js"); +const conf = require('./conf.js'); +const profiler = require('./profiler.js'); +const breadcrumbs = require('./breadcrumbs.js'); + +const MAX_INT32 = Math.pow(2, 31) - 1; + +const hasFieldsExcept = ValidationUtils.hasFieldsExcept; +const isNonemptyString = ValidationUtils.isNonemptyString; +const isStringOfLength = ValidationUtils.isStringOfLength; +const isInteger = ValidationUtils.isInteger; +const isNonnegativeInteger = ValidationUtils.isNonnegativeInteger; +const isPositiveInteger = ValidationUtils.isPositiveInteger; +const isNonemptyArray = ValidationUtils.isNonemptyArray; +const isValidAddress = ValidationUtils.isValidAddress; +const isValidBase64 = ValidationUtils.isValidBase64; + + +function hasValidHashes({unit}) { + const objUnit = unit; if (objectHash.getUnitHash(objUnit) !== objUnit.unit) return false; @@ -43,13 +42,13 @@ function hasValidHashes(objJoint){ function validate(objJoint, callbacks) { - var objUnit = objJoint.unit; + const objUnit = objJoint.unit; if (typeof objUnit !== "object" || objUnit === null) throw Error("no unit object"); if (!objUnit.unit) throw Error("no unit"); - console.log("\nvalidating joint identified by unit "+objJoint.unit.unit); + console.log(`\nvalidating joint identified by unit ${objJoint.unit.unit}`); if (!isStringOfLength(objUnit.unit, constants.HASH_LENGTH)) return callbacks.ifJointError("wrong unit length"); @@ -57,10 +56,10 @@ function validate(objJoint, callbacks) { try{ // UnitError is linked to objUnit.unit, so we need to ensure objUnit.unit is true before we throw any UnitErrors if (objectHash.getUnitHash(objUnit) !== objUnit.unit) - return callbacks.ifJointError("wrong unit hash: "+objectHash.getUnitHash(objUnit)+" != "+objUnit.unit); + return callbacks.ifJointError(`wrong unit hash: ${objectHash.getUnitHash(objUnit)} != ${objUnit.unit}`); } catch(e){ - return callbacks.ifJointError("failed to calc unit hash: "+e); + return callbacks.ifJointError(`failed to calc unit hash: ${e}`); } if (objJoint.unsigned){ @@ -107,9 +106,9 @@ function validate(objJoint, callbacks) { return callbacks.ifUnitError("too many messages"); if (objectLength.getHeadersSize(objUnit) !== objUnit.headers_commission) - return callbacks.ifJointError("wrong headers commission, expected "+objectLength.getHeadersSize(objUnit)); + return callbacks.ifJointError(`wrong headers commission, expected ${objectLength.getHeadersSize(objUnit)}`); if (objectLength.getTotalPayloadSize(objUnit) !== objUnit.payload_commission) - return callbacks.ifJointError("wrong payload commission, unit "+objUnit.unit+", calculated "+objectLength.getTotalPayloadSize(objUnit)+", expected "+objUnit.payload_commission); + return callbacks.ifJointError(`wrong payload commission, unit ${objUnit.unit}, calculated ${objectLength.getTotalPayloadSize(objUnit)}, expected ${objUnit.payload_commission}`); } if (!isNonemptyArray(objUnit.authors)) @@ -136,9 +135,9 @@ function validate(objJoint, callbacks) { if ("witness_list_unit" in objUnit && "witnesses" in objUnit) return callbacks.ifUnitError("ambiguous witnesses"); - var arrAuthorAddresses = objUnit.authors ? objUnit.authors.map(function(author) { return author.address; } ) : []; + const arrAuthorAddresses = objUnit.authors ? objUnit.authors.map(({address}) => address ) : []; - var objValidationState = { + const objValidationState = { arrAdditionalQueries: [], arrDoubleSpendInputs: [], arrInputKeys: [] @@ -150,76 +149,76 @@ function validate(objJoint, callbacks) { if (!isPositiveInteger(objUnit.timestamp) && !objJoint.unsigned) return callbacks.ifJointError("bad timestamp"); if (objJoint.ball) - return callbacks.ifJointError("I'm light, can't accept stable unit "+objUnit.unit+" without proof"); + return callbacks.ifJointError(`I'm light, can't accept stable unit ${objUnit.unit} without proof`); return objJoint.unsigned ? callbacks.ifOkUnsigned(true) - : callbacks.ifOk({sequence: 'good', arrDoubleSpendInputs: [], arrAdditionalQueries: []}, function(){}); + : callbacks.ifOk({sequence: 'good', arrDoubleSpendInputs: [], arrAdditionalQueries: []}, () => {}); } else{ if ("timestamp" in objUnit && !isPositiveInteger(objUnit.timestamp)) return callbacks.ifJointError("bad timestamp"); } - mutex.lock(arrAuthorAddresses, function(unlock){ + mutex.lock(arrAuthorAddresses, unlock => { - var conn = null; + let conn = null; async.series( [ - function(cb){ - db.takeConnectionFromPool(function(new_conn){ + cb => { + db.takeConnectionFromPool(new_conn => { conn = new_conn; - conn.query("BEGIN", function(){cb();}); + conn.query("BEGIN", () => {cb();}); }); }, - function(cb){ + cb => { profiler.start(); checkDuplicate(conn, objUnit.unit, cb); }, - function(cb){ + cb => { profiler.stop('validation-checkDuplicate'); profiler.start(); objUnit.content_hash ? cb() : validateHeadersCommissionRecipients(objUnit, cb); }, - function(cb){ + cb => { profiler.stop('validation-hc-recipients'); profiler.start(); !objUnit.parent_units ? cb() : validateHashTree(conn, objJoint, objValidationState, cb); }, - function(cb){ + cb => { profiler.stop('validation-hash-tree'); profiler.start(); !objUnit.parent_units ? cb() : validateParents(conn, objJoint, objValidationState, cb); }, - function(cb){ + cb => { profiler.stop('validation-parents'); profiler.start(); !objJoint.skiplist_units ? cb() : validateSkiplist(conn, objJoint.skiplist_units, cb); }, - function(cb){ + cb => { profiler.stop('validation-skiplist'); validateWitnesses(conn, objUnit, objValidationState, cb); }, - function(cb){ + cb => { profiler.start(); validateAuthors(conn, objUnit.authors, objUnit, objValidationState, cb); }, - function(cb){ + cb => { profiler.stop('validation-authors'); profiler.start(); objUnit.content_hash ? cb() : validateMessages(conn, objUnit.messages, objUnit, objValidationState, cb); } ], - function(err){ + err => { profiler.stop('validation-messages'); if(err){ - conn.query("ROLLBACK", function(){ + conn.query("ROLLBACK", () => { conn.release(); unlock(); if (typeof err === "object"){ @@ -240,7 +239,7 @@ function validate(objJoint, callbacks) { } else{ profiler.start(); - conn.query("COMMIT", function(){ + conn.query("COMMIT", () => { conn.release(); profiler.stop('validation-commit'); if (objJoint.unsigned){ @@ -264,51 +263,51 @@ function validate(objJoint, callbacks) { function checkDuplicate(conn, unit, cb){ - conn.query("SELECT 1 FROM units WHERE unit=?", [unit], function(rows){ - if (rows.length === 0) + conn.query("SELECT 1 FROM units WHERE unit=?", [unit], ({length}) => { + if (length === 0) return cb(); - cb("unit "+unit+" already exists"); + cb(`unit ${unit} already exists`); }); } -function validateHashTree(conn, objJoint, objValidationState, callback){ - if (!objJoint.ball) +function validateHashTree(conn, {ball, unit, skiplist_units}, objValidationState, callback) { + if (!ball) return callback(); - var objUnit = objJoint.unit; - conn.query("SELECT unit FROM hash_tree_balls WHERE ball=?", [objJoint.ball], function(rows){ + const objUnit = unit; + conn.query("SELECT unit FROM hash_tree_balls WHERE ball=?", [ball], rows => { if (rows.length === 0) - return callback({error_code: "need_hash_tree", message: "ball "+objJoint.ball+" is not known in hash tree"}); + return callback({error_code: "need_hash_tree", message: `ball ${ball} is not known in hash tree`}); if (rows[0].unit !== objUnit.unit) - return callback(createJointError("ball "+objJoint.ball+" unit "+objUnit.unit+" contradicts hash tree")); + return callback(createJointError(`ball ${ball} unit ${objUnit.unit} contradicts hash tree`)); conn.query( "SELECT ball FROM hash_tree_balls WHERE unit IN(?) \n\ UNION \n\ SELECT ball FROM balls WHERE unit IN(?) \n\ ORDER BY ball", [objUnit.parent_units, objUnit.parent_units], - function(prows){ + prows => { if (prows.length !== objUnit.parent_units.length) return callback(createJointError("some parents not found in balls nor in hash tree")); // while the child is found in hash tree - var arrParentBalls = prows.map(function(prow){ return prow.ball; }); - if (!objJoint.skiplist_units) + const arrParentBalls = prows.map(({ball}) => ball); + if (!skiplist_units) return validateBallHash(); conn.query( "SELECT ball FROM hash_tree_balls WHERE unit IN(?) \n\ UNION \n\ SELECT ball FROM balls WHERE unit IN(?) \n\ ORDER BY ball", - [objJoint.skiplist_units, objJoint.skiplist_units], - function(srows){ - if (srows.length !== objJoint.skiplist_units.length) + [skiplist_units, skiplist_units], + srows => { + if (srows.length !== skiplist_units.length) return callback(createJointError("some skiplist balls not found")); - objValidationState.arrSkiplistBalls = srows.map(function(srow){ return srow.ball; }); + objValidationState.arrSkiplistBalls = srows.map(({ball}) => ball); validateBallHash(); } ); function validateBallHash(){ - var hash = objectHash.getBallHash(objUnit.unit, arrParentBalls, objValidationState.arrSkiplistBalls, !!objUnit.content_hash); - if (hash !== objJoint.ball) + const hash = objectHash.getBallHash(objUnit.unit, arrParentBalls, objValidationState.arrSkiplistBalls, !!objUnit.content_hash); + if (hash !== ball) return callback(createJointError("ball hash is wrong")); callback(); } @@ -320,24 +319,24 @@ function validateHashTree(conn, objJoint, objValidationState, callback){ // we cannot verify that skiplist units lie on MC if they are unstable yet, // but if they don't, we'll get unmatching ball hash when the current unit reaches stability function validateSkiplist(conn, arrSkiplistUnits, callback){ - var prev = ""; + const prev = ""; async.eachSeries( arrSkiplistUnits, - function(skiplist_unit, cb){ + (skiplist_unit, cb) => { //if (skiplist_unit.charAt(0) !== "0") // return cb("skiplist unit doesn't start with 0"); if (skiplist_unit <= prev) return cb(createJointError("skiplist units not ordered")); - conn.query("SELECT unit, is_stable, is_on_main_chain, main_chain_index FROM units WHERE unit=?", [skiplist_unit], function(rows){ + conn.query("SELECT unit, is_stable, is_on_main_chain, main_chain_index FROM units WHERE unit=?", [skiplist_unit], rows => { if (rows.length === 0) - return cb("skiplist unit "+skiplist_unit+" not found"); - var objSkiplistUnitProps = rows[0]; + return cb(`skiplist unit ${skiplist_unit} not found`); + const objSkiplistUnitProps = rows[0]; // if not stable, can't check that it is on MC as MC is not stable in its area yet if (objSkiplistUnitProps.is_stable === 1){ if (objSkiplistUnitProps.is_on_main_chain !== 1) - return cb("skiplist unit "+skiplist_unit+" is not on MC"); + return cb(`skiplist unit ${skiplist_unit} is not on MC`); if (objSkiplistUnitProps.main_chain_index % 10 !== 0) - return cb("skiplist unit "+skiplist_unit+" MCI is not divisible by 10"); + return cb(`skiplist unit ${skiplist_unit} MCI is not divisible by 10`); } // we can't verify the choice of skiplist unit. // If we try to find a skiplist unit now, we might find something matching on unstable part of MC. @@ -349,15 +348,15 @@ function validateSkiplist(conn, arrSkiplistUnits, callback){ ); } -function validateParents(conn, objJoint, objValidationState, callback){ +function validateParents(conn, {unit, ball}, objValidationState, callback) { // avoid merging the obvious nonserials function checkNoSameAddressInDifferentParents(){ if (objUnit.parent_units.length === 1) return checkLastBallDidNotRetreat(); - conn.query("SELECT address, COUNT(*) AS c FROM unit_authors WHERE unit IN(?) GROUP BY address HAVING c>1", [objUnit.parent_units], function(rows){ + conn.query("SELECT address, COUNT(*) AS c FROM unit_authors WHERE unit IN(?) GROUP BY address HAVING c>1", [objUnit.parent_units], rows => { if (rows.length > 0) - return callback("some addresses found more than once in parents, e.g. "+rows[0].address); + return callback(`some addresses found more than once in parents, e.g. ${rows[0].address}`); return checkLastBallDidNotRetreat(); }); } @@ -368,56 +367,56 @@ function validateParents(conn, objJoint, objValidationState, callback){ FROM units JOIN units AS lb_units ON units.last_ball_unit=lb_units.unit \n\ WHERE units.unit IN(?)", [objUnit.parent_units], - function(rows){ - var max_parent_last_ball_mci = rows[0].max_parent_last_ball_mci; + rows => { + const max_parent_last_ball_mci = rows[0].max_parent_last_ball_mci; if (max_parent_last_ball_mci > objValidationState.last_ball_mci) - return callback("last ball mci must not retreat, parents: "+objUnit.parent_units.join(', ')); + return callback(`last ball mci must not retreat, parents: ${objUnit.parent_units.join(', ')}`); callback(); } ); } - var objUnit = objJoint.unit; + var objUnit = unit; if (objUnit.parent_units.length > constants.MAX_PARENTS_PER_UNIT) // anti-spam - return callback("too many parents: "+objUnit.parent_units.length); + return callback(`too many parents: ${objUnit.parent_units.length}`); // obsolete: when handling a ball, we can't trust parent list before we verify ball hash // obsolete: when handling a fresh unit, we can begin trusting parent list earlier, after we verify parents_hash - var createError = objJoint.ball ? createJointError : function(err){ return err; }; + const createError = ball ? createJointError : err => err; // after this point, we can trust parent list as it either agrees with parents_hash or agrees with hash tree // hence, there are no more joint errors, except unordered parents or skiplist units - var last_ball = objUnit.last_ball; - var last_ball_unit = objUnit.last_ball_unit; - var prev = ""; - var arrMissingParentUnits = []; - var arrPrevParentUnitProps = []; + const last_ball = objUnit.last_ball; + const last_ball_unit = objUnit.last_ball_unit; + let prev = ""; + const arrMissingParentUnits = []; + const arrPrevParentUnitProps = []; objValidationState.max_parent_limci = 0; - var join = objJoint.ball ? 'LEFT JOIN balls USING(unit) LEFT JOIN hash_tree_balls ON units.unit=hash_tree_balls.unit' : ''; - var field = objJoint.ball ? ', IFNULL(balls.ball, hash_tree_balls.ball) AS ball' : ''; + const join = ball ? 'LEFT JOIN balls USING(unit) LEFT JOIN hash_tree_balls ON units.unit=hash_tree_balls.unit' : ''; + const field = ball ? ', IFNULL(balls.ball, hash_tree_balls.ball) AS ball' : ''; async.eachSeries( objUnit.parent_units, - function(parent_unit, cb){ + (parent_unit, cb) => { if (parent_unit <= prev) return cb(createError("parent units not ordered")); prev = parent_unit; - conn.query("SELECT units.*"+field+" FROM units "+join+" WHERE units.unit=?", [parent_unit], function(rows){ + conn.query(`SELECT units.*${field} FROM units ${join} WHERE units.unit=?`, [parent_unit], rows => { if (rows.length === 0){ arrMissingParentUnits.push(parent_unit); return cb(); } - var objParentUnitProps = rows[0]; + const objParentUnitProps = rows[0]; // already checked in validateHashTree that the parent ball is known, that's why we throw - if (objJoint.ball && objParentUnitProps.ball === null) - throw Error("no ball corresponding to parent unit "+parent_unit); + if (ball && objParentUnitProps.ball === null) + throw Error(`no ball corresponding to parent unit ${parent_unit}`); if (objParentUnitProps.latest_included_mc_index > objValidationState.max_parent_limci) objValidationState.max_parent_limci = objParentUnitProps.latest_included_mc_index; async.eachSeries( arrPrevParentUnitProps, - function(objPrevParentUnitProps, cb2){ - graph.compareUnitsByProps(conn, objPrevParentUnitProps, objParentUnitProps, function(result){ - (result === null) ? cb2() : cb2("parent unit "+parent_unit+" is related to one of the other parent units"); + (objPrevParentUnitProps, cb2) => { + graph.compareUnitsByProps(conn, objPrevParentUnitProps, objParentUnitProps, result => { + (result === null) ? cb2() : cb2(`parent unit ${parent_unit} is related to one of the other parent units`); }); }, - function(err){ + err => { if (err) return cb(err); arrPrevParentUnitProps.push(objParentUnitProps); @@ -426,23 +425,23 @@ function validateParents(conn, objJoint, objValidationState, callback){ ); }); }, - function(err){ + err => { if (err) return callback(err); if (arrMissingParentUnits.length > 0){ - conn.query("SELECT error FROM known_bad_joints WHERE unit IN(?)", [arrMissingParentUnits], function(rows){ + conn.query("SELECT error FROM known_bad_joints WHERE unit IN(?)", [arrMissingParentUnits], rows => { (rows.length > 0) - ? callback("some of the unit's parents are known bad: "+rows[0].error) + ? callback(`some of the unit's parents are known bad: ${rows[0].error}`) : callback({error_code: "unresolved_dependency", arrMissingUnits: arrMissingParentUnits}); }); return; } // this is redundant check, already checked in validateHashTree() - if (objJoint.ball){ - var arrParentBalls = arrPrevParentUnitProps.map(function(objParentUnitProps){ return objParentUnitProps.ball; }).sort(); + if (ball){ + const arrParentBalls = arrPrevParentUnitProps.map(({ball}) => ball).sort(); //if (arrParentBalls.indexOf(null) === -1){ - var hash = objectHash.getBallHash(objUnit.unit, arrParentBalls, objValidationState.arrSkiplistBalls, !!objUnit.content_hash); - if (hash !== objJoint.ball) + const hash = objectHash.getBallHash(objUnit.unit, arrParentBalls, objValidationState.arrSkiplistBalls, !!objUnit.content_hash); + if (hash !== ball) throw Error("ball hash is wrong"); // shouldn't happen, already validated in validateHashTree() //} } @@ -450,45 +449,44 @@ function validateParents(conn, objJoint, objValidationState, callback){ "SELECT is_stable, is_on_main_chain, main_chain_index, ball, (SELECT MAX(main_chain_index) FROM units) AS max_known_mci \n\ FROM units LEFT JOIN balls USING(unit) WHERE unit=?", [last_ball_unit], - function(rows){ + rows => { if (rows.length !== 1) // at the same time, direct parents already received - return callback("last ball unit "+last_ball_unit+" not found"); - var objLastBallUnitProps = rows[0]; + return callback(`last ball unit ${last_ball_unit} not found`); + const objLastBallUnitProps = rows[0]; // it can be unstable and have a received (not self-derived) ball //if (objLastBallUnitProps.ball !== null && objLastBallUnitProps.is_stable === 0) // throw "last ball "+last_ball+" is unstable"; if (objLastBallUnitProps.ball === null && objLastBallUnitProps.is_stable === 1) - throw Error("last ball unit "+last_ball_unit+" is stable but has no ball"); + throw Error(`last ball unit ${last_ball_unit} is stable but has no ball`); if (objLastBallUnitProps.is_on_main_chain !== 1) - return callback("last ball "+last_ball+" is not on MC"); + return callback(`last ball ${last_ball} is not on MC`); if (objLastBallUnitProps.ball && objLastBallUnitProps.ball !== last_ball) - return callback("last_ball "+last_ball+" and last_ball_unit "+last_ball_unit+" do not match"); + return callback(`last_ball ${last_ball} and last_ball_unit ${last_ball_unit} do not match`); objValidationState.last_ball_mci = objLastBallUnitProps.main_chain_index; objValidationState.max_known_mci = objLastBallUnitProps.max_known_mci; if (objValidationState.max_parent_limci < objValidationState.last_ball_mci) - return callback("last ball unit "+last_ball_unit+" is not included in parents, unit "+objUnit.unit); + return callback(`last ball unit ${last_ball_unit} is not included in parents, unit ${objUnit.unit}`); if (objLastBallUnitProps.is_stable === 1){ // if it were not stable, we wouldn't have had the ball at all if (objLastBallUnitProps.ball !== last_ball) - return callback("stable: last_ball "+last_ball+" and last_ball_unit "+last_ball_unit+" do not match"); + return callback(`stable: last_ball ${last_ball} and last_ball_unit ${last_ball_unit} do not match`); if (objValidationState.last_ball_mci <= 1300000) return checkNoSameAddressInDifferentParents(); } // Last ball is not stable yet in our view. Check if it is stable in view of the parents - main_chain.determineIfStableInLaterUnitsAndUpdateStableMcFlag(conn, last_ball_unit, objUnit.parent_units, objLastBallUnitProps.is_stable, function(bStable){ + main_chain.determineIfStableInLaterUnitsAndUpdateStableMcFlag(conn, last_ball_unit, objUnit.parent_units, objLastBallUnitProps.is_stable, bStable => { /*if (!bStable && objLastBallUnitProps.is_stable === 1){ var eventBus = require('./event_bus.js'); eventBus.emit('nonfatal_error', "last ball is stable, but not stable in parents, unit "+objUnit.unit, new Error()); return checkNoSameAddressInDifferentParents(); } else */if (!bStable) - return callback(objUnit.unit+": last ball unit "+last_ball_unit+" is not stable in view of your parents "+objUnit.parent_units); - conn.query("SELECT ball FROM balls WHERE unit=?", [last_ball_unit], function(ball_rows){ + return callback(`${objUnit.unit}: last ball unit ${last_ball_unit} is not stable in view of your parents ${objUnit.parent_units}`); + conn.query("SELECT ball FROM balls WHERE unit=?", [last_ball_unit], ball_rows => { if (ball_rows.length === 0) - throw Error("last ball unit "+last_ball_unit+" just became stable but ball not found"); + throw Error(`last ball unit ${last_ball_unit} just became stable but ball not found`); if (ball_rows[0].ball !== last_ball) - return callback("last_ball "+last_ball+" and last_ball_unit "+last_ball_unit - +" do not match after advancing stability point"); + return callback(`last_ball ${last_ball} and last_ball_unit ${last_ball_unit} do not match after advancing stability point`); checkNoSameAddressInDifferentParents(); }); }); @@ -503,7 +501,7 @@ function validateWitnesses(conn, objUnit, objValidationState, callback){ function validateWitnessListMutations(arrWitnesses){ if (!objUnit.parent_units) // genesis return callback(); - storage.determineIfHasWitnessListMutationsAlongMc(conn, objUnit, last_ball_unit, arrWitnesses, function(err){ + storage.determineIfHasWitnessListMutationsAlongMc(conn, objUnit, last_ball_unit, arrWitnesses, err => { if (err && objValidationState.last_ball_mci >= 512000) // do not enforce before the || bug was fixed return callback(err); checkNoReferencesInWitnessAddressDefinitions(arrWitnesses); @@ -512,43 +510,43 @@ function validateWitnesses(conn, objUnit, objValidationState, callback){ function checkNoReferencesInWitnessAddressDefinitions(arrWitnesses){ profiler.start(); - var cross = (conf.storage === 'sqlite') ? 'CROSS' : ''; // correct the query planner + const cross = (conf.storage === 'sqlite') ? 'CROSS' : ''; // correct the query planner conn.query( - "SELECT 1 \n\ - FROM address_definition_changes \n\ - JOIN definitions USING(definition_chash) \n\ - JOIN units AS change_units USING(unit) -- units where the change was declared \n\ - JOIN unit_authors USING(definition_chash) \n\ - JOIN units AS definition_units ON unit_authors.unit=definition_units.unit -- units where the definition was disclosed \n\ - WHERE address_definition_changes.address IN(?) AND has_references=1 \n\ - AND change_units.is_stable=1 AND change_units.main_chain_index<=? AND +change_units.sequence='good' \n\ - AND definition_units.is_stable=1 AND definition_units.main_chain_index<=? AND +definition_units.sequence='good' \n\ - UNION \n\ - SELECT 1 \n\ - FROM definitions \n\ - "+cross+" JOIN unit_authors USING(definition_chash) \n\ - JOIN units AS definition_units ON unit_authors.unit=definition_units.unit -- units where the definition was disclosed \n\ - WHERE definition_chash IN(?) AND has_references=1 \n\ - AND definition_units.is_stable=1 AND definition_units.main_chain_index<=? AND +definition_units.sequence='good' \n\ - LIMIT 1", + `SELECT 1 \n\ + FROM address_definition_changes \n\ + JOIN definitions USING(definition_chash) \n\ + JOIN units AS change_units USING(unit) -- units where the change was declared \n\ + JOIN unit_authors USING(definition_chash) \n\ + JOIN units AS definition_units ON unit_authors.unit=definition_units.unit -- units where the definition was disclosed \n\ + WHERE address_definition_changes.address IN(?) AND has_references=1 \n\ + AND change_units.is_stable=1 AND change_units.main_chain_index<=? AND +change_units.sequence='good' \n\ + AND definition_units.is_stable=1 AND definition_units.main_chain_index<=? AND +definition_units.sequence='good' \n\ + UNION \n\ + SELECT 1 \n\ + FROM definitions \n\ + ${cross} JOIN unit_authors USING(definition_chash) \n\ + JOIN units AS definition_units ON unit_authors.unit=definition_units.unit -- units where the definition was disclosed \n\ + WHERE definition_chash IN(?) AND has_references=1 \n\ + AND definition_units.is_stable=1 AND definition_units.main_chain_index<=? AND +definition_units.sequence='good' \n\ + LIMIT 1`, [arrWitnesses, objValidationState.last_ball_mci, objValidationState.last_ball_mci, arrWitnesses, objValidationState.last_ball_mci], - function(rows){ + ({length}) => { profiler.stop('validation-witnesses-no-refs'); - (rows.length > 0) ? callback("some witnesses have references in their addresses") : checkWitnessedLevelDidNotRetreat(arrWitnesses); + (length > 0) ? callback("some witnesses have references in their addresses") : checkWitnessedLevelDidNotRetreat(arrWitnesses); } ); } function checkWitnessedLevelDidNotRetreat(arrWitnesses){ - storage.determineWitnessedLevelAndBestParent(conn, objUnit.parent_units, arrWitnesses, function(witnessed_level, best_parent_unit){ + storage.determineWitnessedLevelAndBestParent(conn, objUnit.parent_units, arrWitnesses, (witnessed_level, best_parent_unit) => { objValidationState.witnessed_level = witnessed_level; objValidationState.best_parent_unit = best_parent_unit; if (objValidationState.last_ball_mci < 1400000) // not enforced return callback(); - storage.readStaticUnitProps(conn, best_parent_unit, function(props){ + storage.readStaticUnitProps(conn, best_parent_unit, props => { (witnessed_level >= props.witnessed_level) ? callback() - : callback("witnessed level retreats from "+props.witnessed_level+" to "+witnessed_level); + : callback(`witnessed level retreats from ${props.witnessed_level} to ${witnessed_level}`); }); }); } @@ -556,30 +554,30 @@ function validateWitnesses(conn, objUnit, objValidationState, callback){ profiler.start(); var last_ball_unit = objUnit.last_ball_unit; if (typeof objUnit.witness_list_unit === "string"){ - conn.query("SELECT sequence, is_stable, main_chain_index FROM units WHERE unit=?", [objUnit.witness_list_unit], function(unit_rows){ + conn.query("SELECT sequence, is_stable, main_chain_index FROM units WHERE unit=?", [objUnit.witness_list_unit], unit_rows => { if (unit_rows.length === 0) - return callback("witness list unit "+objUnit.witness_list_unit+" not found"); - var objWitnessListUnitProps = unit_rows[0]; + return callback(`witness list unit ${objUnit.witness_list_unit} not found`); + const objWitnessListUnitProps = unit_rows[0]; if (objWitnessListUnitProps.sequence !== 'good') - return callback("witness list unit "+objUnit.witness_list_unit+" is not serial"); + return callback(`witness list unit ${objUnit.witness_list_unit} is not serial`); if (objWitnessListUnitProps.is_stable !== 1) - return callback("witness list unit "+objUnit.witness_list_unit+" is not stable"); + return callback(`witness list unit ${objUnit.witness_list_unit} is not stable`); if (objWitnessListUnitProps.main_chain_index > objValidationState.last_ball_mci) - return callback("witness list unit "+objUnit.witness_list_unit+" must come before last ball"); - storage.readWitnessList(conn, objUnit.witness_list_unit, function(arrWitnesses){ + return callback(`witness list unit ${objUnit.witness_list_unit} must come before last ball`); + storage.readWitnessList(conn, objUnit.witness_list_unit, arrWitnesses => { if (arrWitnesses.length === 0) - return callback("referenced witness list unit "+objUnit.witness_list_unit+" has no witnesses"); + return callback(`referenced witness list unit ${objUnit.witness_list_unit} has no witnesses`); profiler.stop('validation-witnesses-read-list'); validateWitnessListMutations(arrWitnesses); }, true); }); } else if (Array.isArray(objUnit.witnesses) && objUnit.witnesses.length === constants.COUNT_WITNESSES){ - var prev_witness = objUnit.witnesses[0]; - for (var i=0; i { if (rows[0].count_stable_good_witnesses !== constants.COUNT_WITNESSES) return callback("some witnesses are not stable, not serial, or don't come before last ball"); profiler.stop('validation-witnesses-stable'); @@ -616,10 +614,10 @@ function validateHeadersCommissionRecipients(objUnit, cb){ if ("earned_headers_commission_recipients" in objUnit){ if (!isNonemptyArray(objUnit.earned_headers_commission_recipients)) return cb("empty earned_headers_commission_recipients array"); - var total_earned_headers_commission_share = 0; - var prev_address = ""; - for (var i=0; i constants.MAX_AUTHORS_PER_UNIT) // this is anti-spam. Otherwise an attacker would send nonserial balls signed by zillions of authors. return callback("too many authors"); objValidationState.arrAddressesWithForkedPath = []; - var prev_address = ""; - for (var i=0; i { validateAuthor(conn, objAuthor, objUnit, objValidationState, cb); }, callback); } @@ -663,16 +661,16 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ return callback("unknown fields in author"); if (!ValidationUtils.isNonemptyObject(objAuthor.authentifiers) && !objUnit.content_hash) return callback("no authentifiers"); - for (var path in objAuthor.authentifiers){ + for (const path in objAuthor.authentifiers){ if (!isNonemptyString(objAuthor.authentifiers[path])) return callback("authentifiers must be nonempty strings"); if (objAuthor.authentifiers[path].length > constants.MAX_AUTHENTIFIER_LENGTH) return callback("authentifier too long"); } - var bNonserial = false; + let bNonserial = false; - var arrAddressDefinition = objAuthor.definition; + const arrAddressDefinition = objAuthor.definition; if (isNonemptyArray(arrAddressDefinition)){ // todo: check that the address is really new? validateAuthentifiers(arrAddressDefinition); @@ -686,10 +684,10 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ } // we check signatures using the latest address definition before last ball storage.readDefinitionByAddress(conn, objAuthor.address, objValidationState.last_ball_mci, { - ifDefinitionNotFound: function(definition_chash){ - callback("definition "+definition_chash+" bound to address "+objAuthor.address+" is not defined"); + ifDefinitionNotFound(definition_chash) { + callback(`definition ${definition_chash} bound to address ${objAuthor.address} is not defined`); }, - ifFound: function(arrAddressDefinition){ + ifFound(arrAddressDefinition) { validateAuthentifiers(arrAddressDefinition); } }); @@ -701,7 +699,7 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ function validateAuthentifiers(arrAddressDefinition){ Definition.validateAuthentifiers( conn, objAuthor.address, null, arrAddressDefinition, objUnit, objValidationState, objAuthor.authentifiers, - function(err, res){ + (err, res) => { if (err) // error in address definition return callback(err); if (!res) // wrong signature or the like @@ -730,18 +728,18 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ CROSS JOIN units USING(unit) \n\ WHERE address=? AND _mci IS NULL AND unit != ?", [objAuthor.address, objValidationState.max_parent_limci, objUnit.unit, objAuthor.address, objUnit.unit], - function(rows){ - var arrConflictingUnitProps = []; + rows => { + const arrConflictingUnitProps = []; async.eachSeries( rows, - function(row, cb){ - graph.determineIfIncludedOrEqual(conn, row.unit, objUnit.parent_units, function(bIncluded){ + (row, cb) => { + graph.determineIfIncludedOrEqual(conn, row.unit, objUnit.parent_units, bIncluded => { if (!bIncluded) arrConflictingUnitProps.push(row); cb(); }); }, - function(){ + () => { handleConflictingUnits(arrConflictingUnitProps); } ); @@ -751,28 +749,24 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ function checkSerialAddressUse(){ - var next = checkNoPendingChangeOfDefinitionChash; - findConflictingUnits(function(arrConflictingUnitProps){ + const next = checkNoPendingChangeOfDefinitionChash; + findConflictingUnits(arrConflictingUnitProps => { if (arrConflictingUnitProps.length === 0){ // no conflicting units // we can have 2 authors. If the 1st author gave bad sequence but the 2nd is good then don't overwrite objValidationState.sequence = objValidationState.sequence || 'good'; return next(); } - var arrConflictingUnits = arrConflictingUnitProps.map(function(objConflictingUnitProps){ return objConflictingUnitProps.unit; }); - breadcrumbs.add("========== found conflicting units "+arrConflictingUnits+" ========="); - breadcrumbs.add("========== will accept a conflicting unit "+objUnit.unit+" ========="); + const arrConflictingUnits = arrConflictingUnitProps.map(({unit}) => unit); + breadcrumbs.add(`========== found conflicting units ${arrConflictingUnits} =========`); + breadcrumbs.add(`========== will accept a conflicting unit ${objUnit.unit} =========`); objValidationState.arrAddressesWithForkedPath.push(objAuthor.address); objValidationState.arrConflictingUnits = (objValidationState.arrConflictingUnits || []).concat(arrConflictingUnits); bNonserial = true; - var arrUnstableConflictingUnitProps = arrConflictingUnitProps.filter(function(objConflictingUnitProps){ - return (objConflictingUnitProps.is_stable === 0); - }); - var bConflictsWithStableUnits = arrConflictingUnitProps.some(function(objConflictingUnitProps){ - return (objConflictingUnitProps.is_stable === 1); - }); + const arrUnstableConflictingUnitProps = arrConflictingUnitProps.filter(({is_stable}) => is_stable === 0); + const bConflictsWithStableUnits = arrConflictingUnitProps.some(({is_stable}) => is_stable === 1); if (objValidationState.sequence !== 'final-bad') // if it were already final-bad because of 1st author, it can't become temp-bad due to 2nd author objValidationState.sequence = bConflictsWithStableUnits ? 'final-bad' : 'temp-bad'; - var arrUnstableConflictingUnits = arrUnstableConflictingUnitProps.map(function(objConflictingUnitProps){ return objConflictingUnitProps.unit; }); + const arrUnstableConflictingUnits = arrUnstableConflictingUnitProps.map(({unit}) => unit); if (bConflictsWithStableUnits) // don't temp-bad the unstable conflicting units return next(); if (arrUnstableConflictingUnits.length === 0) @@ -787,13 +781,13 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ // don't allow contradicting pending keychanges. // We don't trust pending keychanges even when they are serial, as another unit may arrive and make them nonserial function checkNoPendingChangeOfDefinitionChash(){ - var next = checkNoPendingDefinition; + const next = checkNoPendingDefinition; //var filter = bNonserial ? "AND sequence='good'" : ""; conn.query( "SELECT unit FROM address_definition_changes JOIN units USING(unit) \n\ WHERE address=? AND (is_stable=0 OR main_chain_index>? OR main_chain_index IS NULL)", [objAuthor.address, objValidationState.last_ball_mci], - function(rows){ + rows => { if (rows.length === 0) return next(); if (!bNonserial || objValidationState.arrAddressesWithForkedPath.indexOf(objAuthor.address) === -1) @@ -801,14 +795,14 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ // from this point, our unit is nonserial async.eachSeries( rows, - function(row, cb){ - graph.determineIfIncludedOrEqual(conn, row.unit, objUnit.parent_units, function(bIncluded){ + ({unit}, cb) => { + graph.determineIfIncludedOrEqual(conn, unit, objUnit.parent_units, bIncluded => { if (bIncluded) - console.log("checkNoPendingChangeOfDefinitionChash: unit "+row.unit+" is included"); + console.log(`checkNoPendingChangeOfDefinitionChash: unit ${unit} is included`); bIncluded ? cb("found") : cb(); }); }, - function(err){ + err => { (err === "found") ? callback("you can't send anything before your last included keychange is stable and before last ball (self is nonserial)") : next(); @@ -822,7 +816,7 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ // then the definition will be removed function checkNoPendingDefinition(){ //var next = checkNoPendingOrRetrievableNonserialIncluded; - var next = validateDefinition; + const next = validateDefinition; //var filter = bNonserial ? "AND sequence='good'" : ""; // var cross = (objValidationState.max_known_mci - objValidationState.last_ball_mci < 1000) ? 'CROSS' : ''; conn.query( // _left_ join forces use of indexes in units @@ -833,7 +827,7 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ UNION \n\ SELECT unit FROM unit_authors WHERE address=? AND definition_chash IS NOT NULL AND _mci IS NULL", [objAuthor.address, objValidationState.last_ball_mci, objAuthor.address], - function(rows){ + rows => { if (rows.length === 0) return next(); if (!bNonserial || objValidationState.arrAddressesWithForkedPath.indexOf(objAuthor.address) === -1) @@ -841,14 +835,14 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ // from this point, our unit is nonserial async.eachSeries( rows, - function(row, cb){ - graph.determineIfIncludedOrEqual(conn, row.unit, objUnit.parent_units, function(bIncluded){ + ({unit}, cb) => { + graph.determineIfIncludedOrEqual(conn, unit, objUnit.parent_units, bIncluded => { if (bIncluded) - console.log("checkNoPendingDefinition: unit "+row.unit+" is included"); + console.log(`checkNoPendingDefinition: unit ${unit} is included`); bIncluded ? cb("found") : cb(); }); }, - function(err){ + err => { (err === "found") ? callback("you can't send anything before your last included definition is stable and before last ball (self is nonserial)") : next(); @@ -905,14 +899,14 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ if (!("definition" in objAuthor)) return callback(); // the rest assumes that the definition is explicitly defined - var arrAddressDefinition = objAuthor.definition; + const arrAddressDefinition = objAuthor.definition; storage.readDefinitionByAddress(conn, objAuthor.address, objValidationState.last_ball_mci, { - ifDefinitionNotFound: function(definition_chash){ // first use of the definition_chash (in particular, of the address, when definition_chash=address) + ifDefinitionNotFound(definition_chash) { // first use of the definition_chash (in particular, of the address, when definition_chash=address) if (objectHash.getChash160(arrAddressDefinition) !== definition_chash) - return callback("wrong definition: "+objectHash.getChash160(arrAddressDefinition) +"!=="+ definition_chash); + return callback(`wrong definition: ${objectHash.getChash160(arrAddressDefinition)}!==${definition_chash}`); callback(); }, - ifFound: function(arrAddressDefinition2){ // arrAddressDefinition2 can be different + ifFound(arrAddressDefinition2) { // arrAddressDefinition2 can be different handleDuplicateAddressDefinition(arrAddressDefinition2); } }); @@ -920,7 +914,7 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ function handleDuplicateAddressDefinition(arrAddressDefinition){ if (!bNonserial || objValidationState.arrAddressesWithForkedPath.indexOf(objAuthor.address) === -1) - return callback("duplicate definition of address "+objAuthor.address+", bNonserial="+bNonserial); + return callback(`duplicate definition of address ${objAuthor.address}, bNonserial=${bNonserial}`); // todo: investigate if this can split the nodes // in one particular case, the attacker changes his definition then quickly sends a new ball with the old definition - the new definition will not be active yet if (objectHash.getChash160(arrAddressDefinition) !== objectHash.getChash160(objAuthor.definition)) @@ -931,13 +925,13 @@ function validateAuthor(conn, objAuthor, objUnit, objValidationState, callback){ } function validateMessages(conn, arrMessages, objUnit, objValidationState, callback){ - console.log("validateMessages "+objUnit.unit); + console.log(`validateMessages ${objUnit.unit}`); async.forEachOfSeries( arrMessages, - function(objMessage, message_index, cb){ + (objMessage, message_index, cb) => { validateMessage(conn, objMessage, message_index, objUnit, objValidationState, cb); }, - function(err){ + err => { if (err) return callback(err); if (!objValidationState.bHasBasePayment) @@ -959,12 +953,12 @@ function validateMessage(conn, objMessage, message_index, objUnit, objValidation if ("spend_proofs" in objMessage){ if (!Array.isArray(objMessage.spend_proofs) || objMessage.spend_proofs.length === 0 || objMessage.spend_proofs.length > constants.MAX_SPEND_PROOFS_PER_MESSAGE) - return callback("spend_proofs must be non-empty array max "+constants.MAX_SPEND_PROOFS_PER_MESSAGE+" elements"); - var arrAuthorAddresses = objUnit.authors.map(function(author) { return author.address; } ); + return callback(`spend_proofs must be non-empty array max ${constants.MAX_SPEND_PROOFS_PER_MESSAGE} elements`); + const arrAuthorAddresses = objUnit.authors.map(({address}) => address ); // spend proofs are sorted in the same order as their corresponding inputs //var prev_spend_proof = ""; - for (var i=0; i= 0) - return cb("spend proof "+objSpendProof.spend_proof+" already used"); + return cb(`spend proof ${objSpendProof.spend_proof} already used`); objValidationState.arrInputKeys.push(objSpendProof.spend_proof); //prev_spend_proof = objSpendProof.spend_proof; @@ -1000,7 +994,7 @@ function validateMessage(conn, objMessage, message_index, objUnit, objValidation } if (objMessage.payload_location !== "inline" && objMessage.payload_location !== "uri" && objMessage.payload_location !== "none") - return callback("wrong payload location: "+objMessage.payload_location); + return callback(`wrong payload location: ${objMessage.payload_location}`); if (objMessage.payload_location === "none" && ("payload" in objMessage || "payload_uri" in objMessage || "payload_uri_hash" in objMessage)) return callback("must be no payload"); @@ -1029,9 +1023,9 @@ function validateMessage(conn, objMessage, message_index, objUnit, objValidation return callback("private payment must come with spend proof(s)"); } - var arrInlineOnlyApps = ["address_definition_change", "data_feed", "definition_template", "asset", "asset_attestors", "attestation", "poll", "vote"]; + const arrInlineOnlyApps = ["address_definition_change", "data_feed", "definition_template", "asset", "asset_attestors", "attestation", "poll", "vote"]; if (arrInlineOnlyApps.indexOf(objMessage.app) >= 0 && objMessage.payload_location !== "inline") - return callback(objMessage.app+" must be inline"); + return callback(`${objMessage.app} must be inline`); function validatePayload(cb){ @@ -1048,59 +1042,65 @@ function validateMessage(conn, objMessage, message_index, objUnit, objValidation function validateSpendProofs(cb){ if (!("spend_proofs" in objMessage)) return cb(); - var arrEqs = objMessage.spend_proofs.map(function(objSpendProof){ - return "spend_proof="+conn.escape(objSpendProof.spend_proof)+ - " AND address="+conn.escape(objSpendProof.address ? objSpendProof.address : objUnit.authors[0].address); - }); + const arrEqs = objMessage.spend_proofs.map(({spend_proof, address}) => `spend_proof=${conn.escape(spend_proof)} AND address=${conn.escape(address ? address : objUnit.authors[0].address)}`); checkForDoublespends(conn, "spend proof", - "SELECT address, unit, main_chain_index, sequence FROM spend_proofs JOIN units USING(unit) WHERE unit != ? AND ("+arrEqs.join(" OR ")+")", + `SELECT address, unit, main_chain_index, sequence FROM spend_proofs JOIN units USING(unit) WHERE unit != ? AND (${arrEqs.join(" OR ")})`, [objUnit.unit], - objUnit, objValidationState, function(cb2){ cb2(); }, cb); + objUnit, objValidationState, cb2 => { cb2(); }, cb); } async.series([validateSpendProofs, validatePayload], callback); } -function checkForDoublespends(conn, type, sql, arrSqlArgs, objUnit, objValidationState, onAcceptedDoublespends, cb){ +function checkForDoublespends( + conn, + type, + sql, + arrSqlArgs, + {authors, parent_units, unit}, + {last_ball_mci, arrAddressesWithForkedPath}, + onAcceptedDoublespends, + cb +) { conn.query( sql, arrSqlArgs, - function(rows){ + rows => { if (rows.length === 0) return cb(); - var arrAuthorAddresses = objUnit.authors.map(function(author) { return author.address; } ); + const arrAuthorAddresses = authors.map(({address}) => address ); async.eachSeries( rows, - function(objConflictingRecord, cb2){ - if (arrAuthorAddresses.indexOf(objConflictingRecord.address) === -1) - throw Error("conflicting "+type+" spent from another address?"); - graph.determineIfIncludedOrEqual(conn, objConflictingRecord.unit, objUnit.parent_units, function(bIncluded){ + ({address, unit, main_chain_index, sequence}, cb2) => { + if (arrAuthorAddresses.indexOf(address) === -1) + throw Error(`conflicting ${type} spent from another address?`); + graph.determineIfIncludedOrEqual(conn, unit, parent_units, bIncluded => { if (bIncluded){ - var error = objUnit.unit+": conflicting "+type+" in inner unit "+objConflictingRecord.unit; + const error = `${unit}: conflicting ${type} in inner unit ${unit}`; // too young (serial or nonserial) - if (objConflictingRecord.main_chain_index > objValidationState.last_ball_mci || objConflictingRecord.main_chain_index === null) + if (main_chain_index > last_ball_mci || main_chain_index === null) return cb2(error); // in good sequence (final state) - if (objConflictingRecord.sequence === 'good') + if (sequence === 'good') return cb2(error); // to be voided: can reuse the output - if (objConflictingRecord.sequence === 'final-bad') + if (sequence === 'final-bad') return cb2(); - throw Error("unreachable code, conflicting "+type+" in unit "+objConflictingRecord.unit); + throw Error(`unreachable code, conflicting ${type} in unit ${unit}`); } else{ // arrAddressesWithForkedPath is not set when validating private payments - if (objValidationState.arrAddressesWithForkedPath && objValidationState.arrAddressesWithForkedPath.indexOf(objConflictingRecord.address) === -1) - throw Error("double spending "+type+" without double spending address?"); + if (arrAddressesWithForkedPath && arrAddressesWithForkedPath.indexOf(address) === -1) + throw Error(`double spending ${type} without double spending address?`); cb2(); } }); }, - function(err){ + err => { if (err) return cb(err); onAcceptedDoublespends(cb); @@ -1111,11 +1111,11 @@ function checkForDoublespends(conn, type, sql, arrSqlArgs, objUnit, objValidatio } function validateInlinePayload(conn, objMessage, message_index, objUnit, objValidationState, callback){ - var payload = objMessage.payload; + const payload = objMessage.payload; if (typeof payload === "undefined") return callback("no inline payload"); if (objectHash.getBase64Hash(payload) !== objMessage.payload_hash) - return callback("wrong payload hash: expected "+objectHash.getBase64Hash(payload)+", got "+objMessage.payload_hash); + return callback(`wrong payload hash: expected ${objectHash.getBase64Hash(payload)}, got ${objMessage.payload_hash}`); switch (objMessage.app){ @@ -1127,8 +1127,8 @@ function validateInlinePayload(conn, objMessage, message_index, objUnit, objVali case "address_definition_change": if (hasFieldsExcept(payload, ["definition_chash", "address"])) return callback("unknown fields in address_definition_change"); - var arrAuthorAddresses = objUnit.authors.map(function(author) { return author.address; } ); - var address; + const arrAuthorAddresses = objUnit.authors.map(author => author.address ); + let address; if (objUnit.authors.length > 1){ if (!isValidAddress(payload.address)) return callback("when multi-authored, must indicate address"); @@ -1157,14 +1157,14 @@ function validateInlinePayload(conn, objMessage, message_index, objUnit, objVali if (typeof payload !== "object" || Array.isArray(payload)) return callback("poll payload must be object"); if (hasFieldsExcept(payload, ["question", "choices"])) - return callback("unknown fields in "+objMessage.app); + return callback(`unknown fields in ${objMessage.app}`); if (typeof payload.question !== 'string') return callback("no question in poll"); if (!isNonemptyArray(payload.choices)) return callback("no choices in poll"); if (payload.choices.length > constants.MAX_CHOICES_PER_POLL) return callback("too many choices in poll"); - for (var i=0; i { if (poll_unit_rows.length > 1) throw Error("more than one poll?"); if (poll_unit_rows.length === 0) - return callback("invalid choice "+payload.choice+" or poll "+payload.unit); - var objPollUnitProps = poll_unit_rows[0]; + return callback(`invalid choice ${payload.choice} or poll ${payload.unit}`); + const objPollUnitProps = poll_unit_rows[0]; if (objPollUnitProps.main_chain_index === null || objPollUnitProps.main_chain_index > objValidationState.last_ball_mci) return callback("poll unit must be before last ball"); if (objPollUnitProps.sequence !== 'good') @@ -1200,20 +1200,20 @@ function validateInlinePayload(conn, objMessage, message_index, objUnit, objVali objValidationState.bHasDataFeed = true; if (typeof payload !== "object" || Array.isArray(payload) || Object.keys(payload).length === 0) return callback("data feed payload must be non-empty object"); - for (var feed_name in payload){ + for (const feed_name in payload){ if (feed_name.length > constants.MAX_DATA_FEED_NAME_LENGTH) - return callback("feed name "+feed_name+" too long"); - var value = payload[feed_name]; + return callback(`feed name ${feed_name} too long`); + const value = payload[feed_name]; if (typeof value === 'string'){ if (value.length > constants.MAX_DATA_FEED_VALUE_LENGTH) - return callback("value "+value+" too long"); + return callback(`value ${value} too long`); } else if (typeof value === 'number'){ if (!isInteger(value)) return callback("fractional numbers not allowed in data feeds"); } else - return callback("data feed "+feed_name+" must be string or number"); + return callback(`data feed ${feed_name} must be string or number`); } return callback(); @@ -1226,7 +1226,7 @@ function validateInlinePayload(conn, objMessage, message_index, objUnit, objVali // no break, continuing case "data": if (typeof payload !== "object" || payload === null) - return callback(objMessage.app+" payload must be object"); + return callback(`${objMessage.app} payload must be object`); return callback(); case "definition_template": @@ -1234,14 +1234,14 @@ function validateInlinePayload(conn, objMessage, message_index, objUnit, objVali return callback("can be only one definition template"); objValidationState.bHasDefinitionTemplate = true; if (!ValidationUtils.isArrayOfLength(payload, 2)) - return callback(objMessage.app+" payload must be array of two elements"); + return callback(`${objMessage.app} payload must be array of two elements`); return callback(); case "attestation": if (objUnit.authors.length !== 1) return callback("attestation must be single-authored"); if (hasFieldsExcept(payload, ["address", "profile"])) - return callback("unknown fields in "+objMessage.app); + return callback(`unknown fields in ${objMessage.app}`); if (!isValidAddress(payload.address)) return callback("attesting an invalid address"); if (typeof payload.profile !== 'object' || payload.profile === null) @@ -1271,7 +1271,7 @@ function validateInlinePayload(conn, objMessage, message_index, objUnit, objVali break; default: - return callback("unknown app: "+objMessage.app); + return callback(`unknown app: ${objMessage.app}`); } } @@ -1291,9 +1291,9 @@ function validatePayment(conn, payload, message_index, objUnit, objValidationSta if (!isStringOfLength(payload.asset, constants.HASH_LENGTH)) return callback("invalid asset"); - var arrAuthorAddresses = objUnit.authors.map(function(author) { return author.address; } ); + const arrAuthorAddresses = objUnit.authors.map(({address}) => address ); // note that light clients cannot check attestations - storage.loadAssetWithListOfAttestedAuthors(conn, payload.asset, objValidationState.last_ball_mci, arrAuthorAddresses, function(err, objAsset){ + storage.loadAssetWithListOfAttestedAuthors(conn, payload.asset, objValidationState.last_ball_mci, arrAuthorAddresses, (err, objAsset) => { if (err) return callback(err); if (hasFieldsExcept(payload, ["inputs", "outputs", "asset", "denomination"])) @@ -1312,8 +1312,8 @@ function validatePayment(conn, payload, message_index, objUnit, objValidationSta } if (!!objAsset.is_private !== !!objValidationState.bPrivate) return callback("asset privacy mismatch"); - var bIssue = (payload.inputs[0].type === "issue"); - var issuer_address; + const bIssue = (payload.inputs[0].type === "issue"); + let issuer_address; if (bIssue){ if (arrAuthorAddresses.length === 1) issuer_address = arrAuthorAddresses[0]; @@ -1345,11 +1345,11 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, // if (objAsset) // profiler2.start(); - var denomination = payload.denomination || 1; - var arrAuthorAddresses = objUnit.authors.map(function(author) { return author.address; } ); - var arrInputAddresses = []; // used for non-transferrable assets only - var arrOutputAddresses = []; - var total_input = 0; + const denomination = payload.denomination || 1; + const arrAuthorAddresses = objUnit.authors.map(({address}) => address ); + let arrInputAddresses = []; // used for non-transferrable assets only + const arrOutputAddresses = []; + let total_input = 0; if (payload.inputs.length > constants.MAX_INPUTS_PER_PAYMENT_MESSAGE) return callback("too many inputs"); if (payload.outputs.length > constants.MAX_OUTPUTS_PER_PAYMENT_MESSAGE) @@ -1358,16 +1358,16 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, if (objAsset && objAsset.fixed_denominations && payload.inputs.length !== 1) return callback("fixed denominations payment must have 1 input"); - var total_output = 0; - var prev_address = ""; // if public, outputs must be sorted by address - var prev_amount = 0; - var count_open_outputs = 0; - for (var i=0; i output.address) return callback("output addresses not sorted"); else if (prev_address === output.address && prev_amount > output.amount) @@ -1405,32 +1405,32 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, total_output += output.amount; } if (objAsset && objAsset.is_private && count_open_outputs !== 1) - return callback("found "+count_open_outputs+" open outputs, expected 1"); + return callback(`found ${count_open_outputs} open outputs, expected 1`); - var bIssue = false; - var bHaveHeadersComissions = false; - var bHaveWitnessings = false; + let bIssue = false; + let bHaveHeadersComissions = false; + let bHaveWitnessings = false; // same for both public and private - function validateIndivisibleIssue(input, cb){ + function validateIndivisibleIssue({amount}, cb) { // if (objAsset) // profiler2.start(); conn.query( "SELECT count_coins FROM asset_denominations WHERE asset=? AND denomination=?", [payload.asset, denomination], - function(rows){ + rows => { if (rows.length === 0) - return cb("invalid denomination: "+denomination); + return cb(`invalid denomination: ${denomination}`); if (rows.length > 1) throw Error("more than one record per denomination?"); - var denomInfo = rows[0]; + const denomInfo = rows[0]; if (denomInfo.count_coins === null){ // uncapped - if (input.amount % denomination !== 0) + if (amount % denomination !== 0) return cb("issue amount must be multiple of denomination"); } else{ - if (input.amount !== denomination * denomInfo.count_coins) - return cb("wrong size of issue of denomination "+denomination); + if (amount !== denomination * denomInfo.count_coins) + return cb(`wrong size of issue of denomination ${denomination}`); } // if (objAsset) // profiler2.stop('validateIndivisibleIssue'); @@ -1446,7 +1446,7 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, // no particular sorting order within the groups async.forEachOfSeries( payload.inputs, - function(input, input_index, cb){ + (input, input_index, cb) => { if (objAsset){ if ("type" in input && input.type !== "issue") return cb("non-base input can have only type=issue"); @@ -1455,44 +1455,43 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, if ("type" in input && !isNonemptyString(input.type)) return cb("bad input type"); } - var type = input.type || "transfer"; + const type = input.type || "transfer"; - var doubleSpendFields = "unit, address, message_index, input_index, main_chain_index, sequence, is_stable"; - var doubleSpendWhere; - var doubleSpendVars = []; + const doubleSpendFields = "unit, address, message_index, input_index, main_chain_index, sequence, is_stable"; + let doubleSpendWhere; + let doubleSpendVars = []; function checkInputDoubleSpend(cb2){ // if (objAsset) // profiler2.start(); - doubleSpendWhere += " AND unit != " + conn.escape(objUnit.unit); + doubleSpendWhere += ` AND unit != ${conn.escape(objUnit.unit)}`; if (objAsset){ doubleSpendWhere += " AND asset=?"; doubleSpendVars.push(payload.asset); } else doubleSpendWhere += " AND asset IS NULL"; - var doubleSpendQuery = "SELECT "+doubleSpendFields+" FROM inputs JOIN units USING(unit) WHERE "+doubleSpendWhere; + const doubleSpendQuery = `SELECT ${doubleSpendFields} FROM inputs JOIN units USING(unit) WHERE ${doubleSpendWhere}`; checkForDoublespends( conn, "divisible input", doubleSpendQuery, doubleSpendVars, objUnit, objValidationState, function acceptDoublespends(cb3){ - console.log("--- accepting doublespend on unit "+objUnit.unit); - var sql = "UPDATE inputs SET is_unique=NULL WHERE "+doubleSpendWhere+ - " AND (SELECT is_stable FROM units WHERE units.unit=inputs.unit)=0"; + console.log(`--- accepting doublespend on unit ${objUnit.unit}`); + const sql = `UPDATE inputs SET is_unique=NULL WHERE ${doubleSpendWhere} AND (SELECT is_stable FROM units WHERE units.unit=inputs.unit)=0`; if (!(objAsset && objAsset.is_private)){ - objValidationState.arrAdditionalQueries.push({sql: sql, params: doubleSpendVars}); - objValidationState.arrDoubleSpendInputs.push({message_index: message_index, input_index: input_index}); + objValidationState.arrAdditionalQueries.push({sql, params: doubleSpendVars}); + objValidationState.arrDoubleSpendInputs.push({message_index, input_index}); return cb3(); } - mutex.lock(["private_write"], function(unlock){ - console.log("--- will ununique the conflicts of unit "+objUnit.unit); + mutex.lock(["private_write"], unlock => { + console.log(`--- will ununique the conflicts of unit ${objUnit.unit}`); conn.query( sql, doubleSpendVars, - function(){ - console.log("--- ununique done unit "+objUnit.unit); - objValidationState.arrDoubleSpendInputs.push({message_index: message_index, input_index: input_index}); + () => { + console.log(`--- ununique done unit ${objUnit.unit}`); + objValidationState.arrDoubleSpendInputs.push({message_index, input_index}); unlock(); cb3(); } @@ -1501,7 +1500,7 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, }, function onDone(err){ if (err && objAsset && objAsset.is_private) - throw Error("spend proof didn't help: "+err); + throw Error(`spend proof didn't help: ${err}`); // if (objAsset) // profiler2.stop('checkInputDoubleSpend'); cb2(err); @@ -1539,7 +1538,7 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, if (typeof input.address !== "string") return cb("when multi-authored, must put address in issue input"); if (arrAuthorAddresses.indexOf(input.address) === -1) - return cb("issue input address "+input.address+" is not an author"); + return cb(`issue input address ${input.address} is not an author`); address = input.address; } @@ -1556,9 +1555,9 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, } total_input += input.amount; - var input_key = (payload.asset || "base") + "-" + denomination + "-" + address + "-" + input.serial_number; + var input_key = `${payload.asset || "base"}-${denomination}-${address}-${input.serial_number}`; if (objValidationState.arrInputKeys.indexOf(input_key) >= 0) - return callback("input "+input_key+" already used"); + return callback(`input ${input_key} already used`); objValidationState.arrInputKeys.push(input_key); doubleSpendWhere = "type='issue'"; doubleSpendVars = []; @@ -1577,7 +1576,7 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, // if (objAsset) // profiler2.stop('validate issue'); if (objAsset && objAsset.fixed_denominations){ - validateIndivisibleIssue(input, function(err){ + validateIndivisibleIssue(input, err => { if (err) return cb(err); checkInputDoubleSpend(cb); @@ -1602,9 +1601,9 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, if (!isNonnegativeInteger(input.output_index)) return cb("no output_index in payment input"); - var input_key = (payload.asset || "base") + "-" + input.unit + "-" + input.message_index + "-" + input.output_index; + var input_key = `${payload.asset || "base"}-${input.unit}-${input.message_index}-${input.output_index}`; if (objValidationState.arrInputKeys.indexOf(input_key) >= 0) - return cb("input "+input_key+" already used"); + return cb(`input ${input_key} already used`); objValidationState.arrInputKeys.push(input_key); doubleSpendWhere = "type=? AND src_unit=? AND src_message_index=? AND src_output_index=?"; @@ -1616,14 +1615,14 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, if (objAsset && objAsset.is_private && objAsset.fixed_denominations){ if (!objValidationState.src_coin) throw Error("no src_coin"); - var src_coin = objValidationState.src_coin; + const src_coin = objValidationState.src_coin; if (!src_coin.src_output) throw Error("no src_output"); if (!isPositiveInteger(src_coin.denomination)) throw Error("no denomination in src coin"); if (!isPositiveInteger(src_coin.amount)) throw Error("no src coin amount"); - var owner_address = src_coin.src_output.address; + const owner_address = src_coin.src_output.address; if (arrAuthorAddresses.indexOf(owner_address) === -1) return cb("output owner is not among authors"); if (denomination !== src_coin.denomination) @@ -1635,7 +1634,7 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, if (arrInputAddresses.indexOf(owner_address) === -1) arrInputAddresses.push(owner_address); total_input += src_coin.amount; - console.log("-- val state "+JSON.stringify(objValidationState)); + console.log(`-- val state ${JSON.stringify(objValidationState)}`); // if (objAsset) // profiler2.stop('validate transfer'); return checkInputDoubleSpend(cb); @@ -1647,12 +1646,12 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, JOIN units USING(unit) \n\ WHERE outputs.unit=? AND message_index=? AND output_index=?", [input.unit, input.message_index, input.output_index], - function(rows){ + rows => { if (rows.length > 1) throw Error("more than 1 src output"); if (rows.length === 0) - return cb("input unit "+input.unit+" not found"); - var src_output = rows[0]; + return cb(`input unit ${input.unit} not found`); + const src_output = rows[0]; if (typeof src_output.amount !== 'number') throw Error("src output amount is not a number"); if (!(!payload.asset && !src_output.asset || payload.asset === src_output.asset)) @@ -1665,8 +1664,8 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, return cb("src output must be before last ball"); } if (src_output.sequence !== 'good') // it is also stable or private - return cb("input unit "+input.unit+" is not serial"); - var owner_address = src_output.address; + return cb(`input unit ${input.unit} is not serial`); + const owner_address = src_output.address; if (arrAuthorAddresses.indexOf(owner_address) === -1) return cb("output owner is not among authors"); if (denomination !== src_output.denomination) @@ -1686,9 +1685,9 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, // when divisible, the asset is also non-transferrable and auto-destroy, // then this transfer is a transfer back to the issuer // and input.unit is known both to payer and the payee (issuer), even if light - graph.determineIfIncluded(conn, input.unit, [objUnit.unit], function(bIncluded){ + graph.determineIfIncluded(conn, input.unit, [objUnit.unit], bIncluded => { if (!bIncluded) - return cb("input "+input.unit+" is not in your genes"); + return cb(`input ${input.unit} is not in your genes`); checkInputDoubleSpend(cb); }); } @@ -1706,7 +1705,7 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, else bHaveWitnessings = true; if (objAsset) - return cb("only base asset can have "+type); + return cb(`only base asset can have ${type}`); if (hasFieldsExcept(input, ["type", "from_main_chain_index", "to_main_chain_index", "address"])) return cb("unknown fields in witnessing input"); if (!isNonnegativeInteger(input.from_main_chain_index)) @@ -1723,42 +1722,42 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, var address = null; if (arrAuthorAddresses.length === 1){ if ("address" in input) - return cb("when single-authored, must not put address in "+type+" input"); + return cb(`when single-authored, must not put address in ${type} input`); address = arrAuthorAddresses[0]; } else{ if (typeof input.address !== "string") - return cb("when multi-authored, must put address in "+type+" input"); + return cb(`when multi-authored, must put address in ${type} input`); if (arrAuthorAddresses.indexOf(input.address) === -1) - return cb(type+" input address "+input.address+" is not an author"); + return cb(`${type} input address ${input.address} is not an author`); address = input.address; } - var input_key = type + "-" + address + "-" + input.from_main_chain_index; + var input_key = `${type}-${address}-${input.from_main_chain_index}`; if (objValidationState.arrInputKeys.indexOf(input_key) >= 0) - return cb("input "+input_key+" already used"); + return cb(`input ${input_key} already used`); objValidationState.arrInputKeys.push(input_key); doubleSpendWhere = "type=? AND from_main_chain_index=? AND address=? AND asset IS NULL"; doubleSpendVars = [type, input.from_main_chain_index, address]; - mc_outputs.readNextSpendableMcIndex(conn, type, address, objValidationState.arrConflictingUnits, function(next_spendable_mc_index){ + mc_outputs.readNextSpendableMcIndex(conn, type, address, objValidationState.arrConflictingUnits, next_spendable_mc_index => { if (input.from_main_chain_index < next_spendable_mc_index) - return cb(type+" ranges must not overlap"); // gaps allowed, in case a unit becomes bad due to another address being nonserial - var max_mci = (type === "headers_commission") + return cb(`${type} ranges must not overlap`); // gaps allowed, in case a unit becomes bad due to another address being nonserial + const max_mci = (type === "headers_commission") ? headers_commission.getMaxSpendableMciForLastBallMci(objValidationState.last_ball_mci) : paid_witnessing.getMaxSpendableMciForLastBallMci(objValidationState.last_ball_mci); if (input.to_main_chain_index > max_mci) - return cb(type+" to_main_chain_index is too large"); + return cb(`${type} to_main_chain_index is too large`); - var calcFunc = (type === "headers_commission") ? mc_outputs.calcEarnings : paid_witnessing.calcWitnessEarnings; + const calcFunc = (type === "headers_commission") ? mc_outputs.calcEarnings : paid_witnessing.calcWitnessEarnings; calcFunc(conn, type, input.from_main_chain_index, input.to_main_chain_index, address, { - ifError: function(err){ + ifError(err) { throw Error(err); }, - ifOk: function(commission){ + ifOk(commission) { if (commission === 0) - return cb("zero "+type+" commission"); + return cb(`zero ${type} commission`); total_input += commission; checkInputDoubleSpend(cb); } @@ -1767,16 +1766,16 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, break; default: - return cb("unrecognized input type: "+input.type); + return cb(`unrecognized input type: ${input.type}`); } }, - function(err){ - console.log("inputs done "+payload.asset, arrInputAddresses, arrOutputAddresses); + err => { + console.log(`inputs done ${payload.asset}`, arrInputAddresses, arrOutputAddresses); if (err) return callback(err); if (objAsset){ if (total_input !== total_output) - return callback("inputs and outputs do not balance: "+total_input+" !== "+total_output); + return callback(`inputs and outputs do not balance: ${total_input} !== ${total_output}`); if (!objAsset.is_transferrable){ // the condition holds for issues too if (arrInputAddresses.length === 1 && arrInputAddresses[0] === objAsset.definer_address || arrOutputAddresses.length === 1 && arrOutputAddresses[0] === objAsset.definer_address @@ -1792,25 +1791,25 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, return callback("the asset is not transferrable"); } async.series([ - function(cb){ + cb => { if (!objAsset.spender_attested) return cb(); storage.filterAttestedAddresses( conn, objAsset, lobjValidationState.last_ball_mci, arrOutputAddresses, - function(arrAttestedOutputAddresses){ - if (arrAttestedOutputAddresses.length !== arrOutputAddresses.length) + ({length}) => { + if (length !== arrOutputAddresses.length) return cb("some output addresses are not attested"); cb(); } ); }, - function(cb){ - var arrCondition = bIssue ? objAsset.issue_condition : objAsset.transfer_condition; + cb => { + const arrCondition = bIssue ? objAsset.issue_condition : objAsset.transfer_condition; if (!arrCondition) return cb(); Definition.evaluateAssetCondition( conn, payload.asset, arrCondition, objUnit, objValidationState, - function(cond_err, bSatisfiesCondition){ + (cond_err, bSatisfiesCondition) => { if (cond_err) return cb(cond_err); if (!bSatisfiesCondition) @@ -1824,7 +1823,7 @@ function validatePaymentInputsAndOutputs(conn, payload, objAsset, message_index, } else{ // base asset if (total_input !== total_output + objUnit.headers_commission + objUnit.payload_commission) - return callback("inputs and outputs do not balance: "+total_input+" !== "+total_output+" + "+objUnit.headers_commission+" + "+objUnit.payload_commission); + return callback(`inputs and outputs do not balance: ${total_input} !== ${total_output} + ${objUnit.headers_commission} + ${objUnit.payload_commission}`); callback(); } // console.log("validatePaymentInputsAndOutputs done"); @@ -1843,31 +1842,33 @@ function initPrivatePaymentValidationState(conn, unit, message_index, payload, o LEFT JOIN units AS lb_units ON units.last_ball_unit=lb_units.unit \n\ WHERE messages.unit=? AND message_index=?", [unit, message_index], - function(rows){ + rows => { if (rows.length > 1) throw Error("more than 1 message by index"); if (rows.length === 0) return onError("message not found"); - var row = rows[0]; + const row = rows[0]; if (row.sequence !== "good" && row.is_stable === 1) return onError("unit is final nonserial"); - var bStable = (row.is_stable === 1); // it's ok if the unit is not stable yet + const bStable = (row.is_stable === 1); // it's ok if the unit is not stable yet if (row.app !== "payment") return onError("invalid app"); if (objectHash.getBase64Hash(payload) !== row.payload_hash) return onError("payload hash does not match"); - var objValidationState = { + const objValidationState = { last_ball_mci: row.last_ball_mci, arrDoubleSpendInputs: [], arrInputKeys: [], bPrivate: true }; - var objPartialUnit = {unit: unit}; - storage.readUnitAuthors(conn, unit, function(arrAuthors){ - objPartialUnit.authors = arrAuthors.map(function(address){ return {address: address}; }); // array of objects {address: address} + const objPartialUnit = {unit}; + storage.readUnitAuthors(conn, unit, arrAuthors => { + objPartialUnit.authors = arrAuthors.map(address => ({ + address + })); // array of objects {address: address} // we need parent_units in checkForDoublespends in case it is a doublespend - conn.query("SELECT parent_unit FROM parenthoods WHERE child_unit=? ORDER BY parent_unit", [unit], function(prows){ - objPartialUnit.parent_units = prows.map(function(prow){ return prow.parent_unit; }); + conn.query("SELECT parent_unit FROM parenthoods WHERE child_unit=? ORDER BY parent_unit", [unit], prows => { + objPartialUnit.parent_units = prows.map(({parent_unit}) => parent_unit); onDone(bStable, objPartialUnit, objValidationState); }); }); @@ -1888,7 +1889,7 @@ function validateAssetDefinition(conn, payload, objUnit, objValidationState, cal return callback("invalid cap"); // attestors - var err; + let err; if ( payload.spender_attested && (err=checkAttestorList(payload.attestors)) ) return callback(err); @@ -1898,11 +1899,11 @@ function validateAssetDefinition(conn, payload, objUnit, objValidationState, cal if (payload.denominations){ if (payload.denominations.length > constants.MAX_DENOMINATIONS_PER_ASSET_DEFINITION) return callback("too many denominations"); - var total_cap_from_denominations = 0; - var bHasUncappedDenominations = false; - var prev_denom = 0; - for (var i=0; i { if (!("issue_condition" in payload)) return cb(); Definition.validateDefinition(conn, payload.issue_condition, objUnit, objValidationState, null, true, cb); }, - function(cb){ + cb => { if (!("transfer_condition" in payload)) return cb(); Definition.validateDefinition(conn, payload.transfer_condition, objUnit, objValidationState, null, true, cb); @@ -1959,19 +1960,19 @@ function validateAssetDefinition(conn, payload, objUnit, objValidationState, cal ], callback); } -function validateAssetorListUpdate(conn, payload, objUnit, objValidationState, callback){ - if (objUnit.authors.length !== 1) +function validateAssetorListUpdate(conn, {asset, attestors}, {authors}, {last_ball_mci}, callback) { + if (authors.length !== 1) return callback("attestor list must be single-authored"); - if (!isStringOfLength(payload.asset, constants.HASH_LENGTH)) + if (!isStringOfLength(asset, constants.HASH_LENGTH)) return callback("invalid asset in attestor list update"); - storage.readAsset(conn, payload.asset, objValidationState.last_ball_mci, function(err, objAsset){ + storage.readAsset(conn, asset, last_ball_mci, (err, {spender_attested, definer_address}) => { if (err) return callback(err); - if (!objAsset.spender_attested) + if (!spender_attested) return callback("this asset does not require attestors"); - if (objUnit.authors[0].address !== objAsset.definer_address) + if (authors[0].address !== definer_address) return callback("attestor list can be edited only by definer"); - err = checkAttestorList(payload.attestors); + err = checkAttestorList(attestors); if (err) return callback(err); callback(); @@ -1983,12 +1984,12 @@ function checkAttestorList(arrAttestors){ return "attestors not defined"; if (arrAttestors.length > constants.MAX_ATTESTORS_PER_ASSET) return "too many attestors"; - var prev=""; - for (var i=0; i { if (err) // error in address definition return callback(err); if (!res) // wrong signature or the like diff --git a/validation_utils.js b/validation_utils.js index 0be0c20a..1c2e0ed7 100644 --- a/validation_utils.js +++ b/validation_utils.js @@ -1,12 +1,11 @@ /*jslint node: true */ -"use strict"; -var chash = require('./chash.js'); +const chash = require('./chash.js'); /** * True if there is at least one field in obj that is not in arrFields. */ function hasFieldsExcept(obj, arrFields){ - for (var field in obj) + for (const field in obj) if (arrFields.indexOf(field) === -1) return true; return false; @@ -19,7 +18,7 @@ function hasFieldsExcept(obj, arrFields){ */ function isInteger(value){ return typeof value === 'number' && isFinite(value) && Math.floor(value) === value; -}; +} /** * True if int is an integer strictly greater than zero. diff --git a/wallet.js b/wallet.js index 26754f75..f25becea 100644 --- a/wallet.js +++ b/wallet.js @@ -1,47 +1,46 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var _ = require('lodash'); -var db = require('./db.js'); -var constants = require('./constants.js'); -var conf = require('./conf.js'); -var mutex = require('./mutex.js'); -var objectHash = require('./object_hash.js'); -var ecdsaSig = require('./signature.js'); -var network = require('./network.js'); -var storage = require('./storage.js'); -var device = require('./device.js'); -var walletGeneral = require('./wallet_general.js'); -var lightWallet = require('./light_wallet.js'); -var walletDefinedByKeys = require('./wallet_defined_by_keys.js'); -var walletDefinedByAddresses = require('./wallet_defined_by_addresses.js'); -var eventBus = require('./event_bus.js'); -var ValidationUtils = require("./validation_utils.js"); -var composer = require('./composer.js'); -var indivisibleAsset = require('./indivisible_asset.js'); -var divisibleAsset = require('./divisible_asset.js'); -var profiler = require('./profiler.js'); -var breadcrumbs = require('./breadcrumbs.js'); -var balances = require('./balances'); -var Mnemonic = require('bitcore-mnemonic'); - -var message_counter = 0; -var assocLastFailedAssetMetadataTimestamps = {}; -var ASSET_METADATA_RETRY_PERIOD = 3600*1000; +const async = require('async'); +const _ = require('lodash'); +const db = require('./db.js'); +const constants = require('./constants.js'); +const conf = require('./conf.js'); +const mutex = require('./mutex.js'); +const objectHash = require('./object_hash.js'); +const ecdsaSig = require('./signature.js'); +const network = require('./network.js'); +const storage = require('./storage.js'); +const device = require('./device.js'); +const walletGeneral = require('./wallet_general.js'); +const lightWallet = require('./light_wallet.js'); +const walletDefinedByKeys = require('./wallet_defined_by_keys.js'); +const walletDefinedByAddresses = require('./wallet_defined_by_addresses.js'); +const eventBus = require('./event_bus.js'); +const ValidationUtils = require("./validation_utils.js"); +const composer = require('./composer.js'); +const indivisibleAsset = require('./indivisible_asset.js'); +const divisibleAsset = require('./divisible_asset.js'); +const profiler = require('./profiler.js'); +const breadcrumbs = require('./breadcrumbs.js'); +const balances = require('./balances'); +const Mnemonic = require('bitcore-mnemonic'); + +let message_counter = 0; +const assocLastFailedAssetMetadataTimestamps = {}; +const ASSET_METADATA_RETRY_PERIOD = 3600*1000; function handleJustsaying(ws, subject, body){ switch (subject){ // I'm connected to a hub, received challenge case 'hub/challenge': - var challenge = body; + const challenge = body; device.handleChallenge(ws, challenge); break; // I'm connected to a hub, received a message through the hub case 'hub/message': - var objDeviceMessage = body.message; - var message_hash = body.message_hash; - var respondWithError = function(error){ + const objDeviceMessage = body.message; + const message_hash = body.message_hash; + const respondWithError = error => { network.sendError(ws, error); network.sendJustsaying(ws, 'hub/delete', message_hash); }; @@ -60,25 +59,25 @@ function handleJustsaying(ws, subject, body){ // end of checks on the open (unencrypted) part of the message. These checks should've been made by the hub before accepting the message // decrypt the message - var json = device.decryptPackage(objDeviceMessage.encrypted_package); + const json = device.decryptPackage(objDeviceMessage.encrypted_package); if (!json) return respondWithError("failed to decrypt"); // who is the sender - var from_address = objectHash.getDeviceAddress(objDeviceMessage.pubkey); + const from_address = objectHash.getDeviceAddress(objDeviceMessage.pubkey); // the hub couldn't mess with json.from as it was encrypted, but it could replace the objDeviceMessage.pubkey and re-sign. It'll be caught here if (from_address !== json.from) return respondWithError("wrong message signature"); - var handleMessage = function(bIndirectCorrespondent){ + const handleMessage = bIndirectCorrespondent => { // serialize all messages from hub - mutex.lock(["from_hub"], function(unlock){ + mutex.lock(["from_hub"], unlock => { handleMessageFromHub(ws, json, objDeviceMessage.pubkey, bIndirectCorrespondent, { - ifError: function(err){ + ifError(err) { respondWithError(err); unlock(); }, - ifOk: function(){ + ifOk() { network.sendJustsaying(ws, 'hub/delete', message_hash); unlock(); } @@ -86,17 +85,17 @@ function handleJustsaying(ws, subject, body){ }); }; // check that we know this device - db.query("SELECT hub, is_indirect FROM correspondent_devices WHERE device_address=?", [from_address], function(rows){ + db.query("SELECT hub, is_indirect FROM correspondent_devices WHERE device_address=?", [from_address], rows => { if (rows.length > 0){ if (json.device_hub && json.device_hub !== rows[0].hub) // update correspondent's home address if necessary - db.query("UPDATE correspondent_devices SET hub=? WHERE device_address=?", [json.device_hub, from_address], function(){ + db.query("UPDATE correspondent_devices SET hub=? WHERE device_address=?", [json.device_hub, from_address], () => { handleMessage(rows[0].is_indirect); }); else handleMessage(rows[0].is_indirect); } else{ // correspondent not known - var arrSubjectsAllowedFromNoncorrespondents = ["pairing", "my_xpubkey", "wallet_fully_approved"]; + const arrSubjectsAllowedFromNoncorrespondents = ["pairing", "my_xpubkey", "wallet_fully_approved"]; if (arrSubjectsAllowedFromNoncorrespondents.indexOf(json.subject) === -1) return respondWithError("correspondent not known and not whitelisted subject"); handleMessage(false); @@ -127,18 +126,18 @@ eventBus.on("message_for_light", handleJustsaying); // called from UI after user confirms signing request initiated from another device, initiator device being the recipient of this message function sendSignature(device_address, signed_text, signature, signing_path, address){ - device.sendMessageToDevice(device_address, "signature", {signed_text: signed_text, signature: signature, signing_path: signing_path, address: address}); + device.sendMessageToDevice(device_address, "signature", {signed_text, signature, signing_path, address}); } // one of callbacks MUST be called, otherwise the mutex will stay locked function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, callbacks){ - var subject = json.subject; - var body = json.body; + const subject = json.subject; + const body = json.body; if (!subject || typeof body == "undefined") return callbacks.ifError("no subject or body"); //if (bIndirectCorrespondent && ["cancel_new_wallet", "my_xpubkey", "new_wallet_address"].indexOf(subject) === -1) // return callbacks.ifError("you're indirect correspondent, cannot trust "+subject+" from you"); - var from_address = objectHash.getDeviceAddress(device_pubkey); + const from_address = objectHash.getDeviceAddress(device_pubkey); switch (subject){ case "pairing": @@ -157,12 +156,12 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c case "removed_paired_device": if(conf.bIgnoreUnpairRequests) { // unpairing is ignored - callbacks.ifError("removed_paired_device ignored: "+from_address); + callbacks.ifError(`removed_paired_device ignored: ${from_address}`); } else { - determineIfDeviceCanBeRemoved(from_address, function(bRemovable){ + determineIfDeviceCanBeRemoved(from_address, bRemovable => { if (!bRemovable) - return callbacks.ifError("device "+from_address+" is not removable"); - device.removeCorrespondentDevice(from_address, function(){ + return callbacks.ifError(`device ${from_address} is not removable`); + device.removeCorrespondentDevice(from_address, () => { eventBus.emit("removed_paired_device", from_address); callbacks.ifOk(); }); @@ -216,7 +215,7 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c return callbacks.ifError("bad address_index"); if (!ValidationUtils.isValidAddress(body.address)) return callbacks.ifError("no address or bad address"); - walletDefinedByKeys.addNewAddress(body.wallet, body.is_change, body.address_index, body.address, function(err){ + walletDefinedByKeys.addNewAddress(body.wallet, body.is_change, body.address_index, body.address, err => { if (err) return callbacks.ifError(err); callbacks.ifOk(); @@ -229,7 +228,7 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c return callbacks.ifError("no address definition template"); walletDefinedByAddresses.validateAddressDefinitionTemplate( body.address_definition_template, from_address, - function(err, assocMemberDeviceAddressesBySigningPaths){ + (err, assocMemberDeviceAddressesBySigningPaths) => { if (err) return callbacks.ifError(err); // this event should trigger a confirmatin dialog, user needs to approve creation of the shared address and choose his @@ -266,7 +265,7 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c // {address: "BASE32", definition: [...], signers: {...}} walletDefinedByAddresses.handleNewSharedAddress(body, { ifError: callbacks.ifError, - ifOk: function(){ + ifOk() { callbacks.ifOk(); eventBus.emit('maybe_new_transactions'); } @@ -283,49 +282,49 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c return callbacks.ifError("no address or bad address"); if (!ValidationUtils.isNonemptyString(body.signing_path) || body.signing_path.charAt(0) !== 'r') return callbacks.ifError("bad signing path"); - var objUnit = body.unsigned_unit; + const objUnit = body.unsigned_unit; if (typeof objUnit !== "object") return callbacks.ifError("no unsigned unit"); // replace all existing signatures with placeholders so that signing requests sent to us on different stages of signing become identical, // hence the hashes of such unsigned units are also identical - objUnit.authors.forEach(function(author){ - var authentifiers = author.authentifiers; - for (var path in authentifiers) + objUnit.authors.forEach(author => { + const authentifiers = author.authentifiers; + for (const path in authentifiers) authentifiers[path] = authentifiers[path].replace(/./, '-'); }); - var assocPrivatePayloads = body.private_payloads; + const assocPrivatePayloads = body.private_payloads; if ("private_payloads" in body){ if (typeof assocPrivatePayloads !== "object" || !assocPrivatePayloads) return callbacks.ifError("bad private payloads"); - for (var payload_hash in assocPrivatePayloads){ - var payload = assocPrivatePayloads[payload_hash]; - var hidden_payload = _.cloneDeep(payload); + for (const payload_hash in assocPrivatePayloads){ + const payload = assocPrivatePayloads[payload_hash]; + const hidden_payload = _.cloneDeep(payload); if (payload.denomination) // indivisible asset. In this case, payload hash is calculated based on output_hash rather than address and blinding - hidden_payload.outputs.forEach(function(o){ - delete o.address; - delete o.blinding; + hidden_payload.outputs.forEach(({address, blinding}) => { + delete address; + delete blinding; }); - var calculated_payload_hash = objectHash.getBase64Hash(hidden_payload); + const calculated_payload_hash = objectHash.getBase64Hash(hidden_payload); if (payload_hash !== calculated_payload_hash) return callbacks.ifError("private payload hash does not match"); if (!ValidationUtils.isNonemptyArray(objUnit.messages)) return callbacks.ifError("no messages in unsigned unit"); - if (objUnit.messages.filter(function(objMessage){ return (objMessage.payload_hash === payload_hash); }).length !== 1) + if (objUnit.messages.filter(objMessage => objMessage.payload_hash === payload_hash).length !== 1) return callbacks.ifError("no such payload hash in the messages"); } } // findAddress handles both types of addresses findAddress(body.address, body.signing_path, { ifError: callbacks.ifError, - ifLocal: function(objAddress){ + ifLocal(objAddress) { // the commented check would make multilateral signing impossible //db.query("SELECT 1 FROM extended_pubkeys WHERE wallet=? AND device_address=?", [row.wallet, from_address], function(sender_rows){ // if (sender_rows.length !== 1) // return callbacks.ifError("sender is not cosigner of this address"); callbacks.ifOk(); objUnit.unit = objectHash.getUnitHash(objUnit); - var objJoint = {unit: objUnit, unsigned: true}; - eventBus.once("validated-"+objUnit.unit, function(bValid){ + const objJoint = {unit: objUnit, unsigned: true}; + eventBus.once(`validated-${objUnit.unit}`, bValid => { if (!bValid){ console.log("===== unit in signing request is invalid"); return; @@ -341,28 +340,28 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c network.handleOnlineJoint(ws, objJoint); //}); }, - ifRemote: function(device_address){ + ifRemote(device_address) { if (device_address === from_address){ - callbacks.ifError("looping signing request for address "+body.address+", path "+body.signing_path); - throw Error("looping signing request for address "+body.address+", path "+body.signing_path); + callbacks.ifError(`looping signing request for address ${body.address}, path ${body.signing_path}`); + throw Error(`looping signing request for address ${body.address}, path ${body.signing_path}`); } - var text_to_sign = objectHash.getUnitHashToSign(body.unsigned_unit).toString("base64"); + const text_to_sign = objectHash.getUnitHashToSign(body.unsigned_unit).toString("base64"); // I'm a proxy, wait for response from the actual signer and forward to the requestor - eventBus.once("signature-"+device_address+"-"+body.address+"-"+body.signing_path+"-"+text_to_sign, function(sig){ + eventBus.once(`signature-${device_address}-${body.address}-${body.signing_path}-${text_to_sign}`, sig => { sendSignature(from_address, text_to_sign, sig, body.signing_path, body.address); }); // forward the offer to the actual signer device.sendMessageToDevice(device_address, subject, body); callbacks.ifOk(); }, - ifMerkle: function(bLocal){ - callbacks.ifError("there is merkle proof at signing path "+body.signing_path); + ifMerkle(bLocal) { + callbacks.ifError(`there is merkle proof at signing path ${body.signing_path}`); }, - ifUnknownAddress: function(){ - callbacks.ifError("not aware of address "+body.address+" but will see if I learn about it later"); - eventBus.once("new_address-"+body.address, function(){ + ifUnknownAddress() { + callbacks.ifError(`not aware of address ${body.address} but will see if I learn about it later`); + eventBus.once(`new_address-${body.address}`, () => { // rewrite callbacks to avoid duplicate unlocking of mutex - handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, { ifOk: function(){}, ifError: function(){} }); + handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, { ifOk() {}, ifError() {} }); }); } }); @@ -378,12 +377,12 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c return callbacks.ifError("bad signing path"); if (!ValidationUtils.isValidAddress(body.address)) return callbacks.ifError("bad address"); - eventBus.emit("signature-" + from_address + "-" + body.address + "-" + body.signing_path + "-" + body.signed_text, body.signature); + eventBus.emit(`signature-${from_address}-${body.address}-${body.signing_path}-${body.signed_text}`, body.signature); callbacks.ifOk(); break; case 'private_payments': - var arrChains = body.chains; + const arrChains = body.chains; if (!ValidationUtils.isNonemptyArray(arrChains)) return callbacks.ifError("no chains found"); profiler.increment(); @@ -391,28 +390,28 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c if (conf.bLight) network.requestUnfinishedPastUnitsOfPrivateChains(arrChains); // it'll work in the background - var assocValidatedByKey = {}; - var bParsingComplete = false; - var cancelAllKeys = function(){ - for (var key in assocValidatedByKey) + let assocValidatedByKey = {}; + let bParsingComplete = false; + const cancelAllKeys = () => { + for (const key in assocValidatedByKey) eventBus.removeAllListeners(key); }; var current_message_counter = ++message_counter; - var checkIfAllValidated = function(){ + const checkIfAllValidated = () => { if (!assocValidatedByKey) // duplicate call - ignore return console.log('duplicate call of checkIfAllValidated'); - for (var key in assocValidatedByKey) + for (const key in assocValidatedByKey) if (!assocValidatedByKey[key]) return console.log('not all private payments validated yet'); assocValidatedByKey = null; // to avoid duplicate calls if (!body.forwarded){ emitNewPrivatePaymentReceived(from_address, arrChains, current_message_counter); // note, this forwarding won't work if the user closes the wallet before validation of the private chains - var arrUnits = arrChains.map(function(arrPrivateElements){ return arrPrivateElements[0].unit; }); - db.query("SELECT address FROM unit_authors WHERE unit IN(?)", [arrUnits], function(rows){ - var arrAuthorAddresses = rows.map(function(row){ return row.address; }); + const arrUnits = arrChains.map(arrPrivateElements => arrPrivateElements[0].unit); + db.query("SELECT address FROM unit_authors WHERE unit IN(?)", [arrUnits], rows => { + const arrAuthorAddresses = rows.map(({address}) => address); // if the addresses are not shared, it doesn't forward anything forwardPrivateChainsToOtherMembersOfSharedAddresses(arrChains, arrAuthorAddresses, from_address, true); }); @@ -422,32 +421,32 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c async.eachSeries( arrChains, - function(arrPrivateElements, cb){ // validate each chain individually - var objHeadPrivateElement = arrPrivateElements[0]; + (arrPrivateElements, cb) => { // validate each chain individually + const objHeadPrivateElement = arrPrivateElements[0]; if (!!objHeadPrivateElement.payload.denomination !== ValidationUtils.isNonnegativeInteger(objHeadPrivateElement.output_index)) return cb("divisibility doesn't match presence of output_index"); - var output_index = objHeadPrivateElement.payload.denomination ? objHeadPrivateElement.output_index : -1; - var payload_hash = objectHash.getBase64Hash(objHeadPrivateElement.payload); - var key = 'private_payment_validated-'+objHeadPrivateElement.unit+'-'+payload_hash+'-'+output_index; + const output_index = objHeadPrivateElement.payload.denomination ? objHeadPrivateElement.output_index : -1; + const payload_hash = objectHash.getBase64Hash(objHeadPrivateElement.payload); + const key = `private_payment_validated-${objHeadPrivateElement.unit}-${payload_hash}-${output_index}`; assocValidatedByKey[key] = false; network.handleOnlinePrivatePayment(ws, arrPrivateElements, true, { - ifError: function(error){ - console.log("handleOnlinePrivatePayment error: "+error); + ifError(error) { + console.log(`handleOnlinePrivatePayment error: ${error}`); cb("an error"); // do not leak error message to the hub }, - ifValidationError: function(unit, error){ - console.log("handleOnlinePrivatePayment validation error: "+error); + ifValidationError(unit, error) { + console.log(`handleOnlinePrivatePayment validation error: ${error}`); cb("an error"); // do not leak error message to the hub }, - ifAccepted: function(unit){ + ifAccepted(unit) { console.log("handleOnlinePrivatePayment accepted"); assocValidatedByKey[key] = true; cb(); // do not leak unit info to the hub }, // this is the most likely outcome for light clients - ifQueued: function(){ - console.log("handleOnlinePrivatePayment queued, will wait for "+key); - eventBus.once(key, function(bValid){ + ifQueued() { + console.log(`handleOnlinePrivatePayment queued, will wait for ${key}`); + eventBus.once(key, bValid => { if (!bValid) return cancelAllKeys(); assocValidatedByKey[key] = true; @@ -460,7 +459,7 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c } }); }, - function(err){ + err => { bParsingComplete = true; if (err){ cancelAllKeys(); @@ -480,66 +479,66 @@ function handleMessageFromHub(ws, json, device_pubkey, bIndirectCorrespondent, c // (we'll be fooled to believe it was sent by the evil user). It is only possible if he learns our address, e.g. if we make it public. // Normally, we generate a one-time address and share it in chat session with the future payer only. var current_message_counter = ++message_counter; - var unit = body; + const unit = body; if (!ValidationUtils.isStringOfLength(unit, constants.HASH_LENGTH)) return callbacks.ifError("invalid unit in payment notification"); - var bEmitted = false; - var emitPn = function(objJoint){ + let bEmitted = false; + const emitPn = objJoint => { if (bEmitted) return; bEmitted = true; emitNewPublicPaymentReceived(from_address, objJoint.unit, current_message_counter); }; - eventBus.once('saved_unit-'+unit, emitPn); + eventBus.once(`saved_unit-${unit}`, emitPn); storage.readJoint(db, unit, { - ifNotFound: function(){ - console.log("received payment notification for unit "+unit+" which is not known yet, will wait for it"); + ifNotFound() { + console.log(`received payment notification for unit ${unit} which is not known yet, will wait for it`); callbacks.ifOk(); }, - ifFound: function(objJoint){ + ifFound(objJoint) { emitPn(objJoint); - eventBus.removeListener('saved_unit-'+unit, emitPn); + eventBus.removeListener(`saved_unit-${unit}`, emitPn); callbacks.ifOk(); } }); break; default: - callbacks.ifError("unknnown subject: "+subject); + callbacks.ifError(`unknnown subject: ${subject}`); } } function forwardPrivateChainsToOtherMembersOfOutputAddresses(arrChains, conn, onSaved){ console.log("forwardPrivateChainsToOtherMembersOfOutputAddresses", arrChains); - var assocOutputAddresses = {}; - arrChains.forEach(function(arrPrivateElements){ - var objHeadPrivateElement = arrPrivateElements[0]; - var payload = objHeadPrivateElement.payload; - payload.outputs.forEach(function(output){ - if (output.address) - assocOutputAddresses[output.address] = true; + const assocOutputAddresses = {}; + arrChains.forEach(arrPrivateElements => { + const objHeadPrivateElement = arrPrivateElements[0]; + const payload = objHeadPrivateElement.payload; + payload.outputs.forEach(({address}) => { + if (address) + assocOutputAddresses[address] = true; }); if (objHeadPrivateElement.output && objHeadPrivateElement.output.address) assocOutputAddresses[objHeadPrivateElement.output.address] = true; }); - var arrOutputAddresses = Object.keys(assocOutputAddresses); + const arrOutputAddresses = Object.keys(assocOutputAddresses); console.log("output addresses", arrOutputAddresses); conn = conn || db; if (!onSaved) - onSaved = function(){}; - readWalletsByAddresses(conn, arrOutputAddresses, function(arrWallets){ + onSaved = () => {}; + readWalletsByAddresses(conn, arrOutputAddresses, arrWallets => { if (arrWallets.length === 0){ // breadcrumbs.add("forwardPrivateChainsToOtherMembersOfOutputAddresses: " + JSON.stringify(arrChains)); // remove in livenet - eventBus.emit('nonfatal_error', "not my wallet? output addresses: "+arrOutputAddresses.join(', '), new Error()); + eventBus.emit('nonfatal_error', `not my wallet? output addresses: ${arrOutputAddresses.join(', ')}`, new Error()); // throw Error("not my wallet? output addresses: "+arrOutputAddresses.join(', ')); } - var arrFuncs = []; + const arrFuncs = []; if (arrWallets.length > 0) - arrFuncs.push(function(cb){ + arrFuncs.push(cb => { walletDefinedByKeys.forwardPrivateChainsToOtherMembersOfWallets(arrChains, arrWallets, conn, cb); }); - arrFuncs.push(function(cb){ + arrFuncs.push(cb => { walletDefinedByAddresses.forwardPrivateChainsToOtherMembersOfAddresses(arrChains, arrOutputAddresses, conn, cb); }); async.series(arrFuncs, onSaved); @@ -547,13 +546,13 @@ function forwardPrivateChainsToOtherMembersOfOutputAddresses(arrChains, conn, on } function readWalletsByAddresses(conn, arrAddresses, handleWallets){ - conn.query("SELECT DISTINCT wallet FROM my_addresses WHERE address IN(?)", [arrAddresses], function(rows){ - var arrWallets = rows.map(function(row){ return row.wallet; }); - conn.query("SELECT DISTINCT address FROM shared_address_signing_paths WHERE shared_address IN(?)", [arrAddresses], function(rows){ + conn.query("SELECT DISTINCT wallet FROM my_addresses WHERE address IN(?)", [arrAddresses], rows => { + const arrWallets = rows.map(({wallet}) => wallet); + conn.query("SELECT DISTINCT address FROM shared_address_signing_paths WHERE shared_address IN(?)", [arrAddresses], rows => { if (rows.length === 0) return handleWallets(arrWallets); - var arrNewAddresses = rows.map(function(row){ return row.address; }); - readWalletsByAddresses(conn, arrNewAddresses, function(arrNewWallets){ + const arrNewAddresses = rows.map(({address}) => address); + readWalletsByAddresses(conn, arrNewAddresses, arrNewWallets => { handleWallets(_.union(arrWallets, arrNewWallets)); }); }); @@ -568,65 +567,65 @@ eventBus.on("new_direct_private_chains", forwardPrivateChainsToOtherMembersOfOut function emitNewPrivatePaymentReceived(payer_device_address, arrChains, message_counter){ console.log('emitNewPrivatePaymentReceived'); - walletGeneral.readMyAddresses(function(arrAddresses){ - var assocAmountsByAsset = {}; - var assocMyReceivingAddresses = {}; - arrChains.forEach(function(arrPrivateElements){ - var objHeadPrivateElement = arrPrivateElements[0]; - var payload = objHeadPrivateElement.payload; - var asset = payload.asset || 'base'; + walletGeneral.readMyAddresses(arrAddresses => { + const assocAmountsByAsset = {}; + const assocMyReceivingAddresses = {}; + arrChains.forEach(arrPrivateElements => { + const objHeadPrivateElement = arrPrivateElements[0]; + const payload = objHeadPrivateElement.payload; + const asset = payload.asset || 'base'; if (!assocAmountsByAsset[asset]) assocAmountsByAsset[asset] = 0; - payload.outputs.forEach(function(output){ - if (output.address && arrAddresses.indexOf(output.address) >= 0){ - assocAmountsByAsset[asset] += output.amount; - assocMyReceivingAddresses[output.address] = true; + payload.outputs.forEach(({address, amount}) => { + if (address && arrAddresses.indexOf(address) >= 0){ + assocAmountsByAsset[asset] += amount; + assocMyReceivingAddresses[address] = true; } }); // indivisible - var output = objHeadPrivateElement.output; + const output = objHeadPrivateElement.output; if (output && output.address && arrAddresses.indexOf(output.address) >= 0){ assocAmountsByAsset[asset] += payload.outputs[objHeadPrivateElement.output_index].amount; assocMyReceivingAddresses[output.address] = true; } }); console.log('assocAmountsByAsset', assocAmountsByAsset); - var arrMyReceivingAddresses = Object.keys(assocMyReceivingAddresses); + const arrMyReceivingAddresses = Object.keys(assocMyReceivingAddresses); if (arrMyReceivingAddresses.length === 0) return; - db.query("SELECT 1 FROM shared_addresses WHERE shared_address IN(?)", [arrMyReceivingAddresses], function(rows){ - var bToSharedAddress = (rows.length > 0); - for (var asset in assocAmountsByAsset) + db.query("SELECT 1 FROM shared_addresses WHERE shared_address IN(?)", [arrMyReceivingAddresses], ({length}) => { + const bToSharedAddress = (length > 0); + for (const asset in assocAmountsByAsset) if (assocAmountsByAsset[asset]) eventBus.emit('received_payment', payer_device_address, assocAmountsByAsset[asset], asset, message_counter, bToSharedAddress); }); }); } -function emitNewPublicPaymentReceived(payer_device_address, objUnit, message_counter){ - walletGeneral.readMyAddresses(function(arrAddresses){ - var assocAmountsByAsset = {}; - var assocMyReceivingAddresses = {}; - objUnit.messages.forEach(function(message){ +function emitNewPublicPaymentReceived(payer_device_address, {messages}, message_counter) { + walletGeneral.readMyAddresses(arrAddresses => { + const assocAmountsByAsset = {}; + const assocMyReceivingAddresses = {}; + messages.forEach(message => { if (message.app !== 'payment' || !message.payload) return; - var payload = message.payload; - var asset = payload.asset || 'base'; + const payload = message.payload; + const asset = payload.asset || 'base'; if (!assocAmountsByAsset[asset]) assocAmountsByAsset[asset] = 0; - payload.outputs.forEach(function(output){ - if (output.address && arrAddresses.indexOf(output.address) >= 0){ - assocAmountsByAsset[asset] += output.amount; - assocMyReceivingAddresses[output.address] = true; + payload.outputs.forEach(({address, amount}) => { + if (address && arrAddresses.indexOf(address) >= 0){ + assocAmountsByAsset[asset] += amount; + assocMyReceivingAddresses[address] = true; } }); }); - var arrMyReceivingAddresses = Object.keys(assocMyReceivingAddresses); + const arrMyReceivingAddresses = Object.keys(assocMyReceivingAddresses); if (arrMyReceivingAddresses.length === 0) return; - db.query("SELECT 1 FROM shared_addresses WHERE shared_address IN(?)", [arrMyReceivingAddresses], function(rows){ - var bToSharedAddress = (rows.length > 0); - for (var asset in assocAmountsByAsset) + db.query("SELECT 1 FROM shared_addresses WHERE shared_address IN(?)", [arrMyReceivingAddresses], ({length}) => { + const bToSharedAddress = (length > 0); + for (const asset in assocAmountsByAsset) if (assocAmountsByAsset[asset]) eventBus.emit('received_payment', payer_device_address, assocAmountsByAsset[asset], asset, message_counter, bToSharedAddress); }); @@ -640,17 +639,17 @@ function findAddress(address, signing_path, callbacks, fallback_remote_device_ad FROM my_addresses JOIN wallets USING(wallet) JOIN wallet_signing_paths USING(wallet) \n\ WHERE address=? AND signing_path=?", [address, signing_path], - function(rows){ + rows => { if (rows.length > 1) throw Error("more than 1 address found"); if (rows.length === 1){ - var row = rows[0]; + const row = rows[0]; if (!row.full_approval_date) - return callbacks.ifError("wallet of address "+address+" not approved"); + return callbacks.ifError(`wallet of address ${address} not approved`); if (row.device_address !== device.getMyDeviceAddress()) return callbacks.ifRemote(row.device_address); - var objAddress = { - address: address, + const objAddress = { + address, wallet: row.wallet, account: row.account, is_change: row.is_change, @@ -665,17 +664,17 @@ function findAddress(address, signing_path, callbacks, fallback_remote_device_ad "SELECT address, device_address, signing_path FROM shared_address_signing_paths \n\ WHERE shared_address=? AND signing_path=SUBSTR(?, 1, LENGTH(signing_path))", [address, signing_path], - function(sa_rows){ + sa_rows => { if (rows.length > 1) - throw Error("more than 1 member address found for shared address "+address+" and signing path "+signing_path); + throw Error(`more than 1 member address found for shared address ${address} and signing path ${signing_path}`); if (sa_rows.length === 0){ if (fallback_remote_device_address) return callbacks.ifRemote(fallback_remote_device_address); return callbacks.ifUnknownAddress(); } - var objSharedAddress = sa_rows[0]; - var relative_signing_path = 'r' + signing_path.substr(objSharedAddress.signing_path.length); - var bLocal = (objSharedAddress.device_address === device.getMyDeviceAddress()); // local keys + const objSharedAddress = sa_rows[0]; + const relative_signing_path = `r${signing_path.substr(objSharedAddress.signing_path.length)}`; + const bLocal = (objSharedAddress.device_address === device.getMyDeviceAddress()); // local keys if (objSharedAddress.address === '') return callbacks.ifMerkle(bLocal); findAddress(objSharedAddress.address, relative_signing_path, callbacks, bLocal ? null : objSharedAddress.device_address); @@ -690,10 +689,10 @@ function readSharedBalance(wallet, handleBalance){ } function readBalance(wallet, handleBalance){ - balances.readBalance(wallet, function(assocBalances) { + balances.readBalance(wallet, assocBalances => { handleBalance(assocBalances); if (conf.bLight){ // make sure we have all asset definitions available - var arrAssets = Object.keys(assocBalances).filter(function(asset){ return (asset !== 'base'); }); + const arrAssets = Object.keys(assocBalances).filter(asset => asset !== 'base'); if (arrAssets.length === 0) return; network.requestProofsOfJointsIfNewOrUnstable(arrAssets); @@ -706,42 +705,42 @@ function readBalancesOnAddresses(walletId, handleBalancesOnAddresses) { FROM outputs, my_addresses \n\ WHERE outputs.address = my_addresses.address AND my_addresses.wallet = ? AND outputs.is_spent=0 \n\ GROUP BY outputs.address, outputs.asset \n\ - ORDER BY my_addresses.address_index ASC", [walletId], function(rows) { + ORDER BY my_addresses.address_index ASC", [walletId], rows => { handleBalancesOnAddresses(rows); }); } function readAssetMetadata(arrAssets, handleMetadata){ - var sql = "SELECT asset, metadata_unit, name, suffix, decimals FROM asset_metadata"; + let sql = "SELECT asset, metadata_unit, name, suffix, decimals FROM asset_metadata"; if (arrAssets && arrAssets.length) - sql += " WHERE asset IN ("+arrAssets.map(db.escape).join(', ')+")"; - db.query(sql, function(rows){ - var assocAssetMetadata = {}; - for (var i=0; i { + const assocAssetMetadata = {}; + for (let i=0; i { if (assocAssetMetadata[asset] || asset === 'base' && asset === constants.BLACKBYTES_ASSET) return; if ((assocLastFailedAssetMetadataTimestamps[asset] || 0) > Date.now() - ASSET_METADATA_RETRY_PERIOD) return; - fetchAssetMetadata(asset, function(err, objMetadata){ + fetchAssetMetadata(asset, (err, {metadata_unit, decimals, suffix, name}) => { if (err) return console.log(err); assocAssetMetadata[asset] = { - metadata_unit: objMetadata.metadata_unit, - decimals: objMetadata.decimals, - name: objMetadata.suffix ? objMetadata.name+'.'+objMetadata.suffix : objMetadata.name + metadata_unit: metadata_unit, + decimals: decimals, + name: suffix ? `${name}.${suffix}` : name }; eventBus.emit('maybe_new_transactions'); }); @@ -750,54 +749,54 @@ function readAssetMetadata(arrAssets, handleMetadata){ } function fetchAssetMetadata(asset, handleMetadata){ - device.requestFromHub('hub/get_asset_metadata', asset, function(err, response){ + device.requestFromHub('hub/get_asset_metadata', asset, (err, response) => { if (err){ if (err === 'no metadata') assocLastFailedAssetMetadataTimestamps[asset] = Date.now(); - return handleMetadata("error from get_asset_metadata "+asset+": "+err); + return handleMetadata(`error from get_asset_metadata ${asset}: ${err}`); } - var metadata_unit = response.metadata_unit; - var registry_address = response.registry_address; - var suffix = response.suffix; + const metadata_unit = response.metadata_unit; + const registry_address = response.registry_address; + const suffix = response.suffix; if (!ValidationUtils.isStringOfLength(metadata_unit, constants.HASH_LENGTH)) - return handleMetadata("bad metadata_unit: "+metadata_unit); + return handleMetadata(`bad metadata_unit: ${metadata_unit}`); if (!ValidationUtils.isValidAddress(registry_address)) - return handleMetadata("bad registry_address: "+registry_address); - var fetchMetadataUnit = conf.bLight - ? function(onDone){ + return handleMetadata(`bad registry_address: ${registry_address}`); + const fetchMetadataUnit = conf.bLight + ? onDone => { network.requestProofsOfJointsIfNewOrUnstable([metadata_unit], onDone); } - : function(onDone){ + : onDone => { onDone(); }; - fetchMetadataUnit(function(err){ + fetchMetadataUnit(err => { if (err) - return handleMetadata("fetchMetadataUnit failed: "+err); + return handleMetadata(`fetchMetadataUnit failed: ${err}`); storage.readJoint(db, metadata_unit, { - ifNotFound: function(){ - handleMetadata("metadata unit "+metadata_unit+" not found"); + ifNotFound() { + handleMetadata(`metadata unit ${metadata_unit} not found`); }, - ifFound: function(objJoint){ - objJoint.unit.messages.forEach(function(message){ + ifFound({unit}) { + unit.messages.forEach(message => { if (message.app !== 'data') return; - var payload = message.payload; + const payload = message.payload; if (payload.asset !== asset) return; if (!payload.name) - return handleMetadata("no name in asset metadata "+metadata_unit); - var decimals = (payload.decimals !== undefined) ? parseInt(payload.decimals) : undefined; + return handleMetadata(`no name in asset metadata ${metadata_unit}`); + const decimals = (payload.decimals !== undefined) ? parseInt(payload.decimals) : undefined; if (decimals !== undefined && !ValidationUtils.isNonnegativeInteger(decimals)) - return handleMetadata("bad decimals in asset metadata "+metadata_unit); + return handleMetadata(`bad decimals in asset metadata ${metadata_unit}`); db.query( - "INSERT "+db.getIgnore()+" INTO asset_metadata (asset, metadata_unit, registry_address, suffix, name, decimals) \n\ - VALUES (?,?,?, ?,?,?)", + `INSERT ${db.getIgnore()} INTO asset_metadata (asset, metadata_unit, registry_address, suffix, name, decimals) \n\ + VALUES (?,?,?, ?,?,?)`, [asset, metadata_unit, registry_address, suffix, payload.name, decimals], - function(){ - var objMetadata = { - metadata_unit: metadata_unit, - suffix: suffix, - decimals: decimals, + () => { + const objMetadata = { + metadata_unit, + suffix, + decimals, name: payload.name }; handleMetadata(null, objMetadata); @@ -811,40 +810,40 @@ function fetchAssetMetadata(asset, handleMetadata){ } function readTransactionHistory(opts, handleHistory){ - var asset = opts.asset && (opts.asset !== "base") ? opts.asset : null; + const asset = opts.asset && (opts.asset !== "base") ? opts.asset : null; if (opts.wallet && opts.address || !opts.wallet && !opts.address) throw Error('invalid wallet and address params'); - var wallet = opts.wallet || opts.address; - var walletIsAddress = ValidationUtils.isValidAddress(wallet); - var join_my_addresses = walletIsAddress ? "" : "JOIN my_addresses USING(address)"; - var where_condition = walletIsAddress ? "address=?" : "wallet=?"; - var asset_condition = asset ? "asset="+db.escape(asset) : "asset IS NULL"; - var cross = ""; + const wallet = opts.wallet || opts.address; + const walletIsAddress = ValidationUtils.isValidAddress(wallet); + const join_my_addresses = walletIsAddress ? "" : "JOIN my_addresses USING(address)"; + let where_condition = walletIsAddress ? "address=?" : "wallet=?"; + const asset_condition = asset ? `asset=${db.escape(asset)}` : "asset IS NULL"; + let cross = ""; if (opts.unit) - where_condition += " AND unit="+db.escape(opts.unit); + where_condition += ` AND unit=${db.escape(opts.unit)}`; else if (opts.since_mci && ValidationUtils.isNonnegativeInteger(opts.since_mci)){ - where_condition += " AND main_chain_index>="+opts.since_mci; + where_condition += ` AND main_chain_index>=${opts.since_mci}`; cross = "CROSS"; } db.query( - "SELECT unit, level, is_stable, sequence, address, \n\ - "+db.getUnixTimestamp("units.creation_date")+" AS ts, headers_commission+payload_commission AS fee, \n\ - SUM(amount) AS amount, address AS to_address, NULL AS from_address, main_chain_index AS mci \n\ - FROM units "+cross+" JOIN outputs USING(unit) "+join_my_addresses+" \n\ - WHERE "+where_condition+" AND "+asset_condition+" \n\ - GROUP BY unit, address \n\ - UNION \n\ - SELECT unit, level, is_stable, sequence, address, \n\ - "+db.getUnixTimestamp("units.creation_date")+" AS ts, headers_commission+payload_commission AS fee, \n\ - NULL AS amount, NULL AS to_address, address AS from_address, main_chain_index AS mci \n\ - FROM units "+cross+" JOIN inputs USING(unit) "+join_my_addresses+" \n\ - WHERE "+where_condition+" AND "+asset_condition+" \n\ - ORDER BY ts DESC"+(opts.limit ? " LIMIT ?" : ""), + `SELECT unit, level, is_stable, sequence, address, \n\ + ${db.getUnixTimestamp("units.creation_date")} AS ts, headers_commission+payload_commission AS fee, \n\ + SUM(amount) AS amount, address AS to_address, NULL AS from_address, main_chain_index AS mci \n\ + FROM units ${cross} JOIN outputs USING(unit) ${join_my_addresses} \n\ + WHERE ${where_condition} AND ${asset_condition} \n\ + GROUP BY unit, address \n\ + UNION \n\ + SELECT unit, level, is_stable, sequence, address, \n\ + ${db.getUnixTimestamp("units.creation_date")} AS ts, headers_commission+payload_commission AS fee, \n\ + NULL AS amount, NULL AS to_address, address AS from_address, main_chain_index AS mci \n\ + FROM units ${cross} JOIN inputs USING(unit) ${join_my_addresses} \n\ + WHERE ${where_condition} AND ${asset_condition} \n\ + ORDER BY ts DESC${opts.limit ? " LIMIT ?" : ""}`, opts.limit ? [wallet, wallet, opts.limit] : [wallet, wallet], - function(rows){ - var assocMovements = {}; - for (var i=0; i { + const assocMovements = {}; + for (let i=0; i { if (movement.sequence !== 'good'){ - var transaction = { + const transaction = { action: 'invalid', confirmations: movement.is_stable, - unit: unit, + unit, fee: movement.fee, time: movement.ts, level: movement.level, @@ -882,18 +881,18 @@ function readTransactionHistory(opts, handleHistory){ else if (movement.plus && !movement.has_minus){ // light clients will sometimes have input address = NULL db.query( - "SELECT DISTINCT address FROM inputs WHERE unit=? AND "+asset_condition+" ORDER BY address", + `SELECT DISTINCT address FROM inputs WHERE unit=? AND ${asset_condition} ORDER BY address`, [unit], - function(address_rows){ - var arrPayerAddresses = address_rows.map(function(address_row){ return address_row.address; }); - movement.arrMyRecipients.forEach(function(objRecipient){ - var transaction = { + address_rows => { + const arrPayerAddresses = address_rows.map(({address}) => address); + movement.arrMyRecipients.forEach(({amount, my_address}) => { + const transaction = { action: 'received', - amount: objRecipient.amount, - my_address: objRecipient.my_address, - arrPayerAddresses: arrPayerAddresses, + amount: amount, + my_address: my_address, + arrPayerAddresses, confirmations: movement.is_stable, - unit: unit, + unit, fee: movement.fee, time: movement.ts, level: movement.level, @@ -906,43 +905,41 @@ function readTransactionHistory(opts, handleHistory){ ); } else if (movement.has_minus){ - var queryString, parameters; - queryString = "SELECT outputs.address, SUM(outputs.amount) AS amount, outputs.asset, (" - + ( walletIsAddress ? "outputs.address!=?" : "my_addresses.address IS NULL") + ") AS is_external, \n\ - sent_mnemonics.textAddress, sent_mnemonics.mnemonic, \n\ - (SELECT unit_authors.unit FROM unit_authors WHERE unit_authors.address = sent_mnemonics.address LIMIT 1) AS claiming_unit \n\ - FROM outputs " - + (walletIsAddress ? "" : "LEFT JOIN my_addresses ON outputs.address=my_addresses.address AND wallet=? ") + - "LEFT JOIN sent_mnemonics USING(unit) \n\ - WHERE outputs.unit=? \n\ - GROUP BY outputs.address, asset"; - parameters = [wallet, unit]; - db.query(queryString, parameters, - function(payee_rows){ - var action = payee_rows.some(function(payee){ return payee.is_external; }) ? 'sent' : 'moved'; + let queryString; + let parameters; + queryString = `SELECT outputs.address, SUM(outputs.amount) AS amount, outputs.asset, (${walletIsAddress ? "outputs.address!=?" : "my_addresses.address IS NULL"}) AS is_external, \n\ + sent_mnemonics.textAddress, sent_mnemonics.mnemonic, \n\ + (SELECT unit_authors.unit FROM unit_authors WHERE unit_authors.address = sent_mnemonics.address LIMIT 1) AS claiming_unit \n\ + FROM outputs ${walletIsAddress ? "" : "LEFT JOIN my_addresses ON outputs.address=my_addresses.address AND wallet=? "}LEFT JOIN sent_mnemonics USING(unit) \n\ + WHERE outputs.unit=? \n\ + GROUP BY outputs.address, asset`; + parameters = [wallet, unit]; + db.query(queryString, parameters, + payee_rows => { + const action = payee_rows.some(({is_external}) => is_external) ? 'sent' : 'moved'; if (payee_rows.length == 0) { cb(); return; } - var has_asset = payee_rows.some(function(payee){ return payee.asset; }); + const has_asset = payee_rows.some(payee => payee.asset); if (has_asset && !asset) { // filter out "fees" txs from history cb(); return; } - async.eachSeries(payee_rows, function(payee, cb2){ + async.eachSeries(payee_rows, (payee, cb2) => { if ((action === 'sent' && !payee.is_external) || (asset != payee.asset)) { return cb2(); } - var transaction = { - action: action, + const transaction = { + action, amount: payee.amount, addressTo: payee.address, textAddress: ValidationUtils.isValidEmail(payee.textAddress) ? payee.textAddress : "", claimed: !!payee.claiming_unit, mnemonic: payee.mnemonic, confirmations: movement.is_stable, - unit: unit, + unit, fee: movement.fee, time: movement.ts, level: movement.level, @@ -959,8 +956,8 @@ function readTransactionHistory(opts, handleHistory){ UNION SELECT shared_address AS address FROM shared_addresses \n\ ) USING (address) \n\ WHERE outputs.unit=?", [payee.claiming_unit], - function(rows) { - transaction.claimedByMe = (rows.length > 0); + ({length}) => { + transaction.claimedByMe = (length > 0); arrTransactions.push(transaction); cb2(); } @@ -969,32 +966,32 @@ function readTransactionHistory(opts, handleHistory){ arrTransactions.push(transaction); cb2(); } - }, function() { + }, () => { cb(); }); } ); - } + } }, - function(){ - arrTransactions.sort(function(a, b){ - if (a.mci && b.mci){ - if (a.mci < b.mci) + () => { + arrTransactions.sort(({mci, level, time}, {mci, level, time}) => { + if (mci && mci){ + if (mci < mci) return 1; - if (a.mci > b.mci) + if (mci > mci) return -1; } - if (a.level < b.level) + if (level < level) return 1; - if (a.level > b.level) + if (level > level) return -1; - if (a.time < b.time) + if (time < time) return 1; - if (a.time > b.time) + if (time > time) return -1; return 0; }); - arrTransactions.forEach(function(transaction){ transaction.asset = opts.asset; }); + arrTransactions.forEach(transaction => { transaction.asset = opts.asset; }); handleHistory(arrTransactions); } ); @@ -1005,19 +1002,19 @@ function readTransactionHistory(opts, handleHistory){ // returns assoc array signing_path => (key|merkle) function readFullSigningPaths(conn, address, arrSigningDeviceAddresses, handleSigningPaths){ - var assocSigningPaths = {}; + const assocSigningPaths = {}; function goDeeper(member_address, path_prefix, onDone){ // first, look for wallet addresses - var sql = "SELECT signing_path FROM my_addresses JOIN wallet_signing_paths USING(wallet) WHERE address=?"; - var arrParams = [member_address]; + let sql = "SELECT signing_path FROM my_addresses JOIN wallet_signing_paths USING(wallet) WHERE address=?"; + let arrParams = [member_address]; if (arrSigningDeviceAddresses && arrSigningDeviceAddresses.length > 0){ sql += " AND device_address IN(?)"; arrParams.push(arrSigningDeviceAddresses); } - conn.query(sql, arrParams, function(rows){ - rows.forEach(function(row){ - assocSigningPaths[path_prefix + row.signing_path.substr(1)] = 'key'; + conn.query(sql, arrParams, rows => { + rows.forEach(({signing_path}) => { + assocSigningPaths[path_prefix + signing_path.substr(1)] = 'key'; }); if (rows.length > 0) return onDone(); @@ -1028,11 +1025,11 @@ function readFullSigningPaths(conn, address, arrSigningDeviceAddresses, handleSi sql += " AND device_address IN(?)"; arrParams.push(arrSigningDeviceAddresses); } - conn.query(sql, arrParams, function(rows){ + conn.query(sql, arrParams, rows => { if(rows.length > 0) { async.eachSeries( rows, - function (row, cb) { + (row, cb) => { if (row.address === '') { // merkle assocSigningPaths[path_prefix + row.signing_path.substr(1)] = 'merkle'; return cb(); @@ -1050,7 +1047,7 @@ function readFullSigningPaths(conn, address, arrSigningDeviceAddresses, handleSi }); } - goDeeper(address, 'r', function(){ + goDeeper(address, 'r', () => { handleSigningPaths(assocSigningPaths); // order of signing paths is not significant }); } @@ -1058,34 +1055,34 @@ function readFullSigningPaths(conn, address, arrSigningDeviceAddresses, handleSi function determineIfFixedDenominations(asset, handleResult){ if (!asset) return handleResult(false); - storage.readAsset(db, asset, null, function(err, objAsset){ + storage.readAsset(db, asset, null, (err, {fixed_denominations}) => { if (err) throw Error(err); - handleResult(objAsset.fixed_denominations); + handleResult(fixed_denominations); }); } function readFundedAddresses(asset, wallet, estimated_amount, handleFundedAddresses){ - var walletIsAddresses = ValidationUtils.isNonemptyArray(wallet); + const walletIsAddresses = ValidationUtils.isNonemptyArray(wallet); if (walletIsAddresses) return composer.readSortedFundedAddresses(asset, wallet, estimated_amount, handleFundedAddresses); if (estimated_amount && typeof estimated_amount !== 'number') - throw Error('invalid estimated amount: '+estimated_amount); + throw Error(`invalid estimated amount: ${estimated_amount}`); // addresses closest to estimated amount come first - var order_by = estimated_amount ? "(SUM(amount)>"+estimated_amount+") DESC, ABS(SUM(amount)-"+estimated_amount+") ASC" : "SUM(amount) DESC"; + const order_by = estimated_amount ? `(SUM(amount)>${estimated_amount}) DESC, ABS(SUM(amount)-${estimated_amount}) ASC` : "SUM(amount) DESC"; db.query( - "SELECT address, SUM(amount) AS total \n\ - FROM outputs JOIN my_addresses USING(address) \n\ - CROSS JOIN units USING(unit) \n\ - WHERE wallet=? AND is_stable=1 AND sequence='good' AND is_spent=0 AND "+(asset ? "asset=?" : "asset IS NULL")+" \n\ - AND NOT EXISTS ( \n\ - SELECT * FROM unit_authors JOIN units USING(unit) \n\ - WHERE is_stable=0 AND unit_authors.address=outputs.address AND definition_chash IS NOT NULL \n\ - ) \n\ - GROUP BY address ORDER BY "+order_by, + `SELECT address, SUM(amount) AS total \n\ + FROM outputs JOIN my_addresses USING(address) \n\ + CROSS JOIN units USING(unit) \n\ + WHERE wallet=? AND is_stable=1 AND sequence='good' AND is_spent=0 AND ${asset ? "asset=?" : "asset IS NULL"} \n\ + AND NOT EXISTS ( \n\ + SELECT * FROM unit_authors JOIN units USING(unit) \n\ + WHERE is_stable=0 AND unit_authors.address=outputs.address AND definition_chash IS NOT NULL \n\ + ) \n\ + GROUP BY address ORDER BY ${order_by}`, asset ? [wallet, asset] : [wallet], - function(rows){ - determineIfFixedDenominations(asset, function(bFixedDenominations){ + rows => { + determineIfFixedDenominations(asset, bFixedDenominations => { if (bFixedDenominations) estimated_amount = 0; // don't shorten the list of addresses, indivisible_asset.js will do it later according to denominations handleFundedAddresses(composer.filterMostFundedAddresses(rows, estimated_amount)); @@ -1102,8 +1099,8 @@ function readFundedAddresses(asset, wallet, estimated_amount, handleFundedAddres } function readAdditionalSigningAddresses(arrPayingAddresses, arrSigningAddresses, arrSigningDeviceAddresses, handleAdditionalSigningAddresses){ - var arrFromAddresses = arrPayingAddresses.concat(arrSigningAddresses); - var sql = "SELECT DISTINCT address FROM shared_address_signing_paths \n\ + const arrFromAddresses = arrPayingAddresses.concat(arrSigningAddresses); + let sql = "SELECT DISTINCT address FROM shared_address_signing_paths \n\ WHERE shared_address IN(?) \n\ AND ( \n\ EXISTS (SELECT 1 FROM my_addresses WHERE my_addresses.address=shared_address_signing_paths.address) \n\ @@ -1119,7 +1116,7 @@ function readAdditionalSigningAddresses(arrPayingAddresses, arrSigningAddresses, ORDER BY level DESC LIMIT 1 \n\ ) IS NULL \n\ )"; - var arrParams = [arrFromAddresses]; + const arrParams = [arrFromAddresses]; if (arrSigningAddresses.length > 0){ sql += " AND address NOT IN(?)"; arrParams.push(arrSigningAddresses); @@ -1131,42 +1128,42 @@ function readAdditionalSigningAddresses(arrPayingAddresses, arrSigningAddresses, db.query( sql, arrParams, - function(rows){ - var arrAdditionalAddresses = rows.map(function(row){ return row.address; }); + rows => { + const arrAdditionalAddresses = rows.map(({address}) => address); if (arrAdditionalAddresses.length === 0) return handleAdditionalSigningAddresses([]); - readAdditionalSigningAddresses([], arrSigningAddresses.concat(arrAdditionalAddresses), arrSigningDeviceAddresses, function(arrMoreAddresses){ + readAdditionalSigningAddresses([], arrSigningAddresses.concat(arrAdditionalAddresses), arrSigningDeviceAddresses, arrMoreAddresses => { handleAdditionalSigningAddresses(arrAdditionalAddresses.concat(arrMoreAddresses)); }); } ); } -var TYPICAL_FEE = 1000; +const TYPICAL_FEE = 1000; // fee_paying_wallet is used only if there are no bytes on the asset wallet, it is a sort of fallback wallet for fees function readFundedAndSigningAddresses( asset, wallet, estimated_amount, fee_paying_wallet, arrSigningAddresses, arrSigningDeviceAddresses, handleFundedAndSigningAddresses) { - readFundedAddresses(asset, wallet, estimated_amount, function(arrFundedAddresses){ + readFundedAddresses(asset, wallet, estimated_amount, arrFundedAddresses => { if (arrFundedAddresses.length === 0) return handleFundedAndSigningAddresses([], [], []); - var arrBaseFundedAddresses = []; - var addSigningAddressesAndReturn = function(){ - var arrPayingAddresses = _.union(arrFundedAddresses, arrBaseFundedAddresses); - readAdditionalSigningAddresses(arrPayingAddresses, arrSigningAddresses, arrSigningDeviceAddresses, function(arrAdditionalAddresses){ + let arrBaseFundedAddresses = []; + const addSigningAddressesAndReturn = () => { + const arrPayingAddresses = _.union(arrFundedAddresses, arrBaseFundedAddresses); + readAdditionalSigningAddresses(arrPayingAddresses, arrSigningAddresses, arrSigningDeviceAddresses, arrAdditionalAddresses => { handleFundedAndSigningAddresses(arrFundedAddresses, arrBaseFundedAddresses, arrSigningAddresses.concat(arrAdditionalAddresses)); }); }; if (!asset) return addSigningAddressesAndReturn(); - readFundedAddresses(null, wallet, TYPICAL_FEE, function(_arrBaseFundedAddresses){ + readFundedAddresses(null, wallet, TYPICAL_FEE, _arrBaseFundedAddresses => { // fees will be paid from the same addresses as the asset if (_arrBaseFundedAddresses.length > 0 || !fee_paying_wallet || fee_paying_wallet === wallet){ arrBaseFundedAddresses = _arrBaseFundedAddresses; return addSigningAddressesAndReturn(); } - readFundedAddresses(null, fee_paying_wallet, TYPICAL_FEE, function(_arrBaseFundedAddresses){ + readFundedAddresses(null, fee_paying_wallet, TYPICAL_FEE, _arrBaseFundedAddresses => { arrBaseFundedAddresses = _arrBaseFundedAddresses; addSigningAddressesAndReturn(); }); @@ -1178,38 +1175,38 @@ function sendPaymentFromWallet( asset, wallet, to_address, amount, change_address, arrSigningDeviceAddresses, recipient_device_address, signWithLocalPrivateKey, handleResult) { sendMultiPayment({ - asset: asset, - wallet: wallet, - to_address: to_address, - amount: amount, - change_address: change_address, - arrSigningDeviceAddresses: arrSigningDeviceAddresses, - recipient_device_address: recipient_device_address, - signWithLocalPrivateKey: signWithLocalPrivateKey + asset, + wallet, + to_address, + amount, + change_address, + arrSigningDeviceAddresses, + recipient_device_address, + signWithLocalPrivateKey }, handleResult); } function sendMultiPayment(opts, handleResult) { - var asset = opts.asset; + let asset = opts.asset; if (asset === 'base') asset = null; - var wallet = opts.wallet; - var arrPayingAddresses = opts.paying_addresses; - var fee_paying_wallet = opts.fee_paying_wallet; - var arrSigningAddresses = opts.signing_addresses || []; - var to_address = opts.to_address; - var amount = opts.amount; - var bSendAll = opts.send_all; - var change_address = opts.change_address; - var arrSigningDeviceAddresses = opts.arrSigningDeviceAddresses; - var recipient_device_address = opts.recipient_device_address; - var signWithLocalPrivateKey = opts.signWithLocalPrivateKey; - var merkle_proof = opts.merkle_proof; + const wallet = opts.wallet; + const arrPayingAddresses = opts.paying_addresses; + const fee_paying_wallet = opts.fee_paying_wallet; + const arrSigningAddresses = opts.signing_addresses || []; + let to_address = opts.to_address; + const amount = opts.amount; + const bSendAll = opts.send_all; + const change_address = opts.change_address; + const arrSigningDeviceAddresses = opts.arrSigningDeviceAddresses; + let recipient_device_address = opts.recipient_device_address; + const signWithLocalPrivateKey = opts.signWithLocalPrivateKey; + const merkle_proof = opts.merkle_proof; - var base_outputs = opts.base_outputs; - var asset_outputs = opts.asset_outputs; - var messages = opts.messages; + const base_outputs = opts.base_outputs; + const asset_outputs = opts.asset_outputs; + const messages = opts.messages; if (!wallet && !arrPayingAddresses) throw Error("neither wallet id nor paying addresses"); @@ -1229,28 +1226,28 @@ function sendMultiPayment(opts, handleResult) if (recipient_device_address === device.getMyDeviceAddress()) recipient_device_address = null; - var estimated_amount = amount; + let estimated_amount = amount; if (!estimated_amount && asset_outputs) - estimated_amount = asset_outputs.reduce(function(acc, output){ return acc+output.amount; }, 0); + estimated_amount = asset_outputs.reduce((acc, output) => acc+output.amount, 0); if (estimated_amount && !asset) estimated_amount += TYPICAL_FEE; readFundedAndSigningAddresses( asset, wallet || arrPayingAddresses, estimated_amount, fee_paying_wallet, arrSigningAddresses, arrSigningDeviceAddresses, - function(arrFundedAddresses, arrBaseFundedAddresses, arrAllSigningAddresses){ + (arrFundedAddresses, arrBaseFundedAddresses, arrAllSigningAddresses) => { if (arrFundedAddresses.length === 0) return handleResult("There are no funded addresses"); if (asset && arrBaseFundedAddresses.length === 0) return handleResult("No bytes to pay fees"); - var bRequestedConfirmation = false; - var signer = { - readSigningPaths: function(conn, address, handleLengthsBySigningPaths){ // returns assoc array signing_path => length - readFullSigningPaths(conn, address, arrSigningDeviceAddresses, function(assocTypesBySigningPaths){ - var assocLengthsBySigningPaths = {}; - for (var signing_path in assocTypesBySigningPaths){ - var type = assocTypesBySigningPaths[signing_path]; + let bRequestedConfirmation = false; + const signer = { + readSigningPaths(conn, address, handleLengthsBySigningPaths) { // returns assoc array signing_path => length + readFullSigningPaths(conn, address, arrSigningDeviceAddresses, assocTypesBySigningPaths => { + const assocLengthsBySigningPaths = {}; + for (const signing_path in assocTypesBySigningPaths){ + const type = assocTypesBySigningPaths[signing_path]; if (type === 'key') assocLengthsBySigningPaths[signing_path] = constants.SIG_LENGTH; else if (type === 'merkle'){ @@ -1258,39 +1255,45 @@ function sendMultiPayment(opts, handleResult) assocLengthsBySigningPaths[signing_path] = merkle_proof.length; } else - throw Error("unknown type "+type+" at "+signing_path); + throw Error(`unknown type ${type} at ${signing_path}`); } handleLengthsBySigningPaths(assocLengthsBySigningPaths); }); }, - readDefinition: function(conn, address, handleDefinition){ + readDefinition(conn, address, handleDefinition) { conn.query( "SELECT definition FROM my_addresses WHERE address=? UNION SELECT definition FROM shared_addresses WHERE shared_address=?", [address, address], - function(rows){ + rows => { if (rows.length !== 1) throw Error("definition not found"); handleDefinition(null, JSON.parse(rows[0].definition)); } ); }, - sign: function(objUnsignedUnit, assocPrivatePayloads, address, signing_path, handleSignature){ - var buf_to_sign = objectHash.getUnitHashToSign(objUnsignedUnit); + sign( + objUnsignedUnit, + assocPrivatePayloads, + address, + signing_path, + handleSignature + ) { + const buf_to_sign = objectHash.getUnitHashToSign(objUnsignedUnit); findAddress(address, signing_path, { - ifError: function(err){ + ifError(err) { throw Error(err); }, - ifUnknownAddress: function(err){ - throw Error("unknown address "+address+" at "+signing_path); + ifUnknownAddress(err) { + throw Error(`unknown address ${address} at ${signing_path}`); }, - ifLocal: function(objAddress){ - signWithLocalPrivateKey(objAddress.wallet, objAddress.account, objAddress.is_change, objAddress.address_index, buf_to_sign, function(sig){ + ifLocal(objAddress) { + signWithLocalPrivateKey(objAddress.wallet, objAddress.account, objAddress.is_change, objAddress.address_index, buf_to_sign, sig => { handleSignature(null, sig); }); }, - ifRemote: function(device_address){ + ifRemote(device_address) { // we'll receive this event after the peer signs - eventBus.once("signature-"+device_address+"-"+address+"-"+signing_path+"-"+buf_to_sign.toString("base64"), function(sig){ + eventBus.once(`signature-${device_address}-${address}-${signing_path}-${buf_to_sign.toString("base64")}`, sig => { handleSignature(null, sig); if (sig === '[refused]') eventBus.emit('refused_to_sign', device_address); @@ -1301,11 +1304,11 @@ function sendMultiPayment(opts, handleResult) bRequestedConfirmation = true; } }, - ifMerkle: function(bLocal){ + ifMerkle(bLocal) { if (!bLocal) - throw Error("merkle proof at path "+signing_path+" should be provided by another device"); + throw Error(`merkle proof at path ${signing_path} should be provided by another device`); if (!merkle_proof) - throw Error("merkle proof at path "+signing_path+" not provided"); + throw Error(`merkle proof at path ${signing_path} not provided`); handleSignature(null, merkle_proof); } }); @@ -1313,18 +1316,18 @@ function sendMultiPayment(opts, handleResult) }; // if we have any output with text addresses / not byteball addresses (e.g. email) - generate new addresses and return them - var assocMnemonics = {}; // return all generated wallet mnemonics to caller in callback - var assocPaymentsByEmail = {}; // wallet mnemonics to send by emails - var assocAddresses = {}; - var prefix = "textcoin:"; + const assocMnemonics = {}; // return all generated wallet mnemonics to caller in callback + const assocPaymentsByEmail = {}; // wallet mnemonics to send by emails + const assocAddresses = {}; + const prefix = "textcoin:"; function generateNewMnemonicIfNoAddress(output_asset, outputs) { - var generated = 0; - outputs.forEach(function(output){ + let generated = 0; + outputs.forEach(output => { if (output.address.indexOf(prefix) !== 0) return false; - var address = output.address.slice(prefix.length); - var strMnemonic = assocMnemonics[output.address] || ""; - var mnemonic = new Mnemonic(strMnemonic.replace(/-/g, " ")); + const address = output.address.slice(prefix.length); + let strMnemonic = assocMnemonics[output.address] || ""; + let mnemonic = new Mnemonic(strMnemonic.replace(/-/g, " ")); if (!strMnemonic) { while (!Mnemonic.isValid(mnemonic.toString())) mnemonic = new Mnemonic(); @@ -1334,7 +1337,7 @@ function sendMultiPayment(opts, handleResult) assocPaymentsByEmail[address] = {mnemonic: strMnemonic, amount: output.amount, asset: output_asset}; } assocMnemonics[output.address] = strMnemonic; - var pubkey = mnemonic.toHDPrivateKey().derive("m/44'/0'/0'/0/0").publicKey.toBuffer().toString("base64"); + const pubkey = mnemonic.toHDPrivateKey().derive("m/44'/0'/0'/0/0").publicKey.toBuffer().toString("base64"); assocAddresses[output.address] = objectHash.getChash160(["sig", {"pubkey": pubkey}]); output.address = assocAddresses[output.address]; generated++; @@ -1342,31 +1345,31 @@ function sendMultiPayment(opts, handleResult) return generated; } if (to_address) { - var to_address_output = {address: to_address, amount: amount}; - var cnt = generateNewMnemonicIfNoAddress(asset, [to_address_output]); + const to_address_output = {address: to_address, amount}; + const cnt = generateNewMnemonicIfNoAddress(asset, [to_address_output]); if (cnt) to_address = to_address_output.address; } if (base_outputs) generateNewMnemonicIfNoAddress(null, base_outputs); if (asset_outputs) generateNewMnemonicIfNoAddress(asset, asset_outputs); - var params = { + const params = { available_paying_addresses: arrFundedAddresses, // forces 'minimal' for payments from shared addresses too, it doesn't hurt signing_addresses: arrAllSigningAddresses, - messages: messages, - signer: signer, + messages, + signer, callbacks: { - ifNotEnoughFunds: function(err){ + ifNotEnoughFunds(err) { handleResult(err); }, - ifError: function(err){ + ifError(err) { handleResult(err); }, - preCommitCb: function(conn, objJoint, cb){ - var i = 0; + preCommitCb(conn, {unit}, cb) { + let i = 0; if (Object.keys(assocMnemonics).length) { - for (var to in assocMnemonics) { - conn.query("INSERT INTO sent_mnemonics (unit, address, mnemonic, textAddress) VALUES (?, ?, ?, ?)", [objJoint.unit.unit, assocAddresses[to], assocMnemonics[to], to.slice(prefix.length)], - function(){ + for (const to in assocMnemonics) { + conn.query("INSERT INTO sent_mnemonics (unit, address, mnemonic, textAddress) VALUES (?, ?, ?, ?)", [unit.unit, assocAddresses[to], assocMnemonics[to], to.slice(prefix.length)], + () => { if (++i == Object.keys(assocMnemonics).length) { // stored all mnemonics cb(); } @@ -1377,15 +1380,19 @@ function sendMultiPayment(opts, handleResult) }, // for asset payments, 2nd argument is array of chains of private elements // for base asset, 2nd argument is assocPrivatePayloads which is null - ifOk: function(objJoint, arrChainsOfRecipientPrivateElements, arrChainsOfCosignerPrivateElements){ + ifOk( + objJoint, + arrChainsOfRecipientPrivateElements, + arrChainsOfCosignerPrivateElements + ) { network.broadcastJoint(objJoint); if (!arrChainsOfRecipientPrivateElements && recipient_device_address) // send notification about public payment walletGeneral.sendPaymentNotification(recipient_device_address, objJoint.unit.unit); if (Object.keys(assocPaymentsByEmail).length) { // need to send emails - var sent = 0; - for (var email in assocPaymentsByEmail) { - var objPayment = assocPaymentsByEmail[email]; + let sent = 0; + for (const email in assocPaymentsByEmail) { + const objPayment = assocPaymentsByEmail[email]; sendTextcoinEmail(email, opts.email_subject, objPayment.amount, objPayment.asset, objPayment.mnemonic); if (++sent == Object.keys(assocPaymentsByEmail).length) handleResult(null, objJoint.unit.unit, assocMnemonics); @@ -1411,23 +1418,28 @@ function sendMultiPayment(opts, handleResult) params.base_outputs = base_outputs; // only destinations, without the change } params.change_address = change_address; - storage.readAsset(db, asset, null, function(err, objAsset){ + storage.readAsset(db, asset, null, (err, {is_private, fixed_denominations}) => { if (err) throw Error(err); // if (objAsset.is_private && !recipient_device_address) // return handleResult("for private asset, need recipient's device address to send private payload to"); - if (objAsset.is_private){ + if (is_private){ // save messages in outbox before committing - params.callbacks.preCommitCb = function(conn, arrChainsOfRecipientPrivateElements, arrChainsOfCosignerPrivateElements, cb){ + params.callbacks.preCommitCb = ( + conn, + arrChainsOfRecipientPrivateElements, + arrChainsOfCosignerPrivateElements, + cb + ) => { if (!arrChainsOfRecipientPrivateElements || !arrChainsOfCosignerPrivateElements) throw Error('no private elements'); - var sendToRecipients = function(cb2){ + const sendToRecipients = cb2 => { if (recipient_device_address) walletGeneral.sendPrivatePayments(recipient_device_address, arrChainsOfRecipientPrivateElements, false, conn, cb2); else // paying to another wallet on the same device forwardPrivateChainsToOtherMembersOfOutputAddresses(arrChainsOfRecipientPrivateElements, conn, cb2); }; - var sendToCosigners = function(cb2){ + const sendToCosigners = cb2 => { if (wallet) walletDefinedByKeys.forwardPrivateChainsToOtherMembersOfWallets(arrChainsOfCosignerPrivateElements, [wallet], conn, cb2); else // arrPayingAddresses can be only shared addresses @@ -1436,7 +1448,7 @@ function sendMultiPayment(opts, handleResult) async.series([sendToRecipients, sendToCosigners], cb); }; } - if (objAsset.fixed_denominations){ // indivisible + if (fixed_denominations){ // indivisible params.tolerance_plus = 0; params.tolerance_minus = 0; indivisibleAsset.composeAndSaveMinimalIndivisibleAssetPaymentJoint(params); @@ -1452,7 +1464,7 @@ function sendMultiPayment(opts, handleResult) params.outputs = [{address: to_address, amount: 0}]; } else{ - params.outputs = to_address ? [{address: to_address, amount: amount}] : (base_outputs || []); + params.outputs = to_address ? [{address: to_address, amount}] : (base_outputs || []); params.outputs.push({address: change_address, amount: 0}); } composer.composeAndSaveMinimalJoint(params); @@ -1463,14 +1475,12 @@ function sendMultiPayment(opts, handleResult) } function forwardPrivateChainsToOtherMembersOfSharedAddresses(arrChainsOfCosignerPrivateElements, arrPayingAddresses, excluded_device_address, bForwarded, conn, onDone){ - walletDefinedByAddresses.readAllControlAddresses(conn, arrPayingAddresses, function(arrControlAddresses, arrControlDeviceAddresses){ - arrControlDeviceAddresses = arrControlDeviceAddresses.filter(function(device_address) { - return (device_address !== device.getMyDeviceAddress() && device_address !== excluded_device_address); - }); - walletDefinedByKeys.readDeviceAddressesControllingPaymentAddresses(conn, arrControlAddresses, function(arrMultisigDeviceAddresses){ + walletDefinedByAddresses.readAllControlAddresses(conn, arrPayingAddresses, (arrControlAddresses, arrControlDeviceAddresses) => { + arrControlDeviceAddresses = arrControlDeviceAddresses.filter(device_address => device_address !== device.getMyDeviceAddress() && device_address !== excluded_device_address); + walletDefinedByKeys.readDeviceAddressesControllingPaymentAddresses(conn, arrControlAddresses, arrMultisigDeviceAddresses => { arrMultisigDeviceAddresses = _.difference(arrMultisigDeviceAddresses, arrControlDeviceAddresses); // counterparties on shared addresses must forward further, that's why bForwarded=false - walletGeneral.forwardPrivateChainsToDevices(arrControlDeviceAddresses, arrChainsOfCosignerPrivateElements, bForwarded, conn, function(){ + walletGeneral.forwardPrivateChainsToDevices(arrControlDeviceAddresses, arrChainsOfCosignerPrivateElements, bForwarded, conn, () => { walletGeneral.forwardPrivateChainsToDevices(arrMultisigDeviceAddresses, arrChainsOfCosignerPrivateElements, true, conn, onDone); }); }); @@ -1478,17 +1488,17 @@ function forwardPrivateChainsToOtherMembersOfSharedAddresses(arrChainsOfCosigner } function sendTextcoinEmail(email, subject, amount, asset, mnemonic){ - var mail = require('./mail.js'+''); - var usd_amount_str = ''; + const mail = require('./mail.js'+''); + let usd_amount_str = ''; if (!asset){ amount -= constants.TEXTCOIN_CLAIM_FEE; if (network.exchangeRates['GBYTE_USD']) { - usd_amount_str = " (≈" + ((amount/1e9)*network.exchangeRates['GBYTE_USD']).toLocaleString([], {maximumFractionDigits: 2}) + " USD)"; + usd_amount_str = ` (≈${((amount/1e9)*network.exchangeRates['GBYTE_USD']).toLocaleString([], {maximumFractionDigits: 2})} USD)`; } amount = (amount/1e9).toLocaleString([], {maximumFractionDigits: 9}); asset = 'GB'; } - replaceInTextcoinTemplate({amount: amount, asset: asset, mnemonic: mnemonic, usd_amount_str: usd_amount_str}, function(html, text){ + replaceInTextcoinTemplate({amount, asset, mnemonic, usd_amount_str}, (html, text) => { mail.sendmail({ to: email, from: conf.from_email || "noreply@byteball.org", @@ -1500,17 +1510,17 @@ function sendTextcoinEmail(email, subject, amount, asset, mnemonic){ } function replaceInTextcoinTemplate(params, handleText){ - var fs = require('fs'+''); - fs.readFile(__dirname + '/email_template.html', 'utf8', function(err, template) { + const fs = require('fs'+''); + fs.readFile(`${__dirname}/email_template.html`, 'utf8', (err, template) => { if (err) - throw Error("failed to read template: "+err); - _.forOwn(params, function(value, key){ - var re = new RegExp('\\{\\{' + key + '\\}\\}',"g"); + throw Error(`failed to read template: ${err}`); + _.forOwn(params, (value, key) => { + const re = new RegExp(`\\{\\{${key}\\}\\}`,"g"); template = template.replace(re, value); }); template = template.replace(/\{\{\w*\}\}/g, ''); - var text = "Here is your link to receive " + params.amount + " " + params.asset + params.usd_amount_str + ": https://byteball.org/openapp.html#textcoin?" + params.mnemonic; + const text = `Here is your link to receive ${params.amount} ${params.asset}${params.usd_amount_str}: https://byteball.org/openapp.html#textcoin?${params.mnemonic}`; handleText(template, text); }); } @@ -1518,7 +1528,7 @@ function replaceInTextcoinTemplate(params, handleText){ function receiveTextCoin(mnemonic, addressTo, cb) { mnemonic = mnemonic.toLowerCase().split('-').join(' '); if ((mnemonic.split(' ').length % 3 !== 0) || !Mnemonic.isValid(mnemonic)) { - return cb("invalid mnemonic: "+mnemonic); + return cb(`invalid mnemonic: ${mnemonic}`); } var mnemonic = new Mnemonic(mnemonic); try { @@ -1528,36 +1538,46 @@ function receiveTextCoin(mnemonic, addressTo, cb) { cb(e.message); return; } - var definition = ["sig", {"pubkey": pubkey}]; - var address = objectHash.getChash160(definition); - var signer = { - readSigningPaths: function(conn, address, handleLengthsBySigningPaths){ // returns assoc array signing_path => length - var assocLengthsBySigningPaths = {}; + const definition = ["sig", {"pubkey": pubkey}]; + const address = objectHash.getChash160(definition); + const signer = { + readSigningPaths(conn, address, handleLengthsBySigningPaths) { // returns assoc array signing_path => length + const assocLengthsBySigningPaths = {}; assocLengthsBySigningPaths["r"] = constants.SIG_LENGTH; handleLengthsBySigningPaths(assocLengthsBySigningPaths); }, - readDefinition: function(conn, address, handleDefinition){ + readDefinition(conn, address, handleDefinition) { handleDefinition(null, definition); }, - sign: function(objUnsignedUnit, assocPrivatePayloads, address, signing_path, handleSignature){ + sign( + objUnsignedUnit, + assocPrivatePayloads, + address, + signing_path, + handleSignature + ) { handleSignature(null, ecdsaSig.sign(objectHash.getUnitHashToSign(objUnsignedUnit), xPrivKey.privateKey.bn.toBuffer({size:32}))); } }; - var opts = {}; - var asset = null; + const opts = {}; + let asset = null; opts.signer = signer; opts.paying_addresses = [address]; opts.callbacks = { - ifNotEnoughFunds: function(err){ + ifNotEnoughFunds(err) { cb("This textcoin was already claimed"); }, - ifError: function(err){ + ifError(err) { if (err.indexOf("some definition changes") == 0) return cb("This textcoin was already claimed but not confirmed yet"); cb(err); }, - ifOk: function(objJoint, arrChainsOfRecipientPrivateElements, arrChainsOfCosignerPrivateElements){ + ifOk( + objJoint, + arrChainsOfRecipientPrivateElements, + arrChainsOfCosignerPrivateElements + ) { network.broadcastJoint(objJoint); cb(null, objJoint.unit.unit, asset); } @@ -1568,8 +1588,8 @@ function receiveTextCoin(mnemonic, addressTo, cb) { "SELECT 1 \n\ FROM outputs JOIN units USING(unit) WHERE address=? LIMIT 1", [address], - function(rows){ - if (rows.length === 0) { + ({length}) => { + if (length === 0) { network.requestHistoryFor([], [address], checkStability); } else @@ -1586,11 +1606,11 @@ function receiveTextCoin(mnemonic, addressTo, cb) { "SELECT is_stable, asset, is_spent, amount \n\ FROM outputs JOIN units USING(unit) WHERE address=? AND sequence='good' ORDER BY asset DESC, is_spent ASC LIMIT 1", [address], - function(rows){ + rows => { if (rows.length === 0) { cb("This payment doesn't exist in the network"); } else { - var row = rows[0]; + const row = rows[0]; if (!row.is_stable) { cb("This payment is not confirmed yet, try again later"); } else { @@ -1599,13 +1619,13 @@ function receiveTextCoin(mnemonic, addressTo, cb) { opts.asset = row.asset; opts.amount = row.amount; opts.fee_paying_addresses = [address]; - storage.readAsset(db, row.asset, null, function(err, objAsset){ + storage.readAsset(db, row.asset, null, (err, {fixed_denominations}) => { if (err && err.indexOf("not found" !== -1)) { return network.requestHistoryFor([opts.asset], [], checkStability); } asset = opts.asset; opts.to_address = addressTo; - if (objAsset.fixed_denominations){ // indivisible + if (fixed_denominations){ // indivisible opts.tolerance_plus = 0; opts.tolerance_minus = 0; indivisibleAsset.composeAndSaveIndivisibleAssetPaymentJoint(opts); @@ -1630,17 +1650,17 @@ function receiveTextCoin(mnemonic, addressTo, cb) { // if a textcoin was not claimed for 'days' days, claims it back function claimBackOldTextcoins(to_address, days){ db.query( - "SELECT mnemonic FROM sent_mnemonics LEFT JOIN unit_authors USING(address) \n\ - WHERE mnemonic!='' AND unit_authors.address IS NULL AND creation_date<"+db.addTime("-"+days+" DAYS"), - function(rows){ + `SELECT mnemonic FROM sent_mnemonics LEFT JOIN unit_authors USING(address) \n\ + WHERE mnemonic!='' AND unit_authors.address IS NULL AND creation_date<${db.addTime(`-${days} DAYS`)}`, + rows => { async.eachSeries( rows, - function(row, cb){ - receiveTextCoin(row.mnemonic, to_address, function(err, unit, asset){ + ({mnemonic}, cb) => { + receiveTextCoin(mnemonic, to_address, (err, unit, asset) => { if (err) - console.log("failed claiming back old textcoin "+row.mnemonic+": "+err); + console.log(`failed claiming back old textcoin ${mnemonic}: ${err}`); else - console.log("claimed back mnemonic "+row.mnemonic+", unit "+unit+", asset "+asset); + console.log(`claimed back mnemonic ${mnemonic}, unit ${unit}, asset ${asset}`); cb(); }); } @@ -1654,21 +1674,21 @@ function eraseTextcoin(unit, address) { "UPDATE sent_mnemonics \n\ SET mnemonic='' WHERE unit=? AND address=?", [unit, address], - function(){} + () => {} ); } function readDeviceAddressesUsedInSigningPaths(onDone){ - var sql = "SELECT DISTINCT device_address FROM shared_address_signing_paths "; + let sql = "SELECT DISTINCT device_address FROM shared_address_signing_paths "; sql += "UNION SELECT DISTINCT device_address FROM wallet_signing_paths "; sql += "UNION SELECT DISTINCT device_address FROM pending_shared_address_signing_paths"; db.query( sql, - function(rows){ + rows => { - var arrDeviceAddress = rows.map(function(r) { return r.device_address; }); + const arrDeviceAddress = rows.map(({device_address}) => device_address); onDone(arrDeviceAddress); } @@ -1676,14 +1696,14 @@ function readDeviceAddressesUsedInSigningPaths(onDone){ } function determineIfDeviceCanBeRemoved(device_address, handleResult) { - device.readCorrespondent(device_address, function(correspondent){ + device.readCorrespondent(device_address, correspondent => { if (!correspondent) return handleResult(false); - readDeviceAddressesUsedInSigningPaths(function(arrDeviceAddresses){ + readDeviceAddressesUsedInSigningPaths(arrDeviceAddresses => { handleResult(arrDeviceAddresses.indexOf(device_address) === -1); }); }); -}; +} // todo, almost same as payment diff --git a/wallet_defined_by_addresses.js b/wallet_defined_by_addresses.js index 60d43b3f..84b175e4 100644 --- a/wallet_defined_by_addresses.js +++ b/wallet_defined_by_addresses.js @@ -1,23 +1,21 @@ /*jslint node: true */ -"use strict"; - -var async = require('async'); -var db = require('./db.js'); -var constants = require('./constants.js'); -var conf = require('./conf.js'); -var composer = require('./composer.js'); -var objectHash = require('./object_hash.js'); -var _ = require('lodash'); -var network = require('./network.js'); -var device = require('./device.js'); -var walletGeneral = require('./wallet_general.js'); -var eventBus = require('./event_bus.js'); -var Definition = require("./definition.js"); -var ValidationUtils = require("./validation_utils.js"); -var indivisibleAsset = require('./indivisible_asset.js'); -var divisibleAsset = require('./divisible_asset.js'); - -var MAX_INT32 = Math.pow(2, 31) - 1; +const async = require('async'); +const db = require('./db.js'); +const constants = require('./constants.js'); +const conf = require('./conf.js'); +const composer = require('./composer.js'); +const objectHash = require('./object_hash.js'); +const _ = require('lodash'); +const network = require('./network.js'); +const device = require('./device.js'); +const walletGeneral = require('./wallet_general.js'); +const eventBus = require('./event_bus.js'); +const Definition = require("./definition.js"); +const ValidationUtils = require("./validation_utils.js"); +const indivisibleAsset = require('./indivisible_asset.js'); +const divisibleAsset = require('./divisible_asset.js'); + +const MAX_INT32 = Math.pow(2, 31) - 1; @@ -29,8 +27,8 @@ function sendOfferToCreateNewSharedAddress(device_address, arrAddressDefinitionT // called from UI (unused) function sendApprovalOfNewSharedAddress(device_address, address_definition_template_chash, address, assocDeviceAddressesByRelativeSigningPaths){ device.sendMessageToDevice(device_address, "approve_new_shared_address", { - address_definition_template_chash: address_definition_template_chash, - address: address, + address_definition_template_chash, + address, device_addresses_by_relative_signing_paths: assocDeviceAddressesByRelativeSigningPaths }); } @@ -38,13 +36,13 @@ function sendApprovalOfNewSharedAddress(device_address, address_definition_templ // called from UI (unused) function sendRejectionOfNewSharedAddress(device_address, address_definition_template_chash){ device.sendMessageToDevice(device_address, "reject_new_shared_address", { - address_definition_template_chash: address_definition_template_chash + address_definition_template_chash }); } function sendNewSharedAddress(device_address, address, arrDefinition, assocSignersByPath, bForwarded){ device.sendMessageToDevice(device_address, "new_shared_address", { - address: address, definition: arrDefinition, signers: assocSignersByPath, forwarded: bForwarded + address, definition: arrDefinition, signers: assocSignersByPath, forwarded: bForwarded }); } @@ -56,37 +54,37 @@ function sendNewSharedAddress(device_address, address, arrDefinition, assocSigne // my address is not filled explicitly, it is specified as variable in the template like external addresses // assocMyDeviceAddressesByRelativeSigningPaths points to my device addresses that hold the actual signing keys function createNewSharedAddressByTemplate(arrAddressDefinitionTemplate, my_address, assocMyDeviceAddressesByRelativeSigningPaths){ - validateAddressDefinitionTemplate(arrAddressDefinitionTemplate, device.getMyDeviceAddress(), function(err, assocMemberDeviceAddressesBySigningPaths){ + validateAddressDefinitionTemplate(arrAddressDefinitionTemplate, device.getMyDeviceAddress(), (err, assocMemberDeviceAddressesBySigningPaths) => { if(err) { throw Error(err); } // assocMemberDeviceAddressesBySigningPaths are keyed by paths from root to member addresses (not all the way to signing keys) - var arrMemberSigningPaths = Object.keys(assocMemberDeviceAddressesBySigningPaths); - var address_definition_template_chash = objectHash.getChash160(arrAddressDefinitionTemplate); + const arrMemberSigningPaths = Object.keys(assocMemberDeviceAddressesBySigningPaths); + const address_definition_template_chash = objectHash.getChash160(arrAddressDefinitionTemplate); db.query( "INSERT INTO pending_shared_addresses (definition_template_chash, definition_template) VALUES(?,?)", [address_definition_template_chash, JSON.stringify(arrAddressDefinitionTemplate)], - function(){ + () => { async.eachSeries( arrMemberSigningPaths, - function(signing_path, cb){ - var device_address = assocMemberDeviceAddressesBySigningPaths[signing_path]; - var fields = "definition_template_chash, device_address, signing_path"; - var values = "?,?,?"; - var arrParams = [address_definition_template_chash, device_address, signing_path]; + (signing_path, cb) => { + const device_address = assocMemberDeviceAddressesBySigningPaths[signing_path]; + let fields = "definition_template_chash, device_address, signing_path"; + let values = "?,?,?"; + const arrParams = [address_definition_template_chash, device_address, signing_path]; if (device_address === device.getMyDeviceAddress()){ fields += ", address, device_addresses_by_relative_signing_paths, approval_date"; - values += ",?,?,"+db.getNow(); + values += `,?,?,${db.getNow()}`; arrParams.push(my_address, JSON.stringify(assocMyDeviceAddressesByRelativeSigningPaths)); } - db.query("INSERT INTO pending_shared_address_signing_paths ("+fields+") VALUES("+values+")", arrParams, function(){ + db.query(`INSERT INTO pending_shared_address_signing_paths (${fields}) VALUES(${values})`, arrParams, () => { cb(); }); }, - function(){ - var arrMemberDeviceAddresses = _.uniq(_.values(assocMemberDeviceAddressesBySigningPaths)); - arrMemberDeviceAddresses.forEach(function(device_address){ + () => { + const arrMemberDeviceAddresses = _.uniq(_.values(assocMemberDeviceAddressesBySigningPaths)); + arrMemberDeviceAddresses.forEach(device_address => { if (device_address !== device.getMyDeviceAddress()) sendOfferToCreateNewSharedAddress(device_address, arrAddressDefinitionTemplate); }) @@ -101,47 +99,47 @@ function createNewSharedAddressByTemplate(arrAddressDefinitionTemplate, my_addre // received approval from co-signer address function approvePendingSharedAddress(address_definition_template_chash, from_address, address, assocDeviceAddressesByRelativeSigningPaths){ db.query( // may update several rows if the device is referenced multiple times from the definition template - "UPDATE pending_shared_address_signing_paths SET address=?, device_addresses_by_relative_signing_paths=?, approval_date="+db.getNow()+" \n\ - WHERE definition_template_chash=? AND device_address=?", + `UPDATE pending_shared_address_signing_paths SET address=?, device_addresses_by_relative_signing_paths=?, approval_date=${db.getNow()} \n\ + WHERE definition_template_chash=? AND device_address=?`, [address, JSON.stringify(assocDeviceAddressesByRelativeSigningPaths), address_definition_template_chash, from_address], - function(){ + () => { // check if this is the last required approval db.query( "SELECT device_address, signing_path, address, device_addresses_by_relative_signing_paths \n\ FROM pending_shared_address_signing_paths \n\ WHERE definition_template_chash=?", [address_definition_template_chash], - function(rows){ + rows => { if (rows.length === 0) // another device rejected the address at the same time return; - if (rows.some(function(row){ return !row.address; })) // some devices haven't approved yet + if (rows.some(row => !row.address)) // some devices haven't approved yet return; // all approvals received - var params = {}; - rows.forEach(function(row){ // the same device_address can be mentioned in several rows - params['address@'+row.device_address] = row.address; + const params = {}; + rows.forEach(row => { // the same device_address can be mentioned in several rows + params[`address@${row.device_address}`] = row.address; }); db.query( "SELECT definition_template FROM pending_shared_addresses WHERE definition_template_chash=?", [address_definition_template_chash], - function(templ_rows){ + templ_rows => { if (templ_rows.length !== 1) throw Error("template not found"); - var arrAddressDefinitionTemplate = JSON.parse(templ_rows[0].definition_template); - var arrDefinition = Definition.replaceInTemplate(arrAddressDefinitionTemplate, params); - var shared_address = objectHash.getChash160(arrDefinition); + const arrAddressDefinitionTemplate = JSON.parse(templ_rows[0].definition_template); + const arrDefinition = Definition.replaceInTemplate(arrAddressDefinitionTemplate, params); + const shared_address = objectHash.getChash160(arrDefinition); db.query( "INSERT INTO shared_addresses (shared_address, definition) VALUES (?,?)", [shared_address, JSON.stringify(arrDefinition)], - function(){ - var arrQueries = []; - var assocSignersByPath = {}; - rows.forEach(function(row){ - var assocDeviceAddressesByRelativeSigningPaths = JSON.parse(row.device_addresses_by_relative_signing_paths); - for (var member_signing_path in assocDeviceAddressesByRelativeSigningPaths){ - var signing_device_address = assocDeviceAddressesByRelativeSigningPaths[member_signing_path]; + () => { + const arrQueries = []; + const assocSignersByPath = {}; + rows.forEach(row => { + const assocDeviceAddressesByRelativeSigningPaths = JSON.parse(row.device_addresses_by_relative_signing_paths); + for (const member_signing_path in assocDeviceAddressesByRelativeSigningPaths){ + const signing_device_address = assocDeviceAddressesByRelativeSigningPaths[member_signing_path]; // this is full signing path, from root of shared address (not from root of member address) - var full_signing_path = row.signing_path + member_signing_path.substring(1); + const full_signing_path = row.signing_path + member_signing_path.substring(1); // note that we are inserting row.device_address (the device we requested approval from), not signing_device_address // (the actual signer), because signing_device_address might not be our correspondent. When we need to sign, we'll // send unsigned unit to row.device_address and it'll forward the request to signing_device_address (subject to @@ -153,16 +151,16 @@ function approvePendingSharedAddress(address_definition_template_chash, from_add assocSignersByPath[full_signing_path] = { device_address: row.device_address, address: row.address, - member_signing_path: member_signing_path + member_signing_path }; } }); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { deletePendingSharedAddress(address_definition_template_chash); // notify all other member-devices about the new shared address they are a part of - rows.forEach(function(row){ - if (row.device_address !== device.getMyDeviceAddress()) - sendNewSharedAddress(row.device_address, shared_address, arrDefinition, assocSignersByPath); + rows.forEach(({device_address}) => { + if (device_address !== device.getMyDeviceAddress()) + sendNewSharedAddress(device_address, shared_address, arrDefinition, assocSignersByPath); }); forwardNewSharedAddressToCosignersOfMyMemberAddresses(shared_address, arrDefinition, assocSignersByPath); if (conf.bLight) @@ -180,8 +178,8 @@ function approvePendingSharedAddress(address_definition_template_chash, from_add // unused function deletePendingSharedAddress(address_definition_template_chash){ - db.query("DELETE FROM pending_shared_address_signing_paths WHERE definition_template_chash=?", [address_definition_template_chash], function(){ - db.query("DELETE FROM pending_shared_addresses WHERE definition_template_chash=?", [address_definition_template_chash], function(){}); + db.query("DELETE FROM pending_shared_address_signing_paths WHERE definition_template_chash=?", [address_definition_template_chash], () => { + db.query("DELETE FROM pending_shared_addresses WHERE definition_template_chash=?", [address_definition_template_chash], () => {}); }); } @@ -191,20 +189,20 @@ function deletePendingSharedAddress(address_definition_template_chash){ function addNewSharedAddress(address, arrDefinition, assocSignersByPath, bForwarded, onDone){ // network.addWatchedAddress(address); db.query( - "INSERT "+db.getIgnore()+" INTO shared_addresses (shared_address, definition) VALUES (?,?)", + `INSERT ${db.getIgnore()} INTO shared_addresses (shared_address, definition) VALUES (?,?)`, [address, JSON.stringify(arrDefinition)], - function(){ - var arrQueries = []; - for (var signing_path in assocSignersByPath){ - var signerInfo = assocSignersByPath[signing_path]; + () => { + const arrQueries = []; + for (const signing_path in assocSignersByPath){ + const signerInfo = assocSignersByPath[signing_path]; db.addQuery(arrQueries, - "INSERT "+db.getIgnore()+" INTO shared_address_signing_paths \n\ - (shared_address, address, signing_path, member_signing_path, device_address) VALUES (?,?,?,?,?)", + `INSERT ${db.getIgnore()} INTO shared_address_signing_paths \n\ + (shared_address, address, signing_path, member_signing_path, device_address) VALUES (?,?,?,?,?)`, [address, signerInfo.address, signing_path, signerInfo.member_signing_path, signerInfo.device_address]); } - async.series(arrQueries, function(){ - console.log('added new shared address '+address); - eventBus.emit("new_address-"+address); + async.series(arrQueries, () => { + console.log(`added new shared address ${address}`); + eventBus.emit(`new_address-${address}`); if (conf.bLight) network.addLightWatchedAddress(address); if (!bForwarded) @@ -217,8 +215,8 @@ function addNewSharedAddress(address, arrDefinition, assocSignersByPath, bForwar } function includesMyDeviceAddress(assocSignersByPath){ - for (var signing_path in assocSignersByPath){ - var signerInfo = assocSignersByPath[signing_path]; + for (const signing_path in assocSignersByPath){ + const signerInfo = assocSignersByPath[signing_path]; if (signerInfo.device_address === device.getMyDeviceAddress()) return true; } @@ -228,16 +226,16 @@ function includesMyDeviceAddress(assocSignersByPath){ // Checks if any of my payment addresses is mentioned. // It is possible that my device address is not mentioned in the definition if I'm a member of multisig address, one of my cosigners is mentioned instead function determineIfIncludesMeAndRewriteDeviceAddress(assocSignersByPath, handleResult){ - var assocMemberAddresses = {}; - var bHasMyDeviceAddress = false; - for (var signing_path in assocSignersByPath){ - var signerInfo = assocSignersByPath[signing_path]; + const assocMemberAddresses = {}; + let bHasMyDeviceAddress = false; + for (const signing_path in assocSignersByPath){ + const signerInfo = assocSignersByPath[signing_path]; if (signerInfo.device_address === device.getMyDeviceAddress()) bHasMyDeviceAddress = true; if (signerInfo.address) assocMemberAddresses[signerInfo.address] = true; } - var arrMemberAddresses = Object.keys(assocMemberAddresses); + const arrMemberAddresses = Object.keys(assocMemberAddresses); if (arrMemberAddresses.length === 0) return handleResult("no member addresses?"); db.query( @@ -245,15 +243,15 @@ function determineIfIncludesMeAndRewriteDeviceAddress(assocSignersByPath, handle UNION \n\ SELECT shared_address AS address, 'shared' AS type FROM shared_addresses WHERE shared_address IN(?)", [arrMemberAddresses, arrMemberAddresses], - function(rows){ + rows => { // handleResult(rows.length === arrMyMemberAddresses.length ? null : "Some of my member addresses not found"); if (rows.length === 0) return handleResult("I am not a member of this shared address"); - var arrMyMemberAddresses = rows.filter(function(row){ return (row.type === 'my'); }).map(function(row){ return row.address; }); + const arrMyMemberAddresses = rows.filter(({type}) => type === 'my').map(({address}) => address); // rewrite device address for my addresses if (!bHasMyDeviceAddress){ - for (var signing_path in assocSignersByPath){ - var signerInfo = assocSignersByPath[signing_path]; + for (const signing_path in assocSignersByPath){ + const signerInfo = assocSignersByPath[signing_path]; if (signerInfo.address && arrMyMemberAddresses.indexOf(signerInfo.address) >= 0) signerInfo.device_address = device.getMyDeviceAddress(); } @@ -264,46 +262,46 @@ function determineIfIncludesMeAndRewriteDeviceAddress(assocSignersByPath, handle } function forwardNewSharedAddressToCosignersOfMyMemberAddresses(address, arrDefinition, assocSignersByPath){ - var assocMyMemberAddresses = {}; - for (var signing_path in assocSignersByPath){ - var signerInfo = assocSignersByPath[signing_path]; + const assocMyMemberAddresses = {}; + for (const signing_path in assocSignersByPath){ + const signerInfo = assocSignersByPath[signing_path]; if (signerInfo.device_address === device.getMyDeviceAddress() && signerInfo.address) assocMyMemberAddresses[signerInfo.address] = true; } - var arrMyMemberAddresses = Object.keys(assocMyMemberAddresses); + const arrMyMemberAddresses = Object.keys(assocMyMemberAddresses); if (arrMyMemberAddresses.length === 0) throw Error("my member addresses not found"); db.query( "SELECT DISTINCT device_address FROM my_addresses JOIN wallet_signing_paths USING(wallet) WHERE address IN(?) AND device_address!=?", [arrMyMemberAddresses, device.getMyDeviceAddress()], - function(rows){ - rows.forEach(function(row){ - sendNewSharedAddress(row.device_address, address, arrDefinition, assocSignersByPath, true); + rows => { + rows.forEach(({device_address}) => { + sendNewSharedAddress(device_address, address, arrDefinition, assocSignersByPath, true); }); } ); } // {address: "BASE32", definition: [...], signers: {...}} -function handleNewSharedAddress(body, callbacks){ - if (!ValidationUtils.isArrayOfLength(body.definition, 2)) +function handleNewSharedAddress({definition, signers, address, forwarded}, callbacks) { + if (!ValidationUtils.isArrayOfLength(definition, 2)) return callbacks.ifError("invalid definition"); - if (typeof body.signers !== "object" || Object.keys(body.signers).length === 0) + if (typeof signers !== "object" || Object.keys(signers).length === 0) return callbacks.ifError("invalid signers"); - if (body.address !== objectHash.getChash160(body.definition)) + if (address !== objectHash.getChash160(definition)) return callbacks.ifError("definition doesn't match its c-hash"); - for (var signing_path in body.signers){ - var signerInfo = body.signers[signing_path]; + for (const signing_path in signers){ + const signerInfo = signers[signing_path]; if (signerInfo.address && !ValidationUtils.isValidAddress(signerInfo.address)) - return callbacks.ifError("invalid member address: "+signerInfo.address); + return callbacks.ifError(`invalid member address: ${signerInfo.address}`); } - determineIfIncludesMeAndRewriteDeviceAddress(body.signers, function(err){ + determineIfIncludesMeAndRewriteDeviceAddress(signers, err => { if (err) return callbacks.ifError(err); - validateAddressDefinition(body.definition, function(err){ + validateAddressDefinition(definition, err => { if (err) return callbacks.ifError(err); - addNewSharedAddress(body.address, body.definition, body.signers, body.forwarded, callbacks.ifOk); + addNewSharedAddress(address, definition, signers, forwarded, callbacks.ifOk); }); }); } @@ -311,18 +309,18 @@ function handleNewSharedAddress(body, callbacks){ function createNewSharedAddress(arrDefinition, assocSignersByPath, callbacks){ if (!includesMyDeviceAddress(assocSignersByPath)) return callbacks.ifError("my device address not mentioned"); - var address = objectHash.getChash160(arrDefinition); - handleNewSharedAddress({address: address, definition: arrDefinition, signers: assocSignersByPath}, { + const address = objectHash.getChash160(arrDefinition); + handleNewSharedAddress({address, definition: arrDefinition, signers: assocSignersByPath}, { ifError: callbacks.ifError, - ifOk: function(){ + ifOk() { // share the new address with all cosigners - var arrDeviceAddresses = []; - for (var signing_path in assocSignersByPath){ - var signerInfo = assocSignersByPath[signing_path]; + const arrDeviceAddresses = []; + for (const signing_path in assocSignersByPath){ + const signerInfo = assocSignersByPath[signing_path]; if (signerInfo.device_address !== device.getMyDeviceAddress() && arrDeviceAddresses.indexOf(signerInfo.device_address) === -1) arrDeviceAddresses.push(signerInfo.device_address); } - arrDeviceAddresses.forEach(function(device_address){ + arrDeviceAddresses.forEach(device_address => { sendNewSharedAddress(device_address, address, arrDefinition, assocSignersByPath); }); callbacks.ifOk(address); @@ -332,38 +330,38 @@ function createNewSharedAddress(arrDefinition, assocSignersByPath, callbacks){ function getMemberDeviceAddressesBySigningPaths(arrAddressDefinitionTemplate){ function evaluate(arr, path){ - var op = arr[0]; - var args = arr[1]; + const op = arr[0]; + const args = arr[1]; if (!args) return; switch (op){ case 'or': case 'and': for (var i=0; i { + params[`address@${device_address}`] = fake_address; }); try{ var arrFakeDefinition = Definition.replaceInTemplate(arrDefinitionTemplate, params); @@ -395,9 +393,9 @@ function validateAddressDefinitionTemplate(arrDefinitionTemplate, from_address, catch(e){ return handleResult(e.toString()); } - var objFakeUnit = {authors: [{address: fake_address, definition: ["sig", {pubkey: device.getMyDevicePubKey()}]}]}; - var objFakeValidationState = {last_ball_mci: MAX_INT32}; - Definition.validateDefinition(db, arrFakeDefinition, objFakeUnit, objFakeValidationState, null, false, function(err){ + const objFakeUnit = {authors: [{address: fake_address, definition: ["sig", {pubkey: device.getMyDevicePubKey()}]}]}; + const objFakeValidationState = {last_ball_mci: MAX_INT32}; + Definition.validateDefinition(db, arrFakeDefinition, objFakeUnit, objFakeValidationState, null, false, err => { if (err) return handleResult(err); handleResult(null, assocMemberDeviceAddressesBySigningPaths); @@ -407,9 +405,9 @@ function validateAddressDefinitionTemplate(arrDefinitionTemplate, from_address, // fix: // 1. check that my address is referenced in the definition function validateAddressDefinition(arrDefinition, handleResult){ - var objFakeUnit = {authors: []}; - var objFakeValidationState = {last_ball_mci: MAX_INT32, bAllowUnresolvedInnerDefinitions: true}; - Definition.validateDefinition(db, arrDefinition, objFakeUnit, objFakeValidationState, null, false, function(err){ + const objFakeUnit = {authors: []}; + const objFakeValidationState = {last_ball_mci: MAX_INT32, bAllowUnresolvedInnerDefinitions: true}; + Definition.validateDefinition(db, arrDefinition, objFakeUnit, objFakeValidationState, null, false, err => { if (err) return handleResult(err); handleResult(); @@ -417,31 +415,29 @@ function validateAddressDefinition(arrDefinition, handleResult){ } -function forwardPrivateChainsToOtherMembersOfAddresses(arrChains, arrAddresses, conn, onSaved){ - conn = conn || db; - conn.query( +function forwardPrivateChainsToOtherMembersOfAddresses(arrChains, arrAddresses, conn = db, onSaved) { + conn.query( "SELECT device_address FROM shared_address_signing_paths WHERE shared_address IN(?) AND device_address!=?", [arrAddresses, device.getMyDeviceAddress()], - function(rows){ - console.log("shared address devices: "+rows.length); - var arrDeviceAddresses = rows.map(function(row){ return row.device_address; }); + rows => { + console.log(`shared address devices: ${rows.length}`); + const arrDeviceAddresses = rows.map(({device_address}) => device_address); walletGeneral.forwardPrivateChainsToDevices(arrDeviceAddresses, arrChains, true, conn, onSaved); } ); } -function readAllControlAddresses(conn, arrAddresses, handleLists){ - conn = conn || db; - conn.query( +function readAllControlAddresses(conn = db, arrAddresses, handleLists) { + conn.query( "SELECT DISTINCT address, shared_address_signing_paths.device_address, (correspondent_devices.device_address IS NOT NULL) AS have_correspondent \n\ FROM shared_address_signing_paths LEFT JOIN correspondent_devices USING(device_address) WHERE shared_address IN(?)", [arrAddresses], - function(rows){ + rows => { if (rows.length === 0) return handleLists([], []); - var arrControlAddresses = rows.map(function(row){ return row.address; }); - var arrControlDeviceAddresses = rows.filter(function(row){ return row.have_correspondent; }).map(function(row){ return row.device_address; }); - readAllControlAddresses(conn, arrControlAddresses, function(arrControlAddresses2, arrControlDeviceAddresses2){ + const arrControlAddresses = rows.map(({address}) => address); + const arrControlDeviceAddresses = rows.filter(({have_correspondent}) => have_correspondent).map(({device_address}) => device_address); + readAllControlAddresses(conn, arrControlAddresses, (arrControlAddresses2, arrControlDeviceAddresses2) => { handleLists(_.union(arrControlAddresses, arrControlAddresses2), _.union(arrControlDeviceAddresses, arrControlDeviceAddresses2)); }); } @@ -465,12 +461,12 @@ function readRequiredCosigners(shared_address, arrSigningDeviceAddresses, handle function readSharedAddressDefinition(shared_address, handleDefinition){ db.query( - "SELECT definition, "+db.getUnixTimestamp("creation_date")+" AS creation_ts FROM shared_addresses WHERE shared_address=?", + `SELECT definition, ${db.getUnixTimestamp("creation_date")} AS creation_ts FROM shared_addresses WHERE shared_address=?`, [shared_address], - function(rows){ + rows => { if (rows.length !== 1) - throw Error('shared definition not found '+shared_address); - var arrDefinition = JSON.parse(rows[0].definition); + throw Error(`shared definition not found ${shared_address}`); + const arrDefinition = JSON.parse(rows[0].definition); handleDefinition(arrDefinition, rows[0].creation_ts); } ); @@ -479,15 +475,15 @@ function readSharedAddressDefinition(shared_address, handleDefinition){ // returns information about cosigner devices function readSharedAddressCosigners(shared_address, handleCosigners){ db.query( - "SELECT DISTINCT shared_address_signing_paths.device_address, name, "+db.getUnixTimestamp("shared_addresses.creation_date")+" AS creation_ts \n\ - FROM shared_address_signing_paths \n\ - JOIN shared_addresses USING(shared_address) \n\ - LEFT JOIN correspondent_devices USING(device_address) \n\ - WHERE shared_address=? AND device_address!=?", + `SELECT DISTINCT shared_address_signing_paths.device_address, name, ${db.getUnixTimestamp("shared_addresses.creation_date")} AS creation_ts \n\ + FROM shared_address_signing_paths \n\ + JOIN shared_addresses USING(shared_address) \n\ + LEFT JOIN correspondent_devices USING(device_address) \n\ + WHERE shared_address=? AND device_address!=?`, [shared_address, device.getMyDeviceAddress()], - function(rows){ + rows => { if (rows.length === 0) - throw Error("no cosigners found for shared address "+shared_address); + throw Error(`no cosigners found for shared address ${shared_address}`); handleCosigners(rows); } ); @@ -498,24 +494,24 @@ function readSharedAddressPeerAddresses(shared_address, handlePeerAddresses){ db.query( "SELECT DISTINCT address FROM shared_address_signing_paths WHERE shared_address=? AND device_address!=?", [shared_address, device.getMyDeviceAddress()], - function(rows){ + rows => { // no problem if no peers found: the peer can be part of our multisig address and his device address will be rewritten to ours // if (rows.length === 0) // throw Error("no peers found for shared address "+shared_address); - var arrPeerAddresses = rows.map(function(row){ return row.address; }); + const arrPeerAddresses = rows.map(({address}) => address); handlePeerAddresses(arrPeerAddresses); } ); } function getPeerAddressesFromSigners(assocSignersByPath){ - var assocPeerAddresses = {}; - for (var path in assocSignersByPath){ - var signerInfo = assocSignersByPath[path]; + const assocPeerAddresses = {}; + for (const path in assocSignersByPath){ + const signerInfo = assocSignersByPath[path]; if (signerInfo.device_address !== device.getMyDeviceAddress()) assocPeerAddresses[signerInfo.address] = true; } - var arrPeerAddresses = Object.keys(assocPeerAddresses); + const arrPeerAddresses = Object.keys(assocPeerAddresses); return arrPeerAddresses; } @@ -523,8 +519,8 @@ function determineIfHasMerkle(shared_address, handleResult){ db.query( "SELECT 1 FROM shared_address_signing_paths WHERE shared_address=? AND device_address=? AND address=''", [shared_address, device.getMyDeviceAddress()], - function(rows){ - handleResult(rows.length > 0); + ({length}) => { + handleResult(length > 0); } ); } diff --git a/wallet_defined_by_keys.js b/wallet_defined_by_keys.js index fbac8130..0ab7b299 100644 --- a/wallet_defined_by_keys.js +++ b/wallet_defined_by_keys.js @@ -1,22 +1,21 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var crypto = require('crypto'); -var db = require('./db.js'); -var constants = require('./constants.js'); -var mutex = require('./mutex.js'); -var conf = require('./conf.js'); -var composer = require('./composer.js'); -var objectHash = require('./object_hash.js'); -var _ = require('lodash'); -var storage = require('./storage.js'); -var network = require('./network.js'); -var device = require('./device.js'); -var walletGeneral = require('./wallet_general.js'); -var eventBus = require('./event_bus.js'); -var Definition = require("./definition.js"); -var ValidationUtils = require("./validation_utils.js"); -var breadcrumbs = require('./breadcrumbs.js'); +const async = require('async'); +const crypto = require('crypto'); +const db = require('./db.js'); +const constants = require('./constants.js'); +const mutex = require('./mutex.js'); +const conf = require('./conf.js'); +const composer = require('./composer.js'); +const objectHash = require('./object_hash.js'); +const _ = require('lodash'); +const storage = require('./storage.js'); +const network = require('./network.js'); +const device = require('./device.js'); +const walletGeneral = require('./wallet_general.js'); +const eventBus = require('./event_bus.js'); +const Definition = require("./definition.js"); +const ValidationUtils = require("./validation_utils.js"); +const breadcrumbs = require('./breadcrumbs.js'); try{ var Bitcore = require('bitcore-lib'); } @@ -24,41 +23,41 @@ catch(e){ // if byteballcore is a symlink, load bitcore-lib from the main module var Bitcore = loadBitcoreFromNearestParent(module.parent); } -var MAX_BIP44_GAP = 20; -var MAX_INT32 = Math.pow(2, 31) - 1; +const MAX_BIP44_GAP = 20; +const MAX_INT32 = Math.pow(2, 31) - 1; function loadBitcoreFromNearestParent(mod){ if (!mod) throw Error("reached root but bitcore not found"); try{ - return require(mod.paths[0]+'/bitcore-lib'); + return require(`${mod.paths[0]}/bitcore-lib`); } catch(e){ - console.log("bitcore-lib not found from "+mod.filename+", will try from its parent"); + console.log(`bitcore-lib not found from ${mod.filename}, will try from its parent`); return loadBitcoreFromNearestParent(mod.parent); } } function sendOfferToCreateNewWallet(device_address, wallet, arrWalletDefinitionTemplate, walletName, arrOtherCosigners, isSingleAddress, callbacks){ - var body = {wallet: wallet, wallet_definition_template: arrWalletDefinitionTemplate, wallet_name: walletName, other_cosigners: arrOtherCosigners, is_single_address: isSingleAddress}; + const body = {wallet, wallet_definition_template: arrWalletDefinitionTemplate, wallet_name: walletName, other_cosigners: arrOtherCosigners, is_single_address: isSingleAddress}; device.sendMessageToDevice(device_address, "create_new_wallet", body, callbacks); } function sendCommandToCancelNewWallet(device_address, wallet, callbacks){ - device.sendMessageToDevice(device_address, "cancel_new_wallet", {wallet: wallet}, callbacks); + device.sendMessageToDevice(device_address, "cancel_new_wallet", {wallet}, callbacks); } function sendMyXPubKey(device_address, wallet, my_xpubkey){ - device.sendMessageToDevice(device_address, "my_xpubkey", {wallet: wallet, my_xpubkey: my_xpubkey}); + device.sendMessageToDevice(device_address, "my_xpubkey", {wallet, my_xpubkey}); } function sendNotificationThatWalletFullyApproved(device_address, wallet){ - device.sendMessageToDevice(device_address, "wallet_fully_approved", {wallet: wallet}); + device.sendMessageToDevice(device_address, "wallet_fully_approved", {wallet}); } function sendNewWalletAddress(device_address, wallet, is_change, address_index, address){ device.sendMessageToDevice(device_address, "new_wallet_address", { - wallet: wallet, address: address, is_change: is_change, address_index: address_index + wallet, address, is_change, address_index }); } @@ -80,17 +79,17 @@ function handleOfferToCreateNewWallet(body, from_address, callbacks){ return callbacks.ifError("no other_cosigners"); // the wallet should have an event handler that requests user confirmation, derives (or generates) a new key, records it, // and sends the newly derived xpubkey to other members - validateWalletDefinitionTemplate(body.wallet_definition_template, from_address, function(err, arrDeviceAddresses){ + validateWalletDefinitionTemplate(body.wallet_definition_template, from_address, (err, arrDeviceAddresses) => { if (err) return callbacks.ifError(err); if (body.other_cosigners.length !== arrDeviceAddresses.length - 1) return callbacks.ifError("wrong length of other_cosigners"); - var arrOtherDeviceAddresses = _.uniq(body.other_cosigners.map(function(cosigner){ return cosigner.device_address; })); + const arrOtherDeviceAddresses = _.uniq(body.other_cosigners.map(({device_address}) => device_address)); arrOtherDeviceAddresses.push(from_address); if (!_.isEqual(arrDeviceAddresses.sort(), arrOtherDeviceAddresses.sort())) return callbacks.ifError("wrong other_cosigners"); - for (var i=0; i { + const account = (rows.length === 0) ? 0 : (rows[0].max_account + 1); handleAccount(account); }); } // check that all members agree that the wallet is fully approved now function checkAndFinalizeWallet(wallet, onDone){ - db.query("SELECT member_ready_date FROM wallets LEFT JOIN extended_pubkeys USING(wallet) WHERE wallets.wallet=?", [wallet], function(rows){ + db.query("SELECT member_ready_date FROM wallets LEFT JOIN extended_pubkeys USING(wallet) WHERE wallets.wallet=?", [wallet], rows => { if (rows.length === 0){ // wallet not created yet or already deleted // throw Error("no wallet in checkAndFinalizeWallet"); console.log("no wallet in checkAndFinalizeWallet"); return onDone ? onDone() : null; } - if (rows.some(function(row){ return !row.member_ready_date; })) + if (rows.some(({member_ready_date}) => !member_ready_date)) return onDone ? onDone() : null; - db.query("UPDATE wallets SET ready_date="+db.getNow()+" WHERE wallet=? AND ready_date IS NULL", [wallet], function(){ + db.query(`UPDATE wallets SET ready_date=${db.getNow()} WHERE wallet=? AND ready_date IS NULL`, [wallet], () => { if (onDone) onDone(); eventBus.emit('wallet_completed', wallet); @@ -137,23 +136,23 @@ function checkAndFinalizeWallet(wallet, onDone){ } function checkAndFullyApproveWallet(wallet, onDone){ - db.query("SELECT approval_date FROM wallets LEFT JOIN extended_pubkeys USING(wallet) WHERE wallets.wallet=?", [wallet], function(rows){ + db.query("SELECT approval_date FROM wallets LEFT JOIN extended_pubkeys USING(wallet) WHERE wallets.wallet=?", [wallet], rows => { if (rows.length === 0) // wallet not created yet return onDone ? onDone() : null; - if (rows.some(function(row){ return !row.approval_date; })) + if (rows.some(({approval_date}) => !approval_date)) return onDone ? onDone() : null; - db.query("UPDATE wallets SET full_approval_date="+db.getNow()+" WHERE wallet=? AND full_approval_date IS NULL", [wallet], function(){ + db.query(`UPDATE wallets SET full_approval_date=${db.getNow()} WHERE wallet=? AND full_approval_date IS NULL`, [wallet], () => { db.query( - "UPDATE extended_pubkeys SET member_ready_date="+db.getNow()+" WHERE wallet=? AND device_address=?", + `UPDATE extended_pubkeys SET member_ready_date=${db.getNow()} WHERE wallet=? AND device_address=?`, [wallet, device.getMyDeviceAddress()], - function(){ + () => { db.query( "SELECT device_address FROM extended_pubkeys WHERE wallet=? AND device_address!=?", [wallet, device.getMyDeviceAddress()], - function(rows){ + rows => { // let other members know that I've collected all necessary xpubkeys and ready to use this wallet - rows.forEach(function(row){ - sendNotificationThatWalletFullyApproved(row.device_address, wallet); + rows.forEach(({device_address}) => { + sendNotificationThatWalletFullyApproved(device_address, wallet); }); checkAndFinalizeWallet(wallet, onDone); } @@ -165,57 +164,57 @@ function checkAndFullyApproveWallet(wallet, onDone){ } function addWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, onDone){ - var assocDeviceAddressesBySigningPaths = getDeviceAddressesBySigningPaths(arrWalletDefinitionTemplate); - var arrDeviceAddresses = _.uniq(_.values(assocDeviceAddressesBySigningPaths)); + const assocDeviceAddressesBySigningPaths = getDeviceAddressesBySigningPaths(arrWalletDefinitionTemplate); + const arrDeviceAddresses = _.uniq(_.values(assocDeviceAddressesBySigningPaths)); async.series([ - function(cb){ - var fields = "wallet, account, definition_template"; - var values = "?,?,?"; + cb => { + let fields = "wallet, account, definition_template"; + let values = "?,?,?"; if (arrDeviceAddresses.length === 1){ // single sig fields += ", full_approval_date, ready_date"; - values += ", "+db.getNow()+", "+db.getNow(); + values += `, ${db.getNow()}, ${db.getNow()}`; } - db.query("INSERT INTO wallets ("+fields+") VALUES ("+values+")", [wallet, account, JSON.stringify(arrWalletDefinitionTemplate)], function(){ + db.query(`INSERT INTO wallets (${fields}) VALUES (${values})`, [wallet, account, JSON.stringify(arrWalletDefinitionTemplate)], () => { cb(); }); }, - function(cb){ + cb => { async.eachSeries( arrDeviceAddresses, - function(device_address, cb2){ - console.log("adding device "+device_address+' to wallet '+wallet); - var fields = "wallet, device_address"; - var values = "?,?"; - var arrParams = [wallet, device_address]; + (device_address, cb2) => { + console.log(`adding device ${device_address} to wallet ${wallet}`); + let fields = "wallet, device_address"; + let values = "?,?"; + const arrParams = [wallet, device_address]; // arrDeviceAddresses.length === 1 works for singlesig with external priv key if (device_address === device.getMyDeviceAddress() || arrDeviceAddresses.length === 1){ fields += ", extended_pubkey, approval_date"; - values += ",?,"+db.getNow(); + values += `,?,${db.getNow()}`; arrParams.push(xPubKey); if (arrDeviceAddresses.length === 1){ fields += ", member_ready_date"; - values += ", "+db.getNow(); + values += `, ${db.getNow()}`; } } - db.query("INSERT "+db.getIgnore()+" INTO extended_pubkeys ("+fields+") VALUES ("+values+")", arrParams, function(){ + db.query(`INSERT ${db.getIgnore()} INTO extended_pubkeys (${fields}) VALUES (${values})`, arrParams, () => { cb2(); }); }, cb ); }, - function(cb){ - var arrSigningPaths = Object.keys(assocDeviceAddressesBySigningPaths); + cb => { + const arrSigningPaths = Object.keys(assocDeviceAddressesBySigningPaths); async.eachSeries( arrSigningPaths, - function(signing_path, cb2){ - console.log("adding signing path "+signing_path+' to wallet '+wallet); - var device_address = assocDeviceAddressesBySigningPaths[signing_path]; + (signing_path, cb2) => { + console.log(`adding signing path ${signing_path} to wallet ${wallet}`); + const device_address = assocDeviceAddressesBySigningPaths[signing_path]; db.query( "INSERT INTO wallet_signing_paths (wallet, signing_path, device_address) VALUES (?,?,?)", [wallet, signing_path, device_address], - function(){ + () => { cb2(); } ); @@ -223,18 +222,18 @@ function addWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, onDone cb ); } - ], function(){ - console.log("addWallet done "+wallet); + ], () => { + console.log(`addWallet done ${wallet}`); (arrDeviceAddresses.length === 1) ? onDone() : checkAndFullyApproveWallet(wallet, onDone); }); } // initiator of the new wallet creates records about itself and sends requests to other devices function createWallet(xPubKey, account, arrWalletDefinitionTemplate, walletName, isSingleAddress, handleWallet){ - var wallet = crypto.createHash("sha256").update(xPubKey, "utf8").digest("base64"); - console.log('will create wallet '+wallet); - var arrDeviceAddresses = getDeviceAddresses(arrWalletDefinitionTemplate); - addWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, function(){ + const wallet = crypto.createHash("sha256").update(xPubKey, "utf8").digest("base64"); + console.log(`will create wallet ${wallet}`); + const arrDeviceAddresses = getDeviceAddresses(arrWalletDefinitionTemplate); + addWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, () => { handleWallet(wallet); if (arrDeviceAddresses.length === 1) // single sig return; @@ -242,13 +241,13 @@ function createWallet(xPubKey, account, arrWalletDefinitionTemplate, walletName, // this continues in parallel while the callback handleWallet was already called // We need arrOtherCosigners to make sure all cosigners know the pubkeys of all other cosigners, even when they were not paired. // For example, there are 3 cosigners: A (me), B, and C. A is paired with B, A is paired with C, but B is not paired with C. - device.readCorrespondentsByDeviceAddresses(arrDeviceAddresses, function(arrOtherCosigners){ + device.readCorrespondentsByDeviceAddresses(arrDeviceAddresses, arrOtherCosigners => { if (arrOtherCosigners.length !== arrDeviceAddresses.length - 1) throw Error("incorrect length of other cosigners"); - arrDeviceAddresses.forEach(function(device_address){ + arrDeviceAddresses.forEach(device_address => { if (device_address === device.getMyDeviceAddress()) return; - console.log("sending offer to "+device_address); + console.log(`sending offer to ${device_address}`); sendOfferToCreateNewWallet(device_address, wallet, arrWalletDefinitionTemplate, walletName, arrOtherCosigners, isSingleAddress, null); sendMyXPubKey(device_address, wallet, xPubKey); }); @@ -259,25 +258,25 @@ function createWallet(xPubKey, account, arrWalletDefinitionTemplate, walletName, function createMultisigWallet(xPubKey, account, count_required_signatures, arrDeviceAddresses, walletName, isSingleAddress, handleWallet){ if (count_required_signatures > arrDeviceAddresses.length) throw Error("required > length"); - var set = arrDeviceAddresses.map(function(device_address){ return ["sig", {pubkey: '$pubkey@'+device_address}]; }); - var arrDefinitionTemplate = ["r of set", {required: count_required_signatures, set: set}]; + const set = arrDeviceAddresses.map(device_address => ["sig", {pubkey: `$pubkey@${device_address}`}]); + const arrDefinitionTemplate = ["r of set", {required: count_required_signatures, set}]; createWallet(xPubKey, account, arrDefinitionTemplate, walletName, isSingleAddress, handleWallet); } // walletName will not be used function createSinglesigWallet(xPubKey, account, walletName, handleWallet){ - var arrDefinitionTemplate = ["sig", {pubkey: '$pubkey@'+device.getMyDeviceAddress()}]; + const arrDefinitionTemplate = ["sig", {pubkey: `$pubkey@${device.getMyDeviceAddress()}`}]; createWallet(xPubKey, account, arrDefinitionTemplate, walletName, null, handleWallet); } function createSinglesigWalletWithExternalPrivateKey(xPubKey, account, device_address, handleWallet){ - var arrDefinitionTemplate = ["sig", {pubkey: '$pubkey@'+device_address}]; + const arrDefinitionTemplate = ["sig", {pubkey: `$pubkey@${device_address}`}]; createWallet(xPubKey, account, arrDefinitionTemplate, 'unused wallet name', null, handleWallet); } // called from UI function createWalletByDevices(xPubKey, account, count_required_signatures, arrOtherDeviceAddresses, walletName, isSingleAddress, handleWallet){ - console.log('createWalletByDevices: xPubKey='+xPubKey+", account="+account); + console.log(`createWalletByDevices: xPubKey=${xPubKey}, account=${account}`); if (arrOtherDeviceAddresses.length === 0) createSinglesigWallet(xPubKey, account, walletName, handleWallet); else @@ -287,10 +286,10 @@ function createWalletByDevices(xPubKey, account, count_required_signatures, arrO // called from UI after user confirms creation of wallet initiated by another device function approveWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, arrOtherCosigners, onDone){ - var arrDeviceAddresses = getDeviceAddresses(arrWalletDefinitionTemplate); - device.addIndirectCorrespondents(arrOtherCosigners, function(){ - addWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, function(){ - arrDeviceAddresses.forEach(function(device_address){ + const arrDeviceAddresses = getDeviceAddresses(arrWalletDefinitionTemplate); + device.addIndirectCorrespondents(arrOtherCosigners, () => { + addWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, () => { + arrDeviceAddresses.forEach(device_address => { if (device_address !== device.getMyDeviceAddress()) sendMyXPubKey(device_address, wallet, xPubKey); }); @@ -302,41 +301,41 @@ function approveWallet(wallet, xPubKey, account, arrWalletDefinitionTemplate, ar // called from UI function cancelWallet(wallet, arrDeviceAddresses, arrOtherCosigners){ - console.log("canceling wallet "+wallet); + console.log(`canceling wallet ${wallet}`); // some of the cosigners might not be paired /* arrDeviceAddresses.forEach(function(device_address){ if (device_address !== device.getMyDeviceAddress()) sendCommandToCancelNewWallet(device_address, wallet); });*/ - var arrOtherDeviceAddresses = _.uniq(arrOtherCosigners.map(function(cosigner){ return cosigner.device_address; })); - var arrInitiatorDeviceAddresses = _.difference(arrDeviceAddresses, arrOtherDeviceAddresses); + const arrOtherDeviceAddresses = _.uniq(arrOtherCosigners.map(({device_address}) => device_address)); + const arrInitiatorDeviceAddresses = _.difference(arrDeviceAddresses, arrOtherDeviceAddresses); if (arrInitiatorDeviceAddresses.length !== 1) throw Error("not one initiator?"); - var initiator_device_address = arrInitiatorDeviceAddresses[0]; + const initiator_device_address = arrInitiatorDeviceAddresses[0]; sendCommandToCancelNewWallet(initiator_device_address, wallet); - arrOtherCosigners.forEach(function(cosigner){ - if (cosigner.device_address === device.getMyDeviceAddress()) + arrOtherCosigners.forEach(({device_address, hub, pubkey}) => { + if (device_address === device.getMyDeviceAddress()) return; // can't use device.sendMessageToDevice because some of the proposed cosigners might not be paired - device.sendMessageToHub(cosigner.hub, cosigner.pubkey, "cancel_new_wallet", {wallet: wallet}); + device.sendMessageToHub(hub, pubkey, "cancel_new_wallet", {wallet}); }); - db.query("DELETE FROM extended_pubkeys WHERE wallet=?", [wallet], function(){ - db.query("DELETE FROM wallet_signing_paths WHERE wallet=?", [wallet], function(){}); + db.query("DELETE FROM extended_pubkeys WHERE wallet=?", [wallet], () => { + db.query("DELETE FROM wallet_signing_paths WHERE wallet=?", [wallet], () => {}); }); } // called from network, without user interaction // One of the proposed cosigners declined wallet creation function deleteWallet(wallet, rejector_device_address, onDone){ - db.query("SELECT approval_date FROM extended_pubkeys WHERE wallet=? AND device_address=?", [wallet, rejector_device_address], function(rows){ + db.query("SELECT approval_date FROM extended_pubkeys WHERE wallet=? AND device_address=?", [wallet, rejector_device_address], rows => { if (rows.length === 0) // you are not a member device return onDone(); if (rows[0].approval_date) // you've already approved this wallet, you can't change your mind return onDone(); - db.query("SELECT device_address FROM extended_pubkeys WHERE wallet=?", [wallet], function(rows){ - var arrMemberAddresses = rows.map(function(row){ return row.device_address; }); - var arrQueries = []; + db.query("SELECT device_address FROM extended_pubkeys WHERE wallet=?", [wallet], rows => { + const arrMemberAddresses = rows.map(({device_address}) => device_address); + const arrQueries = []; db.addQuery(arrQueries, "DELETE FROM extended_pubkeys WHERE wallet=?", [wallet]); db.addQuery(arrQueries, "DELETE FROM wallet_signing_paths WHERE wallet=?", [wallet]); db.addQuery(arrQueries, "DELETE FROM wallets WHERE wallet=?", [wallet]); @@ -348,7 +347,7 @@ function deleteWallet(wallet, rejector_device_address, onDone){ )", [arrMemberAddresses] ); - async.series(arrQueries, function(){ + async.series(arrQueries, () => { eventBus.emit('wallet_declined', wallet, rejector_device_address); onDone(); }); @@ -359,13 +358,13 @@ function deleteWallet(wallet, rejector_device_address, onDone){ // called from network, without user interaction function addDeviceXPubKey(wallet, device_address, xPubKey, onDone){ db.query( - "INSERT "+db.getIgnore()+" INTO extended_pubkeys (wallet, device_address) VALUES(?,?)", + `INSERT ${db.getIgnore()} INTO extended_pubkeys (wallet, device_address) VALUES(?,?)`, [wallet, device_address], - function(){ + () => { db.query( - "UPDATE extended_pubkeys SET extended_pubkey=?, approval_date="+db.getNow()+" WHERE wallet=? AND device_address=?", + `UPDATE extended_pubkeys SET extended_pubkey=?, approval_date=${db.getNow()} WHERE wallet=? AND device_address=?`, [xPubKey, wallet, device_address], - function(){ + () => { eventBus.emit('wallet_approved', wallet, device_address); checkAndFullyApproveWallet(wallet, onDone); } @@ -377,13 +376,13 @@ function addDeviceXPubKey(wallet, device_address, xPubKey, onDone){ // called from network, without user interaction function handleNotificationThatWalletFullyApproved(wallet, device_address, onDone){ db.query( // just in case it was not inserted yet - "INSERT "+db.getIgnore()+" INTO extended_pubkeys (wallet, device_address) VALUES(?,?)", + `INSERT ${db.getIgnore()} INTO extended_pubkeys (wallet, device_address) VALUES(?,?)`, [wallet, device_address], - function(){ + () => { db.query( - "UPDATE extended_pubkeys SET member_ready_date="+db.getNow()+" WHERE wallet=? AND device_address=?", + `UPDATE extended_pubkeys SET member_ready_date=${db.getNow()} WHERE wallet=? AND device_address=?`, [wallet, device_address], - function(){ + () => { checkAndFinalizeWallet(wallet, onDone); } ); @@ -396,15 +395,15 @@ function readCosigners(wallet, handleCosigners){ "SELECT extended_pubkeys.device_address, name, approval_date, extended_pubkey \n\ FROM extended_pubkeys LEFT JOIN correspondent_devices USING(device_address) WHERE wallet=?", [wallet], - function(rows){ - rows.forEach(function(row){ + rows => { + rows.forEach(row => { if (row.device_address === device.getMyDeviceAddress()){ if (row.name !== null) throw Error("found self in correspondents"); row.me = true; } else if (row.name === null) - throw Error("cosigner not found among correspondents, cosigner="+row.device_address+", my="+device.getMyDeviceAddress()); + throw Error(`cosigner not found among correspondents, cosigner=${row.device_address}, my=${device.getMyDeviceAddress()}`); }); handleCosigners(rows); } @@ -413,14 +412,14 @@ function readCosigners(wallet, handleCosigners){ // silently adds new address upon receiving a network message function addNewAddress(wallet, is_change, address_index, address, handleError){ - breadcrumbs.add('addNewAddress is_change='+is_change+', index='+address_index+', address='+address); - db.query("SELECT 1 FROM wallets WHERE wallet=?", [wallet], function(rows){ - if (rows.length === 0) - return handleError("wallet "+wallet+" does not exist"); - deriveAddress(wallet, is_change, address_index, function(new_address, arrDefinition){ + breadcrumbs.add(`addNewAddress is_change=${is_change}, index=${address_index}, address=${address}`); + db.query("SELECT 1 FROM wallets WHERE wallet=?", [wallet], ({length}) => { + if (length === 0) + return handleError(`wallet ${wallet} does not exist`); + deriveAddress(wallet, is_change, address_index, (new_address, arrDefinition) => { if (new_address !== address) - return handleError("I derived address "+new_address+", your address "+address); - recordAddress(wallet, is_change, address_index, address, arrDefinition, function(){ + return handleError(`I derived address ${new_address}, your address ${address}`); + recordAddress(wallet, is_change, address_index, address, arrDefinition, () => { eventBus.emit("new_wallet_address", address); handleError(); }); @@ -434,11 +433,11 @@ function getDeviceAddresses(arrWalletDefinitionTemplate){ function getDeviceAddressesBySigningPaths(arrWalletDefinitionTemplate){ function evaluate(arr, path){ - var op = arr[0]; - var args = arr[1]; + const op = arr[0]; + const args = arr[1]; if (!args) return; - var prefix = '$pubkey@'; + const prefix = '$pubkey@'; switch (op){ case 'sig': if (!args.pubkey || args.pubkey.substr(0, prefix.length) !== prefix) @@ -455,23 +454,23 @@ function getDeviceAddressesBySigningPaths(arrWalletDefinitionTemplate){ case 'or': case 'and': for (var i=0; i { + params[`pubkey@${device_address}`] = device.getMyDevicePubKey(); }); try{ var arrFakeDefinition = Definition.replaceInTemplate(arrWalletDefinitionTemplate, params); @@ -498,9 +497,9 @@ function validateWalletDefinitionTemplate(arrWalletDefinitionTemplate, from_addr catch(e){ return handleResult(e.toString()); } - var objFakeUnit = {authors: []}; - var objFakeValidationState = {last_ball_mci: MAX_INT32}; - Definition.validateDefinition(db, arrFakeDefinition, objFakeUnit, objFakeValidationState, null, false, function(err){ + const objFakeUnit = {authors: []}; + const objFakeValidationState = {last_ball_mci: MAX_INT32}; + Definition.validateDefinition(db, arrFakeDefinition, objFakeUnit, objFakeValidationState, null, false, err => { if (err) return handleResult(err); handleResult(null, arrDeviceAddresses); @@ -511,8 +510,8 @@ function validateWalletDefinitionTemplate(arrWalletDefinitionTemplate, from_addr function readNextAddressIndex(wallet, is_change, handleNextAddressIndex){ - db.query("SELECT MAX(address_index) AS last_used_index FROM my_addresses WHERE wallet=? AND is_change=?", [wallet, is_change], function(rows){ - var last_used_index = rows[0].last_used_index; + db.query("SELECT MAX(address_index) AS last_used_index FROM my_addresses WHERE wallet=? AND is_change=?", [wallet, is_change], rows => { + const last_used_index = rows[0].last_used_index; handleNextAddressIndex( (last_used_index === null) ? 0 : (last_used_index+1) ); }); } @@ -522,39 +521,39 @@ function readLastUsedAddressIndex(wallet, is_change, handleLastUsedAddressIndex) db.query( "SELECT MAX(address_index) AS last_used_index FROM my_addresses JOIN outputs USING(address) WHERE wallet=? AND is_change=?", [wallet, is_change], - function(rows){ - var last_used_index = rows[0].last_used_index; + rows => { + const last_used_index = rows[0].last_used_index; handleLastUsedAddressIndex(last_used_index); } ); } function derivePubkey(xPubKey, path){ - var hdPubKey = new Bitcore.HDPublicKey(xPubKey); + const hdPubKey = new Bitcore.HDPublicKey(xPubKey); return hdPubKey.derive(path).publicKey.toBuffer().toString("base64"); } function deriveAddress(wallet, is_change, address_index, handleNewAddress){ - db.query("SELECT definition_template, full_approval_date FROM wallets WHERE wallet=?", [wallet], function(wallet_rows){ + db.query("SELECT definition_template, full_approval_date FROM wallets WHERE wallet=?", [wallet], wallet_rows => { if (wallet_rows.length === 0) - throw Error("wallet not found: "+wallet+", is_change="+is_change+", index="+address_index); + throw Error(`wallet not found: ${wallet}, is_change=${is_change}, index=${address_index}`); if (!wallet_rows[0].full_approval_date) - throw Error("wallet not fully approved yet: "+wallet); - var arrDefinitionTemplate = JSON.parse(wallet_rows[0].definition_template); + throw Error(`wallet not fully approved yet: ${wallet}`); + const arrDefinitionTemplate = JSON.parse(wallet_rows[0].definition_template); db.query( "SELECT device_address, extended_pubkey FROM extended_pubkeys WHERE wallet=?", [wallet], - function(rows){ - var path = "m/"+is_change+"/"+address_index; - var params = {}; - rows.forEach(function(row){ - if (!row.extended_pubkey) - throw Error("no extended_pubkey for wallet "+wallet); - params['pubkey@'+row.device_address] = derivePubkey(row.extended_pubkey, path); - console.log('pubkey for wallet '+wallet+' path '+path+' device '+row.device_address+' xpub '+row.extended_pubkey+': '+params['pubkey@'+row.device_address]); + rows => { + const path = `m/${is_change}/${address_index}`; + const params = {}; + rows.forEach(({extended_pubkey, device_address}) => { + if (!extended_pubkey) + throw Error(`no extended_pubkey for wallet ${wallet}`); + params[`pubkey@${device_address}`] = derivePubkey(extended_pubkey, path); + console.log(`pubkey for wallet ${wallet} path ${path} device ${device_address} xpub ${extended_pubkey}: ${params[`pubkey@${device_address}`]}`); }); - var arrDefinition = Definition.replaceInTemplate(arrDefinitionTemplate, params); - var address = objectHash.getChash160(arrDefinition); + const arrDefinition = Definition.replaceInTemplate(arrDefinitionTemplate, params); + const address = objectHash.getChash160(arrDefinition); handleNewAddress(address, arrDefinition); } ); @@ -564,12 +563,12 @@ function deriveAddress(wallet, is_change, address_index, handleNewAddress){ function recordAddress(wallet, is_change, address_index, address, arrDefinition, onDone){ if (typeof address_index === 'string' && is_change) throw Error("address with string index cannot be change address"); - var address_index_column_name = (typeof address_index === 'string') ? 'app' : 'address_index'; + const address_index_column_name = (typeof address_index === 'string') ? 'app' : 'address_index'; db.query( // IGNORE in case the address was already generated - "INSERT "+db.getIgnore()+" INTO my_addresses (wallet, is_change, "+address_index_column_name+", address, definition) VALUES (?,?,?,?,?)", + `INSERT ${db.getIgnore()} INTO my_addresses (wallet, is_change, ${address_index_column_name}, address, definition) VALUES (?,?,?,?,?)`, [wallet, is_change, address_index, address, JSON.stringify(arrDefinition)], - function(){ - eventBus.emit("new_address-"+address); + () => { + eventBus.emit(`new_address-${address}`); if (onDone) onDone(); // network.addWatchedAddress(address); @@ -580,25 +579,25 @@ function recordAddress(wallet, is_change, address_index, address, arrDefinition, } function deriveAndRecordAddress(wallet, is_change, address_index, handleNewAddress){ - deriveAddress(wallet, is_change, address_index, function(address, arrDefinition){ - recordAddress(wallet, is_change, address_index, address, arrDefinition, function(){ + deriveAddress(wallet, is_change, address_index, (address, arrDefinition) => { + recordAddress(wallet, is_change, address_index, address, arrDefinition, () => { handleNewAddress(address); }); }); } function issueAddress(wallet, is_change, address_index, handleNewAddress){ - breadcrumbs.add('issueAddress wallet='+wallet+', is_change='+is_change+', index='+address_index); - deriveAndRecordAddress(wallet, is_change, address_index, function(address){ - db.query("SELECT device_address FROM extended_pubkeys WHERE wallet=?", [wallet], function(rows){ - rows.forEach(function(row){ - if (row.device_address !== device.getMyDeviceAddress()) - sendNewWalletAddress(row.device_address, wallet, is_change, address_index, address); + breadcrumbs.add(`issueAddress wallet=${wallet}, is_change=${is_change}, index=${address_index}`); + deriveAndRecordAddress(wallet, is_change, address_index, address => { + db.query("SELECT device_address FROM extended_pubkeys WHERE wallet=?", [wallet], rows => { + rows.forEach(({device_address}) => { + if (device_address !== device.getMyDeviceAddress()) + sendNewWalletAddress(device_address, wallet, is_change, address_index, address); }); - handleNewAddress({address: address, is_change: is_change, address_index: address_index, creation_ts: parseInt(Date.now()/1000)}); + handleNewAddress({address, is_change, address_index, creation_ts: parseInt(Date.now()/1000)}); }); }); - setTimeout(function(){ + setTimeout(() => { checkAddress(0, 0, 0); }, 5000); } @@ -606,10 +605,10 @@ function issueAddress(wallet, is_change, address_index, handleNewAddress){ function readAddressByIndex(wallet, is_change, address_index, handleAddress){ db.query( - "SELECT address, address_index, "+db.getUnixTimestamp("creation_date")+" AS creation_ts \n\ - FROM my_addresses WHERE wallet=? AND is_change=? AND address_index=?", + `SELECT address, address_index, ${db.getUnixTimestamp("creation_date")} AS creation_ts \n\ + FROM my_addresses WHERE wallet=? AND is_change=? AND address_index=?`, [wallet, is_change, address_index], - function(rows){ + rows => { handleAddress(rows[0]); } ); @@ -619,19 +618,19 @@ function selectRandomAddress(wallet, is_change, from_index, handleAddress){ if (from_index === null) from_index = -1; db.query( - "SELECT address, address_index, "+db.getUnixTimestamp("creation_date")+" AS creation_ts \n\ - FROM my_addresses WHERE wallet=? AND is_change=? AND address_index>? ORDER BY "+db.getRandom()+" LIMIT 1", + `SELECT address, address_index, ${db.getUnixTimestamp("creation_date")} AS creation_ts \n\ + FROM my_addresses WHERE wallet=? AND is_change=? AND address_index>? ORDER BY ${db.getRandom()} LIMIT 1`, [wallet, is_change, from_index], - function(rows){ + rows => { handleAddress(rows[0]); } ); } function issueNextAddress(wallet, is_change, handleAddress){ - mutex.lock(['issueNextAddress'], function(unlock){ - readNextAddressIndex(wallet, is_change, function(next_index){ - issueAddress(wallet, is_change, next_index, function(addressInfo){ + mutex.lock(['issueNextAddress'], unlock => { + readNextAddressIndex(wallet, is_change, next_index => { + issueAddress(wallet, is_change, next_index, addressInfo => { handleAddress(addressInfo); unlock(); }); @@ -641,10 +640,10 @@ function issueNextAddress(wallet, is_change, handleAddress){ // selects one of recent addresses if the gap is too large, otherwise issues a new address function issueOrSelectNextAddress(wallet, is_change, handleAddress){ - readNextAddressIndex(wallet, is_change, function(next_index){ + readNextAddressIndex(wallet, is_change, next_index => { if (next_index < MAX_BIP44_GAP) return issueAddress(wallet, is_change, next_index, handleAddress); - readLastUsedAddressIndex(wallet, is_change, function(last_used_index){ + readLastUsedAddressIndex(wallet, is_change, last_used_index => { if (last_used_index === null || next_index - last_used_index >= MAX_BIP44_GAP) selectRandomAddress(wallet, is_change, last_used_index, handleAddress); else @@ -654,9 +653,9 @@ function issueOrSelectNextAddress(wallet, is_change, handleAddress){ } function issueOrSelectNextChangeAddress(wallet, handleAddress){ - readNextAddressIndex(wallet, 1, function(next_index){ - readLastUsedAddressIndex(wallet, 1, function(last_used_index){ - var first_unused_index = (last_used_index === null) ? 0 : (last_used_index + 1); + readNextAddressIndex(wallet, 1, next_index => { + readLastUsedAddressIndex(wallet, 1, last_used_index => { + const first_unused_index = (last_used_index === null) ? 0 : (last_used_index + 1); if (first_unused_index > next_index) throw Error("unued > next") if (first_unused_index < next_index) @@ -668,57 +667,57 @@ function issueOrSelectNextChangeAddress(wallet, handleAddress){ } function issueOrSelectAddressForApp(wallet, app_name, handleAddress){ - db.query("SELECT address FROM my_addresses WHERE wallet=? AND app=?", [wallet, app_name], function(rows){ + db.query("SELECT address FROM my_addresses WHERE wallet=? AND app=?", [wallet, app_name], rows => { if (rows.length > 1) - throw Error("more than 1 address for app "+app_name); + throw Error(`more than 1 address for app ${app_name}`); if (rows.length === 1) return handleAddress(rows[0].address); - issueAddress(wallet, 0, app_name, function(addressInfo){ - handleAddress(addressInfo.address); + issueAddress(wallet, 0, app_name, ({address}) => { + handleAddress(address); }); }); } function checkAddress(account, is_change, address_index){ - db.query("SELECT wallet, extended_pubkey FROM wallets JOIN extended_pubkeys USING(wallet) WHERE account=?", [account], function(rows){ + db.query("SELECT wallet, extended_pubkey FROM wallets JOIN extended_pubkeys USING(wallet) WHERE account=?", [account], rows => { if (rows.length === 0 || rows.length > 1) return; - var row = rows[0]; - var pubkey = derivePubkey(row.extended_pubkey, "m/"+is_change+"/"+address_index); - var arrDefinition = ['sig', {pubkey: pubkey}]; - var address = objectHash.getChash160(arrDefinition); + const row = rows[0]; + const pubkey = derivePubkey(row.extended_pubkey, `m/${is_change}/${address_index}`); + const arrDefinition = ['sig', {pubkey}]; + const address = objectHash.getChash160(arrDefinition); db.query( "SELECT address, definition FROM my_addresses WHERE wallet=? AND is_change=? AND address_index=?", [row.wallet, is_change, address_index], - function(address_rows){ + address_rows => { if (address_rows.length === 0) return; - var address_row = address_rows[0]; - var db_pubkey = JSON.parse(address_row.definition)[1].pubkey; + const address_row = address_rows[0]; + const db_pubkey = JSON.parse(address_row.definition)[1].pubkey; if (db_pubkey !== pubkey) - throw Error("pubkey mismatch, derived: "+pubkey+", db: "+db_pubkey); + throw Error(`pubkey mismatch, derived: ${pubkey}, db: ${db_pubkey}`); if (address_row.address !== address) - throw Error("address mismatch, derived: "+address+", db: "+address_row.address); + throw Error(`address mismatch, derived: ${address}, db: ${address_row.address}`); breadcrumbs.add("addresses match"); } ); }); } -function readAddresses(wallet, opts, handleAddresses){ - var sql = "SELECT address, address_index, is_change, "+db.getUnixTimestamp("creation_date")+" AS creation_ts \n\ - FROM my_addresses WHERE wallet=?"; - if (opts.is_change === 0 || opts.is_change === 1) - sql += " AND is_change="+opts.is_change; +function readAddresses(wallet, {is_change, reverse, limit}, handleAddresses) { + let sql = `SELECT address, address_index, is_change, ${db.getUnixTimestamp("creation_date")} AS creation_ts \n\ + FROM my_addresses WHERE wallet=?`; + if (is_change === 0 || is_change === 1) + sql += ` AND is_change=${is_change}`; sql += " ORDER BY creation_ts"; - if (opts.reverse) + if (reverse) sql += " DESC"; - if (opts.limit) - sql += " LIMIT "+opts.limit; + if (limit) + sql += ` LIMIT ${limit}`; db.query( sql, [wallet], - function(rows){ + rows => { handleAddresses(rows); } ); @@ -736,9 +735,9 @@ function readChangeAddresses(wallet, handleAddresses){ // unused so far function readAddressInfo(address, handleAddress){ - db.query("SELECT address_index, is_change FROM my_addresses WHERE address=?", [address], function(rows){ + db.query("SELECT address_index, is_change FROM my_addresses WHERE address=?", [address], rows => { if (rows.length === 0) - return handleAddress("address "+address+" not found"); + return handleAddress(`address ${address} not found`); handleAddress(null, rows[0]); }); } @@ -747,8 +746,8 @@ function readAllAddresses(wallet, handleAddresses){ db.query( "SELECT address FROM my_addresses WHERE wallet=?", [wallet], - function(rows){ - handleAddresses(rows.map(function(row){ return row.address; })); + rows => { + handleAddresses(rows.map(({address}) => address)); } ); } @@ -765,8 +764,8 @@ function forwardPrivateChainsToOtherMembersOfWallets(arrChains, arrWallets, conn conn.query( "SELECT device_address FROM extended_pubkeys WHERE wallet IN(?) AND device_address!=?", [arrWallets, device.getMyDeviceAddress()], - function(rows){ - var arrDeviceAddresses = rows.map(function(row){ return row.device_address; }); + rows => { + const arrDeviceAddresses = rows.map(({device_address}) => device_address); walletGeneral.forwardPrivateChainsToDevices(arrDeviceAddresses, arrChains, true, conn, onSaved); } ); @@ -779,8 +778,8 @@ function readDeviceAddressesControllingPaymentAddresses(conn, arrAddresses, hand conn.query( "SELECT DISTINCT device_address FROM my_addresses JOIN extended_pubkeys USING(wallet) WHERE address IN(?) AND device_address!=?", [arrAddresses, device.getMyDeviceAddress()], - function(rows){ - var arrDeviceAddresses = rows.map(function(row){ return row.device_address; }); + rows => { + const arrDeviceAddresses = rows.map(({device_address}) => device_address); handleDeviceAddresses(arrDeviceAddresses); } ); @@ -789,7 +788,7 @@ function readDeviceAddressesControllingPaymentAddresses(conn, arrAddresses, hand function forwardPrivateChainsToOtherMembersOfAddresses(arrChains, arrAddresses, conn, onSaved){ console.log("forwardPrivateChainsToOtherMembersOfAddresses", arrAddresses); conn = conn || db; - readDeviceAddressesControllingPaymentAddresses(conn, arrAddresses, function(arrDeviceAddresses){ + readDeviceAddressesControllingPaymentAddresses(conn, arrAddresses, arrDeviceAddresses => { walletGeneral.forwardPrivateChainsToDevices(arrDeviceAddresses, arrChains, true, conn, onSaved); }); } diff --git a/wallet_general.js b/wallet_general.js index 65d0a0cd..f5249e2c 100644 --- a/wallet_general.js +++ b/wallet_general.js @@ -1,13 +1,12 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var db = require('./db.js'); -var device = require('./device.js'); +const async = require('async'); +const db = require('./db.js'); +const device = require('./device.js'); function sendOfferToSign(device_address, address, signing_path, objUnsignedUnit, assocPrivatePayloads){ - var body = {address: address, signing_path: signing_path, unsigned_unit: objUnsignedUnit}; + const body = {address, signing_path, unsigned_unit: objUnsignedUnit}; if (assocPrivatePayloads && Object.keys(assocPrivatePayloads).length > 0) body.private_payloads = assocPrivatePayloads; device.sendMessageToDevice(device_address, "sign", body); @@ -15,22 +14,22 @@ function sendOfferToSign(device_address, address, signing_path, objUnsignedUnit, // unlike similar function in network, this function sends multiple chains in a single package function sendPrivatePayments(device_address, arrChains, bForwarded, conn, onSaved){ - var body = {chains: arrChains}; + const body = {chains: arrChains}; if (bForwarded) body.forwarded = true; device.sendMessageToDevice(device_address, "private_payments", body, { - ifOk: function(){}, - ifError: function(){}, - onSaved: onSaved + ifOk() {}, + ifError() {}, + onSaved }, conn); } function forwardPrivateChainsToDevices(arrDeviceAddresses, arrChains, bForwarded, conn, onSaved){ - console.log("devices: "+arrDeviceAddresses); + console.log(`devices: ${arrDeviceAddresses}`); async.eachSeries( arrDeviceAddresses, - function(device_address, cb){ - console.log("forwarding to device "+device_address); + (device_address, cb) => { + console.log(`forwarding to device ${device_address}`); sendPrivatePayments(device_address, arrChains, bForwarded, conn, cb); }, onSaved @@ -46,8 +45,8 @@ function sendPaymentNotification(device_address, unit){ function readMyAddresses(handleAddresses){ db.query("SELECT address FROM my_addresses \n\ UNION SELECT shared_address AS address FROM shared_addresses \n\ - UNION SELECT address FROM sent_mnemonics LEFT JOIN unit_authors USING(address) WHERE unit_authors.unit IS NULL", function(rows){ - var arrAddresses = rows.map(function(row){ return row.address; }); + UNION SELECT address FROM sent_mnemonics LEFT JOIN unit_authors USING(address) WHERE unit_authors.unit IS NULL", rows => { + const arrAddresses = rows.map(({address}) => address); handleAddresses(arrAddresses); }); } diff --git a/witness_proof.js b/witness_proof.js index b72bdb19..4d0e60a2 100644 --- a/witness_proof.js +++ b/witness_proof.js @@ -1,40 +1,39 @@ /*jslint node: true */ -"use strict"; -var async = require('async'); -var storage = require('./storage.js'); -var myWitnesses = require('./my_witnesses.js'); -var objectHash = require("./object_hash.js"); -var db = require('./db.js'); -var constants = require("./constants.js"); -var validation = require('./validation.js'); +const async = require('async'); +const storage = require('./storage.js'); +const myWitnesses = require('./my_witnesses.js'); +const objectHash = require("./object_hash.js"); +const db = require('./db.js'); +const constants = require("./constants.js"); +const validation = require('./validation.js'); function prepareWitnessProof(arrWitnesses, last_stable_mci, handleResult){ - var arrWitnessChangeAndDefinitionJoints = []; - var arrUnstableMcJoints = []; + const arrWitnessChangeAndDefinitionJoints = []; + const arrUnstableMcJoints = []; - var arrLastBallUnits = []; // last ball units referenced from MC-majority-witnessed unstable MC units - var last_ball_unit = null; - var last_ball_mci = null; + const arrLastBallUnits = []; // last ball units referenced from MC-majority-witnessed unstable MC units + let last_ball_unit = null; + let last_ball_mci = null; async.series([ - function(cb){ - storage.determineIfWitnessAddressDefinitionsHaveReferences(db, arrWitnesses, function(bWithReferences){ + cb => { + storage.determineIfWitnessAddressDefinitionsHaveReferences(db, arrWitnesses, bWithReferences => { bWithReferences ? cb("some witnesses have references in their addresses, please change your witness list") : cb(); }); }, - function(cb){ // collect all unstable MC units - var arrFoundWitnesses = []; + cb => { // collect all unstable MC units + const arrFoundWitnesses = []; db.query( "SELECT unit FROM units WHERE is_on_main_chain=1 AND is_stable=0 ORDER BY main_chain_index DESC", - function(rows){ - async.eachSeries(rows, function(row, cb2){ - storage.readJointWithBall(db, row.unit, function(objJoint){ + rows => { + async.eachSeries(rows, ({unit}, cb2) => { + storage.readJointWithBall(db, unit, objJoint => { delete objJoint.ball; // the unit might get stabilized while we were reading other units arrUnstableMcJoints.push(objJoint); - for (var i=0; i= 0 && arrFoundWitnesses.indexOf(address) === -1) arrFoundWitnesses.push(address); } @@ -48,17 +47,17 @@ function prepareWitnessProof(arrWitnesses, last_stable_mci, handleResult){ } ); }, - function(cb){ // select the newest last ball unit + cb => { // select the newest last ball unit if (arrLastBallUnits.length === 0) return cb("your witness list might be too much off, too few witness authored units"); - db.query("SELECT unit, main_chain_index FROM units WHERE unit IN(?) ORDER BY main_chain_index DESC LIMIT 1", [arrLastBallUnits], function(rows){ + db.query("SELECT unit, main_chain_index FROM units WHERE unit IN(?) ORDER BY main_chain_index DESC LIMIT 1", [arrLastBallUnits], rows => { last_ball_unit = rows[0].unit; last_ball_mci = rows[0].main_chain_index; (last_stable_mci >= last_ball_mci) ? cb("already_current") : cb(); }); }, - function(cb){ // add definition changes and new definitions of witnesses - var after_last_stable_mci_cond = (last_stable_mci > 0) ? "latest_included_mc_index>="+last_stable_mci : "1"; + cb => { // add definition changes and new definitions of witnesses + const after_last_stable_mci_cond = (last_stable_mci > 0) ? `latest_included_mc_index>=${last_stable_mci}` : "1"; db.query( /*"SELECT DISTINCT units.unit \n\ FROM unit_authors \n\ @@ -69,24 +68,24 @@ function prepareWitnessProof(arrWitnesses, last_stable_mci, handleResult){ AND (unit_authors.definition_chash IS NOT NULL OR address_definition_changes.unit IS NOT NULL) \n\ ORDER BY `level`", [arrWitnesses],*/ - "SELECT unit, `level` \n\ - FROM unit_authors "+db.forceIndex('unitAuthorsIndexByAddressDefinitionChash')+" \n\ - CROSS JOIN units USING(unit) \n\ - WHERE address IN(?) AND definition_chash IS NOT NULL AND "+after_last_stable_mci_cond+" AND is_stable=1 AND sequence='good' \n\ - UNION \n\ - SELECT unit, `level` \n\ - FROM address_definition_changes \n\ - CROSS JOIN units USING(unit) \n\ - WHERE address_definition_changes.address IN(?) AND "+after_last_stable_mci_cond+" AND is_stable=1 AND sequence='good' \n\ - ORDER BY `level`", + `SELECT unit, \`level\` \n\ + FROM unit_authors ${db.forceIndex('unitAuthorsIndexByAddressDefinitionChash')} \n\ + CROSS JOIN units USING(unit) \n\ + WHERE address IN(?) AND definition_chash IS NOT NULL AND ${after_last_stable_mci_cond} AND is_stable=1 AND sequence='good' \n\ + UNION \n\ + SELECT unit, \`level\` \n\ + FROM address_definition_changes \n\ + CROSS JOIN units USING(unit) \n\ + WHERE address_definition_changes.address IN(?) AND ${after_last_stable_mci_cond} AND is_stable=1 AND sequence='good' \n\ + ORDER BY \`level\``, [arrWitnesses, arrWitnesses], - function(rows){ - async.eachSeries(rows, function(row, cb2){ - storage.readJoint(db, row.unit, { - ifNotFound: function(){ - throw Error("prepareWitnessProof definition changes: not found "+row.unit); + rows => { + async.eachSeries(rows, ({unit}, cb2) => { + storage.readJoint(db, unit, { + ifNotFound() { + throw Error(`prepareWitnessProof definition changes: not found ${unit}`); }, - ifFound: function(objJoint){ + ifFound(objJoint) { arrWitnessChangeAndDefinitionJoints.push(objJoint); cb2(); } @@ -95,7 +94,7 @@ function prepareWitnessProof(arrWitnesses, last_stable_mci, handleResult){ } ); } - ], function(err){ + ], err => { if (err) return handleResult(err); handleResult(null, arrUnstableMcJoints, arrWitnessChangeAndDefinitionJoints, last_ball_unit, last_ball_mci); @@ -105,14 +104,14 @@ function prepareWitnessProof(arrWitnesses, last_stable_mci, handleResult){ function processWitnessProof(arrUnstableMcJoints, arrWitnessChangeAndDefinitionJoints, bFromCurrent, handleResult){ - myWitnesses.readMyWitnesses(function(arrWitnesses){ + myWitnesses.readMyWitnesses(arrWitnesses => { // unstable MC joints - var arrParentUnits = null; - var arrFoundWitnesses = []; - var arrLastBallUnits = []; - var assocLastBallByLastBallUnit = {}; - var arrWitnessJoints = []; + let arrParentUnits = null; + const arrFoundWitnesses = []; + const arrLastBallUnits = []; + const assocLastBallByLastBallUnit = {}; + const arrWitnessJoints = []; for (var i=0; i= 0){ @@ -155,7 +154,7 @@ function processWitnessProof(arrUnstableMcJoints, arrWitnessChangeAndDefinitionJ return handleResult("witness_change_and_definition_joints: joint without ball"); if (!validation.hasValidHashes(objJoint)) return handleResult("witness_change_and_definition_joints: invalid hash"); - var bAuthoredByWitness = false; + let bAuthoredByWitness = false; for (var j=0; j= 0) @@ -165,21 +164,21 @@ function processWitnessProof(arrUnstableMcJoints, arrWitnessChangeAndDefinitionJ return handleResult("not authored by my witness"); } - var assocDefinitions = {}; // keyed by definition chash - var assocDefinitionChashes = {}; // keyed by address + const assocDefinitions = {}; // keyed by definition chash + const assocDefinitionChashes = {}; // keyed by address // checks signatures and updates definitions function validateUnit(objUnit, bRequireDefinitionOrChange, cb2){ - var bFound = false; + let bFound = false; async.eachSeries( objUnit.authors, - function(author, cb3){ - var address = author.address; + (author, cb3) => { + const address = author.address; if (arrWitnesses.indexOf(address) === -1) // not a witness - skip it return cb3(); - var definition_chash = assocDefinitionChashes[address]; + const definition_chash = assocDefinitionChashes[address]; if (!definition_chash) - throw Error("definition chash not known for address "+address); + throw Error(`definition chash not known for address ${address}`); if (author.definition){ if (objectHash.getChash160(author.definition) !== definition_chash) return cb3("definition doesn't hash to the expected value"); @@ -189,11 +188,11 @@ function processWitnessProof(arrUnstableMcJoints, arrWitnessChangeAndDefinitionJ function handleAuthor(){ // FIX - validation.validateAuthorSignaturesWithoutReferences(author, objUnit, assocDefinitions[definition_chash], function(err){ + validation.validateAuthorSignaturesWithoutReferences(author, objUnit, assocDefinitions[definition_chash], err => { if (err) return cb3(err); - for (var i=0; i { if (err) return cb2(err); if (bRequireDefinitionOrChange && !bFound) @@ -226,26 +225,26 @@ function processWitnessProof(arrUnstableMcJoints, arrWitnessChangeAndDefinitionJ ); // each authors } - var unlock = null; + const unlock = null; async.series([ - function(cb){ // read latest known definitions of witness addresses + cb => { // read latest known definitions of witness addresses if (!bFromCurrent){ - arrWitnesses.forEach(function(address){ + arrWitnesses.forEach(address => { assocDefinitionChashes[address] = address; }); return cb(); } async.eachSeries( arrWitnesses, - function(address, cb2){ + (address, cb2) => { storage.readDefinitionByAddress(db, address, null, { - ifFound: function(arrDefinition){ - var definition_chash = objectHash.getChash160(arrDefinition); + ifFound(arrDefinition) { + const definition_chash = objectHash.getChash160(arrDefinition); assocDefinitions[definition_chash] = arrDefinition; assocDefinitionChashes[address] = definition_chash; cb2(); }, - ifDefinitionNotFound: function(definition_chash){ + ifDefinitionNotFound(definition_chash) { assocDefinitionChashes[address] = definition_chash; cb2(); } @@ -254,15 +253,15 @@ function processWitnessProof(arrUnstableMcJoints, arrWitnessChangeAndDefinitionJ cb ); }, - function(cb){ // handle changes of definitions + cb => { // handle changes of definitions async.eachSeries( arrWitnessChangeAndDefinitionJoints, - function(objJoint, cb2){ - var objUnit = objJoint.unit; + ({unit}, cb2) => { + const objUnit = unit; if (!bFromCurrent) return validateUnit(objUnit, true, cb2); - db.query("SELECT 1 FROM units WHERE unit=? AND is_stable=1", [objUnit.unit], function(rows){ - if (rows.length > 0) // already known and stable - skip it + db.query("SELECT 1 FROM units WHERE unit=? AND is_stable=1", [objUnit.unit], ({length}) => { + if (length > 0) // already known and stable - skip it return cb2(); validateUnit(objUnit, true, cb2); }); @@ -270,16 +269,16 @@ function processWitnessProof(arrUnstableMcJoints, arrWitnessChangeAndDefinitionJ cb ); // each change or definition }, - function(cb){ // check signatures of unstable witness joints + cb => { // check signatures of unstable witness joints async.eachSeries( arrWitnessJoints.reverse(), // they came in reverse chronological order, reverse() reverses in place - function(objJoint, cb2){ - validateUnit(objJoint.unit, false, cb2); + ({unit}, cb2) => { + validateUnit(unit, false, cb2); }, cb ); }, - ], function(err){ + ], err => { err ? handleResult(err) : handleResult(null, arrLastBallUnits, assocLastBallByLastBallUnit); }); diff --git a/writer.js b/writer.js index dc6b6193..88de4947 100644 --- a/writer.js +++ b/writer.js @@ -1,47 +1,46 @@ /*jslint node: true */ -"use strict"; -var _ = require('lodash'); -var async = require('async'); -var constants = require("./constants.js"); -var conf = require("./conf.js"); -var storage = require('./storage.js'); -var db = require('./db.js'); -var objectHash = require("./object_hash.js"); -var mutex = require('./mutex.js'); -var main_chain = require("./main_chain.js"); -var Definition = require("./definition.js"); -var eventBus = require('./event_bus.js'); -var profiler = require('./profiler.js'); +const _ = require('lodash'); +const async = require('async'); +const constants = require("./constants.js"); +const conf = require("./conf.js"); +const storage = require('./storage.js'); +const db = require('./db.js'); +const objectHash = require("./object_hash.js"); +const mutex = require('./mutex.js'); +const main_chain = require("./main_chain.js"); +const Definition = require("./definition.js"); +const eventBus = require('./event_bus.js'); +const profiler = require('./profiler.js'); -var count_writes = 0; -var count_units_in_prev_analyze = 0; +let count_writes = 0; +let count_units_in_prev_analyze = 0; function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { - var objUnit = objJoint.unit; - console.log("\nsaving unit "+objUnit.unit); + const objUnit = objJoint.unit; + console.log(`\nsaving unit ${objUnit.unit}`); profiler.start(); - db.takeConnectionFromPool(function(conn){ - var arrQueries = []; + db.takeConnectionFromPool(conn => { + const arrQueries = []; conn.addQuery(arrQueries, "BEGIN"); // additional queries generated by the validator, used only when received a doublespend for (var i=0; i { // in sqlite3, result.affectedRows actually returns the number of _matched_ rows - var count_consumed_free_units = result.affectedRows; - console.log(count_consumed_free_units+" free units consumed"); - objUnit.parent_units.forEach(function(parent_unit){ + const count_consumed_free_units = affectedRows; + console.log(`${count_consumed_free_units} free units consumed`); + objUnit.parent_units.forEach(parent_unit => { if (storage.assocUnstableUnits[parent_unit]) storage.assocUnstableUnits[parent_unit].is_free = 0; }) @@ -78,35 +77,35 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { var address = objUnit.witnesses[i]; conn.addQuery(arrQueries, "INSERT INTO unit_witnesses (unit, address) VALUES(?,?)", [objUnit.unit, address]); } - conn.addQuery(arrQueries, "INSERT "+conn.getIgnore()+" INTO witness_list_hashes (witness_list_unit, witness_list_hash) VALUES (?,?)", + conn.addQuery(arrQueries, `INSERT ${conn.getIgnore()} INTO witness_list_hashes (witness_list_unit, witness_list_hash) VALUES (?,?)`, [objUnit.unit, objectHash.getBase64Hash(objUnit.witnesses)]); } - var arrAuthorAddresses = []; + const arrAuthorAddresses = []; for (var i=0; i { if (rows.length > 1) throw Error("multiple src outputs found"); if (rows.length === 0){ @@ -239,12 +238,12 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { else throw Error("src output not found"); } - var row = rows[0]; + const row = rows[0]; if (!(!asset && !row.asset || asset === row.asset)) throw Error("asset doesn't match"); if (denomination !== row.denomination) throw Error("denomination doesn't match"); - var address = row.address; + const address = row.address; if (arrAuthorAddresses.indexOf(address) === -1) throw Error("src output address not among authors"); handleAddress(address); @@ -255,26 +254,26 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { function addInlinePaymentQueries(cb){ async.forEachOfSeries( objUnit.messages, - function(message, i, cb2){ + (message, i, cb2) => { if (message.payload_location !== 'inline') return cb2(); - var payload = message.payload; + const payload = message.payload; if (message.app !== 'payment') return cb2(); - var denomination = payload.denomination || 1; + const denomination = payload.denomination || 1; async.forEachOfSeries( payload.inputs, - function(input, j, cb3){ - var type = input.type || "transfer"; - var src_unit = (type === "transfer") ? input.unit : null; - var src_message_index = (type === "transfer") ? input.message_index : null; - var src_output_index = (type === "transfer") ? input.output_index : null; - var from_main_chain_index = (type === "witnessing" || type === "headers_commission") ? input.from_main_chain_index : null; - var to_main_chain_index = (type === "witnessing" || type === "headers_commission") ? input.to_main_chain_index : null; + (input, j, cb3) => { + const type = input.type || "transfer"; + const src_unit = (type === "transfer") ? input.unit : null; + const src_message_index = (type === "transfer") ? input.message_index : null; + const src_output_index = (type === "transfer") ? input.output_index : null; + const from_main_chain_index = (type === "witnessing" || type === "headers_commission") ? input.from_main_chain_index : null; + const to_main_chain_index = (type === "witnessing" || type === "headers_commission") ? input.to_main_chain_index : null; - var determineInputAddress = function(handleAddress){ + const determineInputAddress = handleAddress => { if (type === "headers_commission" || type === "witnessing" || type === "issue") return handleAddress((arrAuthorAddresses.length === 1) ? arrAuthorAddresses[0] : input.address); // hereafter, transfer @@ -283,9 +282,9 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { determineInputAddressFromSrcOutput(payload.asset, denomination, input, handleAddress); }; - determineInputAddress(function(address){ - var is_unique = - objValidationState.arrDoubleSpendInputs.some(function(ds){ return (ds.message_index === i && ds.input_index === j); }) + determineInputAddress(address => { + const is_unique = + objValidationState.arrDoubleSpendInputs.some(({message_index, input_index}) => message_index === i && input_index === j) ? null : 1; conn.addQuery(arrQueries, "INSERT INTO inputs \n\ (unit, message_index, input_index, type, \n\ @@ -306,18 +305,18 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { break; case "headers_commission": case "witnessing": - var table = type + "_outputs"; - conn.addQuery(arrQueries, "UPDATE "+table+" SET is_spent=1 \n\ - WHERE main_chain_index>=? AND main_chain_index<=? AND address=?", + const table = `${type}_outputs`; + conn.addQuery(arrQueries, `UPDATE ${table} SET is_spent=1 \n\ + WHERE main_chain_index>=? AND main_chain_index<=? AND address=?`, [from_main_chain_index, to_main_chain_index, address]); break; } cb3(); }); }, - function(){ - for (var j=0; j { + for (let j=0; j { if (rows.length !== 1) throw Error("zero or more than one best parent unit?"); my_best_parent_unit = rows[0].unit; if (my_best_parent_unit !== objValidationState.best_parent_unit) - throwError("different best parents, validation: "+objValidationState.best_parent_unit+", writer: "+my_best_parent_unit); - conn.query("UPDATE units SET best_parent_unit=? WHERE unit=?", [my_best_parent_unit, objUnit.unit], function(){ cb(); }); + throwError(`different best parents, validation: ${objValidationState.best_parent_unit}, writer: ${my_best_parent_unit}`); + conn.query("UPDATE units SET best_parent_unit=? WHERE unit=?", [my_best_parent_unit, objUnit.unit], () => { cb(); }); } ); } function determineMaxLevel(handleMaxLevel){ - var max_level = 0; + let max_level = 0; async.each( objUnit.parent_units, - function(parent_unit, cb){ - storage.readStaticUnitProps(conn, parent_unit, function(props){ - if (props.level > max_level) - max_level = props.level; + (parent_unit, cb) => { + storage.readStaticUnitProps(conn, parent_unit, ({level}) => { + if (level > max_level) + max_level = level; cb(); }); }, - function(){ + () => { handleMaxLevel(max_level); } ); } function updateLevel(cb){ - conn.query("SELECT MAX(level) AS max_level FROM units WHERE unit IN(?)", [objUnit.parent_units], function(rows){ + conn.query("SELECT MAX(level) AS max_level FROM units WHERE unit IN(?)", [objUnit.parent_units], rows => { if (rows.length !== 1) throw Error("not a single max level?"); - determineMaxLevel(function(max_level){ + determineMaxLevel(max_level => { if (max_level !== rows[0].max_level) - throwError("different max level, sql: "+rows[0].max_level+", props: "+max_level); + throwError(`different max level, sql: ${rows[0].max_level}, props: ${max_level}`); objNewUnitProps.level = max_level + 1; - conn.query("UPDATE units SET level=? WHERE unit=?", [rows[0].max_level + 1, objUnit.unit], function(){ + conn.query("UPDATE units SET level=? WHERE unit=?", [rows[0].max_level + 1, objUnit.unit], () => { cb(); }); }); @@ -401,7 +400,7 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { if (objUnit.witnesses) updateWitnessedLevelByWitnesslist(objUnit.witnesses, cb); else - storage.readWitnessList(conn, objUnit.witness_list_unit, function(arrWitnesses){ + storage.readWitnessList(conn, objUnit.witness_list_unit, arrWitnesses => { updateWitnessedLevelByWitnesslist(arrWitnesses, cb); }); } @@ -409,14 +408,14 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { // The level at which we collect at least 7 distinct witnesses while walking up the main chain from our unit. // The unit itself is not counted even if it is authored by a witness function updateWitnessedLevelByWitnesslist(arrWitnesses, cb){ - var arrCollectedWitnesses = []; + const arrCollectedWitnesses = []; function setWitnessedLevel(witnessed_level){ profiler.start(); if (witnessed_level !== objValidationState.witnessed_level) - throwError("different witnessed levels, validation: "+objValidationState.witnessed_level+", writer: "+witnessed_level); + throwError(`different witnessed levels, validation: ${objValidationState.witnessed_level}, writer: ${witnessed_level}`); objNewUnitProps.witnessed_level = witnessed_level; - conn.query("UPDATE units SET witnessed_level=? WHERE unit=?", [witnessed_level, objUnit.unit], function(){ + conn.query("UPDATE units SET witnessed_level=? WHERE unit=?", [witnessed_level, objUnit.unit], () => { profiler.stop('write-wl-update'); cb(); }); @@ -424,20 +423,20 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { function addWitnessesAndGoUp(start_unit){ profiler.start(); - storage.readStaticUnitProps(conn, start_unit, function(props){ + storage.readStaticUnitProps(conn, start_unit, props => { profiler.stop('write-wl-select-bp'); - var best_parent_unit = props.best_parent_unit; - var level = props.level; + const best_parent_unit = props.best_parent_unit; + const level = props.level; if (level === null) throw Error("null level in updateWitnessedLevel"); if (level === 0) // genesis return setWitnessedLevel(0); profiler.start(); - storage.readUnitAuthors(conn, start_unit, function(arrAuthors){ + storage.readUnitAuthors(conn, start_unit, arrAuthors => { profiler.stop('write-wl-select-authors'); profiler.start(); - for (var i=0; i { + console.log(`got lock to write ${objUnit.unit}`); storage.assocUnstableUnits[objUnit.unit] = objNewUnitProps; - addInlinePaymentQueries(function(){ - async.series(arrQueries, function(){ + addInlinePaymentQueries(() => { + async.series(arrQueries, () => { profiler.stop('write-raw'); profiler.start(); - var arrOps = []; + const arrOps = []; if (objUnit.parent_units){ if (!conf.bLight){ arrOps.push(updateBestParent); arrOps.push(updateLevel); arrOps.push(updateWitnessedLevel); - arrOps.push(function(cb){ - console.log("updating MC after adding "+objUnit.unit); + arrOps.push(cb => { + console.log(`updating MC after adding ${objUnit.unit}`); main_chain.updateMainChain(conn, null, objUnit.unit, cb); }); } if (preCommitCallback) - arrOps.push(function(cb){ + arrOps.push(cb => { console.log("executing pre-commit callback"); preCommitCallback(conn, cb); }); } - async.series(arrOps, function(err){ + async.series(arrOps, err => { profiler.start(); - conn.query(err ? "ROLLBACK" : "COMMIT", function(){ + conn.query(err ? "ROLLBACK" : "COMMIT", () => { conn.release(); - console.log((err ? (err+", therefore rolled back unit ") : "committed unit ")+objUnit.unit); + console.log((err ? (`${err}, therefore rolled back unit `) : "committed unit ")+objUnit.unit); profiler.stop('write-commit'); profiler.increment(); if (err) @@ -502,7 +501,7 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { else unlock(); if (!err) - eventBus.emit('saved_unit-'+objUnit.unit, objJoint); + eventBus.emit(`saved_unit-${objUnit.unit}`, objJoint); if (onDone) onDone(err); count_writes++; @@ -520,10 +519,10 @@ function saveJoint(objJoint, objValidationState, preCommitCallback, onDone) { function readCountOfAnalyzedUnits(handleCount){ if (count_units_in_prev_analyze) return handleCount(count_units_in_prev_analyze); - db.query("SELECT * FROM sqlite_master WHERE type='table' AND name='sqlite_stat1'", function(rows){ - if (rows.length === 0) + db.query("SELECT * FROM sqlite_master WHERE type='table' AND name='sqlite_stat1'", ({length}) => { + if (length === 0) return handleCount(0); - db.query("SELECT stat FROM sqlite_stat1 WHERE tbl='units' AND idx='sqlite_autoindex_units_1'", function(rows){ + db.query("SELECT stat FROM sqlite_stat1 WHERE tbl='units' AND idx='sqlite_autoindex_units_1'", rows => { if (rows.length !== 1){ console.log('no stat for sqlite_autoindex_units_1'); return handleCount(0); @@ -533,8 +532,8 @@ function readCountOfAnalyzedUnits(handleCount){ }); } -var start_time = 0; -var prev_time = 0; +let start_time = 0; +let prev_time = 0; // update stats for query planner function updateSqliteStats(){ if (count_writes === 1){ @@ -544,25 +543,25 @@ function updateSqliteStats(){ if (count_writes % 100 !== 0) return; if (count_writes % 1000 === 0){ - var total_time = (Date.now() - start_time)/1000; - var recent_time = (Date.now() - prev_time)/1000; - var recent_tps = 1000/recent_time; - var avg_tps = count_writes/total_time; + const total_time = (Date.now() - start_time)/1000; + const recent_time = (Date.now() - prev_time)/1000; + const recent_tps = 1000/recent_time; + const avg_tps = count_writes/total_time; prev_time = Date.now(); // console.error(count_writes+" units done in "+total_time+" s, recent "+recent_tps+" tps, avg "+avg_tps+" tps"); } - db.query("SELECT MAX(rowid) AS count_units FROM units", function(rows){ - var count_units = rows[0].count_units; + db.query("SELECT MAX(rowid) AS count_units FROM units", rows => { + const count_units = rows[0].count_units; if (count_units > 500000) // the db is too big return; - readCountOfAnalyzedUnits(function(count_analyzed_units){ - console.log('count analyzed units: '+count_analyzed_units); + readCountOfAnalyzedUnits(count_analyzed_units => { + console.log(`count analyzed units: ${count_analyzed_units}`); if (count_units < 2*count_analyzed_units) return; count_units_in_prev_analyze = count_units; console.log("will update sqlite stats"); - db.query("ANALYZE", function(){ - db.query("ANALYZE sqlite_master", function(){ + db.query("ANALYZE", () => { + db.query("ANALYZE sqlite_master", () => { console.log("sqlite stats updated"); }); });