Skip to content
This repository was archived by the owner on Apr 6, 2020. It is now read-only.

Commit 178acbd

Browse files
authored
Merge pull request #52 from vpulim/fix-head-saving
Add getLatestHeader() and getLatestBlock() methods, fix saveHeads() bug and update API docs
2 parents c4fca13 + fe0c8e8 commit 178acbd

File tree

3 files changed

+104
-22
lines changed

3 files changed

+104
-22
lines changed

README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ new Blockchain({db: db}).iterator('i', (block, reorg, cb) => {
3838
- [`BlockChain` methods](#blockchain-methods)
3939
- [`blockchain.putGenesis(genesis, [cb])`](#blockchainputgenesisgenesis-cb)
4040
- [`blockchain.getHead(name, [cb])`](#blockchaingetheadname-cb)
41+
- [`blockchain.getLatestHeader([cb])`](#blockchaingetlatestheadercb)
42+
- [`blockchain.getLatestBlock([cb])`](#blockchaingetlatestblockcb)
4143
- [`blockchain.putBlocks(blocks, [cb])`](#blockchainputblocksblocks-cb)
4244
- [`blockchain.putBlock(block, [cb])`](#blockchainputblockblock-cb)
4345
- [`blockchain.getBlock(hash, [cb])`](#blockchaingetblockhash-cb)
@@ -50,7 +52,7 @@ new Blockchain({db: db}).iterator('i', (block, reorg, cb) => {
5052
Implements functions for retrieving, manipulating and storing Ethereum's blockchain
5153

5254
### `new Blockchain(opts)`
53-
Creates new Blockchain object
55+
Creates new Blockchain object
5456
- `opts.db` - Database to store blocks and metadata. Should be a [levelup](https://github.com/rvagg/node-levelup) instance.
5557
- `opts.validate` - this the flag to validate blocks (e.g. Proof-of-Work).
5658

@@ -69,12 +71,24 @@ Puts the genesis block in the database.
6971
--------------------------------------------------------
7072

7173
#### `blockchain.getHead(name, cb)`
72-
Returns that head block.
74+
Returns the specified iterator head.
7375
- `name` - Optional name of the state root head (default: 'vm')
7476
- `cb` - the callback. It is given two parameters `err` and the returned `block`
7577

7678
--------------------------------------------------------
7779

80+
#### `blockchain.getLatestHeader(cb)`
81+
Returns the latest header in the canonical chain.
82+
- `cb` - the callback. It is given two parameters `err` and the returned `header`
83+
84+
--------------------------------------------------------
85+
86+
#### `blockchain.getLatestBlock(cb)`
87+
Returns the latest full block in the canonical chain.
88+
- `cb` - the callback. It is given two parameters `err` and the returned `block`
89+
90+
--------------------------------------------------------
91+
7892
#### `blockchain.putBlocks(blocks, cb)`
7993
Adds many blocks to the blockchain.
8094
- `blocks` - the blocks to be added to the blockchain
@@ -91,7 +105,7 @@ Adds a block to the blockchain.
91105
#### `blockchain.getBlock(blockTag, cb)`
92106
Gets a block by its blockTag.
93107
- `blockTag` - the block's hash or number
94-
- `cb` - the callback. It is given two parameters `err` and the found `block` (an instance of https://github.com/ethereumjs/ethereumjs-block) if any.
108+
- `cb` - the callback. It is given two parameters `err` and the found `block` (an instance of https://github.com/ethereumjs/ethereumjs-block) if any.
95109

96110
--------------------------------------------------------
97111

@@ -113,7 +127,7 @@ Looks up many blocks relative to blockId.
113127
#### `blockchain.selectNeededHashes(hashes, cb)`
114128
Given an ordered array, returns to the callback an array of hashes that are not in the blockchain yet.
115129
- `hashes` - Ordered array of hashes
116-
- `cb` - the callback. It is given two parameters `err` and hashes found.
130+
- `cb` - the callback. It is given two parameters `err` and hashes found.
117131

118132
--------------------------------------------------------
119133

@@ -135,4 +149,3 @@ Iterates through blocks starting at the specified verified state root head and c
135149
Tests can be found in the ``test`` directory and run with ``npm run test``.
136150

137151
These can also be valuable as examples/inspiration on how to run the library and invoke different parts of the API.
138-

index.js

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
'use strict'
22

3-
/**
4-
* NOTES
5-
* meta.rawHead is the the head of the chain with the most POW
6-
* meta.head is the head of the chain that has had its state root verifed
7-
*/
83
const async = require('async')
94
const Stoplight = require('flow-stoplight')
105
const semaphore = require('semaphore')
@@ -86,8 +81,8 @@ Blockchain.prototype = {
8681

8782
/**
8883
* Fetches the meta info about the blockchain from the db. Meta info contains
89-
* hashes of the headerchain head, blockchain head, genesis block and verified
90-
* state root heads.
84+
* hashes of the headerchain head, blockchain head, genesis block and iterator
85+
* heads.
9186
* @method _init
9287
*/
9388
Blockchain.prototype._init = function (cb) {
@@ -113,8 +108,9 @@ Blockchain.prototype._init = function (cb) {
113108
function getHeads (genesisHash, cb) {
114109
self._genesis = genesisHash
115110
async.series([
116-
// load verified state root heads
111+
// load verified iterator heads
117112
(cb) => self.db.get('heads', {
113+
keyEncoding: 'binary',
118114
valueEncoding: 'json'
119115
}, (err, heads) => {
120116
if (err) heads = {}
@@ -124,13 +120,15 @@ Blockchain.prototype._init = function (cb) {
124120
}),
125121
// load headerchain head
126122
(cb) => self.db.get(headHeaderKey, {
123+
keyEncoding: 'binary',
127124
valueEncoding: 'binary'
128125
}, (err, hash) => {
129126
self._headHeader = err ? genesisHash : hash
130127
cb()
131128
}),
132129
// load blockchain head
133130
(cb) => self.db.get(headBlockKey, {
131+
keyEncoding: 'binary',
134132
valueEncoding: 'binary'
135133
}, (err, hash) => {
136134
self._headBlock = err ? genesisHash : hash
@@ -161,8 +159,9 @@ Blockchain.prototype.putGenesis = function (genesis, cb) {
161159
}
162160

163161
/**
164-
* Returns that head block
162+
* Returns the specified iterator head.
165163
* @method getHead
164+
* @param name name of the head (default: 'vm')
166165
* @param cb Function the callback
167166
*/
168167
Blockchain.prototype.getHead = function (name, cb) {
@@ -185,6 +184,37 @@ Blockchain.prototype.getHead = function (name, cb) {
185184
})
186185
}
187186

187+
/**
188+
* Returns the latest header in the canonical chain.
189+
* @method getLatestHeader
190+
* @param cb Function the callback
191+
*/
192+
Blockchain.prototype.getLatestHeader = function (cb) {
193+
const self = this
194+
195+
// ensure init completed
196+
self._initLock.await(function runGetLatestHeader () {
197+
self.getBlock(self._headHeader, (err, block) => {
198+
if (err) return cb(err)
199+
cb(null, block.header)
200+
})
201+
})
202+
}
203+
204+
/**
205+
* Returns the latest full block in the canonical chain.
206+
* @method getLatestBlock
207+
* @param cb Function the callback
208+
*/
209+
Blockchain.prototype.getLatestBlock = function (cb) {
210+
const self = this
211+
212+
// ensure init completed
213+
self._initLock.await(function runGetLatestBlock () {
214+
self.getBlock(self._headBlock, cb)
215+
})
216+
}
217+
188218
/**
189219
* Adds many blocks to the blockchain
190220
* @method putBlocks
@@ -518,9 +548,26 @@ Blockchain.prototype.selectNeededHashes = function (hashes, cb) {
518548
}
519549

520550
Blockchain.prototype._saveHeads = function (cb) {
521-
this.db.put('heads', this._heads, {
522-
keyEncoding: 'json'
523-
}, cb)
551+
var dbOps = [{
552+
type: 'put',
553+
key: 'heads',
554+
keyEncoding: 'binary',
555+
valueEncoding: 'json',
556+
value: this._heads
557+
}, {
558+
type: 'put',
559+
key: headHeaderKey,
560+
keyEncoding: 'binary',
561+
valueEncoding: 'binary',
562+
value: this._headHeader
563+
}, {
564+
type: 'put',
565+
key: headBlockKey,
566+
keyEncoding: 'binary',
567+
valueEncoding: 'binary',
568+
value: this._headBlock
569+
}]
570+
this._batchDbOps(dbOps, cb)
524571
}
525572

526573
// delete canonical number assignments for specified number and above
@@ -537,7 +584,7 @@ Blockchain.prototype._deleteStaleAssignments = function (number, headHash, ops,
537584
})
538585
self._cache.numberToHash.del(key)
539586

540-
// reset stale verified state root heads to current canonical head
587+
// reset stale iterator heads to current canonical head
541588
Object.keys(self._heads).forEach(function (name) {
542589
if (self._heads[name].equals(hash)) {
543590
self._heads[name] = headHash
@@ -751,10 +798,11 @@ Blockchain.prototype._delChild = function (hash, number, headHash, ops, cb) {
751798
}
752799

753800
/**
754-
* Iterates through blocks starting at the specified verified state root head
755-
* and calls the onBlock function on each block
801+
* Iterates through blocks starting at the specified iterator head and calls
802+
* the onBlock function on each block. The current location of an iterator head
803+
* can be retrieved using the `getHead()`` method
756804
* @method iterator
757-
* @param {String} name - the name of the verified state root head
805+
* @param {String} name - the name of the iterator head
758806
* @param {function} onBlock - function called on each block with params (block, reorg, cb)
759807
* @param {function} cb - a callback function
760808
*/

test/index.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const testData = require('./testdata.json')
1111
const BN = require('bn.js')
1212

1313
test('blockchain test', function (t) {
14-
t.plan(57)
14+
t.plan(59)
1515
var blockchain = new Blockchain()
1616
var genesisBlock
1717
var blocks = []
@@ -343,6 +343,27 @@ test('blockchain test', function (t) {
343343
})
344344
], done)
345345
})
346+
},
347+
function saveHeads (done) {
348+
var db = levelup('', { db: memdown })
349+
var blockchain = new Blockchain({db: db, validate: false})
350+
351+
blockchain.putBlock(blocks[1], (err) => {
352+
if (err) return done(err)
353+
blockchain = new Blockchain({db: db, validate: false})
354+
async.series([
355+
(cb) => blockchain.getLatestHeader((err, header) => {
356+
if (err) return done(err)
357+
t.equals(header.hash().toString('hex'), blocks[1].hash().toString('hex'), 'should get latest header')
358+
cb()
359+
}),
360+
(cb) => blockchain.getLatestBlock((err, headBlock) => {
361+
if (err) return done(err)
362+
t.equals(headBlock.hash().toString('hex'), blocks[1].hash().toString('hex'), 'should get latest block')
363+
cb()
364+
})
365+
], done)
366+
})
346367
}
347368
], function (err) {
348369
if (err) {

0 commit comments

Comments
 (0)