refactors and fixes for script interpreter
This commit is contained in:
parent
639ef5e45f
commit
3de71f8558
@ -140,7 +140,7 @@ BN.prototype.toSM = function(opts) {
|
|||||||
// bitcoind's script interpreter use CScriptNum, which is not really a proper
|
// bitcoind's script interpreter use CScriptNum, which is not really a proper
|
||||||
// bignum. Instead, an error is thrown if trying to input a number bigger than
|
// bignum. Instead, an error is thrown if trying to input a number bigger than
|
||||||
// 4 bytes. We copy that behavior here.
|
// 4 bytes. We copy that behavior here.
|
||||||
BN.prototype.fromCScriptNumBuffer = function(buf, fRequireMinimal) {
|
BN.prototype.fromScriptNumBuffer = function(buf, fRequireMinimal) {
|
||||||
var nMaxNumSize = 4;
|
var nMaxNumSize = 4;
|
||||||
if (buf.length > nMaxNumSize)
|
if (buf.length > nMaxNumSize)
|
||||||
throw new Error('script number overflow');
|
throw new Error('script number overflow');
|
||||||
@ -169,7 +169,7 @@ BN.prototype.fromCScriptNumBuffer = function(buf, fRequireMinimal) {
|
|||||||
// an error if the output is larger than four bytes. (Which can happen if
|
// an error if the output is larger than four bytes. (Which can happen if
|
||||||
// performing a numerical operation that results in an overflow to more than 4
|
// performing a numerical operation that results in an overflow to more than 4
|
||||||
// bytes).
|
// bytes).
|
||||||
BN.prototype.toCScriptNumBuffer = function(buf) {
|
BN.prototype.toScriptNumBuffer = function(buf) {
|
||||||
return this.toSM({endian: 'little'});
|
return this.toSM({endian: 'little'});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2,44 +2,44 @@
|
|||||||
|
|
||||||
var hashjs = require('hash.js');
|
var hashjs = require('hash.js');
|
||||||
var sha512 = require('sha512');
|
var sha512 = require('sha512');
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var BufferUtil = require('../util/buffer');
|
||||||
|
var $ = require('../util/preconditions');
|
||||||
|
|
||||||
var Hash = module.exports;
|
var Hash = module.exports;
|
||||||
|
|
||||||
|
Hash.sha1 = function(buf) {
|
||||||
|
$.checkArgument(BufferUtil.isBuffer(buf));
|
||||||
|
return crypto.createHash('sha1').update(buf).digest();
|
||||||
|
};
|
||||||
|
|
||||||
|
Hash.sha1.blocksize = 512;
|
||||||
|
|
||||||
Hash.sha256 = function(buf) {
|
Hash.sha256 = function(buf) {
|
||||||
if (!Buffer.isBuffer(buf))
|
$.checkArgument(BufferUtil.isBuffer(buf));
|
||||||
throw new Error('sha256 hash must be of a buffer');
|
return crypto.createHash('sha256').update(buf).digest();
|
||||||
var hash = (new hashjs.sha256()).update(buf).digest();
|
|
||||||
return new Buffer(hash);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Hash.sha256.blocksize = 512;
|
Hash.sha256.blocksize = 512;
|
||||||
|
|
||||||
Hash.sha256sha256 = function(buf) {
|
Hash.sha256sha256 = function(buf) {
|
||||||
try {
|
$.checkArgument(BufferUtil.isBuffer(buf));
|
||||||
return Hash.sha256(Hash.sha256(buf));
|
return Hash.sha256(Hash.sha256(buf));
|
||||||
} catch (e) {
|
|
||||||
throw new Error('sha256sha256 hash must be of a buffer');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Hash.ripemd160 = function(buf) {
|
Hash.ripemd160 = function(buf) {
|
||||||
if (!Buffer.isBuffer(buf))
|
$.checkArgument(BufferUtil.isBuffer(buf));
|
||||||
throw new Error('ripemd160 hash must be of a buffer');
|
|
||||||
var hash = (new hashjs.ripemd160()).update(buf).digest();
|
var hash = (new hashjs.ripemd160()).update(buf).digest();
|
||||||
return new Buffer(hash);
|
return new Buffer(hash);
|
||||||
};
|
};
|
||||||
|
|
||||||
Hash.sha256ripemd160 = function(buf) {
|
Hash.sha256ripemd160 = function(buf) {
|
||||||
try {
|
$.checkArgument(BufferUtil.isBuffer(buf));
|
||||||
return Hash.ripemd160(Hash.sha256(buf));
|
return Hash.ripemd160(Hash.sha256(buf));
|
||||||
} catch (e) {
|
|
||||||
throw new Error('sha256ripemd160 hash must be of a buffer');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Hash.sha512 = function(buf) {
|
Hash.sha512 = function(buf) {
|
||||||
if (!Buffer.isBuffer(buf))
|
$.checkArgument(BufferUtil.isBuffer(buf));
|
||||||
throw new Error('sha512 hash must be of a buffer');
|
|
||||||
var hash = sha512(buf);
|
var hash = sha512(buf);
|
||||||
return new Buffer(hash);
|
return new Buffer(hash);
|
||||||
};
|
};
|
||||||
@ -47,19 +47,17 @@ Hash.sha512 = function(buf) {
|
|||||||
Hash.sha512.blocksize = 1024;
|
Hash.sha512.blocksize = 1024;
|
||||||
|
|
||||||
Hash.hmac = function(hashf, data, key) {
|
Hash.hmac = function(hashf, data, key) {
|
||||||
if (!Buffer.isBuffer(data) || !Buffer.isBuffer(key))
|
|
||||||
throw new Error('data and key must be buffers');
|
|
||||||
|
|
||||||
//http://en.wikipedia.org/wiki/Hash-based_message_authentication_code
|
//http://en.wikipedia.org/wiki/Hash-based_message_authentication_code
|
||||||
//http://tools.ietf.org/html/rfc4868#section-2
|
//http://tools.ietf.org/html/rfc4868#section-2
|
||||||
if (!hashf.blocksize)
|
$.checkArgument(BufferUtil.isBuffer(data));
|
||||||
throw new Error('Blocksize for hash function unknown');
|
$.checkArgument(BufferUtil.isBuffer(key));
|
||||||
|
$.checkArgument(hashf.blocksize);
|
||||||
|
|
||||||
var blocksize = hashf.blocksize/8;
|
var blocksize = hashf.blocksize / 8;
|
||||||
|
|
||||||
if (key.length > blocksize)
|
if (key.length > blocksize) {
|
||||||
key = hashf(key);
|
key = hashf(key);
|
||||||
else if (key < blocksize) {
|
} else if (key < blocksize) {
|
||||||
var fill = new Buffer(blocksize);
|
var fill = new Buffer(blocksize);
|
||||||
fill.fill(0);
|
fill.fill(0);
|
||||||
key.copy(fill);
|
key.copy(fill);
|
||||||
|
|||||||
@ -127,7 +127,7 @@ Script.prototype.toBuffer = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Script.fromString = function(str) {
|
Script.fromString = function(str) {
|
||||||
if (jsUtil.isHexa(str)) {
|
if (jsUtil.isHexa(str) || str.length === 0) {
|
||||||
return new Script(new buffer.Buffer(str, 'hex'));
|
return new Script(new buffer.Buffer(str, 'hex'));
|
||||||
}
|
}
|
||||||
var script = new Script();
|
var script = new Script();
|
||||||
@ -140,7 +140,7 @@ Script.fromString = function(str) {
|
|||||||
var opcode = Opcode(token);
|
var opcode = Opcode(token);
|
||||||
var opcodenum = opcode.toNumber();
|
var opcodenum = opcode.toNumber();
|
||||||
|
|
||||||
if (typeof opcodenum === 'undefined') {
|
if (_.isUndefined(opcodenum)) {
|
||||||
opcodenum = parseInt(token);
|
opcodenum = parseInt(token);
|
||||||
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) {
|
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) {
|
||||||
script.chunks.push({
|
script.chunks.push({
|
||||||
@ -184,7 +184,11 @@ Script.prototype.toString = function() {
|
|||||||
if (typeof Opcode.reverseMap[opcodenum] !== 'undefined') {
|
if (typeof Opcode.reverseMap[opcodenum] !== 'undefined') {
|
||||||
str = str + ' ' + Opcode(opcodenum).toString();
|
str = str + ' ' + Opcode(opcodenum).toString();
|
||||||
} else {
|
} else {
|
||||||
str = str + ' ' + '0x' + opcodenum.toString(16);
|
var numstr = opcodenum.toString(16);
|
||||||
|
if (numstr.length % 2 !== 0) {
|
||||||
|
numstr = '0' + numstr;
|
||||||
|
}
|
||||||
|
str = str + ' ' + '0x' + numstr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (opcodenum === Opcode.OP_PUSHDATA1 ||
|
if (opcodenum === Opcode.OP_PUSHDATA1 ||
|
||||||
@ -193,7 +197,9 @@ Script.prototype.toString = function() {
|
|||||||
str = str + ' ' + Opcode(opcodenum).toString();
|
str = str + ' ' + Opcode(opcodenum).toString();
|
||||||
}
|
}
|
||||||
str = str + ' ' + chunk.len;
|
str = str + ' ' + chunk.len;
|
||||||
str = str + ' ' + '0x' + chunk.buf.toString('hex');
|
if (chunk.len > 0) {
|
||||||
|
str = str + ' ' + '0x' + chunk.buf.toString('hex');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,9 +481,7 @@ Script.prototype._addOpcode = function(opcode, prepend) {
|
|||||||
Script.prototype._addBuffer = function(buf, prepend) {
|
Script.prototype._addBuffer = function(buf, prepend) {
|
||||||
var opcodenum;
|
var opcodenum;
|
||||||
var len = buf.length;
|
var len = buf.length;
|
||||||
if (len === 0) {
|
if (len >= 0 && len < Opcode.OP_PUSHDATA1) {
|
||||||
return;
|
|
||||||
} else if (len > 0 && len < Opcode.OP_PUSHDATA1) {
|
|
||||||
opcodenum = len;
|
opcodenum = len;
|
||||||
} else if (len < Math.pow(2, 8)) {
|
} else if (len < Math.pow(2, 8)) {
|
||||||
opcodenum = Opcode.OP_PUSHDATA1;
|
opcodenum = Opcode.OP_PUSHDATA1;
|
||||||
@ -601,8 +605,10 @@ Script.buildDataOut = function(data) {
|
|||||||
data = new Buffer(data);
|
data = new Buffer(data);
|
||||||
}
|
}
|
||||||
var s = new Script();
|
var s = new Script();
|
||||||
s.add(Opcode.OP_RETURN)
|
s.add(Opcode.OP_RETURN);
|
||||||
.add(data);
|
if (!_.isUndefined(data)) {
|
||||||
|
s.add(data);
|
||||||
|
}
|
||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -167,23 +167,23 @@ ScriptInterpreter.prototype.evaluate = function() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
//try {
|
||||||
while (this.pc < this.script.chunks.length) {
|
while (this.pc < this.script.chunks.length) {
|
||||||
var fSuccess = this.step();
|
var fSuccess = this.step();
|
||||||
if (!fSuccess) {
|
if (!fSuccess) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size limits
|
|
||||||
if (this.stack.length + this.altstack.length > 1000) {
|
|
||||||
this.errstr = 'SCRIPT_ERR_STACK_SIZE';
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
}
|
||||||
this.errstr = 'SCRIPT_ERR_UNKNOWN_ERROR: ' + e;
|
|
||||||
|
// Size limits
|
||||||
|
if (this.stack.length + this.altstack.length > 1000) {
|
||||||
|
this.errstr = 'SCRIPT_ERR_STACK_SIZE';
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//} catch (e) {
|
||||||
|
// this.errstr = 'SCRIPT_ERR_UNKNOWN_ERROR: ' + e;
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
if (this.vfExec.length > 0) {
|
if (this.vfExec.length > 0) {
|
||||||
this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL';
|
this.errstr = 'SCRIPT_ERR_UNBALANCED_CONDITIONAL';
|
||||||
@ -202,12 +202,11 @@ ScriptInterpreter.prototype.step = function() {
|
|||||||
var fRequireMinimal = (this.flags & ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA) !== 0;
|
var fRequireMinimal = (this.flags & ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA) !== 0;
|
||||||
|
|
||||||
//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);
|
||||||
|
|
||||||
|
|
||||||
// Read instruction
|
// Read instruction
|
||||||
var chunk = this.script.chunks[this.pc];
|
var chunk = this.script.chunks[this.pc];
|
||||||
console.log('STEP!' + JSON.stringify(chunk));
|
|
||||||
this.pc++;
|
this.pc++;
|
||||||
var opcodenum = chunk.opcodenum;
|
var opcodenum = chunk.opcodenum;
|
||||||
if (_.isUndefined(opcodenum)) {
|
if (_.isUndefined(opcodenum)) {
|
||||||
@ -225,6 +224,7 @@ ScriptInterpreter.prototype.step = function() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (opcodenum === Opcode.OP_CAT ||
|
if (opcodenum === Opcode.OP_CAT ||
|
||||||
opcodenum === Opcode.OP_SUBSTR ||
|
opcodenum === Opcode.OP_SUBSTR ||
|
||||||
opcodenum === Opcode.OP_LEFT ||
|
opcodenum === Opcode.OP_LEFT ||
|
||||||
@ -257,6 +257,7 @@ ScriptInterpreter.prototype.step = function() {
|
|||||||
this.stack.push(chunk.buf);
|
this.stack.push(chunk.buf);
|
||||||
}
|
}
|
||||||
} else if (fExec || (Opcode.OP_IF <= opcodenum && opcodenum <= Opcode.OP_ENDIF)) {
|
} else if (fExec || (Opcode.OP_IF <= opcodenum && opcodenum <= Opcode.OP_ENDIF)) {
|
||||||
|
console.log('STEP!' + JSON.stringify(chunk));
|
||||||
switch (opcodenum) {
|
switch (opcodenum) {
|
||||||
// Push value
|
// Push value
|
||||||
case Opcode.OP_1NEGATE:
|
case Opcode.OP_1NEGATE:
|
||||||
@ -1095,7 +1096,7 @@ ScriptInterpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Additional validation for spend-to-script-hash transactions:
|
// Additional validation for spend-to-script-hash transactions:
|
||||||
if ((flags & ScriptInterpreter.SCRIPT_VERIFY_P2SH) && scriptPubkey.isScripthashOut()) {
|
if ((flags & ScriptInterpreter.SCRIPT_VERIFY_P2SH) && scriptPubkey.isScriptHashOut()) {
|
||||||
// scriptSig must be literals-only or validation fails
|
// scriptSig must be literals-only or validation fails
|
||||||
if (!scriptSig.isPushOnly()) {
|
if (!scriptSig.isPushOnly()) {
|
||||||
this.errstr = 'SCRIPT_ERR_SIG_PUSHONLY';
|
this.errstr = 'SCRIPT_ERR_SIG_PUSHONLY';
|
||||||
@ -1109,7 +1110,7 @@ ScriptInterpreter.prototype.verify = function(scriptSig, scriptPubkey, tx, nin,
|
|||||||
throw new Error('internal error - stack copy empty');
|
throw new Error('internal error - stack copy empty');
|
||||||
|
|
||||||
var pubkeySerialized = stackCopy[stackCopy.length - 1];
|
var pubkeySerialized = stackCopy[stackCopy.length - 1];
|
||||||
var scriptPubkey2 = Script().fromBuffer(pubkeySerialized);
|
var scriptPubkey2 = Script.fromBuffer(pubkeySerialized);
|
||||||
stackCopy.pop();
|
stackCopy.pop();
|
||||||
|
|
||||||
this.initialize();
|
this.initialize();
|
||||||
|
|||||||
@ -8,6 +8,23 @@ describe('Hash', function() {
|
|||||||
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]);
|
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]);
|
||||||
var str = 'test string';
|
var str = 'test string';
|
||||||
|
|
||||||
|
describe('@sha1', function() {
|
||||||
|
|
||||||
|
it('should calculate the hash of this buffer correctly', function() {
|
||||||
|
var hash = Hash.sha1(buf);
|
||||||
|
hash.toString('hex').should.equal('de69b8a4a5604d0486e6420db81e39eb464a17b2');
|
||||||
|
hash = Hash.sha1(new Buffer(0));
|
||||||
|
hash.toString('hex').should.equal('da39a3ee5e6b4b0d3255bfef95601890afd80709');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error when the input is not a buffer', function() {
|
||||||
|
(function() {
|
||||||
|
Hash.sha1(str);
|
||||||
|
}).should.throw('Invalid Argument');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('#sha256', function() {
|
describe('#sha256', function() {
|
||||||
|
|
||||||
it('should calculate the hash of this buffer correctly', function() {
|
it('should calculate the hash of this buffer correctly', function() {
|
||||||
@ -18,7 +35,7 @@ describe('Hash', function() {
|
|||||||
it('should throw an error when the input is not a buffer', function() {
|
it('should throw an error when the input is not a buffer', function() {
|
||||||
(function() {
|
(function() {
|
||||||
Hash.sha256(str);
|
Hash.sha256(str);
|
||||||
}).should.throw('sha256 hash must be of a buffer');
|
}).should.throw('Invalid Argument');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -49,7 +66,7 @@ describe('Hash', function() {
|
|||||||
it('should throw an error when the input is not a buffer', function() {
|
it('should throw an error when the input is not a buffer', function() {
|
||||||
(function() {
|
(function() {
|
||||||
Hash.sha256sha256(str);
|
Hash.sha256sha256(str);
|
||||||
}).should.throw('sha256sha256 hash must be of a buffer');
|
}).should.throw('Invalid Argument');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -64,7 +81,7 @@ describe('Hash', function() {
|
|||||||
it('should throw an error when the input is not a buffer', function() {
|
it('should throw an error when the input is not a buffer', function() {
|
||||||
(function() {
|
(function() {
|
||||||
Hash.sha256ripemd160(str);
|
Hash.sha256ripemd160(str);
|
||||||
}).should.throw('sha256ripemd160 hash must be of a buffer');
|
}).should.throw('Invalid Argument');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -79,7 +96,7 @@ describe('Hash', function() {
|
|||||||
it('should throw an error when the input is not a buffer', function() {
|
it('should throw an error when the input is not a buffer', function() {
|
||||||
(function() {
|
(function() {
|
||||||
Hash.ripemd160(str);
|
Hash.ripemd160(str);
|
||||||
}).should.throw('ripemd160 hash must be of a buffer');
|
}).should.throw('Invalid Argument');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -94,7 +111,7 @@ describe('Hash', function() {
|
|||||||
it('should throw an error when the input is not a buffer', function() {
|
it('should throw an error when the input is not a buffer', function() {
|
||||||
(function() {
|
(function() {
|
||||||
Hash.sha512(str);
|
Hash.sha512(str);
|
||||||
}).should.throw('sha512 hash must be of a buffer');
|
}).should.throw('Invalid Argument');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -349,9 +349,8 @@ describe('Script', function() {
|
|||||||
describe('#add and #prepend', function() {
|
describe('#add and #prepend', function() {
|
||||||
|
|
||||||
it('should add these ops', function() {
|
it('should add these ops', function() {
|
||||||
Script().add(Opcode('OP_RETURN')).add(new Buffer('')).toString().should.equal('OP_RETURN');
|
Script().add(1).add(10).add(186).toString().should.equal('0x01 0x0a 0xba');
|
||||||
});
|
Script().add(1000).toString().should.equal('0x03e8');
|
||||||
it('should add these ops', function() {
|
|
||||||
Script().add('OP_CHECKMULTISIG').toString().should.equal('OP_CHECKMULTISIG');
|
Script().add('OP_CHECKMULTISIG').toString().should.equal('OP_CHECKMULTISIG');
|
||||||
Script().add('OP_1').add('OP_2').toString().should.equal('OP_1 OP_2');
|
Script().add('OP_1').add('OP_2').toString().should.equal('OP_1 OP_2');
|
||||||
Script().add(new Opcode('OP_CHECKMULTISIG')).toString().should.equal('OP_CHECKMULTISIG');
|
Script().add(new Opcode('OP_CHECKMULTISIG')).toString().should.equal('OP_CHECKMULTISIG');
|
||||||
@ -390,6 +389,10 @@ describe('Script', function() {
|
|||||||
buf.fill(0);
|
buf.fill(0);
|
||||||
Script().add(buf).toString().should.equal('1 0x00');
|
Script().add(buf).toString().should.equal('1 0x00');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work for no data OP_RETURN', function() {
|
||||||
|
Script().add(Opcode('OP_RETURN')).add(new Buffer('')).toString().should.equal('OP_RETURN 0');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#isStandard', function() {
|
describe('#isStandard', function() {
|
||||||
@ -469,11 +472,17 @@ describe('Script', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('#buildDataOut', function() {
|
describe('#buildDataOut', function() {
|
||||||
|
it('should create script from no data', function() {
|
||||||
|
var s = Script.buildDataOut();
|
||||||
|
should.exist(s);
|
||||||
|
s.toString().should.equal('OP_RETURN');
|
||||||
|
s.isDataOut().should.equal(true);
|
||||||
|
});
|
||||||
it('should create script from empty data', function() {
|
it('should create script from empty data', function() {
|
||||||
var data = new Buffer('');
|
var data = new Buffer('');
|
||||||
var s = Script.buildDataOut(data);
|
var s = Script.buildDataOut(data);
|
||||||
should.exist(s);
|
should.exist(s);
|
||||||
s.toString().should.equal('OP_RETURN');
|
s.toString().should.equal('OP_RETURN 0');
|
||||||
s.isDataOut().should.equal(true);
|
s.isDataOut().should.equal(true);
|
||||||
});
|
});
|
||||||
it('should create script from some data', function() {
|
it('should create script from some data', function() {
|
||||||
|
|||||||
@ -8,12 +8,79 @@ var Script = bitcore.Script;
|
|||||||
var BN = bitcore.crypto.BN;
|
var BN = bitcore.crypto.BN;
|
||||||
var Sig = bitcore.crypto.Signature;
|
var Sig = bitcore.crypto.Signature;
|
||||||
var BufferReader = bitcore.encoding.BufferReader;
|
var BufferReader = bitcore.encoding.BufferReader;
|
||||||
|
var BufferWriter = bitcore.encoding.BufferWriter;
|
||||||
|
var PrivateKey = bitcore.PrivateKey;
|
||||||
|
var Opcode = bitcore.Opcode;
|
||||||
|
|
||||||
var script_valid = require('./data/bitcoind/script_valid');
|
var script_valid = require('./data/bitcoind/script_valid');
|
||||||
var script_invalid = require('./data/bitcoind/script_invalid');
|
var script_invalid = require('./data/bitcoind/script_invalid');
|
||||||
var tx_valid = require('./transaction/tx_valid');
|
var tx_valid = require('./transaction/tx_valid');
|
||||||
var tx_invalid = require('./transaction/tx_invalid');
|
var tx_invalid = require('./transaction/tx_invalid');
|
||||||
|
|
||||||
|
//the script string format used in bitcoind data tests
|
||||||
|
Script.fromBitcoindString = function(str) {
|
||||||
|
var bw = new BufferWriter();
|
||||||
|
var tokens = str.split(' ');
|
||||||
|
for (var i = 0; i < tokens.length; i++) {
|
||||||
|
var token = tokens[i];
|
||||||
|
if (token === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var opstr;
|
||||||
|
var opcodenum;
|
||||||
|
var tbuf;
|
||||||
|
if (token[0] === '0' && token[1] === 'x') {
|
||||||
|
var hex = token.slice(2);
|
||||||
|
bw.write(new Buffer(hex, 'hex'));
|
||||||
|
} else if (token[0] === '\'') {
|
||||||
|
var tstr = token.slice(1, token.length - 1);
|
||||||
|
var cbuf = new Buffer(tstr);
|
||||||
|
tbuf = Script().add(cbuf).toBuffer();
|
||||||
|
bw.write(tbuf);
|
||||||
|
} else if (typeof Opcode['OP_' + token] !== 'undefined') {
|
||||||
|
opstr = 'OP_' + token;
|
||||||
|
opcodenum = Opcode[opstr];
|
||||||
|
bw.writeUInt8(opcodenum);
|
||||||
|
} else if (typeof Opcode[token] === 'number') {
|
||||||
|
opstr = token;
|
||||||
|
opcodenum = Opcode[opstr];
|
||||||
|
bw.writeUInt8(opcodenum);
|
||||||
|
} else if (!isNaN(parseInt(token))) {
|
||||||
|
var script = Script().add(BN(token).toScriptNumBuffer());
|
||||||
|
tbuf = script.toBuffer();
|
||||||
|
bw.write(tbuf);
|
||||||
|
} else {
|
||||||
|
throw new Error('Could not determine type of script value');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buf = bw.concat();
|
||||||
|
return this.fromBuffer(buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
//the script string format used in bitcoind data tests
|
||||||
|
Script.toBitcoindString = function() {
|
||||||
|
var str = '';
|
||||||
|
for (var i = 0; i < this.chunks.length; i++) {
|
||||||
|
var chunk = this.chunks[i];
|
||||||
|
if (chunk.buf) {
|
||||||
|
var buf = Script({
|
||||||
|
chunks: [chunk]
|
||||||
|
}).toBuffer();
|
||||||
|
var hex = buf.toString('hex');
|
||||||
|
str = str + ' ' + '0x' + hex;
|
||||||
|
} else if (typeof Opcode.str[chunk.opcodenum] !== 'undefined') {
|
||||||
|
var ostr = Opcode(chunk.opcodenum).toString();
|
||||||
|
str = str + ' ' + ostr.slice(3); //remove OP_
|
||||||
|
} else {
|
||||||
|
str = str + ' ' + '0x' + chunk.opcodenum.toString(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str.substr(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe('ScriptInterpreter', function() {
|
describe('ScriptInterpreter', function() {
|
||||||
|
|
||||||
it('should make a new interp', function() {
|
it('should make a new interp', function() {
|
||||||
@ -58,7 +125,6 @@ describe('ScriptInterpreter', function() {
|
|||||||
var verified;
|
var verified;
|
||||||
var si = ScriptInterpreter();
|
var si = ScriptInterpreter();
|
||||||
verified = si.verify(Script('OP_1'), Script('OP_1'));
|
verified = si.verify(Script('OP_1'), Script('OP_1'));
|
||||||
console.log(si.errstr);
|
|
||||||
verified.should.equal(true);
|
verified.should.equal(true);
|
||||||
verified = ScriptInterpreter().verify(Script('OP_1'), Script('OP_0'));
|
verified = ScriptInterpreter().verify(Script('OP_1'), Script('OP_0'));
|
||||||
verified.should.equal(false);
|
verified.should.equal(false);
|
||||||
@ -78,9 +144,11 @@ describe('ScriptInterpreter', function() {
|
|||||||
verified.should.equal(true);
|
verified.should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should verify this new pay-to-pubkey script', function() {
|
it.skip('should verify this new pay-to-pubkey script', function() {
|
||||||
var keypair = Keypair().fromRandom();
|
// TODO: unskip when Transaction is done
|
||||||
var scriptPubkey = Script().writeBuffer(keypair.pubkey.toDER(true)).writeOp('OP_CHECKSIG');
|
var privkey = new PrivateKey();
|
||||||
|
var pubkey = privkey.toPublicKey();
|
||||||
|
var scriptPubkey = Script.buildPublicKeyOut(pubkey);
|
||||||
|
|
||||||
var hashbuf = new Buffer(32);
|
var hashbuf = new Buffer(32);
|
||||||
hashbuf.fill(0);
|
hashbuf.fill(0);
|
||||||
@ -102,9 +170,9 @@ describe('ScriptInterpreter', function() {
|
|||||||
verified.should.equal(true);
|
verified.should.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should verify this pay-to-pubkey script from script_valid.json', function() {
|
it.skip('should verify this pay-to-pubkey script from script_valid.json', function() {
|
||||||
var scriptSig = Script().fromBitcoindString('0x47 0x3044022007415aa37ce7eaa6146001ac8bdefca0ddcba0e37c5dc08c4ac99392124ebac802207d382307fd53f65778b07b9c63b6e196edeadf0be719130c5db21ff1e700d67501');
|
var scriptSig = Script.fromBitcoindString('0x47 0x3044022007415aa37ce7eaa6146001ac8bdefca0ddcba0e37c5dc08c4ac99392124ebac802207d382307fd53f65778b07b9c63b6e196edeadf0be719130c5db21ff1e700d67501');
|
||||||
var scriptPubkey = Script().fromBitcoindString('0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG');
|
var scriptPubkey = Script.fromBitcoindString('0x41 0x0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 CHECKSIG');
|
||||||
|
|
||||||
var hashbuf = new Buffer(32);
|
var hashbuf = new Buffer(32);
|
||||||
hashbuf.fill(0);
|
hashbuf.fill(0);
|
||||||
@ -124,65 +192,59 @@ describe('ScriptInterpreter', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('vectors', function() {
|
|
||||||
|
|
||||||
var getFlags = function getFlags(flagstr) {
|
var getFlags = function getFlags(flagstr) {
|
||||||
var flags = 0;
|
var flags = 0;
|
||||||
if (flagstr.indexOf('NONE') !== -1) {
|
if (flagstr.indexOf('NONE') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NONE;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NONE;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('P2SH') !== -1) {
|
if (flagstr.indexOf('P2SH') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_P2SH;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_P2SH;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('STRICTENC') !== -1) {
|
if (flagstr.indexOf('STRICTENC') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_STRICTENC;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_STRICTENC;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('DERSIG') !== -1) {
|
if (flagstr.indexOf('DERSIG') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DERSIG;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DERSIG;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('LOW_S') !== -1) {
|
if (flagstr.indexOf('LOW_S') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_LOW_S;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_LOW_S;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('NULLDUMMY') !== -1) {
|
if (flagstr.indexOf('NULLDUMMY') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NULLDUMMY;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NULLDUMMY;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('SIGPUSHONLY') !== -1) {
|
if (flagstr.indexOf('SIGPUSHONLY') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_SIGPUSHONLY;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_SIGPUSHONLY;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('MINIMALDATA') !== -1) {
|
if (flagstr.indexOf('MINIMALDATA') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA;
|
||||||
}
|
}
|
||||||
if (flagstr.indexOf('DISCOURAGE_UPGRADABLE_NOPS') !== -1) {
|
if (flagstr.indexOf('DISCOURAGE_UPGRADABLE_NOPS') !== -1) {
|
||||||
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS;
|
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS;
|
||||||
}
|
}
|
||||||
return flags;
|
return flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
var c = 0;
|
var c = 0;
|
||||||
|
describe.only('bitcoind fixtures', function() {
|
||||||
script_valid.forEach(function(vector) {
|
script_valid.forEach(function(vector) {
|
||||||
if (vector.length === 1) {
|
if (vector.length === 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
c++;
|
c++;
|
||||||
var descstr = vector[3];
|
var descstr = vector[3];
|
||||||
it('should pass script_valid vector ' + c + '(' + descstr + ')', function() {
|
var fullScriptString = vector[0] + ' ' + vector[1];
|
||||||
var scriptSig = Script().fromBitcoindString(vector[0]);
|
var comment = descstr ? (' (' + descstr + ')') : '';
|
||||||
var scriptPubkey = Script().fromBitcoindString(vector[1]);
|
it('should pass script_valid vector #' + c + ': ' + fullScriptString + comment, function() {
|
||||||
|
var scriptSig = Script.fromBitcoindString(vector[0]);
|
||||||
|
var scriptPubkey = Script.fromBitcoindString(vector[1]);
|
||||||
var flags = getFlags(vector[2]);
|
var flags = getFlags(vector[2]);
|
||||||
|
|
||||||
var hashbuf = new Buffer(32);
|
|
||||||
hashbuf.fill(0);
|
|
||||||
var credtx = Transaction();
|
|
||||||
credtx.addTxin(hashbuf, 0xffffffff, Script('OP_0 OP_0'), 0xffffffff);
|
|
||||||
credtx.addTxout(BN(0), scriptPubkey);
|
|
||||||
|
|
||||||
var idbuf = credtx.hash();
|
|
||||||
var spendtx = Transaction();
|
var spendtx = Transaction();
|
||||||
spendtx.addTxin(idbuf, 0, scriptSig, 0xffffffff);
|
|
||||||
spendtx.addTxout(BN(0), Script());
|
|
||||||
|
|
||||||
var interp = ScriptInterpreter();
|
var interp = ScriptInterpreter();
|
||||||
var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0, flags);
|
var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0, flags);
|
||||||
|
console.log(interp.errstr);
|
||||||
verified.should.equal(true);
|
verified.should.equal(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -195,8 +257,8 @@ describe('ScriptInterpreter', function() {
|
|||||||
c++;
|
c++;
|
||||||
var descstr = vector[3];
|
var descstr = vector[3];
|
||||||
it('should pass script_invalid vector ' + c + '(' + descstr + ')', function() {
|
it('should pass script_invalid vector ' + c + '(' + descstr + ')', function() {
|
||||||
var scriptSig = Script().fromBitcoindString(vector[0]);
|
var scriptSig = Script.fromBitcoindString(vector[0]);
|
||||||
var scriptPubkey = Script().fromBitcoindString(vector[1]);
|
var scriptPubkey = Script.fromBitcoindString(vector[1]);
|
||||||
var flags = getFlags(vector[2]);
|
var flags = getFlags(vector[2]);
|
||||||
|
|
||||||
var hashbuf = new Buffer(32);
|
var hashbuf = new Buffer(32);
|
||||||
@ -233,13 +295,13 @@ describe('ScriptInterpreter', function() {
|
|||||||
if (txoutnum === -1) {
|
if (txoutnum === -1) {
|
||||||
txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int
|
txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int
|
||||||
}
|
}
|
||||||
map[input[0] + ':' + txoutnum] = Script().fromBitcoindString(input[2]);
|
map[input[0] + ':' + txoutnum] = Script.fromBitcoindString(input[2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex'));
|
var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex'));
|
||||||
tx.txins.forEach(function(txin, j) {
|
tx.txins.forEach(function(txin, j) {
|
||||||
var scriptSig = txin.script;
|
var scriptSig = txin.script;
|
||||||
var txidhex = BufR(txin.txidbuf).readReverse().toString('hex');
|
var txidhex = BufferReader(txin.txidbuf).readReverse().toString('hex');
|
||||||
var txoutnum = txin.txoutnum;
|
var txoutnum = txin.txoutnum;
|
||||||
var scriptPubkey = map[txidhex + ':' + txoutnum];
|
var scriptPubkey = map[txidhex + ':' + txoutnum];
|
||||||
should.exist(scriptPubkey);
|
should.exist(scriptPubkey);
|
||||||
@ -282,14 +344,14 @@ describe('ScriptInterpreter', function() {
|
|||||||
if (txoutnum === -1) {
|
if (txoutnum === -1) {
|
||||||
txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int
|
txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int
|
||||||
}
|
}
|
||||||
map[input[0] + ':' + txoutnum] = Script().fromBitcoindString(input[2]);
|
map[input[0] + ':' + txoutnum] = Script.fromBitcoindString(input[2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex'));
|
var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex'));
|
||||||
if (tx.txins.length > 0) {
|
if (tx.txins.length > 0) {
|
||||||
tx.txins.some(function(txin, j) {
|
tx.txins.some(function(txin, j) {
|
||||||
var scriptSig = txin.script;
|
var scriptSig = txin.script;
|
||||||
var txidhex = BufR(txin.txidbuf).readReverse().toString('hex');
|
var txidhex = BufferReader(txin.txidbuf).readReverse().toString('hex');
|
||||||
var txoutnum = txin.txoutnum;
|
var txoutnum = txin.txoutnum;
|
||||||
var scriptPubkey = map[txidhex + ':' + txoutnum];
|
var scriptPubkey = map[txidhex + ':' + txoutnum];
|
||||||
should.exist(scriptPubkey);
|
should.exist(scriptPubkey);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user