@feelyourprotocol/vm 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 (115) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +583 -0
  3. package/dist/cjs/bloom/index.d.ts +29 -0
  4. package/dist/cjs/bloom/index.d.ts.map +1 -0
  5. package/dist/cjs/bloom/index.js +76 -0
  6. package/dist/cjs/bloom/index.js.map +1 -0
  7. package/dist/cjs/buildBlock.d.ts +118 -0
  8. package/dist/cjs/buildBlock.d.ts.map +1 -0
  9. package/dist/cjs/buildBlock.js +363 -0
  10. package/dist/cjs/buildBlock.js.map +1 -0
  11. package/dist/cjs/constructors.d.ts +9 -0
  12. package/dist/cjs/constructors.d.ts.map +1 -0
  13. package/dist/cjs/constructors.js +75 -0
  14. package/dist/cjs/constructors.js.map +1 -0
  15. package/dist/cjs/emitEVMProfile.d.ts +9 -0
  16. package/dist/cjs/emitEVMProfile.d.ts.map +1 -0
  17. package/dist/cjs/emitEVMProfile.js +130 -0
  18. package/dist/cjs/emitEVMProfile.js.map +1 -0
  19. package/dist/cjs/index.d.ts +11 -0
  20. package/dist/cjs/index.d.ts.map +1 -0
  21. package/dist/cjs/index.js +36 -0
  22. package/dist/cjs/index.js.map +1 -0
  23. package/dist/cjs/package.json +3 -0
  24. package/dist/cjs/params.d.ts +3 -0
  25. package/dist/cjs/params.d.ts.map +1 -0
  26. package/dist/cjs/params.js +105 -0
  27. package/dist/cjs/params.js.map +1 -0
  28. package/dist/cjs/requests.d.ts +11 -0
  29. package/dist/cjs/requests.d.ts.map +1 -0
  30. package/dist/cjs/requests.js +208 -0
  31. package/dist/cjs/requests.js.map +1 -0
  32. package/dist/cjs/runBlock.d.ts +35 -0
  33. package/dist/cjs/runBlock.d.ts.map +1 -0
  34. package/dist/cjs/runBlock.js +797 -0
  35. package/dist/cjs/runBlock.js.map +1 -0
  36. package/dist/cjs/runFrameTx.d.ts +18 -0
  37. package/dist/cjs/runFrameTx.d.ts.map +1 -0
  38. package/dist/cjs/runFrameTx.js +313 -0
  39. package/dist/cjs/runFrameTx.js.map +1 -0
  40. package/dist/cjs/runTx.d.ts +18 -0
  41. package/dist/cjs/runTx.d.ts.map +1 -0
  42. package/dist/cjs/runTx.js +900 -0
  43. package/dist/cjs/runTx.js.map +1 -0
  44. package/dist/cjs/types.d.ts +452 -0
  45. package/dist/cjs/types.d.ts.map +1 -0
  46. package/dist/cjs/types.js +3 -0
  47. package/dist/cjs/types.js.map +1 -0
  48. package/dist/cjs/vm.d.ts +75 -0
  49. package/dist/cjs/vm.d.ts.map +1 -0
  50. package/dist/cjs/vm.js +111 -0
  51. package/dist/cjs/vm.js.map +1 -0
  52. package/dist/esm/bloom/index.d.ts +29 -0
  53. package/dist/esm/bloom/index.d.ts.map +1 -0
  54. package/dist/esm/bloom/index.js +72 -0
  55. package/dist/esm/bloom/index.js.map +1 -0
  56. package/dist/esm/buildBlock.d.ts +118 -0
  57. package/dist/esm/buildBlock.d.ts.map +1 -0
  58. package/dist/esm/buildBlock.js +358 -0
  59. package/dist/esm/buildBlock.js.map +1 -0
  60. package/dist/esm/constructors.d.ts +9 -0
  61. package/dist/esm/constructors.d.ts.map +1 -0
  62. package/dist/esm/constructors.js +72 -0
  63. package/dist/esm/constructors.js.map +1 -0
  64. package/dist/esm/emitEVMProfile.d.ts +9 -0
  65. package/dist/esm/emitEVMProfile.d.ts.map +1 -0
  66. package/dist/esm/emitEVMProfile.js +127 -0
  67. package/dist/esm/emitEVMProfile.js.map +1 -0
  68. package/dist/esm/index.d.ts +11 -0
  69. package/dist/esm/index.d.ts.map +1 -0
  70. package/dist/esm/index.js +11 -0
  71. package/dist/esm/index.js.map +1 -0
  72. package/dist/esm/package.json +3 -0
  73. package/dist/esm/params.d.ts +3 -0
  74. package/dist/esm/params.d.ts.map +1 -0
  75. package/dist/esm/params.js +102 -0
  76. package/dist/esm/params.js.map +1 -0
  77. package/dist/esm/requests.d.ts +11 -0
  78. package/dist/esm/requests.d.ts.map +1 -0
  79. package/dist/esm/requests.js +204 -0
  80. package/dist/esm/requests.js.map +1 -0
  81. package/dist/esm/runBlock.d.ts +35 -0
  82. package/dist/esm/runBlock.d.ts.map +1 -0
  83. package/dist/esm/runBlock.js +790 -0
  84. package/dist/esm/runBlock.js.map +1 -0
  85. package/dist/esm/runFrameTx.d.ts +18 -0
  86. package/dist/esm/runFrameTx.d.ts.map +1 -0
  87. package/dist/esm/runFrameTx.js +310 -0
  88. package/dist/esm/runFrameTx.js.map +1 -0
  89. package/dist/esm/runTx.d.ts +18 -0
  90. package/dist/esm/runTx.d.ts.map +1 -0
  91. package/dist/esm/runTx.js +896 -0
  92. package/dist/esm/runTx.js.map +1 -0
  93. package/dist/esm/types.d.ts +452 -0
  94. package/dist/esm/types.d.ts.map +1 -0
  95. package/dist/esm/types.js +2 -0
  96. package/dist/esm/types.js.map +1 -0
  97. package/dist/esm/vm.d.ts +75 -0
  98. package/dist/esm/vm.d.ts.map +1 -0
  99. package/dist/esm/vm.js +107 -0
  100. package/dist/esm/vm.js.map +1 -0
  101. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  102. package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
  103. package/package.json +117 -0
  104. package/src/bloom/index.ts +83 -0
  105. package/src/buildBlock.ts +470 -0
  106. package/src/constructors.ts +91 -0
  107. package/src/emitEVMProfile.ts +151 -0
  108. package/src/index.ts +10 -0
  109. package/src/params.ts +104 -0
  110. package/src/requests.ts +293 -0
  111. package/src/runBlock.ts +1022 -0
  112. package/src/runFrameTx.ts +411 -0
  113. package/src/runTx.ts +1203 -0
  114. package/src/types.ts +511 -0
  115. package/src/vm.ts +147 -0
package/package.json ADDED
@@ -0,0 +1,117 @@
1
+ {
2
+ "name": "@feelyourprotocol/vm",
3
+ "version": "8141.0.0",
4
+ "description": "An Ethereum VM implementation",
5
+ "keywords": [
6
+ "ethereum",
7
+ "VM"
8
+ ],
9
+ "homepage": "https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm#readme",
10
+ "bugs": {
11
+ "url": "https://github.com/ethereumjs/ethereumjs-monorepo/issues?q=is%3Aissue+label%3A%22package%3A+vm%22"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/ethereumjs/ethereumjs-monorepo.git"
16
+ },
17
+ "license": "MPL-2.0",
18
+ "author": "EthereumJS Team",
19
+ "contributors": [
20
+ "mjbecze <mjbecze@gmail.com>",
21
+ "Alex Beregszaszi <alex@rtfs.hu>"
22
+ ],
23
+ "type": "module",
24
+ "sideEffects": false,
25
+ "main": "dist/cjs/index.js",
26
+ "module": "dist/esm/index.js",
27
+ "exports": {
28
+ ".": {
29
+ "import": {
30
+ "typescript": "./src/index.ts",
31
+ "default": "./dist/esm/index.js"
32
+ },
33
+ "require": "./dist/cjs/index.js"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "src"
39
+ ],
40
+ "scripts": {
41
+ "benchmarks": "node --max-old-space-size=4096 ./benchmarks/run.js benchmarks mainnetBlocks:10",
42
+ "biome": "npx @biomejs/biome check",
43
+ "biome:fix": "npx @biomejs/biome check --write",
44
+ "build": "../../config/cli/ts-build.sh",
45
+ "build:benchmarks": "npm run build && tsc -p tsconfig.benchmarks.json",
46
+ "clean": "../../config/cli/clean-package.sh",
47
+ "coverage": "DEBUG=ethjs npx vitest run -c ./vitest.config.coverage.ts",
48
+ "docs:build": "typedoc --options typedoc.mjs",
49
+ "examples": "tsx ../../scripts/examples-runner.ts -- vm",
50
+ "examples:build": "npx embedme README.md",
51
+ "formatTest": "node ./scripts/formatTest",
52
+ "lint": "npm run biome && eslint --config ./eslint.config.mjs .",
53
+ "lint:fix": "npm run biome:fix && eslint --fix --config ./eslint.config.mjs .",
54
+ "prepublishOnly": "../../config/cli/prepublish.sh && npm run test:buildIntegrity",
55
+ "profiling": "0x ./benchmarks/run.js profiling",
56
+ "test": "echo \"[INFO] Generic test cmd not used. See package.json for more specific test run cmds.\"",
57
+ "test:API": "npx vitest run -c ./vitest.config.ts ./test/api/",
58
+ "test:browser": "npx vitest run --config=./vitest.config.browser.mts",
59
+ "test:blockchain": "tsx ./test/tester/vitest-wrapper-blockchain.ts",
60
+ "test:blockchain:allForks": "npm run test:blockchain:newForks && npm run test:blockchain:oldForks && npm run test:blockchain:transitionForks",
61
+ "test:blockchain:newForks": "echo 'Prague' | xargs -n1 | xargs -I {} sh -c 'VITE_FORK={} VITE_VERIFY_TEST_AMOUNT_ALL_TESTS=1 npx vitest test/tester/blockchain.spec.ts'",
62
+ "test:blockchain:oldForks": "echo 'Chainstart Homestead dao TangerineWhistle SpuriousDragon Byzantium Constantinople Petersburg Istanbul MuirGlacier Berlin London Paris Shanghai Cancun' | xargs -n1 | xargs -I {} sh -c 'VITE_FORK={} VITE_VERIFY_TEST_AMOUNT_ALL_TESTS=1 npx vitest test/tester/blockchain.spec.ts'",
63
+ "test:blockchain:transitionForks": "echo 'ByzantiumToConstantinopleFixAt5 EIP158ToByzantiumAt5 FrontierToHomesteadAt5 HomesteadToDaoAt5 HomesteadToEIP150At5 BerlinToLondonAt5' | xargs -n1 | xargs -I {} sh -c 'VITE_FORK={} VITE_VERIFY_TEST_AMOUNT_ALL_TESTS=1 npx vitest test/tester/blockchain.spec.ts'",
64
+ "test:blockchain:buildIntegrity": "npm run test:blockchain -- --file='randomStatetest303'",
65
+ "test:buildIntegrity": "npm run test:state -- --test='stackOverflow'",
66
+ "test:est:stable:state": "TEST_PATH=../execution-spec-tests/stable/state_tests npx vitest run test/tester/executionSpecState.test.ts",
67
+ "test:est:stable:blockchain": "TEST_PATH=../execution-spec-tests/stable/blockchain_tests npx vitest run test/tester/executionSpecBlockchain.test.ts",
68
+ "test:est:dev:state": "TEST_PATH=../execution-spec-tests/dev/state_tests npx vitest run test/tester/executionSpecState.test.ts",
69
+ "test:est:dev:blockchain": "npm run test:est:dev:blockchain:v510",
70
+ "test:est:dev:blockchain:v510": "TEST_PATH=../execution-spec-tests/dev/blockchain_tests/amsterdam/v510_mixed_with_other_eips npx vitest run test/tester/executionSpecBlockchain.test.ts",
71
+ "test:est:dev:blockchain:v301": "TEST_PATH=../execution-spec-tests/dev/blockchain_tests/amsterdam/v301_single_bal_no_bal_defs npx vitest run test/tester/executionSpecBlockchain.test.ts",
72
+ "test:est:dev:blockchain:v200": "TEST_PATH=../execution-spec-tests/dev/blockchain_tests/amsterdam/v200_bal_defs_somewhat_outdated npx vitest run test/tester/executionSpecBlockchain.test.ts",
73
+ "test:state": "tsx ./test/tester/vitest-wrapper.ts",
74
+ "test:state:allForks": "npm run test:state:newForks && npm run test:state:oldForks && npm run test:state:transitionForks",
75
+ "test:state:newForks": "echo 'Prague' | xargs -n1 | xargs -I {} sh -c 'VITE_FORK={} VITE_VERIFY_TEST_AMOUNT_ALL_TESTS=1 npx vitest test/tester/state.spec.ts'",
76
+ "test:state:oldForks": "echo 'Chainstart Homestead dao TangerineWhistle SpuriousDragon Byzantium Constantinople Petersburg Istanbul MuirGlacier Berlin London Paris Shanghai Cancun' | xargs -n1 | xargs -I {} sh -c 'VITE_FORK={} VITE_VERIFY_TEST_AMOUNT_ALL_TESTS=1 npx vitest test/tester/state.spec.ts'",
77
+ "test:state:transitionForks": "echo 'ByzantiumToConstantinopleFixAt5 EIP158ToByzantiumAt5 FrontierToHomesteadAt5 HomesteadToDaoAt5 HomesteadToEIP150At5 BerlinToLondonAt5' | xargs -n1 | xargs -I {} sh -c 'VITE_FORK={} VITE_VERIFY_TEST_AMOUNT_ALL_TESTS=1 npx vitest test/tester/state.spec.ts'",
78
+ "test:state:slow": "VITE_RUN_SKIPPED=slow npx vitest test/tester/state.spec.ts",
79
+ "test:analysis:report": "npx tsx ./test/tester/util/testAnalysisReportAI.ts",
80
+ "tsc": "../../config/cli/ts-compile.sh"
81
+ },
82
+ "dependencies": {
83
+ "@feelyourprotocol/block": "^8141.0.0",
84
+ "@feelyourprotocol/common": "^8141.0.0",
85
+ "@feelyourprotocol/evm": "^8141.0.0",
86
+ "@feelyourprotocol/mpt": "^8141.0.0",
87
+ "@feelyourprotocol/rlp": "^8141.0.0",
88
+ "@feelyourprotocol/statemanager": "^8141.0.0",
89
+ "@feelyourprotocol/tx": "^8141.0.0",
90
+ "@feelyourprotocol/util": "^8141.0.0",
91
+ "@noble/hashes": "^2.0.1",
92
+ "debug": "^4.4.0",
93
+ "eventemitter3": "^5.0.1"
94
+ },
95
+ "devDependencies": {
96
+ "@feelyourprotocol/blockchain": "^8141.0.0",
97
+ "@ethereumjs/ethash": "^10.0.0",
98
+ "@ethereumjs/testdata": "1.0.0",
99
+ "@noble/curves": "^2.0.1",
100
+ "@paulmillr/trusted-setups": "^0.2.0",
101
+ "@types/benchmark": "^2.1.5",
102
+ "@types/debug": "^4.1.12",
103
+ "@types/minimist": "^1.2.5",
104
+ "@types/node-dir": "^0.0.37",
105
+ "benchmark": "^2.1.4",
106
+ "mcl-wasm": "^1.8.0",
107
+ "micro-eth-signer": "^0.15.0",
108
+ "minimist": "^1.2.8",
109
+ "node-dir": "^0.1.17",
110
+ "solc": "^0.8.28",
111
+ "viem": "^2.38.6",
112
+ "yargs": "^17.7.2"
113
+ },
114
+ "engines": {
115
+ "node": ">=20"
116
+ }
117
+ }
@@ -0,0 +1,83 @@
1
+ import { EthereumJSErrorWithoutCode } from '@feelyourprotocol/util'
2
+ import { keccak_256 } from '@noble/hashes/sha3.js'
3
+
4
+ import type { Common } from '@feelyourprotocol/common'
5
+
6
+ const BYTE_SIZE = 256
7
+
8
+ export class Bloom {
9
+ bitvector: Uint8Array
10
+ keccakFunction: (msg: Uint8Array) => Uint8Array
11
+
12
+ /**
13
+ * Represents a Bloom filter.
14
+ */
15
+ constructor(bitvector?: Uint8Array, common?: Common) {
16
+ if (common?.customCrypto.keccak256 !== undefined) {
17
+ this.keccakFunction = common.customCrypto.keccak256
18
+ } else {
19
+ this.keccakFunction = keccak_256
20
+ }
21
+ if (!bitvector) {
22
+ this.bitvector = new Uint8Array(BYTE_SIZE)
23
+ } else {
24
+ if (bitvector.length !== BYTE_SIZE)
25
+ throw EthereumJSErrorWithoutCode('bitvectors must be 2048 bits long')
26
+ this.bitvector = bitvector
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Adds an element to a bit vector of a 64 byte bloom filter.
32
+ * @param e - The element to add
33
+ */
34
+ add(e: Uint8Array) {
35
+ e = this.keccakFunction(e)
36
+ const mask = 2047 // binary 11111111111
37
+
38
+ for (let i = 0; i < 3; i++) {
39
+ const first2bytes = new DataView(e.buffer).getUint16(i * 2)
40
+ const loc = mask & first2bytes
41
+ const byteLoc = loc >> 3
42
+ const bitLoc = 1 << (loc % 8)
43
+ this.bitvector[BYTE_SIZE - byteLoc - 1] |= bitLoc
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Checks if an element is in the bloom.
49
+ * @param e - The element to check
50
+ */
51
+ check(e: Uint8Array): boolean {
52
+ e = this.keccakFunction(e)
53
+ const mask = 2047 // binary 11111111111
54
+ let match = true
55
+
56
+ for (let i = 0; i < 3 && match; i++) {
57
+ const first2bytes = new DataView(e.buffer).getUint16(i * 2)
58
+ const loc = mask & first2bytes
59
+ const byteLoc = loc >> 3
60
+ const bitLoc = 1 << (loc % 8)
61
+ match = (this.bitvector[BYTE_SIZE - byteLoc - 1] & bitLoc) !== 0
62
+ }
63
+
64
+ return Boolean(match)
65
+ }
66
+
67
+ /**
68
+ * Checks if multiple topics are in a bloom.
69
+ * @returns `true` if every topic is in the bloom
70
+ */
71
+ multiCheck(topics: Uint8Array[]): boolean {
72
+ return topics.every((t: Uint8Array) => this.check(t))
73
+ }
74
+
75
+ /**
76
+ * Bitwise or blooms together.
77
+ */
78
+ or(bloom: Bloom) {
79
+ for (let i = 0; i <= BYTE_SIZE; i++) {
80
+ this.bitvector[i] = this.bitvector[i] | bloom.bitvector[i]
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,470 @@
1
+ import {
2
+ createBlock,
3
+ createSealedCliqueBlock,
4
+ genRequestsRoot,
5
+ genTransactionsTrieRoot,
6
+ genWithdrawalsTrieRoot,
7
+ } from '@feelyourprotocol/block'
8
+ import { ConsensusType, Hardfork } from '@feelyourprotocol/common'
9
+ import { MerklePatriciaTrie } from '@feelyourprotocol/mpt'
10
+ import { RLP } from '@feelyourprotocol/rlp'
11
+ import {
12
+ Blob4844Tx,
13
+ NetworkWrapperType,
14
+ createMinimal4844TxFromNetworkWrapper,
15
+ } from '@feelyourprotocol/tx'
16
+ import {
17
+ Address,
18
+ BIGINT_0,
19
+ BIGINT_1,
20
+ BIGINT_2,
21
+ EthereumJSErrorWithoutCode,
22
+ GWEI_TO_WEI,
23
+ KECCAK256_RLP,
24
+ TypeOutput,
25
+ createWithdrawal,
26
+ createZeroAddress,
27
+ toBytes,
28
+ toType,
29
+ } from '@feelyourprotocol/util'
30
+ import { sha256 } from '@noble/hashes/sha2.js'
31
+
32
+ import { Bloom } from './bloom/index.ts'
33
+ import { runTx } from './index.ts'
34
+ import { accumulateRequests } from './requests.ts'
35
+ import {
36
+ accumulateParentBeaconBlockRoot,
37
+ accumulateParentBlockHash,
38
+ calculateMinerReward,
39
+ encodeReceipt,
40
+ rewardAccount,
41
+ } from './runBlock.ts'
42
+
43
+ import type { Block, HeaderData } from '@feelyourprotocol/block'
44
+ import type { TypedTransaction } from '@feelyourprotocol/tx'
45
+ import type { Withdrawal } from '@feelyourprotocol/util'
46
+ import type { BuildBlockOpts, BuilderOpts, RunTxResult, SealBlockOpts } from './types.ts'
47
+ import type { VM } from './vm.ts'
48
+
49
+ export type BuildStatus = (typeof BuildStatus)[keyof typeof BuildStatus]
50
+ export const BuildStatus = {
51
+ Reverted: 'reverted',
52
+ Build: 'build',
53
+ Pending: 'pending',
54
+ } as const
55
+
56
+ type BlockStatus =
57
+ | { status: typeof BuildStatus.Pending | typeof BuildStatus.Reverted }
58
+ | { status: typeof BuildStatus.Build; block: Block }
59
+
60
+ export class BlockBuilder {
61
+ /**
62
+ * The cumulative gas used by the transactions added to the block.
63
+ */
64
+ gasUsed = BIGINT_0
65
+ /**
66
+ * The cumulative blob gas used by the blobs in a block
67
+ */
68
+ blobGasUsed = BIGINT_0
69
+ /**
70
+ * Value of the block, represented by the final transaction fees
71
+ * accruing to the miner.
72
+ */
73
+ private _minerValue = BIGINT_0
74
+
75
+ private readonly vm: VM
76
+ private blockOpts: BuilderOpts
77
+ private headerData: HeaderData
78
+ private transactions: TypedTransaction[] = []
79
+ private transactionResults: RunTxResult[] = []
80
+ private withdrawals?: Withdrawal[]
81
+ private checkpointed = false
82
+ private blockStatus: BlockStatus = { status: BuildStatus.Pending }
83
+
84
+ get transactionReceipts() {
85
+ return this.transactionResults.map((result) => result.receipt)
86
+ }
87
+
88
+ get minerValue() {
89
+ return this._minerValue
90
+ }
91
+
92
+ constructor(vm: VM, opts: BuildBlockOpts) {
93
+ this.vm = vm
94
+ this.blockOpts = { putBlockIntoBlockchain: true, ...opts.blockOpts, common: this.vm.common }
95
+
96
+ this.headerData = {
97
+ ...opts.headerData,
98
+ parentHash: opts.headerData?.parentHash ?? opts.parentBlock.hash(),
99
+ number: opts.headerData?.number ?? opts.parentBlock.header.number + BIGINT_1,
100
+ gasLimit: opts.headerData?.gasLimit ?? opts.parentBlock.header.gasLimit,
101
+ timestamp: opts.headerData?.timestamp ?? Math.round(Date.now() / 1000),
102
+ }
103
+ this.withdrawals = opts.withdrawals?.map(createWithdrawal)
104
+
105
+ if (
106
+ this.vm.common.isActivatedEIP(1559) &&
107
+ typeof this.headerData.baseFeePerGas === 'undefined'
108
+ ) {
109
+ if (this.headerData.number === vm.common.hardforkBlock(Hardfork.London)) {
110
+ this.headerData.baseFeePerGas = vm.common.param('initialBaseFee')
111
+ } else {
112
+ this.headerData.baseFeePerGas = opts.parentBlock.header.calcNextBaseFee()
113
+ }
114
+ }
115
+
116
+ if (typeof this.headerData.gasLimit === 'undefined') {
117
+ if (this.headerData.number === vm.common.hardforkBlock(Hardfork.London)) {
118
+ this.headerData.gasLimit = opts.parentBlock.header.gasLimit * BIGINT_2
119
+ } else {
120
+ this.headerData.gasLimit = opts.parentBlock.header.gasLimit
121
+ }
122
+ }
123
+
124
+ if (
125
+ this.vm.common.isActivatedEIP(4844) &&
126
+ typeof this.headerData.excessBlobGas === 'undefined'
127
+ ) {
128
+ this.headerData.excessBlobGas = opts.parentBlock.header.calcNextExcessBlobGas(this.vm.common)
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Throws if the block has already been built or reverted.
134
+ */
135
+ private checkStatus() {
136
+ if (this.blockStatus.status === BuildStatus.Build) {
137
+ throw EthereumJSErrorWithoutCode('Block has already been built')
138
+ }
139
+ if (this.blockStatus.status === BuildStatus.Reverted) {
140
+ throw EthereumJSErrorWithoutCode('State has already been reverted')
141
+ }
142
+ }
143
+
144
+ public getStatus(): BlockStatus {
145
+ return this.blockStatus
146
+ }
147
+
148
+ /**
149
+ * Calculates and returns the transactionsTrie for the block.
150
+ */
151
+ public async transactionsTrie() {
152
+ return genTransactionsTrieRoot(
153
+ this.transactions,
154
+ new MerklePatriciaTrie({ common: this.vm.common }),
155
+ )
156
+ }
157
+
158
+ /**
159
+ * Calculates and returns the logs bloom for the block.
160
+ */
161
+ public logsBloom() {
162
+ const bloom = new Bloom(undefined, this.vm.common)
163
+ for (const txResult of this.transactionResults) {
164
+ // Combine blooms via bitwise OR
165
+ bloom.or(txResult.bloom)
166
+ }
167
+ return bloom.bitvector
168
+ }
169
+
170
+ /**
171
+ * Calculates and returns the receiptTrie for the block.
172
+ */
173
+ public async receiptTrie() {
174
+ if (this.transactionResults.length === 0) {
175
+ return KECCAK256_RLP
176
+ }
177
+ const receiptTrie = new MerklePatriciaTrie({ common: this.vm.common })
178
+ for (const [i, txResult] of this.transactionResults.entries()) {
179
+ const tx = this.transactions[i]
180
+ const encodedReceipt = encodeReceipt(txResult.receipt, tx.type)
181
+ await receiptTrie.put(RLP.encode(i), encodedReceipt)
182
+ }
183
+ return receiptTrie.root()
184
+ }
185
+
186
+ /**
187
+ * Adds the block miner reward to the coinbase account.
188
+ */
189
+ private async rewardMiner() {
190
+ const minerReward = this.vm.common.param('minerReward')
191
+ const reward = calculateMinerReward(minerReward, 0)
192
+ const coinbase =
193
+ this.headerData.coinbase !== undefined
194
+ ? new Address(toBytes(this.headerData.coinbase))
195
+ : createZeroAddress()
196
+ await rewardAccount(this.vm.evm, coinbase, reward, this.vm.common)
197
+ }
198
+
199
+ /**
200
+ * Adds the withdrawal amount to the withdrawal address
201
+ */
202
+ private async processWithdrawals() {
203
+ for (const withdrawal of this.withdrawals ?? []) {
204
+ const { address, amount } = withdrawal
205
+ // If there is no amount to add, skip touching the account
206
+ // as per the implementation of other clients geth/nethermind
207
+ // although this should never happen as no withdrawals with 0
208
+ // amount should ever land up here.
209
+ if (amount === BIGINT_0) continue
210
+ // Withdrawal amount is represented in Gwei so needs to be
211
+ // converted to wei
212
+ await rewardAccount(this.vm.evm, address, amount * GWEI_TO_WEI, this.vm.common)
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Run and add a transaction to the block being built.
218
+ * Please note that this modifies the state of the VM.
219
+ * Throws if the transaction's gasLimit is greater than
220
+ * the remaining gas in the block.
221
+ */
222
+ async addTransaction(
223
+ tx: TypedTransaction,
224
+ {
225
+ skipHardForkValidation,
226
+ allowNoBlobs,
227
+ }: { skipHardForkValidation?: boolean; allowNoBlobs?: boolean } = {},
228
+ ) {
229
+ this.checkStatus()
230
+
231
+ if (!this.checkpointed) {
232
+ await this.vm.evm.journal.checkpoint()
233
+ this.checkpointed = true
234
+ }
235
+
236
+ // According to the Yellow Paper, a transaction's gas limit
237
+ // cannot be greater than the remaining gas in the block
238
+ const blockGasLimit = toType(this.headerData.gasLimit, TypeOutput.BigInt)
239
+
240
+ const blobGasPerBlob = this.vm.common.param('blobGasPerBlob')
241
+
242
+ const blockGasRemaining = blockGasLimit - this.gasUsed
243
+ if (tx.gasLimit > blockGasRemaining) {
244
+ throw EthereumJSErrorWithoutCode(
245
+ 'tx has a higher gas limit than the remaining gas in the block',
246
+ )
247
+ }
248
+ let blobGasUsed = undefined
249
+ if (tx instanceof Blob4844Tx) {
250
+ const { maxBlobGasPerBlock: blobGasLimit } = this.vm.common.getBlobGasSchedule()
251
+ if (
252
+ tx.networkWrapperVersion === NetworkWrapperType.EIP4844 &&
253
+ this.vm.common.isActivatedEIP(7594)
254
+ ) {
255
+ throw Error('eip4844 blob transaction for eip7594 activated fork')
256
+ } else if (
257
+ tx.networkWrapperVersion === NetworkWrapperType.EIP7594 &&
258
+ !this.vm.common.isActivatedEIP(7594)
259
+ ) {
260
+ throw Error('eip7594 blob transaction but eip not yet activated')
261
+ }
262
+
263
+ if (this.blockOpts.common?.isActivatedEIP(4844) === false) {
264
+ throw Error('eip4844 not activated yet for adding a blob transaction')
265
+ }
266
+ const blobTx = tx as Blob4844Tx
267
+
268
+ // Guard against the case if a tx came into the pool without blobs i.e. network wrapper payload
269
+ if (blobTx.blobs === undefined) {
270
+ // TODO: verify if we want this, do we want to allow the block builder to accept blob txs without the actual blobs?
271
+ // (these must have at least one `blobVersionedHashes`, this is verified at tx-level)
272
+ if (allowNoBlobs !== true) {
273
+ throw EthereumJSErrorWithoutCode('blobs missing for 4844 transaction')
274
+ }
275
+ }
276
+
277
+ if (this.blobGasUsed + BigInt(blobTx.numBlobs()) * blobGasPerBlob > blobGasLimit) {
278
+ throw EthereumJSErrorWithoutCode('block blob gas limit reached')
279
+ }
280
+
281
+ blobGasUsed = this.blobGasUsed
282
+ }
283
+ const header = {
284
+ ...this.headerData,
285
+ gasUsed: this.gasUsed,
286
+ // correct excessBlobGas should already part of headerData used above
287
+ blobGasUsed,
288
+ }
289
+
290
+ const blockData = { header, transactions: this.transactions }
291
+ const block = createBlock(blockData, this.blockOpts)
292
+
293
+ const result = await runTx(this.vm, { tx, block, skipHardForkValidation })
294
+
295
+ // If tx is a blob transaction, remove blobs/kzg commitments before adding to block per EIP-4844
296
+ if (tx instanceof Blob4844Tx) {
297
+ const txData = tx as Blob4844Tx
298
+ this.blobGasUsed += BigInt(txData.blobVersionedHashes.length) * blobGasPerBlob
299
+ tx = createMinimal4844TxFromNetworkWrapper(txData, {
300
+ common: this.blockOpts.common,
301
+ })
302
+ }
303
+ this.transactions.push(tx)
304
+ this.transactionResults.push(result)
305
+ this.gasUsed += result.totalGasSpent
306
+ this._minerValue += result.minerValue
307
+
308
+ return result
309
+ }
310
+
311
+ /**
312
+ * Reverts the checkpoint on the StateManager to reset the state from any transactions that have been run.
313
+ */
314
+ async revert() {
315
+ if (this.checkpointed) {
316
+ await this.vm.evm.journal.revert()
317
+ this.checkpointed = false
318
+ }
319
+ this.blockStatus = { status: BuildStatus.Reverted }
320
+ }
321
+
322
+ /**
323
+ * This method constructs the finalized block, including withdrawals and any CLRequests.
324
+ * It also:
325
+ * - Assigns the reward for miner (PoW)
326
+ * - Commits the checkpoint on the StateManager
327
+ * - Sets the tip of the VM's blockchain to this block
328
+ * For PoW, optionally seals the block with params `nonce` and `mixHash`,
329
+ * which is validated along with the block number and difficulty by ethash.
330
+ * For PoA, please pass `blockOption.cliqueSigner` into the buildBlock constructor,
331
+ * as the signer will be awarded the txs amount spent on gas as they are added.
332
+ *
333
+ * Note: we add CLRequests here because they can be generated at any time during the
334
+ * lifecycle of a pending block so need to be provided only when the block is finalized.
335
+ */
336
+ async build(sealOpts?: SealBlockOpts) {
337
+ this.checkStatus()
338
+ const blockOpts = this.blockOpts
339
+ const consensusType = this.vm.common.consensusType()
340
+
341
+ if (consensusType === ConsensusType.ProofOfWork) {
342
+ await this.rewardMiner()
343
+ }
344
+ await this.processWithdrawals()
345
+
346
+ const transactionsTrie = await this.transactionsTrie()
347
+ const withdrawalsRoot = this.withdrawals
348
+ ? await genWithdrawalsTrieRoot(
349
+ this.withdrawals,
350
+ new MerklePatriciaTrie({ common: this.vm.common }),
351
+ )
352
+ : undefined
353
+ const receiptTrie = await this.receiptTrie()
354
+ const logsBloom = this.logsBloom()
355
+ const gasUsed = this.gasUsed
356
+ // timestamp should already be set in constructor
357
+ const timestamp = this.headerData.timestamp ?? BIGINT_0
358
+
359
+ let blobGasUsed = undefined
360
+ if (this.vm.common.isActivatedEIP(4844)) {
361
+ blobGasUsed = this.blobGasUsed
362
+ }
363
+
364
+ let requests
365
+ let requestsHash
366
+ if (this.vm.common.isActivatedEIP(7685)) {
367
+ const sha256Function = this.vm.common.customCrypto.sha256 ?? sha256
368
+ requests = await accumulateRequests(this.vm, this.transactionResults)
369
+ requestsHash = genRequestsRoot(requests, sha256Function)
370
+ }
371
+
372
+ // get stateRoot after all the accumulateRequests etc have been done
373
+ const stateRoot = await this.vm.stateManager.getStateRoot()
374
+ const headerData = {
375
+ ...this.headerData,
376
+ stateRoot,
377
+ transactionsTrie,
378
+ withdrawalsRoot,
379
+ receiptTrie,
380
+ logsBloom,
381
+ gasUsed,
382
+ timestamp,
383
+ // correct excessBlobGas should already be part of headerData used above
384
+ blobGasUsed,
385
+ requestsHash,
386
+ }
387
+
388
+ if (consensusType === ConsensusType.ProofOfWork) {
389
+ headerData.nonce = sealOpts?.nonce ?? headerData.nonce
390
+ headerData.mixHash = sealOpts?.mixHash ?? headerData.mixHash
391
+ }
392
+
393
+ const blockData = {
394
+ header: headerData,
395
+ transactions: this.transactions,
396
+ withdrawals: this.withdrawals,
397
+ }
398
+
399
+ let block
400
+ const cs = this.blockOpts.cliqueSigner
401
+ if (cs !== undefined) {
402
+ block = createSealedCliqueBlock(blockData, cs, this.blockOpts)
403
+ } else {
404
+ block = createBlock(blockData, blockOpts)
405
+ }
406
+
407
+ if (this.blockOpts.putBlockIntoBlockchain === true) {
408
+ await this.vm.blockchain.putBlock(block)
409
+ }
410
+
411
+ this.blockStatus = { status: BuildStatus.Build, block }
412
+ if (this.checkpointed) {
413
+ await this.vm.evm.journal.commit()
414
+ this.checkpointed = false
415
+ }
416
+
417
+ return { block, requests }
418
+ }
419
+
420
+ async initState() {
421
+ if (this.vm.common.isActivatedEIP(4788)) {
422
+ if (!this.checkpointed) {
423
+ await this.vm.evm.journal.checkpoint()
424
+ this.checkpointed = true
425
+ }
426
+
427
+ const { parentBeaconBlockRoot, timestamp } = this.headerData
428
+ // timestamp should already be set in constructor
429
+ const timestampBigInt = toType(timestamp ?? 0, TypeOutput.BigInt)
430
+ const parentBeaconBlockRootBuf =
431
+ toType(parentBeaconBlockRoot!, TypeOutput.Uint8Array) ?? new Uint8Array(32)
432
+
433
+ await accumulateParentBeaconBlockRoot(this.vm, parentBeaconBlockRootBuf, timestampBigInt)
434
+ }
435
+ if (this.vm.common.isActivatedEIP(2935)) {
436
+ if (!this.checkpointed) {
437
+ await this.vm.evm.journal.checkpoint()
438
+ this.checkpointed = true
439
+ }
440
+
441
+ const { parentHash, number } = this.headerData
442
+ // timestamp should already be set in constructor
443
+ const numberBigInt = toType(number ?? 0, TypeOutput.BigInt)
444
+ const parentHashSanitized = toType(parentHash, TypeOutput.Uint8Array) ?? new Uint8Array(32)
445
+
446
+ await accumulateParentBlockHash(this.vm, numberBigInt, parentHashSanitized)
447
+ }
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Build a block on top of the current state
453
+ * by adding one transaction at a time.
454
+ *
455
+ * Creates a checkpoint on the StateManager and modifies the state
456
+ * as transactions are run. The checkpoint is committed on {@link BlockBuilder.build}
457
+ * or discarded with {@link BlockBuilder.revert}.
458
+ *
459
+ * @param {VM} vm
460
+ * @param {BuildBlockOpts} opts
461
+ * @returns An instance of {@link BlockBuilder} with methods:
462
+ * - {@link BlockBuilder.addTransaction}
463
+ * - {@link BlockBuilder.build}
464
+ * - {@link BlockBuilder.revert}
465
+ */
466
+ export async function buildBlock(vm: VM, opts: BuildBlockOpts): Promise<BlockBuilder> {
467
+ const blockBuilder = new BlockBuilder(vm, opts)
468
+ await blockBuilder.initState()
469
+ return blockBuilder
470
+ }