diff --git a/packages/block/src/header.ts b/packages/block/src/header.ts index c3870714cf..579b9c4b64 100644 --- a/packages/block/src/header.ts +++ b/packages/block/src/header.ts @@ -193,6 +193,7 @@ export class BlockHeader { ) } + const validateConsensusFormat = options.consensusFormatValidation ?? true const defaults = { parentHash: zeros(32), uncleHash: KECCAK256_RLP_ARRAY, @@ -297,7 +298,7 @@ export class BlockHeader { } // Validate consensus format after block is sealed (if applicable) so extraData checks will pass - this._consensusFormatValidation() + if (validateConsensusFormat === true) this._consensusFormatValidation() const freeze = options?.freeze ?? true if (freeze) { diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 049d5488e3..5ebbc19ef4 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -7,7 +7,6 @@ import type { TxData, } from '@ethereumjs/tx' import type { AddressLike, BigIntLike, BufferLike } from '@ethereumjs/util' - /** * An object to set to which blockchain the blocks and their headers belong. This could be specified * using a {@link Common} object, or `chain` and `hardfork`. Defaults to mainnet without specifying a @@ -69,6 +68,10 @@ export interface BlockOptions { * Will throw if provided on a non-PoA chain. */ cliqueSigner?: Buffer + /** + * Perform consensus validation checks on header if set. Defaults to true. + */ + consensusFormatValidation?: boolean } /** diff --git a/packages/block/test/header.spec.ts b/packages/block/test/header.spec.ts index af082ed9e9..53e3de37ec 100644 --- a/packages/block/test/header.spec.ts +++ b/packages/block/test/header.spec.ts @@ -290,6 +290,65 @@ tape('[Block]: Header functions', function (t) { st.end() }) + + t.test('should skip consensusFormatValidation if flag is set to false', (st) => { + const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) + const extraData = Buffer.concat([Buffer.alloc(1)]) + + try { + BlockHeader.fromHeaderData({ extraData }, { common, consensusFormatValidation: false }) + st.pass( + 'should instantiate header with invalid extraData when consensusFormatValidation === false' + ) + } catch (error: any) { + st.fail('should not throw') + } + + st.end() + }) + + t.test('_genericFormatValidation checks', (st) => { + const badHash = Buffer.alloc(31) + + st.throws( + () => BlockHeader.fromHeaderData({ parentHash: badHash }), + (err: any) => err.message.includes('parentHash must be 32 bytes'), + 'throws on invalid parent hash length' + ) + st.throws( + () => BlockHeader.fromHeaderData({ stateRoot: badHash }), + (err: any) => err.message.includes('stateRoot must be 32 bytes'), + 'throws on invalid state root hash length' + ) + st.throws( + () => BlockHeader.fromHeaderData({ transactionsTrie: badHash }), + (err: any) => err.message.includes('transactionsTrie must be 32 bytes'), + 'throws on invalid transactionsTrie root hash length' + ) + + st.throws( + () => BlockHeader.fromHeaderData({ nonce: Buffer.alloc(5) }), + (err: any) => err.message.includes('nonce must be 8 bytes'), + 'contains nonce length error message' + ) + + const kovanCommon = new Common({ chain: Chain.Kovan }) + st.throws( + () => BlockHeader.fromHeaderData({ nonce: Buffer.alloc(5) }, { common: kovanCommon }), + (err: any) => err.message.includes('nonce must be 65 bytes on kovan'), + 'contains kovan nonce error message' + ) + + st.doesNotThrow( + () => + BlockHeader.fromHeaderData( + { nonce: Buffer.alloc(65) }, + { common: kovanCommon, consensusFormatValidation: false } + ), + 'was able to create Kovan block with nonce 65 bytes long' + ) + st.end() + }) /* TODO: Decide if we need to move these tests to blockchain t.test('header validation -> poa checks', async function (st) {