@feelyourprotocol/statemanager 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 +331 -0
- package/dist/cjs/cache/account.d.ts +85 -0
- package/dist/cjs/cache/account.d.ts.map +1 -0
- package/dist/cjs/cache/account.js +252 -0
- package/dist/cjs/cache/account.js.map +1 -0
- package/dist/cjs/cache/cache.d.ts +23 -0
- package/dist/cjs/cache/cache.d.ts.map +1 -0
- package/dist/cjs/cache/cache.js +31 -0
- package/dist/cjs/cache/cache.js.map +1 -0
- package/dist/cjs/cache/caches.d.ts +19 -0
- package/dist/cjs/cache/caches.d.ts.map +1 -0
- package/dist/cjs/cache/caches.js +107 -0
- package/dist/cjs/cache/caches.js.map +1 -0
- package/dist/cjs/cache/code.d.ts +87 -0
- package/dist/cjs/cache/code.d.ts.map +1 -0
- package/dist/cjs/cache/code.js +258 -0
- package/dist/cjs/cache/code.js.map +1 -0
- package/dist/cjs/cache/index.d.ts +7 -0
- package/dist/cjs/cache/index.d.ts.map +1 -0
- package/dist/cjs/cache/index.js +23 -0
- package/dist/cjs/cache/index.js.map +1 -0
- package/dist/cjs/cache/originalStorageCache.d.ts +21 -0
- package/dist/cjs/cache/originalStorageCache.d.ts.map +1 -0
- package/dist/cjs/cache/originalStorageCache.js +52 -0
- package/dist/cjs/cache/originalStorageCache.js.map +1 -0
- package/dist/cjs/cache/storage.d.ts +101 -0
- package/dist/cjs/cache/storage.d.ts.map +1 -0
- package/dist/cjs/cache/storage.js +337 -0
- package/dist/cjs/cache/storage.js.map +1 -0
- package/dist/cjs/cache/types.d.ts +36 -0
- package/dist/cjs/cache/types.d.ts.map +1 -0
- package/dist/cjs/cache/types.js +8 -0
- package/dist/cjs/cache/types.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 +24 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/merkleStateManager.d.ts +260 -0
- package/dist/cjs/merkleStateManager.d.ts.map +1 -0
- package/dist/cjs/merkleStateManager.js +616 -0
- package/dist/cjs/merkleStateManager.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/proof/index.d.ts +3 -0
- package/dist/cjs/proof/index.d.ts.map +1 -0
- package/dist/cjs/proof/index.js +19 -0
- package/dist/cjs/proof/index.js.map +1 -0
- package/dist/cjs/proof/merkle.d.ts +40 -0
- package/dist/cjs/proof/merkle.d.ts.map +1 -0
- package/dist/cjs/proof/merkle.js +182 -0
- package/dist/cjs/proof/merkle.js.map +1 -0
- package/dist/cjs/proof/rpc.d.ts +10 -0
- package/dist/cjs/proof/rpc.d.ts.map +1 -0
- package/dist/cjs/proof/rpc.js +20 -0
- package/dist/cjs/proof/rpc.js.map +1 -0
- package/dist/cjs/rpcStateManager.d.ts +162 -0
- package/dist/cjs/rpcStateManager.d.ts.map +1 -0
- package/dist/cjs/rpcStateManager.js +313 -0
- package/dist/cjs/rpcStateManager.js.map +1 -0
- package/dist/cjs/simpleStateManager.d.ts +54 -0
- package/dist/cjs/simpleStateManager.d.ts.map +1 -0
- package/dist/cjs/simpleStateManager.js +125 -0
- package/dist/cjs/simpleStateManager.js.map +1 -0
- package/dist/cjs/statefulBinaryTreeStateManager.d.ts +69 -0
- package/dist/cjs/statefulBinaryTreeStateManager.d.ts.map +1 -0
- package/dist/cjs/statefulBinaryTreeStateManager.js +576 -0
- package/dist/cjs/statefulBinaryTreeStateManager.js.map +1 -0
- package/dist/cjs/types.d.ts +92 -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/cjs/util.d.ts +4 -0
- package/dist/cjs/util.d.ts.map +1 -0
- package/dist/cjs/util.js +21 -0
- package/dist/cjs/util.js.map +1 -0
- package/dist/esm/cache/account.d.ts +85 -0
- package/dist/esm/cache/account.d.ts.map +1 -0
- package/dist/esm/cache/account.js +248 -0
- package/dist/esm/cache/account.js.map +1 -0
- package/dist/esm/cache/cache.d.ts +23 -0
- package/dist/esm/cache/cache.d.ts.map +1 -0
- package/dist/esm/cache/cache.js +27 -0
- package/dist/esm/cache/cache.js.map +1 -0
- package/dist/esm/cache/caches.d.ts +19 -0
- package/dist/esm/cache/caches.d.ts.map +1 -0
- package/dist/esm/cache/caches.js +103 -0
- package/dist/esm/cache/caches.js.map +1 -0
- package/dist/esm/cache/code.d.ts +87 -0
- package/dist/esm/cache/code.d.ts.map +1 -0
- package/dist/esm/cache/code.js +254 -0
- package/dist/esm/cache/code.js.map +1 -0
- package/dist/esm/cache/index.d.ts +7 -0
- package/dist/esm/cache/index.d.ts.map +1 -0
- package/dist/esm/cache/index.js +7 -0
- package/dist/esm/cache/index.js.map +1 -0
- package/dist/esm/cache/originalStorageCache.d.ts +21 -0
- package/dist/esm/cache/originalStorageCache.d.ts.map +1 -0
- package/dist/esm/cache/originalStorageCache.js +48 -0
- package/dist/esm/cache/originalStorageCache.js.map +1 -0
- package/dist/esm/cache/storage.d.ts +101 -0
- package/dist/esm/cache/storage.d.ts.map +1 -0
- package/dist/esm/cache/storage.js +333 -0
- package/dist/esm/cache/storage.js.map +1 -0
- package/dist/esm/cache/types.d.ts +36 -0
- package/dist/esm/cache/types.d.ts.map +1 -0
- package/dist/esm/cache/types.js +5 -0
- package/dist/esm/cache/types.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/merkleStateManager.d.ts +260 -0
- package/dist/esm/merkleStateManager.d.ts.map +1 -0
- package/dist/esm/merkleStateManager.js +612 -0
- package/dist/esm/merkleStateManager.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/proof/index.d.ts +3 -0
- package/dist/esm/proof/index.d.ts.map +1 -0
- package/dist/esm/proof/index.js +3 -0
- package/dist/esm/proof/index.js.map +1 -0
- package/dist/esm/proof/merkle.d.ts +40 -0
- package/dist/esm/proof/merkle.d.ts.map +1 -0
- package/dist/esm/proof/merkle.js +175 -0
- package/dist/esm/proof/merkle.js.map +1 -0
- package/dist/esm/proof/rpc.d.ts +10 -0
- package/dist/esm/proof/rpc.d.ts.map +1 -0
- package/dist/esm/proof/rpc.js +17 -0
- package/dist/esm/proof/rpc.js.map +1 -0
- package/dist/esm/rpcStateManager.d.ts +162 -0
- package/dist/esm/rpcStateManager.d.ts.map +1 -0
- package/dist/esm/rpcStateManager.js +308 -0
- package/dist/esm/rpcStateManager.js.map +1 -0
- package/dist/esm/simpleStateManager.d.ts +54 -0
- package/dist/esm/simpleStateManager.d.ts.map +1 -0
- package/dist/esm/simpleStateManager.js +121 -0
- package/dist/esm/simpleStateManager.js.map +1 -0
- package/dist/esm/statefulBinaryTreeStateManager.d.ts +69 -0
- package/dist/esm/statefulBinaryTreeStateManager.d.ts.map +1 -0
- package/dist/esm/statefulBinaryTreeStateManager.js +572 -0
- package/dist/esm/statefulBinaryTreeStateManager.js.map +1 -0
- package/dist/esm/types.d.ts +92 -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/esm/util.d.ts +4 -0
- package/dist/esm/util.d.ts.map +1 -0
- package/dist/esm/util.js +18 -0
- package/dist/esm/util.js.map +1 -0
- package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
- package/package.json +74 -0
- package/src/cache/account.ts +277 -0
- package/src/cache/cache.ts +35 -0
- package/src/cache/caches.ts +125 -0
- package/src/cache/code.ts +277 -0
- package/src/cache/index.ts +6 -0
- package/src/cache/originalStorageCache.ts +57 -0
- package/src/cache/storage.ts +369 -0
- package/src/cache/types.ts +38 -0
- package/src/index.ts +7 -0
- package/src/merkleStateManager.ts +737 -0
- package/src/proof/index.ts +2 -0
- package/src/proof/merkle.ts +264 -0
- package/src/proof/rpc.ts +24 -0
- package/src/rpcStateManager.ts +381 -0
- package/src/simpleStateManager.ts +154 -0
- package/src/statefulBinaryTreeStateManager.ts +789 -0
- package/src/types.ts +103 -0
- package/src/util.ts +28 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
import { BinaryTree } from '@feelyourprotocol/binarytree'
|
|
2
|
+
import { BinaryTreeAccessedStateType } from '@feelyourprotocol/common'
|
|
3
|
+
import { RLP } from '@feelyourprotocol/rlp'
|
|
4
|
+
import type { Address, BinaryTreeExecutionWitness, PrefixedHexString } from '@feelyourprotocol/util'
|
|
5
|
+
import {
|
|
6
|
+
Account,
|
|
7
|
+
BINARY_TREE_CODE_CHUNK_SIZE,
|
|
8
|
+
BINARY_TREE_CODE_OFFSET,
|
|
9
|
+
BINARY_TREE_NODE_WIDTH,
|
|
10
|
+
BinaryTreeLeafType,
|
|
11
|
+
EthereumJSErrorWithoutCode,
|
|
12
|
+
KECCAK256_NULL,
|
|
13
|
+
MapDB,
|
|
14
|
+
bigIntToBytes,
|
|
15
|
+
bytesToBigInt,
|
|
16
|
+
bytesToHex,
|
|
17
|
+
chunkifyBinaryTreeCode,
|
|
18
|
+
createAddressFromString,
|
|
19
|
+
createPartialAccount,
|
|
20
|
+
createPartialAccountFromRLP,
|
|
21
|
+
decodeBinaryTreeLeafBasicData,
|
|
22
|
+
encodeBinaryTreeLeafBasicData,
|
|
23
|
+
equalsBytes,
|
|
24
|
+
generateBinaryTreeChunkSuffixes,
|
|
25
|
+
generateBinaryTreeCodeStems,
|
|
26
|
+
getBinaryTreeKeyForStorageSlot,
|
|
27
|
+
getBinaryTreeStem,
|
|
28
|
+
hexToBigInt,
|
|
29
|
+
hexToBytes,
|
|
30
|
+
isDebugEnabled,
|
|
31
|
+
padToEven,
|
|
32
|
+
setLengthLeft,
|
|
33
|
+
setLengthRight,
|
|
34
|
+
short,
|
|
35
|
+
unprefixedHexToBytes,
|
|
36
|
+
} from '@feelyourprotocol/util'
|
|
37
|
+
import { blake3 } from '@noble/hashes/blake3.js'
|
|
38
|
+
import { keccak_256 } from '@noble/hashes/sha3.js'
|
|
39
|
+
import debugDefault from 'debug'
|
|
40
|
+
|
|
41
|
+
import { OriginalStorageCache } from './cache/originalStorageCache.ts'
|
|
42
|
+
import { modifyAccountFields } from './util.ts'
|
|
43
|
+
|
|
44
|
+
import type {
|
|
45
|
+
AccountFields,
|
|
46
|
+
BinaryTreeAccessWitnessInterface,
|
|
47
|
+
BinaryTreeAccessedStateWithAddress,
|
|
48
|
+
GenesisState,
|
|
49
|
+
StateManagerInterface,
|
|
50
|
+
StorageDump,
|
|
51
|
+
StoragePair,
|
|
52
|
+
StorageRange,
|
|
53
|
+
} from '@feelyourprotocol/common'
|
|
54
|
+
import type { Debugger } from 'debug'
|
|
55
|
+
import type { Caches } from './cache/caches.ts'
|
|
56
|
+
import type { BinaryTreeState, StatefulBinaryTreeStateManagerOpts } from './types.ts'
|
|
57
|
+
|
|
58
|
+
const ZEROVALUE = '0x0000000000000000000000000000000000000000000000000000000000000000'
|
|
59
|
+
export class StatefulBinaryTreeStateManager implements StateManagerInterface {
|
|
60
|
+
protected _debug: Debugger
|
|
61
|
+
protected _caches?: Caches
|
|
62
|
+
|
|
63
|
+
preStateRoot: Uint8Array
|
|
64
|
+
originalStorageCache: OriginalStorageCache
|
|
65
|
+
hashFunction: (input: Uint8Array) => Uint8Array
|
|
66
|
+
|
|
67
|
+
protected _tree: BinaryTree
|
|
68
|
+
|
|
69
|
+
protected _checkpointCount: number
|
|
70
|
+
|
|
71
|
+
// Post-state provided from the executionWitness.
|
|
72
|
+
// Should not update. Used for comparing our computed post-state with the canonical one.
|
|
73
|
+
private _postState: BinaryTreeState = {}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* StateManager is run in DEBUG mode (default: false)
|
|
77
|
+
* Taken from DEBUG environment variable
|
|
78
|
+
*
|
|
79
|
+
* Safeguards on debug() calls are added for
|
|
80
|
+
* performance reasons to avoid string literal evaluation
|
|
81
|
+
* @hidden
|
|
82
|
+
*/
|
|
83
|
+
protected readonly DEBUG: boolean = false
|
|
84
|
+
|
|
85
|
+
private keccakFunction: Function
|
|
86
|
+
|
|
87
|
+
constructor(opts: StatefulBinaryTreeStateManagerOpts) {
|
|
88
|
+
// Skip DEBUG calls unless 'ethjs' included in environmental DEBUG variables
|
|
89
|
+
this.DEBUG = isDebugEnabled('ethjs')
|
|
90
|
+
|
|
91
|
+
this._checkpointCount = 0
|
|
92
|
+
|
|
93
|
+
if (opts.common?.isActivatedEIP(7864) === false) {
|
|
94
|
+
throw EthereumJSErrorWithoutCode('EIP-7864 required for binary tree state management')
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.hashFunction = opts.hashFunction ?? blake3
|
|
98
|
+
this._tree =
|
|
99
|
+
opts.tree ??
|
|
100
|
+
new BinaryTree({
|
|
101
|
+
hashFunction: this.hashFunction,
|
|
102
|
+
db: new MapDB<string, string | Uint8Array>(),
|
|
103
|
+
useRootPersistence: false,
|
|
104
|
+
cacheSize: 0,
|
|
105
|
+
})
|
|
106
|
+
this._debug = debugDefault('statemanager:binarytree')
|
|
107
|
+
this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this))
|
|
108
|
+
this._caches = opts.caches
|
|
109
|
+
this.keccakFunction = keccak_256
|
|
110
|
+
this.preStateRoot = new Uint8Array(32) // Initial state root is zeroes
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Gets the account associated with `address` or `undefined` if account does not exist
|
|
115
|
+
* @param address - Address of the `account` to get
|
|
116
|
+
*/
|
|
117
|
+
getAccount = async (address: Address): Promise<Account | undefined> => {
|
|
118
|
+
const elem = this._caches?.account?.get(address)
|
|
119
|
+
if (elem !== undefined) {
|
|
120
|
+
return elem.accountRLP !== undefined
|
|
121
|
+
? createPartialAccountFromRLP(elem.accountRLP)
|
|
122
|
+
: undefined
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const stem = getBinaryTreeStem(this.hashFunction, address, 0)
|
|
126
|
+
|
|
127
|
+
// First retrieve the account "header" values from the trie
|
|
128
|
+
const accountValues = await this._tree.get(stem, [
|
|
129
|
+
BinaryTreeLeafType.BasicData,
|
|
130
|
+
BinaryTreeLeafType.CodeHash,
|
|
131
|
+
])
|
|
132
|
+
|
|
133
|
+
let account
|
|
134
|
+
if (accountValues[0] !== null && accountValues[0] !== undefined) {
|
|
135
|
+
const basicData = decodeBinaryTreeLeafBasicData(accountValues[0]!)
|
|
136
|
+
account = createPartialAccount({
|
|
137
|
+
version: basicData.version,
|
|
138
|
+
balance: basicData.balance,
|
|
139
|
+
nonce: basicData.nonce,
|
|
140
|
+
// Codehash is either untouched (i.e. null) or deleted (i.e. overwritten with zeros)
|
|
141
|
+
codeHash:
|
|
142
|
+
accountValues[1] === null ||
|
|
143
|
+
accountValues[1] === undefined ||
|
|
144
|
+
equalsBytes(accountValues[1], new Uint8Array(32))
|
|
145
|
+
? KECCAK256_NULL
|
|
146
|
+
: accountValues[1],
|
|
147
|
+
codeSize: basicData.codeSize,
|
|
148
|
+
storageRoot: KECCAK256_NULL,
|
|
149
|
+
})
|
|
150
|
+
} else if (accountValues[1] === undefined || accountValues[1] === null) {
|
|
151
|
+
// account does not exist if both basic fields and codehash are undefined
|
|
152
|
+
if (this.DEBUG) {
|
|
153
|
+
this._debug(`getAccount address=${address.toString()} from DB (non-existent)`)
|
|
154
|
+
}
|
|
155
|
+
this._caches?.account?.put(address, account)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (this.DEBUG) {
|
|
159
|
+
this._debug(`getAccount address=${address.toString()} stem=${short(stem)}`)
|
|
160
|
+
}
|
|
161
|
+
return account
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public initBinaryTreeExecutionWitness(
|
|
165
|
+
_blockNum: bigint,
|
|
166
|
+
executionWitness?: BinaryTreeExecutionWitness | null,
|
|
167
|
+
) {
|
|
168
|
+
if (executionWitness === null || executionWitness === undefined) {
|
|
169
|
+
const errorMsg = `Invalid executionWitness=${executionWitness} for initBinaryTreeExecutionWitness`
|
|
170
|
+
this._debug(errorMsg)
|
|
171
|
+
throw Error(errorMsg)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this.preStateRoot = hexToBytes(executionWitness.parentStateRoot) // set prestate root
|
|
175
|
+
|
|
176
|
+
// Populate the post-state from the executionWitness
|
|
177
|
+
|
|
178
|
+
const postStateRaw = executionWitness.stateDiff.flatMap(({ stem, suffixDiffs }) => {
|
|
179
|
+
const suffixDiffPairs = suffixDiffs.map(({ newValue, currentValue, suffix }) => {
|
|
180
|
+
const key = `${stem}${padToEven(Number(suffix).toString(16))}` as PrefixedHexString
|
|
181
|
+
// A postState value of null means there was no change from the preState.
|
|
182
|
+
// In this implementation, we therefore replace null with the preState.
|
|
183
|
+
const value = newValue ?? currentValue
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
[key]: value,
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
return suffixDiffPairs
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const postState = postStateRaw.reduce((prevValue, currentValue) => {
|
|
194
|
+
const acc = { ...prevValue, ...currentValue }
|
|
195
|
+
return acc
|
|
196
|
+
}, {})
|
|
197
|
+
|
|
198
|
+
this._postState = postState
|
|
199
|
+
|
|
200
|
+
this._debug(`initBinaryTreeExecutionWitness postState=${JSON.stringify(this._postState)}`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Saves an account into state under the provided `address`.
|
|
205
|
+
* @param address - Address under which to store `account`
|
|
206
|
+
* @param account - The account to store or undefined if to be deleted
|
|
207
|
+
*/
|
|
208
|
+
putAccount = async (address: Address, account?: Account): Promise<void> => {
|
|
209
|
+
if (this.DEBUG) {
|
|
210
|
+
this._debug(
|
|
211
|
+
`putAccount address=${address} nonce=${account?.nonce} balance=${
|
|
212
|
+
account?.balance
|
|
213
|
+
} contract=${account && account.isContract() ? 'yes' : 'no'} empty=${
|
|
214
|
+
account && account.isEmpty() ? 'yes' : 'no'
|
|
215
|
+
}`,
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
if (this._caches?.account === undefined) {
|
|
219
|
+
if (account !== undefined) {
|
|
220
|
+
const stem = getBinaryTreeStem(this.hashFunction, address, 0)
|
|
221
|
+
const basicDataBytes = encodeBinaryTreeLeafBasicData(account)
|
|
222
|
+
await this._tree.put(
|
|
223
|
+
stem,
|
|
224
|
+
[BinaryTreeLeafType.BasicData, BinaryTreeLeafType.CodeHash],
|
|
225
|
+
[basicDataBytes, account.codeHash],
|
|
226
|
+
)
|
|
227
|
+
} else {
|
|
228
|
+
// Delete account
|
|
229
|
+
await this.deleteAccount(address)
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
if (account !== undefined) {
|
|
233
|
+
this._caches?.account?.put(address, account, true)
|
|
234
|
+
} else {
|
|
235
|
+
this._caches?.account?.del(address)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Deletes an account from state under the provided `address`.
|
|
242
|
+
* @param address - Address of the account which should be deleted
|
|
243
|
+
*/
|
|
244
|
+
deleteAccount = async (address: Address): Promise<void> => {
|
|
245
|
+
if (this.DEBUG) {
|
|
246
|
+
this._debug(`Delete account ${address}`)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this._caches?.deleteAccount(address)
|
|
250
|
+
|
|
251
|
+
if (this._caches?.account === undefined) {
|
|
252
|
+
const stem = getBinaryTreeStem(this.hashFunction, address)
|
|
253
|
+
// Special instance where we delete the account and revert the trie value to untouched
|
|
254
|
+
await this._tree.put(
|
|
255
|
+
stem,
|
|
256
|
+
[BinaryTreeLeafType.BasicData, BinaryTreeLeafType.CodeHash],
|
|
257
|
+
[null, null],
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
modifyAccountFields = async (address: Address, accountFields: AccountFields): Promise<void> => {
|
|
263
|
+
await modifyAccountFields(this, address, accountFields)
|
|
264
|
+
}
|
|
265
|
+
putCode = async (address: Address, value: Uint8Array): Promise<void> => {
|
|
266
|
+
if (this.DEBUG) {
|
|
267
|
+
this._debug(`putCode address=${address.toString()} value=${short(value)}`)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
this._caches?.code?.put(address, value)
|
|
271
|
+
|
|
272
|
+
const codeHash = keccak_256(value)
|
|
273
|
+
if (equalsBytes(codeHash, KECCAK256_NULL)) {
|
|
274
|
+
// If the code hash is the null hash, no code has to be stored
|
|
275
|
+
return
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if ((await this.getAccount(address)) === undefined) {
|
|
279
|
+
await this.putAccount(address, new Account())
|
|
280
|
+
}
|
|
281
|
+
if (this.DEBUG) {
|
|
282
|
+
this._debug(`Update codeHash (-> ${short(codeHash)}) for account ${address}`)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const codeChunks = chunkifyBinaryTreeCode(value)
|
|
286
|
+
const chunkStems = generateBinaryTreeCodeStems(codeChunks.length, address, this.hashFunction)
|
|
287
|
+
|
|
288
|
+
const chunkSuffixes: number[] = generateBinaryTreeChunkSuffixes(codeChunks.length)
|
|
289
|
+
// Put the code chunks corresponding to the first stem (up to 128 chunks)
|
|
290
|
+
await this._tree.put(
|
|
291
|
+
chunkStems[0],
|
|
292
|
+
chunkSuffixes.slice(
|
|
293
|
+
0,
|
|
294
|
+
chunkSuffixes.length <= BINARY_TREE_CODE_OFFSET
|
|
295
|
+
? chunkSuffixes.length
|
|
296
|
+
: BINARY_TREE_CODE_OFFSET,
|
|
297
|
+
),
|
|
298
|
+
codeChunks.slice(
|
|
299
|
+
0,
|
|
300
|
+
codeChunks.length <= BINARY_TREE_CODE_OFFSET ? codeChunks.length : BINARY_TREE_CODE_OFFSET,
|
|
301
|
+
),
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
// Put additional chunks under additional stems as applicable
|
|
305
|
+
for (let stem = 1; stem < chunkStems.length; stem++) {
|
|
306
|
+
const sliceStart = BINARY_TREE_CODE_OFFSET + BINARY_TREE_NODE_WIDTH * (stem - 1)
|
|
307
|
+
const sliceEnd =
|
|
308
|
+
value.length <= BINARY_TREE_CODE_OFFSET + BINARY_TREE_NODE_WIDTH * stem
|
|
309
|
+
? value.length
|
|
310
|
+
: BINARY_TREE_CODE_OFFSET + BINARY_TREE_NODE_WIDTH * stem
|
|
311
|
+
await this._tree.put(
|
|
312
|
+
chunkStems[stem],
|
|
313
|
+
chunkSuffixes.slice(sliceStart, sliceEnd),
|
|
314
|
+
codeChunks.slice(sliceStart, sliceEnd),
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
await this.modifyAccountFields(address, {
|
|
318
|
+
codeHash,
|
|
319
|
+
codeSize: value.length,
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
getCode = async (address: Address): Promise<Uint8Array> => {
|
|
324
|
+
if (this.DEBUG) {
|
|
325
|
+
this._debug(`getCode address=${address.toString()}`)
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const elem = this._caches?.code?.get(address)
|
|
329
|
+
if (elem !== undefined) {
|
|
330
|
+
return elem.code ?? new Uint8Array(0)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const account = await this.getAccount(address)
|
|
334
|
+
if (!account) {
|
|
335
|
+
return new Uint8Array(0)
|
|
336
|
+
}
|
|
337
|
+
if (!account.isContract()) {
|
|
338
|
+
return new Uint8Array(0)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// allocate the code
|
|
342
|
+
const codeSize = account.codeSize
|
|
343
|
+
|
|
344
|
+
const stems = generateBinaryTreeCodeStems(
|
|
345
|
+
Math.ceil(codeSize / BINARY_TREE_CODE_CHUNK_SIZE),
|
|
346
|
+
address,
|
|
347
|
+
this.hashFunction,
|
|
348
|
+
)
|
|
349
|
+
const chunkSuffixes = generateBinaryTreeChunkSuffixes(
|
|
350
|
+
Math.ceil(codeSize / BINARY_TREE_CODE_CHUNK_SIZE),
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
const chunksByStem = new Array(stems.length)
|
|
354
|
+
// Retrieve the code chunks stored in the first leaf node
|
|
355
|
+
chunksByStem[0] = await this._tree.get(
|
|
356
|
+
stems[0],
|
|
357
|
+
chunkSuffixes.slice(
|
|
358
|
+
0,
|
|
359
|
+
codeSize <= BINARY_TREE_CODE_OFFSET ? codeSize : BINARY_TREE_CODE_OFFSET,
|
|
360
|
+
),
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
// Retrieve code chunks on any additional stems
|
|
364
|
+
for (let stem = 1; stem < stems.length; stem++) {
|
|
365
|
+
const sliceStart = BINARY_TREE_CODE_OFFSET + BINARY_TREE_NODE_WIDTH * (stem - 1)
|
|
366
|
+
const sliceEnd =
|
|
367
|
+
codeSize <= BINARY_TREE_CODE_OFFSET + BINARY_TREE_NODE_WIDTH * stem
|
|
368
|
+
? codeSize
|
|
369
|
+
: BINARY_TREE_CODE_OFFSET + BINARY_TREE_NODE_WIDTH * stem
|
|
370
|
+
chunksByStem[stem] = await this._tree.get(
|
|
371
|
+
stems[stem],
|
|
372
|
+
chunkSuffixes.slice(sliceStart, sliceEnd),
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
const chunks = chunksByStem.flat()
|
|
376
|
+
const code = new Uint8Array(codeSize)
|
|
377
|
+
// Insert code chunks into final array (skipping PUSHDATA overflow indicator byte)
|
|
378
|
+
for (let x = 0; x < chunks.length; x++) {
|
|
379
|
+
if (chunks[x] === undefined)
|
|
380
|
+
throw EthereumJSErrorWithoutCode(`expected code chunk at index ${x}, got undefined`)
|
|
381
|
+
|
|
382
|
+
let lastChunkByteIndex = BINARY_TREE_CODE_CHUNK_SIZE
|
|
383
|
+
// Determine code ending byte (if we're on the last chunk)
|
|
384
|
+
if (x === chunks.length - 1) {
|
|
385
|
+
// On the last chunk, the slice either ends on a partial chunk (if codeSize doesn't exactly fit in full chunks), or a full chunk
|
|
386
|
+
lastChunkByteIndex = codeSize % BINARY_TREE_CODE_CHUNK_SIZE || BINARY_TREE_CODE_CHUNK_SIZE
|
|
387
|
+
}
|
|
388
|
+
code.set(
|
|
389
|
+
chunks[x]!.slice(1, lastChunkByteIndex + 1),
|
|
390
|
+
code.byteOffset + x * BINARY_TREE_CODE_CHUNK_SIZE,
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
this._caches?.code?.put(address, code)
|
|
394
|
+
|
|
395
|
+
return code
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
getCodeSize = async (address: Address): Promise<number> => {
|
|
399
|
+
const accountBytes = (
|
|
400
|
+
await this._tree.get(getBinaryTreeStem(this.hashFunction, address), [
|
|
401
|
+
BinaryTreeLeafType.BasicData,
|
|
402
|
+
])
|
|
403
|
+
)[0]
|
|
404
|
+
if (accountBytes === null) return 0
|
|
405
|
+
return decodeBinaryTreeLeafBasicData(accountBytes).codeSize
|
|
406
|
+
}
|
|
407
|
+
getStorage = async (address: Address, key: Uint8Array): Promise<Uint8Array> => {
|
|
408
|
+
if (key.length !== 32) {
|
|
409
|
+
throw EthereumJSErrorWithoutCode('Storage key must be 32 bytes long')
|
|
410
|
+
}
|
|
411
|
+
const cachedValue = this._caches?.storage?.get(address, key)
|
|
412
|
+
if (cachedValue !== undefined) {
|
|
413
|
+
const decoded = RLP.decode(cachedValue ?? new Uint8Array(0)) as Uint8Array
|
|
414
|
+
return decoded
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const account = await this.getAccount(address)
|
|
418
|
+
if (!account) {
|
|
419
|
+
return new Uint8Array()
|
|
420
|
+
}
|
|
421
|
+
const storageKey = getBinaryTreeKeyForStorageSlot(
|
|
422
|
+
address,
|
|
423
|
+
bytesToBigInt(key),
|
|
424
|
+
this.hashFunction,
|
|
425
|
+
)
|
|
426
|
+
const value = await this._tree.get(storageKey.slice(0, 31), [storageKey[31]])
|
|
427
|
+
|
|
428
|
+
this._caches?.storage?.put(address, key, value[0] ?? hexToBytes('0x80'))
|
|
429
|
+
const decoded = (value[0] ?? new Uint8Array(0)) as Uint8Array
|
|
430
|
+
return setLengthLeft(decoded, 32)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
putStorage = async (address: Address, key: Uint8Array, value: Uint8Array): Promise<void> => {
|
|
434
|
+
this._caches?.storage?.put(address, key, RLP.encode(value))
|
|
435
|
+
if (this._caches?.storage === undefined) {
|
|
436
|
+
const storageKey = getBinaryTreeKeyForStorageSlot(
|
|
437
|
+
address,
|
|
438
|
+
bytesToBigInt(key),
|
|
439
|
+
this.hashFunction,
|
|
440
|
+
)
|
|
441
|
+
await this._tree.put(storageKey.slice(0, 31), [storageKey[31]], [setLengthLeft(value, 32)])
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
clearStorage = async (address: Address): Promise<void> => {
|
|
446
|
+
this._caches?.storage?.clearStorage(address)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
checkpoint = async (): Promise<void> => {
|
|
450
|
+
this._tree.checkpoint()
|
|
451
|
+
this._caches?.checkpoint()
|
|
452
|
+
this._checkpointCount++
|
|
453
|
+
}
|
|
454
|
+
commit = async (): Promise<void> => {
|
|
455
|
+
await this._tree.commit()
|
|
456
|
+
this._caches?.commit()
|
|
457
|
+
this._checkpointCount--
|
|
458
|
+
|
|
459
|
+
if (this._checkpointCount === 0) {
|
|
460
|
+
await this.flush()
|
|
461
|
+
this.originalStorageCache.clear()
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (this.DEBUG) {
|
|
465
|
+
this._debug(`state checkpoint committed`)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
revert = async (): Promise<void> => {
|
|
469
|
+
await this._tree.revert()
|
|
470
|
+
this._caches?.revert()
|
|
471
|
+
|
|
472
|
+
this._checkpointCount--
|
|
473
|
+
|
|
474
|
+
if (this._checkpointCount === 0) {
|
|
475
|
+
await this.flush()
|
|
476
|
+
this.originalStorageCache.clear()
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
flush = async (): Promise<void> => {
|
|
481
|
+
const codeItems = this._caches?.code?.flush() ?? []
|
|
482
|
+
for (const item of codeItems) {
|
|
483
|
+
const addr = createAddressFromString(`0x${item[0]}`)
|
|
484
|
+
|
|
485
|
+
const code = item[1].code
|
|
486
|
+
if (code === undefined) {
|
|
487
|
+
continue
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
await this.putCode(addr, code)
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const storageItems = this._caches?.storage?.flush() ?? []
|
|
494
|
+
for (const item of storageItems) {
|
|
495
|
+
const address = createAddressFromString(`0x${item[0]}`)
|
|
496
|
+
const keyHex = item[1]
|
|
497
|
+
const keyBytes = unprefixedHexToBytes(keyHex)
|
|
498
|
+
const value = item[2]
|
|
499
|
+
|
|
500
|
+
const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array
|
|
501
|
+
const account = await this.getAccount(address)
|
|
502
|
+
if (account) {
|
|
503
|
+
await this.putStorage(address, keyBytes, decoded)
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const accountItems = this._caches?.account?.flush() ?? []
|
|
508
|
+
for (const item of accountItems) {
|
|
509
|
+
const address = createAddressFromString(`0x${item[0]}`)
|
|
510
|
+
const elem = item[1]
|
|
511
|
+
if (elem.accountRLP === undefined) {
|
|
512
|
+
await this.deleteAccount(address)
|
|
513
|
+
} else {
|
|
514
|
+
const account = createPartialAccountFromRLP(elem.accountRLP)
|
|
515
|
+
await this.putAccount(address, account)
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
async getComputedValue(
|
|
521
|
+
accessedState: BinaryTreeAccessedStateWithAddress,
|
|
522
|
+
): Promise<PrefixedHexString | null> {
|
|
523
|
+
const { address, type } = accessedState
|
|
524
|
+
|
|
525
|
+
switch (type) {
|
|
526
|
+
case BinaryTreeAccessedStateType.BasicData: {
|
|
527
|
+
if (this._caches === undefined) {
|
|
528
|
+
const accountData = await this.getAccount(address)
|
|
529
|
+
if (accountData === undefined) {
|
|
530
|
+
return null
|
|
531
|
+
}
|
|
532
|
+
const basicDataBytes = encodeBinaryTreeLeafBasicData(accountData)
|
|
533
|
+
return bytesToHex(basicDataBytes)
|
|
534
|
+
} else {
|
|
535
|
+
const encodedAccount = this._caches?.account?.get(address)?.accountRLP
|
|
536
|
+
if (encodedAccount === undefined) {
|
|
537
|
+
return null
|
|
538
|
+
}
|
|
539
|
+
const basicDataBytes = encodeBinaryTreeLeafBasicData(
|
|
540
|
+
createPartialAccountFromRLP(encodedAccount),
|
|
541
|
+
)
|
|
542
|
+
return bytesToHex(basicDataBytes)
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
case BinaryTreeAccessedStateType.CodeHash: {
|
|
547
|
+
if (this._caches === undefined) {
|
|
548
|
+
const accountData = await this.getAccount(address)
|
|
549
|
+
if (accountData === undefined) {
|
|
550
|
+
return null
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return bytesToHex(accountData.codeHash)
|
|
554
|
+
} else {
|
|
555
|
+
const encodedAccount = this._caches?.account?.get(address)?.accountRLP
|
|
556
|
+
if (encodedAccount === undefined) {
|
|
557
|
+
return null
|
|
558
|
+
}
|
|
559
|
+
return bytesToHex(createPartialAccountFromRLP(encodedAccount).codeHash)
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
case BinaryTreeAccessedStateType.Code: {
|
|
564
|
+
const { codeOffset } = accessedState
|
|
565
|
+
let code: Uint8Array | undefined | null = null
|
|
566
|
+
if (this._caches === undefined) {
|
|
567
|
+
code = await this.getCode(address)
|
|
568
|
+
if (code === undefined) {
|
|
569
|
+
return null
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
code = this._caches?.code?.get(address)?.code
|
|
573
|
+
if (code === undefined) {
|
|
574
|
+
return null
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// we can only compare the actual code because to compare the first byte would
|
|
579
|
+
// be very tricky and impossible in certain scenarios like when the previous code chunk
|
|
580
|
+
// was not accessed and hence not even provided in the witness
|
|
581
|
+
// We are left-padding with two zeroes to get a 32-byte length, but these bytes should not be considered reliable
|
|
582
|
+
return bytesToHex(
|
|
583
|
+
setLengthLeft(
|
|
584
|
+
setLengthRight(
|
|
585
|
+
code.slice(codeOffset, codeOffset + BINARY_TREE_CODE_CHUNK_SIZE),
|
|
586
|
+
BINARY_TREE_CODE_CHUNK_SIZE,
|
|
587
|
+
),
|
|
588
|
+
BINARY_TREE_CODE_CHUNK_SIZE + 1,
|
|
589
|
+
),
|
|
590
|
+
)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
case BinaryTreeAccessedStateType.Storage: {
|
|
594
|
+
const { slot } = accessedState
|
|
595
|
+
const key = setLengthLeft(bigIntToBytes(slot), 32)
|
|
596
|
+
let storage: Uint8Array | undefined | null = null
|
|
597
|
+
if (this._caches === undefined) {
|
|
598
|
+
storage = await this.getStorage(address, key)
|
|
599
|
+
if (storage === undefined) {
|
|
600
|
+
return null
|
|
601
|
+
}
|
|
602
|
+
} else {
|
|
603
|
+
storage = this._caches?.storage?.get(address, key)
|
|
604
|
+
}
|
|
605
|
+
if (storage === undefined) {
|
|
606
|
+
return null
|
|
607
|
+
}
|
|
608
|
+
return bytesToHex(setLengthLeft(storage, 32))
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Verifies that the witness post-state matches the computed post-state
|
|
614
|
+
async verifyBinaryTreePostState(
|
|
615
|
+
accessWitness: BinaryTreeAccessWitnessInterface,
|
|
616
|
+
): Promise<boolean> {
|
|
617
|
+
// track what all chunks were accessed so as to compare in the end if any chunks were missed
|
|
618
|
+
// in access while comparing against the provided poststate in the execution witness
|
|
619
|
+
const accessedChunks = new Map<string, boolean>()
|
|
620
|
+
// switch to false if postVerify fails
|
|
621
|
+
let postFailures = 0
|
|
622
|
+
|
|
623
|
+
for (const accessedState of accessWitness?.accesses() ?? []) {
|
|
624
|
+
const { address, type } = accessedState
|
|
625
|
+
let extraMeta = ''
|
|
626
|
+
if (accessedState.type === BinaryTreeAccessedStateType.Code) {
|
|
627
|
+
extraMeta = `codeOffset=${accessedState.codeOffset}`
|
|
628
|
+
} else if (accessedState.type === BinaryTreeAccessedStateType.Storage) {
|
|
629
|
+
extraMeta = `slot=${accessedState.slot}`
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const { chunkKey } = accessedState
|
|
633
|
+
accessedChunks.set(chunkKey, true)
|
|
634
|
+
let computedValue: PrefixedHexString | null | undefined =
|
|
635
|
+
await this.getComputedValue(accessedState)
|
|
636
|
+
if (computedValue === undefined) {
|
|
637
|
+
this.DEBUG &&
|
|
638
|
+
this._debug(
|
|
639
|
+
`Missing computed value for address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`,
|
|
640
|
+
)
|
|
641
|
+
postFailures++
|
|
642
|
+
continue
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
let canonicalValue: PrefixedHexString | null | undefined = this._postState[chunkKey]
|
|
646
|
+
|
|
647
|
+
if (canonicalValue === undefined) {
|
|
648
|
+
this.DEBUG &&
|
|
649
|
+
this._debug(
|
|
650
|
+
`Block accesses missing from postState for address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`,
|
|
651
|
+
)
|
|
652
|
+
postFailures++
|
|
653
|
+
continue
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// if the access type is code, then we can't match the first byte because since the computed value
|
|
657
|
+
// doesn't has the first byte for push data since previous chunk code itself might not be available
|
|
658
|
+
if (accessedState.type === BinaryTreeAccessedStateType.Code) {
|
|
659
|
+
computedValue = computedValue !== null ? `0x${computedValue.slice(4)}` : null
|
|
660
|
+
canonicalValue = canonicalValue !== null ? `0x${canonicalValue.slice(4)}` : null
|
|
661
|
+
} else if (
|
|
662
|
+
accessedState.type === BinaryTreeAccessedStateType.Storage &&
|
|
663
|
+
canonicalValue === null &&
|
|
664
|
+
computedValue === ZEROVALUE
|
|
665
|
+
) {
|
|
666
|
+
canonicalValue = ZEROVALUE
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
this._debug(`computed ${computedValue} canonical ${canonicalValue}`)
|
|
670
|
+
if (computedValue !== canonicalValue) {
|
|
671
|
+
if (type === BinaryTreeAccessedStateType.BasicData) {
|
|
672
|
+
this.DEBUG &&
|
|
673
|
+
this._debug(
|
|
674
|
+
`canonical value: `,
|
|
675
|
+
canonicalValue === null
|
|
676
|
+
? null
|
|
677
|
+
: decodeBinaryTreeLeafBasicData(hexToBytes(canonicalValue)),
|
|
678
|
+
)
|
|
679
|
+
this.DEBUG &&
|
|
680
|
+
this._debug(
|
|
681
|
+
`computed value: `,
|
|
682
|
+
computedValue === null
|
|
683
|
+
? null
|
|
684
|
+
: decodeBinaryTreeLeafBasicData(hexToBytes(computedValue)),
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
this.DEBUG &&
|
|
688
|
+
this._debug(
|
|
689
|
+
`Block accesses mismatch address=${address} type=${type} ${extraMeta} chunkKey=${chunkKey}`,
|
|
690
|
+
)
|
|
691
|
+
this.DEBUG && this._debug(`expected=${canonicalValue}`)
|
|
692
|
+
this.DEBUG && this._debug(`computed=${computedValue}`)
|
|
693
|
+
postFailures++
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
for (const canChunkKey of Object.keys(this._postState)) {
|
|
698
|
+
if (accessedChunks.get(canChunkKey) === undefined) {
|
|
699
|
+
this.DEBUG && this._debug(`Missing chunk access for canChunkKey=${canChunkKey}`)
|
|
700
|
+
postFailures++
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const verifyPassed = postFailures === 0
|
|
705
|
+
this.DEBUG &&
|
|
706
|
+
this._debug(
|
|
707
|
+
`verifyBinaryTreePostState verifyPassed=${verifyPassed} postFailures=${postFailures}`,
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
return verifyPassed
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
getStateRoot(): Promise<Uint8Array> {
|
|
714
|
+
return Promise.resolve(this._tree.root())
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
setStateRoot(stateRoot: Uint8Array, clearCache?: boolean): Promise<void> {
|
|
718
|
+
this._tree.root(stateRoot)
|
|
719
|
+
clearCache === true && this.clearCaches()
|
|
720
|
+
return Promise.resolve()
|
|
721
|
+
}
|
|
722
|
+
hasStateRoot(root: Uint8Array): Promise<boolean> {
|
|
723
|
+
return this._tree.checkRoot(root)
|
|
724
|
+
}
|
|
725
|
+
dumpStorage?(_address: Address): Promise<StorageDump> {
|
|
726
|
+
throw EthereumJSErrorWithoutCode('Method not implemented.')
|
|
727
|
+
}
|
|
728
|
+
dumpStorageRange?(_address: Address, _startKey: bigint, _limit: number): Promise<StorageRange> {
|
|
729
|
+
throw EthereumJSErrorWithoutCode('Method not implemented.')
|
|
730
|
+
}
|
|
731
|
+
clearCaches(): void {
|
|
732
|
+
this._caches?.clear()
|
|
733
|
+
}
|
|
734
|
+
shallowCopy(_downlevelCaches?: boolean): StateManagerInterface {
|
|
735
|
+
throw EthereumJSErrorWithoutCode('Method not implemented.')
|
|
736
|
+
}
|
|
737
|
+
async checkChunkWitnessPresent(_address: Address, _codeOffset: number): Promise<boolean> {
|
|
738
|
+
throw EthereumJSErrorWithoutCode('Method not implemented.')
|
|
739
|
+
}
|
|
740
|
+
async generateCanonicalGenesis(genesisState: GenesisState) {
|
|
741
|
+
await this._tree.createRootNode()
|
|
742
|
+
await this.checkpoint()
|
|
743
|
+
for (const addressStr of Object.keys(genesisState) as PrefixedHexString[]) {
|
|
744
|
+
const addrState = genesisState[addressStr]
|
|
745
|
+
let nonce: PrefixedHexString | undefined
|
|
746
|
+
let balance: PrefixedHexString | bigint
|
|
747
|
+
let code: PrefixedHexString | undefined
|
|
748
|
+
let storage: StoragePair[] | undefined = []
|
|
749
|
+
if (Array.isArray(addrState)) {
|
|
750
|
+
;[balance, code, storage, nonce] = addrState
|
|
751
|
+
} else {
|
|
752
|
+
balance = hexToBigInt(addrState)
|
|
753
|
+
nonce = '0x1'
|
|
754
|
+
code = '0x'
|
|
755
|
+
}
|
|
756
|
+
const address = createAddressFromString(addressStr)
|
|
757
|
+
await this.putAccount(address, new Account())
|
|
758
|
+
const codeBuf = hexToBytes(code ?? '0x')
|
|
759
|
+
|
|
760
|
+
const codeHash = this.keccakFunction(codeBuf)
|
|
761
|
+
|
|
762
|
+
// Set contract storage
|
|
763
|
+
if (storage !== undefined) {
|
|
764
|
+
for (const [storageKey, valHex] of storage) {
|
|
765
|
+
const val = hexToBytes(valHex)
|
|
766
|
+
if (['0x', '0x00'].includes(bytesToHex(val))) {
|
|
767
|
+
continue
|
|
768
|
+
}
|
|
769
|
+
const key = setLengthLeft(hexToBytes(storageKey), 32)
|
|
770
|
+
await this.putStorage(address, key, val)
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
// Put contract code
|
|
774
|
+
await this.putCode(address, codeBuf)
|
|
775
|
+
|
|
776
|
+
// Put account data
|
|
777
|
+
const account = createPartialAccount({
|
|
778
|
+
nonce,
|
|
779
|
+
balance,
|
|
780
|
+
codeHash,
|
|
781
|
+
codeSize: codeBuf.byteLength,
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
await this.putAccount(address, account)
|
|
785
|
+
}
|
|
786
|
+
await this.commit()
|
|
787
|
+
await this.flush()
|
|
788
|
+
}
|
|
789
|
+
}
|