add sighash tests

This commit is contained in:
Manuel Araoz 2014-03-26 11:51:28 -03:00
parent 72570719d8
commit b020fe1e80
2 changed files with 153 additions and 24 deletions

View File

@ -445,15 +445,7 @@ Transaction.prototype.hashForSignature =
}
// Clone transaction
var txTmp = new Transaction();
this.ins.forEach(function(txin, i) {
txTmp.ins.push(new TransactionIn(txin));
});
this.outs.forEach(function(txout) {
txTmp.outs.push(new TransactionOut(txout));
});
txTmp.version = this.version;
txTmp.lock_time = this.lock_time;
var txTmp = new Transaction(this);
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible
@ -505,10 +497,13 @@ Transaction.prototype.hashForSignature =
} else {
var outsLen;
if (hashTypeMode === SIGHASH_SINGLE) {
// TODO: Untested
if (inIndex >= txTmp.outs.length) {
throw new Error("Transaction.hashForSignature(): SIGHASH_SINGLE " +
"no corresponding txout found - out of bounds");
// bug present in bitcoind which must be also present in bitcore
// Transaction.hashForSignature(): SIGHASH_SINGLE
// no corresponding txout found - out of bounds
var ret = new Buffer(1);
ret.writeUInt8(1, 0);
return ret; // return 1 bug
}
outsLen = inIndex + 1;
} else {

View File

@ -9,9 +9,20 @@ var bitcore = bitcore || require('../bitcore');
var Transaction = bitcore.Transaction;
var Script = bitcore.Script;
var Opcode = bitcore.Opcode;
var util = bitcore.util;
var Put = bitcore.Put;
var Put = require('bufferput');
var buffertools = require('buffertools');
var seed = 1;
// seedable pseudo-random function
var random = function() {
var x = Math.sin(seed++) * 10000;
return x - Math.floor(x);
};
var randInt = function(low, high) {
return Math.floor(Math.random() * (high - low + 1) + low);
return Math.floor(random() * (high - low + 1) + low);
};
var randUIntN = function(nBits) {
return randInt(0, Math.pow(2, nBits));
@ -20,7 +31,7 @@ var randUInt32 = function() {
return randUIntN(32);
};
var randBool = function() {
return Math.random() < 0.5;
return random() < 0.5;
};
var hexAlphabet = '0123456789abcdef';
var randHex = function() {
@ -37,7 +48,7 @@ var randTxHash = function() {
return randHexN(64);
};
var randPick = function(list) {
return list[randInt(0, list.length-1)];
return list[randInt(0, list.length - 1)];
};
@ -45,8 +56,8 @@ var opList = Opcode.asList();
var randomScript = function() {
var s = new Script();
var ops = randInt(0,10);
for (var i=0; i<ops; i++) {
var ops = randInt(0, 10);
for (var i = 0; i < ops; i++) {
var op = randPick(opList);
s.writeOp(Opcode.map[op]);
}
@ -70,8 +81,8 @@ var randomTx = function(single) {
tx.ins.push(txin);
}
for (i = 0; i < outsN; i++) {
var txout = Transaction.Out({
value: randInt(0,100000000),
var txout = new Transaction.Out({
value: new Buffer(8),
script: randomScript().serialize()
});
tx.outs.push(txout);
@ -79,13 +90,136 @@ var randomTx = function(single) {
return tx;
};
var signatureHashOld = function(tx, script, inIndex, hashType) {
if (+inIndex !== inIndex ||
inIndex < 0 || inIndex >= tx.ins.length) {
throw new Error('Input index "' + inIndex + '" invalid or out of bounds ' +
'(' + tx.ins.length + ' inputs)');
}
// Clone transaction
var txTmp = new Transaction();
tx.ins.forEach(function(txin) {
txTmp.ins.push(new Transaction.In(txin));
});
tx.outs.forEach(function(txout) {
txTmp.outs.push(new Transaction.Out(txout));
});
txTmp.version = tx.version;
txTmp.lock_time = tx.lock_time;
// In case concatenating two scripts ends up with two codeseparators,
// or an extra one at the end, this prevents all those possible
// incompatibilities.
script.findAndDelete(Opcode.map.OP_CODESEPARATOR);
// Get mode portion of hashtype
var hashTypeMode = hashType & 0x1f;
// Generate modified transaction data for hash
var bytes = (new Put());
bytes.word32le(tx.version);
// Serialize inputs
if (hashType & Transaction.SIGHASH_ANYONECANPAY) {
// Blank out all inputs except current one, not recommended for open
// transactions.
bytes.varint(1);
bytes.put(tx.ins[inIndex].o);
bytes.varint(script.buffer.length);
bytes.put(script.buffer);
bytes.word32le(tx.ins[inIndex].q);
} else {
bytes.varint(tx.ins.length);
for (var i = 0, l = tx.ins.length; i < l; i++) {
var txin = tx.ins[i];
bytes.put(txin.o);
// Current input's script gets set to the script to be signed, all others
// get blanked.
if (inIndex === i) {
bytes.varint(script.buffer.length);
bytes.put(script.buffer);
} else {
bytes.varint(0);
}
if (hashTypeMode === Transaction.SIGHASH_NONE && inIndex !== i) {
bytes.word32le(0);
} else {
bytes.word32le(tx.ins[i].q);
}
}
}
// Serialize outputs
if (hashTypeMode === Transaction.SIGHASH_NONE) {
bytes.varint(0);
} else {
var outsLen;
if (hashTypeMode === Transaction.SIGHASH_SINGLE) {
if (inIndex >= txTmp.outs.length) {
// bug present in bitcoind which must be also present in bitcore
// Transaction.hashForSignature(): SIGHASH_SINGLE
// no corresponding txout found - out of bounds
var ret = new Buffer(1);
ret.writeUInt8(1, 0);
return ret; // return 1 bug
}
outsLen = inIndex + 1;
} else {
outsLen = tx.outs.length;
}
bytes.varint(outsLen);
for (var i = 0; i < outsLen; i++) {
if (hashTypeMode === Transaction.SIGHASH_SINGLE && i !== inIndex) {
// Zero all outs except the one we want to keep
bytes.put(util.INT64_MAX);
bytes.varint(0);
} else {
bytes.put(tx.outs[i].v);
bytes.varint(tx.outs[i].s.length);
bytes.put(tx.outs[i].s);
}
}
}
bytes.word32le(tx.lock_time);
var buffer = bytes.buffer();
// Append hashType
buffer = Buffer.concat([buffer, new Buffer([parseInt(hashType), 0, 0, 0])]);
return util.twoSha256(buffer);
};
describe('Transaction sighash (#hashForSignature)', function() {
for (var i = 0; i < 10; i++) {
it('should hash correctly random tx #'+(i+1), function() {
for (var i = 0; i < 250; i++) {
it('should hash correctly random tx #' + (i + 1), function() {
var tx = randomTx();
console.log(tx);
tx.hashForSignature(script, inIndex, hashType);
return;
var l = tx.ins.length;
for (var i = 0; i < l; i++) {
var script = randomScript();
var hashType = randUInt32();
var h = buffertools.toHex(tx.hashForSignature(script, i, hashType));
var oh = buffertools.toHex(signatureHashOld(tx, script, i, hashType));
h.should.equal(oh);
}
});
}
});