Skip to content

Commit

Permalink
blockchain: delete putGenesis
Browse files Browse the repository at this point in the history
blockchain: more comments
blockchain: rename/remove some unused/confusing variables
  • Loading branch information
jochem-brouwer committed Oct 30, 2020
1 parent 67580b8 commit 652d9a1
Showing 1 changed file with 76 additions and 80 deletions.
156 changes: 76 additions & 80 deletions packages/blockchain/src/index.ts
Expand Up @@ -96,11 +96,16 @@ export default class Blockchain implements BlockchainInterface {
ethash?: Ethash

private _genesis?: Buffer // the genesis hash of this blockchain

// The following three head hashes (or maps) never point to a stale hash and always point to a hash in the canonical chain
// However, these might point to a different hash than the hash with the highest Total Difficulty, with the exception of the _headHeaderHash
// The _headHeaderHash always points to the head with the highest total difficulty.
private _headBlockHash?: Buffer // the hash of the current head block
private _headHeaderHash?: Buffer // the hash of the current head header
private _heads: { [key: string]: Buffer } // a Map which stores the head of each key (for instance the "vm" key)
// a Map which stores the head of each key (for instance the "vm" key)
// this is updated if you run the iterator method and can be used to (re)run non-verified blocks, for instance, in the VM.
private _heads: { [key: string]: Buffer }

private _initDone: boolean
public initPromise: Promise<void>
private _lock: Semaphore

Expand Down Expand Up @@ -144,7 +149,6 @@ export default class Blockchain implements BlockchainInterface {
this._heads = {}

this._lock = new Semaphore(1)
this._initDone = false

if (opts.genesisBlock && !opts.genesisBlock.isGenesis()) {
throw 'supplied block is not a genesis block'
Expand Down Expand Up @@ -247,7 +251,6 @@ export default class Blockchain implements BlockchainInterface {
}
this._headBlockHash = genesisHash
}
this._initDone = true
}

/**
Expand Down Expand Up @@ -289,18 +292,6 @@ export default class Blockchain implements BlockchainInterface {
return genesis
}

/**
* Puts the genesis block in the database
*
* @param genesis - The genesis block to be added
*/
async putGenesis(genesis: Block) {
if (!genesis.isGenesis()) {
throw new Error('Supplied block is not a genesis block')
}
await this.putBlock(genesis)
}

/**
* Returns the specified iterator head.
*
Expand Down Expand Up @@ -349,7 +340,8 @@ export default class Blockchain implements BlockchainInterface {

/**
* Adds many blocks to the blockchain.
*
* If any of the blocks is invalid, this function will throw; the blocks before this block will be put in the DB, but the blocks after will not be put in
* If any of these blocks has a higher total difficulty than the current max total difficulty, then the canonical chain is rebuilt and any stale heads/hashes are overwritten.
* @param blocks - The blocks to be added to the blockchain
*/
async putBlocks(blocks: Block[]) {
Expand All @@ -361,7 +353,7 @@ export default class Blockchain implements BlockchainInterface {

/**
* Adds a block to the blockchain.
*
* If this block is valid, and it has a higher total difficulty than the current max total difficulty, the canonical chain is rebuilt and any stale heads/hashes are overwritten.
* @param block - The block to be added to the blockchain
*/
async putBlock(block: Block) {
Expand All @@ -371,7 +363,8 @@ export default class Blockchain implements BlockchainInterface {

/**
* Adds many headers to the blockchain.
*
* If any of the headers is invalid, this function will throw; the headers before this header will be put in the DB, but the headers after will not be put in
* If any of these headers has a higher total difficulty than the current max total difficulty, then the canonical chain is rebuilt and any stale heads/hashes are overwritten.
* @param headers - The headers to be added to the blockchain
*/
async putHeaders(headers: Array<any>) {
Expand All @@ -383,7 +376,7 @@ export default class Blockchain implements BlockchainInterface {

/**
* Adds a header to the blockchain.
*
* If this header is valid, and it has a higher total difficulty than the current max total difficulty, the canonical chain is rebuilt and any stale heads/hashes are overwritten.
* @param header - The header to be added to the blockchain
*/
async putHeader(header: BlockHeader) {
Expand All @@ -392,6 +385,11 @@ export default class Blockchain implements BlockchainInterface {
}

/**
* Entry point for putting any block or block header. Verifies this block, checks the total TD: if this TD is higher than the current highest TD,
* we have thus found a new canonical block and have to rewrite the canonical chain. This also updates the head block hashes.
* If any of the older known canonical chains just became stale, then we also reset every _heads header which points to a stale header
* to the last verified header which was in the old canonical chain, but also in the new canonical chain.
* This thus rolls back these headers so that these can be updated to the "new" canonical header using the iterator method.
* @hidden
*/
async _putBlockOrHeader(item: Block | BlockHeader) {
Expand Down Expand Up @@ -428,55 +426,49 @@ export default class Blockchain implements BlockchainInterface {
}
}

if (!isGenesis) {
// set total difficulty in the current context scope
if (this._headHeaderHash) {
currentTd.header = await this._getTd(this._headHeaderHash)
}
if (this._headBlockHash) {
currentTd.block = await this._getTd(this._headBlockHash)
}

// calculate the total difficulty of the new block
const parentTd = await this._getTd(header.parentHash, blockNumber.subn(1))
td.iadd(parentTd)
// set total difficulty in the current context scope
if (this._headHeaderHash) {
currentTd.header = await this._getTd(this._headHeaderHash)
}
if (this._headBlockHash) {
currentTd.block = await this._getTd(this._headBlockHash)
}

const rebuildInfo = async () => {
// save block and total difficulty to the database
dbOps = dbOps.concat(DBSetTD(td, blockNumber, blockHash))
// calculate the total difficulty of the new block
const parentTd = await this._getTd(header.parentHash, blockNumber.subn(1))
td.iadd(parentTd)

// save header/block
dbOps = dbOps.concat(DBSetBlockOrHeader(block))
// save block and total difficulty to the database
dbOps = dbOps.concat(DBSetTD(td, blockNumber, blockHash))

// if total difficulty is higher than current, add it to canonical chain
if (block.isGenesis() || td.gt(currentTd.header)) {
this._headHeaderHash = blockHash
if (item instanceof Block) {
this._headBlockHash = blockHash
}
// save header/block
dbOps = dbOps.concat(DBSetBlockOrHeader(block))

// TODO SET THIS IN CONSTRUCTOR
if (block.isGenesis()) {
this._genesis = blockHash
}
// if total difficulty is higher than current, add it to canonical chain
if (block.isGenesis() || td.gt(currentTd.header)) {
this._headHeaderHash = blockHash
if (item instanceof Block) {
this._headBlockHash = blockHash
}

// delete higher number assignments and overwrite stale canonical chain
await this._deleteCanonicalChainReferences(blockNumber.addn(1), blockHash, dbOps)
// from the current header block, check the blockchain in reverse (i.e. traverse `parentHash`) until `numberToHash` matches the current number/hash in the canonical chain
// also: overwrite any heads if these heads are stale in `_heads` and `_headBlockHash`
await this._rebuildCanonical(header, dbOps)
} else {
// the TD is lower than the current highest TD so we will add the block to the DB, but will not mark it as the canonical chain.
if (td.gt(currentTd.block) && item instanceof Block) {
this._headBlockHash = blockHash
}
// save hash to number lookup info even if rebuild not needed
dbOps.push(DBSetHashToNumber(blockHash, blockNumber))
// TODO SET THIS IN CONSTRUCTOR
if (block.isGenesis()) {
this._genesis = blockHash
}
}

await rebuildInfo()
// delete higher number assignments and overwrite stale canonical chain
await this._deleteCanonicalChainReferences(blockNumber.addn(1), blockHash, dbOps)
// from the current header block, check the blockchain in reverse (i.e. traverse `parentHash`) until `numberToHash` matches the current number/hash in the canonical chain
// also: overwrite any heads if these heads are stale in `_heads` and `_headBlockHash`
await this._rebuildCanonical(header, dbOps)
} else {
// the TD is lower than the current highest TD so we will add the block to the DB, but will not mark it as the canonical chain.
if (td.gt(currentTd.block) && item instanceof Block) {
this._headBlockHash = blockHash
}
// save hash to number lookup info even if rebuild not needed
dbOps.push(DBSetHashToNumber(blockHash, blockNumber))
}

const ops = dbOps.concat(this._saveHeadOps())
await this.dbManager.batch(ops)
Expand Down Expand Up @@ -583,9 +575,11 @@ export default class Blockchain implements BlockchainInterface {
}

/**
* Completely deletes a block from the blockchain including any references to this block. All child blocks in the chain are deleted and any
* encountered heads are set to the parent block. If the block was in the canonical chain, also update the canonical chain and set the head of the canonical chain to the parent block.
*
* Completely deletes a block from the blockchain including any references to this block.
* If this block was in the canonical chain, then also each child block of this block is deleted
* Also, if this was a canonical block, each head header which is part of this now stale chain will be set to the parentHeader of this block
* An example reason to execute is when running the block in the VM invalidates this block: this will then reset the canonical head to the past block
* (which has been validated in the past by the VM, so we can be sure it is correct).
* @param blockHash - The hash of the block to be deleted
*/
async delBlock(blockHash: Buffer) {
Expand Down Expand Up @@ -767,43 +761,45 @@ export default class Blockchain implements BlockchainInterface {
}

/**
* Given a `header`, put all operations to change the canonical chain directly into `ops`. This
* Given a `header`, put all operations to change the canonical chain directly into `ops`.
* This walks the supplied `header` backwards. It is thus assumed that this header should be canonical header.
* For each header the corresponding hash corresponding to the current canonical chain in the DB is checked
* If the number => hash reference does not correspond to the reference in the DB, we overwrite this reference with the implied number => hash reference
* Also, each `_heads` member is checked; if these point to a stale hash, then the hash which we terminate the loop (i.e. the first hash which matches the number => hash of the implied chain)
* is put as this stale head hash
* The same happens to _headBlockHash
* @param header - The canonical header.
* @param ops - The database operations list.
* @hidden
*/
private async _rebuildCanonical(header: BlockHeader, ops: DatabaseOperation[]) {
const currentNumber = header.number.clone() // we change this during this method with `isubn`
let currentBlockHash: Buffer = header.hash()
let currentCanonicalHash: Buffer = header.hash()

// track the staleHash: this is the hash currently in the DB which matches the block number of the provided header.
let staleHash: Buffer | false = false
let staleHeads: string[] = []
let staleHeadBlock = false

const self = this
async function loopCondition() {
staleHash = await self.safeNumberToHash(currentNumber)
currentBlockHash = header.hash()
return !staleHash || !currentBlockHash.equals(staleHash)
const loopCondition = async () => {
staleHash = await this.safeNumberToHash(currentNumber)
currentCanonicalHash = header.hash()
return !staleHash || !currentCanonicalHash.equals(staleHash)
}

while (await loopCondition()) {
// handle genesis block
const blockHash = header.hash()
const blockNumber = header.number

if (blockNumber.isZero()) {
DBSaveLookups(blockHash, blockNumber).map((op) => {
ops.push(op)
})
break
}

DBSaveLookups(blockHash, blockNumber).map((op) => {
ops.push(op)
})

if (blockNumber.isZero()) {
break
}

// mark each key `_heads` which is currently set to the hash in the DB as stale to overwrite this later.
Object.keys(this._heads).forEach((name) => {
if (staleHash && this._heads[name].equals(staleHash)) {
Expand All @@ -829,11 +825,11 @@ export default class Blockchain implements BlockchainInterface {
// the stale hash is equal to the blockHash
// set stale heads to last previously valid canonical block
staleHeads.forEach((name: string) => {
this._heads[name] = currentBlockHash!
this._heads[name] = currentCanonicalHash
})
// set stale headBlock to last previously valid canonical block
if (staleHeadBlock) {
this._headBlockHash = currentBlockHash!
this._headBlockHash = currentCanonicalHash
}
}

Expand Down

0 comments on commit 652d9a1

Please sign in to comment.