Changes internal serialization to use Buffers instead
This commit is contained in:
parent
c8bda6dde6
commit
a8cf2fdd9e
5 changed files with 123 additions and 78 deletions
|
@ -14,7 +14,7 @@ function Script(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Script.fromBuffer = function(buffer) {
|
Script.fromBuffer = function(buffer) {
|
||||||
// assert(Buffer.isBuffer(buffer)) // FIXME: transitionary
|
assert(Buffer.isBuffer(buffer)) // FIXME: transitionary
|
||||||
|
|
||||||
return new Script(Array.prototype.slice.call(buffer))
|
return new Script(Array.prototype.slice.call(buffer))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
var Address = require('./address')
|
var Address = require('./address')
|
||||||
var BigInteger = require('bigi')
|
var BigInteger = require('bigi')
|
||||||
|
var BufferExt = require('./buffer')
|
||||||
var Script = require('./script')
|
var Script = require('./script')
|
||||||
var convert = require('./convert')
|
var convert = require('./convert')
|
||||||
var crypto = require('./crypto')
|
var crypto = require('./crypto')
|
||||||
|
@ -122,36 +123,68 @@ Transaction.prototype.addOutput = function (address, value, network) {
|
||||||
* accordance with the Bitcoin protocol.
|
* accordance with the Bitcoin protocol.
|
||||||
*/
|
*/
|
||||||
Transaction.prototype.serialize = function () {
|
Transaction.prototype.serialize = function () {
|
||||||
var buffer = []
|
var txInSize = this.ins.reduce(function(a, x) {
|
||||||
buffer = buffer.concat(convert.numToBytes(parseInt(this.version), 4))
|
return a + (40 + BufferExt.varIntSize(x.script.buffer.length) + x.script.buffer.length)
|
||||||
buffer = buffer.concat(convert.numToVarInt(this.ins.length))
|
}, 0)
|
||||||
|
|
||||||
this.ins.forEach(function(txin) {
|
var txOutSize = this.outs.reduce(function(a, x) {
|
||||||
// Why do blockchain.info, blockexplorer.com, sx and just about everybody
|
return a + (8 + BufferExt.varIntSize(x.script.buffer.length) + x.script.buffer.length)
|
||||||
// else use little-endian hashes? No idea...
|
}, 0)
|
||||||
buffer = buffer.concat(convert.hexToBytes(txin.outpoint.hash).reverse())
|
|
||||||
|
|
||||||
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
|
var offset = 0
|
||||||
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
|
function writeSlice(slice) {
|
||||||
buffer = buffer.concat(scriptBytes)
|
if (Array.isArray(slice)) slice = new Buffer(slice)
|
||||||
buffer = buffer.concat(convert.numToBytes(txin.sequence, 4))
|
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 writeVI(i) {
|
||||||
|
var n = BufferExt.writeVarInt(buffer, i, offset)
|
||||||
|
offset += n
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUInt32(this.version)
|
||||||
|
writeVI(this.ins.length)
|
||||||
|
|
||||||
|
this.ins.forEach(function(txin, i) {
|
||||||
|
var hash = new Buffer(txin.outpoint.hash, 'hex')
|
||||||
|
|
||||||
|
// Hash is big-endian, we want little-endian for the hex
|
||||||
|
Array.prototype.reverse.call(hash)
|
||||||
|
|
||||||
|
writeSlice(hash)
|
||||||
|
writeUInt32(txin.outpoint.index)
|
||||||
|
writeVI(txin.script.buffer.length)
|
||||||
|
writeSlice(txin.script.buffer)
|
||||||
|
writeUInt32(txin.sequence)
|
||||||
})
|
})
|
||||||
|
|
||||||
buffer = buffer.concat(convert.numToVarInt(this.outs.length))
|
writeVI(this.outs.length)
|
||||||
|
|
||||||
this.outs.forEach(function(txout) {
|
this.outs.forEach(function(txout) {
|
||||||
buffer = buffer.concat(convert.numToBytes(txout.value,8))
|
writeUInt64(txout.value)
|
||||||
|
writeVI(txout.script.buffer.length)
|
||||||
var scriptBytes = txout.script.buffer
|
writeSlice(txout.script.buffer)
|
||||||
buffer = buffer.concat(convert.numToVarInt(scriptBytes.length))
|
|
||||||
buffer = buffer.concat(scriptBytes)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
buffer = buffer.concat(convert.numToBytes(parseInt(this.locktime), 4))
|
writeUInt32(this.locktime)
|
||||||
|
assert.equal(offset, buffer.length, 'Invalid transaction object')
|
||||||
|
|
||||||
return new Buffer(buffer)
|
return buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction.prototype.serializeHex = function() {
|
Transaction.prototype.serializeHex = function() {
|
||||||
|
@ -219,17 +252,14 @@ Transaction.prototype.hashTransactionForSignature =
|
||||||
return crypto.hash256(buffer)
|
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 ()
|
Transaction.prototype.getHash = function ()
|
||||||
{
|
{
|
||||||
var buffer = this.serialize()
|
var buffer = crypto.hash256(this.serialize())
|
||||||
var hash = crypto.hash256(buffer)
|
|
||||||
|
|
||||||
return Array.prototype.slice.call(hash).reverse()
|
// Little-endian is used for Transaction hash hex
|
||||||
|
Array.prototype.reverse.call(buffer)
|
||||||
|
|
||||||
|
return buffer.toString('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction.prototype.clone = function ()
|
Transaction.prototype.clone = function ()
|
||||||
|
@ -250,63 +280,79 @@ Transaction.prototype.clone = function ()
|
||||||
}
|
}
|
||||||
|
|
||||||
Transaction.deserialize = function(buffer) {
|
Transaction.deserialize = function(buffer) {
|
||||||
if (typeof buffer == "string") {
|
if (typeof buffer == "string") buffer = new Buffer(buffer, 'hex')
|
||||||
buffer = convert.hexToBytes(buffer)
|
else if (Array.isArray(buffer)) buffer = new Buffer(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)
|
|
||||||
|
|
||||||
pos += result.bytes.length
|
var offset = 0
|
||||||
return result.number
|
function readSlice(n) {
|
||||||
|
offset += n
|
||||||
|
return buffer.slice(offset - n, offset)
|
||||||
}
|
}
|
||||||
var readBytes = function(bytes) {
|
function readUInt32() {
|
||||||
pos += bytes
|
var i = buffer.readUInt32LE(offset)
|
||||||
return buffer.slice(pos - bytes, pos)
|
offset += 4
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
var readVarString = function() {
|
function readUInt64() {
|
||||||
var size = readVarInt()
|
var i = BufferExt.readUInt64LE(buffer, offset)
|
||||||
return readBytes(size)
|
offset += 8
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
var obj = {
|
function readVI() {
|
||||||
ins: [],
|
var vi = BufferExt.readVarInt(buffer, offset)
|
||||||
outs: []
|
offset += vi.size
|
||||||
|
return vi.number
|
||||||
}
|
}
|
||||||
obj.version = readAsInt(4)
|
|
||||||
var ins = readVarInt()
|
|
||||||
var i
|
|
||||||
|
|
||||||
for (i = 0; i < ins; i++) {
|
var ins = []
|
||||||
var hash = readBytes(32)
|
var outs = []
|
||||||
|
|
||||||
|
var version = readUInt32()
|
||||||
|
var vinLen = readVI()
|
||||||
|
|
||||||
|
for (var i = 0; i < vinLen; ++i) {
|
||||||
|
var hash = readSlice(32)
|
||||||
|
|
||||||
|
// Hash is big-endian, we want little-endian for the hex
|
||||||
Array.prototype.reverse.call(hash)
|
Array.prototype.reverse.call(hash)
|
||||||
|
|
||||||
obj.ins.push({
|
var vout = readUInt32()
|
||||||
|
var scriptLen = readVI()
|
||||||
|
var script = readSlice(scriptLen)
|
||||||
|
var sequence = readUInt32()
|
||||||
|
|
||||||
|
ins.push({
|
||||||
outpoint: {
|
outpoint: {
|
||||||
hash: convert.bytesToHex(hash),
|
hash: hash.toString('hex'),
|
||||||
index: readAsInt(4)
|
index: vout,
|
||||||
},
|
},
|
||||||
script: Script.fromBuffer(readVarString()),
|
script: Script.fromBuffer(script),
|
||||||
sequence: readAsInt(4)
|
sequence: sequence
|
||||||
})
|
|
||||||
}
|
|
||||||
var outs = readVarInt()
|
|
||||||
|
|
||||||
for (i = 0; i < outs; i++) {
|
|
||||||
obj.outs.push({
|
|
||||||
value: convert.bytesToNum(readBytes(8)),
|
|
||||||
script: Script.fromBuffer(readVarString())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.locktime = readAsInt(4)
|
var voutLen = readVI()
|
||||||
|
|
||||||
return new Transaction(obj)
|
for (i = 0; i < voutLen; ++i) {
|
||||||
|
var value = readUInt64()
|
||||||
|
var scriptLen = readVI()
|
||||||
|
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) {
|
this.processTx = function(tx) {
|
||||||
var txhash = convert.bytesToHex(tx.getHash())
|
var txhash = tx.getHash()
|
||||||
|
|
||||||
tx.outs.forEach(function(txOut, i){
|
tx.outs.forEach(function(txOut, i){
|
||||||
var address = txOut.address.toString()
|
var address = txOut.address.toString()
|
||||||
|
|
|
@ -72,7 +72,7 @@ describe('Transaction', function() {
|
||||||
|
|
||||||
it('assigns hash to deserialized object', function(){
|
it('assigns hash to deserialized object', function(){
|
||||||
var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c"
|
var hashHex = "a9d4599e15b53f3eb531608ddb31f48c695c3d0b3538a6bda871e8b34f2f430c"
|
||||||
assert.equal(b2h(tx.hash), hashHex)
|
assert.equal(tx.hash, hashHex)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('decodes large inputs correctly', function() {
|
it('decodes large inputs correctly', function() {
|
||||||
|
|
|
@ -4,7 +4,6 @@ var T = require('../src/transaction.js')
|
||||||
var Transaction = T.Transaction
|
var Transaction = T.Transaction
|
||||||
var TransactionOut = T.TransactionOut
|
var TransactionOut = T.TransactionOut
|
||||||
var Script = require('../src/script.js')
|
var Script = require('../src/script.js')
|
||||||
var convert = require('../src/convert.js')
|
|
||||||
var assert = require('assert')
|
var assert = require('assert')
|
||||||
var sinon = require('sinon')
|
var sinon = require('sinon')
|
||||||
var crypto = require('../').crypto
|
var crypto = require('../').crypto
|
||||||
|
@ -338,7 +337,7 @@ describe('Wallet', function() {
|
||||||
|
|
||||||
function verifyOutputAdded(index) {
|
function verifyOutputAdded(index) {
|
||||||
var txOut = tx.outs[index]
|
var txOut = tx.outs[index]
|
||||||
var key = convert.bytesToHex(tx.getHash()) + ":" + index
|
var key = tx.getHash() + ":" + index
|
||||||
var output = wallet.outputs[key]
|
var output = wallet.outputs[key]
|
||||||
assert.equal(output.receive, key)
|
assert.equal(output.receive, key)
|
||||||
assert.equal(output.value, txOut.value)
|
assert.equal(output.value, txOut.value)
|
||||||
|
@ -367,7 +366,7 @@ describe('Wallet', function() {
|
||||||
var key = txIn.outpoint.hash + ":" + txIn.outpoint.index
|
var key = txIn.outpoint.hash + ":" + txIn.outpoint.index
|
||||||
var output = wallet.outputs[key]
|
var output = wallet.outputs[key]
|
||||||
|
|
||||||
assert.equal(output.spend, convert.bytesToHex(tx.getHash()) + ':' + 0)
|
assert.equal(output.spend, tx.getHash() + ':' + 0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue