-
Notifications
You must be signed in to change notification settings - Fork 773
/
simpleStateManager.ts
154 lines (131 loc) · 5.19 KB
/
simpleStateManager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import { Account, bytesToHex } from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak.js'
import { OriginalStorageCache } from './cache/originalStorageCache.js'
import { modifyAccountFields } from './util.js'
import type { SimpleStateManagerOpts } from './index.js'
import type { AccountFields, Common, StateManagerInterface } from '@ethereumjs/common'
import type { Address, PrefixedHexString } from '@ethereumjs/util'
/**
* Simple and dependency-free state manager for basic state access use cases
* where a merkle-patricia or verkle tree backed state manager is too heavy-weight.
*
* This state manager comes with the basic state access logic for
* accounts, storage and code (put* and get* methods) as well as a simple
* implementation of checkpointing but lacks methods implementations of
* state root related logic as well as some other non-core functions.
*
* Functionality provided is sufficient to be used for simple EVM use
* cases and the state manager is used as default there.
*
* For a more full fledged and MPT-backed state manager implementation
* have a look at the `@ethereumjs/statemanager` package.
*/
export class SimpleStateManager implements StateManagerInterface {
public accountStack: Map<PrefixedHexString, Account | undefined>[] = []
public codeStack: Map<PrefixedHexString, Uint8Array>[] = []
public storageStack: Map<string, Uint8Array>[] = []
originalStorageCache: {
get(address: Address, key: Uint8Array): Promise<Uint8Array>
clear(): void
}
public readonly common?: Common
constructor(opts: SimpleStateManagerOpts = {}) {
this.checkpointSync()
this.originalStorageCache = new OriginalStorageCache(this.getStorage.bind(this))
this.common = opts.common
}
protected topAccountStack() {
return this.accountStack[this.accountStack.length - 1]
}
protected topCodeStack() {
return this.codeStack[this.codeStack.length - 1]
}
protected topStorageStack() {
return this.storageStack[this.storageStack.length - 1]
}
// Synchronous version of checkpoint() to allow to call from constructor
protected checkpointSync() {
const newTopA = new Map(this.topAccountStack())
for (const [address, account] of newTopA) {
const accountCopy =
account !== undefined
? Object.assign(Object.create(Object.getPrototypeOf(account)), account)
: undefined
newTopA.set(address, accountCopy)
}
this.accountStack.push(newTopA)
this.codeStack.push(new Map(this.topCodeStack()))
this.storageStack.push(new Map(this.topStorageStack()))
}
async getAccount(address: Address): Promise<Account | undefined> {
return this.topAccountStack().get(address.toString())
}
async putAccount(address: Address, account?: Account | undefined): Promise<void> {
this.topAccountStack().set(address.toString(), account)
}
async deleteAccount(address: Address): Promise<void> {
this.topAccountStack().set(address.toString(), undefined)
}
async modifyAccountFields(address: Address, accountFields: AccountFields): Promise<void> {
await modifyAccountFields(this, address, accountFields)
}
async getCode(address: Address): Promise<Uint8Array> {
return this.topCodeStack().get(address.toString()) ?? new Uint8Array(0)
}
async putCode(address: Address, value: Uint8Array): Promise<void> {
this.topCodeStack().set(address.toString(), value)
if ((await this.getAccount(address)) === undefined) {
await this.putAccount(address, new Account())
}
await this.modifyAccountFields(address, {
codeHash: (this.common?.customCrypto.keccak256 ?? keccak256)(value),
})
}
async getCodeSize(address: Address): Promise<number> {
const contractCode = await this.getCode(address)
return contractCode.length
}
async getStorage(address: Address, key: Uint8Array): Promise<Uint8Array> {
return (
this.topStorageStack().get(`${address.toString()}_${bytesToHex(key)}`) ?? new Uint8Array(0)
)
}
async putStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise<void> {
this.topStorageStack().set(`${address.toString()}_${bytesToHex(key)}`, value)
}
async clearStorage(): Promise<void> {}
async checkpoint(): Promise<void> {
this.checkpointSync()
}
async commit(): Promise<void> {
this.accountStack.splice(-2, 1)
this.codeStack.splice(-2, 1)
this.storageStack.splice(-2, 1)
}
async revert(): Promise<void> {
this.accountStack.pop()
this.codeStack.pop()
this.storageStack.pop()
}
async flush(): Promise<void> {}
clearCaches(): void {}
shallowCopy(): StateManagerInterface {
const copy = new SimpleStateManager({ common: this.common })
for (let i = 0; i < this.accountStack.length; i++) {
copy.accountStack.push(new Map(this.accountStack[i]))
copy.codeStack.push(new Map(this.codeStack[i]))
copy.storageStack.push(new Map(this.storageStack[i]))
}
return copy
}
// State root functionality not implemented
getStateRoot(): Promise<Uint8Array> {
throw new Error('Method not implemented.')
}
setStateRoot(): Promise<void> {
throw new Error('Method not implemented.')
}
hasStateRoot(): Promise<boolean> {
throw new Error('Method not implemented.')
}
}