From adbb0099a1f40e17405a509e2598a50e152a9a24 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 8 Dec 2015 11:24:22 -0800 Subject: [PATCH] add checklocktimeverify. pass in tx and input to execute() instead of using hasher callback. --- lib/bcoin/protocol/constants.js | 5 +++- lib/bcoin/script.js | 49 +++++++++++++++++++++++++++------ lib/bcoin/tx.js | 7 ++--- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/lib/bcoin/protocol/constants.js b/lib/bcoin/protocol/constants.js index f52804e7..e22dcb83 100644 --- a/lib/bcoin/protocol/constants.js +++ b/lib/bcoin/protocol/constants.js @@ -137,7 +137,8 @@ exports.opcodes = { checksig: 0xac, checksigverify: 0xad, checkmultisig: 0xae, - checkmultisigverify: 0xaf + checkmultisigverify: 0xaf, + checklocktimeverify: 0xb1 }; for (var i = 1; i <= 16; i++) @@ -169,3 +170,5 @@ exports.block = { maxSigops: 1000000 / 50, maxOrphanTx: 1000000 / 100 }; + +exports.locktimeThreshold = 500000000; // Tue Nov 5 00:53:20 1985 UTC diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 615d1e96..0adc2478 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -130,10 +130,12 @@ script.verify = function verify(hash, sig, pub) { } }; -script.execute = function execute(s, stack, hasher) { +script.execute = function execute(s, stack, tx, index) { if (s.length > 10000) return false; + var input = tx.inputs[index]; + for (var pc = 0; pc < s.length; pc++) { var o = s[pc]; if (Array.isArray(o)) { @@ -145,6 +147,8 @@ script.execute = function execute(s, stack, hasher) { return false; stack.push(stack[stack.length - 1]); + } else if (o === 'drop') { + stack.pop(); } else if (o === 'hash160') { if (stack.length === 0) return false; @@ -163,7 +167,7 @@ script.execute = function execute(s, stack, hasher) { } } else if (o === 'checksigverify' || o === 'checksig') { - if (!hasher || stack.length < 2) + if (!tx || stack.length < 2) return false; var pub = stack.pop(); @@ -172,9 +176,8 @@ script.execute = function execute(s, stack, hasher) { if (!constants.rhashType[type & 0x7f]) return false; - var hash = hasher; - if (typeof hash === 'function') - hash = hash(type); + var subscript = input.out.tx.getSubscript(input.out.index); + var hash = tx.subscriptHash(index, subscript, type); var res = script.verify(hash, sig.slice(0, -1), pub); if (o === 'checksigverify') { @@ -184,7 +187,7 @@ script.execute = function execute(s, stack, hasher) { stack.push(res ? [ 1 ] : []); } } else if (o === 'checkmultisigverify' || o === 'checkmultisig') { - if (!hasher || stack.length < 3) + if (!tx || stack.length < 3) return false; var n = stack.pop(); @@ -220,9 +223,8 @@ script.execute = function execute(s, stack, hasher) { if (!constants.rhashType[type & 0x7f]) return false; - var hash = hasher; - if (typeof hash === 'function') - hash = hash(type); + var subscript = input.out.tx.getSubscript(input.out.index); + var hash = tx.subscriptHash(index, subscript, type); var res = false; for (; !res && j < n; j++) @@ -241,6 +243,35 @@ script.execute = function execute(s, stack, hasher) { } else { stack.push(res ? [ 1 ] : []); } + } else if (o === 'checklocktimeverify') { + // input: [[], sig1, sig2, 1] + // prev_out: [[lock], 'checklocktimeverify', 'drop', + // 'dup', 'hash160', pubkey, 'equalverify', 'checksig'] + if (stack.length === 0) + return false; + + var lock = stack[stack.length - 1]; + if (lock.length !== 1) + return false; + + lock = lock[0]; + + if (lock < 0) + return false; + + var threshold = constants.locktimeThreshold; + if (!( + (tx.lock < threshold && lock < threshold) || + (tx.lock >= threshold && lock >= threshold) + )) { + return false; + } + + if (lock > tx.lock) + return false; + + if (input.seq === 0xffffffff) + return false; } else { // Unknown operation return false; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index d677b5c2..5b7fbc5c 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -427,13 +427,10 @@ TX.prototype.verify = function verify(index, force) { assert(input.out.tx.outputs.length > input.out.index); - var subscript = input.out.tx.getSubscript(input.out.index); - var hasher = this.subscriptHash.bind(this, i, subscript); - var stack = []; - bcoin.script.execute(input.script, stack); + bcoin.script.execute(input.script, stack, this, i); var prev = input.out.tx.outputs[input.out.index].script; - var res = bcoin.script.execute(prev, stack, hasher); + var res = bcoin.script.execute(prev, stack, this, i); if (!res) return false;