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

Commit

Permalink
Add getLatestHeader() and getLatestBlock() methods, fix saveHeads() b…
Browse files Browse the repository at this point in the history
…ug and update API docs
  • Loading branch information
vpulim committed May 23, 2018
1 parent c4fca13 commit ebae528
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 22 deletions.
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])`](#blockchaingetlatestheader-cb)
- [`blockchain.getLatestBlock([cb])`](#blockchaingetlatestblock-cb)
- [`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

0 comments on commit ebae528

Please sign in to comment.