fix old tests for sighash
This commit is contained in:
parent
1119b6f9e7
commit
db38feacce
120
Transaction.js
120
Transaction.js
|
@ -294,15 +294,6 @@ Transaction.SIGHASH_NONE = SIGHASH_NONE;
|
||||||
Transaction.SIGHASH_SINGLE = SIGHASH_SINGLE;
|
Transaction.SIGHASH_SINGLE = SIGHASH_SINGLE;
|
||||||
Transaction.SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY;
|
Transaction.SIGHASH_ANYONECANPAY = SIGHASH_ANYONECANPAY;
|
||||||
|
|
||||||
var oneBuffer = function() {
|
|
||||||
// bug present in bitcoind which must be also present in bitcore
|
|
||||||
// see https://bitcointalk.org/index.php?topic=260595
|
|
||||||
var ret = new Buffer(1);
|
|
||||||
ret.writeUInt8(1, 0);
|
|
||||||
return ret; // return 1 bug
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var TransactionSignatureSerializer = function(txTo, scriptCode, nIn, nHashType) {
|
var TransactionSignatureSerializer = function(txTo, scriptCode, nIn, nHashType) {
|
||||||
this.txTo = txTo;
|
this.txTo = txTo;
|
||||||
this.scriptCode = scriptCode;
|
this.scriptCode = scriptCode;
|
||||||
|
@ -406,6 +397,16 @@ TransactionSignatureSerializer.prototype.buffer = function() {
|
||||||
return this.bytes.buffer();
|
return this.bytes.buffer();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Transaction.Serializer = TransactionSignatureSerializer;
|
||||||
|
|
||||||
|
var oneBuffer = function() {
|
||||||
|
// bug present in bitcoind which must be also present in bitcore
|
||||||
|
// see https://bitcointalk.org/index.php?topic=260595
|
||||||
|
var ret = new Buffer(1);
|
||||||
|
ret.writeUInt8(1, 0);
|
||||||
|
return ret; // return 1 bug
|
||||||
|
};
|
||||||
|
|
||||||
Transaction.prototype.hashForSignature =
|
Transaction.prototype.hashForSignature =
|
||||||
function hashForSignature(script, inIndex, hashType) {
|
function hashForSignature(script, inIndex, hashType) {
|
||||||
|
|
||||||
|
@ -428,108 +429,7 @@ Transaction.prototype.hashForSignature =
|
||||||
// Append hashType
|
// Append hashType
|
||||||
var hashBuf = new Put().word32le(hashType).buffer();
|
var hashBuf = new Put().word32le(hashType).buffer();
|
||||||
buffer = Buffer.concat([buffer, hashBuf]);
|
buffer = Buffer.concat([buffer, hashBuf]);
|
||||||
var bth = buffertools.toHex(buffer);
|
|
||||||
console.log('tx sig b ' + bth);
|
|
||||||
var expected = 'f40a750701b5788174aef79788716f96af779d7959147a0c2e0e5bfb6c2dba2df5b4b978940300000004005163acffffffff0445e6fd0200000000096aac536365526a526aa6546b000000000008acab656a6552535141a0fd010000000000c897ea030000000008526500ab526a6a631b39dba395e20496';
|
|
||||||
var rawtx = 'f40a750702af06efff3ea68e5d56e42bc41cdb8b6065c98f1221fe04a325a898cb61f3d7ee030000000363acacffffffffb5788174aef79788716f96af779d7959147a0c2e0e5bfb6c2dba2df5b4b97894030000000965510065535163ac6affffffff0445e6fd0200000000096aac536365526a526aa6546b000000000008acab656a6552535141a0fd010000000000c897ea030000000008526500ab526a6a631b39dba3';
|
|
||||||
console.log('expected '+expected);
|
|
||||||
for (var i=0; i<expected.length/2;i++) {
|
|
||||||
var byt = expected.substring(i*2, i*2+2);
|
|
||||||
var rbyt = bth.substring(i*2, i*2+2);
|
|
||||||
var interest = buffertools.toHex(this.serialize()) === rawtx;
|
|
||||||
if (interest) {
|
|
||||||
if (byt !== rbyt) throw new Error(byt +'!='+rbyt+' on pos '+i);
|
|
||||||
//console.log(byt+ ' OK at '+i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffertools.reverse(util.twoSha256(buffer));
|
return buffertools.reverse(util.twoSha256(buffer));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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(OP_CODESEPARATOR);
|
|
||||||
|
|
||||||
// Get mode portion of hashtype
|
|
||||||
var hashTypeMode = hashType & 0x1f;
|
|
||||||
|
|
||||||
// Generate modified transaction data for hash
|
|
||||||
var bytes = (new Put());
|
|
||||||
bytes.word32le(this.version);
|
|
||||||
|
|
||||||
// Serialize inputs
|
|
||||||
if (hashType & SIGHASH_ANYONECANPAY) {
|
|
||||||
// Blank out all inputs except current one
|
|
||||||
bytes.varint(1);
|
|
||||||
bytes.put(this.ins[inIndex].o);
|
|
||||||
bytes.varint(script.buffer.length);
|
|
||||||
bytes.put(script.buffer);
|
|
||||||
bytes.word32le(this.ins[inIndex].q);
|
|
||||||
} else {
|
|
||||||
bytes.varint(this.ins.length);
|
|
||||||
for (var i = 0, l = this.ins.length; i < l; i++) {
|
|
||||||
var txin = this.ins[i];
|
|
||||||
bytes.put(this.ins[i].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 === SIGHASH_NONE && inIndex !== i) {
|
|
||||||
bytes.word32le(0);
|
|
||||||
} else {
|
|
||||||
bytes.word32le(this.ins[i].q);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize outputs
|
|
||||||
if (hashTypeMode === SIGHASH_NONE) {
|
|
||||||
bytes.varint(0);
|
|
||||||
} else {
|
|
||||||
var outsLen;
|
|
||||||
var hashTypeMode = hashType & 0x1f;
|
|
||||||
if (hashTypeMode === SIGHASH_SINGLE) {
|
|
||||||
if (inIndex >= this.outs.length) {
|
|
||||||
return oneBuffer();
|
|
||||||
}
|
|
||||||
outsLen = inIndex + 1;
|
|
||||||
} else {
|
|
||||||
outsLen = this.outs.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: If hashTypeMode !== SIGHASH_SINGLE, we could memcpy this whole
|
|
||||||
// section from the original transaction as is.
|
|
||||||
bytes.varint(outsLen);
|
|
||||||
for (var i = 0; i < outsLen; i++) {
|
|
||||||
if (hashTypeMode === 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(this.outs[i].v);
|
|
||||||
bytes.varint(this.outs[i].s.length);
|
|
||||||
bytes.put(this.outs[i].s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.word32le(this.lock_time);
|
|
||||||
|
|
||||||
var buffer = bytes.buffer();
|
|
||||||
|
|
||||||
// An array of bytes is constructed from the serialized txCopy
|
|
||||||
// appended by four bytes for the hash type.
|
|
||||||
buffer = Buffer.concat([buffer, new Buffer([parseInt(hashType), 0, 0, 0])]);
|
|
||||||
|
|
||||||
return util.twoSha256(buffer);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -95,111 +95,37 @@ var randomTx = function(single) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var oneBuffer = function() {
|
||||||
|
// bug present in bitcoind which must be also present in bitcore
|
||||||
|
// see https://bitcointalk.org/index.php?topic=260595
|
||||||
|
var ret = new Buffer(1);
|
||||||
|
ret.writeUInt8(1, 0);
|
||||||
|
return ret; // return 1 bug
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var signatureHashOld = function(tx, script, inIndex, hashType) {
|
var signatureHashOld = function(tx, script, inIndex, hashType) {
|
||||||
if (+inIndex !== inIndex ||
|
if (+inIndex !== inIndex ||
|
||||||
inIndex < 0 || inIndex >= tx.ins.length) {
|
inIndex < 0 || inIndex >= tx.ins.length) {
|
||||||
throw new Error('Input index "' + inIndex + '" invalid or out of bounds ' +
|
return oneBuffer();
|
||||||
'(' + tx.ins.length + ' inputs)');
|
|
||||||
}
|
}
|
||||||
|
// Check for invalid use of SIGHASH_SINGLE
|
||||||
// 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;
|
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 (hashTypeMode === Transaction.SIGHASH_SINGLE) {
|
||||||
if (inIndex >= txTmp.outs.length) {
|
if (inIndex >= tx.outs.length) {
|
||||||
// bug present in bitcoind which must be also present in bitcore
|
return oneBuffer();
|
||||||
// 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);
|
// Wrapper to serialize only the necessary parts of the transaction being signed
|
||||||
|
var serializer = new Transaction.Serializer(tx, script, inIndex, hashType);
|
||||||
var buffer = bytes.buffer();
|
// Serialize
|
||||||
|
var buffer = serializer.buffer();
|
||||||
// Append hashType
|
// Append hashType
|
||||||
buffer = Buffer.concat([buffer, new Buffer([parseInt(hashType), 0, 0, 0])]);
|
var hashBuf = new Put().word32le(hashType).buffer();
|
||||||
|
buffer = Buffer.concat([buffer, hashBuf]);
|
||||||
return util.twoSha256(buffer);
|
return buffertools.reverse(util.twoSha256(buffer));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -211,7 +137,7 @@ var signatureHashOld = function(tx, script, inIndex, hashType) {
|
||||||
|
|
||||||
describe('Transaction sighash (#hashForSignature)', function() {
|
describe('Transaction sighash (#hashForSignature)', function() {
|
||||||
for (var i = 0; i < 250; i++) {
|
for (var i = 0; i < 250; i++) {
|
||||||
it.skip('should hash correctly random tx #' + (i + 1), function() {
|
it('should hash correctly random tx #' + (i + 1), function() {
|
||||||
var tx = randomTx();
|
var tx = randomTx();
|
||||||
var l = tx.ins.length;
|
var l = tx.ins.length;
|
||||||
for (var i = 0; i < l; i++) {
|
for (var i = 0; i < l; i++) {
|
||||||
|
@ -237,7 +163,7 @@ describe('Transaction sighash (#hashForSignature)', function() {
|
||||||
var ser_tx = buffertools.toHex(tx.serialize());
|
var ser_tx = buffertools.toHex(tx.serialize());
|
||||||
ser_tx.should.equal(buffertools.toHex(raw_tx));
|
ser_tx.should.equal(buffertools.toHex(raw_tx));
|
||||||
var h = buffertools.toHex(tx.hashForSignature(scriptPubKey, input_index, hashType));
|
var h = buffertools.toHex(tx.hashForSignature(scriptPubKey, input_index, hashType));
|
||||||
h.should.equal(sighash); // compare our output with bitcoind's
|
h.should.equal(sighash); // compare our output with bitcoind's output
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue