From 78d62c73b82e1953999d1cf80c90ed2035d4996e Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 29 Jun 2017 01:36:12 -0700 Subject: [PATCH] refactor: start using multiple return values. --- lib/blockchain/chain.js | 80 +++----- lib/crypto/merkle.js | 45 +--- lib/crypto/siphash.js | 30 +-- lib/mempool/mempool.js | 34 ++- lib/mining/template.js | 8 +- lib/net/bip152.js | 9 +- lib/primitives/block.js | 87 ++++---- lib/primitives/merkleblock.js | 27 ++- lib/primitives/tx.js | 376 +++++++++++++++------------------- lib/protocol/errors.js | 11 - lib/utils/bech32.js | 23 +-- lib/utils/fs.js | 13 +- lib/utils/gcs.js | 7 +- lib/utils/protoreader.js | 6 +- lib/utils/reader.js | 34 +-- test/block-test.js | 18 +- test/siphash-test.js | 33 +-- 17 files changed, 337 insertions(+), 504 deletions(-) diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index ce8da7bf..8367d77f 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -23,7 +23,6 @@ var Script = require('../script/script'); var errors = require('../protocol/errors'); var co = require('../utils/co'); var VerifyError = errors.VerifyError; -var VerifyResult = errors.VerifyResult; /** * Represents a blockchain. @@ -162,7 +161,7 @@ Chain.prototype.verifyContext = async function verifyContext(block, prev, flags) // Verify scripts, spend and add coins. view = await this.verifyInputs(block, prev, state); - return new ContextResult(view, state); + return [view, state]; }; /** @@ -220,11 +219,10 @@ Chain.prototype.isGenesis = function isGenesis(block) { Chain.prototype.verify = async function verify(block, prev, flags) { var hash = block.hash('hex'); - var ret = new VerifyResult(); var now = this.network.now(); var height = prev.height + 1; var i, ts, tx, mtp; - var commit, state, bits; + var valid, reason, score, commit, state, bits; assert(typeof flags === 'number'); @@ -258,13 +256,10 @@ Chain.prototype.verify = async function verify(block, prev, flags) { // Non-contextual checks. if (flags & common.flags.VERIFY_BODY) { - if (!block.verifyBody(ret)) { - throw new VerifyError(block, - 'invalid', - ret.reason, - ret.score, - true); - } + [valid, reason, score] = block.checkBody(); + + if (!valid) + throw new VerifyError(block, 'invalid', reason, score, true); } // Skip all blocks in spv mode. @@ -548,14 +543,13 @@ Chain.prototype.verifyDuplicates = async function verifyDuplicates(block, prev, Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) { var interval = this.network.halvingInterval; - var ret = new VerifyResult(); var view = new CoinView(); var height = prev.height + 1; var historical = prev.isHistorical(); var sigops = 0; var reward = 0; var jobs = []; - var i, tx, valid, fee; + var i, tx, valid, reason, score, fee; if (this.options.spv) return view; @@ -606,13 +600,13 @@ Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) { // Contextual sanity checks. if (i > 0) { - fee = tx.checkContext(view, height, ret); + [fee, reason, score] = tx.checkContext(view, height); if (fee === -1) { throw new VerifyError(block, 'invalid', - ret.reason, - ret.score); + reason, + score); } reward += fee; @@ -864,7 +858,7 @@ Chain.prototype.disconnect = async function disconnect(entry) { Chain.prototype.reconnect = async function reconnect(entry) { var block = await this.db.getBlock(entry.hash); var flags = common.flags.VERIFY_NONE; - var prev, result; + var prev, view, state; if (!block) { if (!this.options.spv) @@ -876,7 +870,7 @@ Chain.prototype.reconnect = async function reconnect(entry) { assert(prev); try { - result = await this.verifyContext(block, prev, flags); + [view, state] = await this.verifyContext(block, prev, flags); } catch (err) { if (err.type === 'VerifyError') { if (!err.malleated) @@ -888,16 +882,16 @@ Chain.prototype.reconnect = async function reconnect(entry) { throw err; } - await this.db.reconnect(entry, block, result.view); + await this.db.reconnect(entry, block, view); this.tip = entry; this.height = entry.height; - this.setDeploymentState(result.state); + this.setDeploymentState(state); this.emit('tip', entry); this.emit('reconnect', entry, block); - await this.fire('connect', entry, block, result.view); + await this.fire('connect', entry, block, view); }; /** @@ -915,7 +909,7 @@ Chain.prototype.reconnect = async function reconnect(entry) { */ Chain.prototype.setBestChain = async function setBestChain(entry, block, prev, flags) { - var result; + var view, state; // A higher fork has arrived. // Time to reorganize the chain. @@ -942,7 +936,7 @@ Chain.prototype.setBestChain = async function setBestChain(entry, block, prev, f // now that we're certain its previous // block is in the chain. try { - result = await this.verifyContext(block, prev, flags); + [view, state] = await this.verifyContext(block, prev, flags); } catch (err) { if (err.type === 'VerifyError') { if (!err.malleated) @@ -955,17 +949,17 @@ Chain.prototype.setBestChain = async function setBestChain(entry, block, prev, f } // Save block and connect inputs. - await this.db.save(entry, block, result.view); + await this.db.save(entry, block, view); // Expose the new state. this.tip = entry; this.height = entry.height; - this.setDeploymentState(result.state); + this.setDeploymentState(state); this.emit('tip', entry); this.emit('block', block, entry); - await this.fire('connect', entry, block, result.view); + await this.fire('connect', entry, block, view); }; /** @@ -2288,7 +2282,7 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) { var i, input, entry; if (tx.isCoinbase() || tx.version < 2 || !hasFlag) - return new LockTimes(minHeight, minTime); + return [minHeight, minTime]; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -2315,7 +2309,7 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) { minTime = Math.max(minTime, coinTime); } - return new LockTimes(minHeight, minTime); + return [minHeight, minTime]; }; /** @@ -2329,20 +2323,20 @@ Chain.prototype.getLocks = async function getLocks(prev, tx, view, flags) { */ Chain.prototype.verifyLocks = async function verifyLocks(prev, tx, view, flags) { - var locks = await this.getLocks(prev, tx, view, flags); + var [height, time] = await this.getLocks(prev, tx, view, flags); var mtp; // Also catches case where // height is `-1`. Fall through. - if (locks.height >= prev.height + 1) + if (height >= prev.height + 1) return false; - if (locks.time === -1) + if (time === -1) return true; mtp = await prev.getMedianTime(); - if (locks.time >= mtp) + if (time >= mtp) return false; return true; @@ -2582,28 +2576,6 @@ DeploymentState.prototype.hasWitness = function hasWitness() { return (this.flags & Script.flags.VERIFY_WITNESS) !== 0; }; -/** - * LockTimes - * @constructor - * @ignore - */ - -function LockTimes(height, time) { - this.height = height; - this.time = time; -} - -/** - * ContextResult - * @constructor - * @ignore - */ - -function ContextResult(view, state) { - this.view = view; - this.state = state; -} - /** * Orphan * @constructor diff --git a/lib/crypto/merkle.js b/lib/crypto/merkle.js index a294192f..5b3faf9d 100644 --- a/lib/crypto/merkle.js +++ b/lib/crypto/merkle.js @@ -18,7 +18,7 @@ var native = require('../native').binding; * Build a merkle tree from leaves. * Note that this will mutate the `leaves` array! * @param {Buffer[]} leaves - * @returns {MerkleTree} + * @returns {Array} [nodes, malleated] */ exports.createTree = function createTree(leaves) { @@ -31,12 +31,12 @@ exports.createTree = function createTree(leaves) { hash = Buffer.allocUnsafe(32); hash.fill(0); nodes.push(hash); - return new MerkleTree(nodes, malleated); + return [nodes, malleated]; } lr = Buffer.allocUnsafe(64); - for (j = 0; size > 1; size = ((size + 1) / 2) | 0) { + for (j = 0; size > 1; size = (size + 1) >>> 1) { for (i = 0; i < size; i += 2) { k = Math.min(i + 1, size - 1); left = nodes[j + i]; @@ -57,7 +57,7 @@ exports.createTree = function createTree(leaves) { j += size; } - return new MerkleTree(nodes, malleated); + return [nodes, malleated]; }; if (native) @@ -66,14 +66,13 @@ if (native) /** * Calculate merkle root from leaves. * @param {Buffer[]} leaves - * @returns {MerkleRoot} + * @returns {Array} [root, malleated] */ exports.createRoot = function createRoot(leaves) { - var tree = exports.createTree(leaves); - var hash = tree.nodes[tree.nodes.length - 1]; - var malleated = tree.malleated; - return new MerkleRoot(hash, malleated); + var [nodes, malleated] = exports.createTree(leaves); + var root = nodes[nodes.length - 1]; + return [root, malleated]; }; /** @@ -90,7 +89,7 @@ exports.createBranch = function createBranch(index, leaves) { var j = 0; var i; - for (; size > 1; size = (size + 1) / 2 | 0) { + for (; size > 1; size = (size + 1) >>> 1) { i = Math.min(index ^ 1, size - 1); branch.push(tree.nodes[j + i]); index >>>= 1; @@ -136,29 +135,3 @@ exports.verifyBranch = function verifyBranch(hash, branch, index) { if (native) exports.verifyBranch = native.verifyMerkleBranch; - -/** - * Merkle Tree - * @constructor - * @ignore - * @param {Buffer[]} nodes - * @param {Boolean} malleated - */ - -function MerkleTree(nodes, malleated) { - this.nodes = nodes; - this.malleated = malleated; -} - -/** - * Merkle Root - * @constructor - * @ignore - * @param {Buffer} hash - * @param {Boolean} malleated - */ - -function MerkleRoot(hash, malleated) { - this.hash = hash; - this.malleated = malleated; -} diff --git a/lib/crypto/siphash.js b/lib/crypto/siphash.js index 8afaa2fc..996dbc6f 100644 --- a/lib/crypto/siphash.js +++ b/lib/crypto/siphash.js @@ -20,7 +20,7 @@ var native = require('../native').binding; * @alias module:crypto/siphash.siphash24 * @param {Buffer} data * @param {Buffer} key - 128 bit key. - * @returns {Buffer} uint64le + * @returns {Array} [hi, lo] */ function siphash24(data, key, shift) { @@ -83,7 +83,7 @@ function siphash24(data, key, shift) { v0.ixor(v2); v0.ixor(v3); - return v0; + return [v0.hi, v0.lo]; } function sipround(v0, v1, v2, v3) { @@ -109,11 +109,11 @@ function sipround(v0, v1, v2, v3) { } /** - * Javascript siphash implementation. Used for compact block relay. + * Javascript siphash implementation (shift=56). * @alias module:crypto/siphash.siphash * @param {Buffer} data * @param {Buffer} key - 128 bit key. - * @returns {Buffer} uint64le + * @returns {Array} [hi, lo] */ function siphash(data, key) { @@ -121,11 +121,11 @@ function siphash(data, key) { } /** - * Javascript siphash implementation. Used for compact block relay. + * Javascript siphash implementation (shift=59). * @alias module:crypto/siphash.siphash256 * @param {Buffer} data * @param {Buffer} key - 128 bit key. - * @returns {Buffer} uint64le + * @returns {Array} [hi, lo] */ function siphash256(data, key) { @@ -213,22 +213,9 @@ U64.prototype.irotl = function irotl(bits) { return this; }; -U64.prototype.toRaw = function toRaw() { - var data = Buffer.allocUnsafe(8); - data.writeUInt32LE(this.lo >>> 0, 0, true); - data.writeUInt32LE(this.hi >>> 0, 4, true); - return data; -}; - U64.fromRaw = function fromRaw(data, off) { - var hi, lo; - - if (!off) - off = 0; - - lo = data.readUInt32LE(off, true); - hi = data.readUInt32LE(off + 4, true); - + var lo = data.readUInt32LE(off, true); + var hi = data.readUInt32LE(off + 4, true); return new U64(hi, lo); }; @@ -239,6 +226,5 @@ U64.fromRaw = function fromRaw(data, off) { exports = siphash; exports.siphash = siphash; exports.siphash256 = siphash256; -exports.U64 = U64; module.exports = exports; diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index bbceb34a..c9d4c93c 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -30,7 +30,6 @@ var CoinView = require('../coins/coinview'); var Coins = require('../coins/coins'); var Heap = require('../utils/heap'); var VerifyError = errors.VerifyError; -var VerifyResult = errors.VerifyResult; /** * Represents a mempool. @@ -742,20 +741,17 @@ Mempool.prototype.insertTX = async function insertTX(tx, id) { var lockFlags = common.lockFlags.STANDARD_LOCKTIME_FLAGS; var height = this.chain.height; var hash = tx.hash('hex'); - var ret = new VerifyResult(); - var entry, view, missing; + var valid, reason, score, entry, view, missing; assert(!tx.mutable, 'Cannot add mutable TX to mempool.'); // Basic sanity checks. // This is important because it ensures // other functions will be overflow safe. - if (!tx.isSane(ret)) { - throw new VerifyError(tx, - 'invalid', - ret.reason, - ret.score); - } + [valid, reason, score] = tx.checkSanity(); + + if (!valid) + throw new VerifyError(tx, 'invalid', reason, score); // Coinbases are an insta-ban. // Why? Who knows. @@ -789,12 +785,11 @@ Mempool.prototype.insertTX = async function insertTX(tx, id) { // Non-contextual standardness checks. if (this.options.requireStandard) { - if (!tx.isStandard(ret)) { - throw new VerifyError(tx, - 'nonstandard', - ret.reason, - ret.score); - } + [valid, reason, score] = tx.checkStandard(); + + if (!valid) + throw new VerifyError(tx, 'nonstandard', reason, score); + if (!this.options.replaceByFee) { if (tx.isRBF()) { throw new VerifyError(tx, @@ -886,9 +881,8 @@ Mempool.prototype.verify = async function verify(entry, view) { var height = this.chain.height + 1; var lockFlags = common.lockFlags.STANDARD_LOCKTIME_FLAGS; var flags = Script.flags.STANDARD_VERIFY_FLAGS; - var ret = new VerifyResult(); var tx = entry.tx; - var now, minFee, result; + var fee, reason, score, now, minFee, result; // Verify sequence locks. if (!(await this.verifyLocks(tx, view, lockFlags))) { @@ -971,8 +965,10 @@ Mempool.prototype.verify = async function verify(entry, view) { } // Contextual sanity checks. - if (!tx.checkInputs(view, height, ret)) - throw new VerifyError(tx, 'invalid', ret.reason, ret.score); + [fee, reason, score] = tx.checkContext(view, height); + + if (fee === -1) + throw new VerifyError(tx, 'invalid', reason, score); // Script verification. try { diff --git a/lib/mining/template.js b/lib/mining/template.js index 0425c81c..4f926889 100644 --- a/lib/mining/template.js +++ b/lib/mining/template.js @@ -173,7 +173,7 @@ BlockTemplate.fromOptions = function fromOptions(options) { BlockTemplate.prototype.getWitnessHash = function getWitnessHash() { var nonce = encoding.ZERO_HASH; var leaves = []; - var i, item, root, data; + var i, item, root, malleated, data; leaves.push(encoding.ZERO_HASH); @@ -182,11 +182,11 @@ BlockTemplate.prototype.getWitnessHash = function getWitnessHash() { leaves.push(item.tx.witnessHash()); } - root = merkle.createRoot(leaves); + [root, malleated] = merkle.createRoot(leaves); - assert(!root.malleated); + assert(!malleated); - data = util.concat(root.hash, nonce); + data = util.concat(root, nonce); return digest.hash256(data); }; diff --git a/lib/net/bip152.js b/lib/net/bip152.js index 191f97d8..8c5b1a0b 100644 --- a/lib/net/bip152.js +++ b/lib/net/bip152.js @@ -379,17 +379,14 @@ CompactBlock.prototype.fillMissing = function fillMissing(res) { */ CompactBlock.prototype.sid = function sid(hash) { - var lo, hi; + var hi, lo; if (typeof hash === 'string') hash = Buffer.from(hash, 'hex'); - hash = siphash256(hash, this.sipKey); + [hi, lo] = siphash256(hash, this.sipKey); - lo = hash.lo >>> 0; - hi = hash.hi & 0xffff; - - return hi * 0x100000000 + lo; + return (hi & 0xffff) * 0x100000000 + (lo >>> 0); }; /** diff --git a/lib/primitives/block.js b/lib/primitives/block.js index ef59dba4..3a0c9a3a 100644 --- a/lib/primitives/block.js +++ b/lib/primitives/block.js @@ -14,7 +14,6 @@ var digest = require('../crypto/digest'); var merkle = require('../crypto/merkle'); var consensus = require('../protocol/consensus'); var AbstractBlock = require('./abstractblock'); -var VerifyResult = require('../protocol/errors').VerifyResult; var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var TX = require('./tx'); @@ -287,21 +286,19 @@ Block.prototype.indexOf = function indexOf(hash) { Block.prototype.createMerkleRoot = function createMerkleRoot(enc) { var leaves = []; - var i, tx, root; + var i, tx, root, malleated; for (i = 0; i < this.txs.length; i++) { tx = this.txs[i]; leaves.push(tx.hash()); } - root = merkle.createRoot(leaves); + [root, malleated] = merkle.createRoot(leaves); - if (root.malleated) + if (malleated) return null; - return enc === 'hex' - ? root.hash.toString('hex') - : root.hash; + return enc === 'hex' ? root.toString('hex') : root; }; /** @@ -334,12 +331,12 @@ Block.prototype.createCommitmentHash = function createCommitmentHash(enc) { leaves.push(tx.witnessHash()); } - root = merkle.createRoot(leaves); + [root] = merkle.createRoot(leaves); // Note: malleation check ignored here. - // assert(!root.malleated); + // assert(!malleated); - data = util.concat(root.hash, nonce); + data = util.concat(root, nonce); hash = digest.hash256(data); @@ -422,78 +419,68 @@ Block.prototype.getCommitmentHash = function getCommitmentHash(enc) { /** * Do non-contextual verification on the block. Including checking the block * size, the coinbase and the merkle root. This is consensus-critical. - * @alias Block#verify - * @param {Object?} ret - Return object, may be - * set with properties `reason` and `score`. * @returns {Boolean} */ -Block.prototype.verifyBody = function verifyBody(ret) { +Block.prototype.verifyBody = function verifyBody() { + var [valid] = this.checkBody(); + return valid; +}; + +/** + * Do non-contextual verification on the block. Including checking the block + * size, the coinbase and the merkle root. This is consensus-critical. + * @returns {Array} [valid, reason, score] + */ + +Block.prototype.checkBody = function checkBody() { var sigops = 0; var scale = consensus.WITNESS_SCALE_FACTOR; - var i, tx, merkle; - - if (!ret) - ret = new VerifyResult(); + var i, valid, reason, score, tx, root; // Check merkle root. - merkle = this.createMerkleRoot('hex'); + root = this.createMerkleRoot('hex'); // If the merkle is mutated, // we have duplicate txs. - if (!merkle) { - ret.reason = 'bad-txns-duplicate'; - ret.score = 100; - return false; - } + if (!root) + return [false, 'bad-txns-duplicate', 100]; - if (this.merkleRoot !== merkle) { - ret.reason = 'bad-txnmrklroot'; - ret.score = 100; - return false; - } + if (this.merkleRoot !== root) + return [false, 'bad-txnmrklroot', 100]; // Check base size. if (this.txs.length === 0 || this.txs.length > consensus.MAX_BLOCK_SIZE || this.getBaseSize() > consensus.MAX_BLOCK_SIZE) { - ret.reason = 'bad-blk-length'; - ret.score = 100; - return false; + return [false, 'bad-blk-length', 100]; } // First TX must be a coinbase. - if (this.txs.length === 0 || !this.txs[0].isCoinbase()) { - ret.reason = 'bad-cb-missing'; - ret.score = 100; - return false; - } + if (this.txs.length === 0 || !this.txs[0].isCoinbase()) + return [false, 'bad-cb-missing', 100]; // Test all transactions. for (i = 0; i < this.txs.length; i++) { tx = this.txs[i]; // The rest of the txs must not be coinbases. - if (i > 0 && tx.isCoinbase()) { - ret.reason = 'bad-cb-multiple'; - ret.score = 100; - return false; - } + if (i > 0 && tx.isCoinbase()) + return [false, 'bad-cb-multiple', 100]; // Sanity checks. - if (!tx.isSane(ret)) - return false; + [valid, reason, score] = tx.checkSanity(); + + if (!valid) + return [valid, reason, score]; // Count legacy sigops (do not count scripthash or witness). sigops += tx.getLegacySigops(); - if (sigops * scale > consensus.MAX_BLOCK_SIGOPS_COST) { - ret.reason = 'bad-blk-sigops'; - ret.score = 100; - return false; - } + if (sigops * scale > consensus.MAX_BLOCK_SIGOPS_COST) + return [false, 'bad-blk-sigops', 100]; } - return true; + return [true, 'valid', 0]; }; /** diff --git a/lib/primitives/merkleblock.js b/lib/primitives/merkleblock.js index 0e701524..efc40920 100644 --- a/lib/primitives/merkleblock.js +++ b/lib/primitives/merkleblock.js @@ -11,7 +11,6 @@ var assert = require('assert'); var util = require('../utils/util'); var digest = require('../crypto/digest'); var AbstractBlock = require('./abstractblock'); -var VerifyResult = require('../protocol/errors').VerifyResult; var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); var encoding = require('../utils/encoding'); @@ -149,19 +148,25 @@ MerkleBlock.prototype.indexOf = function indexOf(hash) { * @returns {Boolean} */ -MerkleBlock.prototype.verifyBody = function verifyBody(ret) { +MerkleBlock.prototype.verifyBody = function verifyBody() { + var [valid] = this.checkBody(); + return valid; +}; + +/** + * Verify the partial merkletree. Push leaves onto + * {@link MerkleBlock#tx} and into {@link MerkleBlock#map}. + * @private + * @returns {Array} [valid, reason, score] + */ + +MerkleBlock.prototype.checkBody = function checkBody() { var tree = this.getTree(); - if (!ret) - ret = new VerifyResult(); + if (tree.root !== this.merkleRoot) + return [false, 'bad-txnmrklroot', 100]; - if (tree.root !== this.merkleRoot) { - ret.reason = 'bad-txnmrklroot'; - ret.score = 100; - return false; - } - - return true; + return [true, 'valid', 100]; }; /** diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 58d8871f..541f4c6f 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -17,7 +17,6 @@ var Network = require('../protocol/network'); var Script = require('../script/script'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); -var VerifyResult = require('../protocol/errors').VerifyResult; var Input = require('./input'); var Output = require('./output'); var Outpoint = require('./outpoint'); @@ -26,6 +25,7 @@ var workerPool = require('../workers/workerpool').pool; var Bloom = require('../utils/bloom'); var consensus = require('../protocol/consensus'); var policy = require('../protocol/policy'); +var ScriptError = require('../script/common').ScriptError; /** * A static transaction object. @@ -784,25 +784,13 @@ TX.prototype.signature = function signature(index, prev, value, key, type, versi */ TX.prototype.verify = function verify(view, flags) { - var i, input, coin; - - if (this.inputs.length === 0) - return false; - - if (this.isCoinbase()) - return true; - - for (i = 0; i < this.inputs.length; i++) { - input = this.inputs[i]; - coin = view.getOutput(input); - - if (!coin) - return false; - - if (!this.verifyInput(i, coin, flags)) + try { + this.check(view, flags); + } catch (e) { + if (e.type === 'ScriptError') return false; + throw e; } - return true; }; @@ -816,30 +804,69 @@ TX.prototype.verify = function verify(view, flags) { */ TX.prototype.verifyInput = function verifyInput(index, coin, flags) { - var input = this.inputs[index]; - - assert(input, 'Input does not exist.'); - assert(coin, 'No coin passed.'); - try { - Script.verify( - input.script, - input.witness, - coin.script, - this, - index, - coin.value, - flags - ); + this.checkInput(index, coin, flags); } catch (e) { if (e.type === 'ScriptError') return false; throw e; } - return true; }; +/** + * Verify all transaction inputs. + * @param {CoinView} view + * @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS] + * @throws {ScriptError} on invalid input + */ + +TX.prototype.check = function check(view, flags) { + var i, input, coin; + + if (this.inputs.length === 0) + throw new ScriptError('UNKNOWN_ERROR', 'No inputs.'); + + if (this.isCoinbase()) + return; + + for (i = 0; i < this.inputs.length; i++) { + input = this.inputs[i]; + coin = view.getOutput(input); + + if (!coin) + throw new ScriptError('UNKNOWN_ERROR', 'No coin available.'); + + this.checkInput(i, coin, flags); + } +}; + +/** + * Verify a transaction input. + * @param {Number} index - Index of output being + * verified. + * @param {Coin|Output} coin - Previous output. + * @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS] + * @throws {ScriptError} on invalid input + */ + +TX.prototype.checkInput = function checkInput(index, coin, flags) { + var input = this.inputs[index]; + + assert(input, 'Input does not exist.'); + assert(coin, 'No coin passed.'); + + Script.verify( + input.script, + input.witness, + coin.script, + this, + index, + coin.value, + flags + ); +}; + /** * Verify the transaction inputs on the worker pool * (if workers are enabled). @@ -971,7 +998,7 @@ TX.prototype._getInputAddresses = function getInputAddresses(view) { var i, address, hash, input, coin; if (this.isCoinbase()) - return new AddrResult(addrs, table); + return [addrs, table]; for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; @@ -989,7 +1016,7 @@ TX.prototype._getInputAddresses = function getInputAddresses(view) { } } - return new AddrResult(addrs, table); + return [addrs, table]; }; /** @@ -1018,7 +1045,7 @@ TX.prototype._getOutputAddresses = function getOutputAddresses() { } } - return new AddrResult(addrs, table); + return [addrs, table]; }; /** @@ -1029,10 +1056,8 @@ TX.prototype._getOutputAddresses = function getOutputAddresses() { */ TX.prototype._getAddresses = function getAddresses(view) { - var input = this._getInputAddresses(view); + var [addrs, table] = this._getInputAddresses(view); var output = this.getOutputAddresses(); - var addrs = input.addrs; - var table = input.table; var i, address, hash; for (i = 0; i < output.length; i++) { @@ -1045,7 +1070,7 @@ TX.prototype._getAddresses = function getAddresses(view) { } } - return input; + return [addrs, table]; }; /** @@ -1055,7 +1080,8 @@ TX.prototype._getAddresses = function getAddresses(view) { */ TX.prototype.getInputAddresses = function getInputAddresses(view) { - return this._getInputAddresses(view).addrs; + var [addrs] = this._getInputAddresses(view); + return addrs; }; /** @@ -1064,7 +1090,8 @@ TX.prototype.getInputAddresses = function getInputAddresses(view) { */ TX.prototype.getOutputAddresses = function getOutputAddresses() { - return this._getOutputAddresses().addrs; + var [addrs] = this._getOutputAddresses(); + return addrs; }; /** @@ -1074,7 +1101,8 @@ TX.prototype.getOutputAddresses = function getOutputAddresses() { */ TX.prototype.getAddresses = function getAddresses(view) { - return this._getAddresses(view).addrs; + var [addrs] = this._getAddresses(view); + return addrs; }; /** @@ -1085,11 +1113,11 @@ TX.prototype.getAddresses = function getAddresses(view) { TX.prototype.getInputHashes = function getInputHashes(view, enc) { var hashes = []; - var i, input, address; + var i, table, input, address; if (enc === 'hex') { - input = this._getInputAddresses(view); - return Object.keys(input.table); + [, table] = this._getInputAddresses(view); + return Object.keys(table); } input = this.getInputAddresses(view); @@ -1109,11 +1137,11 @@ TX.prototype.getInputHashes = function getInputHashes(view, enc) { TX.prototype.getOutputHashes = function getOutputHashes(enc) { var hashes = []; - var i, output, address; + var i, table, output, address; if (enc === 'hex') { - output = this._getOutputAddresses(); - return Object.keys(output.table); + [, table] = this._getOutputAddresses(); + return Object.keys(table); } output = this.getOutputAddresses(); @@ -1134,11 +1162,11 @@ TX.prototype.getOutputHashes = function getOutputHashes(enc) { TX.prototype.getHashes = function getHashes(view, enc) { var hashes = []; - var i, result, address; + var i, table, result, address; if (enc === 'hex') { - result = this._getAddresses(view); - return Object.keys(result.table); + [, table] = this._getAddresses(view); + return Object.keys(table); } result = this.getAddresses(view); @@ -1383,91 +1411,71 @@ TX.prototype.getSigops = function getSigops(view, flags) { * Non-contextual sanity checks for the transaction. * Will mostly verify coin and output values. * @see CheckTransaction() - * @param {Object?} ret - Return object, may be - * set with properties `reason` and `score`. - * @returns {Boolean} sane + * @returns {Array} [result, reason, score] */ -TX.prototype.isSane = function isSane(ret) { +TX.prototype.isSane = function isSane() { + var [valid] = this.checkSanity(); + return valid; +}; + +/** + * Non-contextual sanity checks for the transaction. + * Will mostly verify coin and output values. + * @see CheckTransaction() + * @returns {Array} [valid, reason, score] + */ + +TX.prototype.checkSanity = function checkSanity() { var prevout = {}; var total = 0; var i, input, output, size, key; - if (!ret) - ret = new VerifyResult(); + if (this.inputs.length === 0) + return [false, 'bad-txns-vin-empty', 100]; - if (this.inputs.length === 0) { - ret.reason = 'bad-txns-vin-empty'; - ret.score = 100; - return false; - } + if (this.outputs.length === 0) + return [false, 'bad-txns-vout-empty', 100]; - if (this.outputs.length === 0) { - ret.reason = 'bad-txns-vout-empty'; - ret.score = 100; - return false; - } - - if (this.getBaseSize() > consensus.MAX_BLOCK_SIZE) { - ret.reason = 'bad-txns-oversize'; - ret.score = 100; - return false; - } + if (this.getBaseSize() > consensus.MAX_BLOCK_SIZE) + return [false, 'bad-txns-oversize', 100]; for (i = 0; i < this.outputs.length; i++) { output = this.outputs[i]; - if (output.value < 0) { - ret.reason = 'bad-txns-vout-negative'; - ret.score = 100; - return false; - } + if (output.value < 0) + return [false, 'bad-txns-vout-negative', 100]; - if (output.value > consensus.MAX_MONEY) { - ret.reason = 'bad-txns-vout-toolarge'; - ret.score = 100; - return false; - } + if (output.value > consensus.MAX_MONEY) + return [false, 'bad-txns-vout-toolarge', 100]; total += output.value; - if (total < 0 || total > consensus.MAX_MONEY) { - ret.reason = 'bad-txns-txouttotal-toolarge'; - ret.score = 100; - return false; - } + if (total < 0 || total > consensus.MAX_MONEY) + return [false, 'bad-txns-txouttotal-toolarge', 100]; } for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; key = input.prevout.toKey(); - if (prevout[key]) { - ret.reason = 'bad-txns-inputs-duplicate'; - ret.score = 100; - return false; - } + if (prevout[key]) + return [false, 'bad-txns-inputs-duplicate', 100]; prevout[key] = true; } if (this.isCoinbase()) { size = this.inputs[0].script.getSize(); - if (size < 2 || size > 100) { - ret.reason = 'bad-cb-length'; - ret.score = 100; - return false; - } + if (size < 2 || size > 100) + return [false, 'bad-cb-length', 100]; } else { for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; - if (input.prevout.isNull()) { - ret.reason = 'bad-txns-prevout-null'; - ret.score = 10; - return false; - } + if (input.prevout.isNull()) + return [false, 'bad-txns-prevout-null', 10]; } } - return true; + return [true, 'valid', 0]; }; /** @@ -1477,80 +1485,67 @@ TX.prototype.isSane = function isSane(ret) { * pushdatas in the code. * Will mostly verify coin and output values. * @see IsStandardTx() - * @param {Object?} ret - Return object, may be - * set with properties `reason` and `score`. - * @returns {Boolean} + * @returns {Array} [valid, reason, score] */ -TX.prototype.isStandard = function isStandard(ret) { +TX.prototype.isStandard = function isStandard() { + var [valid] = this.checkStandard(); + return valid; +}; + +/** + * Non-contextual checks to determine whether the + * transaction has all standard output script + * types and standard input script size with only + * pushdatas in the code. + * Will mostly verify coin and output values. + * @see IsStandardTx() + * @returns {Array} [valid, reason, score] + */ + +TX.prototype.checkStandard = function checkStandard() { var i, input, output; var nulldata = 0; - if (!ret) - ret = new VerifyResult(); - - if (this.version < 1 || this.version > policy.MAX_TX_VERSION) { - ret.reason = 'version'; - ret.score = 0; - return false; - } + if (this.version < 1 || this.version > policy.MAX_TX_VERSION) + return [false, 'version', 0]; if (this.getWeight() >= policy.MAX_TX_WEIGHT) { - ret.reason = 'tx-size'; - ret.score = 0; - return false; + return [false, 'tx-size', 0]; } for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; - if (input.script.getSize() > 1650) { - ret.reason = 'scriptsig-size'; - ret.score = 0; - return false; - } + if (input.script.getSize() > 1650) + return [false, 'scriptsig-size', 0]; - if (!input.script.isPushOnly()) { - ret.reason = 'scriptsig-not-pushonly'; - ret.score = 0; - return false; - } + if (!input.script.isPushOnly()) + return [false, 'scriptsig-not-pushonly', 0]; } for (i = 0; i < this.outputs.length; i++) { output = this.outputs[i]; - if (!output.script.isStandard()) { - ret.reason = 'scriptpubkey'; - ret.score = 0; - return false; - } + if (!output.script.isStandard()) + return [false, 'scriptpubkey', 0]; if (output.script.isNulldata()) { nulldata++; continue; } - if (output.script.isMultisig() && !policy.BARE_MULTISIG) { - ret.reason = 'bare-multisig'; - ret.score = 0; - return false; - } + if (output.script.isMultisig() && !policy.BARE_MULTISIG) + return [false, 'bare-multisig', 0]; - if (output.isDust(policy.MIN_RELAY)) { - ret.reason = 'dust'; - ret.score = 0; - return false; - } + if (output.isDust(policy.MIN_RELAY)) + return [false, 'dust', 0]; } - if (nulldata > 1) { - ret.reason = 'multi-op-return'; - ret.score = 0; - return false; - } + if (nulldata > 1) + return [false, 'multi-op-return', 0]; - return true; + return [true, 'valid', 0]; }; /** @@ -1724,13 +1719,12 @@ TX.prototype.hasStandardWitness = function hasStandardWitness(view) { * @param {Number} height - Height at which the * transaction is being spent. In the mempool this is * the chain height plus one at the time it entered the pool. - * @param {Object?} ret - Return object, may be - * set with properties `reason` and `score`. * @returns {Boolean} */ -TX.prototype.checkInputs = function checkInputs(view, height, ret) { - return this.checkContext(view, height, ret) !== -1; +TX.prototype.checkInputs = function checkInputs(view, height) { + var [fee] = this.checkContext(view, height); + return fee !== -1; }; /** @@ -1743,85 +1737,56 @@ TX.prototype.checkInputs = function checkInputs(view, height, ret) { * @param {Number} height - Height at which the * transaction is being spent. In the mempool this is * the chain height plus one at the time it entered the pool. - * @param {Object?} ret - Return object, may be - * set with properties `reason` and `score`. - * @returns {Amount} + * @returns {Array} [fee, reason, score] */ -TX.prototype.checkContext = function checkContext(view, height, ret) { +TX.prototype.checkContext = function checkContext(view, height) { var total = 0; var i, input, coins, coin, fee, value; - if (!ret) - ret = new VerifyResult(); - assert(typeof height === 'number'); for (i = 0; i < this.inputs.length; i++) { input = this.inputs[i]; coins = view.get(input.prevout.hash); - if (!coins) { - ret.reason = 'bad-txns-inputs-missingorspent'; - ret.score = 0; - return -1; - } + if (!coins) + return [-1, 'bad-txns-inputs-missingorspent', 0]; if (coins.coinbase) { - if (height - coins.height < consensus.COINBASE_MATURITY) { - ret.reason = 'bad-txns-premature-spend-of-coinbase'; - ret.score = 0; - return -1; - } + if (height - coins.height < consensus.COINBASE_MATURITY) + return [-1, 'bad-txns-premature-spend-of-coinbase', 0]; } coin = coins.getOutput(input.prevout.index); - if (!coin) { - ret.reason = 'bad-txns-inputs-missingorspent'; - ret.score = 0; - return -1; - } + if (!coin) + return [-1, 'bad-txns-inputs-missingorspent', 0]; - if (coin.value < 0 || coin.value > consensus.MAX_MONEY) { - ret.reason = 'bad-txns-inputvalues-outofrange'; - ret.score = 100; - return -1; - } + if (coin.value < 0 || coin.value > consensus.MAX_MONEY) + return [-1, 'bad-txns-inputvalues-outofrange', 100]; total += coin.value; - if (total < 0 || total > consensus.MAX_MONEY) { - ret.reason = 'bad-txns-inputvalues-outofrange'; - ret.score = 100; - return -1; - } + if (total < 0 || total > consensus.MAX_MONEY) + return [-1, 'bad-txns-inputvalues-outofrange', 100]; } // Overflows already checked in `isSane()`. value = this.getOutputValue(); - if (total < value) { - ret.reason = 'bad-txns-in-belowout'; - ret.score = 100; - return -1; - } + if (total < value) + return [-1, 'bad-txns-in-belowout', 100]; fee = total - value; - if (fee < 0) { - ret.reason = 'bad-txns-fee-negative'; - ret.score = 100; - return -1; - } + if (fee < 0) + return [-1, 'bad-txns-fee-negative', 100]; - if (fee > consensus.MAX_MONEY) { - ret.reason = 'bad-txns-fee-outofrange'; - ret.score = 100; - return -1; - } + if (fee > consensus.MAX_MONEY) + return [-1, 'bad-txns-fee-outofrange', 100]; - return fee; + return [fee, 'valid', 0]; }; /** @@ -2665,11 +2630,6 @@ function RawTX(total, witness) { this.witness = witness; } -function AddrResult(addrs, table) { - this.addrs = addrs; - this.table = table; -} - /* * Expose */ diff --git a/lib/protocol/errors.js b/lib/protocol/errors.js index 8a7ba35f..49bdb7ec 100644 --- a/lib/protocol/errors.js +++ b/lib/protocol/errors.js @@ -61,19 +61,8 @@ function VerifyError(msg, code, reason, score, malleated) { util.inherits(VerifyError, Error); -/** - * Verication result. - * @constructor - */ - -function VerifyResult() { - this.reason = 'unknown'; - this.score = 0; -} - /* * Expose */ exports.VerifyError = VerifyError; -exports.VerifyResult = VerifyResult; diff --git a/lib/utils/bech32.js b/lib/utils/bech32.js index e055b67a..0bbd3c14 100644 --- a/lib/utils/bech32.js +++ b/lib/utils/bech32.js @@ -124,7 +124,7 @@ function serialize(hrp, data) { /** * Decode a bech32 string. * @param {String} str - * @returns {Bech32Result} + * @returns {Array} [hrp, data] */ function deserialize(str) { @@ -199,7 +199,7 @@ function deserialize(str) { if (chk !== 1) throw new Error('Invalid bech32 checksum.'); - return new Bech32Result(hrp, data.slice(0, dlen)); + return [hrp, data.slice(0, dlen)]; } /** @@ -283,9 +283,7 @@ if (native) */ function decode(str) { - var result = deserialize(str); - var hrp = result.hrp; - var data = result.data; + var [hrp, data] = deserialize(str); var version, hash, output; if (data.length === 0 || data.length > 65) @@ -307,21 +305,6 @@ function decode(str) { if (native) decode = native.fromBech32; -/** - * Bech32Result - * @constructor - * @private - * @param {String} hrp - * @param {Buffer} data - * @property {String} hrp - * @property {Buffer} data - */ - -function Bech32Result(hrp, data) { - this.hrp = hrp; - this.data = data; -} - /** * AddrResult * @constructor diff --git a/lib/utils/fs.js b/lib/utils/fs.js index ce3bf0f1..dc36265a 100644 --- a/lib/utils/fs.js +++ b/lib/utils/fs.js @@ -85,9 +85,7 @@ exports.writeFile = co.promisify(fs.writeFile); exports.writeFileSync = fs.writeFileSync; exports.mkdirpSync = function mkdirpSync(dir, mode) { - var data = getParts(dir); - var parts = data.parts; - var path = data.root; + var [path, parts] = getParts(dir); var i, stat; if (mode == null) @@ -112,9 +110,7 @@ exports.mkdirpSync = function mkdirpSync(dir, mode) { }; exports.mkdirp = async function mkdirp(dir, mode) { - var data = getParts(dir); - var parts = data.parts; - var path = data.root; + var [path, parts] = getParts(dir); var i, stat; if (mode == null) @@ -159,8 +155,5 @@ function getParts(path) { } } - return { - root: root, - parts: parts - }; + return [root, parts]; } diff --git a/lib/utils/gcs.js b/lib/utils/gcs.js index 96b3893f..0235bd85 100644 --- a/lib/utils/gcs.js +++ b/lib/utils/gcs.js @@ -543,11 +543,8 @@ function compare(a, b) { } function siphash(data, key) { - var num = new Int64(); - var hash = siphash24(data, key); - num.hi = hash.hi | 0; - num.lo = hash.lo | 0; - return num; + var [hi, lo] = siphash24(data, key); + return new Int64().join(hi, lo); } function getPushes(items, script) { diff --git a/lib/utils/protoreader.js b/lib/utils/protoreader.js index fb9f725c..3da500a8 100644 --- a/lib/utils/protoreader.js +++ b/lib/utils/protoreader.js @@ -39,9 +39,9 @@ function ProtoReader(data, zeroCopy) { util.inherits(ProtoReader, BufferReader); ProtoReader.prototype.readVarint = function _readVarint() { - var result = readVarint(this.data, this.offset); - this.offset += result.size; - return result.value; + var {size, value} = readVarint(this.data, this.offset); + this.offset += size; + return value; }; ProtoReader.prototype.readFieldValue = function readFieldValue(tag, opt) { diff --git a/lib/utils/reader.js b/lib/utils/reader.js index 91dc7d33..a2fff9b5 100644 --- a/lib/utils/reader.js +++ b/lib/utils/reader.js @@ -506,9 +506,9 @@ BufferReader.prototype.readDoubleBE = function readDoubleBE() { */ BufferReader.prototype.readVarint = function readVarint() { - var result = encoding.readVarint(this.data, this.offset); - this.offset += result.size; - return result.value; + var {size, value} = encoding.readVarint(this.data, this.offset); + this.offset += size; + return value; }; /** @@ -528,9 +528,9 @@ BufferReader.prototype.skipVarint = function skipVarint() { */ BufferReader.prototype.readVarintBN = function readVarintBN() { - var result = encoding.readVarintBN(this.data, this.offset); - this.offset += result.size; - return result.value; + var {size, value} = encoding.readVarintBN(this.data, this.offset); + this.offset += size; + return value; }; /** @@ -539,9 +539,9 @@ BufferReader.prototype.readVarintBN = function readVarintBN() { */ BufferReader.prototype.readVarint2 = function readVarint2() { - var result = encoding.readVarint2(this.data, this.offset); - this.offset += result.size; - return result.value; + var {size, value} = encoding.readVarint2(this.data, this.offset); + this.offset += size; + return value; }; /** @@ -561,9 +561,9 @@ BufferReader.prototype.skipVarint2 = function skipVarint2() { */ BufferReader.prototype.readVarint2BN = function readVarint2BN() { - var result = encoding.readVarint2BN(this.data, this.offset); - this.offset += result.size; - return result.value; + var {size, value} = encoding.readVarint2BN(this.data, this.offset); + this.offset += size; + return value; }; /** @@ -671,8 +671,14 @@ BufferReader.prototype.readNullString = function readNullString(enc) { */ BufferReader.prototype.createChecksum = function createChecksum() { - var start = this.stack[this.stack.length - 1] || 0; - var data = this.data.slice(start, this.offset); + var start = 0; + var data; + + if (this.stack.length > 0) + start = this.stack[this.stack.length - 1]; + + data = this.data.slice(start, this.offset); + return digest.hash256(data).readUInt32LE(0, true); }; diff --git a/test/block-test.js b/test/block-test.js index 13150ccb..bff32485 100644 --- a/test/block-test.js +++ b/test/block-test.js @@ -236,13 +236,13 @@ describe('Block', function() { it('should fail with a bad merkle root', function() { var block2 = new Block(block); - var ret = {}; + var reason; block2.merkleRoot = encoding.NULL_HASH; block2.refresh(); assert(!block2.verifyPOW()); - assert(!block2.verifyBody(ret)); + [, reason] = block2.checkBody(); + assert.equal(reason, 'bad-txnmrklroot'); assert(!block2.verify()); - assert.equal(ret.reason, 'bad-txnmrklroot'); block2.merkleRoot = block.merkleRoot; block2.refresh(); assert(block2.verify()); @@ -250,13 +250,13 @@ describe('Block', function() { it('should fail on merkle block with a bad merkle root', function() { var mblock2 = new MerkleBlock(mblock); - var ret = {}; + var reason; mblock2.merkleRoot = encoding.NULL_HASH; mblock2.refresh(); assert(!mblock2.verifyPOW()); - assert(!mblock2.verifyBody(ret)); + [, reason] = mblock2.checkBody(); + assert.equal(reason, 'bad-txnmrklroot'); assert(!mblock2.verify()); - assert.equal(ret.reason, 'bad-txnmrklroot'); mblock2.merkleRoot = mblock.merkleRoot; mblock2.refresh(); assert(mblock2.verify()); @@ -276,11 +276,11 @@ describe('Block', function() { it('should fail on duplicate txs', function() { var block2 = new Block(block); - var ret = {}; + var reason; block2.txs.push(block2.txs[block2.txs.length - 1]); block2.refresh(); - assert(!block2.verifyBody(ret)); - assert.equal(ret.reason, 'bad-txns-duplicate'); + [, reason] = block2.checkBody(); + assert.equal(reason, 'bad-txns-duplicate'); }); it('should verify with headers', function() { diff --git a/test/siphash-test.js b/test/siphash-test.js index f7ce660f..a7ed4846 100644 --- a/test/siphash-test.js +++ b/test/siphash-test.js @@ -3,36 +3,25 @@ var assert = require('assert'); var siphash = require('../lib/crypto/siphash'); var siphash256 = siphash.siphash256; -var U64 = siphash.U64; - -function toRaw(num) { - var data = Buffer.allocUnsafe(8); - data.writeUInt32LE(num.lo >>> 0, 0, true); - data.writeUInt32LE(num.hi >>> 0, 4, true); - return data; -}; describe('SipHash', function() { it('should perform siphash with no data', function() { - var k0 = U64(0x07060504, 0x03020100).toRaw(); - var k1 = U64(0x0f0e0d0c, 0x0b0a0908).toRaw(); - var key = Buffer.concat([k0, k1]); - assert.equal(toRaw(siphash256(Buffer.alloc(0), key)).toString('hex'), '310e0edd47db6f72'); + var data = Buffer.alloc(0); + var key = Buffer.from('000102030405060708090a0b0c0d0e0f', 'hex'); + assert.deepEqual(siphash256(data, key), [1919933255, -586281423]); }); it('should perform siphash with data', function() { - var k0 = U64(0x07060504, 0x03020100).toRaw(); - var k1 = U64(0x0f0e0d0c, 0x0b0a0908).toRaw(); - var data = U64(0x07060504, 0x03020100).toRaw(); - var key = Buffer.concat([k0, k1]); - assert.equal(toRaw(siphash256(data, key)).toString('hex'), '6224939a79f5f593'); + var data = Buffer.from('0001020304050607', 'hex'); + var key = Buffer.from('000102030405060708090a0b0c0d0e0f', 'hex'); + assert.deepEqual(siphash256(data, key), [-1812597383, -1701632926]); }); it('should perform siphash with uint256', function() { - var k0 = U64(0x07060504, 0x03020100).toRaw(); - var k1 = U64(0x0f0e0d0c, 0x0b0a0908).toRaw(); - var hash = Buffer.from('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', 'hex'); - var key = Buffer.concat([k0, k1]); - assert.equal(toRaw(siphash256(hash, key)).toString('hex'), 'ce7cf2722f512771'); + var data = Buffer.from( + '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', + 'hex'); + var key = Buffer.from('000102030405060708090a0b0c0d0e0f', 'hex'); + assert.deepEqual(siphash256(data, key), [1898402095, 1928494286]); }); });