Merge pull request #734 from maraoz/add/interpreter4

Add ScriptInterpreter for script evaluation
This commit is contained in:
Esteban Ordano 2014-12-16 13:54:34 -03:00
commit bacdff5c27
22 changed files with 2089 additions and 208 deletions

View File

@ -46,6 +46,7 @@ bitcore.PaymentProtocol = require('./lib/paymentprotocol');
bitcore.PrivateKey = require('./lib/privatekey');
bitcore.PublicKey = require('./lib/publickey');
bitcore.Script = require('./lib/script');
bitcore.ScriptInterpreter = require('./lib/script_interpreter');
bitcore.Transaction = require('./lib/transaction');
bitcore.URI = require('./lib/uri');
bitcore.Unit = require('./lib/unit');

View File

@ -140,7 +140,7 @@ BN.prototype.toSM = function(opts) {
// 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
// 4 bytes. We copy that behavior here.
BN.prototype.fromCScriptNumBuffer = function(buf, fRequireMinimal) {
BN.prototype.fromScriptNumBuffer = function(buf, fRequireMinimal) {
var nMaxNumSize = 4;
if (buf.length > nMaxNumSize)
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
// performing a numerical operation that results in an overflow to more than 4
// bytes).
BN.prototype.toCScriptNumBuffer = function(buf) {
BN.prototype.toScriptNumBuffer = function(buf) {
return this.toSM({endian: 'little'});
};

View File

@ -1,7 +1,6 @@
'use strict';
var BN = require('./bn');
var BufferReader = require('../encoding/bufferreader');
var Point = require('./point');
var Signature = require('./signature');
var PublicKey = require('../publickey');
@ -66,7 +65,7 @@ ECDSA.prototype.fromString = function(str) {
this.privkey = PrivateKey.fromString(obj.privkey);
}
if (obj.sig) {
this.sig = Signature().fromString(obj.sig);
this.sig = Signature.fromString(obj.sig);
}
if (obj.k) {
this.k = BN(obj.k, 10);

View File

@ -2,44 +2,44 @@
var hashjs = require('hash.js');
var sha512 = require('sha512');
var crypto = require('crypto');
var BufferUtil = require('../util/buffer');
var $ = require('../util/preconditions');
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) {
if (!Buffer.isBuffer(buf))
throw new Error('sha256 hash must be of a buffer');
var hash = (new hashjs.sha256()).update(buf).digest();
return new Buffer(hash);
$.checkArgument(BufferUtil.isBuffer(buf));
return crypto.createHash('sha256').update(buf).digest();
};
Hash.sha256.blocksize = 512;
Hash.sha256sha256 = function(buf) {
try {
return Hash.sha256(Hash.sha256(buf));
} catch (e) {
throw new Error('sha256sha256 hash must be of a buffer');
}
$.checkArgument(BufferUtil.isBuffer(buf));
return Hash.sha256(Hash.sha256(buf));
};
Hash.ripemd160 = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('ripemd160 hash must be of a buffer');
$.checkArgument(BufferUtil.isBuffer(buf));
var hash = (new hashjs.ripemd160()).update(buf).digest();
return new Buffer(hash);
};
Hash.sha256ripemd160 = function(buf) {
try {
return Hash.ripemd160(Hash.sha256(buf));
} catch (e) {
throw new Error('sha256ripemd160 hash must be of a buffer');
}
$.checkArgument(BufferUtil.isBuffer(buf));
return Hash.ripemd160(Hash.sha256(buf));
};
Hash.sha512 = function(buf) {
if (!Buffer.isBuffer(buf))
throw new Error('sha512 hash must be of a buffer');
$.checkArgument(BufferUtil.isBuffer(buf));
var hash = sha512(buf);
return new Buffer(hash);
};
@ -47,19 +47,17 @@ Hash.sha512 = function(buf) {
Hash.sha512.blocksize = 1024;
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://tools.ietf.org/html/rfc4868#section-2
if (!hashf.blocksize)
throw new Error('Blocksize for hash function unknown');
$.checkArgument(BufferUtil.isBuffer(data));
$.checkArgument(BufferUtil.isBuffer(key));
$.checkArgument(hashf.blocksize);
var blocksize = hashf.blocksize/8;
if (key.length > blocksize)
var blocksize = hashf.blocksize / 8;
if (key.length > blocksize) {
key = hashf(key);
else if (key < blocksize) {
} else if (key < blocksize) {
var fill = new Buffer(blocksize);
fill.fill(0);
key.copy(fill);

View File

@ -10,8 +10,7 @@ var Signature = function Signature(r, s) {
r: r,
s: s
});
}
else if (r) {
} else if (r) {
var obj = r;
this.set(obj);
}
@ -25,88 +24,114 @@ Signature.prototype.set = function(obj) {
return this;
};
Signature.prototype.fromCompact = function(buf) {
Signature.fromCompact = function(buf) {
var sig = new Signature();
var compressed = true;
var i = buf.slice(0, 1)[0] - 27 - 4;
//TODO: handle uncompressed pubkeys
/*
if (i < 0) {
compressed = false;
i = i + 4;
}
*/
var b2 = buf.slice(1, 33);
var b3 = buf.slice(33, 65);
if (!(i === 0 || i === 1 || i === 2 || i === 3))
if (!(i === 0 || i === 1 || i === 2 || i === 3)) {
throw new Error('i must be 0, 1, 2, or 3');
if (b2.length !== 32)
}
if (b2.length !== 32) {
throw new Error('r must be 32 bytes');
if (b3.length !== 32)
}
if (b3.length !== 32) {
throw new Error('s must be 32 bytes');
}
this.compressed = compressed;
this.i = i;
this.r = BN().fromBuffer(b2);
this.s = BN().fromBuffer(b3);
sig.compressed = compressed;
sig.i = i;
sig.r = BN().fromBuffer(b2);
sig.s = BN().fromBuffer(b3);
return this;
return sig;
};
Signature.prototype.fromDER = function(buf) {
var obj = Signature.parseDER(buf);
this.r = obj.r;
this.s = obj.s;
Signature.fromDER = function(buf, strict) {
var obj = Signature.parseDER(buf, strict);
var sig = new Signature();
return this;
sig.r = obj.r;
sig.s = obj.s;
return sig;
};
Signature.prototype.fromString = function(str) {
// The format used in a tx
Signature.fromTxFormat = function(buf) {
var nhashtype = buf.readUInt8(buf.length - 1);
var derbuf = buf.slice(0, buf.length - 1);
var sig = new Signature.fromDER(derbuf, false);
sig.nhashtype = nhashtype;
return sig;
};
Signature.fromString = function(str) {
var buf = new Buffer(str, 'hex');
this.fromDER(buf);
return this;
return Signature.fromDER(buf);
};
Signature.parseDER = function(buf) {
if (!Buffer.isBuffer(buf))
/**
* In order to mimic the non-strict DER encoding of OpenSSL, set strict = false.
*/
Signature.parseDER = function(buf, strict) {
if (typeof strict === 'undefined') {
strict = true;
}
if (!Buffer.isBuffer(buf)) {
throw new Error('DER formatted signature should be a buffer');
}
var header = buf[0];
if (header !== 0x30)
if (header !== 0x30) {
throw new Error('Header byte should be 0x30');
}
var length = buf[1];
if (length !== buf.slice(2).length)
var buflength = buf.slice(2).length;
if (strict && length !== buflength) {
throw new Error('Length byte should length of what follows');
} else {
length = length < buflength ? length : buflength;
}
var rheader = buf[2 + 0];
if (rheader !== 0x02)
if (rheader !== 0x02) {
throw new Error('Integer byte for r should be 0x02');
}
var rlength = buf[2 + 1];
var rbuf = buf.slice(2 + 2, 2 + 2 + rlength);
var r = BN().fromBuffer(rbuf);
var rneg = buf[2 + 1 + 1] === 0x00 ? true : false;
if (rlength !== rbuf.length)
if (rlength !== rbuf.length) {
throw new Error('Length of r incorrect');
}
var sheader = buf[2 + 2 + rlength + 0];
if (sheader !== 0x02)
if (sheader !== 0x02) {
throw new Error('Integer byte for s should be 0x02');
}
var slength = buf[2 + 2 + rlength + 1];
var sbuf = buf.slice(2 + 2 + rlength + 2, 2 + 2 + rlength + 2 + slength);
var s = BN().fromBuffer(sbuf);
var sneg = buf[2 + 2 + rlength + 2 + 2] === 0x00 ? true : false;
if (slength !== sbuf.length)
if (slength !== sbuf.length) {
throw new Error('Length of s incorrect');
}
var sumlength = 2 + 2 + rlength + 2 + slength;
if (length !== sumlength - 2)
if (length !== sumlength - 2) {
throw new Error('Length of signature incorrect');
}
var obj = {
header: header,
@ -126,19 +151,25 @@ Signature.parseDER = function(buf) {
return obj;
};
Signature.prototype.toCompact = function(i, compressed) {
i = typeof i === 'number' ? i : this.i;
compressed = typeof compressed === 'boolean' ? compressed : this.compressed;
if (!(i === 0 || i === 1 || i === 2 || i === 3))
if (!(i === 0 || i === 1 || i === 2 || i === 3)) {
throw new Error('i must be equal to 0, 1, 2, or 3');
}
var val = i + 27 + 4;
if (compressed === false)
val = val - 4;
var b1 = new Buffer([val]);
var b2 = this.r.toBuffer({size: 32});
var b3 = this.s.toBuffer({size: 32});
var b2 = this.r.toBuffer({
size: 32
});
var b3 = this.s.toBuffer({
size: 32
});
return Buffer.concat([b1, b2, b3]);
};
@ -168,6 +199,115 @@ Signature.prototype.toString = function() {
return buf.toString('hex');
};
/**
* This function is translated from bitcoind's IsDERSignature and is used in
* the script interpreter. This "DER" format actually includes an extra byte,
* the nhashtype, at the end. It is really the tx format, not DER format.
*
* A canonical signature exists of: [30] [total len] [02] [len R] [R] [02] [len S] [S] [hashtype]
* Where R and S are not negative (their first byte has its highest bit not set), and not
* excessively padded (do not start with a 0 byte, unless an otherwise negative number follows,
* in which case a single 0 byte is necessary and even required).
*
* See https://bitcointalk.org/index.php?topic=8392.msg127623#msg127623
*/
Signature.isTxDER = function(buf) {
if (buf.length < 9) {
// Non-canonical signature: too short
return false;
}
if (buf.length > 73) {
// Non-canonical signature: too long
return false;
}
if (buf[0] !== 0x30) {
// Non-canonical signature: wrong type
return false;
}
if (buf[1] !== buf.length - 3) {
// Non-canonical signature: wrong length marker
return false;
}
var nLenR = buf[3];
if (5 + nLenR >= buf.length) {
// Non-canonical signature: S length misplaced
return false;
}
var nLenS = buf[5 + nLenR];
if ((nLenR + nLenS + 7) !== buf.length) {
// Non-canonical signature: R+S length mismatch
return false;
}
var R = buf.slice(4);
if (buf[4 - 2] !== 0x02) {
// Non-canonical signature: R value type mismatch
return false;
}
if (nLenR === 0) {
// Non-canonical signature: R length is zero
return false;
}
if (R[0] & 0x80) {
// Non-canonical signature: R value negative
return false;
}
if (nLenR > 1 && (R[0] === 0x00) && !(R[1] & 0x80)) {
// Non-canonical signature: R value excessively padded
return false;
}
var S = buf.slice(6 + nLenR);
if (buf[6 + nLenR - 2] !== 0x02) {
// Non-canonical signature: S value type mismatch
return false;
}
if (nLenS === 0) {
// Non-canonical signature: S length is zero
return false;
}
if (S[0] & 0x80) {
// Non-canonical signature: S value negative
return false;
}
if (nLenS > 1 && (S[0] === 0x00) && !(S[1] & 0x80)) {
// Non-canonical signature: S value excessively padded
return false;
}
return true;
};
/**
* Compares to bitcoind's IsLowDERSignature
* See also ECDSA signature algorithm which enforces this.
* See also BIP 62, "low S values in signatures"
*/
Signature.prototype.hasLowS = function() {
if (this.s.lt(1) ||
this.s.gt(BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0'))) {
return false;
}
return true;
};
/**
* @returns true if the nhashtype is exactly equal to one of the standard options or combinations thereof.
* Translated from bitcoind's IsDefinedHashtypeSignature
*/
Signature.prototype.hasDefinedHashtype = function() {
if (this.nhashtype < Signature.SIGHASH_ALL || this.nhashtype > Signature.SIGHASH_SINGLE) {
return false;
}
return true;
};
Signature.prototype.toTxFormat = function() {
var derbuf = this.toDER();
var buf = new Buffer(1);
buf.writeUInt8(this.nhashtype, 0);
return Buffer.concat([derbuf, buf]);
};
Signature.SIGHASH_ALL = 0x01;
Signature.SIGHASH_NONE = 0x02;
Signature.SIGHASH_SINGLE = 0x03;

View File

@ -388,7 +388,7 @@ PaymentProtocol.prototype.sinVerify = function() {
var buf = this.serializeForSig();
var hash = magicHash(buf);
var publicKey = PublicKey.fromBuffer(pubkey);
var signature = new Signature().fromString(sig);
var signature = new Signature.fromString(sig);
var verified = ECDSA.verify(hash, signature, publicKey);
return verified;
};

View File

@ -1,11 +1,12 @@
'use strict';
var _ = require('lodash');
var Address = require('./address');
var BN = require('./crypto/bn');
var Point = require('./crypto/point');
var JSUtil = require('./util/js');
var Network = require('./networks');
var _ = require('lodash');
var $ = require('./util/preconditions');
/**
* Instantiate a PublicKey from a 'PrivateKey', 'Point', 'string', 'Buffer'.
@ -33,9 +34,9 @@ var PublicKey = function PublicKey(data, extra) {
if (!(this instanceof PublicKey)) {
return new PublicKey(data, extra);
}
if (!data) {
throw new TypeError('First argument is required, please include public key data.');
}
$.checkArgument(data, new TypeError('First argument is required, please include public key data.'));
if (data instanceof PublicKey) {
// Return copy, but as it's an immutable object, return same argument
return data;
@ -92,8 +93,8 @@ var PublicKey = function PublicKey(data, extra) {
* @private
*/
PublicKey._isPrivateKey = function(param) {
return param && param.constructor && param.constructor.name
&& param.constructor.name === 'PrivateKey';
var PrivateKey = require('./privatekey');
return param instanceof PrivateKey;
};
/**
@ -126,10 +127,9 @@ PublicKey._isJSON = function(json) {
* @private
*/
PublicKey._transformPrivateKey = function(privkey) {
$.checkArgument(PublicKey._isPrivateKey(privkey),
new TypeError('Must be an instance of PrivateKey'));
var info = {};
if (!PublicKey._isPrivateKey(privkey)) {
throw new TypeError('Must be an instance of PrivateKey');
}
info.point = Point.getG().mul(privkey.bn);
info.compressed = privkey.compressed;
info.network = privkey.network;
@ -140,21 +140,22 @@ PublicKey._transformPrivateKey = function(privkey) {
* Internal function to transform DER into a public key point
*
* @param {Buffer} buf - An hex encoded buffer
* @param {bool} [strict] - if set to false, will loosen some conditions
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformDER = function(buf){
PublicKey._transformDER = function(buf, strict) {
$.checkArgument(PublicKey._isBuffer(buf), new TypeError('Must be a hex buffer of DER encoded public key'));
var info = {};
if (!PublicKey._isBuffer(buf)) {
throw new TypeError('Must be a hex buffer of DER encoded public key');
}
strict = _.isUndefined(strict) ? true : strict;
var x;
var y;
var xbuf;
var ybuf;
if (buf[0] === 0x04) {
if (buf[0] === 0x04 || (!strict && (buf[0] === 0x06 || buf[0] === 0x07))) {
xbuf = buf.slice(1, 33);
ybuf = buf.slice(33, 65);
if (xbuf.length !== 32 || ybuf.length !== 32 || buf.length !== 65) {
@ -169,7 +170,7 @@ PublicKey._transformDER = function(buf){
x = BN(xbuf);
info = PublicKey._transformX(true, x);
info.compressed = true;
} else if (buf[0] == 0x02) {
} else if (buf[0] === 0x02) {
xbuf = buf.slice(1);
x = BN(xbuf);
info = PublicKey._transformX(false, x);
@ -188,11 +189,10 @@ PublicKey._transformDER = function(buf){
* @returns {Object} An object with keys: point and compressed
* @private
*/
PublicKey._transformX = function(odd, x){
PublicKey._transformX = function(odd, x) {
$.checkArgument(typeof odd === 'boolean',
new TypeError('Must specify whether y is odd or not (true or false)'));
var info = {};
if (typeof odd !== 'boolean') {
throw new TypeError('Must specify whether y is odd or not (true or false)');
}
info.point = Point.fromX(odd, x);
return info;
};
@ -204,10 +204,8 @@ PublicKey._transformX = function(odd, x){
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromJSON = function(json) {
if (!PublicKey._isJSON(json)) {
throw new TypeError('Must be a valid JSON string or plain object');
}
$.checkArgument(PublicKey._isJSON(json),
new TypeError('Must be a valid JSON string or plain object'));
return new PublicKey(json);
};
@ -225,7 +223,9 @@ PublicKey._transformJSON = function(json) {
var x = BN(json.x, 'hex');
var y = BN(json.y, 'hex');
var point = new Point(x, y);
return new PublicKey(point, {compressed: json.compressed});
return new PublicKey(point, {
compressed: json.compressed
});
};
/**
@ -235,25 +235,27 @@ PublicKey._transformJSON = function(json) {
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromPrivateKey = function(privkey) {
if (!PublicKey._isPrivateKey(privkey)) {
throw new TypeError('Must be an instance of PrivateKey');
}
$.checkArgument(PublicKey._isPrivateKey(privkey), new TypeError('Must be an instance of PrivateKey'));
var info = PublicKey._transformPrivateKey(privkey);
return new PublicKey(info.point, {compressed: info.compressed, network: info.network});
return new PublicKey(info.point, {
compressed: info.compressed,
network: info.network
});
};
/**
* Instantiate a PublicKey from a Buffer
*
* @param {Buffer} buf - A DER buffer
* @param {Buffer} buf - A DER hex buffer
* @param {bool} [strict] - if set to false, will loosen some conditions
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromDER = PublicKey.fromBuffer = function(buf) {
if (!PublicKey._isBuffer(buf)) {
throw new TypeError('Must be a hex buffer of DER encoded public key');
}
var info = PublicKey._transformDER(buf);
return new PublicKey(info.point, {compressed: info.compressed});
PublicKey.fromDER = PublicKey.fromBuffer = function(buf, strict) {
$.checkArgument(PublicKey._isBuffer(buf),
new TypeError('Must be a hex buffer of DER encoded public key'));
var info = PublicKey._transformDER(buf, strict);
return new PublicKey(info.point, {
compressed: info.compressed
});
};
/**
@ -263,11 +265,12 @@ PublicKey.fromDER = PublicKey.fromBuffer = function(buf) {
* @param {boolean=true} compressed - whether to store this public key as compressed format
* @returns {PublicKey} A new valid instance of PublicKey
*/
PublicKey.fromPoint = function(point, compressed){
if (!(point instanceof Point)) {
throw new TypeError('First argument must be an instance of Point.');
}
return new PublicKey(point, {compressed: compressed});
PublicKey.fromPoint = function(point, compressed) {
$.checkArgument(point instanceof Point,
new TypeError('First argument must be an instance of Point.'));
return new PublicKey(point, {
compressed: compressed
});
};
/**
@ -280,7 +283,9 @@ PublicKey.fromPoint = function(point, compressed){
PublicKey.fromString = function(str, encoding) {
var buf = new Buffer(str, encoding || 'hex');
var info = PublicKey._transformDER(buf);
return new PublicKey(info.point, {compressed: info.compressed});
return new PublicKey(info.point, {
compressed: info.compressed
});
};
/**
@ -292,7 +297,9 @@ PublicKey.fromString = function(str, encoding) {
*/
PublicKey.fromX = function(odd, x) {
var info = PublicKey._transformX(odd, x);
return new PublicKey(info.point, {compressed: info.compressed});
return new PublicKey(info.point, {
compressed: info.compressed
});
};
/**
@ -334,7 +341,7 @@ PublicKey.prototype.toObject = function toObject() {
};
};
PublicKey.prototype.toJSON = function toJSON(){
PublicKey.prototype.toJSON = function toJSON() {
return JSON.stringify(this.toObject());
};
@ -347,8 +354,12 @@ PublicKey.prototype.toBuffer = PublicKey.prototype.toDER = function() {
var x = this.point.getX();
var y = this.point.getY();
var xbuf = x.toBuffer({size: 32});
var ybuf = y.toBuffer({size: 32});
var xbuf = x.toBuffer({
size: 32
});
var ybuf = y.toBuffer({
size: 32
});
var prefix;
if (!this.compressed) {
@ -395,4 +406,5 @@ PublicKey.prototype.inspect = function() {
(this.network ? this.network.name : '') + '>';
};
module.exports = PublicKey;

View File

@ -127,7 +127,7 @@ Script.prototype.toBuffer = function() {
};
Script.fromString = function(str) {
if (jsUtil.isHexa(str)) {
if (jsUtil.isHexa(str) || str.length === 0) {
return new Script(new buffer.Buffer(str, 'hex'));
}
var script = new Script();
@ -140,7 +140,7 @@ Script.fromString = function(str) {
var opcode = Opcode(token);
var opcodenum = opcode.toNumber();
if (typeof opcodenum === 'undefined') {
if (_.isUndefined(opcodenum)) {
opcodenum = parseInt(token);
if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) {
script.chunks.push({
@ -184,7 +184,11 @@ Script.prototype.toString = function() {
if (typeof Opcode.reverseMap[opcodenum] !== 'undefined') {
str = str + ' ' + Opcode(opcodenum).toString();
} 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 {
if (opcodenum === Opcode.OP_PUSHDATA1 ||
@ -193,7 +197,9 @@ Script.prototype.toString = function() {
str = str + ' ' + Opcode(opcodenum).toString();
}
str = str + ' ' + chunk.len;
str = str + ' ' + '0x' + chunk.buf.toString('hex');
if (chunk.len > 0) {
str = str + ' ' + '0x' + chunk.buf.toString('hex');
}
}
}
@ -258,11 +264,11 @@ Script.prototype.isPublicKeyIn = function() {
* @returns true if this is a p2sh output script
*/
Script.prototype.isScriptHashOut = function() {
return this.chunks.length === 3 &&
this.chunks[0].opcodenum === Opcode.OP_HASH160 &&
this.chunks[1].buf &&
this.chunks[1].buf.length === 20 &&
this.chunks[2].opcodenum === Opcode.OP_EQUAL;
var buf = this.toBuffer();
return (buf.length === 23 &&
buf[0] === Opcode.OP_HASH160 &&
buf[1] === 0x14 &&
buf[buf.length - 1] === Opcode.OP_EQUAL);
};
/**
@ -475,9 +481,7 @@ Script.prototype._addOpcode = function(opcode, prepend) {
Script.prototype._addBuffer = function(buf, prepend) {
var opcodenum;
var len = buf.length;
if (len === 0) {
return;
} else if (len > 0 && len < Opcode.OP_PUSHDATA1) {
if (len >= 0 && len < Opcode.OP_PUSHDATA1) {
opcodenum = len;
} else if (len < Math.pow(2, 8)) {
opcodenum = Opcode.OP_PUSHDATA1;
@ -522,7 +526,9 @@ Script.buildMultisigOut = function(pubkeys, m, opts) {
opts = opts || {};
var s = new Script();
s.add(Opcode.smallInt(m));
pubkeys = _.map(pubkeys, function(pubkey) { return PublicKey(pubkey); });
pubkeys = _.map(pubkeys, function(pubkey) {
return PublicKey(pubkey);
});
var sorted = pubkeys;
if (!opts.noSorting) {
sorted = _.sortBy(pubkeys, function(pubkey) {
@ -548,7 +554,7 @@ Script.buildMultisigOut = function(pubkeys, m, opts) {
* @param {boolean=false} opts.noSorting don't sort the given public keys before creating the script
* @param {Script=} opts.cachedMultisig don't recalculate the redeemScript
*
* @returns Script
* @returns Script
*/
Script.buildP2SHMultisigIn = function(pubkeys, threshold, signatures, opts) {
opts = opts || {};
@ -601,8 +607,10 @@ Script.buildDataOut = function(data) {
data = new Buffer(data);
}
var s = new Script();
s.add(Opcode.OP_RETURN)
.add(data);
s.add(Opcode.OP_RETURN);
if (!_.isUndefined(data)) {
s.add(data);
}
return s;
};
@ -663,4 +671,61 @@ Script.fromAddress = function(address) {
throw new errors.Script.UnrecognizedAddress(address);
};
/**
* Analagous to bitcoind's FindAndDelete. Find and delete equivalent chunks,
* typically used with push data chunks. Note that this will find and delete
* not just the same data, but the same data with the same push data op as
* produced by default. i.e., if a pushdata in a tx does not use the minimal
* pushdata op, then when you try to remove the data it is pushing, it will not
* be removed, because they do not use the same pushdata op.
*/
Script.prototype.findAndDelete = function(script) {
var buf = script.toBuffer();
var hex = buf.toString('hex');
for (var i = 0; i < this.chunks.length; i++) {
var script2 = Script({
chunks: [this.chunks[i]]
});
var buf2 = script2.toBuffer();
var hex2 = buf2.toString('hex');
if (hex === hex2) {
this.chunks.splice(i, 1);
}
}
return this;
};
/**
* @returns true if the chunk {i} is the smallest way to push that particular data.
* Comes from bitcoind's script interpreter CheckMinimalPush function
*/
Script.prototype.checkMinimalPush = function(i) {
var chunk = this.chunks[i];
var buf = chunk.buf;
var opcodenum = chunk.opcodenum;
if (!buf) {
return true;
}
if (buf.length === 0) {
// Could have used OP_0.
return opcodenum === Opcode.OP_0;
} else if (buf.length === 1 && buf[0] >= 1 && buf[0] <= 16) {
// Could have used OP_1 .. OP_16.
return opcodenum === Opcode.OP_1 + (buf[0] - 1);
} else if (buf.length === 1 && buf[0] === 0x81) {
// Could have used OP_1NEGATE
return opcodenum === Opcode.OP_1NEGATE;
} else if (buf.length <= 75) {
// Could have used a direct push (opcode indicating number of bytes pushed + those bytes).
return opcodenum === buf.length;
} else if (buf.length <= 255) {
// Could have used OP_PUSHDATA.
return opcodenum === Opcode.OP_PUSHDATA1;
} else if (buf.length <= 65535) {
// Could have used OP_PUSHDATA2.
return opcodenum === Opcode.OP_PUSHDATA2;
}
return true;
};
module.exports = Script;

1148
lib/script_interpreter.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ var _ = require('lodash');
var errors = require('../../errors');
var BufferWriter = require('../../encoding/bufferwriter');
var buffer = require('buffer');
var bufferUtil = require('../../util/buffer');
var BufferUtil = require('../../util/buffer');
var JSUtil = require('../../util/js');
var Script = require('../../script');
var Sighash = require('../sighash');
@ -99,7 +99,7 @@ Input.prototype.setScript = function(script) {
if (script instanceof Script) {
this._script = script;
this._scriptBuffer = script.toBuffer();
} else if (bufferUtil.isBuffer(script)) {
} else if (BufferUtil.isBuffer(script)) {
this._script = null;
this._scriptBuffer = new buffer.Buffer(script);
} else {

View File

@ -10,6 +10,7 @@ var BufferWriter = require('../encoding/bufferwriter');
var BN = require('../crypto/bn');
var Hash = require('../crypto/hash');
var ECDSA = require('../crypto/ecdsa');
var $ = require('../util/preconditions');
var SIGHASH_SINGLE_BUG = '0000000000000000000000000000000000000000000000000000000000000001';
var BITS_64_ON = 'ffffffffffffffff';
@ -23,7 +24,7 @@ var BITS_64_ON = 'ffffffffffffffff';
* @param {number} inputNumber the input index for the signature
* @param {Script} subscript the script that will be signed
*/
function sighash(transaction, sighashType, inputNumber, subscript) {
var sighash = function sighash(transaction, sighashType, inputNumber, subscript) {
var Transaction = require('./transaction');
var Input = require('./input');
@ -39,11 +40,11 @@ function sighash(transaction, sighashType, inputNumber, subscript) {
// Blank signatures for other inputs
txcopy.inputs[i] = new Input(txcopy.inputs[i]).setScript(Script.empty());
}
txcopy.inputs[inputNumber] = new Input(txcopy.inputs[inputNumber]).setScript(subscript);
if ((sighashType & 31) === Signature.SIGHASH_NONE ||
(sighashType & 31) === Signature.SIGHASH_SINGLE) {
(sighashType & 31) === Signature.SIGHASH_SINGLE) {
// clear all sequenceNumbers
for (i = 0; i < txcopy.inputs.length; i++) {
@ -84,21 +85,25 @@ function sighash(transaction, sighashType, inputNumber, subscript) {
.write(txcopy.toBuffer())
.writeInt32LE(sighashType)
.toBuffer();
return BufferReader(Hash.sha256sha256(buf)).readReverse();
}
var ret = Hash.sha256sha256(buf);
ret = new BufferReader(ret).readReverse();
return ret;
};
function sign(transaction, keypair, nhashtype, nin, subscript) {
var sign = function sign(transaction, keypair, nhashtype, nin, subscript) {
var hashbuf = sighash(transaction, nhashtype, nin, subscript);
hashbuf = new BufferReader(hashbuf).readReverse();
var sig = ECDSA.sign(hashbuf, keypair, 'little').set({nhashtype: nhashtype});
var sig = ECDSA.sign(hashbuf, keypair, 'little').set({
nhashtype: nhashtype
});
return sig;
}
};
function verify(transaction, sig, pubkey, nin, subscript) {
var verify = function verify(transaction, sig, pubkey, nin, subscript) {
$.checkArgument(transaction);
$.checkArgument(sig && sig.nhashtype);
var hashbuf = sighash(transaction, sig.nhashtype, nin, subscript);
hashbuf = new BufferReader(hashbuf).readReverse();
return ECDSA.verify(hashbuf, sig, pubkey, 'little');
}
};
module.exports = {
sighash: sighash,

View File

@ -11,6 +11,7 @@ var BufferReader = require('../encoding/bufferreader');
var BufferWriter = require('../encoding/bufferwriter');
var Hash = require('../crypto/hash');
var Signature = require('../crypto/signature');
var Sighash = require('./sighash');
var Address = require('../address');
var Unit = require('../unit');
@ -97,7 +98,7 @@ Transaction.prototype.serialize = Transaction.prototype.toString = function() {
return this.toBuffer().toString('hex');
};
Transaction.prototype.inspect = function () {
Transaction.prototype.inspect = function() {
return '<Transaction: ' + this.toString() + '>';
};
@ -231,12 +232,16 @@ Transaction.prototype._fromNonP2SH = function(utxo) {
};
Transaction._isNewUtxo = function(utxo) {
var isDefined = function(param) { return !_.isUndefined(param); };
var isDefined = function(param) {
return !_.isUndefined(param);
};
return _.all(_.map([utxo.txId, utxo.outputIndex, utxo.satoshis, utxo.script], isDefined));
};
Transaction._isOldUtxo = function(utxo) {
var isDefined = function(param) { return !_.isUndefined(param); };
var isDefined = function(param) {
return !_.isUndefined(param);
};
return _.all(_.map([utxo.txid, utxo.vout, utxo.scriptPubKey, utxo.amount], isDefined));
};
@ -383,4 +388,11 @@ Transaction.prototype.isValidSignature = function(signature) {
return this.inputs[signature.inputIndex].isValidSignature(self, signature);
};
/**
* @returns {bool} whether the signature is valid for this transaction input
*/
Transaction.prototype.verify = function(sig, pubkey, nin, subscript) {
return Sighash.verify(this, sig, pubkey, nin, subscript);
};
module.exports = Transaction;

View File

@ -127,7 +127,7 @@ describe("ECDSA", function() {
it('should calculate the correct public key for this signature with low s', function() {
ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10);
ecdsa.sig = Signature().fromString('3045022100ec3cfe0e335791ad278b4ec8eac93d0347a97877bb1d54d35d189e225c15f6650220278cf15b05ce47fb37d2233802899d94c774d5480bba9f0f2d996baa13370c43');
ecdsa.sig = Signature.fromString('3045022100ec3cfe0e335791ad278b4ec8eac93d0347a97877bb1d54d35d189e225c15f6650220278cf15b05ce47fb37d2233802899d94c774d5480bba9f0f2d996baa13370c43');
ecdsa.sig.i = 0;
var pubkey = ecdsa.sig2pubkey();
pubkey.point.eq(ecdsa.pubkey.point).should.equal(true);
@ -136,7 +136,7 @@ describe("ECDSA", function() {
it('should calculate the correct public key for this signature with high s', function() {
ecdsa.k = BN('114860389168127852803919605627759231199925249596762615988727970217268189974335', 10);
ecdsa.sign();
ecdsa.sig = Signature().fromString('3046022100ec3cfe0e335791ad278b4ec8eac93d0347a97877bb1d54d35d189e225c15f665022100d8730ea4fa31b804c82ddcc7fd766269f33a079ea38e012c9238f2e2bcff34fe');
ecdsa.sig = Signature.fromString('3046022100ec3cfe0e335791ad278b4ec8eac93d0347a97877bb1d54d35d189e225c15f665022100d8730ea4fa31b804c82ddcc7fd766269f33a079ea38e012c9238f2e2bcff34fe');
ecdsa.sig.i = 1;
var pubkey = ecdsa.sig2pubkey();
pubkey.point.eq(ecdsa.pubkey.point).should.equal(true);
@ -169,8 +169,7 @@ describe("ECDSA", function() {
});
it('should return an error if the signature is incorrect', function() {
ecdsa.sig = new Signature();
ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.sig.r = ecdsa.sig.r.add(BN(1));
ecdsa.sigError().should.equal("Invalid signature");
});
@ -235,8 +234,7 @@ describe("ECDSA", function() {
describe('#verify', function() {
it('should verify a signature that was just signed', function() {
ecdsa.sig = new Signature();
ecdsa.sig.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.sig = new Signature.fromString('3046022100e9915e6236695f093a4128ac2a956c40ed971531de2f4f41ba05fac7e2bd019c02210094e6a4a769cc7f2a8ab3db696c7cd8d56bcdbfff860a8c81de4bc6a798b90827');
ecdsa.verify().verified.should.equal(true);
});

View File

@ -8,6 +8,23 @@ describe('Hash', function() {
var buf = new Buffer([0, 1, 2, 3, 253, 254, 255]);
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() {
it('should calculate the hash of this buffer correctly', function() {
@ -18,13 +35,13 @@ describe('Hash', function() {
it('should throw an error when the input is not a buffer', function() {
(function() {
Hash.sha256(str);
}).should.throw('sha256 hash must be of a buffer');
}).should.throw('Invalid Argument');
});
});
describe('#sha256hmac', function() {
it('should compute this known empty test vector correctly', function() {
var key = new Buffer('');
var data = new Buffer('');
@ -49,7 +66,7 @@ describe('Hash', function() {
it('should throw an error when the input is not a buffer', function() {
(function() {
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() {
(function() {
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() {
(function() {
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() {
(function() {
Hash.sha512(str);
}).should.throw('sha512 hash must be of a buffer');
}).should.throw('Invalid Argument');
});
});

View File

@ -4,6 +4,10 @@ var should = require('chai').should();
var bitcore = require('../..');
var BN = bitcore.crypto.BN;
var Signature = bitcore.crypto.Signature;
var JSUtil = bitcore.util.js;
var sig_canonical = require('../data/bitcoind/sig_canonical');
var sig_noncanonical = require('../data/bitcoind/sig_noncanonical');
describe('Signature', function() {
@ -41,8 +45,7 @@ describe('Signature', function() {
blank,
blank
]);
var sig = new Signature();
sig.fromCompact(compressed);
var sig = Signature.fromCompact(compressed);
sig.r.cmp(0).should.equal(0);
sig.s.cmp(0).should.equal(0);
});
@ -54,8 +57,7 @@ describe('Signature', function() {
var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex');
it('should parse this DER format signature', function() {
var sig = new Signature();
sig.fromDER(buf);
var sig = Signature.fromDER(buf);
sig.r.toBuffer({
size: 32
}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277');
@ -71,8 +73,7 @@ describe('Signature', function() {
var buf = new Buffer('3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2', 'hex');
it('should parse this DER format signature in hex', function() {
var sig = new Signature();
sig.fromString(buf.toString('hex'));
var sig = Signature.fromString(buf.toString('hex'));
sig.r.toBuffer({
size: 32
}).toString('hex').should.equal('75fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e6277');
@ -83,6 +84,25 @@ describe('Signature', function() {
});
describe('#fromTxFormat', function() {
it('should convert from this known tx-format buffer', function() {
var buf = new Buffer('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e7201', 'hex');
var sig = Signature.fromTxFormat(buf);
sig.r.toString().should.equal('63173831029936981022572627018246571655303050627048489594159321588908385378810');
sig.s.toString().should.equal('4331694221846364448463828256391194279133231453999942381442030409253074198130');
sig.nhashtype.should.equal(Signature.SIGHASH_ALL);
});
it('should parse this known signature and rebuild it', function() {
var hex = "3044022007415aa37ce7eaa6146001ac8bdefca0ddcba0e37c5dc08c4ac99392124ebac802207d382307fd53f65778b07b9c63b6e196edeadf0be719130c5db21ff1e700d67501";
var buf = new Buffer(hex, 'hex');
var sig = Signature.fromTxFormat(buf);
sig.toTxFormat().toString('hex').should.equal(hex);
});
});
describe('#parseDER', function() {
it('should parse this signature generated in node', function() {
@ -133,6 +153,13 @@ describe('Signature', function() {
parsed.s.toString().should.equal('44212963026209759051804639008236126356702363229859210154760104982946304432721');
});
it('should parse this signature from script_valid.json', function() {
var sighex = '304502203e4516da7253cf068effec6b95c41221c0cf3a8e6ccb8cbf1725b562e9afde2c022100ab1e3da73d67e32045a20e0b999e049978ea8d6ee5480d485fcf2ce0d03b2ef051';
var sig = Buffer(sighex, 'hex');
var parsed = Signature.parseDER(sig, false);
should.exist(parsed);
});
});
describe('#toDER', function() {
@ -151,7 +178,6 @@ describe('Signature', function() {
});
describe('#toString', function() {
it('should convert this signature in to hex DER', function() {
var r = BN('63173831029936981022572627018246571655303050627048489594159321588908385378810');
var s = BN('4331694221846364448463828256391194279133231453999942381442030409253074198130');
@ -162,7 +188,62 @@ describe('Signature', function() {
var hex = sig.toString();
hex.should.equal('30450221008bab1f0a2ff2f9cb8992173d8ad73c229d31ea8e10b0f4d4ae1a0d8ed76021fa02200993a6ec81755b9111762fc2cf8e3ede73047515622792110867d12654275e72');
});
});
describe('@isTxDER', function() {
it('should know this is a DER signature', function() {
var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a5101';
var sigbuf = new Buffer(sighex, 'hex');
Signature.isTxDER(sigbuf).should.equal(true);
});
it('should know this is not a DER signature', function() {
//for more extensive tests, see the script interpreter
var sighex = '3042021e17cfe77536c3fb0526bd1a72d7a8e0973f463add210be14063c8a9c37632022061bfa677f825ded82ba0863fb0c46ca1388dd3e647f6a93c038168b59d131a5101';
var sigbuf = new Buffer(sighex, 'hex');
sigbuf[0] = 0x31;
Signature.isTxDER(sigbuf).should.equal(false);
});
describe('bitcoind fixtures', function() {
var test_sigs = function(set, expected) {
var i = 0;
set.forEach(function(vector) {
if (!JSUtil.isHexa(vector)) {
// non-hex strings are ignored
return;
}
it('should be ' + (expected ? '' : 'in') + 'valid for fixture #' + i, function() {
var sighex = vector;
Signature.isTxDER(new Buffer(sighex, 'hex')).should.equal(expected);
});
i++;
});
};
test_sigs(sig_canonical, true);
//test_sigs(sig_noncanonical, false);
});
});
describe('#hasLowS', function() {
it('should detect high and low S', function() {
var r = BN('63173831029936981022572627018246571655303050627048489594159321588908385378810');
var s = BN('4331694221846364448463828256391194279133231453999942381442030409253074198130');
var s2 = BN('7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B2000');
var sig = new Signature({
r: r,
s: s
});
var sig2 = new Signature({
r: r,
s: s2
});
sig2.hasLowS().should.equal(true);
sig.hasLowS().should.equal(false);
});
});
});

View File

@ -0,0 +1,7 @@
[
"300602010002010001",
"3008020200ff020200ff01",
"304402203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df0c022030b61dd36543125d56b9f9f3a1f9353189e5af33cdda8d77a5209aec03978fa001",
"30450220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01",
"3046022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f90a0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585db6ba01"
]

View File

@ -0,0 +1,22 @@
[
"non-hex strings are ignored",
"too short:", "30050201FF020001",
"too long:", "30470221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"hashtype:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed11",
"type:", "314402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"total length:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"S len oob:", "301F01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb101",
"R+S:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed0001",
"R type:", "304401205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"R len = 0:", "3024020002202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"R<0:", "304402208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"R padded:", "30450221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610502202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"S type:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba610501202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"S len = 0:", "302402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba6105020001",
"S<0:", "304402205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01",
"S padded:", "304502205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61050221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695ed01"
]

View File

@ -7,7 +7,6 @@ var Opcode = bitcore.Opcode;
var PublicKey = bitcore.PublicKey;
var Address = bitcore.Address;
describe('Script', function() {
it('should make a new script', function() {
@ -19,7 +18,7 @@ describe('Script', function() {
it('should parse this buffer containing an OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_0').toNumber();
buf[0] = Opcode.OP_0;
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].opcodenum.should.equal(buf[0]);
@ -27,7 +26,7 @@ describe('Script', function() {
it('should parse this buffer containing another OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_CHECKMULTISIG').toNumber();
buf[0] = Opcode.OP_CHECKMULTISIG;
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].opcodenum.should.equal(buf[0]);
@ -42,7 +41,7 @@ describe('Script', function() {
it('should parse this buffer containing OP_PUSHDATA1 and three bytes of data', function() {
var buf = new Buffer([0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA1').toNumber();
buf[0] = Opcode.OP_PUSHDATA1;
buf.writeUInt8(3, 1);
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
@ -51,7 +50,7 @@ describe('Script', function() {
it('should parse this buffer containing OP_PUSHDATA2 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA2').toNumber();
buf[0] = Opcode.OP_PUSHDATA2;
buf.writeUInt16LE(3, 1);
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
@ -60,7 +59,7 @@ describe('Script', function() {
it('should parse this buffer containing OP_PUSHDATA4 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA4').toNumber();
buf[0] = Opcode.OP_PUSHDATA4;
buf.writeUInt16LE(3, 1);
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
@ -69,10 +68,10 @@ describe('Script', function() {
it('should parse this buffer an OP code, data, and another OP code', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]);
buf[0] = Opcode('OP_0').toNumber();
buf[1] = Opcode('OP_PUSHDATA4').toNumber();
buf[0] = Opcode.OP_0;
buf[1] = Opcode.OP_PUSHDATA4;
buf.writeUInt16LE(3, 2);
buf[buf.length - 1] = Opcode('OP_0').toNumber();
buf[buf.length - 1] = Opcode.OP_0;
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(3);
script.chunks[0].opcodenum.should.equal(buf[0]);
@ -86,7 +85,7 @@ describe('Script', function() {
it('should output this buffer containing an OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_0').toNumber();
buf[0] = Opcode.OP_0;
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].opcodenum.should.equal(buf[0]);
@ -95,7 +94,7 @@ describe('Script', function() {
it('should output this buffer containing another OP code', function() {
var buf = new Buffer(1);
buf[0] = Opcode('OP_CHECKMULTISIG').toNumber();
buf[0] = Opcode.OP_CHECKMULTISIG;
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
script.chunks[0].opcodenum.should.equal(buf[0]);
@ -112,7 +111,7 @@ describe('Script', function() {
it('should output this buffer containing OP_PUSHDATA1 and three bytes of data', function() {
var buf = new Buffer([0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA1').toNumber();
buf[0] = Opcode.OP_PUSHDATA1;
buf.writeUInt8(3, 1);
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
@ -122,7 +121,7 @@ describe('Script', function() {
it('should output this buffer containing OP_PUSHDATA2 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA2').toNumber();
buf[0] = Opcode.OP_PUSHDATA2;
buf.writeUInt16LE(3, 1);
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
@ -132,7 +131,7 @@ describe('Script', function() {
it('should output this buffer containing OP_PUSHDATA4 and three bytes of data', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 1, 2, 3]);
buf[0] = Opcode('OP_PUSHDATA4').toNumber();
buf[0] = Opcode.OP_PUSHDATA4;
buf.writeUInt16LE(3, 1);
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(1);
@ -142,10 +141,10 @@ describe('Script', function() {
it('should output this buffer an OP code, data, and another OP code', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]);
buf[0] = Opcode('OP_0').toNumber();
buf[1] = Opcode('OP_PUSHDATA4').toNumber();
buf[0] = Opcode.OP_0;
buf[1] = Opcode.OP_PUSHDATA4;
buf.writeUInt16LE(3, 2);
buf[buf.length - 1] = Opcode('OP_0').toNumber();
buf[buf.length - 1] = Opcode.OP_0;
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(3);
script.chunks[0].opcodenum.should.equal(buf[0]);
@ -176,10 +175,10 @@ describe('Script', function() {
it('should output this buffer an OP code, data, and another OP code', function() {
var buf = new Buffer([0, 0, 0, 0, 0, 0, 1, 2, 3, 0]);
buf[0] = Opcode('OP_0').toNumber();
buf[1] = Opcode('OP_PUSHDATA4').toNumber();
buf[0] = Opcode.OP_0;
buf[1] = Opcode.OP_PUSHDATA4;
buf.writeUInt16LE(3, 2);
buf[buf.length - 1] = Opcode('OP_0').toNumber();
buf[buf.length - 1] = Opcode.OP_0;
var script = Script.fromBuffer(buf);
script.chunks.length.should.equal(3);
script.chunks[0].opcodenum.should.equal(buf[0]);
@ -349,12 +348,11 @@ describe('Script', function() {
describe('#add and #prepend', function() {
it('should add these ops', function() {
Script().add(Opcode('OP_RETURN')).add(new Buffer('')).toString().should.equal('OP_RETURN');
});
it('should add these ops', function() {
Script().add(1).add(10).add(186).toString().should.equal('0x01 0x0a 0xba');
Script().add(1000).toString().should.equal('0x03e8');
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(new Opcode('OP_CHECKMULTISIG')).toString().should.equal('OP_CHECKMULTISIG');
Script().add(Opcode.OP_CHECKMULTISIG).toString().should.equal('OP_CHECKMULTISIG');
Script().add(Opcode.map.OP_CHECKMULTISIG).toString().should.equal('OP_CHECKMULTISIG');
});
@ -390,6 +388,10 @@ describe('Script', function() {
buf.fill(0);
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() {
@ -469,11 +471,17 @@ describe('Script', 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() {
var data = new Buffer('');
var s = Script.buildDataOut(data);
should.exist(s);
s.toString().should.equal('OP_RETURN');
s.toString().should.equal('OP_RETURN 0');
s.isDataOut().should.equal(true);
});
it('should create script from some data', function() {
@ -515,4 +523,47 @@ describe('Script', function() {
});
});
describe('#findAndDelete', function() {
it('should find and delete this buffer', function() {
Script('OP_RETURN 2 0xf0f0')
.findAndDelete(Script('2 0xf0f0'))
.toString()
.should.equal('OP_RETURN');
});
it('should do nothing', function() {
Script('OP_RETURN 2 0xf0f0')
.findAndDelete(Script('2 0xffff'))
.toString()
.should.equal('OP_RETURN 2 0xf0f0');
});
});
describe('#checkMinimalPush', function() {
it('should check these minimal pushes', function() {
Script().add(1).checkMinimalPush(0).should.equal(true);
Script().add(0).checkMinimalPush(0).should.equal(true);
Script().add(-1).checkMinimalPush(0).should.equal(true);
Script().add(1000).checkMinimalPush(0).should.equal(true);
Script().add(0xffffffff).checkMinimalPush(0).should.equal(true);
Script().add(0xffffffffffffffff).checkMinimalPush(0).should.equal(true);
Script().add(new Buffer([0])).checkMinimalPush(0).should.equal(true);
var buf = new Buffer(75);
buf.fill(1);
Script().add(buf).checkMinimalPush(0).should.equal(true);
buf = new Buffer(76);
buf.fill(1);
Script().add(buf).checkMinimalPush(0).should.equal(true);
buf = new Buffer(256);
buf.fill(1);
Script().add(buf).checkMinimalPush(0).should.equal(true);
});
});
});

315
test/script_interpreter.js Normal file
View File

@ -0,0 +1,315 @@
'use strict';
var should = require('chai').should();
var bitcore = require('..');
var ScriptInterpreter = bitcore.ScriptInterpreter;
var Transaction = bitcore.Transaction;
var Script = bitcore.Script;
var BN = bitcore.crypto.BN;
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var Opcode = bitcore.Opcode;
var script_valid = require('./data/bitcoind/script_valid');
var script_invalid = require('./data/bitcoind/script_invalid');
var tx_valid = require('./transaction/tx_valid');
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);
};
describe('ScriptInterpreter', function() {
it('should make a new interp', function() {
var interp = new ScriptInterpreter();
(interp instanceof ScriptInterpreter).should.equal(true);
interp.stack.length.should.equal(0);
interp.altstack.length.should.equal(0);
interp.pc.should.equal(0);
interp.pbegincodehash.should.equal(0);
interp.nOpCount.should.equal(0);
interp.vfExec.length.should.equal(0);
interp.errstr.should.equal('');
interp.flags.should.equal(0);
});
describe('@castToBool', function() {
it('should cast these bufs to bool correctly', function() {
ScriptInterpreter.castToBool(BN(0).toSM({
endian: 'little'
})).should.equal(false);
ScriptInterpreter.castToBool(new Buffer('0080', 'hex')).should.equal(false); //negative 0
ScriptInterpreter.castToBool(BN(1).toSM({
endian: 'little'
})).should.equal(true);
ScriptInterpreter.castToBool(BN(-1).toSM({
endian: 'little'
})).should.equal(true);
var buf = new Buffer('00', 'hex');
var bool = BN().fromSM(buf, {
endian: 'little'
}).cmp(0) !== 0;
ScriptInterpreter.castToBool(buf).should.equal(bool);
});
});
describe('#verify', function() {
it('should verify these trivial scripts', function() {
var verified;
var si = ScriptInterpreter();
verified = si.verify(Script('OP_1'), Script('OP_1'));
verified.should.equal(true);
verified = ScriptInterpreter().verify(Script('OP_1'), Script('OP_0'));
verified.should.equal(false);
verified = ScriptInterpreter().verify(Script('OP_0'), Script('OP_1'));
verified.should.equal(true);
verified = ScriptInterpreter().verify(Script('OP_CODESEPARATOR'), Script('OP_1'));
verified.should.equal(true);
verified = ScriptInterpreter().verify(Script(''), Script('OP_DEPTH OP_0 OP_EQUAL'));
verified.should.equal(true);
verified = ScriptInterpreter().verify(Script('OP_1 OP_2'), Script('OP_2 OP_EQUALVERIFY OP_1 OP_EQUAL'));
verified.should.equal(true);
verified = ScriptInterpreter().verify(Script('9 0x000000000000000010'), Script(''));
verified.should.equal(true);
verified = ScriptInterpreter().verify(Script('OP_1'), Script('OP_15 OP_ADD OP_16 OP_EQUAL'));
verified.should.equal(true);
verified = ScriptInterpreter().verify(Script('OP_0'), Script('OP_IF OP_VER OP_ELSE OP_1 OP_ENDIF'));
verified.should.equal(true);
});
});
var getFlags = function getFlags(flagstr) {
var flags = 0;
if (flagstr.indexOf('NONE') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NONE;
}
if (flagstr.indexOf('P2SH') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_P2SH;
}
if (flagstr.indexOf('STRICTENC') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_STRICTENC;
}
if (flagstr.indexOf('DERSIG') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DERSIG;
}
if (flagstr.indexOf('LOW_S') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_LOW_S;
}
if (flagstr.indexOf('NULLDUMMY') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_NULLDUMMY;
}
if (flagstr.indexOf('SIGPUSHONLY') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_SIGPUSHONLY;
}
if (flagstr.indexOf('MINIMALDATA') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_MINIMALDATA;
}
if (flagstr.indexOf('DISCOURAGE_UPGRADABLE_NOPS') !== -1) {
flags = flags | ScriptInterpreter.SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS;
}
return flags;
};
var testToFromString = function(script) {
var s = script.toString();
Script.fromString(s).toString().should.equal(s);
};
var testFixture = function(vector, expected) {
var scriptSig = Script.fromBitcoindString(vector[0]);
var scriptPubkey = Script.fromBitcoindString(vector[1]);
var flags = getFlags(vector[2]);
//testToFromString(scriptSig);
//testToFromString(scriptPubkey);
var hashbuf = new Buffer(32);
hashbuf.fill(0);
var credtx = Transaction();
credtx.inputs.push(new Transaction.Input({
prevTxId: '0000000000000000000000000000000000000000000000000000000000000000',
outputIndex: 0xffffffff,
sequenceNumber: 0xffffffff,
script: Script('OP_0 OP_0')
}));
credtx._addOutput(new Transaction.Output({
script: scriptPubkey,
satoshis: 0
}));
var idbuf = credtx.id;
var spendtx = Transaction();
spendtx.inputs.push(new Transaction.Input({
prevTxId: idbuf.toString('hex'),
outputIndex: 0,
sequenceNumber: 0xffffffff,
script: scriptSig
}));
spendtx._addOutput(new Transaction.Output({
script: Script(),
satoshis: 0
}));
var interp = ScriptInterpreter();
var verified = interp.verify(scriptSig, scriptPubkey, spendtx, 0, flags);
verified.should.equal(expected);
};
describe('bitcoind fixtures', function() {
var testAllFixtures = function(set, expected) {
var c = 0;
set.forEach(function(vector) {
if (vector.length === 1) {
return;
}
c++;
var descstr = vector[3];
var fullScriptString = vector[0] + ' ' + vector[1];
var comment = descstr ? (' (' + descstr + ')') : '';
it('should pass script_' + (expected ? '' : 'in') + 'valid vector #' + c + ': ' + fullScriptString + comment, function() {
testFixture(vector, expected);
});
});
};
testAllFixtures(script_valid, true);
testAllFixtures(script_invalid, false);
var c = 0;
tx_valid.forEach(function(vector) {
if (vector.length === 1) {
return;
}
c++;
it.skip('should pass tx_valid vector ' + c, function() {
var inputs = vector[0];
var txhex = vector[1];
var flags = getFlags(vector[2]);
var map = {};
inputs.forEach(function(input) {
var txid = input[0];
var txoutnum = input[1];
var scriptPubKeyStr = input[2];
if (txoutnum === -1) {
txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int
}
var txkey = txid + ':' + txoutnum;
map[txkey] = Script.fromBitcoindString(scriptPubKeyStr);
});
var tx = Transaction(txhex);
tx.inputs.forEach(function(txin, j) {
var scriptSig = txin.script;
var txidhex = txin.prevTxId.toString('hex');
var txoutnum = txin.outputIndex;
var txkey = txidhex + ':' + txoutnum;
var scriptPubkey = map[txkey];
should.exist(scriptPubkey);
var interp = ScriptInterpreter();
var verified = interp.verify(scriptSig, scriptPubkey, tx, j, flags);
verified.should.equal(true);
});
});
});
c = 0;
tx_invalid.forEach(function(vector) {
if (vector.length === 1) {
return;
}
c++;
// tests intentionally not performed by the script interpreter:
// TODO: check this?
/*
if (c === 7 || // tests if valuebn is negative
c === 8 || // tests if valuebn is greater than MAX_MONEY
c === 10 || // tests if two inputs are equal
c === 11 || // coinbase
c === 12 || // coinbase
c === 13 // null input
) {
return;
}
*/
it.skip('should pass tx_invalid vector ' + c, function() {
var inputs = vector[0];
var txhex = vector[1];
var flags = getFlags(vector[2]);
var map = {};
inputs.forEach(function(input) {
var txoutnum = input[1];
if (txoutnum === -1) {
txoutnum = 0xffffffff; //bitcoind casts -1 to an unsigned int
}
map[input[0] + ':' + txoutnum] = Script.fromBitcoindString(input[2]);
});
var tx = Transaction().fromBuffer(new Buffer(txhex, 'hex'));
if (tx.txins.length > 0) {
tx.txins.some(function(txin, j) {
var scriptSig = txin.script;
var txidhex = BufferReader(txin.txidbuf).readReverse().toString('hex');
var txoutnum = txin.txoutnum;
var scriptPubkey = map[txidhex + ':' + txoutnum];
should.exist(scriptPubkey);
var interp = ScriptInterpreter();
var verified = interp.verify(scriptSig, scriptPubkey, tx, j, flags);
return verified === false;
}).should.equal(true);
}
});
});
});
});

View File

@ -14,7 +14,7 @@ module.exports = [
}],
'to', ['mrU9pEmAx26HcbKVrABvgL7AwA5fjNFoDc', 1010000],
'sign', ['cSBnVM4xvxarwGQuAfQFwqDg9k5tErHUHzgWsEfD4zdwUasvqRVY'],
'serialize', '01000000015884e5db9de218238671572340b207ee85b628074e7e467096c267266baf77a4000000006b4830450221009972100061da4a17a471ac1906c18bb5445c03da2a0be52c59aca6c58f1e342302205eac5ba43830a397f613f40addea4a2eeaa485a1f9a6efa61344c3560762fe3d01210223078d2942df62c45621d209fab84ea9a7a23346201b7727b9b45a29c4e76f5effffffff0150690f00000000001976a9147821c0a3768aa9d1a37e16cf76002aef5373f1a888ac00000000'
'serialize', '01000000015884e5db9de218238671572340b207ee85b628074e7e467096c267266baf77a4000000006a473044022013fa3089327b50263029265572ae1b022a91d10ac80eb4f32f291c914533670b02200d8a5ed5f62634a7e1a0dc9188a3cc460a986267ae4d58faf50c79105431327501210223078d2942df62c45621d209fab84ea9a7a23346201b7727b9b45a29c4e76f5effffffff0150690f00000000001976a9147821c0a3768aa9d1a37e16cf76002aef5373f1a888ac00000000'
],
[
'from', [{
@ -27,7 +27,7 @@ module.exports = [
'to', ['mn9new5vPYWuVN5m3gUBujfKh1uPQvR9mf', 500000],
'to', ['mw5ctwgEaNRbxkM4JhXH3rp5AyGvTWDZCD', 570000],
'sign', ['cSQUuwwJBAg6tYQhzqqLWW115D1s5KFZDyhCF2ffrnukZxMK6rNZ'],
'serialize', '0100000001863957ca797bf847eae50f6999e4c3616dc64b1e6661b16d9da2b57d184724e4010000006b483045022100855691c90510edf83ab632f0a0b17f5202d2cf7071050dcf0c2778325ed403cd02207270a2f0b30c13dc3c1dee74b5ccabcc2632b402c4f38adabcd07357df1442270121039dd446bbc85db6917f39c0b4c295b0f8cce76d1926fa76d7b84e3f7ff1c5eec5ffffffff0220a10700000000001976a91448c819246ae5645ceecd41fbe1aa6202a0a9b5ca88ac90b20800000000001976a914aab76ba4877d696590d94ea3e02948b55294815188ac00000000'
'serialize', '0100000001863957ca797bf847eae50f6999e4c3616dc64b1e6661b16d9da2b57d184724e4010000006b4830450221009d23f7c1e790ecf839e0e53248dacfa559194735e477aa3ee5897fd74fe3ec0402205eff578518e7c59beeb03ee85e5c4b5bc2730addca2f0321d80aadfbcc1976de0121039dd446bbc85db6917f39c0b4c295b0f8cce76d1926fa76d7b84e3f7ff1c5eec5ffffffff0220a10700000000001976a91448c819246ae5645ceecd41fbe1aa6202a0a9b5ca88ac90b20800000000001976a914aab76ba4877d696590d94ea3e02948b55294815188ac00000000'
],
[
'from', [[{
@ -49,7 +49,7 @@ module.exports = [
}]],
'to', ['mtymcCX5KixPjT1zxtg59qewBGWptj9etH', 1060000],
'sign', [['cPGbA2C54ZZ1sw4dc2ckBE1WqkdrNSbEV8Tkjhi2p1J15oErdgP2', 'cSpyve5bXAuyHrNeV9MjTdFz3HLw739yUjjUAUSMe3ppf2qzj2hw']],
'serialize', '0100000002b3028cf4ae5b4b6d8f79ab6fc0dd251b28ab28287d33861e35c90f6e5684dba9000000006a4730440220635e95e1981bbb360feaf4c232f626a0af8eb5c043a99749a21b0e37fd0048fd02207889f6974f0cad39ce8c2a6dff05c8ca402da9ff6fc41e06c12d86853c91a9d80121030253c73236acf5ea9085d408220141197f6094de07426bd0d32c7a543614fdd7ffffffffb3028cf4ae5b4b6d8f79ab6fc0dd251b28ab28287d33861e35c90f6e5684dba9010000006a4730440220319a0b5ee9c67ccb7de4222234f31059354be4f239c99ca24bff30adfec8e8ec022056e6e99e50f7ceaa062958b8424cde1d504019f95c1dc0a0f0778848d0fb9f4b012102977a001a0a7bbfd1f8a647c7d46e13e8f6920635b328390b43b3303977101149ffffffff01a02c1000000000001976a91493abf1e9e4a20c125b93f93ee39efc16b6e4bc4688ac00000000'
'serialize', '0100000002b3028cf4ae5b4b6d8f79ab6fc0dd251b28ab28287d33861e35c90f6e5684dba9000000006a47304402205d591b93871b63205ea80f6976b4d76ce23ca95c825d0c74b44e9816c9488ae8022012476dd8a2780028ed3e72ac1f2620580e82a25c22d1c31afca6fb14b125a35c0121030253c73236acf5ea9085d408220141197f6094de07426bd0d32c7a543614fdd7ffffffffb3028cf4ae5b4b6d8f79ab6fc0dd251b28ab28287d33861e35c90f6e5684dba9010000006a4730440220320367535c9bc525c581939b36ebe70dd0845851e68fa9e3cf2d90642bf551b3022055d6fcaef32d9b584eea757fa633c31449f43272d131588ddd87a22d16dd7129012102977a001a0a7bbfd1f8a647c7d46e13e8f6920635b328390b43b3303977101149ffffffff01a02c1000000000001976a91493abf1e9e4a20c125b93f93ee39efc16b6e4bc4688ac00000000'
],
[
'from', [{
@ -62,7 +62,7 @@ module.exports = [
}],
'to', ['2NEQb8rtiUgxqQ9eif4XVeMUEW2LSZ64s58', 1050000],
'sign', ['cMh7xdJ5EZVg6kvFsBybwK1EYGJw3G1DHhe5sNPAwbDts94ohKyK'],
'serialize', '01000000019c58233f15d582a2e969d37bd05f2e0cb52994015defc65eaec7de64b9cebec8000000006a473044022050442862e892b1d12bcaa03857746f0ed168122e093d799861f4e081756bb8aa0220081d4eaf9281ae8f954efaeb47500d9a02e5a74b3ada51b6a258ac83c1f4f6420121039dbeac2610d53eb7107b14c0fa9be4006a731fa5bcef392d4e1a25ec0e58f0d3ffffffff01900510000000000017a91490edc43da6b052c4a23fc178979ce358a8caad5e8700000000'
'serialize', '01000000019c58233f15d582a2e969d37bd05f2e0cb52994015defc65eaec7de64b9cebec8000000006a47304402205db94e075d4cf740c69e878fa0079e004bbc323be71b1f8944de702b362ca6880220616e2791168e0a2ffc36dc1847f46b79d7ffb5314ae20ee4066feea2b5a32cdc0121039dbeac2610d53eb7107b14c0fa9be4006a731fa5bcef392d4e1a25ec0e58f0d3ffffffff01900510000000000017a91490edc43da6b052c4a23fc178979ce358a8caad5e8700000000'
],
[
'from', [{
@ -74,6 +74,18 @@ module.exports = [
}, ['03fd45c8cd28c4c6a9a89b4515173edebc66a2418353976eccf01c73a7da9bbb12', '0349e0138b2c2f496121258e0426e1dbd698b2c6038e70fd17e3563aa87b4384f9'], 2],
'to', ['mssMdcEm6PiJEr4XZtjk6kkai84EjBbi91', 1040000],
'sign', [['L3wRFe9XHLnkLquf41F56ac77uRXwJ97HZPQ9tppqyMANBKXpoc5', 'KzkfNSL1gvdyU3CGLaP1Cs3pW167M8r9uE8yMtWQrAzz5vCv59CM']],
'serialize', '010000000140c1ae9d6933e4a08594f814ba73a4e94d19c8a83f45784b1684b3a3f84ee66600000000da004730440220366678972728684a94f35635b855583603b28065d430949c08be89412a4ee45d02201aa62e3129c8819ecf2048230e8c77e244d6a496f296954a5bb4a0d0185f8c0201483045022100d06f348b4ef793f2bf749b288f1df165c0946779391c50ddc050e5b1608b2dda02200fcc8c6874b9a313374020253c5de346fe3517c97b18bfa769cea1089ad97144014752210349e0138b2c2f496121258e0426e1dbd698b2c6038e70fd17e3563aa87b4384f92103fd45c8cd28c4c6a9a89b4515173edebc66a2418353976eccf01c73a7da9bbb1252aeffffffff0180de0f00000000001976a914877d4f3be444448f868b345153bc4fc7a11a7c6388ac00000000'
'serialize', '010000000140c1ae9d6933e4a08594f814ba73a4e94d19c8a83f45784b1684b3a3f84ee66600000000d900473044022057c6961adc330ad231f7e1e58f46987637118f85f5f621425a401c609f36abca022017a21edf778d115bab05c70d3c8c4ac16fff52a2686cbb6b60a08b192a5e4e8a01473044022049c8bc0137c49ff87c1c6ef6ef9a7162a64e4519022bd7d68ae523dd6b14c4b2022012f28917b1602d0311ab6c43fa901bf3e5414524252ac85bc9ef8a52d9094210014752210349e0138b2c2f496121258e0426e1dbd698b2c6038e70fd17e3563aa87b4384f92103fd45c8cd28c4c6a9a89b4515173edebc66a2418353976eccf01c73a7da9bbb1252aeffffffff0180de0f00000000001976a914877d4f3be444448f868b345153bc4fc7a11a7c6388ac00000000'
],
[
'from', [{
"address": "mgJT8iegL4f9NCgQFeFyfvnSw1Yj4M5Woi",
"txid": "f50e13cecda9a438ebd7df213a2899e42b2461a18d4630ee773d26b4f2688bdc",
"vout": 1,
"scriptPubKey": "76a914089acaba6af8b2b4fb4bed3b747ab1e4e60b496588ac",
"amount": 0.01
}],
'to', ['n3riXZowrjGnY74rx7Hdi9wCyvgyJC28zZ', 990000],
'sign', ['cPwWtDztEgRCMCU8pMQp4HgphvyadrAsYBrCjXUZuDSmnZkyoyNF'],
'serialize', '0100000001dc8b68f2b4263d77ee30468da161242be499283a21dfd7eb38a4a9cdce130ef5010000006a4730440220337e09c2729423302abe5e386d5e0f060ae8c006693f87342322bb1fe50065ff0220217a12de44139c57f01d35e988ffe3b0f86005d0cefcecf877b54c67473211d2012103e26b47e7c0d8946954bf9dd4bc7f9e415437eb98271d05f69e78cef8fc6c9a54ffffffff01301b0f00000000001976a914f50f9826ef186074c6fe206cca6b71472ff07ba888ac00000000'
]
];

View File

@ -1,10 +1,8 @@
'use strict';
var buffer = require('buffer');
var bufferUtil = require('../../lib/util/buffer');
var Script = require('../../lib/script');
var Signature = require('../../lib/crypto/signature');
var Transaction = require('../../lib/transaction');
var sighash = require('../../lib/transaction/sighash');
@ -12,12 +10,12 @@ var vectors_sighash = require('./sighash.json');
describe('sighash', function() {
it('test vector from bitcoind', function() {
vectors_sighash.forEach(function(vector, i) {
if (i === 0) {
// First element is just a row describing the next ones
return;
}
vectors_sighash.forEach(function(vector, i) {
if (i === 0) {
// First element is just a row describing the next ones
return;
}
it('test vector from bitcoind #' + i + ' (' + vector[4].substring(0, 16) + ')', function() {
var txbuf = new buffer.Buffer(vector[0], 'hex');
var scriptbuf = new buffer.Buffer(vector[1], 'hex');
var subscript = Script(scriptbuf);

View File

@ -50,7 +50,7 @@ describe('Transaction', function() {
var transaction = new Transaction();
while (i < vector.length) {
var command = vector[i];
var args = vector[i+1];
var args = vector[i + 1];
if (command === 'serialize') {
transaction.serialize().should.equal(args);
} else {