Merge pull request #827 from eordano/lint/interpreter

Lint script/interpreter
This commit is contained in:
Manuel Aráoz 2014-12-19 15:26:35 -03:00
commit ccf9527bd7
1 changed files with 79 additions and 73 deletions

View File

@ -6,8 +6,6 @@ var Script = require('./script');
var Opcode = require('../opcode'); var Opcode = require('../opcode');
var BN = require('../crypto/bn'); var BN = require('../crypto/bn');
var Hash = require('../crypto/hash'); var Hash = require('../crypto/hash');
var BufferReader = require('../encoding/bufferreader');
var BufferWriter = require('../encoding/bufferwriter');
var Signature = require('../crypto/signature'); var Signature = require('../crypto/signature');
var PublicKey = require('../publickey'); var PublicKey = require('../publickey');
@ -323,7 +321,9 @@ Interpreter.prototype.step = function() {
//bool fExec = !count(vfExec.begin(), vfExec.end(), false); //bool fExec = !count(vfExec.begin(), vfExec.end(), false);
var fExec = (this.vfExec.indexOf(false) === -1); var fExec = (this.vfExec.indexOf(false) === -1);
var buf, buf1, buf2, spliced, n, x1, x2, bn, bn1, bn2, bufSig, bufPubkey, subscript;
var sig, pubkey;
var fValue, fSuccess;
// Read instruction // Read instruction
var chunk = this.script.chunks[this.pc]; var chunk = this.script.chunks[this.pc];
@ -399,8 +399,8 @@ Interpreter.prototype.step = function() {
{ {
// ( -- value) // ( -- value)
// ScriptNum bn((int)opcode - (int)(Opcode.OP_1 - 1)); // ScriptNum bn((int)opcode - (int)(Opcode.OP_1 - 1));
var n = opcodenum - (Opcode.OP_1 - 1); n = opcodenum - (Opcode.OP_1 - 1);
var buf = BN(n).toScriptNumBuffer(); buf = BN(n).toScriptNumBuffer();
this.stack.push(buf); this.stack.push(buf);
// The result of these opcodes should always be the minimal way to push the data // The result of these opcodes should always be the minimal way to push the data
// they push, so no need for a CheckMinimalPush here. // they push, so no need for a CheckMinimalPush here.
@ -437,16 +437,17 @@ Interpreter.prototype.step = function() {
{ {
// <expression> if [statements] [else [statements]] endif // <expression> if [statements] [else [statements]] endif
// bool fValue = false; // bool fValue = false;
var fValue = false; fValue = false;
if (fExec) { if (fExec) {
if (this.stack.length < 1) { if (this.stack.length < 1) {
this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL'; this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL';
return false; return false;
} }
var buf = this.stack.pop(); buf = this.stack.pop();
fValue = Interpreter.castToBool(buf); fValue = Interpreter.castToBool(buf);
if (opcodenum === Opcode.OP_NOTIF) if (opcodenum === Opcode.OP_NOTIF) {
fValue = !fValue; fValue = !fValue;
}
} }
this.vfExec.push(fValue); this.vfExec.push(fValue);
} }
@ -480,11 +481,11 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf = this.stack[this.stack.length - 1]; buf = this.stack[this.stack.length - 1];
var fValue = Interpreter.castToBool(buf); fValue = Interpreter.castToBool(buf);
if (fValue) if (fValue) {
this.stack.pop(); this.stack.pop();
else { } else {
this.errstr = 'SCRIPT_ERR_VERIFY'; this.errstr = 'SCRIPT_ERR_VERIFY';
return false; return false;
} }
@ -541,8 +542,8 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf1 = this.stack[this.stack.length - 2]; buf1 = this.stack[this.stack.length - 2];
var buf2 = this.stack[this.stack.length - 1]; buf2 = this.stack[this.stack.length - 1];
this.stack.push(buf1); this.stack.push(buf1);
this.stack.push(buf2); this.stack.push(buf2);
} }
@ -555,8 +556,8 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf1 = this.stack[this.stack.length - 3]; buf1 = this.stack[this.stack.length - 3];
var buf2 = this.stack[this.stack.length - 2]; buf2 = this.stack[this.stack.length - 2];
var buf3 = this.stack[this.stack.length - 1]; var buf3 = this.stack[this.stack.length - 1];
this.stack.push(buf1); this.stack.push(buf1);
this.stack.push(buf2); this.stack.push(buf2);
@ -571,8 +572,8 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf1 = this.stack[this.stack.length - 4]; buf1 = this.stack[this.stack.length - 4];
var buf2 = this.stack[this.stack.length - 3]; buf2 = this.stack[this.stack.length - 3];
this.stack.push(buf1); this.stack.push(buf1);
this.stack.push(buf2); this.stack.push(buf2);
} }
@ -585,7 +586,7 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var spliced = this.stack.splice(this.stack.length - 6, 2); spliced = this.stack.splice(this.stack.length - 6, 2);
this.stack.push(spliced[0]); this.stack.push(spliced[0]);
this.stack.push(spliced[1]); this.stack.push(spliced[1]);
} }
@ -598,7 +599,7 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var spliced = this.stack.splice(this.stack.length - 4, 2); spliced = this.stack.splice(this.stack.length - 4, 2);
this.stack.push(spliced[0]); this.stack.push(spliced[0]);
this.stack.push(spliced[1]); this.stack.push(spliced[1]);
} }
@ -611,17 +612,18 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf = this.stack[this.stack.length - 1]; buf = this.stack[this.stack.length - 1];
var fValue = Interpreter.castToBool(buf); fValue = Interpreter.castToBool(buf);
if (fValue) if (fValue) {
this.stack.push(buf); this.stack.push(buf);
}
} }
break; break;
case Opcode.OP_DEPTH: case Opcode.OP_DEPTH:
{ {
// -- stacksize // -- stacksize
var buf = BN(this.stack.length).toScriptNumBuffer(); buf = BN(this.stack.length).toScriptNumBuffer();
this.stack.push(buf); this.stack.push(buf);
} }
break; break;
@ -679,17 +681,18 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf = this.stack[this.stack.length - 1]; buf = this.stack[this.stack.length - 1];
var bn = BN().fromScriptNumBuffer(buf, fRequireMinimal); bn = BN().fromScriptNumBuffer(buf, fRequireMinimal);
var n = bn.toNumber(); n = bn.toNumber();
this.stack.pop(); this.stack.pop();
if (n < 0 || n >= this.stack.length) { if (n < 0 || n >= this.stack.length) {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf = this.stack[this.stack.length - n - 1]; buf = this.stack[this.stack.length - n - 1];
if (opcodenum === Opcode.OP_ROLL) if (opcodenum === Opcode.OP_ROLL) {
this.stack.splice(this.stack.length - n - 1, 1); this.stack.splice(this.stack.length - n - 1, 1);
}
this.stack.push(buf); this.stack.push(buf);
} }
break; break;
@ -703,8 +706,8 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var x1 = this.stack[this.stack.length - 3]; x1 = this.stack[this.stack.length - 3];
var x2 = this.stack[this.stack.length - 2]; x2 = this.stack[this.stack.length - 2];
var x3 = this.stack[this.stack.length - 1]; var x3 = this.stack[this.stack.length - 1];
this.stack[this.stack.length - 3] = x2; this.stack[this.stack.length - 3] = x2;
this.stack[this.stack.length - 2] = x3; this.stack[this.stack.length - 2] = x3;
@ -719,8 +722,8 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var x1 = this.stack[this.stack.length - 2]; x1 = this.stack[this.stack.length - 2];
var x2 = this.stack[this.stack.length - 1]; x2 = this.stack[this.stack.length - 1];
this.stack[this.stack.length - 2] = x2; this.stack[this.stack.length - 2] = x2;
this.stack[this.stack.length - 1] = x1; this.stack[this.stack.length - 1] = x1;
} }
@ -745,7 +748,7 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var bn = BN(this.stack[this.stack.length - 1].length); bn = BN(this.stack[this.stack.length - 1].length);
this.stack.push(bn.toScriptNumBuffer()); this.stack.push(bn.toScriptNumBuffer());
} }
break; break;
@ -763,8 +766,8 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf1 = this.stack[this.stack.length - 2]; buf1 = this.stack[this.stack.length - 2];
var buf2 = this.stack[this.stack.length - 1]; buf2 = this.stack[this.stack.length - 1];
var fEqual = buf1.toString('hex') === buf2.toString('hex'); var fEqual = buf1.toString('hex') === buf2.toString('hex');
// Opcode.OP_NOTEQUAL is disabled because it would be too easy to say // Opcode.OP_NOTEQUAL is disabled because it would be too easy to say
// something like n != 1 and have some wiseguy pass in 1 with extra // something like n != 1 and have some wiseguy pass in 1 with extra
@ -775,9 +778,9 @@ Interpreter.prototype.step = function() {
this.stack.pop(); this.stack.pop();
this.stack.push(fEqual ? Interpreter.true : Interpreter.false); this.stack.push(fEqual ? Interpreter.true : Interpreter.false);
if (opcodenum === Opcode.OP_EQUALVERIFY) { if (opcodenum === Opcode.OP_EQUALVERIFY) {
if (fEqual) if (fEqual) {
this.stack.pop(); this.stack.pop();
else { } else {
this.errstr = 'SCRIPT_ERR_EQUALVERIFY'; this.errstr = 'SCRIPT_ERR_EQUALVERIFY';
return false; return false;
} }
@ -801,8 +804,8 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf = this.stack[this.stack.length - 1]; buf = this.stack[this.stack.length - 1];
var bn = BN().fromScriptNumBuffer(buf, fRequireMinimal); bn = BN().fromScriptNumBuffer(buf, fRequireMinimal);
switch (opcodenum) { switch (opcodenum) {
case Opcode.OP_1ADD: case Opcode.OP_1ADD:
bn = bn.add(1); bn = bn.add(1);
@ -814,7 +817,9 @@ Interpreter.prototype.step = function() {
bn = bn.neg(); bn = bn.neg();
break; break;
case Opcode.OP_ABS: case Opcode.OP_ABS:
if (bn.cmp(0) < 0) bn = bn.neg(); if (bn.cmp(0) < 0) {
bn = bn.neg();
}
break; break;
case Opcode.OP_NOT: case Opcode.OP_NOT:
bn = BN((bn.cmp(0) === 0) + 0); bn = BN((bn.cmp(0) === 0) + 0);
@ -848,9 +853,9 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal);
var bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal);
var bn = BN(0); bn = BN(0);
switch (opcodenum) { switch (opcodenum) {
case Opcode.OP_ADD: case Opcode.OP_ADD:
@ -911,9 +916,9 @@ Interpreter.prototype.step = function() {
if (opcodenum === Opcode.OP_NUMEQUALVERIFY) { if (opcodenum === Opcode.OP_NUMEQUALVERIFY) {
// if (CastToBool(stacktop(-1))) // if (CastToBool(stacktop(-1)))
if (Interpreter.castToBool(this.stack[this.stack.length - 1])) if (Interpreter.castToBool(this.stack[this.stack.length - 1])) {
this.stack.pop(); this.stack.pop();
else { } else {
this.errstr = 'SCRIPT_ERR_NUMEQUALVERIFY'; this.errstr = 'SCRIPT_ERR_NUMEQUALVERIFY';
return false; return false;
} }
@ -928,11 +933,11 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 3], fRequireMinimal); bn1 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 3], fRequireMinimal);
var bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal); bn2 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 2], fRequireMinimal);
var bn3 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal); var bn3 = BN().fromScriptNumBuffer(this.stack[this.stack.length - 1], fRequireMinimal);
//bool fValue = (bn2 <= bn1 && bn1 < bn3); //bool fValue = (bn2 <= bn1 && bn1 < bn3);
var fValue = (bn2.cmp(bn1) <= 0) && (bn1.cmp(bn3) < 0); fValue = (bn2.cmp(bn1) <= 0) && (bn1.cmp(bn3) < 0);
this.stack.pop(); this.stack.pop();
this.stack.pop(); this.stack.pop();
this.stack.pop(); this.stack.pop();
@ -955,19 +960,21 @@ Interpreter.prototype.step = function() {
this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION'; this.errstr = 'SCRIPT_ERR_INVALID_STACK_OPERATION';
return false; return false;
} }
var buf = this.stack[this.stack.length - 1]; buf = this.stack[this.stack.length - 1];
//valtype vchHash((opcode == Opcode.OP_RIPEMD160 || opcode == Opcode.OP_SHA1 || opcode == Opcode.OP_HASH160) ? 20 : 32); //valtype vchHash((opcode == Opcode.OP_RIPEMD160 ||
// opcode == Opcode.OP_SHA1 || opcode == Opcode.OP_HASH160) ? 20 : 32);
var bufHash; var bufHash;
if (opcodenum === Opcode.OP_RIPEMD160) if (opcodenum === Opcode.OP_RIPEMD160) {
bufHash = Hash.ripemd160(buf); bufHash = Hash.ripemd160(buf);
else if (opcodenum === Opcode.OP_SHA1) } else if (opcodenum === Opcode.OP_SHA1) {
bufHash = Hash.sha1(buf); bufHash = Hash.sha1(buf);
else if (opcodenum === Opcode.OP_SHA256) } else if (opcodenum === Opcode.OP_SHA256) {
bufHash = Hash.sha256(buf); bufHash = Hash.sha256(buf);
else if (opcodenum === Opcode.OP_HASH160) } else if (opcodenum === Opcode.OP_HASH160) {
bufHash = Hash.sha256ripemd160(buf); bufHash = Hash.sha256ripemd160(buf);
else if (opcodenum === Opcode.OP_HASH256) } else if (opcodenum === Opcode.OP_HASH256) {
bufHash = Hash.sha256sha256(buf); bufHash = Hash.sha256sha256(buf);
}
this.stack.pop(); this.stack.pop();
this.stack.push(bufHash); this.stack.push(bufHash);
} }
@ -989,27 +996,26 @@ Interpreter.prototype.step = function() {
return false; return false;
} }
var bufSig = this.stack[this.stack.length - 2]; bufSig = this.stack[this.stack.length - 2];
var bufPubkey = this.stack[this.stack.length - 1]; bufPubkey = this.stack[this.stack.length - 1];
// Subset of script starting at the most recent codeseparator // Subset of script starting at the most recent codeseparator
// CScript scriptCode(pbegincodehash, pend); // CScript scriptCode(pbegincodehash, pend);
var subscript = Script().set({ subscript = new Script().set({
chunks: this.script.chunks.slice(this.pbegincodehash) chunks: this.script.chunks.slice(this.pbegincodehash)
}); });
// Drop the signature, since there's no way for a signature to sign itself // Drop the signature, since there's no way for a signature to sign itself
var tmpScript = Script().add(bufSig); var tmpScript = new Script().add(bufSig);
subscript.findAndDelete(tmpScript); subscript.findAndDelete(tmpScript);
if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) { if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) {
return false; return false;
} }
var fSuccess;
try { try {
var sig = Signature.fromTxFormat(bufSig); sig = Signature.fromTxFormat(bufSig);
var pubkey = PublicKey.fromBuffer(bufPubkey, false); pubkey = PublicKey.fromBuffer(bufPubkey, false);
fSuccess = this.tx.verifySignature(sig, pubkey, this.nin, subscript); fSuccess = this.tx.verifySignature(sig, pubkey, this.nin, subscript);
} catch (e) { } catch (e) {
//invalid sig or pubkey //invalid sig or pubkey
@ -1074,22 +1080,22 @@ Interpreter.prototype.step = function() {
} }
// Subset of script starting at the most recent codeseparator // Subset of script starting at the most recent codeseparator
var subscript = Script().set({ subscript = new Script().set({
chunks: this.script.chunks.slice(this.pbegincodehash) chunks: this.script.chunks.slice(this.pbegincodehash)
}); });
// Drop the signatures, since there's no way for a signature to sign itself // Drop the signatures, since there's no way for a signature to sign itself
for (var k = 0; k < nSigsCount; k++) { for (var k = 0; k < nSigsCount; k++) {
var bufSig = this.stack[this.stack.length - isig - k]; bufSig = this.stack[this.stack.length - isig - k];
subscript.findAndDelete(Script().add(bufSig)); subscript.findAndDelete(new Script().add(bufSig));
} }
var fSuccess = true; fSuccess = true;
while (fSuccess && nSigsCount > 0) { while (fSuccess && nSigsCount > 0) {
// valtype& vchSig = stacktop(-isig); // valtype& vchSig = stacktop(-isig);
var bufSig = this.stack[this.stack.length - isig]; bufSig = this.stack[this.stack.length - isig];
// valtype& vchPubKey = stacktop(-ikey); // valtype& vchPubKey = stacktop(-ikey);
var bufPubkey = this.stack[this.stack.length - ikey]; bufPubkey = this.stack[this.stack.length - ikey];
if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) { if (!this.checkSignatureEncoding(bufSig) || !this.checkPubkeyEncoding(bufPubkey)) {
return false; return false;
@ -1097,8 +1103,8 @@ Interpreter.prototype.step = function() {
var fOk; var fOk;
try { try {
var sig = Signature.fromTxFormat(bufSig); sig = Signature.fromTxFormat(bufSig);
var pubkey = PublicKey.fromBuffer(bufPubkey, false); pubkey = PublicKey.fromBuffer(bufPubkey, false);
fOk = this.tx.verifySignature(sig, pubkey, this.nin, subscript); fOk = this.tx.verifySignature(sig, pubkey, this.nin, subscript);
} catch (e) { } catch (e) {
//invalid sig or pubkey //invalid sig or pubkey
@ -1143,9 +1149,9 @@ Interpreter.prototype.step = function() {
this.stack.push(fSuccess ? Interpreter.true : Interpreter.false); this.stack.push(fSuccess ? Interpreter.true : Interpreter.false);
if (opcodenum === Opcode.OP_CHECKMULTISIGVERIFY) { if (opcodenum === Opcode.OP_CHECKMULTISIGVERIFY) {
if (fSuccess) if (fSuccess) {
this.stack.pop(); this.stack.pop();
else { } else {
this.errstr = 'SCRIPT_ERR_CHECKMULTISIGVERIFY'; this.errstr = 'SCRIPT_ERR_CHECKMULTISIGVERIFY';
return false; return false;
} }