From d18f2dba009d7e2a4ad9ea0f8d1bee00554ef0c5 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Wed, 28 May 2014 19:34:15 +1000 Subject: [PATCH] bufferutils: add PUSHDATA implementation --- src/bufferutils.js | 82 +++++++++++++++++++++++++++++++++++++-- test/bufferutils.js | 70 ++++++++++++++++++++++++++++----- test/fixtures/buffer.json | 27 ++++++++----- 3 files changed, 157 insertions(+), 22 deletions(-) diff --git a/src/bufferutils.js b/src/bufferutils.js index 352a556..ac340e9 100644 --- a/src/bufferutils.js +++ b/src/bufferutils.js @@ -1,4 +1,46 @@ var assert = require('assert') +var opcodes = require('./opcodes') + +function pushDataSize(i) { + return i < opcodes.OP_PUSHDATA1 ? 1 + : i < 0xff ? 2 + : i < 0xffff ? 3 + : 5 +} + +function readPushDataInt(buffer, offset) { + var opcode = buffer.readUInt8(offset) + var number, size + + // ~6 bit + if (opcode < opcodes.OP_PUSHDATA1) { + number = opcode + size = 1 + + // 8 bit + } else if (opcode === opcodes.OP_PUSHDATA1) { + number = buffer.readUInt8(offset + 1) + size = 2 + + // 16 bit + } else if (opcode === opcodes.OP_PUSHDATA2) { + number = buffer.readUInt16LE(offset + 1) + size = 3 + + // 32 bit + } else { + assert.equal(opcode, opcodes.OP_PUSHDATA4, 'Unexpected opcode') + + number = buffer.readUInt32LE(offset + 1) + size = 5 + + } + + return { + number: number, + size: size + } +} function readUInt64LE(buffer, offset) { var a = buffer.readUInt32LE(offset) @@ -16,17 +58,17 @@ function readVarInt(buffer, offset) { var t = buffer.readUInt8(offset) var number, size - // 8-bit + // 8 bit if (t < 253) { number = t size = 1 - // 16-bit + // 16 bit } else if (t < 254) { number = buffer.readUInt16LE(offset + 1) size = 3 - // 32-bit + // 32 bit } else if (t < 255) { number = buffer.readUInt32LE(offset + 1) size = 5 @@ -43,6 +85,37 @@ function readVarInt(buffer, offset) { } } +function writePushDataInt(buffer, number, offset) { + var size = pushDataSize(number) + + // ~6 bit + if (size === 1) { + buffer.writeUInt8(number, offset) + + // 8 bit + } else if (size === 2) { + buffer.writeUInt8(opcodes.OP_PUSHDATA1, offset) + buffer.writeUInt8(number, offset + 1) + + // 16 bit + } else if (size === 3) { + buffer.writeUInt8(opcodes.OP_PUSHDATA2, offset) + buffer.writeUInt16LE(number, offset + 1) + + // 32 bit + } else { + // Javascript Safe Integer limitation + // assert(Number.isSafeInteger(value), 'value must be < 2^53') + assert(number < 0x0020000000000000, 'value must be < 2^53') + + buffer.writeUInt8(opcodes.OP_PUSHDATA4, offset) + buffer.writeUInt32LE(number, offset + 1) + + } + + return size +} + function writeUInt64LE(buffer, value, offset) { // Javascript Safe Integer limitation // assert(Number.isSafeInteger(value), 'value must be < 2^53') @@ -86,9 +159,12 @@ function writeVarInt(buffer, number, offset) { } module.exports = { + pushDataSize: pushDataSize, + readPushDataInt: readPushDataInt, readUInt64LE: readUInt64LE, readVarInt: readVarInt, varIntSize: varIntSize, + writePushDataInt: writePushDataInt, writeUInt64LE: writeUInt64LE, writeVarInt: writeVarInt } diff --git a/test/bufferutils.js b/test/bufferutils.js index 97a4007..a06f4d1 100644 --- a/test/bufferutils.js +++ b/test/bufferutils.js @@ -4,6 +4,32 @@ var bufferutils = require('../src/bufferutils') var fixtures = require('./fixtures/buffer.json') describe('bufferutils', function() { + describe('pushDataSize', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f) { + if (!f.hexPD) return + + var size = bufferutils.pushDataSize(f.dec) + + assert.equal(size, f.hexPD.length / 2) + }) + }) + }) + + describe('readPushDataInt', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f) { + if (!f.hexPD) return + + var buffer = new Buffer(f.hexPD, 'hex') + var d = bufferutils.readPushDataInt(buffer, 0) + + assert.equal(d.number, f.dec) + assert.equal(d.size, buffer.length) + }) + }) + }) + describe('readUInt64LE', function() { it('matches test vectors', function() { fixtures.valid.forEach(function(f) { @@ -30,14 +56,38 @@ describe('bufferutils', function() { describe('varIntSize', function() { it('matches test vectors', function() { fixtures.valid.forEach(function(f) { - var number = parseInt(f.dec) - var size = bufferutils.varIntSize(number) + var size = bufferutils.varIntSize(f.dec) assert.equal(size, f.hexVI.length / 2) }) }) }) + describe('writePushDataInt', function() { + it('matches test vectors', function() { + fixtures.valid.forEach(function(f, i) { + if (!f.hexPD) return + + var buffer = new Buffer(5) + buffer.fill(0) + + var n = bufferutils.writePushDataInt(buffer, f.dec, 0) + assert.equal(buffer.slice(0, n).toString('hex'), f.hexPD) + }) + }) + + fixtures.invalid.forEach(function(f) { + it('throws on ' + f.description, function() { + var buffer = new Buffer(5) + buffer.fill(0) + + assert.throws(function() { + bufferutils.writePushDataInt(buffer, f.dec, 0) + }, /value must be < 2\^53/) + }) + }) + }) + describe('writeUInt64LE', function() { it('matches test vectors', function() { fixtures.valid.forEach(function(f) { @@ -51,12 +101,12 @@ describe('bufferutils', function() { fixtures.invalid.forEach(function(f) { it('throws on ' + f.description, function() { - assert.throws(function() { - var buffer = new Buffer(8) - buffer.fill(0) + var buffer = new Buffer(8) + buffer.fill(0) + assert.throws(function() { bufferutils.writeUInt64LE(buffer, f.dec, 0) - }) + }, /value must be < 2\^53/) }) }) }) @@ -74,12 +124,12 @@ describe('bufferutils', function() { fixtures.invalid.forEach(function(f) { it('throws on ' + f.description, function() { - assert.throws(function() { - var buffer = new Buffer(9) - buffer.fill(0) + var buffer = new Buffer(9) + buffer.fill(0) + assert.throws(function() { bufferutils.writeVarInt(buffer, f.dec, 0) - }) + }, /value must be < 2\^53/) }) }) }) diff --git a/test/fixtures/buffer.json b/test/fixtures/buffer.json index 268a92d..16d5c47 100644 --- a/test/fixtures/buffer.json +++ b/test/fixtures/buffer.json @@ -3,47 +3,56 @@ { "dec": 0, "hex64": "0000000000000000", - "hexVI": "00" + "hexVI": "00", + "hexPD": "00" }, { "dec": 1, "hex64": "0100000000000000", - "hexVI": "01" + "hexVI": "01", + "hexPD": "01" }, { "dec": 252, "hex64": "fc00000000000000", - "hexVI": "fc" + "hexVI": "fc", + "hexPD": "4cfc" }, { "dec": 253, "hex64": "fd00000000000000", - "hexVI": "fdfd00" + "hexVI": "fdfd00", + "hexPD": "4cfd" }, { "dec": 254, "hex64": "fe00000000000000", - "hexVI": "fdfe00" + "hexVI": "fdfe00", + "hexPD": "4cfe" }, { "dec": 65535, "hex64": "ffff000000000000", - "hexVI": "fdffff" + "hexVI": "fdffff", + "hexPD": "4effff0000" }, { "dec": 65536, "hex64": "0000010000000000", - "hexVI": "fe00000100" + "hexVI": "fe00000100", + "hexPD": "4e00000100" }, { "dec": 65537, "hex64": "0100010000000000", - "hexVI": "fe01000100" + "hexVI": "fe01000100", + "hexPD": "4e01000100" }, { "dec": 4294967295, "hex64": "ffffffff00000000", - "hexVI": "feffffffff" + "hexVI": "feffffffff", + "hexPD": "4effffffff" }, { "dec": 4294967296,