fix Script parsing for some cases, setup ScriptInterpreter tests

This commit is contained in:
Manuel Araoz 2014-03-07 14:43:29 -03:00
parent 1a9c062760
commit e83590f528
6 changed files with 110 additions and 71 deletions

View File

@ -271,17 +271,33 @@ function spec(b) {
for (var i = 0; i < split.length; i++) { for (var i = 0; i < split.length; i++) {
var word = split[i]; var word = split[i];
if (word.length > 2 && word.substring(0, 2) === '0x') { if (word.length > 2 && word.substring(0, 2) === '0x') {
// raw hex value
//console.log('hex value');
chunks.push(new Buffer(word.substring(2, word.length), 'hex')); chunks.push(new Buffer(word.substring(2, word.length), 'hex'));
} else { } else {
var opcode = Opcode.map['OP_' + word]; var opcode = Opcode.map['OP_' + word];
if (opcode) { if (opcode) {
// op code in string form
//console.log('opcode');
chunks.push(opcode); chunks.push(opcode);
} else { } else {
var integer = parseInt(word); var integer = parseInt(word);
if (!isNaN(integer)) { if (!isNaN(integer)) {
//console.log(integer+' bits=\t'+integer.toString(2).replace('-','').length); // integer
//console.log('integer');
var data = util.intToBuffer(integer); var data = util.intToBuffer(integer);
chunks.push(data); chunks.push(data);
} else if (word[0] === '\'' && word[word.length-1] === '\'') {
// string
//console.log('string');
word = word.substring(1,word.length-1);
var hex = '';
for(var c=0;c<word.length;c++) {
hex += ''+word.charCodeAt(c).toString(16);
}
chunks.push(new Buffer(hex,'hex'));
} else {
throw new Error('Could not parse word from script: ' +word);
} }
} }
} }
@ -307,7 +323,11 @@ function spec(b) {
} }
if (Buffer.isBuffer(chunk)) { if (Buffer.isBuffer(chunk)) {
s += '0x' + util.formatBuffer(chunk, truncate ? null : 0); if (chunk.length === 0) {
s += '\'\''
} else {
s += '0x' + util.formatBuffer(chunk, truncate ? null : 0);
}
} else { } else {
s += Opcode.reverseMap[chunk]; s += Opcode.reverseMap[chunk];
} }

View File

@ -74,22 +74,22 @@ function spec(b) {
} }
if (this.disableUnsafeOpcodes && if (this.disableUnsafeOpcodes &&
"number" === typeof opcode && "number" === typeof opcode &&
(opcode === OP_CAT || (opcode === OP_CAT ||
opcode === OP_SUBSTR || opcode === OP_SUBSTR ||
opcode === OP_LEFT || opcode === OP_LEFT ||
opcode === OP_RIGHT || opcode === OP_RIGHT ||
opcode === OP_INVERT || opcode === OP_INVERT ||
opcode === OP_AND || opcode === OP_AND ||
opcode === OP_OR || opcode === OP_OR ||
opcode === OP_XOR || opcode === OP_XOR ||
opcode === OP_2MUL || opcode === OP_2MUL ||
opcode === OP_2DIV || opcode === OP_2DIV ||
opcode === OP_MUL || opcode === OP_MUL ||
opcode === OP_DIV || opcode === OP_DIV ||
opcode === OP_MOD || opcode === OP_MOD ||
opcode === OP_LSHIFT || opcode === OP_LSHIFT ||
opcode === OP_RSHIFT)) { opcode === OP_RSHIFT)) {
throw new Error("Encountered a disabled opcode"); throw new Error("Encountered a disabled opcode");
} }
@ -615,15 +615,15 @@ function spec(b) {
// Verify signature // Verify signature
checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) { checkSig(sig, pubkey, scriptCode, tx, inIndex, hashType, function(e, result) {
try { try {
var success; var success;
if (e) { if (e) {
// We intentionally ignore errors during signature verification and // We intentionally ignore errors during signature verification and
// treat these cases as an invalid signature. // treat these cases as an invalid signature.
success = false; success = false;
} else { } else {
success = result; success = result;
} }
// Update stack // Update stack
@ -631,18 +631,18 @@ function spec(b) {
this.stackPop(); this.stackPop();
this.stack.push(new Buffer([success ? 1 : 0])); this.stack.push(new Buffer([success ? 1 : 0]));
if (opcode === OP_CHECKSIGVERIFY) { if (opcode === OP_CHECKSIGVERIFY) {
if (success) { if (success) {
this.stackPop(); this.stackPop();
} else { } else {
throw new Error("OP_CHECKSIGVERIFY negative"); throw new Error("OP_CHECKSIGVERIFY negative");
} }
} }
// Run next step // Run next step
executeStep.call(this, cb); executeStep.call(this, cb);
} catch (e) { } catch (e) {
cb(e); cb(e);
} }
}.bind(this)); }.bind(this));
// Note that for asynchronous opcodes we have to return here to prevent // Note that for asynchronous opcodes we have to return here to prevent
@ -686,12 +686,12 @@ function spec(b) {
// Drop the signatures, since a signature can't sign itself // Drop the signatures, since a signature can't sign itself
sigs.forEach(function(sig) { sigs.forEach(function(sig) {
scriptCode.findAndDelete(sig); scriptCode.findAndDelete(sig);
}); });
var success = true, var success = true,
isig = 0, isig = 0,
ikey = 0; ikey = 0;
checkMultiSigStep.call(this); checkMultiSigStep.call(this);
function checkMultiSigStep() { function checkMultiSigStep() {
@ -701,26 +701,26 @@ function spec(b) {
var key = keys[ikey]; var key = keys[ikey];
checkSig(sig, key, scriptCode, tx, inIndex, hashType, function(e, result) { checkSig(sig, key, scriptCode, tx, inIndex, hashType, function(e, result) {
try { try {
if (!e && result) { if (!e && result) {
isig++; isig++;
sigsCount--; sigsCount--;
} else { } else {
ikey++; ikey++;
keysCount--; keysCount--;
// If there are more signatures than keys left, then too many // If there are more signatures than keys left, then too many
// signatures have failed // signatures have failed
if (sigsCount > keysCount) { if (sigsCount > keysCount) {
success = false; success = false;
} }
} }
checkMultiSigStep.call(this); checkMultiSigStep.call(this);
} catch (e) { } catch (e) {
cb(e); cb(e);
} }
}.bind(this)); }.bind(this));
} else { } else {
this.stack.push(new Buffer([success ? 1 : 0])); this.stack.push(new Buffer([success ? 1 : 0]));
if (opcode === OP_CHECKMULTISIGVERIFY) { if (opcode === OP_CHECKMULTISIGVERIFY) {
@ -763,7 +763,7 @@ function spec(b) {
} }
} catch (e) { } catch (e) {
log.debug("Script aborted: " + log.debug("Script aborted: " +
(e.message ? e : e)); (e.message ? e.message : e));
cb(e); cb(e);
} }
} }
@ -774,14 +774,14 @@ function spec(b) {
var self = this; var self = this;
self.eval(scriptSig, tx, n, hashType, function(e) { self.eval(scriptSig, tx, n, hashType, function(e) {
if (e) { if (e) {
callback(e) callback(e)
return; return;
} }
self.eval(scriptPubkey, tx, n, hashType, callback); self.eval(scriptPubkey, tx, n, hashType, callback);
}); });
}; };
/** /**
* Get the top element of the stack. * Get the top element of the stack.
@ -821,7 +821,7 @@ function spec(b) {
} }
var s = this.stack, var s = this.stack,
l = s.length; l = s.length;
var tmp = s[l - a]; var tmp = s[l - a];
s[l - a] = s[l - b]; s[l - a] = s[l - b];
@ -836,16 +836,16 @@ function spec(b) {
*/ */
ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() { ScriptInterpreter.prototype.getPrimitiveStack = function getPrimitiveStack() {
return this.stack.map(function(entry) { return this.stack.map(function(entry) {
if (entry.length > 2) { if (entry.length > 2) {
return buffertools.toHex(entry.slice(0)); return buffertools.toHex(entry.slice(0));
} }
var num = castBigint(entry); var num = castBigint(entry);
if (num.cmp(-128) >= 0 && num.cmp(127) <= 0) { if (num.cmp(-128) >= 0 && num.cmp(127) <= 0) {
return num.toNumber(); return num.toNumber();
} else { } else {
return buffertools.toHex(entry.slice(0)); return buffertools.toHex(entry.slice(0));
} }
}); });
}; };
var castBool = ScriptInterpreter.castBool = function castBool(v) { var castBool = ScriptInterpreter.castBool = function castBool(v) {

View File

@ -1,8 +1,8 @@
[ [
["0x01 0x0b", "11 EQUAL", "push 1 byte"], ["0x01 0x0b", "11 EQUAL", "push 1 byte"],
["0x02 0x417a", "0x417a EQUAL"], ["0x02 0x417a", "'Az' EQUAL"],
["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a", ["0x4b 0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a",
"0x417a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a EQUAL", "push 75 bytes"], "'Azzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz' EQUAL", "push 75 bytes"],
["0x4c 0x01 0x07","7 EQUAL", "0x4c is OP_PUSHDATA1"], ["0x4c 0x01 0x07","7 EQUAL", "0x4c is OP_PUSHDATA1"],
["0x4d 0x0100 0x08","8 EQUAL", "0x4d is OP_PUSHDATA2"], ["0x4d 0x0100 0x08","8 EQUAL", "0x4d is OP_PUSHDATA2"],
@ -11,7 +11,7 @@
["0x4c 0x00","0 EQUAL"], ["0x4c 0x00","0 EQUAL"],
["0x4d 0x0000","0 EQUAL"], ["0x4d 0x0000","0 EQUAL"],
["0x4e 0x00000000","0 EQUAL"], ["0x4e 0x00000000","0 EQUAL"],
["0x4f 0x03e8 ADD","0x03e7 EQUAL"], ["0x4f 1000 ADD","999 EQUAL"],
["0", "IF 0x50 ENDIF 1", "0x50 is reserved (ok if not executed)"], ["0", "IF 0x50 ENDIF 1", "0x50 is reserved (ok if not executed)"],
["0x51", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"], ["0x51", "0x5f ADD 0x60 EQUAL", "0x51 through 0x60 push 1 through 16 onto stack"],
["1","NOP"], ["1","NOP"],
@ -53,8 +53,8 @@
["1 1", "VERIFY"], ["1 1", "VERIFY"],
["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 0x15 EQUAL"], ["10 0 11 TOALTSTACK DROP FROMALTSTACK", "ADD 21 EQUAL"],
["0x676176696e5f7761735f68657265 TOALTSTACK 11 FROMALTSTACK", "0x676176696e5f7761735f68657265 EQUALVERIFY 11 EQUAL"], ["'gavin_was_here' TOALTSTACK 11 FROMALTSTACK", "'gavin_was_here' EQUALVERIFY 11 EQUAL"],
["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL"], ["0 IFDUP", "DEPTH 1 EQUALVERIFY 0 EQUAL"],
["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL"], ["1 IFDUP", "DEPTH 2 EQUALVERIFY 1 EQUALVERIFY 1 EQUAL"],

View File

@ -84,8 +84,6 @@ describe('Script', function() {
}); });
}); });
test_data.dataScriptValid.forEach(function(datum) { test_data.dataScriptValid.forEach(function(datum) {
if (datum.length < 2) throw new Error('Invalid test data'); if (datum.length < 2) throw new Error('Invalid test data');
var human = datum[0] + ' ' + datum[1]; var human = datum[0] + ' ' + datum[1];
@ -93,8 +91,6 @@ describe('Script', function() {
var h2 = Script.fromStringContent(human).getStringContent(false, null); var h2 = Script.fromStringContent(human).getStringContent(false, null);
Script.fromStringContent(h2).getStringContent(false, null).should.equal(h2); Script.fromStringContent(h2).getStringContent(false, null).should.equal(h2);
}); });
}); });
}); });

View File

@ -4,8 +4,10 @@ var chai = require('chai');
var bitcore = require('../bitcore'); var bitcore = require('../bitcore');
var should = chai.should(); var should = chai.should();
var test_data = require('./testdata');
var ScriptInterpreterModule = bitcore.ScriptInterpreter; var ScriptInterpreterModule = bitcore.ScriptInterpreter;
var Script = bitcore.Script.class();
var ScriptInterpreter; var ScriptInterpreter;
describe('ScriptInterpreter', function() { describe('ScriptInterpreter', function() {
@ -20,6 +22,26 @@ describe('ScriptInterpreter', function() {
var si = new ScriptInterpreter(); var si = new ScriptInterpreter();
should.exist(si); should.exist(si);
}); });
var i = 0;
test_data.dataScriptValid.forEach(function(datum) {
if (datum.length < 2) throw new Error('Invalid test data');
var scriptSig = datum[0]; // script inputs
var scriptPubKey = datum[1]; // output script
var human = scriptSig + ' ' + scriptPubKey;
it('should validate script ' + human, function(done) {
i++;
console.log(i + ' ' + human);
ScriptInterpreter.verify(Script.fromStringContent(scriptSig),
Script.fromStringContent(scriptPubKey),
null, 0, 0, // tx, output index, and hashtype
function (err, result) {
should.not.exist(err);
result.should.equal(true);
done();
}
);
});
});
}); });

View File

@ -17,3 +17,4 @@ module.exports.dataTxValid = dataTxValid;
module.exports.dataTxInvalid = dataTxInvalid; module.exports.dataTxInvalid = dataTxInvalid;
module.exports.dataScriptValid = dataScriptValid; module.exports.dataScriptValid = dataScriptValid;
module.exports.dataScriptInvalid = dataScriptInvalid; module.exports.dataScriptInvalid = dataScriptInvalid;
module.exports.dataScriptAll = dataScriptValid.concat(dataScriptInvalid);