Merge pull request #170 from dcousens/bufferx
BufferExt and Transaction serialization to use Buffers
This commit is contained in:
commit
5deab5188f
13 changed files with 438 additions and 227 deletions
94
src/buffer.js
Normal file
94
src/buffer.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
var assert = require('assert')
|
||||
|
||||
function readUInt64LE(buffer, offset) {
|
||||
var a = buffer.readUInt32LE(offset)
|
||||
var b = buffer.readUInt32LE(offset + 4)
|
||||
b *= 0x100000000
|
||||
|
||||
// Javascript Safe Integer limitation
|
||||
// assert(Number.isSafeInteger(value), 'value must be < 2^53')
|
||||
assert(b + a < 0x0020000000000000, 'value must be < 2^53')
|
||||
|
||||
return b + a
|
||||
}
|
||||
|
||||
function readVarInt(buffer, offset) {
|
||||
var t = buffer.readUInt8(offset)
|
||||
var number, size
|
||||
|
||||
// 8-bit
|
||||
if (t < 253) {
|
||||
number = t
|
||||
size = 1
|
||||
|
||||
// 16-bit
|
||||
} else if (t < 254) {
|
||||
number = buffer.readUInt16LE(offset + 1)
|
||||
size = 3
|
||||
|
||||
// 32-bit
|
||||
} else if (t < 255) {
|
||||
number = buffer.readUInt32LE(offset + 1)
|
||||
size = 5
|
||||
|
||||
// 64 bit
|
||||
} else {
|
||||
number = readUInt64LE(buffer, offset + 1)
|
||||
size = 9
|
||||
}
|
||||
|
||||
return {
|
||||
number: number,
|
||||
size: size
|
||||
}
|
||||
}
|
||||
|
||||
function writeUInt64LE(buffer, value, offset) {
|
||||
// Javascript Safe Integer limitation
|
||||
// assert(Number.isSafeInteger(value), 'value must be < 2^53')
|
||||
assert(value < 0x0020000000000000, 'value must be < 2^53')
|
||||
|
||||
buffer.writeInt32LE(value & -1, offset)
|
||||
buffer.writeUInt32LE(Math.floor(value / 0x100000000), offset + 4)
|
||||
}
|
||||
|
||||
function varIntSize(i) {
|
||||
return i < 253 ? 1
|
||||
: i < 0x10000 ? 3
|
||||
: i < 0x100000000 ? 5
|
||||
: 9
|
||||
}
|
||||
|
||||
function writeVarInt(buffer, number, offset) {
|
||||
var size = varIntSize(number)
|
||||
|
||||
// 8 bit
|
||||
if (size === 1) {
|
||||
buffer.writeUInt8(number, offset)
|
||||
|
||||
// 16 bit
|
||||
} else if (size === 3) {
|
||||
buffer.writeUInt8(253, offset)
|
||||
buffer.writeUInt16LE(number, offset + 1)
|
||||
|
||||
// 32 bit
|
||||
} else if (size === 5) {
|
||||
buffer.writeUInt8(254, offset)
|
||||
buffer.writeUInt32LE(number, offset + 1)
|
||||
|
||||
// 64 bit
|
||||
} else {
|
||||
buffer.writeUInt8(255, offset)
|
||||
writeUInt64LE(buffer, number, offset + 1)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readUInt64LE: readUInt64LE,
|
||||
readVarInt: readVarInt,
|
||||
varIntSize: varIntSize,
|
||||
writeUInt64LE: writeUInt64LE,
|
||||
writeVarInt: writeVarInt
|
||||
}
|
|
@ -24,15 +24,6 @@ function hexToBytes(hex) {
|
|||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a byte array representing a number with the given length
|
||||
*/
|
||||
function numToBytes(num, bytes) {
|
||||
if (bytes === undefined) bytes = 8
|
||||
if (bytes === 0) return []
|
||||
return [num % 256].concat(numToBytes(Math.floor(num / 256), bytes - 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a byte array to the number that it represents
|
||||
*/
|
||||
|
@ -41,42 +32,6 @@ function bytesToNum(bytes) {
|
|||
return bytes[0] + 256 * bytesToNum(bytes.slice(1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn an integer into a "var_int".
|
||||
*
|
||||
* "var_int" is a variable length integer used by Bitcoin's binary format.
|
||||
*
|
||||
* Returns a byte array.
|
||||
*/
|
||||
function numToVarInt(num) {
|
||||
if (num < 253) return [num]
|
||||
if (num < 65536) return [253].concat(numToBytes(num, 2))
|
||||
if (num < 4294967296) return [254].concat(numToBytes(num, 4))
|
||||
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) {
|
||||
assert(Array.isArray(bytes) || Buffer.isBuffer(bytes), 'Input must be a byte array')
|
||||
var words = []
|
||||
|
@ -110,10 +65,7 @@ module.exports = {
|
|||
lpad: lpad,
|
||||
bytesToHex: bytesToHex,
|
||||
hexToBytes: hexToBytes,
|
||||
numToBytes: numToBytes,
|
||||
bytesToNum: bytesToNum,
|
||||
numToVarInt: numToVarInt,
|
||||
varIntToNum: varIntToNum,
|
||||
bytesToWords: bytesToWords,
|
||||
wordsToBytes: wordsToBytes,
|
||||
bytesToWordArray: bytesToWordArray,
|
||||
|
|
|
@ -179,10 +179,11 @@ HDWallet.prototype.toBase58 = function(priv) {
|
|||
}
|
||||
|
||||
HDWallet.prototype.derive = function(i) {
|
||||
var iBytes = convert.numToBytes(i, 4).reverse()
|
||||
, cPar = this.chaincode
|
||||
, usePriv = i >= HDWallet.HIGHEST_BIT
|
||||
, SHA512 = CJS.algo.SHA512
|
||||
var iBuffer = new Buffer(4)
|
||||
iBuffer.writeUInt32BE(i, 0)
|
||||
|
||||
var cPar = this.chaincode
|
||||
var usePriv = i >= HDWallet.HIGHEST_BIT
|
||||
|
||||
var I
|
||||
if (usePriv) {
|
||||
|
@ -191,21 +192,20 @@ HDWallet.prototype.derive = function(i) {
|
|||
// If 1, private derivation is used:
|
||||
// let I = HMAC-SHA512(Key = cpar, Data = 0x00 || kpar || i) [Note:]
|
||||
var kPar = this.priv.toBuffer().slice(0, 32)
|
||||
kPar = Array.prototype.slice.call(kPar)
|
||||
iBuffer = Buffer.concat([new Buffer([0]), kPar, iBuffer], 37)
|
||||
|
||||
// FIXME: Dislikes buffers
|
||||
I = HmacFromBytesToBytes(SHA512, [0].concat(kPar, iBytes), cPar)
|
||||
I = HmacFromBytesToBytes(CJS.algo.SHA512, Array.prototype.slice.call(iBuffer), cPar)
|
||||
} else {
|
||||
// If 0, public derivation is used:
|
||||
// let I = HMAC-SHA512(Key = cpar, Data = χ(kpar*G) || i)
|
||||
var KPar = this.pub.toBuffer()
|
||||
KPar = Array.prototype.slice.call(KPar)
|
||||
iBuffer = Buffer.concat([KPar, iBuffer])
|
||||
|
||||
// FIXME: Dislikes buffers
|
||||
I = HmacFromBytesToBytes(SHA512, KPar.concat(iBytes), cPar)
|
||||
I = HmacFromBytesToBytes(CJS.algo.SHA512, Array.prototype.slice.call(iBuffer), cPar)
|
||||
}
|
||||
|
||||
|
||||
// FIXME: Boo, CSJ.algo.SHA512 uses byte arrays
|
||||
I = new Buffer(I)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ module.exports = {
|
|||
Address: require('./address'),
|
||||
base58: require('./base58'),
|
||||
base58check: require('./base58check'),
|
||||
BufferExt: require('./buffer'),
|
||||
convert: require('./convert'),
|
||||
crypto: require('./crypto'),
|
||||
ec: ec,
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
/// Implements Bitcoin's feature for signing arbitrary messages.
|
||||
|
||||
var Address = require('./address')
|
||||
var convert = require('./convert')
|
||||
var BufferExt = require('./buffer')
|
||||
var crypto = require('./crypto')
|
||||
var ecdsa = require('./ecdsa')
|
||||
var ECPubKey = require('./eckey').ECPubKey
|
||||
|
||||
// FIXME: magicHash is incompatible with other magic messages
|
||||
var magicBytes = new Buffer('Bitcoin Signed Message:\n')
|
||||
// FIXME: incompatible with other networks (Litecoin etc)
|
||||
var MAGIC_PREFIX = new Buffer('\x18Bitcoin Signed Message:\n')
|
||||
|
||||
function magicHash(message) {
|
||||
var messageBytes = new Buffer(message)
|
||||
var messageBuffer = new Buffer(message)
|
||||
var lengthBuffer = new Buffer(BufferExt.varIntSize(messageBuffer.length))
|
||||
BufferExt.writeVarInt(lengthBuffer, messageBuffer.length, 0)
|
||||
|
||||
var buffer = Buffer.concat([
|
||||
new Buffer(convert.numToVarInt(magicBytes.length)),
|
||||
magicBytes,
|
||||
new Buffer(convert.numToVarInt(messageBytes.length)),
|
||||
messageBytes
|
||||
MAGIC_PREFIX, lengthBuffer, messageBuffer
|
||||
])
|
||||
|
||||
return crypto.hash256(buffer)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,14 @@ function Script(data) {
|
|||
this.parse()
|
||||
}
|
||||
|
||||
Script.fromHex = function(data) {
|
||||
return new Script(convert.hexToBytes(data))
|
||||
Script.fromBuffer = function(buffer) {
|
||||
assert(Buffer.isBuffer(buffer)) // FIXME: transitionary
|
||||
|
||||
return new Script(Array.prototype.slice.call(buffer))
|
||||
}
|
||||
|
||||
Script.fromHex = function(hex) {
|
||||
return Script.fromBuffer(new Buffer(hex, 'hex'))
|
||||
}
|
||||
|
||||
Script.fromPubKey = function(str) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
var assert = require('assert')
|
||||
var Address = require('./address')
|
||||
var BigInteger = require('bigi')
|
||||
var BufferExt = require('./buffer')
|
||||
var Script = require('./script')
|
||||
var convert = require('./convert')
|
||||
var crypto = require('./crypto')
|
||||
|
@ -16,7 +17,7 @@ var Transaction = function (doc) {
|
|||
this.locktime = 0
|
||||
this.ins = []
|
||||
this.outs = []
|
||||
this.defaultSequence = [255, 255, 255, 255] // 0xFFFFFFFF
|
||||
this.defaultSequence = 0xffffffff
|
||||
|
||||
if (doc) {
|
||||
if (typeof doc == "string" || Array.isArray(doc)) {
|
||||
|
@ -118,45 +119,76 @@ Transaction.prototype.addOutput = function (address, value, network) {
|
|||
/**
|
||||
* Serialize this transaction.
|
||||
*
|
||||
* Returns the transaction as a byte array in the standard Bitcoin binary
|
||||
* format. This method is byte-perfect, i.e. the resulting byte array can
|
||||
* be hashed to get the transaction's standard Bitcoin hash.
|
||||
* Returns the transaction as a binary buffer in
|
||||
* accordance with the Bitcoin protocol.
|
||||
*/
|
||||
Transaction.prototype.serialize = function () {
|
||||
var buffer = []
|
||||
buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4))
|
||||
buffer = buffer.concat(convert.numToVarInt(this.ins.length))
|
||||
var txInSize = this.ins.reduce(function(a, x) {
|
||||
return a + (40 + BufferExt.varIntSize(x.script.buffer.length) + x.script.buffer.length)
|
||||
}, 0)
|
||||
|
||||
this.ins.forEach(function(txin) {
|
||||
// Why do blockchain.info, blockexplorer.com, sx and just about everybody
|
||||
// else use little-endian hashes? No idea...
|
||||
buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse())
|
||||
var txOutSize = this.outs.reduce(function(a, x) {
|
||||
return a + (8 + BufferExt.varIntSize(x.script.buffer.length) + x.script.buffer.length)
|
||||
}, 0)
|
||||
|
||||
buffer = buffer.concat(convert.numToBytes(parseInt(txin.outpoint.index), 4))
|
||||
var buffer = new Buffer(
|
||||
8 +
|
||||
BufferExt.varIntSize(this.ins.length) +
|
||||
BufferExt.varIntSize(this.outs.length) +
|
||||
txInSize +
|
||||
txOutSize
|
||||
)
|
||||
|
||||
var scriptBytes = txin.script.buffer
|
||||
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
|
||||
buffer = buffer.concat(scriptBytes)
|
||||
buffer = buffer.concat(txin.sequence)
|
||||
var offset = 0
|
||||
function writeSlice(slice) {
|
||||
if (Array.isArray(slice)) slice = new Buffer(slice) // FIXME: Performance: transitionary only
|
||||
slice.copy(buffer, offset)
|
||||
offset += slice.length
|
||||
}
|
||||
function writeUInt32(i) {
|
||||
buffer.writeUInt32LE(i, offset)
|
||||
offset += 4
|
||||
}
|
||||
function writeUInt64(i) {
|
||||
BufferExt.writeUInt64LE(buffer, i, offset)
|
||||
offset += 8
|
||||
}
|
||||
function writeVarInt(i) {
|
||||
var n = BufferExt.writeVarInt(buffer, i, offset)
|
||||
offset += n
|
||||
}
|
||||
|
||||
writeUInt32(this.version)
|
||||
writeVarInt(this.ins.length)
|
||||
|
||||
this.ins.forEach(function(txin, i) {
|
||||
var hash = new Buffer(txin.outpoint.hash, 'hex') // FIXME: Performance: convert on tx.addInput instead
|
||||
|
||||
// TxHash hex is big-endian, we need little-endian
|
||||
Array.prototype.reverse.call(hash)
|
||||
|
||||
writeSlice(hash)
|
||||
writeUInt32(txin.outpoint.index)
|
||||
writeVarInt(txin.script.buffer.length)
|
||||
writeSlice(txin.script.buffer)
|
||||
writeUInt32(txin.sequence)
|
||||
})
|
||||
|
||||
buffer = buffer.concat(convert.numToVarInt(this.outs.length))
|
||||
|
||||
writeVarInt(this.outs.length)
|
||||
this.outs.forEach(function(txout) {
|
||||
buffer = buffer.concat(convert.numToBytes(txout.value,8))
|
||||
|
||||
var scriptBytes = txout.script.buffer
|
||||
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
|
||||
buffer = buffer.concat(scriptBytes)
|
||||
writeUInt64(txout.value)
|
||||
writeVarInt(txout.script.buffer.length)
|
||||
writeSlice(txout.script.buffer)
|
||||
})
|
||||
|
||||
buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime), 4))
|
||||
writeUInt32(this.locktime)
|
||||
assert.equal(offset, buffer.length, 'Invalid transaction object')
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
Transaction.prototype.serializeHex = function() {
|
||||
return convert.bytesToHex(this.serialize())
|
||||
return this.serialize().toString('hex')
|
||||
}
|
||||
|
||||
//var OP_CODESEPARATOR = 171
|
||||
|
@ -213,23 +245,20 @@ Transaction.prototype.hashTransactionForSignature =
|
|||
txTmp.ins = [txTmp.ins[inIndex]]
|
||||
}
|
||||
|
||||
var buffer = txTmp.serialize()
|
||||
buffer = buffer.concat(convert.numToBytes(parseInt(hashType), 4))
|
||||
var htB = new Buffer(4)
|
||||
htB.writeUInt32LE(hashType, 0)
|
||||
|
||||
var buffer = Buffer.concat([txTmp.serialize(), htB])
|
||||
return crypto.hash256(buffer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate and return the transaction's hash.
|
||||
* Reverses hash since blockchain.info, blockexplorer.com and others
|
||||
* use little-endian hashes for some stupid reason
|
||||
*/
|
||||
Transaction.prototype.getHash = function ()
|
||||
{
|
||||
var buffer = this.serialize()
|
||||
var hash = crypto.hash256(buffer)
|
||||
Transaction.prototype.getHash = function () {
|
||||
var buffer = crypto.hash256(this.serialize())
|
||||
|
||||
return Array.prototype.slice.call(hash).reverse()
|
||||
// Big-endian is used for TxHash
|
||||
Array.prototype.reverse.call(buffer)
|
||||
|
||||
return buffer.toString('hex')
|
||||
}
|
||||
|
||||
Transaction.prototype.clone = function ()
|
||||
|
@ -250,60 +279,82 @@ Transaction.prototype.clone = function ()
|
|||
}
|
||||
|
||||
Transaction.deserialize = function(buffer) {
|
||||
if (typeof buffer == "string") {
|
||||
buffer = convert.hexToBytes(buffer)
|
||||
}
|
||||
var pos = 0
|
||||
var readAsInt = function(bytes) {
|
||||
if (bytes === 0) return 0;
|
||||
pos++;
|
||||
return buffer[pos-1] + readAsInt(bytes-1) * 256
|
||||
}
|
||||
var readVarInt = function() {
|
||||
var bytes = buffer.slice(pos, pos + 9) // maximum possible number of bytes to read
|
||||
var result = convert.varIntToNum(bytes)
|
||||
if (typeof buffer == "string") buffer = new Buffer(buffer, 'hex')
|
||||
else if (Array.isArray(buffer)) buffer = new Buffer(buffer)
|
||||
|
||||
pos += result.bytes.length
|
||||
return result.number
|
||||
}
|
||||
var readBytes = function(bytes) {
|
||||
pos += bytes
|
||||
return buffer.slice(pos - bytes, pos)
|
||||
}
|
||||
var readVarString = function() {
|
||||
var size = readVarInt()
|
||||
return readBytes(size)
|
||||
}
|
||||
var obj = {
|
||||
ins: [],
|
||||
outs: []
|
||||
}
|
||||
obj.version = readAsInt(4)
|
||||
var ins = readVarInt()
|
||||
var i
|
||||
// Copy because we mutate (reverse TxOutHashs)
|
||||
buffer = new Buffer(buffer)
|
||||
|
||||
for (i = 0; i < ins; i++) {
|
||||
obj.ins.push({
|
||||
var offset = 0
|
||||
function readSlice(n) {
|
||||
offset += n
|
||||
return buffer.slice(offset - n, offset)
|
||||
}
|
||||
function readUInt32() {
|
||||
var i = buffer.readUInt32LE(offset)
|
||||
offset += 4
|
||||
return i
|
||||
}
|
||||
function readUInt64() {
|
||||
var i = BufferExt.readUInt64LE(buffer, offset)
|
||||
offset += 8
|
||||
return i
|
||||
}
|
||||
function readVarInt() {
|
||||
var vi = BufferExt.readVarInt(buffer, offset)
|
||||
offset += vi.size
|
||||
return vi.number
|
||||
}
|
||||
|
||||
var ins = []
|
||||
var outs = []
|
||||
|
||||
var version = readUInt32()
|
||||
var vinLen = readVarInt()
|
||||
|
||||
for (var i = 0; i < vinLen; ++i) {
|
||||
var hash = readSlice(32)
|
||||
|
||||
// TxHash is little-endian, we want big-endian hex
|
||||
Array.prototype.reverse.call(hash)
|
||||
|
||||
var vout = readUInt32()
|
||||
var scriptLen = readVarInt()
|
||||
var script = readSlice(scriptLen)
|
||||
var sequence = readUInt32()
|
||||
|
||||
ins.push({
|
||||
outpoint: {
|
||||
hash: convert.bytesToHex(readBytes(32).reverse()),
|
||||
index: readAsInt(4)
|
||||
hash: hash.toString('hex'),
|
||||
index: vout,
|
||||
},
|
||||
script: new Script(readVarString()),
|
||||
sequence: readBytes(4)
|
||||
})
|
||||
}
|
||||
var outs = readVarInt()
|
||||
|
||||
for (i = 0; i < outs; i++) {
|
||||
obj.outs.push({
|
||||
value: convert.bytesToNum(readBytes(8)),
|
||||
script: new Script(readVarString())
|
||||
script: Script.fromBuffer(script),
|
||||
sequence: sequence
|
||||
})
|
||||
}
|
||||
|
||||
obj.locktime = readAsInt(4)
|
||||
var voutLen = readVarInt()
|
||||
|
||||
return new Transaction(obj)
|
||||
for (i = 0; i < voutLen; ++i) {
|
||||
var value = readUInt64()
|
||||
var scriptLen = readVarInt()
|
||||
var script = readSlice(scriptLen)
|
||||
|
||||
outs.push({
|
||||
value: value,
|
||||
script: Script.fromBuffer(script)
|
||||
})
|
||||
}
|
||||
|
||||
var locktime = readUInt32()
|
||||
assert.equal(offset, buffer.length, 'Invalid transaction')
|
||||
|
||||
return new Transaction({
|
||||
version: version,
|
||||
ins: ins,
|
||||
outs: outs,
|
||||
locktime: locktime
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -146,7 +146,7 @@ function Wallet(seed, options) {
|
|||
}
|
||||
|
||||
this.processTx = function(tx) {
|
||||
var txhash = convert.bytesToHex(tx.getHash())
|
||||
var txhash = tx.getHash()
|
||||
|
||||
tx.outs.forEach(function(txOut, i){
|
||||
var address = txOut.address.toString()
|
||||
|
|
86
test/buffer.js
Normal file
86
test/buffer.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
var assert = require('assert')
|
||||
var BufferExt = require('../').BufferExt
|
||||
|
||||
var fixtures = require('./fixtures/buffer.js')
|
||||
|
||||
describe('Buffer Extensions', function() {
|
||||
describe('readUInt64LE', function() {
|
||||
it('matches test vectors', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
var buffer = new Buffer(f.hex64, 'hex')
|
||||
var number = BufferExt.readUInt64LE(buffer, 0)
|
||||
|
||||
assert.equal(number, f.dec)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('readVarInt', function() {
|
||||
it('matches test vectors', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
var buffer = new Buffer(f.hexVI, 'hex')
|
||||
var d = BufferExt.readVarInt(buffer, 0)
|
||||
|
||||
assert.equal(d.number, f.dec)
|
||||
assert.equal(d.size, buffer.length)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('varIntSize', function() {
|
||||
it('matches test vectors', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
var number = parseInt(f.dec)
|
||||
var size = BufferExt.varIntSize(number)
|
||||
|
||||
assert.equal(size, f.hexVI.length / 2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('writeUInt64LE', function() {
|
||||
it('matches test vectors', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
var buffer = new Buffer(8)
|
||||
buffer.fill(0)
|
||||
|
||||
BufferExt.writeUInt64LE(buffer, f.dec, 0)
|
||||
assert.equal(buffer.toString('hex'), f.hex64)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.forEach(function(f) {
|
||||
it('throws on ' + f.description, function() {
|
||||
assert.throws(function() {
|
||||
var buffer = new Buffer(8)
|
||||
buffer.fill(0)
|
||||
|
||||
BufferExt.writeUInt64LE(buffer, f.dec, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('writeVarInt', function() {
|
||||
it('matches test vectors', function() {
|
||||
fixtures.valid.forEach(function(f) {
|
||||
var buffer = new Buffer(9)
|
||||
buffer.fill(0)
|
||||
|
||||
var n = BufferExt.writeVarInt(buffer, f.dec, 0)
|
||||
assert.equal(buffer.slice(0, n).toString('hex'), f.hexVI)
|
||||
})
|
||||
})
|
||||
|
||||
fixtures.invalid.forEach(function(f) {
|
||||
it('throws on ' + f.description, function() {
|
||||
assert.throws(function() {
|
||||
var buffer = new Buffer(9)
|
||||
buffer.fill(0)
|
||||
|
||||
BufferExt.writeVarInt(buffer, f.dec, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -52,67 +52,6 @@ describe('convert', function() {
|
|||
})
|
||||
})
|
||||
|
||||
describe('numToVarInt', function() {
|
||||
describe('works', function() {
|
||||
var data = [
|
||||
0, 128, 252, // 8-bit
|
||||
256, 512, 1024, // 16-bit
|
||||
65541, // 32-bit
|
||||
4294967299, // 64-bit
|
||||
]
|
||||
var expected = [
|
||||
[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
|
||||
]
|
||||
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
var actual = convert.numToVarInt(data[i])
|
||||
assert.deepEqual(actual, expected[i])
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
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"
|
||||
|
|
74
test/fixtures/buffer.js
vendored
Normal file
74
test/fixtures/buffer.js
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
module.exports = {
|
||||
"valid": [
|
||||
{
|
||||
"dec": 0,
|
||||
"hex64": "0000000000000000",
|
||||
"hexVI": "00"
|
||||
},
|
||||
{
|
||||
"dec": 1,
|
||||
"hex64": "0100000000000000",
|
||||
"hexVI": "01"
|
||||
},
|
||||
{
|
||||
"dec": 252,
|
||||
"hex64": "fc00000000000000",
|
||||
"hexVI": "fc"
|
||||
},
|
||||
{
|
||||
"dec": 253,
|
||||
"hex64": "fd00000000000000",
|
||||
"hexVI": "fdfd00"
|
||||
},
|
||||
{
|
||||
"dec": 254,
|
||||
"hex64": "fe00000000000000",
|
||||
"hexVI": "fdfe00"
|
||||
},
|
||||
{
|
||||
"dec": 65535,
|
||||
"hex64": "ffff000000000000",
|
||||
"hexVI": "fdffff"
|
||||
},
|
||||
{
|
||||
"dec": 65536,
|
||||
"hex64": "0000010000000000",
|
||||
"hexVI": "fe00000100"
|
||||
},
|
||||
{
|
||||
"dec": 65537,
|
||||
"hex64": "0100010000000000",
|
||||
"hexVI": "fe01000100"
|
||||
},
|
||||
{
|
||||
"dec": 4294967295,
|
||||
"hex64": "ffffffff00000000",
|
||||
"hexVI": "feffffffff"
|
||||
},
|
||||
{
|
||||
"dec": 4294967296,
|
||||
"hex64": "0000000001000000",
|
||||
"hexVI": "ff0000000001000000"
|
||||
},
|
||||
{
|
||||
"dec": 4294967297,
|
||||
"hex64": "0100000001000000",
|
||||
"hexVI": "ff0100000001000000"
|
||||
},
|
||||
{
|
||||
"dec": 9007199254740991,
|
||||
"hex64": "ffffffffffff1f00",
|
||||
"hexVI": "ffffffffffffff1f00"
|
||||
}
|
||||
],
|
||||
"invalid": [
|
||||
{
|
||||
"description": "n === 2^53",
|
||||
"value": 9007199254740992
|
||||
},
|
||||
{
|
||||
"description": "n > 2^53",
|
||||
"value": 18374686479671624000
|
||||
}
|
||||
]
|
||||
}
|
|
@ -38,6 +38,13 @@ describe('Transaction', function() {
|
|||
assert.equal(b2h(actual), expected)
|
||||
})
|
||||
|
||||
it('does not mutate the input buffer', function() {
|
||||
var buffer = new Buffer(serializedTx, 'hex')
|
||||
Transaction.deserialize(buffer)
|
||||
|
||||
assert.equal(buffer.toString('hex'), serializedTx)
|
||||
})
|
||||
|
||||
it('decodes version correctly', function(){
|
||||
assert.equal(tx.version, 1)
|
||||
})
|
||||
|
@ -50,7 +57,7 @@ describe('Transaction', function() {
|
|||
assert.equal(tx.ins.length, 1)
|
||||
|
||||
var input = tx.ins[0]
|
||||
assert.deepEqual(input.sequence, [255, 255, 255, 255])
|
||||
assert.equal(input.sequence, 4294967295)
|
||||
|
||||
assert.equal(input.outpoint.index, 0)
|
||||
assert.equal(input.outpoint.hash, "69d02fc05c4e0ddc87e796eee42693c244a3112fffe1f762c3fb61ffcb304634")
|
||||
|
@ -72,7 +79,7 @@ describe('Transaction', function() {
|
|||
|
||||
it('assigns hash to deserialized object', function(){
|
||||
var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c"
|
||||
assert.equal(b2h(tx.hash), hashHex)
|
||||
assert.equal(tx.hash, hashHex)
|
||||
})
|
||||
|
||||
it('decodes large inputs correctly', function() {
|
||||
|
@ -81,14 +88,18 @@ describe('Transaction', function() {
|
|||
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))
|
||||
var buffer = tx.serialize()
|
||||
|
||||
// the deserialized-serialized transaction should return to its original state (== tx)
|
||||
var bytes2 = Transaction.deserialize(mutated).serialize()
|
||||
assert.deepEqual(bytes, bytes2)
|
||||
// we're going to replace the 8bit VarInt for tx.ins.length with a stretched 32bit equivalent
|
||||
var mutated = Buffer.concat([
|
||||
buffer.slice(0, 4),
|
||||
new Buffer([254, 1, 0, 0, 0]),
|
||||
buffer.slice(5)
|
||||
])
|
||||
|
||||
// the deserialized-serialized transaction should return to its non-mutated state (== tx)
|
||||
var buffer2 = Transaction.deserialize(mutated).serialize()
|
||||
assert.deepEqual(buffer, buffer2)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -128,7 +139,7 @@ describe('Transaction', function() {
|
|||
assert.equal(tx.ins.length, 1)
|
||||
|
||||
var input = tx.ins[0]
|
||||
assert.deepEqual(input.sequence, [255, 255, 255, 255])
|
||||
assert.equal(input.sequence, 4294967295)
|
||||
|
||||
assert.equal(input.outpoint.index, 0)
|
||||
assert.equal(input.outpoint.hash, "0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57")
|
||||
|
|
|
@ -4,7 +4,6 @@ var T = require('../src/transaction.js')
|
|||
var Transaction = T.Transaction
|
||||
var TransactionOut = T.TransactionOut
|
||||
var Script = require('../src/script.js')
|
||||
var convert = require('../src/convert.js')
|
||||
var assert = require('assert')
|
||||
var sinon = require('sinon')
|
||||
var crypto = require('../').crypto
|
||||
|
@ -338,7 +337,7 @@ describe('Wallet', function() {
|
|||
|
||||
function verifyOutputAdded(index) {
|
||||
var txOut = tx.outs[index]
|
||||
var key = convert.bytesToHex(tx.getHash()) + ":" + index
|
||||
var key = tx.getHash() + ":" + index
|
||||
var output = wallet.outputs[key]
|
||||
assert.equal(output.receive, key)
|
||||
assert.equal(output.value, txOut.value)
|
||||
|
@ -367,7 +366,7 @@ describe('Wallet', function() {
|
|||
var key = txIn.outpoint.hash + ":" + txIn.outpoint.index
|
||||
var output = wallet.outputs[key]
|
||||
|
||||
assert.equal(output.spend, convert.bytesToHex(tx.getHash()) + ':' + 0)
|
||||
assert.equal(output.spend, tx.getHash() + ':' + 0)
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -539,7 +538,7 @@ describe('Wallet', function() {
|
|||
})
|
||||
|
||||
function fakeTxHash(i) {
|
||||
return "txtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtxtx" + i
|
||||
return "efefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe" + i
|
||||
}
|
||||
})
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue