@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.
Files changed (133) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +466 -0
  3. package/dist/cjs/block/block.d.ts +147 -0
  4. package/dist/cjs/block/block.d.ts.map +1 -0
  5. package/dist/cjs/block/block.js +415 -0
  6. package/dist/cjs/block/block.js.map +1 -0
  7. package/dist/cjs/block/constructors.d.ts +77 -0
  8. package/dist/cjs/block/constructors.d.ts.map +1 -0
  9. package/dist/cjs/block/constructors.js +298 -0
  10. package/dist/cjs/block/constructors.js.map +1 -0
  11. package/dist/cjs/block/index.d.ts +3 -0
  12. package/dist/cjs/block/index.d.ts.map +1 -0
  13. package/dist/cjs/block/index.js +19 -0
  14. package/dist/cjs/block/index.js.map +1 -0
  15. package/dist/cjs/consensus/clique.d.ts +52 -0
  16. package/dist/cjs/consensus/clique.d.ts.map +1 -0
  17. package/dist/cjs/consensus/clique.js +144 -0
  18. package/dist/cjs/consensus/clique.js.map +1 -0
  19. package/dist/cjs/consensus/ethash.d.ts +9 -0
  20. package/dist/cjs/consensus/ethash.d.ts.map +1 -0
  21. package/dist/cjs/consensus/ethash.js +13 -0
  22. package/dist/cjs/consensus/ethash.js.map +1 -0
  23. package/dist/cjs/consensus/index.d.ts +3 -0
  24. package/dist/cjs/consensus/index.d.ts.map +1 -0
  25. package/dist/cjs/consensus/index.js +29 -0
  26. package/dist/cjs/consensus/index.js.map +1 -0
  27. package/dist/cjs/from-beacon-payload.d.ts +36 -0
  28. package/dist/cjs/from-beacon-payload.d.ts.map +1 -0
  29. package/dist/cjs/from-beacon-payload.js +48 -0
  30. package/dist/cjs/from-beacon-payload.js.map +1 -0
  31. package/dist/cjs/header/constructors.d.ts +39 -0
  32. package/dist/cjs/header/constructors.d.ts.map +1 -0
  33. package/dist/cjs/header/constructors.js +127 -0
  34. package/dist/cjs/header/constructors.js.map +1 -0
  35. package/dist/cjs/header/header.d.ts +134 -0
  36. package/dist/cjs/header/header.d.ts.map +1 -0
  37. package/dist/cjs/header/header.js +699 -0
  38. package/dist/cjs/header/header.js.map +1 -0
  39. package/dist/cjs/header/index.d.ts +3 -0
  40. package/dist/cjs/header/index.d.ts.map +1 -0
  41. package/dist/cjs/header/index.js +19 -0
  42. package/dist/cjs/header/index.js.map +1 -0
  43. package/dist/cjs/helpers.d.ts +59 -0
  44. package/dist/cjs/helpers.d.ts.map +1 -0
  45. package/dist/cjs/helpers.js +172 -0
  46. package/dist/cjs/helpers.js.map +1 -0
  47. package/dist/cjs/index.d.ts +8 -0
  48. package/dist/cjs/index.d.ts.map +1 -0
  49. package/dist/cjs/index.js +31 -0
  50. package/dist/cjs/index.js.map +1 -0
  51. package/dist/cjs/package.json +3 -0
  52. package/dist/cjs/params.d.ts +3 -0
  53. package/dist/cjs/params.d.ts.map +1 -0
  54. package/dist/cjs/params.js +97 -0
  55. package/dist/cjs/params.js.map +1 -0
  56. package/dist/cjs/types.d.ts +228 -0
  57. package/dist/cjs/types.d.ts.map +1 -0
  58. package/dist/cjs/types.js +3 -0
  59. package/dist/cjs/types.js.map +1 -0
  60. package/dist/esm/block/block.d.ts +147 -0
  61. package/dist/esm/block/block.d.ts.map +1 -0
  62. package/dist/esm/block/block.js +411 -0
  63. package/dist/esm/block/block.js.map +1 -0
  64. package/dist/esm/block/constructors.d.ts +77 -0
  65. package/dist/esm/block/constructors.d.ts.map +1 -0
  66. package/dist/esm/block/constructors.js +286 -0
  67. package/dist/esm/block/constructors.js.map +1 -0
  68. package/dist/esm/block/index.d.ts +3 -0
  69. package/dist/esm/block/index.d.ts.map +1 -0
  70. package/dist/esm/block/index.js +3 -0
  71. package/dist/esm/block/index.js.map +1 -0
  72. package/dist/esm/consensus/clique.d.ts +52 -0
  73. package/dist/esm/consensus/clique.d.ts.map +1 -0
  74. package/dist/esm/consensus/clique.js +132 -0
  75. package/dist/esm/consensus/clique.js.map +1 -0
  76. package/dist/esm/consensus/ethash.d.ts +9 -0
  77. package/dist/esm/consensus/ethash.d.ts.map +1 -0
  78. package/dist/esm/consensus/ethash.js +10 -0
  79. package/dist/esm/consensus/ethash.js.map +1 -0
  80. package/dist/esm/consensus/index.d.ts +3 -0
  81. package/dist/esm/consensus/index.d.ts.map +1 -0
  82. package/dist/esm/consensus/index.js +3 -0
  83. package/dist/esm/consensus/index.js.map +1 -0
  84. package/dist/esm/from-beacon-payload.d.ts +36 -0
  85. package/dist/esm/from-beacon-payload.d.ts.map +1 -0
  86. package/dist/esm/from-beacon-payload.js +45 -0
  87. package/dist/esm/from-beacon-payload.js.map +1 -0
  88. package/dist/esm/header/constructors.d.ts +39 -0
  89. package/dist/esm/header/constructors.d.ts.map +1 -0
  90. package/dist/esm/header/constructors.js +120 -0
  91. package/dist/esm/header/constructors.js.map +1 -0
  92. package/dist/esm/header/header.d.ts +134 -0
  93. package/dist/esm/header/header.d.ts.map +1 -0
  94. package/dist/esm/header/header.js +695 -0
  95. package/dist/esm/header/header.js.map +1 -0
  96. package/dist/esm/header/index.d.ts +3 -0
  97. package/dist/esm/header/index.d.ts.map +1 -0
  98. package/dist/esm/header/index.js +3 -0
  99. package/dist/esm/header/index.js.map +1 -0
  100. package/dist/esm/helpers.d.ts +59 -0
  101. package/dist/esm/helpers.d.ts.map +1 -0
  102. package/dist/esm/helpers.js +161 -0
  103. package/dist/esm/helpers.js.map +1 -0
  104. package/dist/esm/index.d.ts +8 -0
  105. package/dist/esm/index.d.ts.map +1 -0
  106. package/dist/esm/index.js +8 -0
  107. package/dist/esm/index.js.map +1 -0
  108. package/dist/esm/package.json +3 -0
  109. package/dist/esm/params.d.ts +3 -0
  110. package/dist/esm/params.d.ts.map +1 -0
  111. package/dist/esm/params.js +94 -0
  112. package/dist/esm/params.js.map +1 -0
  113. package/dist/esm/types.d.ts +228 -0
  114. package/dist/esm/types.d.ts.map +1 -0
  115. package/dist/esm/types.js +2 -0
  116. package/dist/esm/types.js.map +1 -0
  117. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  118. package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
  119. package/package.json +75 -0
  120. package/src/block/block.ts +526 -0
  121. package/src/block/constructors.ts +407 -0
  122. package/src/block/index.ts +2 -0
  123. package/src/consensus/clique.ts +171 -0
  124. package/src/consensus/ethash.ts +11 -0
  125. package/src/consensus/index.ts +12 -0
  126. package/src/from-beacon-payload.ts +82 -0
  127. package/src/header/constructors.ts +169 -0
  128. package/src/header/header.ts +890 -0
  129. package/src/header/index.ts +2 -0
  130. package/src/helpers.ts +223 -0
  131. package/src/index.ts +13 -0
  132. package/src/params.ts +95 -0
  133. package/src/types.ts +254 -0
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@feelyourprotocol/block",
3
+ "version": "8141.0.0",
4
+ "description": "Provides Block serialization and help functions",
5
+ "keywords": [
6
+ "ethereum",
7
+ "block"
8
+ ],
9
+ "homepage": "https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/block#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/ethereumjs/ethereumjs-monorepo/issues?q=is%3Aissue+label%3A%22package%3A+block%22"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/ethereumjs/ethereumjs-monorepo.git"
16
+ },
17
+ "license": "MPL-2.0",
18
+ "author": "mjbecze (mb@ethdev.com)",
19
+ "type": "module",
20
+ "sideEffects": false,
21
+ "main": "dist/cjs/index.js",
22
+ "module": "dist/esm/index.js",
23
+ "exports": {
24
+ ".": {
25
+ "import": {
26
+ "typescript": "./src/index.ts",
27
+ "default": "./dist/esm/index.js"
28
+ },
29
+ "require": "./dist/cjs/index.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "dist",
34
+ "src"
35
+ ],
36
+ "scripts": {
37
+ "biome": "npx @biomejs/biome check",
38
+ "biome:fix": "npx @biomejs/biome check --write",
39
+ "build": "../../config/cli/ts-build.sh",
40
+ "clean": "../../config/cli/clean-package.sh",
41
+ "coverage": "DEBUG=ethjs npx vitest run -c ../../config/vitest.config.coverage.mts",
42
+ "coverage:istanbul": "DEBUG=ethjs npx vitest run -c ../../config/vitest.config.coverage.istanbul.mts",
43
+ "docs:build": "typedoc --options typedoc.mjs",
44
+ "examples": "tsx ../../scripts/examples-runner.ts -- block",
45
+ "examples:build": "npx embedme README.md",
46
+ "lint": "npm run biome && eslint --config ./eslint.config.mjs .",
47
+ "lint:fix": "npm run biome:fix && eslint --fix --config ./eslint.config.mjs .",
48
+ "prepublishOnly": "../../config/cli/prepublish.sh",
49
+ "sc": "npm run spellcheck",
50
+ "spellcheck": "npm run spellcheck:ts && npm run spellcheck:md",
51
+ "spellcheck:ts": "npx cspell --gitignore -c ../../config/cspell-ts.json \"./**/*.ts\" --cache --show-suggestions --show-context",
52
+ "spellcheck:md": "npx cspell --gitignore -c ../../config/cspell-md.json \"**.md\" --cache --show-suggestions --show-context",
53
+ "test": "npm run test:node && npm run test:browser",
54
+ "test:browser": "npx vitest run --config=../../config/vitest.config.browser.mts",
55
+ "test:node": "npx vitest run -c ../../config/vitest.config.mts",
56
+ "tsc": "../../config/cli/ts-compile.sh"
57
+ },
58
+ "dependencies": {
59
+ "@feelyourprotocol/common": "^8141.0.0",
60
+ "@feelyourprotocol/rlp": "^8141.0.0",
61
+ "@feelyourprotocol/mpt": "^8141.0.0",
62
+ "@feelyourprotocol/tx": "^8141.0.0",
63
+ "@feelyourprotocol/util": "^8141.0.0",
64
+ "@noble/curves": "^2.0.1",
65
+ "@noble/hashes": "^2.0.1"
66
+ },
67
+ "devDependencies": {
68
+ "@ethereumjs/testdata": "1.0.0",
69
+ "@paulmillr/trusted-setups": "^0.2.0",
70
+ "micro-eth-signer": "^0.15.0"
71
+ },
72
+ "engines": {
73
+ "node": ">=20"
74
+ }
75
+ }
@@ -0,0 +1,526 @@
1
+ import { ConsensusType } from '@feelyourprotocol/common'
2
+ import { MerklePatriciaTrie } from '@feelyourprotocol/mpt'
3
+ import { RLP } from '@feelyourprotocol/rlp'
4
+ import { Blob4844Tx, Capability } from '@feelyourprotocol/tx'
5
+ import {
6
+ BIGINT_0,
7
+ EthereumJSErrorWithoutCode,
8
+ KECCAK256_RLP,
9
+ KECCAK256_RLP_ARRAY,
10
+ bytesToHex,
11
+ equalsBytes,
12
+ } from '@feelyourprotocol/util'
13
+ import { sha256 } from '@noble/hashes/sha2.js'
14
+ import { keccak_256 } from '@noble/hashes/sha3.js'
15
+
16
+ import type { Common } from '@feelyourprotocol/common'
17
+ import type { FeeMarket1559Tx, LegacyTx, TypedTransaction } from '@feelyourprotocol/tx'
18
+ import type { Withdrawal } from '@feelyourprotocol/util'
19
+ /* eslint-disable */
20
+ // This is to allow for a proper and linked collection of constructors for the class header.
21
+ // For tree shaking/code size this should be no problem since types go away on transpilation.
22
+ // TODO: See if there is an easier way to achieve the same result.
23
+ // See: https://github.com/microsoft/TypeScript/issues/47558
24
+ // (situation will eventually improve on Typescript and/or Eslint update)
25
+ import {
26
+ BlockHeader,
27
+ type createBlock,
28
+ type createBlockFromBeaconPayloadJSON,
29
+ type createBlockFromBytesArray,
30
+ type createBlockFromExecutionPayload,
31
+ type createBlockFromJSONRPCProvider,
32
+ type createBlockFromRLP,
33
+ type createBlockFromRPC,
34
+ genTransactionsTrieRoot,
35
+ genWithdrawalsTrieRoot,
36
+ } from '../index.ts'
37
+ /* eslint-enable */
38
+ import type { BlockBytes, BlockOptions, ExecutionPayload, JSONBlock } from '../types.ts'
39
+
40
+ /**
41
+ * Class representing a block in the Ethereum network. The {@link BlockHeader} has its own
42
+ * class and can be used independently, for a block it is included in the form of the
43
+ * {@link Block.header} property.
44
+ *
45
+ * A block object can be created with one of the following constructor methods
46
+ * (separate from the Block class to allow for tree shaking):
47
+ *
48
+ * - {@link createBlock }
49
+ * - {@link createBlockFromBytesArray }
50
+ * - {@link createBlockFromRLP }
51
+ * - {@link createBlockFromRPC }
52
+ * - {@link createBlockFromJSONRPCProvider }
53
+ * - {@link createBlockFromExecutionPayload }
54
+ * - {@link createBlockFromBeaconPayloadJSON }
55
+ */
56
+ export class Block {
57
+ public readonly header: BlockHeader
58
+ public readonly transactions: TypedTransaction[] = []
59
+ public readonly uncleHeaders: BlockHeader[] = []
60
+ public readonly withdrawals?: Withdrawal[]
61
+ public readonly common: Common
62
+ protected keccakFunction: (msg: Uint8Array) => Uint8Array
63
+ protected sha256Function: (msg: Uint8Array) => Uint8Array
64
+
65
+ protected cache: {
66
+ txTrieRoot?: Uint8Array
67
+ withdrawalsTrieRoot?: Uint8Array
68
+ } = {}
69
+
70
+ /**
71
+ * This constructor takes the values, validates them, assigns them and freezes the object.
72
+ *
73
+ * @deprecated Use the static factory methods (see {@link Block} for an overview) to assist in creating
74
+ * a Block object from varying data types and options.
75
+ */
76
+ constructor(
77
+ header?: BlockHeader,
78
+ transactions: TypedTransaction[] = [],
79
+ uncleHeaders: BlockHeader[] = [],
80
+ withdrawals?: Withdrawal[],
81
+ opts: BlockOptions = {},
82
+ ) {
83
+ this.header = header ?? new BlockHeader({}, opts)
84
+ this.common = this.header.common
85
+ this.keccakFunction = this.common.customCrypto.keccak256 ?? keccak_256
86
+ this.sha256Function = this.common.customCrypto.sha256 ?? sha256
87
+
88
+ this.transactions = transactions
89
+ this.withdrawals = withdrawals ?? (this.common.isActivatedEIP(4895) ? [] : undefined)
90
+
91
+ this.uncleHeaders = uncleHeaders
92
+ if (uncleHeaders.length > 0) {
93
+ this.validateUncles()
94
+ if (this.common.consensusType() === ConsensusType.ProofOfAuthority) {
95
+ const msg = this._errorMsg(
96
+ 'Block initialization with uncleHeaders on a PoA network is not allowed',
97
+ )
98
+ throw EthereumJSErrorWithoutCode(msg)
99
+ }
100
+ if (this.common.consensusType() === ConsensusType.ProofOfStake) {
101
+ const msg = this._errorMsg(
102
+ 'Block initialization with uncleHeaders on a PoS network is not allowed',
103
+ )
104
+ throw EthereumJSErrorWithoutCode(msg)
105
+ }
106
+ }
107
+
108
+ if (!this.common.isActivatedEIP(4895) && withdrawals !== undefined) {
109
+ throw EthereumJSErrorWithoutCode('Cannot have a withdrawals field if EIP 4895 is not active')
110
+ }
111
+
112
+ const freeze = opts?.freeze ?? true
113
+ if (freeze) {
114
+ Object.freeze(this)
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Returns an array of the raw byte arrays for this block, in order.
120
+ */
121
+ raw(): BlockBytes {
122
+ const bytesArray: BlockBytes = [
123
+ this.header.raw(),
124
+ this.transactions.map((tx) =>
125
+ tx.supports(Capability.EIP2718TypedTransaction) ? tx.serialize() : tx.raw(),
126
+ ) as Uint8Array[],
127
+ this.uncleHeaders.map((uh) => uh.raw()),
128
+ ]
129
+ const withdrawalsRaw = this.withdrawals?.map((wt) => wt.raw())
130
+ if (withdrawalsRaw) {
131
+ bytesArray.push(withdrawalsRaw)
132
+ }
133
+
134
+ return bytesArray
135
+ }
136
+
137
+ /**
138
+ * Returns the hash of the block.
139
+ */
140
+ hash(): Uint8Array {
141
+ return this.header.hash()
142
+ }
143
+
144
+ /**
145
+ * Determines if this block is the genesis block.
146
+ */
147
+ isGenesis(): boolean {
148
+ return this.header.isGenesis()
149
+ }
150
+
151
+ /**
152
+ * Returns the rlp encoding of the block.
153
+ */
154
+ serialize(): Uint8Array {
155
+ return RLP.encode(this.raw())
156
+ }
157
+
158
+ /**
159
+ * Generates transaction trie for validation.
160
+ */
161
+ async genTxTrie(): Promise<Uint8Array> {
162
+ return genTransactionsTrieRoot(
163
+ this.transactions,
164
+ new MerklePatriciaTrie({ common: this.common }),
165
+ )
166
+ }
167
+
168
+ /**
169
+ * Validates the transaction trie by generating a trie
170
+ * and do a check on the root hash.
171
+ * @returns True if the transaction trie is valid, false otherwise
172
+ */
173
+ async transactionsTrieIsValid(): Promise<boolean> {
174
+ let result
175
+ if (this.transactions.length === 0) {
176
+ result = equalsBytes(this.header.transactionsTrie, KECCAK256_RLP)
177
+ return result
178
+ }
179
+
180
+ if (this.cache.txTrieRoot === undefined) {
181
+ this.cache.txTrieRoot = await this.genTxTrie()
182
+ }
183
+ result = equalsBytes(this.cache.txTrieRoot, this.header.transactionsTrie)
184
+ return result
185
+ }
186
+
187
+ /**
188
+ * Validates transaction signatures and minimum gas requirements.
189
+ * @returns {string[]} an array of error strings
190
+ */
191
+ getTransactionsValidationErrors(): string[] {
192
+ const errors: string[] = []
193
+ let blobGasUsed = BIGINT_0
194
+
195
+ // eslint-disable-next-line prefer-const
196
+ for (let [i, tx] of this.transactions.entries()) {
197
+ const errs = tx.getValidationErrors()
198
+ if (this.common.isActivatedEIP(1559)) {
199
+ if (tx.supports(Capability.EIP1559FeeMarket)) {
200
+ tx = tx as FeeMarket1559Tx
201
+ if (tx.maxFeePerGas < this.header.baseFeePerGas!) {
202
+ errs.push('tx unable to pay base fee (EIP-1559 tx)')
203
+ }
204
+ } else {
205
+ tx = tx as LegacyTx
206
+ if (tx.gasPrice < this.header.baseFeePerGas!) {
207
+ errs.push('tx unable to pay base fee (non EIP-1559 tx)')
208
+ }
209
+ }
210
+ }
211
+ if (this.common.isActivatedEIP(4844)) {
212
+ const blobGasLimit = this.common.getBlobGasSchedule().maxBlobGasPerBlock
213
+ const blobGasPerBlob = this.common.param('blobGasPerBlob')
214
+ if (tx instanceof Blob4844Tx) {
215
+ blobGasUsed += BigInt(tx.numBlobs()) * blobGasPerBlob
216
+ if (blobGasUsed > blobGasLimit) {
217
+ errs.push(
218
+ `tx causes total blob gas of ${blobGasUsed} to exceed maximum blob gas per block of ${blobGasLimit}`,
219
+ )
220
+ }
221
+ }
222
+ }
223
+ if (errs.length > 0) {
224
+ errors.push(`errors at tx ${i}: ${errs.join(', ')}`)
225
+ }
226
+ }
227
+
228
+ if (this.common.isActivatedEIP(4844)) {
229
+ if (blobGasUsed !== this.header.blobGasUsed) {
230
+ errors.push(`invalid blobGasUsed expected=${this.header.blobGasUsed} actual=${blobGasUsed}`)
231
+ }
232
+ }
233
+
234
+ return errors
235
+ }
236
+
237
+ /**
238
+ * Validates transaction signatures and minimum gas requirements.
239
+ * @returns True if all transactions are valid, false otherwise
240
+ */
241
+ transactionsAreValid(): boolean {
242
+ const errors = this.getTransactionsValidationErrors()
243
+
244
+ return errors.length === 0
245
+ }
246
+
247
+ /**
248
+ * Validates the block data, throwing if invalid.
249
+ * This can be checked on the Block itself without needing access to any parent block
250
+ * It checks:
251
+ * - All transactions are valid
252
+ * - The transactions trie is valid
253
+ * - The uncle hash is valid
254
+ * - Block size limit (EIP-7934)
255
+ * @param onlyHeader if only passed the header, skip validating txTrie and unclesHash (default: false)
256
+ * @param verifyTxs if set to `false`, will not check for transaction validation errors (default: true)
257
+ * @param validateBlockSize if set to `true`, will check for block size limit (EIP-7934) (default: false)
258
+ */
259
+ async validateData(
260
+ onlyHeader: boolean = false,
261
+ verifyTxs: boolean = true,
262
+ validateBlockSize: boolean = false,
263
+ ): Promise<void> {
264
+ // EIP-7934: RLP Execution Block Size Limit validation
265
+ if (validateBlockSize && this.common.isActivatedEIP(7934)) {
266
+ const rlpEncoded = this.serialize()
267
+ const maxRlpBlockSize = this.common.param('maxRlpBlockSize')
268
+ if (rlpEncoded.length > maxRlpBlockSize) {
269
+ const msg = this._errorMsg(
270
+ `Block size exceeds maximum RLP block size limit: ${rlpEncoded.length} bytes > ${maxRlpBlockSize} bytes`,
271
+ )
272
+ throw EthereumJSErrorWithoutCode(msg)
273
+ }
274
+ }
275
+
276
+ if (verifyTxs) {
277
+ const txErrors = this.getTransactionsValidationErrors()
278
+ if (txErrors.length > 0) {
279
+ const msg = this._errorMsg(`invalid transactions: ${txErrors.join(' ')}`)
280
+ throw EthereumJSErrorWithoutCode(msg)
281
+ }
282
+ }
283
+
284
+ if (onlyHeader) {
285
+ return
286
+ }
287
+
288
+ if (verifyTxs) {
289
+ for (const [index, tx] of this.transactions.entries()) {
290
+ if (!tx.isSigned()) {
291
+ const msg = this._errorMsg(
292
+ `invalid transactions: transaction at index ${index} is unsigned`,
293
+ )
294
+ throw EthereumJSErrorWithoutCode(msg)
295
+ }
296
+ }
297
+ }
298
+
299
+ if (!(await this.transactionsTrieIsValid())) {
300
+ const msg = this._errorMsg('invalid transaction trie')
301
+ throw EthereumJSErrorWithoutCode(msg)
302
+ }
303
+
304
+ if (!this.uncleHashIsValid()) {
305
+ const msg = this._errorMsg('invalid uncle hash')
306
+ throw EthereumJSErrorWithoutCode(msg)
307
+ }
308
+
309
+ if (this.common.isActivatedEIP(4895) && !(await this.withdrawalsTrieIsValid())) {
310
+ const msg = this._errorMsg('invalid withdrawals trie')
311
+ throw EthereumJSErrorWithoutCode(msg)
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Validates that blob gas fee for each transaction is greater than or equal to the
317
+ * blobGasPrice for the block and that total blob gas in block is less than maximum
318
+ * blob gas per block
319
+ * @param parentHeader header of parent block
320
+ */
321
+ validateBlobTransactions(parentHeader: BlockHeader) {
322
+ if (this.common.isActivatedEIP(4844)) {
323
+ const blobGasLimit = this.common.getBlobGasSchedule().maxBlobGasPerBlock
324
+ const blobGasPerBlob = this.common.param('blobGasPerBlob')
325
+ let blobGasUsed = BIGINT_0
326
+
327
+ const expectedExcessBlobGas = parentHeader.calcNextExcessBlobGas(this.common)
328
+ if (this.header.excessBlobGas !== expectedExcessBlobGas) {
329
+ throw EthereumJSErrorWithoutCode(
330
+ `block excessBlobGas mismatch: have ${this.header.excessBlobGas}, want ${expectedExcessBlobGas}`,
331
+ )
332
+ }
333
+
334
+ let blobGasPrice
335
+
336
+ for (const tx of this.transactions) {
337
+ if (tx instanceof Blob4844Tx) {
338
+ blobGasPrice = blobGasPrice ?? this.header.getBlobGasPrice()
339
+ if (tx.maxFeePerBlobGas < blobGasPrice) {
340
+ throw EthereumJSErrorWithoutCode(
341
+ `blob transaction maxFeePerBlobGas ${
342
+ tx.maxFeePerBlobGas
343
+ } < than block blob gas price ${blobGasPrice} - ${this.errorStr()}`,
344
+ )
345
+ }
346
+
347
+ blobGasUsed += BigInt(tx.blobVersionedHashes.length) * blobGasPerBlob
348
+
349
+ if (blobGasUsed > blobGasLimit) {
350
+ throw EthereumJSErrorWithoutCode(
351
+ `tx causes total blob gas of ${blobGasUsed} to exceed maximum blob gas per block of ${blobGasLimit}`,
352
+ )
353
+ }
354
+ }
355
+ }
356
+
357
+ if (this.header.blobGasUsed !== blobGasUsed) {
358
+ throw EthereumJSErrorWithoutCode(
359
+ `block blobGasUsed mismatch: have ${this.header.blobGasUsed}, want ${blobGasUsed}`,
360
+ )
361
+ }
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Validates the uncle's hash.
367
+ * @returns true if the uncle's hash is valid, false otherwise.
368
+ */
369
+ uncleHashIsValid(): boolean {
370
+ if (this.uncleHeaders.length === 0) {
371
+ return equalsBytes(KECCAK256_RLP_ARRAY, this.header.uncleHash)
372
+ }
373
+ const uncles = this.uncleHeaders.map((uh) => uh.raw())
374
+ const raw = RLP.encode(uncles)
375
+ return equalsBytes(this.keccakFunction(raw), this.header.uncleHash)
376
+ }
377
+
378
+ /**
379
+ * Validates the withdrawal root
380
+ * @returns true if the withdrawals trie root is valid, false otherwise
381
+ */
382
+ async withdrawalsTrieIsValid(): Promise<boolean> {
383
+ if (!this.common.isActivatedEIP(4895)) {
384
+ throw EthereumJSErrorWithoutCode('EIP 4895 is not activated')
385
+ }
386
+
387
+ let result
388
+ if (this.withdrawals!.length === 0) {
389
+ result = equalsBytes(this.header.withdrawalsRoot!, KECCAK256_RLP)
390
+ return result
391
+ }
392
+
393
+ if (this.cache.withdrawalsTrieRoot === undefined) {
394
+ this.cache.withdrawalsTrieRoot = await genWithdrawalsTrieRoot(
395
+ this.withdrawals!,
396
+ new MerklePatriciaTrie({ common: this.common }),
397
+ )
398
+ }
399
+ result = equalsBytes(this.cache.withdrawalsTrieRoot, this.header.withdrawalsRoot!)
400
+ return result
401
+ }
402
+
403
+ /**
404
+ * Consistency checks for uncles included in the block, if any.
405
+ *
406
+ * Throws if invalid.
407
+ *
408
+ * The rules for uncles checked are the following:
409
+ * Header has at most 2 uncles.
410
+ * Header does not count an uncle twice.
411
+ */
412
+ validateUncles() {
413
+ if (this.isGenesis()) {
414
+ return
415
+ }
416
+
417
+ // Header has at most 2 uncles
418
+ if (this.uncleHeaders.length > 2) {
419
+ const msg = this._errorMsg('too many uncle headers')
420
+ throw EthereumJSErrorWithoutCode(msg)
421
+ }
422
+
423
+ // Header does not count an uncle twice.
424
+ const uncleHashes = this.uncleHeaders.map((header) => bytesToHex(header.hash()))
425
+ if (!(new Set(uncleHashes).size === uncleHashes.length)) {
426
+ const msg = this._errorMsg('duplicate uncles')
427
+ throw EthereumJSErrorWithoutCode(msg)
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Validates if the block gasLimit remains in the boundaries set by the protocol.
433
+ * Throws if invalid
434
+ *
435
+ * @param parentBlock - the parent of this `Block`
436
+ */
437
+ validateGasLimit(parentBlock: Block) {
438
+ return this.header.validateGasLimit(parentBlock.header)
439
+ }
440
+
441
+ /**
442
+ * Returns the block in JSON format.
443
+ */
444
+ toJSON(): JSONBlock {
445
+ const withdrawalsAttr = this.withdrawals
446
+ ? {
447
+ withdrawals: this.withdrawals.map((wt) => wt.toJSON()),
448
+ }
449
+ : {}
450
+ return {
451
+ header: this.header.toJSON(),
452
+ transactions: this.transactions.map((tx) => tx.toJSON()),
453
+ uncleHeaders: this.uncleHeaders.map((uh) => uh.toJSON()),
454
+ ...withdrawalsAttr,
455
+ }
456
+ }
457
+
458
+ /**
459
+ * Maps the block properties to the execution payload structure from the beacon chain,
460
+ * see https://github.com/ethereum/consensus-specs/blob/dev/specs/bellatrix/beacon-chain.md#ExecutionPayload
461
+ *
462
+ * @returns dict with the execution payload parameters with camel case naming
463
+ */
464
+ toExecutionPayload(): ExecutionPayload {
465
+ const blockJSON = this.toJSON()
466
+ const header = blockJSON.header!
467
+ const transactions = this.transactions.map((tx) => bytesToHex(tx.serialize())) ?? []
468
+ const withdrawalsArr = blockJSON.withdrawals ? { withdrawals: blockJSON.withdrawals } : {}
469
+
470
+ const executionPayload: ExecutionPayload = {
471
+ blockNumber: header.number!,
472
+ parentHash: header.parentHash!,
473
+ feeRecipient: header.coinbase!,
474
+ stateRoot: header.stateRoot!,
475
+ receiptsRoot: header.receiptTrie!,
476
+ logsBloom: header.logsBloom!,
477
+ gasLimit: header.gasLimit!,
478
+ gasUsed: header.gasUsed!,
479
+ timestamp: header.timestamp!,
480
+ extraData: header.extraData!,
481
+ baseFeePerGas: header.baseFeePerGas!,
482
+ blobGasUsed: header.blobGasUsed,
483
+ excessBlobGas: header.excessBlobGas,
484
+ blockHash: bytesToHex(this.hash()),
485
+ prevRandao: header.mixHash!,
486
+ transactions,
487
+ ...withdrawalsArr,
488
+ parentBeaconBlockRoot: header.parentBeaconBlockRoot,
489
+ requestsHash: header.requestsHash,
490
+ }
491
+
492
+ return executionPayload
493
+ }
494
+
495
+ /**
496
+ * Return a compact error string representation of the object
497
+ */
498
+ public errorStr() {
499
+ let hash = ''
500
+ try {
501
+ hash = bytesToHex(this.hash())
502
+ } catch {
503
+ hash = 'error'
504
+ }
505
+ let hf = ''
506
+ try {
507
+ hf = this.common.hardfork()
508
+ } catch {
509
+ hf = 'error'
510
+ }
511
+ let errorStr = `block number=${this.header.number} hash=${hash} `
512
+ errorStr += `hf=${hf} baseFeePerGas=${this.header.baseFeePerGas ?? 'none'} `
513
+ errorStr += `txs=${this.transactions.length} uncles=${this.uncleHeaders.length}`
514
+ return errorStr
515
+ }
516
+
517
+ /**
518
+ * Internal helper function to create an annotated error message
519
+ *
520
+ * @param msg Base error message
521
+ * @hidden
522
+ */
523
+ protected _errorMsg(msg: string) {
524
+ return `${msg} (${this.errorStr()})`
525
+ }
526
+ }