Merge remote-tracking branch 'maraoz/feature/add-bitcoin-core-tests'

Conflicts:
	Script.js
	ScriptInterpreter.js

...fixed conflicts in Script.js and ScriptInterpreter.js. Many tests are broken
right now, but that's because we're now including more test data in the tests.
These need to be fixed.
This commit is contained in:
Ryan X. Charles 2014-03-09 12:07:11 -04:00
commit cb00efb092
7 changed files with 130 additions and 48 deletions

106
Script.js
View File

@ -128,7 +128,7 @@ Script.prototype.finishedMultiSig = function() {
return true; return true;
else else
return false; return false;
} };
Script.prototype.removePlaceHolders = function() { Script.prototype.removePlaceHolders = function() {
var chunks = []; var chunks = [];
@ -142,7 +142,7 @@ Script.prototype.removePlaceHolders = function() {
this.chunks = chunks; this.chunks = chunks;
this.updateBuffer(); this.updateBuffer();
return this; return this;
} };
Script.prototype.prependOp0 = function() { Script.prototype.prependOp0 = function() {
var chunks = [0]; var chunks = [0];
@ -154,7 +154,7 @@ Script.prototype.prependOp0 = function() {
this.chunks = chunks; this.chunks = chunks;
this.updateBuffer(); this.updateBuffer();
return this; return this;
} };
// is this a script form we know? // is this a script form we know?
Script.prototype.classify = function() { Script.prototype.classify = function() {
@ -262,30 +262,6 @@ Script.prototype.getBuffer = function() {
return this.buffer; return this.buffer;
}; };
Script.fromStringContent = function(s) {
var chunks = [];
var split = s.split(' ');
for (var i = 0; i < split.length; i++) {
var word = split[i];
if (word.length > 2 && word.substring(0, 2) === '0x') {
chunks.push(new Buffer(word.substring(2, word.length), 'hex'));
} else {
var opcode = Opcode.map['OP_' + word];
if (opcode) {
chunks.push(opcode);
} else {
var integer = parseInt(word);
if (!isNaN(integer)) {
//console.log(integer+' bits=\t'+integer.toString(2).replace('-','').length);
var data = util.intToBuffer(integer);
chunks.push(data);
}
}
}
}
return Script.fromChunks(chunks);
};
Script.prototype.getStringContent = function(truncate, maxEl) { Script.prototype.getStringContent = function(truncate, maxEl) {
if (truncate === null) { if (truncate === null) {
truncate = true; truncate = true;
@ -324,7 +300,6 @@ Script.prototype.toString = function(truncate, maxEl) {
return script; return script;
}; };
Script.prototype.writeOp = function(opcode) { Script.prototype.writeOp = function(opcode) {
var buf = Buffer(this.buffer.length + 1); var buf = Buffer(this.buffer.length + 1);
this.buffer.copy(buf); this.buffer.copy(buf);
@ -481,6 +456,79 @@ Script.fromChunks = function(chunks) {
return script; return script;
}; };
Script.fromHumanReadable = function(s) {
return new Script(Script.stringToBuffer(s));
};
Script.prototype.toHumanReadable = function() {
var s = '';
for (var i = 0, l = this.chunks.length; i < l; i++) {
var chunk = this.chunks[i];
if (i > 0) {
s += ' ';
}
if (Buffer.isBuffer(chunk)) {
if (chunk.length === 0) {
s += '0';
} else {
s += '0x' + util.formatBuffer(encodeLen(chunk.length), 0) + ' ';
s += '0x' + util.formatBuffer(chunk, 0);
}
} else {
var opcode = Opcode.reverseMap[chunk];
if (typeof opcode === 'undefined') {
opcode = '0x'+chunk.toString(16);
}
s += opcode;
}
}
return s;
};
Script.stringToBuffer = function(s) {
var buf = new Put();
var split = s.split(' ');
for (var i = 0; i < split.length; i++) {
var word = split[i];
if (word === '') continue;
if (word.length > 2 && word.substring(0, 2) === '0x') {
// raw hex value
//console.log('hex value');
buf.put(new Buffer(word.substring(2, word.length), 'hex'));
} else {
var opcode = Opcode.map['OP_' + word];
if (typeof opcode !== 'undefined') {
// op code in string form
//console.log('opcode');
buf.word8(opcode);
} else {
var integer = parseInt(word);
if (!isNaN(integer)) {
// integer
//console.log('integer');
var data = util.intToBuffer(integer);
buf.put(Script.chunksToBuffer([data]));
} else if (word[0] === '\'' && word[word.length-1] === '\'') {
// string
//console.log('string');
word = word.substring(1,word.length-1);
var hexString = '';
for(var c=0;c<word.length;c++) {
hexString += ''+word.charCodeAt(c).toString(16);
}
buf.put(Script.chunksToBuffer([new Buffer(word)]));
} else {
throw new Error('Could not parse word "' +word+'" from script "'+s+'"');
}
}
}
}
return buf.buffer();
};
Script.chunksToBuffer = function(chunks) { Script.chunksToBuffer = function(chunks) {
var buf = new Put(); var buf = new Put();
@ -509,4 +557,6 @@ Script.chunksToBuffer = function(chunks) {
return buf.buffer(); return buf.buffer();
}; };
module.exports = require('soop')(Script); module.exports = require('soop')(Script);

View File

@ -21,9 +21,8 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
if ("function" !== typeof callback) { if ("function" !== typeof callback) {
throw new Error("ScriptInterpreter.eval() requires a callback"); throw new Error("ScriptInterpreter.eval() requires a callback");
} }
var pc = 0; var pc = 0;
var execStack = []; var execStack = [];
var altStack = []; var altStack = [];
var hashStart = 0; var hashStart = 0;
@ -758,7 +757,7 @@ ScriptInterpreter.prototype.eval = function eval(script, tx, inIndex, hashType,
} }
} 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);
} }
} }

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,17 +84,18 @@ describe('Script', function() {
}); });
}); });
test_data.dataScriptAll.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];
it('should parse script from human readable ' + human, function() { it('should parse script from human readable ' + human, function() {
var h2 = Script.fromStringContent(human).getStringContent(false, null); //console.log('********');
Script.fromStringContent(h2).getStringContent(false, null).should.equal(h2); //console.log(human);
var script = Script.fromHumanReadable(human);
//console.log(script);
var h2 = script.toHumanReadable();
//console.log(h2);
Script.fromHumanReadable(h2).toHumanReadable().should.equal(h2);
}); });
}); });
}); });

View File

@ -4,8 +4,10 @@ var chai = chai || require('chai');
var bitcore = bitcore || require('../bitcore'); var bitcore = 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;
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.fromHumanReadable(scriptSig),
Script.fromHumanReadable(scriptPubKey),
null, 0, 0, // tx, output index, and hashtype
function (err, result) {
should.not.exist(err);
result.should.equal(true);
done();
}
);
});
});
}); });

View File

@ -15,3 +15,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);

View File

@ -111,18 +111,27 @@ var intTo64Bits = function(integer) {
lo: (integer & 0xFFFFFFFF) >>> 0 lo: (integer & 0xFFFFFFFF) >>> 0
}; };
}; };
var fitsIn32Bits = function(integer) { var fitsInNBits = function(integer, n) {
// TODO: make this efficient!!! // TODO: make this efficient!!!
return integer.toString(2).replace('-','').length < 32; return integer.toString(2).replace('-','').length < n;
} }
exports.intToBuffer = function(integer) { exports.intToBuffer = function(integer) {
if (fitsIn32Bits(integer)) { var data = null;
var data = new Buffer(4); if (fitsInNBits(integer, 8)) {
data = new Buffer(1);
data.writeInt8(integer, 0);
return data;
} else if (fitsInNBits(integer, 16)) {
data = new Buffer(2);
data.writeInt16LE(integer, 0);
return data;
} else if (fitsInNBits(integer, 32)) {
data = new Buffer(4);
data.writeInt32LE(integer, 0); data.writeInt32LE(integer, 0);
return data; return data;
} else { } else {
var x = intTo64Bits(integer); var x = intTo64Bits(integer);
var data = new Buffer(8); data = new Buffer(8);
data.writeInt32LE(x.hi, 0); // high part contains sign information (signed) data.writeInt32LE(x.hi, 0); // high part contains sign information (signed)
data.writeUInt32LE(x.lo, 4); // low part encoded as unsigned integer data.writeUInt32LE(x.lo, 4); // low part encoded as unsigned integer
return data; return data;