Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structural Preparations for PoA Support #937

Merged
merged 6 commits into from
Nov 10, 2020
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
2 changes: 2 additions & 0 deletions packages/block/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
## 3.0.0-beta.2 - UNRELEASED

- Added `freeze` option to allow for block freeze deactivation (e.g. to allow for subclassing block and adding additional parameters), see PR [#941](https://github.com/ethereumjs/ethereumjs-vm/pull/941)
- **Breaking:** Difficulty-depending methods `canonicalDifficulty()` and `validateDifficulty()` in block and header now throw on non-PoW chains, see PR [#937](https://github.com/ethereumjs/ethereumjs-vm/pull/937)
- **Breaking:** Non-blockchain dependent validation checks have been extracted from `validate()` to its own `Block.validateData()` function. For the `validate()` method in block and header `blockchain` is now a mandatory parameter, see PR [#942](https://github.com/ethereumjs/ethereumjs-vm/pull/942)
- Fixed bug where block options have not been passed on to the main constructor from the static factory methods, see PR [#941](https://github.com/ethereumjs/ethereumjs-vm/pull/941)

## 3.0.0-beta.1 - 2020-10-22
Expand Down
24 changes: 18 additions & 6 deletions packages/block/src/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ export class Block {
}

/**
* Validates the transaction trie.
* Validates the transaction trie by generating a trie
* and do a check on the root hash.
*/
async validateTransactionsTrie(): Promise<boolean> {
if (this.transactions.length === 0) {
Expand All @@ -163,7 +164,7 @@ export class Block {
}

/**
* Validates the transactions.
* Validates transaction signatures and minimum gas requirements.
*
* @param stringError - If `true`, a string with the indices of the invalid txs is returned.
*/
Expand All @@ -184,7 +185,14 @@ export class Block {
}

/**
* Validates the block, throwing if invalid.
* Performs the following consistency checks on the block:
*
* - Value checks on the header fields
* - Signature and gasLimit validation for included txs
* - Validation of the tx trie
* - Consistency checks and header validation of included uncles
*
* Throws if invalid.
*
* @param blockchain - validate against a @ethereumjs/blockchain
*/
Expand Down Expand Up @@ -226,9 +234,12 @@ export class Block {
}

/**
* Validates the uncles that are in the block, if any. This method throws if they are invalid.
* Consistency checks and header validation for uncles included
* in the block, if any.
*
* Throws if invalid.
*
* @param blockchain - additionally validate against a @ethereumjs/blockchain
* @param blockchain - additionally validate against an @ethereumjs/blockchain instance
*/
async validateUncles(blockchain: Blockchain): Promise<void> {
if (this.isGenesis()) {
Expand Down Expand Up @@ -268,7 +279,8 @@ export class Block {
}

/**
* Validates the gasLimit.
* Validates if the block gasLimit remains in the
* boundaries set by the protocol.
*
* @param parentBlock - the parent of this `Block`
*/
Expand Down
18 changes: 15 additions & 3 deletions packages/block/src/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ export class BlockHeader {
* @param parentBlockHeader - the header from the parent `Block` of this header
*/
canonicalDifficulty(parentBlockHeader: BlockHeader): BN {
if (this._common.consensusType() !== 'pow') {
throw new Error('difficulty calculation is only supported on PoW chains')
}
if (this._common.consensusAlgorithm() !== 'ethash') {
throw new Error('difficulty calculation currently only supports the ethash algorithm')
}
const hardfork = this._getHardfork()
const blockTs = this.timestamp
const { timestamp: parentTs, difficulty: parentDif } = parentBlockHeader
Expand Down Expand Up @@ -351,11 +357,15 @@ export class BlockHeader {
* @param parentBlockHeader - the header from the parent `Block` of this header
*/
validateDifficulty(parentBlockHeader: BlockHeader): boolean {
if (this._common.consensusType() !== 'pow') {
throw new Error('difficulty validation is currently only supported on PoW chains')
}
return this.canonicalDifficulty(parentBlockHeader).eq(this.difficulty)
}

/**
* Validates the gasLimit.
* Validates if the block gasLimit remains in the
* boundaries set by the protocol.
*
* @param parentBlockHeader - the header from the parent `Block` of this header
*/
Expand Down Expand Up @@ -412,8 +422,10 @@ export class BlockHeader {
throw new Error('invalid timestamp')
}

if (!this.validateDifficulty(header)) {
throw new Error('invalid difficulty')
if (this._common.consensusType() === 'pow') {
if (!this.validateDifficulty(header)) {
throw new Error('invalid difficulty')
}
}

if (!this.validateGasLimit(header)) {
Expand Down
16 changes: 15 additions & 1 deletion packages/block/test/block.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ tape('[Block]: block functions', function (t) {

const testData = require('./testdata/testdata.json')

t.test('should test block validation', async function (st) {
t.test('should test block validation on pow chain', async function (st) {
const blockRlp = testData.blocks[0].rlp
const block = Block.fromRLPSerializedBlock(blockRlp)
const blockchain = new Mockchain()
Expand All @@ -80,6 +80,20 @@ tape('[Block]: block functions', function (t) {
})
})

t.test('should test block validation on poa chain', async function (st) {
const blockRlp = testData.blocks[0].rlp
const common = new Common({ chain: 'goerli' })
const block = Block.fromRLPSerializedBlock(blockRlp, { common })
const blockchain = new Mockchain()
await blockchain.putBlock(Block.fromRLPSerializedBlock(testData.genesisRLP))
try {
await block.validate(blockchain)
} catch (error) {
st.ok(error.toString().match(/block validation is currently only supported on PoW chains/))
}
st.end()
})

async function testTransactionValidation(st: tape.Test, block: Block) {
st.ok(block.validateTransactions())
st.ok(await block.validateTransactionsTrie())
Expand Down
1 change: 1 addition & 0 deletions packages/blockchain/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Genesis handling has been reworked to now be safer and reduce the risk of wiping
**Testing and CI**

- Dedicated `blockchain` reorg test setup and executable test, PR [#926](https://github.com/ethereumjs/ethereumjs-vm/pull/926)
- **Breaking:** `validatePow` option has been renamed to `validateConsensus` to prepare for a future integration of non-PoW (PoA) consensus mechanisms, `validateConsensus` as well as `validateBlocks` options now throw when set to `true` for validation on a non-PoW chain (determined by `Common`, e.g. 'goerli'), see PR [#937](https://github.com/ethereumjs/ethereumjs-vm/pull/937)

## 5.0.0-beta.1 - 2020-10-22

Expand Down
46 changes: 30 additions & 16 deletions packages/blockchain/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,19 @@ export interface BlockchainOptions {
db?: LevelUp

/**
* This flags indicates if Proof-of-work should be validated. Defaults to
* `true`.
* This flags indicates if a block should be validated along the consensus algorithm
* or protocol used by the chain, e.g. by verifying the PoW on the block.
*
* Supported: 'pow' with 'ethash' algorithm (taken from the `Common` instance)
* Default: `true`.
*/
validatePow?: boolean
validateConsensus?: boolean
holgerd77 marked this conversation as resolved.
Show resolved Hide resolved

/**
* This flags indicates if blocks should be validated. See Block#validate for
* details. If `validate` is provided, this option takes its value. If neither
* `validate` nor this option are provided, it defaults to `true`.
* This flag indicates if protocol-given consistency checks on
* block headers and included uncles and transactions should be performed,
* see Block#validate for details.
*
*/
validateBlocks?: boolean

Expand All @@ -91,7 +95,8 @@ export interface BlockchainOptions {
export default class Blockchain implements BlockchainInterface {
db: LevelUp
dbManager: DBManager
ethash?: Ethash

_ethash?: Ethash
holgerd77 marked this conversation as resolved.
Show resolved Hide resolved

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

Expand All @@ -110,7 +115,7 @@ export default class Blockchain implements BlockchainInterface {
private _lock: Semaphore

private _common: Common
private readonly _validatePow: boolean
private readonly _validateConsensus: boolean
private readonly _validateBlocks: boolean

/**
Expand All @@ -137,14 +142,21 @@ export default class Blockchain implements BlockchainInterface {
})
}

this._validatePow = opts.validatePow !== undefined ? opts.validatePow : true
this._validateBlocks = opts.validateBlocks !== undefined ? opts.validateBlocks : true
this._validateConsensus = opts.validateConsensus ?? true
this._validateBlocks = opts.validateBlocks ?? true

this.db = opts.db ? opts.db : level()
this.dbManager = new DBManager(this.db, this._common)

if (this._validatePow) {
this.ethash = new Ethash(this.db)
if (this._validateConsensus) {
if (this._common.consensusType() !== 'pow') {
holgerd77 marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('consensus validation only supported for pow chains')
}
if (this._common.consensusAlgorithm() !== 'ethash') {
throw new Error('consensus validation only supported for pow ethash algorithm')
}

this._ethash = new Ethash(this.db)
holgerd77 marked this conversation as resolved.
Show resolved Hide resolved
}

this._heads = {}
Expand Down Expand Up @@ -444,10 +456,12 @@ export default class Blockchain implements BlockchainInterface {
await block.validate(this)
}

if (this._validatePow && this.ethash) {
const valid = await this.ethash.verifyPOW(block)
if (!valid) {
throw new Error('invalid POW')
if (this._validateConsensus) {
if (this._ethash) {
const valid = await this._ethash.verifyPOW(block)
if (!valid) {
throw new Error('invalid POW')
}
}
}

Expand Down
Loading