refactor and fixes for Transaction, ScriptInterpreter, and Key

This commit is contained in:
Manuel Araoz 2014-03-20 19:41:21 -03:00 committed by MattFaus
parent b227341c12
commit ba92a6b1df
5 changed files with 1073 additions and 1128 deletions

4
Key.js
View File

@ -77,6 +77,10 @@ if (process.versions) {
// return it as a buffer to keep c++ compatibility // return it as a buffer to keep c++ compatibility
return new Buffer(signature); return new Buffer(signature);
}; };
kSpec.prototype.verifySignature = function(hash, sig, callback) {
};
kSpec.prototype.verifySignatureSync = function(hash, sig) { kSpec.prototype.verifySignatureSync = function(hash, sig) {
var self = this; var self = this;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -24,17 +24,15 @@ function parse_test_transaction(entry) {
// Ignore comments // Ignore comments
if (entry.length !== 3) return; if (entry.length !== 3) return;
var inputs = []; var inputs = {};
entry[0].forEach(function(vin) { entry[0].forEach(function(vin) {
var hash = vin[0]; var hash = (vin[0]);
var index = vin[1]; var index = vin[1];
var scriptPubKey = Script.fromHumanReadable(vin[2]); var scriptPubKey = Script.fromHumanReadable(vin[2]);
inputs.push({ var mapKey = [hash, index];
'prev_tx_hash': hash, console.log('mapkey=' + mapKey);
'index': index, inputs[mapKey] = scriptPubKey;
'scriptPubKey': scriptPubKey
});
}); });
@ -341,53 +339,39 @@ describe('Transaction', function() {
* Bitcoin core transaction tests * Bitcoin core transaction tests
*/ */
// Verify that known valid transactions are intepretted correctly // Verify that known valid transactions are intepretted correctly
var cb = function(err, results) {
should.not.exist(err);
should.exist(results);
results.should.equal(true);
};
testdata.dataTxValid.forEach(function(datum) { testdata.dataTxValid.forEach(function(datum) {
var testTx = parse_test_transaction(datum); if (datum.length < 3) return;
if (!testTx) return; var raw = datum[1];
var verifyP2SH = datum[2]; var verifyP2SH = datum[2];
var transactionString = buffertools.toHex(
testTx.transaction.serialize());
it('valid tx=' + transactionString, function() { it('valid tx=' + raw, function() {
// Verify that all inputs are valid // Verify that all inputs are valid
testTx.inputs.forEach(function(input) { var testTx = parse_test_transaction(datum);
console.log(raw);
//buffertools.toHex(testTx.transaction.serialize()).should.equal(raw);
var inputs = testTx.transaction.inputs();
for (var i = 0; i < inputs.length; i++) {
console.log(' input number #########' + i);
var input = inputs[i];
buffertools.reverse(input[0]);
input[0] = buffertools.toHex(input[0]);
var mapKey = [input];
var scriptPubKey = testTx.inputs[mapKey];
if (!scriptPubKey) throw new Error('asdasdasdasd');
testTx.transaction.verifyInput( testTx.transaction.verifyInput(
input.index, i,
input.scriptPubKey, scriptPubKey, {
{ verifyP2SH: verifyP2SH, dontVerifyStrictEnc: true}, verifyP2SH: verifyP2SH,
function(err, results) { dontVerifyStrictEnc: true
// Exceptions raised inside this function will be handled },
// ...by this function, so ignore if that is the case cb);
if (err && err.constructor.name === 'AssertionError') return; }
should.not.exist(err);
should.exist(results);
results.should.equal(true);
});
});
}); });
}); });
// Verify that known invalid transactions are interpretted correctly
testdata.dataTxInvalid.forEach(function(datum) {
var testTx = parse_test_transaction(datum);
if (!testTx) return;
var transactionString = buffertools.toHex(
testTx.transaction.serialize());
it('valid tx=' + transactionString, function() {
// Verify that all inputs are invalid
testTx.inputs.forEach(function(input) {
testTx.transaction.verifyInput(input.index, input.scriptPubKey,
function(err, results) {
// Exceptions raised inside this function will be handled
// ...by this function, so ignore if that is the case
if (err && err.constructor.name === 'AssertionError') return;
// There should either be an error, or the results should be false.
(err !== null || (!err && results === false)).should.equal(true);
});
});
});
});
}); });

View File

@ -1,54 +1,51 @@
var crypto = require('crypto'); var crypto = require('crypto');
var bignum = require('bignum'); var bignum = require('bignum');
var Binary = require('binary'); var Binary = require('binary');
var Put = require('bufferput'); var Put = require('bufferput');
var buffertools = require('buffertools'); var buffertools = require('buffertools');
var browser; var browser;
if (!process.versions) { var inBrowser = !process.versions;
// browser version if (inBrowser) {
browser = require('../browser/vendor-bundle.js'); browser = require('../browser/vendor-bundle.js');
} }
var sha256 = exports.sha256 = function (data) { var sha256 = exports.sha256 = function(data) {
return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary'); return new Buffer(crypto.createHash('sha256').update(data).digest('binary'), 'binary');
}; };
var ripe160 = exports.ripe160 = function (data) { var ripe160 = exports.ripe160 = function(data) {
if (!Buffer.isBuffer(data)) { if (!Buffer.isBuffer(data)) {
throw new Error('arg should be a buffer'); throw new Error('arg should be a buffer');
} }
if (inBrowser) {
if (!process.versions) { var w = new browser.crypto31.lib.WordArray.init(browser.Crypto.util.bytesToWords(data), data.length);
var w = new browser.crypto31.lib.WordArray.init(Crypto.util.bytesToWords(data), data.length);
var wordArray = browser.crypto31.RIPEMD160(w); var wordArray = browser.crypto31.RIPEMD160(w);
var words = wordArray.words; var words = wordArray.words;
var answer = []; var answer = [];
for (var b = 0; b < words.length * 32; b += 8) { for (var b = 0; b < words.length * 32; b += 8) {
answer.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF); answer.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
} }
return new Buffer(answer, 'hex'); return new Buffer(answer, 'hex');
} }
return new Buffer(crypto.createHash('rmd160').update(data).digest('binary'), 'binary'); return new Buffer(crypto.createHash('rmd160').update(data).digest('binary'), 'binary');
}; };
var sha1 = exports.sha1 = function (data) { var sha1 = exports.sha1 = function(data) {
return new Buffer(crypto.createHash('sha1').update(data).digest('binary'), 'binary'); return new Buffer(crypto.createHash('sha1').update(data).digest('binary'), 'binary');
}; };
var twoSha256 = exports.twoSha256 = function (data) { var twoSha256 = exports.twoSha256 = function(data) {
return sha256(sha256(data)); return sha256(sha256(data));
}; };
var sha256ripe160 = exports.sha256ripe160 = function (data) { var sha256ripe160 = exports.sha256ripe160 = function(data) {
return ripe160(sha256(data)); return ripe160(sha256(data));
}; };
/** /**
* Format a block hash like the official client does. * Format a block hash like the official client does.
*/ */
var formatHash = exports.formatHash = function (hash) { var formatHash = exports.formatHash = function(hash) {
var hashEnd = new Buffer(10); var hashEnd = new Buffer(10);
hash.copy(hashEnd, 0, 22, 32); hash.copy(hashEnd, 0, 22, 32);
return buffertools.reverse(hashEnd).toString('hex'); return buffertools.reverse(hashEnd).toString('hex');
@ -57,7 +54,7 @@ var formatHash = exports.formatHash = function (hash) {
/** /**
* Display the whole hash, as hex, in correct endian order. * Display the whole hash, as hex, in correct endian order.
*/ */
var formatHashFull = exports.formatHashFull = function (hash) { var formatHashFull = exports.formatHashFull = function(hash) {
var copy = new Buffer(hash.length); var copy = new Buffer(hash.length);
hash.copy(copy); hash.copy(copy);
var hex = buffertools.toHex(buffertools.reverse(copy)); var hex = buffertools.toHex(buffertools.reverse(copy));
@ -69,13 +66,13 @@ var formatHashFull = exports.formatHashFull = function (hash) {
* *
* Formats a block hash by removing leading zeros and truncating to 10 characters. * Formats a block hash by removing leading zeros and truncating to 10 characters.
*/ */
var formatHashAlt = exports.formatHashAlt = function (hash) { var formatHashAlt = exports.formatHashAlt = function(hash) {
var hex = formatHashFull(hash); var hex = formatHashFull(hash);
hex = hex.replace(/^0*/, ''); hex = hex.replace(/^0*/, '');
return hex.substr(0, 10); return hex.substr(0, 10);
}; };
var formatBuffer = exports.formatBuffer = function (buffer, maxLen) { var formatBuffer = exports.formatBuffer = function(buffer, maxLen) {
// Calculate amount of bytes to display // Calculate amount of bytes to display
if (maxLen === null) { if (maxLen === null) {
maxLen = 10; maxLen = 10;
@ -96,41 +93,47 @@ var formatBuffer = exports.formatBuffer = function (buffer, maxLen) {
return output; return output;
}; };
var valueToBigInt = exports.valueToBigInt = function (valueBuffer) { var valueToBigInt = exports.valueToBigInt = function(valueBuffer) {
if (Buffer.isBuffer(valueBuffer)) { if (Buffer.isBuffer(valueBuffer)) {
return bignum.fromBuffer(valueBuffer, {endian: 'little', size: 8}); return bignum.fromBuffer(valueBuffer, {
endian: 'little',
size: 8
});
} else { } else {
return valueBuffer; return valueBuffer;
} }
}; };
var bigIntToValue = exports.bigIntToValue = function (valueBigInt) { var bigIntToValue = exports.bigIntToValue = function(valueBigInt) {
if (Buffer.isBuffer(valueBigInt)) { if (Buffer.isBuffer(valueBigInt)) {
return valueBigInt; return valueBigInt;
} else { } else {
return valueBigInt.toBuffer({endian: 'little', size: 8}); return valueBigInt.toBuffer({
endian: 'little',
size: 8
});
} }
}; };
var fitsInNBits = function(integer, n) { var fitsInNBits = function(integer, n) {
// TODO: make this efficient!!! // TODO: make this efficient!!!
return integer.toString(2).replace('-','').length < n; return integer.toString(2).replace('-', '').length < n;
}; };
exports.bytesNeededToStore = bytesNeededToStore = function(integer) { exports.bytesNeededToStore = bytesNeededToStore = function(integer) {
if (integer === 0) return 0; if (integer === 0) return 0;
return Math.ceil(((integer).toString(2).replace('-','').length + 1)/ 8); return Math.ceil(((integer).toString(2).replace('-', '').length + 1) / 8);
}; };
exports.negativeBuffer = negativeBuffer = function(b) { exports.negativeBuffer = negativeBuffer = function(b) {
// implement two-complement negative // implement two-complement negative
var c = new Buffer(b.length); var c = new Buffer(b.length);
// negate each byte // negate each byte
for (var i=0; i<b.length; i++){ for (var i = 0; i < b.length; i++) {
c[i] = ~b[i]; c[i] = ~b[i];
if (c[i] < 0) c[i] += 256; if (c[i] < 0) c[i] += 256;
} }
// add one // add one
for (var i=b.length - 1; i>=0; i--){ for (var i = b.length - 1; i >= 0; i--) {
c[i] += 1; c[i] += 1;
if (c[i] >= 256) c[i] -= 256; if (c[i] >= 256) c[i] -= 256;
if (c[i] !== 0) break; if (c[i] !== 0) break;
@ -141,7 +144,7 @@ exports.negativeBuffer = negativeBuffer = function(b) {
/* /*
* Transforms an integer into a buffer using two-complement encoding * Transforms an integer into a buffer using two-complement encoding
* For example, 1 is encoded as 01 and -1 is encoded as ff * For example, 1 is encoded as 01 and -1 is encoded as ff
* For more info see: * For more info see:
* http://en.wikipedia.org/wiki/Signed_number_representations#Two.27s_complement * http://en.wikipedia.org/wiki/Signed_number_representations#Two.27s_complement
*/ */
exports.intToBuffer2C = function(integer) { exports.intToBuffer2C = function(integer) {
@ -149,9 +152,9 @@ exports.intToBuffer2C = function(integer) {
var buf = new Put(); var buf = new Put();
var s = integer.toString(16); var s = integer.toString(16);
var neg = s[0] === '-'; var neg = s[0] === '-';
s = s.replace('-',''); s = s.replace('-', '');
for (var i=0; i<size; i++) { for (var i = 0; i < size; i++) {
var si = s.substring(s.length - 2*(i+1), s.length - 2*(i)); var si = s.substring(s.length - 2 * (i + 1), s.length - 2 * (i));
if (si.lenght === 1) { if (si.lenght === 1) {
si = '0' + si; si = '0' + si;
} }
@ -184,7 +187,7 @@ var padSign = function(b) {
/* /*
* Transforms an integer into a buffer using sign+magnitude encoding * Transforms an integer into a buffer using sign+magnitude encoding
* For example, 1 is encoded as 01 and -1 is encoded as 81 * For example, 1 is encoded as 01 and -1 is encoded as 81
* For more info see: * For more info see:
* http://en.wikipedia.org/wiki/Signed_number_representations#Signed_magnitude_representation * http://en.wikipedia.org/wiki/Signed_number_representations#Signed_magnitude_representation
*/ */
exports.intToBufferSM = function(v) { exports.intToBufferSM = function(v) {
@ -234,50 +237,45 @@ exports.bufferSMToInt = function(v) {
var formatValue = exports.formatValue = function (valueBuffer) { var formatValue = exports.formatValue = function(valueBuffer) {
var value = valueToBigInt(valueBuffer).toString(); var value = valueToBigInt(valueBuffer).toString();
var integerPart = value.length > 8 ? value.substr(0, value.length-8) : '0'; var integerPart = value.length > 8 ? value.substr(0, value.length - 8) : '0';
var decimalPart = value.length > 8 ? value.substr(value.length-8) : value; var decimalPart = value.length > 8 ? value.substr(value.length - 8) : value;
while (decimalPart.length < 8) { while (decimalPart.length < 8) {
decimalPart = "0"+decimalPart; decimalPart = "0" + decimalPart;
} }
decimalPart = decimalPart.replace(/0*$/, ''); decimalPart = decimalPart.replace(/0*$/, '');
while (decimalPart.length < 2) { while (decimalPart.length < 2) {
decimalPart += "0"; decimalPart += "0";
} }
return integerPart+"."+decimalPart; return integerPart + "." + decimalPart;
}; };
var reFullVal = /^\s*(\d+)\.(\d+)/; var reFullVal = /^\s*(\d+)\.(\d+)/;
var reFracVal = /^\s*\.(\d+)/; var reFracVal = /^\s*\.(\d+)/;
var reWholeVal = /^\s*(\d+)/; var reWholeVal = /^\s*(\d+)/;
function padFrac(frac) function padFrac(frac) {
{ frac = frac.substr(0, 8); //truncate to 8 decimal places
frac=frac.substr(0,8); //truncate to 8 decimal places
while (frac.length < 8) while (frac.length < 8)
frac = frac + '0'; frac = frac + '0';
return frac; return frac;
} }
function parseFullValue(res) function parseFullValue(res) {
{
return bignum(res[1]).mul('100000000').add(padFrac(res[2])); return bignum(res[1]).mul('100000000').add(padFrac(res[2]));
} }
function parseFracValue(res) function parseFracValue(res) {
{
return bignum(padFrac(res[1])); return bignum(padFrac(res[1]));
} }
function parseWholeValue(res) function parseWholeValue(res) {
{
return bignum(res[1]).mul('100000000'); return bignum(res[1]).mul('100000000');
} }
exports.parseValue = function parseValue(valueStr) exports.parseValue = function parseValue(valueStr) {
{ if (typeof valueStr !== 'string')
if (typeof valueStr !== 'string')
valueStr = valueStr.toString(); valueStr = valueStr.toString();
var res = valueStr.match(reFullVal); var res = valueStr.match(reFullVal);
@ -296,11 +294,11 @@ exports.parseValue = function parseValue(valueStr)
}; };
// Utility that synchronizes function calls based on a key // Utility that synchronizes function calls based on a key
var createSynchrotron = exports.createSynchrotron = function (fn) { var createSynchrotron = exports.createSynchrotron = function(fn) {
var table = {}; var table = {};
return function (key) { return function(key) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
var run = function () { var run = function() {
// Function fn() will call when it finishes // Function fn() will call when it finishes
args[0] = function next() { args[0] = function next() {
if (table[key]) { if (table[key]) {
@ -333,21 +331,23 @@ var createSynchrotron = exports.createSynchrotron = function (fn) {
* *
* @returns Buffer random nonce * @returns Buffer random nonce
*/ */
var generateNonce = exports.generateNonce = function () { var generateNonce = exports.generateNonce = function() {
var b32 = 0x100000000, ff = 0xff; var b32 = 0x100000000,
var b = new Buffer(8), i = 0; ff = 0xff;
var b = new Buffer(8),
i = 0;
// Generate eight random bytes // Generate eight random bytes
r = Math.random()*b32; r = Math.random() * b32;
b[i++] = r & ff; b[i++] = r & ff;
b[i++] = (r=r>>>8) & ff; b[i++] = (r = r >>> 8) & ff;
b[i++] = (r=r>>>8) & ff; b[i++] = (r = r >>> 8) & ff;
b[i++] = (r=r>>>8) & ff; b[i++] = (r = r >>> 8) & ff;
r = Math.random()*b32; r = Math.random() * b32;
b[i++] = r & ff; b[i++] = r & ff;
b[i++] = (r=r>>>8) & ff; b[i++] = (r = r >>> 8) & ff;
b[i++] = (r=r>>>8) & ff; b[i++] = (r = r >>> 8) & ff;
b[i++] = (r=r>>>8) & ff; b[i++] = (r = r >>> 8) & ff;
return b; return b;
}; };
@ -357,10 +357,10 @@ var generateNonce = exports.generateNonce = function () {
* *
* This function calculates the difficulty target given the difficulty bits. * This function calculates the difficulty target given the difficulty bits.
*/ */
var decodeDiffBits = exports.decodeDiffBits = function (diffBits, asBigInt) { var decodeDiffBits = exports.decodeDiffBits = function(diffBits, asBigInt) {
diffBits = +diffBits; diffBits = +diffBits;
var target = bignum(diffBits & 0xffffff); var target = bignum(diffBits & 0xffffff);
target = target.shiftLeft(8*((diffBits >>> 24) - 3)); target = target.shiftLeft(8 * ((diffBits >>> 24) - 3));
if (asBigInt) { if (asBigInt) {
return target; return target;
@ -370,7 +370,7 @@ var decodeDiffBits = exports.decodeDiffBits = function (diffBits, asBigInt) {
var diffBuf = target.toBuffer(); var diffBuf = target.toBuffer();
var targetBuf = new Buffer(32); var targetBuf = new Buffer(32);
buffertools.fill(targetBuf, 0); buffertools.fill(targetBuf, 0);
diffBuf.copy(targetBuf, 32-diffBuf.length); diffBuf.copy(targetBuf, 32 - diffBuf.length);
return targetBuf; return targetBuf;
}; };
@ -393,8 +393,8 @@ var encodeDiffBits = exports.encodeDiffBits = function encodeDiffBits(target) {
var compact = size << 24; var compact = size << 24;
if (size >= 1) compact |= mpiBuf[4] << 16; if (size >= 1) compact |= mpiBuf[4] << 16;
if (size >= 2) compact |= mpiBuf[5] << 8; if (size >= 2) compact |= mpiBuf[5] << 8;
if (size >= 3) compact |= mpiBuf[6] ; if (size >= 3) compact |= mpiBuf[6];
return compact; return compact;
}; };
@ -405,16 +405,20 @@ var encodeDiffBits = exports.encodeDiffBits = function encodeDiffBits(target) {
* This function calculates the maximum difficulty target divided by the given * This function calculates the maximum difficulty target divided by the given
* difficulty target. * difficulty target.
*/ */
var calcDifficulty = exports.calcDifficulty = function (target) { var calcDifficulty = exports.calcDifficulty = function(target) {
if (!Buffer.isBuffer(target)) { if (!Buffer.isBuffer(target)) {
target = decodeDiffBits(target); target = decodeDiffBits(target);
} }
var targetBigint = bignum.fromBuffer(target, {order: 'forward'}); var targetBigint = bignum.fromBuffer(target, {
var maxBigint = bignum.fromBuffer(MAX_TARGET, {order: 'forward'}); order: 'forward'
});
var maxBigint = bignum.fromBuffer(MAX_TARGET, {
order: 'forward'
});
return maxBigint.div(targetBigint).toNumber(); return maxBigint.div(targetBigint).toNumber();
}; };
var reverseBytes32 = exports.reverseBytes32 = function (data) { var reverseBytes32 = exports.reverseBytes32 = function(data) {
if (data.length % 4) { if (data.length % 4) {
throw new Error("Util.reverseBytes32(): Data length must be multiple of 4"); throw new Error("Util.reverseBytes32(): Data length must be multiple of 4");
} }