refactor: start using multiple return values.
This commit is contained in:
parent
aa05bb5df7
commit
78d62c73b8
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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];
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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]);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user