fix Script parsing for some cases, setup ScriptInterpreter tests
This commit is contained in:
parent
1a9c062760
commit
e83590f528
24
Script.js
24
Script.js
@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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"],
|
||||||
|
|||||||
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user