UNPKG

4.03 kBJavaScriptView Raw
1var bufferutils = require('./bufferutils')
2var bcrypto = require('./crypto')
3var bufferReverse = require('buffer-reverse')
4var fastMerkleRoot = require('merkle-lib/fastRoot')
5var typeforce = require('typeforce')
6var types = require('./types')
7
8var Transaction = require('./transaction')
9
10function Block () {
11 this.version = 1
12 this.prevHash = null
13 this.merkleRoot = null
14 this.timestamp = 0
15 this.bits = 0
16 this.nonce = 0
17}
18
19Block.fromBuffer = function (buffer) {
20 if (buffer.length < 80) throw new Error('Buffer too small (< 80 bytes)')
21
22 var offset = 0
23 function readSlice (n) {
24 offset += n
25 return buffer.slice(offset - n, offset)
26 }
27
28 function readUInt32 () {
29 var i = buffer.readUInt32LE(offset)
30 offset += 4
31 return i
32 }
33
34 function readInt32 () {
35 var i = buffer.readInt32LE(offset)
36 offset += 4
37 return i
38 }
39
40 var block = new Block()
41 block.version = readInt32()
42 block.prevHash = readSlice(32)
43 block.merkleRoot = readSlice(32)
44 block.timestamp = readUInt32()
45 block.bits = readUInt32()
46 block.nonce = readUInt32()
47
48 if (buffer.length === 80) return block
49
50 function readVarInt () {
51 var vi = bufferutils.readVarInt(buffer, offset)
52 offset += vi.size
53 return vi.number
54 }
55
56 function readTransaction () {
57 var tx = Transaction.fromBuffer(buffer.slice(offset), true)
58
59 offset += tx.byteLength()
60 return tx
61 }
62
63 var nTransactions = readVarInt()
64 block.transactions = []
65
66 for (var i = 0; i < nTransactions; ++i) {
67 var tx = readTransaction()
68 block.transactions.push(tx)
69 }
70
71 return block
72}
73
74Block.fromHex = function (hex) {
75 return Block.fromBuffer(new Buffer(hex, 'hex'))
76}
77
78Block.prototype.getHash = function () {
79 return bcrypto.hash256(this.toBuffer(true))
80}
81
82Block.prototype.getId = function () {
83 return bufferReverse(this.getHash()).toString('hex')
84}
85
86Block.prototype.getUTCDate = function () {
87 var date = new Date(0) // epoch
88 date.setUTCSeconds(this.timestamp)
89
90 return date
91}
92
93Block.prototype.toBuffer = function (headersOnly) {
94 var buffer = new Buffer(80)
95
96 var offset = 0
97 function writeSlice (slice) {
98 slice.copy(buffer, offset)
99 offset += slice.length
100 }
101
102 function writeInt32 (i) {
103 buffer.writeInt32LE(i, offset)
104 offset += 4
105 }
106 function writeUInt32 (i) {
107 buffer.writeUInt32LE(i, offset)
108 offset += 4
109 }
110
111 writeInt32(this.version)
112 writeSlice(this.prevHash)
113 writeSlice(this.merkleRoot)
114 writeUInt32(this.timestamp)
115 writeUInt32(this.bits)
116 writeUInt32(this.nonce)
117
118 if (headersOnly || !this.transactions) return buffer
119
120 var txLenBuffer = bufferutils.varIntBuffer(this.transactions.length)
121 var txBuffers = this.transactions.map(function (tx) {
122 return tx.toBuffer()
123 })
124
125 return Buffer.concat([buffer, txLenBuffer].concat(txBuffers))
126}
127
128Block.prototype.toHex = function (headersOnly) {
129 return this.toBuffer(headersOnly).toString('hex')
130}
131
132Block.calculateTarget = function (bits) {
133 var exponent = ((bits & 0xff000000) >> 24) - 3
134 var mantissa = bits & 0x007fffff
135 var i = 31 - exponent
136
137 var target = new Buffer(32)
138 target.fill(0)
139
140 target[i] = mantissa & 0xff
141 target[i - 1] = mantissa >> 8
142 target[i - 2] = mantissa >> 16
143 target[i - 3] = mantissa >> 24
144
145 return target
146}
147
148Block.calculateMerkleRoot = function (transactions) {
149 typeforce([{ getHash: types.Function }], transactions)
150 if (transactions.length === 0) throw TypeError('Cannot compute merkle root for zero transactions')
151
152 var hashes = transactions.map(function (transaction) {
153 return transaction.getHash()
154 })
155
156 return fastMerkleRoot(hashes, bcrypto.hash256)
157}
158
159Block.prototype.checkMerkleRoot = function () {
160 if (!this.transactions) return false
161
162 var actualMerkleRoot = Block.calculateMerkleRoot(this.transactions)
163 return this.merkleRoot.compare(actualMerkleRoot) === 0
164}
165
166Block.prototype.checkProofOfWork = function () {
167 var hash = bufferReverse(this.getHash())
168 var target = Block.calculateTarget(this.bits)
169
170 return hash.compare(target) <= 0
171}
172
173module.exports = Block