From 20599f66c9a89b0c90297515fb1064be89baf90e Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 26 Mar 2014 18:30:17 +1100 Subject: [PATCH 1/3] Adds varIntToNum conversion function --- src/convert.js | 23 +++++++++++++++++++++++ test/convert.js | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/convert.js b/src/convert.js index e54a466..ceb978a 100644 --- a/src/convert.js +++ b/src/convert.js @@ -134,6 +134,28 @@ function numToVarInt(num) { return [255].concat(numToBytes(num, 8)); } +/** + * Turn an VarInt into an integer + * + * "var_int" is a variable length integer used by Bitcoin's binary format. + * + * Returns { bytes: bytesUsed, number: theNumber } + */ +function varIntToNum(bytes) { + var prefix = bytes[0] + + var viBytes = + prefix < 253 ? bytes.slice(0, 1) + : prefix === 253 ? bytes.slice(1, 3) + : prefix === 254 ? bytes.slice(1, 5) + : bytes.slice(1, 9) + + return { + bytes: prefix < 253 ? viBytes : bytes.slice(0, viBytes.length + 1), + number: bytesToNum(viBytes) + } +} + function bytesToWords(bytes) { var words = []; for (var i = 0, b = 0; i < bytes.length; i++, b += 8) { @@ -176,6 +198,7 @@ module.exports = { numToBytes: numToBytes, bytesToNum: bytesToNum, numToVarInt: numToVarInt, + varIntToNum: varIntToNum, bytesToWords: bytesToWords, wordsToBytes: wordsToBytes, bytesToWordArray: bytesToWordArray, diff --git a/test/convert.js b/test/convert.js index 4727692..3fb03b1 100644 --- a/test/convert.js +++ b/test/convert.js @@ -94,6 +94,45 @@ describe('convert', function() { }) }) + describe('varIntToNum', function() { + it('works on valid input', function() { + var data = [ + [0], [128], [252], // 8-bit + [253, 0, 1], [253, 0, 2], [253, 0, 4], // 16-bit + [254, 5, 0, 1, 0], // 32-bit + [255, 3, 0, 0, 0, 1, 0, 0, 0] // 64-bit + ] + var expected = [ + 0, 128, 252, // 8-bit + 256, 512, 1024, // 16-bit + 65541, // 32-bit + 4294967299, // 64-bit + ] + + for (var i = 0; i < data.length; ++i) { + var actual = convert.varIntToNum(data[i]) + assert.equal(actual.number, expected[i]) + assert.deepEqual(actual.bytes, data[i]) + } + }) + + it('uses only what is necessary', function() { + var data = [ + [0, 99], + [253, 0, 1, 99], + [254, 5, 0, 1, 0, 99], + [255, 3, 0, 0, 0, 1, 0, 0, 0, 99] + ] + var expected = [0, 256, 65541, 4294967299] + + for (var i = 0; i < data.length; ++i) { + var actual = convert.varIntToNum(data[i]) + assert.equal(actual.number, expected[i]) + assert.deepEqual(actual.bytes, data[i].slice(0, -1)) + } + }) + }) + describe('reverseEndian', function() { it('works', function() { var bigEndian = "6a4062273ac4f9ea4ffca52d9fd102b08f6c32faa0a4d1318e3a7b2e437bb9c7" From f12f8eac488a1b0a95646088869fc572a1d1ed5e Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 26 Mar 2014 19:11:10 +1100 Subject: [PATCH 2/3] Adds failing test for larger transaction --- test/transaction.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/transaction.js b/test/transaction.js index ab0226f..8a185e6 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -69,6 +69,22 @@ describe('Transaction', function() { var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c" assert.deepEqual(tx.hash, convert.hexToBytes(hashHex)) }) + + it('decodes large inputs correctly', function() { + // transaction has only 1 input + var tx = new Transaction() + tx.addInput("0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57", 0) + tx.addOutput("15mMHKL96tWAUtqF3tbVf99Z8arcmnJrr3", 100) + + // but we're going to replace the tx.ins.length VarInt with a 32-bit equivalent + // however the same resultant number of inputs (1) + var bytes = tx.serialize() + var mutated = bytes.slice(0, 4).concat([254, 1, 0, 0, 0], bytes.slice(5)) + + // the deserialized-serialized transaction should return to its original state (== tx) + var bytes2 = Transaction.deserialize(mutated).serialize() + assert.deepEqual(bytes, bytes2) + }); }) describe('creating a transaction', function() { From 63dd7c7dec8ca1b1fe4a56e2f99f99652909f8d4 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 26 Mar 2014 19:11:37 +1100 Subject: [PATCH 3/3] Now uses varIntToNum --- src/transaction.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index fa9566d..caaf22f 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -254,11 +254,11 @@ Transaction.deserialize = function(buffer) { return buffer[pos-1] + readAsInt(bytes-1) * 256; } var readVarInt = function() { - pos++; - if (buffer[pos-1] < 253) { - return buffer[pos-1]; - } - return readAsInt(buffer[pos-1] - 251); + var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read + var result = convert.varIntToNum(bytes) + + pos += result.bytes.length + return result.number } var readBytes = function(bytes) { pos += bytes;