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

Add getLatestHeader() and getLatestBlock() methods, fix saveHeads() bug and update API docs #52

Merged
merged 1 commit into from
May 24, 2018
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
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ new Blockchain({db: db}).iterator('i', (block, reorg, cb) => {
- [`BlockChain` methods](#blockchain-methods)
- [`blockchain.putGenesis(genesis, [cb])`](#blockchainputgenesisgenesis-cb)
- [`blockchain.getHead(name, [cb])`](#blockchaingetheadname-cb)
- [`blockchain.getLatestHeader([cb])`](#blockchaingetlatestheadercb)
- [`blockchain.getLatestBlock([cb])`](#blockchaingetlatestblockcb)
- [`blockchain.putBlocks(blocks, [cb])`](#blockchainputblocksblocks-cb)
- [`blockchain.putBlock(block, [cb])`](#blockchainputblockblock-cb)
- [`blockchain.getBlock(hash, [cb])`](#blockchaingetblockhash-cb)
Expand All @@ -50,7 +52,7 @@ new Blockchain({db: db}).iterator('i', (block, reorg, cb) => {
Implements functions for retrieving, manipulating and storing Ethereum's blockchain

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

Expand All @@ -69,12 +71,24 @@ Puts the genesis block in the database.
--------------------------------------------------------

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

--------------------------------------------------------

#### `blockchain.getLatestHeader(cb)`
Returns the latest header in the canonical chain.
- `cb` - the callback. It is given two parameters `err` and the returned `header`

--------------------------------------------------------

#### `blockchain.getLatestBlock(cb)`
Returns the latest full block in the canonical chain.
- `cb` - the callback. It is given two parameters `err` and the returned `block`

--------------------------------------------------------

#### `blockchain.putBlocks(blocks, cb)`
Adds many blocks to the blockchain.
- `blocks` - the blocks to be added to the blockchain
Expand All @@ -91,7 +105,7 @@ Adds a block to the blockchain.
#### `blockchain.getBlock(blockTag, cb)`
Gets a block by its blockTag.
- `blockTag` - the block's hash or number
- `cb` - the callback. It is given two parameters `err` and the found `block` (an instance of https://github.com/ethereumjs/ethereumjs-block) if any.
- `cb` - the callback. It is given two parameters `err` and the found `block` (an instance of https://github.com/ethereumjs/ethereumjs-block) if any.

--------------------------------------------------------

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

--------------------------------------------------------

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

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

80 changes: 64 additions & 16 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
'use strict'

/**
* NOTES
* meta.rawHead is the the head of the chain with the most POW
* meta.head is the head of the chain that has had its state root verifed
*/
const async = require('async')
const Stoplight = require('flow-stoplight')
const semaphore = require('semaphore')
Expand Down Expand Up @@ -86,8 +81,8 @@ Blockchain.prototype = {

/**
* Fetches the meta info about the blockchain from the db. Meta info contains
* hashes of the headerchain head, blockchain head, genesis block and verified
* state root heads.
* hashes of the headerchain head, blockchain head, genesis block and iterator
* heads.
* @method _init
*/
Blockchain.prototype._init = function (cb) {
Expand All @@ -113,8 +108,9 @@ Blockchain.prototype._init = function (cb) {
function getHeads (genesisHash, cb) {
self._genesis = genesisHash
async.series([
// load verified state root heads
// load verified iterator heads
(cb) => self.db.get('heads', {
keyEncoding: 'binary',
valueEncoding: 'json'
}, (err, heads) => {
if (err) heads = {}
Expand All @@ -124,13 +120,15 @@ Blockchain.prototype._init = function (cb) {
}),
// load headerchain head
(cb) => self.db.get(headHeaderKey, {
keyEncoding: 'binary',
valueEncoding: 'binary'
}, (err, hash) => {
self._headHeader = err ? genesisHash : hash
cb()
}),
// load blockchain head
(cb) => self.db.get(headBlockKey, {
keyEncoding: 'binary',
valueEncoding: 'binary'
}, (err, hash) => {
self._headBlock = err ? genesisHash : hash
Expand Down Expand Up @@ -161,8 +159,9 @@ Blockchain.prototype.putGenesis = function (genesis, cb) {
}

/**
* Returns that head block
* Returns the specified iterator head.
* @method getHead
* @param name name of the head (default: 'vm')
* @param cb Function the callback
*/
Blockchain.prototype.getHead = function (name, cb) {
Expand All @@ -185,6 +184,37 @@ Blockchain.prototype.getHead = function (name, cb) {
})
}

/**
* Returns the latest header in the canonical chain.
* @method getLatestHeader
* @param cb Function the callback
*/
Blockchain.prototype.getLatestHeader = function (cb) {
const self = this

// ensure init completed
self._initLock.await(function runGetLatestHeader () {
self.getBlock(self._headHeader, (err, block) => {
if (err) return cb(err)
cb(null, block.header)
})
})
}

/**
* Returns the latest full block in the canonical chain.
* @method getLatestBlock
* @param cb Function the callback
*/
Blockchain.prototype.getLatestBlock = function (cb) {
const self = this

// ensure init completed
self._initLock.await(function runGetLatestBlock () {
self.getBlock(self._headBlock, cb)
})
}

/**
* Adds many blocks to the blockchain
* @method putBlocks
Expand Down Expand Up @@ -518,9 +548,26 @@ Blockchain.prototype.selectNeededHashes = function (hashes, cb) {
}

Blockchain.prototype._saveHeads = function (cb) {
this.db.put('heads', this._heads, {
keyEncoding: 'json'
}, cb)
var dbOps = [{
type: 'put',
key: 'heads',
keyEncoding: 'binary',
valueEncoding: 'json',
value: this._heads
}, {
type: 'put',
key: headHeaderKey,
keyEncoding: 'binary',
valueEncoding: 'binary',
value: this._headHeader
}, {
type: 'put',
key: headBlockKey,
keyEncoding: 'binary',
valueEncoding: 'binary',
value: this._headBlock
}]
this._batchDbOps(dbOps, cb)
}

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

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

/**
* Iterates through blocks starting at the specified verified state root head
* and calls the onBlock function on each block
* Iterates through blocks starting at the specified iterator head and calls
* the onBlock function on each block. The current location of an iterator head
* can be retrieved using the `getHead()`` method
* @method iterator
* @param {String} name - the name of the verified state root head
* @param {String} name - the name of the iterator head
* @param {function} onBlock - function called on each block with params (block, reorg, cb)
* @param {function} cb - a callback function
*/
Expand Down
23 changes: 22 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const testData = require('./testdata.json')
const BN = require('bn.js')

test('blockchain test', function (t) {
t.plan(57)
t.plan(59)
var blockchain = new Blockchain()
var genesisBlock
var blocks = []
Expand Down Expand Up @@ -343,6 +343,27 @@ test('blockchain test', function (t) {
})
], done)
})
},
function saveHeads (done) {
var db = levelup('', { db: memdown })
var blockchain = new Blockchain({db: db, validate: false})

blockchain.putBlock(blocks[1], (err) => {
if (err) return done(err)
blockchain = new Blockchain({db: db, validate: false})
async.series([
(cb) => blockchain.getLatestHeader((err, header) => {
if (err) return done(err)
t.equals(header.hash().toString('hex'), blocks[1].hash().toString('hex'), 'should get latest header')
cb()
}),
(cb) => blockchain.getLatestBlock((err, headBlock) => {
if (err) return done(err)
t.equals(headBlock.hash().toString('hex'), blocks[1].hash().toString('hex'), 'should get latest block')
cb()
})
], done)
})
}
], function (err) {
if (err) {
Expand Down