Skip to content
31 changes: 23 additions & 8 deletions packages/block/src/block/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import { Blob4844Tx, Capability } from '@ethereumjs/tx'
import {
BIGINT_0,
CLRequestType,
ErrorCode,
KECCAK256_RLP,
KECCAK256_RLP_ARRAY,
UsageError,
ValueError,
bytesToHex,
equalsBytes,
} from '@ethereumjs/util'
Expand Down Expand Up @@ -133,38 +136,50 @@ export class Block {
const msg = this._errorMsg(
'Block initialization with uncleHeaders on a PoA network is not allowed',
)
throw new Error(msg)
throw new UsageError(msg, ErrorCode.INVALID_OPTION_USAGE)
}
if (this.common.consensusType() === ConsensusType.ProofOfStake) {
const msg = this._errorMsg(
'Block initialization with uncleHeaders on a PoS network is not allowed',
)
throw new Error(msg)
throw new UsageError(msg, ErrorCode.INVALID_OPTION_USAGE)
}
}

if (!this.common.isActivatedEIP(4895) && withdrawals !== undefined) {
throw new Error('Cannot have a withdrawals field if EIP 4895 is not active')
throw new UsageError(
'Cannot have a withdrawals field if EIP 4895 is not active',
ErrorCode.EIP_NOT_ACTIVATED,
)
}

if (
!this.common.isActivatedEIP(6800) &&
executionWitness !== undefined &&
executionWitness !== null
) {
throw new Error(`Cannot have executionWitness field if EIP 6800 is not active `)
throw new UsageError(
`Cannot have executionWitness field if EIP 6800 is not active `,
ErrorCode.EIP_NOT_ACTIVATED,
)
}

if (!this.common.isActivatedEIP(7685) && requests !== undefined) {
throw new Error(`Cannot have requests field if EIP 7685 is not active`)
throw new UsageError(
`Cannot have requests field if EIP 7685 is not active`,
ErrorCode.EIP_NOT_ACTIVATED,
)
}

// Requests should be sorted in monotonically ascending order based on type
// and whatever internal sorting logic is defined by each request type
if (requests !== undefined && requests.length > 1) {
for (let x = 1; x < requests.length; x++) {
if (requests[x].type < requests[x - 1].type)
throw new Error('requests are not sorted in ascending order')
throw new ValueError(
'requests are not sorted in ascending order',
ErrorCode.INVALID_VALUE,
)
}
}
const freeze = opts?.freeze ?? true
Expand Down Expand Up @@ -250,7 +265,7 @@ export class Block {

async requestsTrieIsValid(requestsInput?: CLRequest<CLRequestType>[]): Promise<boolean> {
if (!this.common.isActivatedEIP(7685)) {
throw new Error('EIP 7685 is not activated')
throw new UsageError('EIP 7685 is not activated', ErrorCode.EIP_NOT_ACTIVATED)
}

const requests = requestsInput ?? this.requests!
Expand Down Expand Up @@ -459,7 +474,7 @@ export class Block {
*/
async withdrawalsTrieIsValid(): Promise<boolean> {
if (!this.common.isActivatedEIP(4895)) {
throw new Error('EIP 4895 is not activated')
throw new UsageError('EIP 4895 is not activated', ErrorCode.EIP_NOT_ACTIVATED)
}

let result
Expand Down
6 changes: 4 additions & 2 deletions packages/block/src/consensus/clique.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
Address,
BIGINT_0,
BIGINT_27,
ErrorCode,
UsageError,
bigIntToBytes,
bytesToBigInt,
concatBytes,
Expand All @@ -28,7 +30,7 @@ export function requireClique(header: BlockHeader, name: string) {
const msg = header['_errorMsg'](
`BlockHeader.${name}() call only supported for clique PoA networks`,
)
throw new Error(msg)
throw new UsageError(msg, ErrorCode.INVALID_METHOD_CALL)
}
}

Expand Down Expand Up @@ -84,7 +86,7 @@ export function cliqueEpochTransitionSigners(header: BlockHeader): Address[] {
requireClique(header, 'cliqueEpochTransitionSigners')
if (!cliqueIsEpochTransition(header)) {
const msg = header['_errorMsg']('Signers are only included in epoch transition blocks (clique)')
throw new Error(msg)
throw new UsageError(msg, ErrorCode.INVALID_METHOD_CALL)
}

const start = CLIQUE_EXTRA_VANITY
Expand Down
26 changes: 19 additions & 7 deletions packages/block/src/header/constructors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RLP } from '@ethereumjs/rlp'
import { bigIntToBytes, equalsBytes } from '@ethereumjs/util'
import { ErrorCode, ValueError, bigIntToBytes, equalsBytes } from '@ethereumjs/util'

import { generateCliqueBlockExtraData } from '../consensus/clique.js'
import { numberToHex, valuesArrayToHeaderData } from '../helpers.js'
Expand Down Expand Up @@ -34,22 +34,34 @@ export function createBlockHeaderFromBytesArray(values: BlockHeaderBytes, opts:
eip1559ActivationBlock !== undefined &&
equalsBytes(eip1559ActivationBlock, number as Uint8Array)
) {
throw new Error('invalid header. baseFeePerGas should be provided')
throw new ValueError(
'invalid header. baseFeePerGas should be provided',
ErrorCode.INVALID_VALUE,
)
}
}
if (header.common.isActivatedEIP(4844)) {
if (excessBlobGas === undefined) {
throw new Error('invalid header. excessBlobGas should be provided')
throw new ValueError(
'invalid header. excessBlobGas should be provided',
ErrorCode.INVALID_VALUE,
)
} else if (blobGasUsed === undefined) {
throw new Error('invalid header. blobGasUsed should be provided')
throw new ValueError(
'invalid header. blobGasUsed should be provided',
ErrorCode.INVALID_VALUE,
)
}
}
if (header.common.isActivatedEIP(4788) && parentBeaconBlockRoot === undefined) {
throw new Error('invalid header. parentBeaconBlockRoot should be provided')
throw new ValueError(
'invalid header. parentBeaconBlockRoot should be provided',
ErrorCode.INVALID_VALUE,
)
}

if (header.common.isActivatedEIP(7685) && requestsRoot === undefined) {
throw new Error('invalid header. requestsRoot should be provided')
throw new ValueError('invalid header. requestsRoot should be provided', ErrorCode.INVALID_VALUE)
}
return header
}
Expand All @@ -66,7 +78,7 @@ export function createBlockHeaderFromRLP(
) {
const values = RLP.decode(serializedHeaderData)
if (!Array.isArray(values)) {
throw new Error('Invalid serialized header input. Must be array')
throw new ValueError('Invalid serialized header input. Must be array', ErrorCode.INVALID_VALUE)
}
return createBlockHeaderFromBytesArray(values as Uint8Array[], opts)
}
Expand Down
76 changes: 51 additions & 25 deletions packages/block/src/header/header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import {
BIGINT_1,
BIGINT_2,
BIGINT_7,
ErrorCode,
KECCAK256_RLP,
KECCAK256_RLP_ARRAY,
TypeOutput,
UsageError,
ValueError,
bigIntToHex,
bigIntToUnpaddedBytes,
bytesToHex,
Expand Down Expand Up @@ -75,10 +78,13 @@ export class BlockHeader {
*/
get prevRandao() {
if (!this.common.isActivatedEIP(4399)) {
const msg = this._errorMsg(
throw new UsageError(
'The prevRandao parameter can only be accessed when EIP-4399 is activated',
ErrorCode.EIP_NOT_ACTIVATED,
{
objectContext: this.errorStr(),
},
)
throw new Error(msg)
}
return this.mixHash
}
Expand Down Expand Up @@ -179,7 +185,10 @@ export class BlockHeader {
toType(headerData.requestsRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.requestsRoot

if (!this.common.isActivatedEIP(1559) && baseFeePerGas !== undefined) {
throw new Error('A base fee for a block can only be set with EIP1559 being activated')
throw new UsageError(
'A base fee for a block can only be set with EIP1559 being activated',
ErrorCode.EIP_NOT_ACTIVATED,
)
}

if (!this.common.isActivatedEIP(4895) && withdrawalsRoot !== undefined) {
Expand Down Expand Up @@ -258,33 +267,41 @@ export class BlockHeader {
const { parentHash, stateRoot, transactionsTrie, receiptTrie, mixHash, nonce } = this

if (parentHash.length !== 32) {
const msg = this._errorMsg(`parentHash must be 32 bytes, received ${parentHash.length} bytes`)
throw new Error(msg)
throw new ValueError(`parentHash must be 32 bytes`, ErrorCode.INVALID_VALUE_LENGTH, {
objectContext: this.errorStr(),
received: `${parentHash.length} bytes`,
})
}
if (stateRoot.length !== 32) {
const msg = this._errorMsg(`stateRoot must be 32 bytes, received ${stateRoot.length} bytes`)
throw new Error(msg)
throw new ValueError(`stateRoot must be 32 bytes`, ErrorCode.INVALID_VALUE_LENGTH, {
objectContext: this.errorStr(),
received: `${stateRoot.length} bytes`,
})
}
if (transactionsTrie.length !== 32) {
const msg = this._errorMsg(
`transactionsTrie must be 32 bytes, received ${transactionsTrie.length} bytes`,
)
throw new Error(msg)
throw new ValueError('transactionsTrie must be 32 bytes', ErrorCode.INVALID_VALUE_LENGTH, {
objectContext: this.errorStr(),
received: `${bytesToHex(transactionsTrie)} (${transactionsTrie.length} bytes)`,
})
}
if (receiptTrie.length !== 32) {
const msg = this._errorMsg(
`receiptTrie must be 32 bytes, received ${receiptTrie.length} bytes`,
)
throw new Error(msg)
throw new ValueError('receiptTrie must be 32 bytes', ErrorCode.INVALID_VALUE_LENGTH, {
objectContext: this.errorStr(),
received: `${bytesToHex(receiptTrie)} (${receiptTrie.length} bytes)`,
})
}
if (mixHash.length !== 32) {
const msg = this._errorMsg(`mixHash must be 32 bytes, received ${mixHash.length} bytes`)
throw new Error(msg)
throw new ValueError('mixHash must be 32 bytes', ErrorCode.INVALID_VALUE_LENGTH, {
objectContext: this.errorStr(),
received: `${bytesToHex(mixHash)} (${mixHash.length} bytes)`,
})
}

if (nonce.length !== 8) {
const msg = this._errorMsg(`nonce must be 8 bytes, received ${nonce.length} bytes`)
throw new Error(msg)
throw new ValueError('nonce must be 8 bytes', ErrorCode.INVALID_VALUE_LENGTH, {
objectContext: this.errorStr(),
received: `${bytesToHex(nonce)} (${nonce.length} bytes)`,
})
}

// check if the block used too much gas
Expand Down Expand Up @@ -429,8 +446,9 @@ export class BlockHeader {
}
}
if (error) {
const msg = this._errorMsg(`Invalid PoS block: ${errorMsg}`)
throw new Error(msg)
throw new ValueError(`Invalid PoS block${errorMsg}`, ErrorCode.INVALID_OBJECT, {
objectContext: this.errorStr(),
})
}
}
}
Expand Down Expand Up @@ -659,14 +677,22 @@ export class BlockHeader {
*/
ethashCanonicalDifficulty(parentBlockHeader: BlockHeader): bigint {
if (this.common.consensusType() !== ConsensusType.ProofOfWork) {
const msg = this._errorMsg('difficulty calculation is only supported on PoW chains')
throw new Error(msg)
throw new UsageError(
'difficulty calculation is only supported on PoW chains',
ErrorCode.INVALID_METHOD_CALL,
{
objectContext: this.errorStr(),
},
)
}
if (this.common.consensusAlgorithm() !== ConsensusAlgorithm.Ethash) {
const msg = this._errorMsg(
throw new UsageError(
'difficulty calculation currently only supports the ethash algorithm',
ErrorCode.INVALID_METHOD_CALL,
{
objectContext: this.errorStr(),
},
)
throw new Error(msg)
}
const blockTs = this.timestamp
const { timestamp: parentTs, difficulty: parentDif } = parentBlockHeader
Expand Down
Loading