Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions src/block.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
var assert = require('assert')
var bufferutils = require('./bufferutils')
var crypto = require('./crypto')

var Transaction = require('./transaction')
var Script = require('./script')

function Block() {
this.version = 1
this.prevHash = null
this.merkleRoot = null
this.timestamp = 0
this.bits = 0
this.nonce = 0
}

Block.fromBuffer = function(buffer) {
assert(buffer.length >= 80, 'Buffer too small (< 80 bytes)')

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
}

var block = new Block()
block.version = readUInt32()
block.prevHash = readSlice(32)
block.merkleRoot = readSlice(32)
block.timestamp = readUInt32()
block.bits = readUInt32()
block.nonce = readUInt32()

if (buffer.length === 80) return block

function readUInt64() {
var i = bufferutils.readUInt64LE(buffer, offset)
offset += 8
return i
}

function readVarInt() {
var vi = bufferutils.readVarInt(buffer, offset)
offset += vi.size
return vi.number
}

function readScript() {
return Script.fromBuffer(readSlice(readVarInt()))
}

function readTransaction() {
var tx = new Transaction()
tx.version = readUInt32()

var vinLen = readVarInt()
for (var i = 0; i < vinLen; ++i) {
tx.ins.push({
hash: readSlice(32),
index: readUInt32(),
script: readScript(),
sequence: readUInt32()
})
}

var voutLen = readVarInt()
for (i = 0; i < voutLen; ++i) {
tx.outs.push({
value: readUInt64(),
script: readScript(),
})
}

tx.locktime = readUInt32()

return tx
}

var nTransactions = readVarInt()
block.transactions = []

for (var i = 0; i < nTransactions; ++i) {
var tx = readTransaction()
block.transactions.push(tx)
}

return block
}

Block.fromHex = function(hex) {
return Block.fromBuffer(new Buffer(hex, 'hex'))
}

Block.prototype.getHash = function() {
return crypto.hash256(this.toBuffer(true))
}

Block.prototype.getId = function() {
return bufferutils.reverse(this.getHash()).toString('hex')
}

Block.prototype.getUTCDate = function() {
var date = new Date(0) // epoch
date.setUTCSeconds(this.timestamp)

return date
}

Block.prototype.toBuffer = function(headersOnly) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have toBuffer() and headersToBuffer() instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fromBuffer imports both types.

I'm unsure on this, but absolutely agree it would be consistent with the pattern we have applied to all other instances of this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of headersToBuffer, but I definitely agree this should change.

var buffer = new Buffer(80)

var offset = 0
function writeSlice(slice) {
slice.copy(buffer, offset)
offset += slice.length
}

function writeUInt32(i) {
buffer.writeUInt32LE(i, offset)
offset += 4
}

writeUInt32(this.version)
writeSlice(this.prevHash)
writeSlice(this.merkleRoot)
writeUInt32(this.timestamp)
writeUInt32(this.bits)
writeUInt32(this.nonce)

if (headersOnly || !this.transactions) return buffer

var txLenBuffer = bufferutils.varIntBuffer(this.transactions.length)
var txBuffers = this.transactions.map(function(tx) {
return tx.toBuffer()
})

return Buffer.concat([buffer, txLenBuffer].concat(txBuffers))
}

Block.prototype.toHex = function(headersOnly) {
return this.toBuffer(headersOnly).toString('hex')
}

module.exports = Block
9 changes: 9 additions & 0 deletions src/bufferutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ function writeVarInt(buffer, number, offset) {
return size
}

function varIntBuffer(i) {
var size = varIntSize(i)
var buffer = new Buffer(size)
writeVarInt(buffer, i, 0)

return buffer
}

function reverse(buffer) {
var buffer2 = new Buffer(buffer)
Array.prototype.reverse.call(buffer2)
Expand All @@ -171,6 +179,7 @@ module.exports = {
readUInt64LE: readUInt64LE,
readVarInt: readVarInt,
reverse: reverse,
varIntBuffer: varIntBuffer,
varIntSize: varIntSize,
writePushDataInt: writePushDataInt,
writeUInt64LE: writeUInt64LE,
Expand Down
3 changes: 1 addition & 2 deletions src/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ var ecparams = ecurve.getCurveByName('secp256k1')
function magicHash(message, network) {
var magicPrefix = new Buffer(network.magicPrefix)
var messageBuffer = new Buffer(message)
var lengthBuffer = new Buffer(bufferutils.varIntSize(messageBuffer.length))
bufferutils.writeVarInt(lengthBuffer, messageBuffer.length, 0)
var lengthBuffer = bufferutils.varIntBuffer(messageBuffer.length)

var buffer = Buffer.concat([magicPrefix, lengthBuffer, messageBuffer])
return crypto.hash256(buffer)
Expand Down
88 changes: 88 additions & 0 deletions test/block.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
var assert = require('assert')

var Block = require('../src/block')

var fixtures = require('./fixtures/block')

describe('Block', function() {
describe('fromBuffer/fromHex', function() {
fixtures.valid.forEach(function(f) {
it('imports the block: ' + f.description + ' correctly', function() {
var block = Block.fromHex(f.hex)

assert.equal(block.version, f.version)
assert.equal(block.prevHash.toString('hex'), f.prevHash)
assert.equal(block.merkleRoot.toString('hex'), f.merkleRoot)
assert.equal(block.timestamp, f.timestamp)
assert.equal(block.bits, f.bits)
assert.equal(block.nonce, f.nonce)
})
})

fixtures.invalid.forEach(function(f) {
it('throws on ' + f.exception, function() {
assert.throws(function() {
Block.fromHex(f.hex)
}, new RegExp(f.exception))
})
})
})

describe('toBuffer/toHex', function() {
fixtures.valid.forEach(function(f) {
var block

beforeEach(function() {
block = Block.fromHex(f.hex)
})

it('exports the block: ' + f.description + ' correctly', function() {
assert.equal(block.toHex(), f.hex)
})
})
})

describe('getHash', function() {
fixtures.valid.forEach(function(f) {
var block

beforeEach(function() {
block = Block.fromHex(f.hex)
})

it('calculates ' + f.hash + ' for the block: ' + f.description, function() {
assert.equal(block.getHash().toString('hex'), f.hash)
})
})
})

describe('getId', function() {
fixtures.valid.forEach(function(f) {
var block

beforeEach(function() {
block = Block.fromHex(f.hex)
})

it('calculates ' + f.id + ' for the block: ' + f.description, function() {
assert.equal(block.getId(), f.id)
})
})
})

describe('getUTCDate', function() {
fixtures.valid.forEach(function(f) {
var block

beforeEach(function() {
block = Block.fromHex(f.hex)
})

it('returns UTC date of ' + f.id, function() {
var utcDate = block.getUTCDate().getTime()

assert.equal(utcDate, f.timestamp * 1e3)
})
})
})
})
12 changes: 11 additions & 1 deletion test/bufferutils.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ describe('bufferutils', function() {
})
})

describe('varIntBuffer', function() {
fixtures.valid.forEach(function(f) {
it('encodes ' + f.dec + ' correctly', function() {
var buffer = bufferutils.varIntBuffer(f.dec)

assert.equal(buffer.toString('hex'), f.hexVI)
})
})
})

describe('varIntSize', function() {
fixtures.valid.forEach(function(f) {
it('determines the varIntSize of ' + f.dec + ' correctly', function() {
Expand All @@ -99,7 +109,7 @@ describe('bufferutils', function() {
})

describe('writePushDataInt', function() {
fixtures.valid.forEach(function(f, i) {
fixtures.valid.forEach(function(f) {
if (!f.hexPD) return

it('encodes ' + f.dec + ' correctly', function() {
Expand Down
58 changes: 58 additions & 0 deletions test/fixtures/block.json

Large diffs are not rendered by default.