Skip to content

Commit

Permalink
BTG Block added
Browse files Browse the repository at this point in the history
  • Loading branch information
Vutov committed Mar 27, 2018
1 parent 40edca4 commit 74cb88e
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,3 +2,4 @@ coverage
node_modules
.nyc_output
npm-debug.log
.vscode
149 changes: 149 additions & 0 deletions src/block_gold.js
@@ -0,0 +1,149 @@
// Bitcoin Gold (BTG) block header has differences compared to Bitcoin block headers
// https://github.com/BTCGPU/BTCGPU/wiki/Technical-Spec

var Buffer = require('safe-buffer').Buffer
var varuint = require('varuint-bitcoin')

var Transaction = require('./transaction')
var Block = require('./block')

function BlockGold () {
this.version = 1
this.prevHash = null
this.merkleRoot = null
this.height = 0
this.reserved = null
this.timestamp = 0
this.bits = 0
this.nonce = null
this.solutionLength = 0
this.solution = null
this.transactions = []
}

BlockGold.prototype = Object.create(Block.prototype)

BlockGold.fromBuffer = function (buffer) {
if (buffer.length < 140) throw new Error('Buffer too small (< 140 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
}

function readInt32 () {
var i = buffer.readInt32LE(offset)
offset += 4
return i
}

function readVarInt () {
var vi = varuint.decode(buffer, offset)
offset += varuint.decode.bytes
return vi
}

var block = new BlockGold()
block.version = readInt32()
block.prevHash = readSlice(32)
block.merkleRoot = readSlice(32)
block.height = readUInt32()
block.reserved = readSlice(28)
block.timestamp = readUInt32()
block.bits = readUInt32()
block.nonce = readSlice(32)
block.solutionLength = readVarInt()
block.solution = readSlice(block.solutionLength)

if(buffer.length == offset) {
return block;
}

function readTransaction () {
var tx = Transaction.fromBuffer(buffer.slice(offset), true)
offset += tx.byteLength()
return tx
}

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

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

return block
}

BlockGold.prototype.byteLength = function (headersOnly) {
// Solution can have different size, for regtest/testnet is arround 140-170, for mainnet 1400-1500
var headerSize = 140 + varuint.encodingLength(this.solutionLength) + this.solution.length
if (headersOnly || !this.transactions) return headerSize

return headerSize + varuint.encodingLength(this.transactions.length) + this.transactions.reduce(function (a, x) {
return a + x.byteLength()
}, 0)
}

BlockGold.fromHex = function (hex) {
return BlockGold.fromBuffer(Buffer.from(hex, 'hex'))
}

// TODO: buffer, offset compatibility
BlockGold.prototype.toBuffer = function (headersOnly) {
var buffer = Buffer.allocUnsafe(this.byteLength(headersOnly))

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

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

writeInt32(this.version)
writeSlice(this.prevHash)
writeSlice(this.merkleRoot)
writeInt32(this.height)
writeSlice(this.reserved)
writeUInt32(this.timestamp)
writeUInt32(this.bits)
writeSlice(this.nonce)
varuint.encode(this.solutionLength, buffer, offset)
offset += varuint.encode.bytes
writeSlice(this.solution)

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

varuint.encode(this.transactions.length, buffer, offset)
offset += varuint.encode.bytes

this.transactions.forEach(function (tx) {
var txSize = tx.byteLength() // TODO: extract from toBuffer?
tx.toBuffer(buffer, offset)
offset += txSize
})

return buffer
}

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

module.exports = BlockGold
1 change: 1 addition & 0 deletions src/index.js
Expand Up @@ -9,6 +9,7 @@ module.exports = {
bufferutils: require('./bufferutils'), // TODO: remove in 4.0.0

Block: require('./block'),
BlockGold: require('./block_gold'),
ECPair: require('./ecpair'),
ECSignature: require('./ecsignature'),
HDNode: require('./hdnode'),
Expand Down
12 changes: 12 additions & 0 deletions src/networks.js
Expand Up @@ -4,6 +4,7 @@
module.exports = {
bitcoingold: {
messagePrefix: '\x18Bitcoin Gold Signed Message:\n',
bech32: 'btg',
bip32: {
public: 0x0488b21e,
private: 0x0488ade4
Expand All @@ -12,6 +13,17 @@ module.exports = {
scriptHash: 0x17,
wif: 0x80
},
bitcoingoldtestnet: {
messagePrefix: '\x18Bitcoin Gold Signed Message:\n',
bech32: 'tbtg',
bip32: {
public: 0x043587cf,
private: 0x04358394
},
pubKeyHash: 0x6f,
scriptHash: 0xc4,
wif: 0xef
},
bitcoin: {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bech32: 'bc',
Expand Down
2 changes: 1 addition & 1 deletion test/address.js
Expand Up @@ -66,7 +66,7 @@ describe('address', function () {

assert.throws(function () {
baddress.fromOutputScript(script)
}, new RegExp(f.script + ' ' + f.exception))
}, new RegExp(f.exception))
})
})
})
Expand Down
54 changes: 54 additions & 0 deletions test/block_gold.js
@@ -0,0 +1,54 @@
/* global describe, it, beforeEach */

var assert = require('assert')
var BlockGold = require('../src/block_gold')

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

describe('BlockGold', function () {
var bufferToLittleEndian = function(buffer) {
return buffer.toString('hex').match(/.{2}/g).reverse().join("");
}

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

assert.strictEqual(block.version, f.version)
assert.strictEqual(bufferToLittleEndian(block.prevHash), f.previousblockhash)
assert.strictEqual(bufferToLittleEndian(block.merkleRoot), f.merkleroot)
assert.strictEqual(block.height, f.height)
assert.strictEqual(block.timestamp, f.time)
assert.strictEqual(block.bits.toString(16), f.bits)
assert.strictEqual(bufferToLittleEndian(block.nonce), f.nonce)
assert.strictEqual(block.solution.toString('hex'), f.solution)
assert.strictEqual(block.transactions.length, f.tx !== undefined ? f.tx.length : 0)
})
})

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

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

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

it('exports ' + f.description, function () {
var size = block.byteLength(true)
assert.strictEqual(block.toHex(true), f.hex.slice(0, size * 2))
assert.strictEqual(block.toHex(), f.hex)
})
})
})
})
56 changes: 56 additions & 0 deletions test/fixtures/block_gold.json
@@ -0,0 +1,56 @@
{
"valid": [
{
"description": "Block after fork",
"hash": "3b4489c87ee1ef257c2d2466aa6f5ac5dd6b5366daa4eeab558d48d8b09726c6",
"confirmations": 1,
"strippedsize": 1653,
"size": 1689,
"weight": 6648,
"height": 2642,
"version": 536870912,
"versionHex": "20000000",
"merkleroot": "75c336fe3deaa1cd3489a565d82515ad2e357bd8b7570d343059fbd67f944925",
"tx": [
"3df0fe004bf7edeee53b880fa067886d799e7c33a5fcf0a292e3e510cfd1b755",
"7d9aa686aabc37e19e1430c3ceb264b2b1ba7fcc65de6eb149c902187d201dbf"
],
"time": 1521884416,
"mediantime": 1521884100,
"nonceUint32": 3752984584,
"nonce": "00000cf3b5bff6d1c7e873105c2557ca719319b07578452c6f697bf7dfb20008",
"bits": "207fffff",
"difficulty": 1,
"chainwork": "00000000000000000000000000000000000000000000000000000000000014a6",
"previousblockhash": "57754aa42cbc3bdd14641cce8c706be52f805c00318e46260de465c90520f327",
"solution": "015e17d74197a1a5770fbd0db7249592b7830f12235c54747a19882740193d578f8a1181",
"hex": "0000002027f32005c965e40d26468e31005c802fe56b708cce1c6414dd3bbc2ca44a75572549947fd6fb5930340d57b7d87b352ead1525d865a58934cda1ea3dfe36c375520a000000000000000000000000000000000000000000000000000000000000001db65affff7f200800b2dff77b696f2c457875b0199371ca57255c1073e8c7d1f6bfb5f30c000024015e17d74197a1a5770fbd0db7249592b7830f12235c54747a19882740193d578f8a118102020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0502520a0101ffffffff0212fd000000000000232103707b1a27beefbe73af0ae987cf619aea035986086b359b67c3995f055ff46cf3ac0000000000000000266a24aa21a9edcf889e29fd0c274f50036881e4d7996f362caa1a751ae7b1cd93428a774485a20120000000000000000000000000000000000000000000000000000000000000000000000000020000000b3603cf17b3daba99f8212a49e745fe24fbe0694fb849775d96bad9a88eae7f4200000000494830450221009a423285612ce32196d0f41561def11e4d323d4944ff37e48f5fcc2b92dd31b002204ab16e67b7fae0b2bd19c5e4c8d60d8a909c725379ee1359c1a6b18b49835fc141feffffff458c691b57d73f1d6b70b146ebf6631ade07fa466b5fdc47c48ca8d05b12044a000000004847304402204b4e079c99a48af5146db5efb604b7562673db3c55cc65c15169b35c6c4a6122022059f2cdb74603ed407ea1159baab9c5c4d6d2debb51ce140023bf0aeb4d16338141feffffff86175df97b97b39464a2c7ad92a99600341d1aff0cf3a209edb645257a22d36d00000000494830450221008a1a684cbb342ae030891712cf9f47aa6b8141c413524979523b93d37c81bd82022076036843d119b2c39f60522aea5cb52706d9afb48b89660a03591597daee6d8c41feffffffaf54dd048f5f908f461c5999e341506487ece49c96e663fe31b702ce94a590e90000000049483045022100fd5144e1ddb525024861aa4121591a0fad6522ff2afc456536c00ad8dca6705c0220535932204d51463ae3c4c97af8c59b0845dfb539da8464be70ce67cf30b2caf341feffffffbe1a68dce296f3efa49a828f7e9e164234cc547703da71663f3ef64a1a7c06270000000048473044022052c4dde9d233f8149d8857f79fd3ae6315b9878a7eed9f694c968d4eecc855d50220476a17c0ef2efaee4469d6aaccc5447b1575c81d046d49929bc6e3b5f7c10bf041feffffffc0ded54f80488a1fe8dc595f54cbf96e787c994b380c041aea461c0c69d0d40e0000000049483045022100ed414e016e9dc5d569af473ef84a6fc15094c7c77e7c78c8a55dfe8a573f81c302201c04257cd23185e45578d4c4723f3018602306ad1b2bad91ae8a827f6184906b41feffffffd6f5665fa7c808d4e5f0f392b36aef52a18ad50fef2f6afa4f5ff53105235a4c00000000484730440220353e0011b8da92be6bdf9e8f84b657bb461ac5bd2aadd722253920e8dea5d6c1022051d533e36f2ddb63d4c4d8d2a56f81f32749b3d32076897bb97ff1a67a0b6fad41feffffffe9c7dc8a6eaad9aac5862e599e8c92c4955dabba7efb671f791377615dcdc4820000000048473044022100ca3398a3e21b15933f40edb5fa9eb1a1cfc6893bc04cd42abb4d418b4f1c82b8021f34b5d49e561fa53c850042622848d5db862a1f9663d75e92df5ba2e4d139d641feffffffea82d857ef36e4b9fee80dfdf8485539f829509ea157b83457d2587638af57300000000049483045022100ae501b0f8cd4ce2fb71cb4da168954f53e84cc6bf1f0e48c8f3d0b21344507e302206df3b2d05746caf1f194ac6909f41bf84fc9639ce8a4278f71a45dbae814c18341fefffffff12964d7dfa345e9fee8a973fc7e8cbd12e3ebd0233d45d139f0acb5def8b4720000000049483045022100dadf4be7dda52121261a547835ad03f9eb1ad1ae9a05bc3cfad8eddfcc67e31d0220120736f756e8f83573c282dfb2fea8c508480845110d31787e0b88a931a29e1541fefffffff314481de58fe4fc1035ced966ea1a67d8aa28fed9935326806d8c0da003d4460000000049483045022100a8778ce1b991e1c836b829db383d47f97a5a0184b624829ab3f11b4d505ccde9022046e20276fa80f886865703c50f071c4de56abc5bdb0368cebcff90e9ef5a20b241feffffff02c0833842000000001976a914c76cf02db3e9d93a94081980ea890e15f13cc56e88acaec60f00000000001976a9148b85fdd722cd20970f1d36d40a6f95d3a7c4776688ac510a0000"
},
{
"description": "Headers only",
"hash": "5e121efd987c4bff5ac88da58cdacbda2b29d0215b33df2628b974dccc0a9742",
"confirmations": 1,
"height": 2155,
"version": 536870912,
"versionHex": "20000000",
"merkleroot": "546c2616daaf1474589815fc9f3dafda08211b6744f9565d5019aad26ff494af",
"time": 1522052700,
"mediantime": 1521730196,
"nonceUint32": 4,
"nonce": "0000000000000000000000000000000000000000000000000000000000000004",
"solution": "",
"bits": "207fffff",
"difficulty": 4.656542373906925e-10,
"chainwork": "00000000000000000000000000000000000000000000000000000000000010d8",
"previousblockhash": "4e491178c5ad6d86c39ffe03bcb367a73158a7b3373e708c6257dfb39a47c366",
"hex": "0000002066c3479ab3df57628c703e37b3a75831a767b3bc03fe9fc3866dadc57811494eaf94f46fd2aa19505d56f944671b2108daaf3d9ffc1598587414afda16266c546b080000000000000000000000000000000000000000000000000000000000005caeb85affff7f2004000000000000000000000000000000000000000000000000000000000000000000"
}
],
"invalid": [
{
"exception": "Buffer too small \\(< 140 bytes\\)",
"hex": "020000003385c4b2a3499669987f5d04fa4127b59dbf2ee625694fa0bf08000000000000cf52f0eb"
}
]
}

0 comments on commit 74cb88e

Please sign in to comment.