@feelyourprotocol/block 8141.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/README.md +466 -0
- package/dist/cjs/block/block.d.ts +147 -0
- package/dist/cjs/block/block.d.ts.map +1 -0
- package/dist/cjs/block/block.js +415 -0
- package/dist/cjs/block/block.js.map +1 -0
- package/dist/cjs/block/constructors.d.ts +77 -0
- package/dist/cjs/block/constructors.d.ts.map +1 -0
- package/dist/cjs/block/constructors.js +298 -0
- package/dist/cjs/block/constructors.js.map +1 -0
- package/dist/cjs/block/index.d.ts +3 -0
- package/dist/cjs/block/index.d.ts.map +1 -0
- package/dist/cjs/block/index.js +19 -0
- package/dist/cjs/block/index.js.map +1 -0
- package/dist/cjs/consensus/clique.d.ts +52 -0
- package/dist/cjs/consensus/clique.d.ts.map +1 -0
- package/dist/cjs/consensus/clique.js +144 -0
- package/dist/cjs/consensus/clique.js.map +1 -0
- package/dist/cjs/consensus/ethash.d.ts +9 -0
- package/dist/cjs/consensus/ethash.d.ts.map +1 -0
- package/dist/cjs/consensus/ethash.js +13 -0
- package/dist/cjs/consensus/ethash.js.map +1 -0
- package/dist/cjs/consensus/index.d.ts +3 -0
- package/dist/cjs/consensus/index.d.ts.map +1 -0
- package/dist/cjs/consensus/index.js +29 -0
- package/dist/cjs/consensus/index.js.map +1 -0
- package/dist/cjs/from-beacon-payload.d.ts +36 -0
- package/dist/cjs/from-beacon-payload.d.ts.map +1 -0
- package/dist/cjs/from-beacon-payload.js +48 -0
- package/dist/cjs/from-beacon-payload.js.map +1 -0
- package/dist/cjs/header/constructors.d.ts +39 -0
- package/dist/cjs/header/constructors.d.ts.map +1 -0
- package/dist/cjs/header/constructors.js +127 -0
- package/dist/cjs/header/constructors.js.map +1 -0
- package/dist/cjs/header/header.d.ts +134 -0
- package/dist/cjs/header/header.d.ts.map +1 -0
- package/dist/cjs/header/header.js +699 -0
- package/dist/cjs/header/header.js.map +1 -0
- package/dist/cjs/header/index.d.ts +3 -0
- package/dist/cjs/header/index.d.ts.map +1 -0
- package/dist/cjs/header/index.js +19 -0
- package/dist/cjs/header/index.js.map +1 -0
- package/dist/cjs/helpers.d.ts +59 -0
- package/dist/cjs/helpers.d.ts.map +1 -0
- package/dist/cjs/helpers.js +172 -0
- package/dist/cjs/helpers.js.map +1 -0
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +31 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/params.d.ts +3 -0
- package/dist/cjs/params.d.ts.map +1 -0
- package/dist/cjs/params.js +97 -0
- package/dist/cjs/params.js.map +1 -0
- package/dist/cjs/types.d.ts +228 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/esm/block/block.d.ts +147 -0
- package/dist/esm/block/block.d.ts.map +1 -0
- package/dist/esm/block/block.js +411 -0
- package/dist/esm/block/block.js.map +1 -0
- package/dist/esm/block/constructors.d.ts +77 -0
- package/dist/esm/block/constructors.d.ts.map +1 -0
- package/dist/esm/block/constructors.js +286 -0
- package/dist/esm/block/constructors.js.map +1 -0
- package/dist/esm/block/index.d.ts +3 -0
- package/dist/esm/block/index.d.ts.map +1 -0
- package/dist/esm/block/index.js +3 -0
- package/dist/esm/block/index.js.map +1 -0
- package/dist/esm/consensus/clique.d.ts +52 -0
- package/dist/esm/consensus/clique.d.ts.map +1 -0
- package/dist/esm/consensus/clique.js +132 -0
- package/dist/esm/consensus/clique.js.map +1 -0
- package/dist/esm/consensus/ethash.d.ts +9 -0
- package/dist/esm/consensus/ethash.d.ts.map +1 -0
- package/dist/esm/consensus/ethash.js +10 -0
- package/dist/esm/consensus/ethash.js.map +1 -0
- package/dist/esm/consensus/index.d.ts +3 -0
- package/dist/esm/consensus/index.d.ts.map +1 -0
- package/dist/esm/consensus/index.js +3 -0
- package/dist/esm/consensus/index.js.map +1 -0
- package/dist/esm/from-beacon-payload.d.ts +36 -0
- package/dist/esm/from-beacon-payload.d.ts.map +1 -0
- package/dist/esm/from-beacon-payload.js +45 -0
- package/dist/esm/from-beacon-payload.js.map +1 -0
- package/dist/esm/header/constructors.d.ts +39 -0
- package/dist/esm/header/constructors.d.ts.map +1 -0
- package/dist/esm/header/constructors.js +120 -0
- package/dist/esm/header/constructors.js.map +1 -0
- package/dist/esm/header/header.d.ts +134 -0
- package/dist/esm/header/header.d.ts.map +1 -0
- package/dist/esm/header/header.js +695 -0
- package/dist/esm/header/header.js.map +1 -0
- package/dist/esm/header/index.d.ts +3 -0
- package/dist/esm/header/index.d.ts.map +1 -0
- package/dist/esm/header/index.js +3 -0
- package/dist/esm/header/index.js.map +1 -0
- package/dist/esm/helpers.d.ts +59 -0
- package/dist/esm/helpers.d.ts.map +1 -0
- package/dist/esm/helpers.js +161 -0
- package/dist/esm/helpers.js.map +1 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/params.d.ts +3 -0
- package/dist/esm/params.d.ts.map +1 -0
- package/dist/esm/params.js +94 -0
- package/dist/esm/params.js.map +1 -0
- package/dist/esm/types.d.ts +228 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
- package/package.json +75 -0
- package/src/block/block.ts +526 -0
- package/src/block/constructors.ts +407 -0
- package/src/block/index.ts +2 -0
- package/src/consensus/clique.ts +171 -0
- package/src/consensus/ethash.ts +11 -0
- package/src/consensus/index.ts +12 -0
- package/src/from-beacon-payload.ts +82 -0
- package/src/header/constructors.ts +169 -0
- package/src/header/header.ts +890 -0
- package/src/header/index.ts +2 -0
- package/src/helpers.ts +223 -0
- package/src/index.ts +13 -0
- package/src/params.ts +95 -0
- package/src/types.ts +254 -0
|
@@ -0,0 +1,890 @@
|
|
|
1
|
+
import { Common, ConsensusAlgorithm, ConsensusType, Hardfork, Mainnet } from '@feelyourprotocol/common'
|
|
2
|
+
import { RLP } from '@feelyourprotocol/rlp'
|
|
3
|
+
import {
|
|
4
|
+
Address,
|
|
5
|
+
BIGINT_0,
|
|
6
|
+
BIGINT_1,
|
|
7
|
+
BIGINT_2,
|
|
8
|
+
BIGINT_7,
|
|
9
|
+
EthereumJSErrorWithoutCode,
|
|
10
|
+
KECCAK256_RLP,
|
|
11
|
+
KECCAK256_RLP_ARRAY,
|
|
12
|
+
SHA256_NULL,
|
|
13
|
+
TypeOutput,
|
|
14
|
+
bigIntToHex,
|
|
15
|
+
bigIntToUnpaddedBytes,
|
|
16
|
+
bytesToHex,
|
|
17
|
+
bytesToUtf8,
|
|
18
|
+
createZeroAddress,
|
|
19
|
+
equalsBytes,
|
|
20
|
+
hexToBytes,
|
|
21
|
+
toType,
|
|
22
|
+
} from '@feelyourprotocol/util'
|
|
23
|
+
import { keccak_256 } from '@noble/hashes/sha3.js'
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
CLIQUE_EXTRA_SEAL,
|
|
27
|
+
CLIQUE_EXTRA_VANITY,
|
|
28
|
+
cliqueIsEpochTransition,
|
|
29
|
+
} from '../consensus/clique.ts'
|
|
30
|
+
import { computeBlobGasPrice } from '../helpers.ts'
|
|
31
|
+
import { paramsBlock } from '../params.ts'
|
|
32
|
+
|
|
33
|
+
import type { BlockHeaderBytes, BlockOptions, HeaderData, JSONHeader } from '../types.ts'
|
|
34
|
+
|
|
35
|
+
interface HeaderCache {
|
|
36
|
+
hash: Uint8Array | undefined
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEFAULT_GAS_LIMIT = BigInt('0xffffffffffffff')
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* An object that represents the block header.
|
|
43
|
+
*/
|
|
44
|
+
export class BlockHeader {
|
|
45
|
+
public readonly parentHash: Uint8Array
|
|
46
|
+
public readonly uncleHash: Uint8Array
|
|
47
|
+
public readonly coinbase: Address
|
|
48
|
+
public readonly stateRoot: Uint8Array
|
|
49
|
+
public readonly transactionsTrie: Uint8Array
|
|
50
|
+
public readonly receiptTrie: Uint8Array
|
|
51
|
+
public readonly logsBloom: Uint8Array
|
|
52
|
+
public readonly difficulty: bigint
|
|
53
|
+
public readonly number: bigint
|
|
54
|
+
public readonly gasLimit: bigint
|
|
55
|
+
public readonly gasUsed: bigint
|
|
56
|
+
public readonly timestamp: bigint
|
|
57
|
+
public readonly extraData: Uint8Array
|
|
58
|
+
public readonly mixHash: Uint8Array
|
|
59
|
+
public readonly nonce: Uint8Array
|
|
60
|
+
public readonly baseFeePerGas?: bigint
|
|
61
|
+
public readonly withdrawalsRoot?: Uint8Array
|
|
62
|
+
public readonly blobGasUsed?: bigint
|
|
63
|
+
public readonly excessBlobGas?: bigint
|
|
64
|
+
public readonly parentBeaconBlockRoot?: Uint8Array
|
|
65
|
+
public readonly requestsHash?: Uint8Array
|
|
66
|
+
public readonly blockAccessListHash?: Uint8Array
|
|
67
|
+
public readonly slotNumber?: bigint
|
|
68
|
+
|
|
69
|
+
public readonly common: Common
|
|
70
|
+
|
|
71
|
+
protected keccakFunction: (msg: Uint8Array) => Uint8Array
|
|
72
|
+
|
|
73
|
+
protected cache: HeaderCache = {
|
|
74
|
+
hash: undefined,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* EIP-4399: After merge to PoS, `mixHash` supplanted as `prevRandao`
|
|
79
|
+
*/
|
|
80
|
+
get prevRandao() {
|
|
81
|
+
if (!this.common.isActivatedEIP(4399)) {
|
|
82
|
+
const msg = this._errorMsg(
|
|
83
|
+
'The prevRandao parameter can only be accessed when EIP-4399 is activated',
|
|
84
|
+
)
|
|
85
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
86
|
+
}
|
|
87
|
+
return this.mixHash
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* This constructor takes the values, validates them, assigns them and freezes the object.
|
|
92
|
+
*
|
|
93
|
+
* @deprecated Use the public static factory methods to assist in creating a Header object from
|
|
94
|
+
* varying data types. For a default empty header, use {@link createBlockHeader}.
|
|
95
|
+
*
|
|
96
|
+
*/
|
|
97
|
+
constructor(headerData: HeaderData, opts: BlockOptions = {}) {
|
|
98
|
+
if (opts.common) {
|
|
99
|
+
this.common = opts.common.copy()
|
|
100
|
+
} else {
|
|
101
|
+
this.common = new Common({
|
|
102
|
+
chain: Mainnet, // default
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
this.common.updateParams(opts.params ?? paramsBlock)
|
|
106
|
+
|
|
107
|
+
this.keccakFunction = this.common.customCrypto.keccak256 ?? keccak_256
|
|
108
|
+
|
|
109
|
+
const skipValidateConsensusFormat = opts.skipConsensusFormatValidation ?? false
|
|
110
|
+
|
|
111
|
+
const defaults = {
|
|
112
|
+
parentHash: new Uint8Array(32),
|
|
113
|
+
uncleHash: KECCAK256_RLP_ARRAY,
|
|
114
|
+
coinbase: createZeroAddress(),
|
|
115
|
+
stateRoot: new Uint8Array(32),
|
|
116
|
+
transactionsTrie: KECCAK256_RLP,
|
|
117
|
+
receiptTrie: KECCAK256_RLP,
|
|
118
|
+
logsBloom: new Uint8Array(256),
|
|
119
|
+
difficulty: BIGINT_0,
|
|
120
|
+
number: BIGINT_0,
|
|
121
|
+
gasLimit: DEFAULT_GAS_LIMIT,
|
|
122
|
+
gasUsed: BIGINT_0,
|
|
123
|
+
timestamp: BIGINT_0,
|
|
124
|
+
extraData: new Uint8Array(0),
|
|
125
|
+
mixHash: new Uint8Array(32),
|
|
126
|
+
nonce: new Uint8Array(8),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const parentHash = toType(headerData.parentHash, TypeOutput.Uint8Array) ?? defaults.parentHash
|
|
130
|
+
const uncleHash = toType(headerData.uncleHash, TypeOutput.Uint8Array) ?? defaults.uncleHash
|
|
131
|
+
const coinbase = new Address(
|
|
132
|
+
toType(headerData.coinbase ?? defaults.coinbase, TypeOutput.Uint8Array),
|
|
133
|
+
)
|
|
134
|
+
const stateRoot = toType(headerData.stateRoot, TypeOutput.Uint8Array) ?? defaults.stateRoot
|
|
135
|
+
const transactionsTrie =
|
|
136
|
+
toType(headerData.transactionsTrie, TypeOutput.Uint8Array) ?? defaults.transactionsTrie
|
|
137
|
+
const receiptTrie =
|
|
138
|
+
toType(headerData.receiptTrie, TypeOutput.Uint8Array) ?? defaults.receiptTrie
|
|
139
|
+
const logsBloom = toType(headerData.logsBloom, TypeOutput.Uint8Array) ?? defaults.logsBloom
|
|
140
|
+
const difficulty = toType(headerData.difficulty, TypeOutput.BigInt) ?? defaults.difficulty
|
|
141
|
+
const number = toType(headerData.number, TypeOutput.BigInt) ?? defaults.number
|
|
142
|
+
const gasLimit = toType(headerData.gasLimit, TypeOutput.BigInt) ?? defaults.gasLimit
|
|
143
|
+
const gasUsed = toType(headerData.gasUsed, TypeOutput.BigInt) ?? defaults.gasUsed
|
|
144
|
+
const timestamp = toType(headerData.timestamp, TypeOutput.BigInt) ?? defaults.timestamp
|
|
145
|
+
const extraData = toType(headerData.extraData, TypeOutput.Uint8Array) ?? defaults.extraData
|
|
146
|
+
const mixHash = toType(headerData.mixHash, TypeOutput.Uint8Array) ?? defaults.mixHash
|
|
147
|
+
const nonce = toType(headerData.nonce, TypeOutput.Uint8Array) ?? defaults.nonce
|
|
148
|
+
|
|
149
|
+
const setHardfork = opts.setHardfork ?? false
|
|
150
|
+
if (setHardfork === true) {
|
|
151
|
+
this.common.setHardforkBy({
|
|
152
|
+
blockNumber: number,
|
|
153
|
+
timestamp,
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Hardfork defaults which couldn't be paired with earlier defaults
|
|
158
|
+
const hardforkDefaults = {
|
|
159
|
+
baseFeePerGas: this.common.isActivatedEIP(1559)
|
|
160
|
+
? number === this.common.hardforkBlock(Hardfork.London)
|
|
161
|
+
? this.common.param('initialBaseFee')
|
|
162
|
+
: BIGINT_7
|
|
163
|
+
: undefined,
|
|
164
|
+
withdrawalsRoot: this.common.isActivatedEIP(4895) ? KECCAK256_RLP : undefined,
|
|
165
|
+
blobGasUsed: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined,
|
|
166
|
+
excessBlobGas: this.common.isActivatedEIP(4844) ? BIGINT_0 : undefined,
|
|
167
|
+
parentBeaconBlockRoot: this.common.isActivatedEIP(4788) ? new Uint8Array(32) : undefined,
|
|
168
|
+
// Note: as of devnet-4 we stub the null SHA256 hash, but for devnet5 this will actually
|
|
169
|
+
// be the correct hash for empty requests.
|
|
170
|
+
requestsHash: this.common.isActivatedEIP(7685) ? SHA256_NULL : undefined,
|
|
171
|
+
blockAccessListHash: this.common.isActivatedEIP(7928) ? new Uint8Array(32) : undefined,
|
|
172
|
+
slotNumber: this.common.isActivatedEIP(7843) ? BIGINT_0 : undefined,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const baseFeePerGas =
|
|
176
|
+
toType(headerData.baseFeePerGas, TypeOutput.BigInt) ?? hardforkDefaults.baseFeePerGas
|
|
177
|
+
const withdrawalsRoot =
|
|
178
|
+
toType(headerData.withdrawalsRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.withdrawalsRoot
|
|
179
|
+
const blobGasUsed =
|
|
180
|
+
toType(headerData.blobGasUsed, TypeOutput.BigInt) ?? hardforkDefaults.blobGasUsed
|
|
181
|
+
const excessBlobGas =
|
|
182
|
+
toType(headerData.excessBlobGas, TypeOutput.BigInt) ?? hardforkDefaults.excessBlobGas
|
|
183
|
+
const parentBeaconBlockRoot =
|
|
184
|
+
toType(headerData.parentBeaconBlockRoot, TypeOutput.Uint8Array) ??
|
|
185
|
+
hardforkDefaults.parentBeaconBlockRoot
|
|
186
|
+
const requestsHash =
|
|
187
|
+
toType(headerData.requestsHash, TypeOutput.Uint8Array) ?? hardforkDefaults.requestsHash
|
|
188
|
+
const blockAccessListHash =
|
|
189
|
+
toType(headerData.blockAccessListHash, TypeOutput.Uint8Array) ??
|
|
190
|
+
hardforkDefaults.blockAccessListHash
|
|
191
|
+
const slotNumber =
|
|
192
|
+
toType(headerData.slotNumber, TypeOutput.BigInt) ?? hardforkDefaults.slotNumber
|
|
193
|
+
|
|
194
|
+
if (!this.common.isActivatedEIP(1559) && baseFeePerGas !== undefined) {
|
|
195
|
+
throw EthereumJSErrorWithoutCode(
|
|
196
|
+
'A base fee for a block can only be set with EIP1559 being activated',
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!this.common.isActivatedEIP(4895) && withdrawalsRoot !== undefined) {
|
|
201
|
+
throw EthereumJSErrorWithoutCode(
|
|
202
|
+
'A withdrawalsRoot for a header can only be provided with EIP4895 being activated',
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!this.common.isActivatedEIP(4844)) {
|
|
207
|
+
if (blobGasUsed !== undefined) {
|
|
208
|
+
throw EthereumJSErrorWithoutCode(
|
|
209
|
+
'blob gas used can only be provided with EIP4844 activated',
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (excessBlobGas !== undefined) {
|
|
214
|
+
throw EthereumJSErrorWithoutCode(
|
|
215
|
+
'excess blob gas can only be provided with EIP4844 activated',
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!this.common.isActivatedEIP(4788) && parentBeaconBlockRoot !== undefined) {
|
|
221
|
+
throw EthereumJSErrorWithoutCode(
|
|
222
|
+
'A parentBeaconBlockRoot for a header can only be provided with EIP4788 being activated',
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!this.common.isActivatedEIP(7685) && requestsHash !== undefined) {
|
|
227
|
+
throw EthereumJSErrorWithoutCode('requestsHash can only be provided with EIP 7685 activated')
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (!this.common.isActivatedEIP(7928) && blockAccessListHash !== undefined) {
|
|
231
|
+
throw EthereumJSErrorWithoutCode(
|
|
232
|
+
'blockAccessListHash can only be provided with EIP 7928 activated',
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!this.common.isActivatedEIP(7843) && slotNumber !== undefined) {
|
|
237
|
+
throw EthereumJSErrorWithoutCode('slotNumber can only be provided with EIP 7843 activated')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.parentHash = parentHash
|
|
241
|
+
this.uncleHash = uncleHash
|
|
242
|
+
this.coinbase = coinbase
|
|
243
|
+
this.stateRoot = stateRoot
|
|
244
|
+
this.transactionsTrie = transactionsTrie
|
|
245
|
+
this.receiptTrie = receiptTrie
|
|
246
|
+
this.logsBloom = logsBloom
|
|
247
|
+
this.difficulty = difficulty
|
|
248
|
+
this.number = number
|
|
249
|
+
this.gasLimit = gasLimit
|
|
250
|
+
this.gasUsed = gasUsed
|
|
251
|
+
this.timestamp = timestamp
|
|
252
|
+
this.extraData = extraData
|
|
253
|
+
this.mixHash = mixHash
|
|
254
|
+
this.nonce = nonce
|
|
255
|
+
this.baseFeePerGas = baseFeePerGas
|
|
256
|
+
this.withdrawalsRoot = withdrawalsRoot
|
|
257
|
+
this.blobGasUsed = blobGasUsed
|
|
258
|
+
this.excessBlobGas = excessBlobGas
|
|
259
|
+
this.parentBeaconBlockRoot = parentBeaconBlockRoot
|
|
260
|
+
this.requestsHash = requestsHash
|
|
261
|
+
this.blockAccessListHash = blockAccessListHash
|
|
262
|
+
this.slotNumber = slotNumber
|
|
263
|
+
this._genericFormatValidation()
|
|
264
|
+
this._validateDAOExtraData()
|
|
265
|
+
|
|
266
|
+
// Now we have set all the values of this Header, we possibly have set a dummy
|
|
267
|
+
// `difficulty` value (defaults to 0). If we have a `calcDifficultyFromHeader`
|
|
268
|
+
// block option parameter, we instead set difficulty to this value.
|
|
269
|
+
if (
|
|
270
|
+
opts.calcDifficultyFromHeader &&
|
|
271
|
+
this.common.consensusAlgorithm() === ConsensusAlgorithm.Ethash
|
|
272
|
+
) {
|
|
273
|
+
this.difficulty = this.ethashCanonicalDifficulty(opts.calcDifficultyFromHeader)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Validate consensus format after block is sealed (if applicable) so extraData checks will pass
|
|
277
|
+
if (skipValidateConsensusFormat === false) this._consensusFormatValidation()
|
|
278
|
+
|
|
279
|
+
const freeze = opts?.freeze ?? true
|
|
280
|
+
if (freeze) {
|
|
281
|
+
Object.freeze(this)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Validates correct buffer lengths, throws if invalid.
|
|
287
|
+
*/
|
|
288
|
+
protected _genericFormatValidation() {
|
|
289
|
+
const { parentHash, stateRoot, transactionsTrie, receiptTrie, mixHash, nonce } = this
|
|
290
|
+
|
|
291
|
+
if (parentHash.length !== 32) {
|
|
292
|
+
const msg = this._errorMsg(`parentHash must be 32 bytes, received ${parentHash.length} bytes`)
|
|
293
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
294
|
+
}
|
|
295
|
+
if (stateRoot.length !== 32) {
|
|
296
|
+
const msg = this._errorMsg(`stateRoot must be 32 bytes, received ${stateRoot.length} bytes`)
|
|
297
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
298
|
+
}
|
|
299
|
+
if (transactionsTrie.length !== 32) {
|
|
300
|
+
const msg = this._errorMsg(
|
|
301
|
+
`transactionsTrie must be 32 bytes, received ${transactionsTrie.length} bytes`,
|
|
302
|
+
)
|
|
303
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
304
|
+
}
|
|
305
|
+
if (receiptTrie.length !== 32) {
|
|
306
|
+
const msg = this._errorMsg(
|
|
307
|
+
`receiptTrie must be 32 bytes, received ${receiptTrie.length} bytes`,
|
|
308
|
+
)
|
|
309
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
310
|
+
}
|
|
311
|
+
if (mixHash.length !== 32) {
|
|
312
|
+
const msg = this._errorMsg(`mixHash must be 32 bytes, received ${mixHash.length} bytes`)
|
|
313
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (nonce.length !== 8) {
|
|
317
|
+
const msg = this._errorMsg(`nonce must be 8 bytes, received ${nonce.length} bytes`)
|
|
318
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// check if the block used too much gas
|
|
322
|
+
if (this.gasUsed > this.gasLimit) {
|
|
323
|
+
const msg = this._errorMsg(
|
|
324
|
+
`Invalid block: too much gas used. Used: ${this.gasUsed}, gas limit: ${this.gasLimit}`,
|
|
325
|
+
)
|
|
326
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Validation for EIP-1559 blocks
|
|
330
|
+
if (this.common.isActivatedEIP(1559)) {
|
|
331
|
+
if (typeof this.baseFeePerGas !== 'bigint') {
|
|
332
|
+
const msg = this._errorMsg('EIP1559 block has no base fee field')
|
|
333
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
334
|
+
}
|
|
335
|
+
const londonHfBlock = this.common.hardforkBlock(Hardfork.London)
|
|
336
|
+
if (
|
|
337
|
+
typeof londonHfBlock === 'bigint' &&
|
|
338
|
+
londonHfBlock !== BIGINT_0 &&
|
|
339
|
+
this.number === londonHfBlock
|
|
340
|
+
) {
|
|
341
|
+
const initialBaseFee = this.common.param('initialBaseFee')
|
|
342
|
+
if (this.baseFeePerGas !== initialBaseFee) {
|
|
343
|
+
const msg = this._errorMsg('Initial EIP1559 block does not have initial base fee')
|
|
344
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (this.common.isActivatedEIP(4895)) {
|
|
350
|
+
if (this.withdrawalsRoot === undefined) {
|
|
351
|
+
const msg = this._errorMsg('EIP4895 block has no withdrawalsRoot field')
|
|
352
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
353
|
+
}
|
|
354
|
+
if (this.withdrawalsRoot?.length !== 32) {
|
|
355
|
+
const msg = this._errorMsg(
|
|
356
|
+
`withdrawalsRoot must be 32 bytes, received ${this.withdrawalsRoot!.length} bytes`,
|
|
357
|
+
)
|
|
358
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (this.common.isActivatedEIP(4788)) {
|
|
363
|
+
if (this.parentBeaconBlockRoot === undefined) {
|
|
364
|
+
const msg = this._errorMsg('EIP4788 block has no parentBeaconBlockRoot field')
|
|
365
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
366
|
+
}
|
|
367
|
+
if (this.parentBeaconBlockRoot?.length !== 32) {
|
|
368
|
+
const msg = this._errorMsg(
|
|
369
|
+
`parentBeaconBlockRoot must be 32 bytes, received ${
|
|
370
|
+
this.parentBeaconBlockRoot!.length
|
|
371
|
+
} bytes`,
|
|
372
|
+
)
|
|
373
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (this.common.isActivatedEIP(7685)) {
|
|
378
|
+
if (this.requestsHash === undefined) {
|
|
379
|
+
const msg = this._errorMsg('EIP7685 block has no requestsHash field')
|
|
380
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (this.common.isActivatedEIP(7928)) {
|
|
385
|
+
if (this.blockAccessListHash === undefined) {
|
|
386
|
+
const msg = this._errorMsg('EIP7928 block has no blockAccessListHash field')
|
|
387
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
388
|
+
}
|
|
389
|
+
if (this.blockAccessListHash?.length !== 32) {
|
|
390
|
+
const msg = this._errorMsg(
|
|
391
|
+
`blockAccessListHash must be 32 bytes, received ${this.blockAccessListHash!.length} bytes`,
|
|
392
|
+
)
|
|
393
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (this.common.isActivatedEIP(7843)) {
|
|
398
|
+
if (this.slotNumber === undefined) {
|
|
399
|
+
const msg = this._errorMsg('EIP7843 block has no slotNumber field')
|
|
400
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Checks static parameters related to consensus algorithm
|
|
407
|
+
* @throws if any check fails
|
|
408
|
+
*/
|
|
409
|
+
protected _consensusFormatValidation() {
|
|
410
|
+
const { nonce, uncleHash, difficulty, extraData, number } = this
|
|
411
|
+
|
|
412
|
+
// Consensus type dependent checks
|
|
413
|
+
if (this.common.consensusAlgorithm() === ConsensusAlgorithm.Ethash) {
|
|
414
|
+
// PoW/Ethash
|
|
415
|
+
if (number > BIGINT_0 && this.extraData.length > this.common.param('maxExtraDataSize')) {
|
|
416
|
+
// Check length of data on all post-genesis blocks
|
|
417
|
+
const msg = this._errorMsg('invalid amount of extra data')
|
|
418
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (this.common.consensusAlgorithm() === ConsensusAlgorithm.Clique) {
|
|
422
|
+
// PoA/Clique
|
|
423
|
+
const minLength = CLIQUE_EXTRA_VANITY + CLIQUE_EXTRA_SEAL
|
|
424
|
+
if (!cliqueIsEpochTransition(this)) {
|
|
425
|
+
// ExtraData length on epoch transition
|
|
426
|
+
if (this.extraData.length !== minLength) {
|
|
427
|
+
const msg = this._errorMsg(
|
|
428
|
+
`extraData must be ${minLength} bytes on non-epoch transition blocks, received ${this.extraData.length} bytes`,
|
|
429
|
+
)
|
|
430
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
const signerLength = this.extraData.length - minLength
|
|
434
|
+
if (signerLength % 20 !== 0) {
|
|
435
|
+
const msg = this._errorMsg(
|
|
436
|
+
`invalid signer list length in extraData, received signer length of ${signerLength} (not divisible by 20)`,
|
|
437
|
+
)
|
|
438
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
439
|
+
}
|
|
440
|
+
// coinbase (beneficiary) on epoch transition
|
|
441
|
+
if (!this.coinbase.isZero()) {
|
|
442
|
+
const msg = this._errorMsg(
|
|
443
|
+
`coinbase must be filled with zeros on epoch transition blocks, received ${this.coinbase}`,
|
|
444
|
+
)
|
|
445
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// MixHash format
|
|
449
|
+
if (!equalsBytes(this.mixHash, new Uint8Array(32))) {
|
|
450
|
+
const msg = this._errorMsg(`mixHash must be filled with zeros, received ${this.mixHash}`)
|
|
451
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
// Validation for PoS blocks (EIP-3675)
|
|
455
|
+
if (this.common.consensusType() === ConsensusType.ProofOfStake) {
|
|
456
|
+
let error = false
|
|
457
|
+
let errorMsg = ''
|
|
458
|
+
|
|
459
|
+
if (!equalsBytes(uncleHash, KECCAK256_RLP_ARRAY)) {
|
|
460
|
+
errorMsg += `, uncleHash: ${bytesToHex(uncleHash)} (expected: ${bytesToHex(
|
|
461
|
+
KECCAK256_RLP_ARRAY,
|
|
462
|
+
)})`
|
|
463
|
+
error = true
|
|
464
|
+
}
|
|
465
|
+
if (number !== BIGINT_0) {
|
|
466
|
+
// Skip difficulty, nonce, and extraData check for PoS genesis block as genesis block may have non-zero difficulty (if TD is > 0)
|
|
467
|
+
if (difficulty !== BIGINT_0) {
|
|
468
|
+
errorMsg += `, difficulty: ${difficulty} (expected: 0)`
|
|
469
|
+
error = true
|
|
470
|
+
}
|
|
471
|
+
if (extraData.length > 32) {
|
|
472
|
+
errorMsg += `, extraData: ${bytesToHex(
|
|
473
|
+
extraData,
|
|
474
|
+
)} (cannot exceed 32 bytes length, received ${extraData.length} bytes)`
|
|
475
|
+
error = true
|
|
476
|
+
}
|
|
477
|
+
if (!equalsBytes(nonce, new Uint8Array(8))) {
|
|
478
|
+
errorMsg += `, nonce: ${bytesToHex(nonce)} (expected: ${bytesToHex(new Uint8Array(8))})`
|
|
479
|
+
error = true
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (error) {
|
|
483
|
+
const msg = this._errorMsg(`Invalid PoS block: ${errorMsg}`)
|
|
484
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Validates if the block gasLimit remains in the boundaries set by the protocol.
|
|
491
|
+
* Throws if out of bounds.
|
|
492
|
+
*
|
|
493
|
+
* @param parentBlockHeader - the header from the parent `Block` of this header
|
|
494
|
+
*/
|
|
495
|
+
validateGasLimit(parentBlockHeader: BlockHeader) {
|
|
496
|
+
let parentGasLimit = parentBlockHeader.gasLimit
|
|
497
|
+
// EIP-1559: assume double the parent gas limit on fork block
|
|
498
|
+
// to adopt to the new gas target centered logic
|
|
499
|
+
const londonHardforkBlock = this.common.hardforkBlock(Hardfork.London)
|
|
500
|
+
if (
|
|
501
|
+
typeof londonHardforkBlock === 'bigint' &&
|
|
502
|
+
londonHardforkBlock !== BIGINT_0 &&
|
|
503
|
+
this.number === londonHardforkBlock
|
|
504
|
+
) {
|
|
505
|
+
const elasticity = this.common.param('elasticityMultiplier')
|
|
506
|
+
parentGasLimit = parentGasLimit * elasticity
|
|
507
|
+
}
|
|
508
|
+
const gasLimit = this.gasLimit
|
|
509
|
+
|
|
510
|
+
const a = parentGasLimit / this.common.param('gasLimitBoundDivisor')
|
|
511
|
+
const maxGasLimit = parentGasLimit + a
|
|
512
|
+
const minGasLimit = parentGasLimit - a
|
|
513
|
+
|
|
514
|
+
if (gasLimit >= maxGasLimit) {
|
|
515
|
+
const msg = this._errorMsg(
|
|
516
|
+
`gas limit increased too much. Gas limit: ${gasLimit}, max gas limit: ${maxGasLimit}`,
|
|
517
|
+
)
|
|
518
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
if (gasLimit <= minGasLimit) {
|
|
522
|
+
const msg = this._errorMsg(
|
|
523
|
+
`gas limit decreased too much. Gas limit: ${gasLimit}, min gas limit: ${minGasLimit}`,
|
|
524
|
+
)
|
|
525
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (gasLimit < this.common.param('minGasLimit')) {
|
|
529
|
+
const msg = this._errorMsg(
|
|
530
|
+
`gas limit decreased below minimum gas limit. Gas limit: ${gasLimit}, minimum gas limit: ${this.common.param(
|
|
531
|
+
'minGasLimit',
|
|
532
|
+
)}`,
|
|
533
|
+
)
|
|
534
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Calculates the base fee for a potential next block
|
|
540
|
+
*/
|
|
541
|
+
public calcNextBaseFee(): bigint {
|
|
542
|
+
if (!this.common.isActivatedEIP(1559)) {
|
|
543
|
+
const msg = this._errorMsg(
|
|
544
|
+
'calcNextBaseFee() can only be called with EIP1559 being activated',
|
|
545
|
+
)
|
|
546
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
547
|
+
}
|
|
548
|
+
let nextBaseFee: bigint
|
|
549
|
+
const elasticity = this.common.param('elasticityMultiplier')
|
|
550
|
+
const parentGasTarget = this.gasLimit / elasticity
|
|
551
|
+
|
|
552
|
+
if (parentGasTarget === this.gasUsed) {
|
|
553
|
+
nextBaseFee = this.baseFeePerGas!
|
|
554
|
+
} else if (this.gasUsed > parentGasTarget) {
|
|
555
|
+
const gasUsedDelta = this.gasUsed - parentGasTarget
|
|
556
|
+
const baseFeeMaxChangeDenominator = this.common.param('baseFeeMaxChangeDenominator')
|
|
557
|
+
|
|
558
|
+
const calculatedDelta =
|
|
559
|
+
(this.baseFeePerGas! * gasUsedDelta) / parentGasTarget / baseFeeMaxChangeDenominator
|
|
560
|
+
nextBaseFee = (calculatedDelta > BIGINT_1 ? calculatedDelta : BIGINT_1) + this.baseFeePerGas!
|
|
561
|
+
} else {
|
|
562
|
+
const gasUsedDelta = parentGasTarget - this.gasUsed
|
|
563
|
+
const baseFeeMaxChangeDenominator = this.common.param('baseFeeMaxChangeDenominator')
|
|
564
|
+
|
|
565
|
+
const calculatedDelta =
|
|
566
|
+
(this.baseFeePerGas! * gasUsedDelta) / parentGasTarget / baseFeeMaxChangeDenominator
|
|
567
|
+
nextBaseFee =
|
|
568
|
+
this.baseFeePerGas! - calculatedDelta > BIGINT_0
|
|
569
|
+
? this.baseFeePerGas! - calculatedDelta
|
|
570
|
+
: BIGINT_0
|
|
571
|
+
}
|
|
572
|
+
return nextBaseFee
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Returns the price per unit of blob gas for a blob transaction in the current/pending block
|
|
577
|
+
* @returns the price in gwei per unit of blob gas spent
|
|
578
|
+
*/
|
|
579
|
+
getBlobGasPrice(): bigint {
|
|
580
|
+
if (this.excessBlobGas === undefined) {
|
|
581
|
+
throw EthereumJSErrorWithoutCode('header must have excessBlobGas field populated')
|
|
582
|
+
}
|
|
583
|
+
return computeBlobGasPrice(this.excessBlobGas, this.common)
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Returns the total fee for blob gas spent for including blobs in block.
|
|
588
|
+
*
|
|
589
|
+
* @param numBlobs number of blobs in the transaction/block
|
|
590
|
+
* @returns the total blob gas fee for numBlobs blobs
|
|
591
|
+
*/
|
|
592
|
+
calcDataFee(numBlobs: number): bigint {
|
|
593
|
+
const blobGasPerBlob = this.common.param('blobGasPerBlob')
|
|
594
|
+
const blobGasUsed = blobGasPerBlob * BigInt(numBlobs)
|
|
595
|
+
|
|
596
|
+
const blobGasPrice = this.getBlobGasPrice()
|
|
597
|
+
return blobGasUsed * blobGasPrice
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Calculates the excess blob gas for next (hopefully) post EIP 4844 block.
|
|
602
|
+
*/
|
|
603
|
+
public calcNextExcessBlobGas(childCommon: Common): bigint {
|
|
604
|
+
const excessBlobGas = this.excessBlobGas ?? BIGINT_0
|
|
605
|
+
const blobGasUsed = this.blobGasUsed ?? BIGINT_0
|
|
606
|
+
|
|
607
|
+
const { targetBlobGasPerBlock: targetPerBlock, maxBlobGasPerBlock: maxPerBlock } =
|
|
608
|
+
childCommon.getBlobGasSchedule()
|
|
609
|
+
|
|
610
|
+
// Early exit (strictly < per spec)
|
|
611
|
+
if (excessBlobGas + blobGasUsed < targetPerBlock) {
|
|
612
|
+
return BIGINT_0
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// EIP-7918 reserve price check
|
|
616
|
+
if (childCommon.isActivatedEIP(7918)) {
|
|
617
|
+
const blobBaseCost = childCommon.param('blobBaseCost')
|
|
618
|
+
const gasPerBlob = childCommon.param('blobGasPerBlob')
|
|
619
|
+
const baseFee = this.baseFeePerGas ?? BIGINT_0
|
|
620
|
+
const blobFee = computeBlobGasPrice(excessBlobGas, childCommon)
|
|
621
|
+
|
|
622
|
+
if (blobBaseCost * baseFee > gasPerBlob * blobFee) {
|
|
623
|
+
const increase = (blobGasUsed * (maxPerBlock - targetPerBlock)) / maxPerBlock
|
|
624
|
+
return excessBlobGas + increase
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Original 4844 path
|
|
629
|
+
return excessBlobGas + blobGasUsed - targetPerBlock
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Calculate the blob gas price of the block built on top of this one
|
|
634
|
+
* @returns The blob gas price
|
|
635
|
+
*/
|
|
636
|
+
public calcNextBlobGasPrice(childCommon: Common): bigint {
|
|
637
|
+
return computeBlobGasPrice(this.calcNextExcessBlobGas(childCommon), childCommon)
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Returns a Uint8Array Array of the raw Bytes in this header, in order.
|
|
642
|
+
*/
|
|
643
|
+
raw(): BlockHeaderBytes {
|
|
644
|
+
const rawItems = [
|
|
645
|
+
this.parentHash,
|
|
646
|
+
this.uncleHash,
|
|
647
|
+
this.coinbase.bytes,
|
|
648
|
+
this.stateRoot,
|
|
649
|
+
this.transactionsTrie,
|
|
650
|
+
this.receiptTrie,
|
|
651
|
+
this.logsBloom,
|
|
652
|
+
bigIntToUnpaddedBytes(this.difficulty),
|
|
653
|
+
bigIntToUnpaddedBytes(this.number),
|
|
654
|
+
bigIntToUnpaddedBytes(this.gasLimit),
|
|
655
|
+
bigIntToUnpaddedBytes(this.gasUsed),
|
|
656
|
+
bigIntToUnpaddedBytes(this.timestamp ?? BIGINT_0),
|
|
657
|
+
this.extraData,
|
|
658
|
+
this.mixHash,
|
|
659
|
+
this.nonce,
|
|
660
|
+
]
|
|
661
|
+
|
|
662
|
+
if (this.common.isActivatedEIP(1559)) {
|
|
663
|
+
rawItems.push(bigIntToUnpaddedBytes(this.baseFeePerGas!))
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (this.common.isActivatedEIP(4895)) {
|
|
667
|
+
rawItems.push(this.withdrawalsRoot!)
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (this.common.isActivatedEIP(4844)) {
|
|
671
|
+
rawItems.push(bigIntToUnpaddedBytes(this.blobGasUsed!))
|
|
672
|
+
rawItems.push(bigIntToUnpaddedBytes(this.excessBlobGas!))
|
|
673
|
+
}
|
|
674
|
+
if (this.common.isActivatedEIP(4788)) {
|
|
675
|
+
rawItems.push(this.parentBeaconBlockRoot!)
|
|
676
|
+
}
|
|
677
|
+
if (this.common.isActivatedEIP(7685)) {
|
|
678
|
+
rawItems.push(this.requestsHash!)
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (this.common.isActivatedEIP(7928)) {
|
|
682
|
+
rawItems.push(this.blockAccessListHash!)
|
|
683
|
+
}
|
|
684
|
+
if (this.common.isActivatedEIP(7843)) {
|
|
685
|
+
rawItems.push(bigIntToUnpaddedBytes(this.slotNumber!))
|
|
686
|
+
}
|
|
687
|
+
return rawItems
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Returns the hash of the block header.
|
|
692
|
+
*/
|
|
693
|
+
hash(): Uint8Array {
|
|
694
|
+
if (Object.isFrozen(this)) {
|
|
695
|
+
this.cache.hash ??= this.keccakFunction(RLP.encode(this.raw())) as Uint8Array
|
|
696
|
+
return this.cache.hash
|
|
697
|
+
}
|
|
698
|
+
return this.keccakFunction(RLP.encode(this.raw()))
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Checks if the block header is a genesis header.
|
|
703
|
+
*/
|
|
704
|
+
isGenesis(): boolean {
|
|
705
|
+
return this.number === BIGINT_0
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Returns the canonical difficulty for this block.
|
|
710
|
+
*
|
|
711
|
+
* @param parentBlockHeader - the header from the parent `Block` of this header
|
|
712
|
+
*/
|
|
713
|
+
ethashCanonicalDifficulty(parentBlockHeader: BlockHeader): bigint {
|
|
714
|
+
if (this.common.consensusType() !== ConsensusType.ProofOfWork) {
|
|
715
|
+
const msg = this._errorMsg('difficulty calculation is only supported on PoW chains')
|
|
716
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
717
|
+
}
|
|
718
|
+
if (this.common.consensusAlgorithm() !== ConsensusAlgorithm.Ethash) {
|
|
719
|
+
const msg = this._errorMsg(
|
|
720
|
+
'difficulty calculation currently only supports the ethash algorithm',
|
|
721
|
+
)
|
|
722
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
723
|
+
}
|
|
724
|
+
const blockTs = this.timestamp
|
|
725
|
+
const { timestamp: parentTs, difficulty: parentDif } = parentBlockHeader
|
|
726
|
+
const minimumDifficulty = this.common.param('minimumDifficulty')
|
|
727
|
+
const offset = parentDif / this.common.param('difficultyBoundDivisor')
|
|
728
|
+
let num = this.number
|
|
729
|
+
|
|
730
|
+
// We use a ! here as TS cannot follow this hardfork-dependent logic, but it always gets assigned
|
|
731
|
+
let dif!: bigint
|
|
732
|
+
|
|
733
|
+
if (this.common.gteHardfork(Hardfork.Byzantium)) {
|
|
734
|
+
// max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99) (EIP100)
|
|
735
|
+
const uncleAddend = equalsBytes(parentBlockHeader.uncleHash, KECCAK256_RLP_ARRAY) ? 1 : 2
|
|
736
|
+
let a = BigInt(uncleAddend) - (blockTs - parentTs) / BigInt(9)
|
|
737
|
+
const cutoff = BigInt(-99)
|
|
738
|
+
// MAX(cutoff, a)
|
|
739
|
+
if (cutoff > a) {
|
|
740
|
+
a = cutoff
|
|
741
|
+
}
|
|
742
|
+
dif = parentDif + offset * a
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (this.common.gteHardfork(Hardfork.Byzantium)) {
|
|
746
|
+
// Get delay as parameter from common
|
|
747
|
+
num = num - this.common.param('difficultyBombDelay')
|
|
748
|
+
if (num < BIGINT_0) {
|
|
749
|
+
num = BIGINT_0
|
|
750
|
+
}
|
|
751
|
+
} else if (this.common.gteHardfork(Hardfork.Homestead)) {
|
|
752
|
+
// 1 - (block_timestamp - parent_timestamp) // 10
|
|
753
|
+
let a = BIGINT_1 - (blockTs - parentTs) / BigInt(10)
|
|
754
|
+
const cutoff = BigInt(-99)
|
|
755
|
+
// MAX(cutoff, a)
|
|
756
|
+
if (cutoff > a) {
|
|
757
|
+
a = cutoff
|
|
758
|
+
}
|
|
759
|
+
dif = parentDif + offset * a
|
|
760
|
+
} else {
|
|
761
|
+
// pre-homestead
|
|
762
|
+
if (parentTs + this.common.param('durationLimit') > blockTs) {
|
|
763
|
+
dif = offset + parentDif
|
|
764
|
+
} else {
|
|
765
|
+
dif = parentDif - offset
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const exp = num / BigInt(100000) - BIGINT_2
|
|
770
|
+
if (exp >= 0) {
|
|
771
|
+
dif = dif + BIGINT_2 ** exp
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (dif < minimumDifficulty) {
|
|
775
|
+
dif = minimumDifficulty
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return dif
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/**
|
|
782
|
+
* Returns the rlp encoding of the block header.
|
|
783
|
+
*/
|
|
784
|
+
serialize(): Uint8Array {
|
|
785
|
+
return RLP.encode(this.raw())
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Returns the block header in JSON format.
|
|
790
|
+
*/
|
|
791
|
+
toJSON(): JSONHeader {
|
|
792
|
+
const withdrawalAttr = this.withdrawalsRoot
|
|
793
|
+
? { withdrawalsRoot: bytesToHex(this.withdrawalsRoot) }
|
|
794
|
+
: {}
|
|
795
|
+
const JSONDict: JSONHeader = {
|
|
796
|
+
parentHash: bytesToHex(this.parentHash),
|
|
797
|
+
uncleHash: bytesToHex(this.uncleHash),
|
|
798
|
+
coinbase: this.coinbase.toString(),
|
|
799
|
+
stateRoot: bytesToHex(this.stateRoot),
|
|
800
|
+
transactionsTrie: bytesToHex(this.transactionsTrie),
|
|
801
|
+
...withdrawalAttr,
|
|
802
|
+
receiptTrie: bytesToHex(this.receiptTrie),
|
|
803
|
+
logsBloom: bytesToHex(this.logsBloom),
|
|
804
|
+
difficulty: bigIntToHex(this.difficulty),
|
|
805
|
+
number: bigIntToHex(this.number),
|
|
806
|
+
gasLimit: bigIntToHex(this.gasLimit),
|
|
807
|
+
gasUsed: bigIntToHex(this.gasUsed),
|
|
808
|
+
timestamp: bigIntToHex(this.timestamp),
|
|
809
|
+
extraData: bytesToHex(this.extraData),
|
|
810
|
+
mixHash: bytesToHex(this.mixHash),
|
|
811
|
+
nonce: bytesToHex(this.nonce),
|
|
812
|
+
}
|
|
813
|
+
if (this.common.isActivatedEIP(1559)) {
|
|
814
|
+
JSONDict.baseFeePerGas = bigIntToHex(this.baseFeePerGas!)
|
|
815
|
+
}
|
|
816
|
+
if (this.common.isActivatedEIP(4844)) {
|
|
817
|
+
JSONDict.blobGasUsed = bigIntToHex(this.blobGasUsed!)
|
|
818
|
+
JSONDict.excessBlobGas = bigIntToHex(this.excessBlobGas!)
|
|
819
|
+
}
|
|
820
|
+
if (this.common.isActivatedEIP(4788)) {
|
|
821
|
+
JSONDict.parentBeaconBlockRoot = bytesToHex(this.parentBeaconBlockRoot!)
|
|
822
|
+
}
|
|
823
|
+
if (this.common.isActivatedEIP(7685)) {
|
|
824
|
+
JSONDict.requestsHash = bytesToHex(this.requestsHash!)
|
|
825
|
+
}
|
|
826
|
+
if (this.common.isActivatedEIP(7928)) {
|
|
827
|
+
JSONDict.blockAccessListHash = bytesToHex(this.blockAccessListHash!)
|
|
828
|
+
}
|
|
829
|
+
if (this.common.isActivatedEIP(7843)) {
|
|
830
|
+
JSONDict.slotNumber = bigIntToHex(this.slotNumber!)
|
|
831
|
+
}
|
|
832
|
+
return JSONDict
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Validates extra data is DAO_ExtraData for DAO_ForceExtraDataRange blocks after DAO
|
|
837
|
+
* activation block (see: https://blog.slock.it/hard-fork-specification-24b889e70703)
|
|
838
|
+
*/
|
|
839
|
+
protected _validateDAOExtraData() {
|
|
840
|
+
if (!this.common.hardforkIsActiveOnBlock(Hardfork.Dao, this.number)) {
|
|
841
|
+
return
|
|
842
|
+
}
|
|
843
|
+
const DAOActivationBlock = this.common.hardforkBlock(Hardfork.Dao)
|
|
844
|
+
if (DAOActivationBlock === null || this.number < DAOActivationBlock) {
|
|
845
|
+
return
|
|
846
|
+
}
|
|
847
|
+
const DAO_ExtraData = hexToBytes('0x64616f2d686172642d666f726b')
|
|
848
|
+
const DAO_ForceExtraDataRange = BigInt(9)
|
|
849
|
+
const drift = this.number - DAOActivationBlock
|
|
850
|
+
if (drift <= DAO_ForceExtraDataRange && !equalsBytes(this.extraData, DAO_ExtraData)) {
|
|
851
|
+
const msg = this._errorMsg(
|
|
852
|
+
`extraData should be 'dao-hard-fork', got ${bytesToUtf8(this.extraData)} (hex: ${bytesToHex(
|
|
853
|
+
this.extraData,
|
|
854
|
+
)})`,
|
|
855
|
+
)
|
|
856
|
+
throw EthereumJSErrorWithoutCode(msg)
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Return a compact error string representation of the object
|
|
862
|
+
*/
|
|
863
|
+
public errorStr() {
|
|
864
|
+
let hash = ''
|
|
865
|
+
try {
|
|
866
|
+
hash = bytesToHex(this.hash())
|
|
867
|
+
} catch {
|
|
868
|
+
hash = 'error'
|
|
869
|
+
}
|
|
870
|
+
let hf = ''
|
|
871
|
+
try {
|
|
872
|
+
hf = this.common.hardfork()
|
|
873
|
+
} catch {
|
|
874
|
+
hf = 'error'
|
|
875
|
+
}
|
|
876
|
+
let errorStr = `block header number=${this.number} hash=${hash} `
|
|
877
|
+
errorStr += `hf=${hf} baseFeePerGas=${this.baseFeePerGas ?? 'none'}`
|
|
878
|
+
return errorStr
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Helper function to create an annotated error message
|
|
883
|
+
*
|
|
884
|
+
* @param msg Base error message
|
|
885
|
+
* @hidden
|
|
886
|
+
*/
|
|
887
|
+
protected _errorMsg(msg: string) {
|
|
888
|
+
return `${msg} (${this.errorStr()})`
|
|
889
|
+
}
|
|
890
|
+
}
|