|
|
@ -31,6 +31,70 @@ var Interpreter = function Interpreter(obj) { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Interpreter.prototype.verifyWitnessProgram = function(version, program, witness, satoshis, flags) { |
|
|
|
var scriptPubKey = new Script; |
|
|
|
var stack = []; |
|
|
|
|
|
|
|
if (version == 0) { |
|
|
|
if (program.length == 32) { |
|
|
|
if (witness.length == 0) { |
|
|
|
this.errstr = 'v0 scripthash program empty'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
scriptPubKey = witness[witness.length - 1]; |
|
|
|
var hash = Hash.sha256(scriptPubKey); |
|
|
|
if (hash !== program.script) { |
|
|
|
this.errstr = 'witness program mismatch'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
stack = witness.slice(0, -1); |
|
|
|
} else if (program.script.length == 20) { |
|
|
|
if (witness.length != 2) { |
|
|
|
this.errstr = 'witness program mismatch'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
scriptPubKey.add(Opcode.OP_DUP).add(Opcode.OP_HASH160).add(program.script).add(Opcode.OP_EQUALVERIFY).add(Opcode.OP_CHECKSIG); |
|
|
|
stack = witness; |
|
|
|
} else { |
|
|
|
this.errstr = 'Witness program wrong length'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} else if ((flags & this.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM)) { |
|
|
|
this.errstr = 'Upgradeable witness program discouraged'; |
|
|
|
return false; |
|
|
|
} else { |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
this.set({ |
|
|
|
script: scriptPubKey, |
|
|
|
stack: stack, |
|
|
|
sigversion: 1, |
|
|
|
satoshis: satoshis |
|
|
|
}); |
|
|
|
|
|
|
|
if (!this.evaluate()) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
if (this.stack.length !== 1) { |
|
|
|
this.errstr = 'SCRIPT_ERR_EVAL_FALSE'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
var buf = this.stack[this.stack.length - 1]; |
|
|
|
if (!Interpreter.castToBool(buf)) { |
|
|
|
this.errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_STACK'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Verifies a Script by executing it and returns true if it is valid. |
|
|
|
* This function needs to be provided with the scriptSig and the scriptPubkey |
|
|
@ -41,10 +105,13 @@ var Interpreter = function Interpreter(obj) { |
|
|
|
* to check signature validity for some opcodes like OP_CHECKSIG) |
|
|
|
* @param {number} nin - index of the transaction input containing the scriptSig verified. |
|
|
|
* @param {number} flags - evaluation flags. See Interpreter.SCRIPT_* constants |
|
|
|
* @param {number} witness - array of witness data |
|
|
|
* @param {number} satoshis - number of satoshis created by this output |
|
|
|
* |
|
|
|
* Translated from bitcoind's VerifyScript |
|
|
|
*/ |
|
|
|
Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags) { |
|
|
|
Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags, witness, satoshis) { |
|
|
|
|
|
|
|
var Transaction = require('../transaction'); |
|
|
|
if (_.isUndefined(tx)) { |
|
|
|
tx = new Transaction(); |
|
|
@ -55,10 +122,19 @@ Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags) |
|
|
|
if (_.isUndefined(flags)) { |
|
|
|
flags = 0; |
|
|
|
} |
|
|
|
if (_.isUndefined(witness)) { |
|
|
|
witness = null; |
|
|
|
} |
|
|
|
if (_.isUndefined(satoshis)) { |
|
|
|
satoshis = 0; |
|
|
|
} |
|
|
|
|
|
|
|
this.set({ |
|
|
|
script: scriptSig, |
|
|
|
tx: tx, |
|
|
|
nin: nin, |
|
|
|
sigversion: 0, |
|
|
|
satoshis: 0, |
|
|
|
flags: flags |
|
|
|
}); |
|
|
|
var stackCopy; |
|
|
@ -103,6 +179,24 @@ Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags) |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
var hadWitness = false; |
|
|
|
var version, program; |
|
|
|
|
|
|
|
if ((flags & Interpreter.SCRIPT_VERIFY_WITNESS)) { |
|
|
|
if (scriptPubkey.isWitnessProgram()) { |
|
|
|
version = scriptPubkey[0]; |
|
|
|
program = s.toProgram(); |
|
|
|
hadWitness = true; |
|
|
|
if (scriptSig.toBuffer().length != 0) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
if (!this.verifyWitnessProgram(version, program, witness, satoshis, flags)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Additional validation for spend-to-script-hash transactions:
|
|
|
|
if ((flags & Interpreter.SCRIPT_VERIFY_P2SH) && scriptPubkey.isScriptHashOut()) { |
|
|
|
// scriptSig must be literals-only or validation fails
|
|
|
@ -144,8 +238,30 @@ Interpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin, flags) |
|
|
|
if (!Interpreter.castToBool(stackCopy[stackCopy.length - 1])) { |
|
|
|
this.errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_P2SH_STACK'; |
|
|
|
return false; |
|
|
|
} else { |
|
|
|
return true; |
|
|
|
} |
|
|
|
if ((flags & this.SCRIPT_VERIFY_WITNESS)) { |
|
|
|
if (redeemScript.isWitnessOut()) { |
|
|
|
version = redeemScript[0]; |
|
|
|
program = redeemScript.toProgram(); |
|
|
|
hadWitness = true; |
|
|
|
if (scriptSig !== redeemScript) { |
|
|
|
this.errstr = 'Malleated scriptSig'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
if (!this.verifyWitnessProgram(version, program, witness, satoshis, flags)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
stack = [stack[0]]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if ((flags & this.SCRIPT_VERIFY_WITNESS)) { |
|
|
|
if (!hadWitness && witness.length > 0) { |
|
|
|
this.errstr = 'Witness unexpected'; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -158,6 +274,8 @@ Interpreter.prototype.initialize = function(obj) { |
|
|
|
this.stack = []; |
|
|
|
this.altstack = []; |
|
|
|
this.pc = 0; |
|
|
|
this.satoshis = 0; |
|
|
|
this.sigversion = 0; |
|
|
|
this.pbegincodehash = 0; |
|
|
|
this.nOpCount = 0; |
|
|
|
this.vfExec = []; |
|
|
@ -173,6 +291,8 @@ Interpreter.prototype.set = function(obj) { |
|
|
|
this.altstack = obj.altack || this.altstack; |
|
|
|
this.pc = typeof obj.pc !== 'undefined' ? obj.pc : this.pc; |
|
|
|
this.pbegincodehash = typeof obj.pbegincodehash !== 'undefined' ? obj.pbegincodehash : this.pbegincodehash; |
|
|
|
this.sigversion = typeof obj.sigversion !== 'undefined' ? obj.sigversion : this.sigversion; |
|
|
|
this.satoshis = typeof obj.satoshis !== 'undefined' ? obj.satoshis : this.satoshis; |
|
|
|
this.nOpCount = typeof obj.nOpCount !== 'undefined' ? obj.nOpCount : this.nOpCount; |
|
|
|
this.vfExec = obj.vfExec || this.vfExec; |
|
|
|
this.errstr = obj.errstr || this.errstr; |
|
|
@ -231,6 +351,8 @@ Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 7); |
|
|
|
|
|
|
|
// CLTV See BIP65 for details.
|
|
|
|
Interpreter.SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY = (1 << 9); |
|
|
|
Interpreter.SCRIPT_VERIFY_WITNESS = (1 << 10); |
|
|
|
Interpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = (1 << 11); |
|
|
|
|
|
|
|
Interpreter.castToBool = function(buf) { |
|
|
|
for (var i = 0; i < buf.length; i++) { |
|
|
@ -1111,7 +1233,7 @@ Interpreter.prototype.step = function() { |
|
|
|
try { |
|
|
|
sig = Signature.fromTxFormat(bufSig); |
|
|
|
pubkey = PublicKey.fromBuffer(bufPubkey, false); |
|
|
|
fSuccess = this.tx.verifySignature(sig, pubkey, this.nin, subscript); |
|
|
|
fSuccess = this.tx.verifySignature(sig, pubkey, this.nin, subscript, this.sigversion); |
|
|
|
} catch (e) { |
|
|
|
//invalid sig or pubkey
|
|
|
|
fSuccess = false; |
|
|
@ -1200,7 +1322,7 @@ Interpreter.prototype.step = function() { |
|
|
|
try { |
|
|
|
sig = Signature.fromTxFormat(bufSig); |
|
|
|
pubkey = PublicKey.fromBuffer(bufPubkey, false); |
|
|
|
fOk = this.tx.verifySignature(sig, pubkey, this.nin, subscript); |
|
|
|
fOk = this.tx.verifySignature(sig, pubkey, this.nin, subscript, this.sigversion); |
|
|
|
} catch (e) { |
|
|
|
//invalid sig or pubkey
|
|
|
|
fOk = false; |
|
|
|