refactor: start using multiple return values.

This commit is contained in:
Christopher Jeffrey 2017-06-29 01:36:12 -07:00
parent aa05bb5df7
commit 78d62c73b8
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
17 changed files with 337 additions and 504 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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 {

View File

@ -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);
};

View File

@ -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);
};
/**

View File

@ -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];
};
/**

View File

@ -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];
};
/**

View File

@ -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
*/

View File

@ -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;

View File

@ -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

View File

@ -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];
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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);
};

View File

@ -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() {

View File

@ -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]);
});
});