Browse Source

Add support for verifying segwit scripts

patch-2
Thomas Kerin 9 years ago
committed by Braydon Fuller
parent
commit
b48bb42953
  1. 132
      lib/script/interpreter.js

132
lib/script/interpreter.js

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

Loading…
Cancel
Save