@ -19,11 +19,11 @@ var PublicKey = require('../publickey');
* "true" value , then the transaction is valid .
*
* The primary way to use this class is via the verify function .
* e . g . , Script Interpreter( ) . verify ( ... ) ;
* e . g . , Interpreter ( ) . verify ( ... ) ;
* /
var Script Interpreter = function Script Interpreter( obj ) {
if ( ! ( this instanceof Script Interpreter) ) {
return new Script Interpreter( obj ) ;
var Interpreter = function Interpreter ( obj ) {
if ( ! ( this instanceof Interpreter ) ) {
return new Interpreter ( obj ) ;
}
if ( obj ) {
this . initialize ( ) ;
@ -33,9 +33,130 @@ var ScriptInterpreter = function ScriptInterpreter(obj) {
}
} ;
module . exports = ScriptInterpreter ;
/ * *
* 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
* separately .
* @ param { Script } scriptSig - the script ' s first part ( corresponding to the tx input )
* @ param { Script } scriptPubkey - the script ' s last part ( corresponding to the tx output )
* @ param { Transaction } [ tx ] - the Transaction containing the scriptSig in one input ( used
* 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
*
* Translated from bitcoind ' s VerifyScript
* /
Interpreter . prototype . verify = function ( scriptSig , scriptPubkey , tx , nin , flags ) {
var Transaction = require ( '../transaction' ) ;
if ( _ . isUndefined ( tx ) ) {
tx = new Transaction ( ) ;
}
if ( _ . isUndefined ( nin ) ) {
nin = 0 ;
}
if ( _ . isUndefined ( flags ) ) {
flags = 0 ;
}
this . set ( {
script : scriptSig ,
tx : tx ,
nin : nin ,
flags : flags
} ) ;
var stackCopy ;
if ( ( flags & Interpreter . SCRIPT_VERIFY_SIGPUSHONLY ) !== 0 && ! scriptSig . isPushOnly ( ) ) {
this . errstr = 'SCRIPT_ERR_SIG_PUSHONLY' ;
return false ;
}
// evaluate scriptSig
if ( ! this . evaluate ( ) ) {
return false ;
}
if ( flags & Interpreter . SCRIPT_VERIFY_P2SH ) {
stackCopy = this . stack . slice ( ) ;
}
var stack = this . stack ;
this . initialize ( ) ;
this . set ( {
script : scriptPubkey ,
stack : stack ,
tx : tx ,
nin : nin ,
flags : flags
} ) ;
// evaluate scriptPubkey
if ( ! this . evaluate ( ) ) {
return false ;
}
if ( this . stack . length === 0 ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_NO_RESULT' ;
return false ;
}
var buf = this . stack [ this . stack . length - 1 ] ;
if ( ! Interpreter . castToBool ( buf ) ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_STACK' ;
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
if ( ! scriptSig . isPushOnly ( ) ) {
this . errstr = 'SCRIPT_ERR_SIG_PUSHONLY' ;
return false ;
}
// stackCopy cannot be empty here, because if it was the
// P2SH HASH <> EQUAL scriptPubKey would be evaluated with
// an empty stack and the EvalScript above would return false.
if ( stackCopy . length === 0 ) {
throw new Error ( 'internal error - stack copy empty' ) ;
}
var redeemScriptSerialized = stackCopy [ stackCopy . length - 1 ] ;
var redeemScript = Script . fromBuffer ( redeemScriptSerialized ) ;
stackCopy . pop ( ) ;
this . initialize ( ) ;
this . set ( {
script : redeemScript ,
stack : stackCopy ,
tx : tx ,
nin : nin ,
flags : flags
} ) ;
// evaluate redeemScript
if ( ! this . evaluate ( ) ) {
return false ;
}
if ( stackCopy . length === 0 ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_NO_P2SH_STACK' ;
return false ;
}
if ( ! Interpreter . castToBool ( stackCopy [ stackCopy . length - 1 ] ) ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_P2SH_STACK' ;
return false ;
} else {
return true ;
}
}
return true ;
} ;
ScriptInterpreter . prototype . initialize = function ( obj ) {
module . exports = Interpreter ;
Interpreter . prototype . initialize = function ( obj ) {
this . stack = [ ] ;
this . altstack = [ ] ;
this . pc = 0 ;
@ -46,7 +167,7 @@ ScriptInterpreter.prototype.initialize = function(obj) {
this . flags = 0 ;
} ;
Script Interpreter. prototype . set = function ( obj ) {
Interpreter . prototype . set = function ( obj ) {
this . script = obj . script || this . script ;
this . tx = obj . tx || this . tx ;
this . nin = typeof obj . nin !== 'undefined' ? obj . nin : this . nin ;
@ -60,42 +181,42 @@ ScriptInterpreter.prototype.set = function(obj) {
this . flags = typeof obj . flags !== 'undefined' ? obj . flags : this . flags ;
} ;
Script Interpreter. true = new Buffer ( [ 1 ] ) ;
Script Interpreter. false = new Buffer ( [ ] ) ;
Interpreter . true = new Buffer ( [ 1 ] ) ;
Interpreter . false = new Buffer ( [ ] ) ;
Script Interpreter. MAX_SCRIPT_ELEMENT_SIZE = 520 ;
Interpreter . MAX_SCRIPT_ELEMENT_SIZE = 520 ;
// flags taken from bitcoind
// bitcoind commit: b5d1b1092998bc95313856d535c632ea5a8f9104
Script Interpreter. SCRIPT_VERIFY_NONE = 0 ;
Interpreter . SCRIPT_VERIFY_NONE = 0 ;
// Evaluate P2SH subscripts (softfork safe, BIP16).
Script Interpreter. SCRIPT_VERIFY_P2SH = ( 1 << 0 ) ;
Interpreter . SCRIPT_VERIFY_P2SH = ( 1 << 0 ) ;
// Passing a non-strict-DER signature or one with undefined hashtype to a checksig operation causes script failure.
// Passing a pubkey that is not (0x04 + 64 bytes) or (0x02 or 0x03 + 32 bytes) to checksig causes that pubkey to be
// skipped (not softfork safe: this flag can widen the validity of OP_CHECKSIG OP_NOT).
Script Interpreter. SCRIPT_VERIFY_STRICTENC = ( 1 << 1 ) ;
Interpreter . SCRIPT_VERIFY_STRICTENC = ( 1 << 1 ) ;
// Passing a non-strict-DER signature to a checksig operation causes script failure (softfork safe, BIP62 rule 1)
Script Interpreter. SCRIPT_VERIFY_DERSIG = ( 1 << 2 ) ;
Interpreter . SCRIPT_VERIFY_DERSIG = ( 1 << 2 ) ;
// Passing a non-strict-DER signature or one with S > order/2 to a checksig operation causes script failure
// (softfork safe, BIP62 rule 5).
Script Interpreter. SCRIPT_VERIFY_LOW_S = ( 1 << 3 ) ;
Interpreter . SCRIPT_VERIFY_LOW_S = ( 1 << 3 ) ;
// verify dummy stack item consumed by CHECKMULTISIG is of zero-length (softfork safe, BIP62 rule 7).
Script Interpreter. SCRIPT_VERIFY_NULLDUMMY = ( 1 << 4 ) ;
Interpreter . SCRIPT_VERIFY_NULLDUMMY = ( 1 << 4 ) ;
// Using a non-push operator in the scriptSig causes script failure (softfork safe, BIP62 rule 2).
Script Interpreter. SCRIPT_VERIFY_SIGPUSHONLY = ( 1 << 5 ) ;
Interpreter . SCRIPT_VERIFY_SIGPUSHONLY = ( 1 << 5 ) ;
// Require minimal encodings for all push operations (OP_0... OP_16, OP_1NEGATE where possible, direct
// pushes up to 75 bytes, OP_PUSHDATA up to 255 bytes, OP_PUSHDATA2 for anything larger). Evaluating
// any other push causes the script to fail (BIP62 rule 3).
// In addition, whenever a stack element is interpreted as a number, it must be of minimal length (BIP62 rule 4).
// (softfork safe)
Script Interpreter. SCRIPT_VERIFY_MINIMALDATA = ( 1 << 6 ) ;
Interpreter . SCRIPT_VERIFY_MINIMALDATA = ( 1 << 6 ) ;
// Discourage use of NOPs reserved for upgrades (NOP1-10)
//
@ -105,9 +226,9 @@ ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA = (1 << 6);
// discouraged NOPs fails the script. This verification flag will never be
// a mandatory flag applied to scripts in a block. NOPs that are not
// executed, e.g. within an unexecuted IF ENDIF block, are *not* rejected.
Script Interpreter. SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = ( 1 << 7 ) ;
Interpreter . SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS = ( 1 << 7 ) ;
Script Interpreter. castToBool = function ( buf ) {
Interpreter . castToBool = function ( buf ) {
for ( var i = 0 ; i < buf . length ; i ++ ) {
if ( buf [ i ] !== 0 ) {
// can be negative zero
@ -123,18 +244,18 @@ ScriptInterpreter.castToBool = function(buf) {
/ * *
* Translated from bitcoind ' s CheckSignatureEncoding
* /
Script Interpreter. prototype . checkSignatureEncoding = function ( buf ) {
Interpreter . prototype . checkSignatureEncoding = function ( buf ) {
var sig ;
if ( ( this . flags & ( Script Interpreter. SCRIPT_VERIFY_DERSIG | Script Interpreter. SCRIPT_VERIFY_LOW_S | Script Interpreter. SCRIPT_VERIFY_STRICTENC ) ) !== 0 && ! Signature . isTxDER ( buf ) ) {
if ( ( this . flags & ( Interpreter . SCRIPT_VERIFY_DERSIG | Interpreter . SCRIPT_VERIFY_LOW_S | Interpreter . SCRIPT_VERIFY_STRICTENC ) ) !== 0 && ! Signature . isTxDER ( buf ) ) {
this . errstr = 'SCRIPT_ERR_SIG_DER_INVALID_FORMAT' ;
return false ;
} else if ( ( this . flags & Script Interpreter. SCRIPT_VERIFY_LOW_S ) !== 0 ) {
} else if ( ( this . flags & Interpreter . SCRIPT_VERIFY_LOW_S ) !== 0 ) {
sig = Signature . fromTxFormat ( buf ) ;
if ( ! sig . hasLowS ( ) ) {
this . errstr = 'SCRIPT_ERR_SIG_DER_HIGH_S' ;
return false ;
}
} else if ( ( this . flags & Script Interpreter. SCRIPT_VERIFY_STRICTENC ) !== 0 ) {
} else if ( ( this . flags & Interpreter . SCRIPT_VERIFY_STRICTENC ) !== 0 ) {
sig = Signature . fromTxFormat ( buf ) ;
if ( ! sig . hasDefinedHashtype ( ) ) {
this . errstr = 'SCRIPT_ERR_SIG_HASHTYPE' ;
@ -147,8 +268,8 @@ ScriptInterpreter.prototype.checkSignatureEncoding = function(buf) {
/ * *
* Translated from bitcoind ' s CheckPubKeyEncoding
* /
Script Interpreter. prototype . checkPubkeyEncoding = function ( buf ) {
if ( ( this . flags & Script Interpreter. SCRIPT_VERIFY_STRICTENC ) !== 0 && ! PublicKey . isValid ( buf ) ) {
Interpreter . prototype . checkPubkeyEncoding = function ( buf ) {
if ( ( this . flags & Interpreter . SCRIPT_VERIFY_STRICTENC ) !== 0 && ! PublicKey . isValid ( buf ) ) {
this . errstr = 'SCRIPT_ERR_PUBKEYTYPE' ;
return false ;
}
@ -157,10 +278,10 @@ ScriptInterpreter.prototype.checkPubkeyEncoding = function(buf) {
/ * *
* Based on bitcoind ' s EvalScript function , with the inner loop moved to
* Script Interpreter. prototype . step ( )
* Interpreter . prototype . step ( )
* bitcoind commit : b5d1b1092998bc95313856d535c632ea5a8f9104
* /
Script Interpreter. prototype . evaluate = function ( ) {
Interpreter . prototype . evaluate = function ( ) {
if ( this . script . toBuffer ( ) . length > 10000 ) {
this . errstr = 'SCRIPT_ERR_SCRIPT_SIZE' ;
return false ;
@ -196,9 +317,9 @@ ScriptInterpreter.prototype.evaluate = function() {
* Based on the inner loop of bitcoind ' s EvalScript function
* bitcoind commit : b5d1b1092998bc95313856d535c632ea5a8f9104
* /
Script Interpreter. prototype . step = function ( ) {
Interpreter . prototype . step = function ( ) {
var fRequireMinimal = ( this . flags & Script Interpreter. SCRIPT_VERIFY_MINIMALDATA ) !== 0 ;
var fRequireMinimal = ( this . flags & Interpreter . SCRIPT_VERIFY_MINIMALDATA ) !== 0 ;
//bool fExec = !count(vfExec.begin(), vfExec.end(), false);
var fExec = ( this . vfExec . indexOf ( false ) === - 1 ) ;
@ -212,7 +333,7 @@ ScriptInterpreter.prototype.step = function() {
this . errstr = 'SCRIPT_ERR_UNDEFINED_OPCODE' ;
return false ;
}
if ( chunk . buf && chunk . buf . length > Script Interpreter. MAX_SCRIPT_ELEMENT_SIZE ) {
if ( chunk . buf && chunk . buf . length > Interpreter . MAX_SCRIPT_ELEMENT_SIZE ) {
this . errstr = 'SCRIPT_ERR_PUSH_SIZE' ;
return false ;
}
@ -249,7 +370,7 @@ ScriptInterpreter.prototype.step = function() {
return false ;
}
if ( ! chunk . buf ) {
this . stack . push ( Script Interpreter. false ) ;
this . stack . push ( Interpreter . false ) ;
} else if ( chunk . len !== chunk . buf . length ) {
throw new Error ( 'Length of push value not equal to length of data' ) ;
} else {
@ -304,7 +425,7 @@ ScriptInterpreter.prototype.step = function() {
case Opcode . OP_NOP9 :
case Opcode . OP_NOP10 :
{
if ( this . flags & Script Interpreter. SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS ) {
if ( this . flags & Interpreter . SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS ) {
this . errstr = 'SCRIPT_ERR_DISCOURAGE_UPGRADABLE_NOPS' ;
return false ;
}
@ -323,7 +444,7 @@ ScriptInterpreter.prototype.step = function() {
return false ;
}
var buf = this . stack . pop ( ) ;
fValue = Script Interpreter. castToBool ( buf ) ;
fValue = Interpreter . castToBool ( buf ) ;
if ( opcodenum === Opcode . OP_NOTIF )
fValue = ! fValue ;
}
@ -360,7 +481,7 @@ ScriptInterpreter.prototype.step = function() {
return false ;
}
var buf = this . stack [ this . stack . length - 1 ] ;
var fValue = Script Interpreter. castToBool ( buf ) ;
var fValue = Interpreter . castToBool ( buf ) ;
if ( fValue )
this . stack . pop ( ) ;
else {
@ -491,7 +612,7 @@ ScriptInterpreter.prototype.step = function() {
return false ;
}
var buf = this . stack [ this . stack . length - 1 ] ;
var fValue = Script Interpreter. castToBool ( buf ) ;
var fValue = Interpreter . castToBool ( buf ) ;
if ( fValue )
this . stack . push ( buf ) ;
}
@ -652,7 +773,7 @@ ScriptInterpreter.prototype.step = function() {
// fEqual = !fEqual;
this . stack . pop ( ) ;
this . stack . pop ( ) ;
this . stack . push ( fEqual ? Script Interpreter. true : Script Interpreter. false ) ;
this . stack . push ( fEqual ? Interpreter . true : Interpreter . false ) ;
if ( opcodenum === Opcode . OP_EQUALVERIFY ) {
if ( fEqual )
this . stack . pop ( ) ;
@ -790,7 +911,7 @@ ScriptInterpreter.prototype.step = function() {
if ( opcodenum === Opcode . OP_NUMEQUALVERIFY ) {
// if (CastToBool(stacktop(-1)))
if ( Script Interpreter. castToBool ( this . stack [ this . stack . length - 1 ] ) )
if ( Interpreter . castToBool ( this . stack [ this . stack . length - 1 ] ) )
this . stack . pop ( ) ;
else {
this . errstr = 'SCRIPT_ERR_NUMEQUALVERIFY' ;
@ -815,7 +936,7 @@ ScriptInterpreter.prototype.step = function() {
this . stack . pop ( ) ;
this . stack . pop ( ) ;
this . stack . pop ( ) ;
this . stack . push ( fValue ? Script Interpreter. true : Script Interpreter. false ) ;
this . stack . push ( fValue ? Interpreter . true : Interpreter . false ) ;
}
break ;
@ -898,7 +1019,7 @@ ScriptInterpreter.prototype.step = function() {
this . stack . pop ( ) ;
this . stack . pop ( ) ;
// stack.push_back(fSuccess ? vchTrue : vchFalse);
this . stack . push ( fSuccess ? Script Interpreter. true : Script Interpreter. false ) ;
this . stack . push ( fSuccess ? Interpreter . true : Interpreter . false ) ;
if ( opcodenum === Opcode . OP_CHECKSIGVERIFY ) {
if ( fSuccess ) {
this . stack . pop ( ) ;
@ -1013,13 +1134,13 @@ ScriptInterpreter.prototype.step = function() {
this . errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION' ;
return false ;
}
if ( ( this . flags & Script Interpreter. SCRIPT_VERIFY_NULLDUMMY ) && this . stack [ this . stack . length - 1 ] . length ) {
if ( ( this . flags & Interpreter . SCRIPT_VERIFY_NULLDUMMY ) && this . stack [ this . stack . length - 1 ] . length ) {
this . errstr = 'SCRIPT_ERR_SIG_NULLDUMMY' ;
return false ;
}
this . stack . pop ( ) ;
this . stack . push ( fSuccess ? Script Interpreter. true : Script Interpreter. false ) ;
this . stack . push ( fSuccess ? Interpreter . true : Interpreter . false ) ;
if ( opcodenum === Opcode . OP_CHECKMULTISIGVERIFY ) {
if ( fSuccess )
@ -1038,121 +1159,6 @@ ScriptInterpreter.prototype.step = function() {
}
}
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
* separately .
* @ param { Script } scriptSig - the script ' s first part ( corresponding to the tx input )
* @ param { Script } scriptPubkey - the script ' s last part ( corresponding to the tx output )
* @ param { Transaction } [ tx ] - the Transaction containing the scriptSig in one input ( used
* 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 ScriptInterpreter . SCRIPT_ * constants
*
* Translated from bitcoind ' s VerifyScript
* /
ScriptInterpreter . prototype . verify = function ( scriptSig , scriptPubkey , tx , nin , flags ) {
var Transaction = require ( '../transaction' ) ;
if ( _ . isUndefined ( tx ) ) {
tx = new Transaction ( ) ;
}
if ( _ . isUndefined ( nin ) ) {
nin = 0 ;
}
if ( _ . isUndefined ( flags ) ) {
flags = 0 ;
}
this . set ( {
script : scriptSig ,
tx : tx ,
nin : nin ,
flags : flags
} ) ;
if ( ( flags & ScriptInterpreter . SCRIPT_VERIFY_SIGPUSHONLY ) != 0 && ! scriptSig . isPushOnly ( ) ) {
this . errstr = 'SCRIPT_ERR_SIG_PUSHONLY' ;
return false ;
}
// evaluate scriptSig
if ( ! this . evaluate ( ) ) {
return false ;
}
if ( flags & ScriptInterpreter . SCRIPT_VERIFY_P2SH )
var stackCopy = this . stack . slice ( ) ;
var stack = this . stack ;
this . initialize ( ) ;
this . set ( {
script : scriptPubkey ,
stack : stack ,
tx : tx ,
nin : nin ,
flags : flags
} ) ;
// evaluate scriptPubkey
if ( ! this . evaluate ( ) )
return false ;
if ( this . stack . length === 0 ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_NO_RESULT' ;
return false ;
}
var buf = this . stack [ this . stack . length - 1 ] ;
if ( ! ScriptInterpreter . castToBool ( buf ) ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_STACK' ;
return false ;
}
// Additional validation for spend-to-script-hash transactions:
if ( ( flags & ScriptInterpreter . SCRIPT_VERIFY_P2SH ) && scriptPubkey . isScriptHashOut ( ) ) {
// scriptSig must be literals-only or validation fails
if ( ! scriptSig . isPushOnly ( ) ) {
this . errstr = 'SCRIPT_ERR_SIG_PUSHONLY' ;
return false ;
}
// stackCopy cannot be empty here, because if it was the
// P2SH HASH <> EQUAL scriptPubKey would be evaluated with
// an empty stack and the EvalScript above would return false.
if ( stackCopy . length === 0 )
throw new Error ( 'internal error - stack copy empty' ) ;
var redeemScriptSerialized = stackCopy [ stackCopy . length - 1 ] ;
var redeemScript = Script . fromBuffer ( redeemScriptSerialized ) ;
stackCopy . pop ( ) ;
this . initialize ( ) ;
this . set ( {
script : redeemScript ,
stack : stackCopy ,
tx : tx ,
nin : nin ,
flags : flags
} ) ;
// evaluate redeemScript
if ( ! this . evaluate ( ) )
return false ;
if ( stackCopy . length === 0 ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_NO_P2SH_STACK' ;
return false ;
}
if ( ! ScriptInterpreter . castToBool ( stackCopy [ stackCopy . length - 1 ] ) ) {
this . errstr = 'SCRIPT_ERR_EVAL_FALSE_IN_P2SH_STACK' ;
return false ;
} else {
return true ;
}
}
return true ;
} ;