@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,277 @@
|
|
|
1
|
+
import { bytesToUnprefixedHex } from '@feelyourprotocol/util'
|
|
2
|
+
import { OrderedMap } from '@js-sdsl/ordered-map'
|
|
3
|
+
import debugDefault from 'debug'
|
|
4
|
+
import { LRUCache } from 'lru-cache'
|
|
5
|
+
|
|
6
|
+
import { Cache } from './cache.ts'
|
|
7
|
+
import { CacheType } from './types.ts'
|
|
8
|
+
|
|
9
|
+
import type { Account, Address } from '@feelyourprotocol/util'
|
|
10
|
+
import type { CacheOpts } from './types.ts'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* account: undefined
|
|
14
|
+
*
|
|
15
|
+
* Account is known to not exist in the trie
|
|
16
|
+
*/
|
|
17
|
+
type AccountCacheElement = {
|
|
18
|
+
accountRLP: Uint8Array | undefined
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class AccountCache extends Cache {
|
|
22
|
+
_lruCache: LRUCache<string, AccountCacheElement> | undefined
|
|
23
|
+
_orderedMapCache: OrderedMap<string, AccountCacheElement> | undefined
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Diff cache collecting the state of the cache
|
|
27
|
+
* at the beginning of checkpoint height
|
|
28
|
+
* (respectively: before a first modification)
|
|
29
|
+
*
|
|
30
|
+
* If the whole cache element is undefined (in contrast
|
|
31
|
+
* to the account), the element didn't exist in the cache
|
|
32
|
+
* before.
|
|
33
|
+
*/
|
|
34
|
+
_diffCache: Map<string, AccountCacheElement | undefined>[] = []
|
|
35
|
+
constructor(opts: CacheOpts) {
|
|
36
|
+
super()
|
|
37
|
+
if (opts.type === CacheType.LRU) {
|
|
38
|
+
this._lruCache = new LRUCache({
|
|
39
|
+
max: opts.size,
|
|
40
|
+
updateAgeOnGet: true,
|
|
41
|
+
})
|
|
42
|
+
} else {
|
|
43
|
+
this._orderedMapCache = new OrderedMap()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this._diffCache.push(new Map<string, AccountCacheElement | undefined>())
|
|
47
|
+
this._debug = debugDefault('statemanager:cache:account')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_saveCachePreState(cacheKeyHex: string) {
|
|
51
|
+
const diffMap = this._diffCache[this._checkpoints]
|
|
52
|
+
if (!diffMap.has(cacheKeyHex)) {
|
|
53
|
+
let oldElem: AccountCacheElement | undefined
|
|
54
|
+
if (this._lruCache) {
|
|
55
|
+
oldElem = this._lruCache!.get(cacheKeyHex)
|
|
56
|
+
} else {
|
|
57
|
+
oldElem = this._orderedMapCache!.getElementByKey(cacheKeyHex)
|
|
58
|
+
}
|
|
59
|
+
diffMap.set(cacheKeyHex, oldElem)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Puts account to cache under its address.
|
|
65
|
+
* @param address - Address of account
|
|
66
|
+
* @param account - Account or undefined if account doesn't exist in the trie
|
|
67
|
+
*/
|
|
68
|
+
put(
|
|
69
|
+
address: Address,
|
|
70
|
+
account: Account | undefined,
|
|
71
|
+
couldBePartialAccount: boolean = false,
|
|
72
|
+
): void {
|
|
73
|
+
// Using deprecated bytesToUnprefixedHex for performance: used as Map keys for cache lookups.
|
|
74
|
+
const addressHex = bytesToUnprefixedHex(address.bytes)
|
|
75
|
+
this._saveCachePreState(addressHex)
|
|
76
|
+
const elem = {
|
|
77
|
+
accountRLP:
|
|
78
|
+
account !== undefined
|
|
79
|
+
? couldBePartialAccount
|
|
80
|
+
? account.serializeWithPartialInfo()
|
|
81
|
+
: account.serialize()
|
|
82
|
+
: undefined,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (this.DEBUG) {
|
|
86
|
+
this._debug(`Put account ${addressHex}`)
|
|
87
|
+
}
|
|
88
|
+
if (this._lruCache) {
|
|
89
|
+
this._lruCache!.set(addressHex, elem)
|
|
90
|
+
} else {
|
|
91
|
+
this._orderedMapCache!.setElement(addressHex, elem)
|
|
92
|
+
}
|
|
93
|
+
this._stats.writes += 1
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns the queried account or undefined if account doesn't exist
|
|
98
|
+
* @param address - Address of account
|
|
99
|
+
*/
|
|
100
|
+
get(address: Address): AccountCacheElement | undefined {
|
|
101
|
+
// Using deprecated bytesToUnprefixedHex for performance: used as Map keys for cache lookups.
|
|
102
|
+
const addressHex = bytesToUnprefixedHex(address.bytes)
|
|
103
|
+
if (this.DEBUG) {
|
|
104
|
+
this._debug(`Get account ${addressHex}`)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let elem: AccountCacheElement | undefined
|
|
108
|
+
if (this._lruCache) {
|
|
109
|
+
elem = this._lruCache!.get(addressHex)
|
|
110
|
+
} else {
|
|
111
|
+
elem = this._orderedMapCache!.getElementByKey(addressHex)
|
|
112
|
+
}
|
|
113
|
+
this._stats.reads += 1
|
|
114
|
+
if (elem) {
|
|
115
|
+
this._stats.hits += 1
|
|
116
|
+
}
|
|
117
|
+
return elem
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Marks address as deleted in cache.
|
|
122
|
+
* @param address - Address
|
|
123
|
+
*/
|
|
124
|
+
del(address: Address): void {
|
|
125
|
+
// Using deprecated bytesToUnprefixedHex for performance: used as Map keys for cache lookups.
|
|
126
|
+
const addressHex = bytesToUnprefixedHex(address.bytes)
|
|
127
|
+
this._saveCachePreState(addressHex)
|
|
128
|
+
if (this.DEBUG) {
|
|
129
|
+
this._debug(`Delete account ${addressHex}`)
|
|
130
|
+
}
|
|
131
|
+
if (this._lruCache) {
|
|
132
|
+
this._lruCache!.set(addressHex, {
|
|
133
|
+
accountRLP: undefined,
|
|
134
|
+
})
|
|
135
|
+
} else {
|
|
136
|
+
this._orderedMapCache!.setElement(addressHex, {
|
|
137
|
+
accountRLP: undefined,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this._stats.deletions += 1
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Flushes cache by returning accounts that have been modified
|
|
146
|
+
* or deleted and resetting the diff cache (at checkpoint height).
|
|
147
|
+
*/
|
|
148
|
+
flush(): [string, AccountCacheElement][] {
|
|
149
|
+
if (this.DEBUG) {
|
|
150
|
+
this._debug(`Flushing cache on checkpoint ${this._checkpoints}`)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const diffMap = this._diffCache[this._checkpoints]!
|
|
154
|
+
|
|
155
|
+
const items: [string, AccountCacheElement][] = []
|
|
156
|
+
|
|
157
|
+
for (const entry of diffMap.entries()) {
|
|
158
|
+
const cacheKeyHex = entry[0]
|
|
159
|
+
let elem: AccountCacheElement | undefined
|
|
160
|
+
if (this._lruCache) {
|
|
161
|
+
elem = this._lruCache!.get(cacheKeyHex)
|
|
162
|
+
} else {
|
|
163
|
+
elem = this._orderedMapCache!.getElementByKey(cacheKeyHex)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (elem !== undefined) {
|
|
167
|
+
items.push([cacheKeyHex, elem])
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
this._diffCache[this._checkpoints] = new Map<string, AccountCacheElement | undefined>()
|
|
171
|
+
return items
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Revert changes to cache last checkpoint (no effect on trie).
|
|
176
|
+
*/
|
|
177
|
+
revert(): void {
|
|
178
|
+
this._checkpoints -= 1
|
|
179
|
+
if (this.DEBUG) {
|
|
180
|
+
this._debug(`Revert to checkpoint ${this._checkpoints}`)
|
|
181
|
+
}
|
|
182
|
+
const diffMap = this._diffCache.pop()!
|
|
183
|
+
for (const entry of diffMap.entries()) {
|
|
184
|
+
const addressHex = entry[0]
|
|
185
|
+
const elem = entry[1]
|
|
186
|
+
if (elem === undefined) {
|
|
187
|
+
if (this._lruCache) {
|
|
188
|
+
this._lruCache!.delete(addressHex)
|
|
189
|
+
} else {
|
|
190
|
+
this._orderedMapCache!.eraseElementByKey(addressHex)
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
if (this._lruCache) {
|
|
194
|
+
this._lruCache!.set(addressHex, elem)
|
|
195
|
+
} else {
|
|
196
|
+
this._orderedMapCache!.setElement(addressHex, elem)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Commits to current state of cache (no effect on trie).
|
|
204
|
+
*/
|
|
205
|
+
commit(): void {
|
|
206
|
+
this._checkpoints -= 1
|
|
207
|
+
if (this.DEBUG) {
|
|
208
|
+
this._debug(`Commit to checkpoint ${this._checkpoints}`)
|
|
209
|
+
}
|
|
210
|
+
const diffMap = this._diffCache.pop()!
|
|
211
|
+
for (const entry of diffMap.entries()) {
|
|
212
|
+
const addressHex = entry[0]
|
|
213
|
+
const oldEntry = this._diffCache[this._checkpoints].has(addressHex)
|
|
214
|
+
if (!oldEntry) {
|
|
215
|
+
const elem = entry[1]
|
|
216
|
+
this._diffCache[this._checkpoints].set(addressHex, elem)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Marks current state of cache as checkpoint, which can
|
|
223
|
+
* later on be reverted or committed.
|
|
224
|
+
*/
|
|
225
|
+
checkpoint(): void {
|
|
226
|
+
this._checkpoints += 1
|
|
227
|
+
if (this.DEBUG) {
|
|
228
|
+
this._debug(`New checkpoint ${this._checkpoints}`)
|
|
229
|
+
}
|
|
230
|
+
this._diffCache.push(new Map<string, AccountCacheElement | undefined>())
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Returns the size of the cache
|
|
235
|
+
* @returns
|
|
236
|
+
*/
|
|
237
|
+
size() {
|
|
238
|
+
if (this._lruCache) {
|
|
239
|
+
return this._lruCache!.size
|
|
240
|
+
} else {
|
|
241
|
+
return this._orderedMapCache!.size()
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Returns a dict with cache stats
|
|
247
|
+
* @param reset
|
|
248
|
+
*/
|
|
249
|
+
stats(reset = true) {
|
|
250
|
+
const stats = { ...this._stats }
|
|
251
|
+
stats.size = this.size()
|
|
252
|
+
if (reset) {
|
|
253
|
+
this._stats = {
|
|
254
|
+
size: 0,
|
|
255
|
+
reads: 0,
|
|
256
|
+
hits: 0,
|
|
257
|
+
writes: 0,
|
|
258
|
+
deletions: 0,
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return stats
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Clears cache.
|
|
266
|
+
*/
|
|
267
|
+
clear(): void {
|
|
268
|
+
if (this.DEBUG) {
|
|
269
|
+
this._debug(`Clear cache`)
|
|
270
|
+
}
|
|
271
|
+
if (this._lruCache) {
|
|
272
|
+
this._lruCache!.clear()
|
|
273
|
+
} else {
|
|
274
|
+
this._orderedMapCache!.clear()
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { isDebugEnabled } from '@feelyourprotocol/util'
|
|
2
|
+
import debugDefault from 'debug'
|
|
3
|
+
|
|
4
|
+
import type { Debugger } from 'debug'
|
|
5
|
+
|
|
6
|
+
export class Cache {
|
|
7
|
+
_debug: Debugger
|
|
8
|
+
|
|
9
|
+
_checkpoints = 0
|
|
10
|
+
|
|
11
|
+
_stats = {
|
|
12
|
+
size: 0,
|
|
13
|
+
reads: 0,
|
|
14
|
+
hits: 0,
|
|
15
|
+
writes: 0,
|
|
16
|
+
deletions: 0,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* StateManager cache is run in DEBUG mode (default: false)
|
|
21
|
+
* Taken from DEBUG environment variable
|
|
22
|
+
*
|
|
23
|
+
* Safeguards on debug() calls are added for
|
|
24
|
+
* performance reasons to avoid string literal evaluation
|
|
25
|
+
* @hidden
|
|
26
|
+
*/
|
|
27
|
+
protected readonly DEBUG: boolean = false
|
|
28
|
+
|
|
29
|
+
constructor() {
|
|
30
|
+
// Skip DEBUG calls unless 'ethjs' included in environmental DEBUG variables
|
|
31
|
+
this.DEBUG = isDebugEnabled('ethjs')
|
|
32
|
+
|
|
33
|
+
this._debug = debugDefault('statemanager:cache')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { AccountCache } from './account.ts'
|
|
2
|
+
import { CodeCache } from './code.ts'
|
|
3
|
+
import { StorageCache } from './storage.ts'
|
|
4
|
+
import { type CacheOpts, CacheType, type CachesStateManagerOpts } from './types.ts'
|
|
5
|
+
|
|
6
|
+
import type { Address } from '@feelyourprotocol/util'
|
|
7
|
+
|
|
8
|
+
export class Caches {
|
|
9
|
+
account?: AccountCache
|
|
10
|
+
code?: CodeCache
|
|
11
|
+
storage?: StorageCache
|
|
12
|
+
|
|
13
|
+
settings: Record<'account' | 'code' | 'storage', CacheOpts>
|
|
14
|
+
|
|
15
|
+
constructor(opts: CachesStateManagerOpts = {}) {
|
|
16
|
+
const accountSettings = {
|
|
17
|
+
type: opts.account?.type ?? CacheType.ORDERED_MAP,
|
|
18
|
+
size: opts.account?.size ?? 100000,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const codeSettings = {
|
|
22
|
+
type: opts.code?.type ?? CacheType.ORDERED_MAP,
|
|
23
|
+
size: opts.code?.size ?? 20000,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const storageSettings = {
|
|
27
|
+
type: opts.storage?.type ?? CacheType.ORDERED_MAP,
|
|
28
|
+
size: opts.storage?.size ?? 20000,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.settings = {
|
|
32
|
+
account: accountSettings,
|
|
33
|
+
code: codeSettings,
|
|
34
|
+
storage: storageSettings,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (this.settings.account.size !== 0) {
|
|
38
|
+
this.account = new AccountCache({
|
|
39
|
+
size: this.settings.account.size,
|
|
40
|
+
type: this.settings.account.type,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (this.settings.code.size !== 0) {
|
|
45
|
+
this.code = new CodeCache({
|
|
46
|
+
size: this.settings.code.size,
|
|
47
|
+
type: this.settings.code.type,
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (this.settings.storage.size !== 0) {
|
|
52
|
+
this.storage = new StorageCache({
|
|
53
|
+
size: this.settings.storage.size,
|
|
54
|
+
type: this.settings.storage.type,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
checkpoint() {
|
|
60
|
+
this.account?.checkpoint()
|
|
61
|
+
this.storage?.checkpoint()
|
|
62
|
+
this.code?.checkpoint()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
clear() {
|
|
66
|
+
this.account?.clear()
|
|
67
|
+
this.storage?.clear()
|
|
68
|
+
this.code?.clear()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
commit() {
|
|
72
|
+
this.account?.commit()
|
|
73
|
+
this.storage?.commit()
|
|
74
|
+
this.code?.commit()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
deleteAccount(address: Address) {
|
|
78
|
+
this.code?.del(address)
|
|
79
|
+
this.account?.del(address)
|
|
80
|
+
this.storage?.clearStorage(address)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
shallowCopy(downlevelCaches: boolean) {
|
|
84
|
+
let cacheOptions: CachesStateManagerOpts | undefined
|
|
85
|
+
|
|
86
|
+
// Account cache options
|
|
87
|
+
if (this.settings.account.size !== 0) {
|
|
88
|
+
cacheOptions = {
|
|
89
|
+
account: downlevelCaches
|
|
90
|
+
? { size: this.settings.account.size, type: CacheType.ORDERED_MAP }
|
|
91
|
+
: this.settings.account,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Storage cache options
|
|
96
|
+
if (this.settings.storage.size !== 0) {
|
|
97
|
+
cacheOptions = {
|
|
98
|
+
...cacheOptions,
|
|
99
|
+
storage: downlevelCaches
|
|
100
|
+
? { size: this.settings.storage.size, type: CacheType.ORDERED_MAP }
|
|
101
|
+
: this.settings.storage,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Code cache options
|
|
106
|
+
if (this.settings.code.size !== 0) {
|
|
107
|
+
cacheOptions = {
|
|
108
|
+
...cacheOptions,
|
|
109
|
+
code: downlevelCaches
|
|
110
|
+
? { size: this.settings.code.size, type: CacheType.ORDERED_MAP }
|
|
111
|
+
: this.settings.code,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (cacheOptions !== undefined) {
|
|
116
|
+
return new Caches(cacheOptions)
|
|
117
|
+
} else return undefined
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
revert() {
|
|
121
|
+
this.account?.revert()
|
|
122
|
+
this.storage?.revert()
|
|
123
|
+
this.code?.revert()
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { bytesToUnprefixedHex } from '@feelyourprotocol/util'
|
|
2
|
+
import { OrderedMap } from '@js-sdsl/ordered-map'
|
|
3
|
+
import debugDefault from 'debug'
|
|
4
|
+
import { LRUCache } from 'lru-cache'
|
|
5
|
+
|
|
6
|
+
import { Cache } from './cache.ts'
|
|
7
|
+
import { CacheType } from './types.ts'
|
|
8
|
+
|
|
9
|
+
import type { Address } from '@feelyourprotocol/util'
|
|
10
|
+
import type { CacheOpts } from './types.ts'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Represents a cached code element.
|
|
14
|
+
*/
|
|
15
|
+
type CodeCacheElement = {
|
|
16
|
+
code: Uint8Array | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class CodeCache extends Cache {
|
|
20
|
+
_lruCache: LRUCache<string, CodeCacheElement> | undefined
|
|
21
|
+
_orderedMapCache: OrderedMap<string, CodeCacheElement> | undefined
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Diff cache collecting the state of the cache
|
|
25
|
+
* at the beginning of checkpoint height
|
|
26
|
+
* (respectively: before a first modification)
|
|
27
|
+
*
|
|
28
|
+
* If the whole cache element is undefined (in contrast
|
|
29
|
+
* to the code), the element didn't exist in the cache
|
|
30
|
+
* before.
|
|
31
|
+
*/
|
|
32
|
+
_diffCache: Map<string, CodeCacheElement | undefined>[] = []
|
|
33
|
+
|
|
34
|
+
constructor(opts: CacheOpts) {
|
|
35
|
+
super()
|
|
36
|
+
if (opts.type === CacheType.LRU) {
|
|
37
|
+
this._lruCache = new LRUCache({
|
|
38
|
+
max: opts.size,
|
|
39
|
+
updateAgeOnGet: true,
|
|
40
|
+
})
|
|
41
|
+
} else {
|
|
42
|
+
this._orderedMapCache = new OrderedMap()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this._diffCache.push(new Map<string, CodeCacheElement | undefined>())
|
|
46
|
+
this._debug = debugDefault('statemanager:cache:code')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Saves the state of the code cache before making changes to it.
|
|
51
|
+
*
|
|
52
|
+
* @param cacheKeyHex Account key for which code is being modified.
|
|
53
|
+
*/
|
|
54
|
+
_saveCachePreState(cacheKeyHex: string) {
|
|
55
|
+
const diffMap = this._diffCache[this._checkpoints]
|
|
56
|
+
if (!diffMap.has(cacheKeyHex)) {
|
|
57
|
+
let oldElem: CodeCacheElement | undefined
|
|
58
|
+
if (this._lruCache) {
|
|
59
|
+
oldElem = this._lruCache.get(cacheKeyHex)
|
|
60
|
+
} else {
|
|
61
|
+
oldElem = this._orderedMapCache!.getElementByKey(cacheKeyHex)
|
|
62
|
+
}
|
|
63
|
+
diffMap.set(cacheKeyHex, oldElem)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Puts code into the cache under its hash.
|
|
69
|
+
*
|
|
70
|
+
* @param address - Address of account code is being modified for.
|
|
71
|
+
* @param code - Bytecode or undefined if code doesn't exist.
|
|
72
|
+
*/
|
|
73
|
+
put(address: Address, code: Uint8Array | undefined): void {
|
|
74
|
+
// Using deprecated bytesToUnprefixedHex for performance: used as Map keys for cache lookups.
|
|
75
|
+
const addressHex = bytesToUnprefixedHex(address.bytes)
|
|
76
|
+
this._saveCachePreState(addressHex)
|
|
77
|
+
const elem = {
|
|
78
|
+
code,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (this.DEBUG) {
|
|
82
|
+
this._debug(`Put code ${addressHex}`)
|
|
83
|
+
}
|
|
84
|
+
if (this._lruCache) {
|
|
85
|
+
this._lruCache.set(addressHex, elem)
|
|
86
|
+
} else {
|
|
87
|
+
this._orderedMapCache!.setElement(addressHex, elem)
|
|
88
|
+
}
|
|
89
|
+
this._stats.writes += 1
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Returns the queried code or undefined if it doesn't exist.
|
|
94
|
+
*
|
|
95
|
+
* @param address - Account address for which code is being fetched.
|
|
96
|
+
*/
|
|
97
|
+
get(address: Address): CodeCacheElement | undefined {
|
|
98
|
+
// Using deprecated bytesToUnprefixedHex for performance: used as Map keys for cache lookups.
|
|
99
|
+
const addressHex = bytesToUnprefixedHex(address.bytes)
|
|
100
|
+
if (this.DEBUG) {
|
|
101
|
+
this._debug(`Get code ${addressHex}`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let elem: CodeCacheElement | undefined
|
|
105
|
+
if (this._lruCache) {
|
|
106
|
+
elem = this._lruCache.get(addressHex)
|
|
107
|
+
} else {
|
|
108
|
+
elem = this._orderedMapCache!.getElementByKey(addressHex)
|
|
109
|
+
}
|
|
110
|
+
this._stats.reads += 1
|
|
111
|
+
if (elem) {
|
|
112
|
+
this._stats.hits += 1
|
|
113
|
+
}
|
|
114
|
+
return elem
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Marks code as deleted in the cache.
|
|
119
|
+
*
|
|
120
|
+
* @param address - Account address for which code is being fetched.
|
|
121
|
+
*/
|
|
122
|
+
del(address: Address): void {
|
|
123
|
+
// Using deprecated bytesToUnprefixedHex for performance: used as Map keys for cache lookups.
|
|
124
|
+
const addressHex = bytesToUnprefixedHex(address.bytes)
|
|
125
|
+
this._saveCachePreState(addressHex)
|
|
126
|
+
if (this.DEBUG) {
|
|
127
|
+
this._debug(`Delete code ${addressHex}`)
|
|
128
|
+
}
|
|
129
|
+
if (this._lruCache) {
|
|
130
|
+
this._lruCache.set(addressHex, {
|
|
131
|
+
code: undefined,
|
|
132
|
+
})
|
|
133
|
+
} else {
|
|
134
|
+
this._orderedMapCache!.setElement(addressHex, {
|
|
135
|
+
code: undefined,
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
this._stats.deletions += 1
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Flushes the cache by returning codes that have been modified
|
|
144
|
+
* or deleted and resetting the diff cache (at checkpoint height).
|
|
145
|
+
*/
|
|
146
|
+
flush(): [string, CodeCacheElement][] {
|
|
147
|
+
if (this.DEBUG) {
|
|
148
|
+
this._debug(`Flushing cache on checkpoint ${this._checkpoints}`)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const diffMap = this._diffCache[this._checkpoints]
|
|
152
|
+
|
|
153
|
+
const items: [string, CodeCacheElement][] = []
|
|
154
|
+
|
|
155
|
+
for (const entry of diffMap.entries()) {
|
|
156
|
+
const cacheKeyHex = entry[0]
|
|
157
|
+
let elem: CodeCacheElement | undefined
|
|
158
|
+
if (this._lruCache) {
|
|
159
|
+
elem = this._lruCache.get(cacheKeyHex)
|
|
160
|
+
} else {
|
|
161
|
+
elem = this._orderedMapCache!.getElementByKey(cacheKeyHex)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (elem !== undefined) {
|
|
165
|
+
items.push([cacheKeyHex, elem])
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
this._diffCache[this._checkpoints] = new Map<string, CodeCacheElement | undefined>()
|
|
169
|
+
return items
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Revert changes to the cache to the last checkpoint (no effect on trie).
|
|
174
|
+
*/
|
|
175
|
+
revert(): void {
|
|
176
|
+
this._checkpoints -= 1
|
|
177
|
+
if (this.DEBUG) {
|
|
178
|
+
this._debug(`Revert to checkpoint ${this._checkpoints}`)
|
|
179
|
+
}
|
|
180
|
+
const diffMap = this._diffCache.pop()!
|
|
181
|
+
for (const entry of diffMap.entries()) {
|
|
182
|
+
const addressHex = entry[0]
|
|
183
|
+
const elem = entry[1]
|
|
184
|
+
if (elem === undefined) {
|
|
185
|
+
if (this._lruCache) {
|
|
186
|
+
this._lruCache.delete(addressHex)
|
|
187
|
+
} else {
|
|
188
|
+
this._orderedMapCache!.eraseElementByKey(addressHex)
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
if (this._lruCache) {
|
|
192
|
+
this._lruCache.set(addressHex, elem)
|
|
193
|
+
} else {
|
|
194
|
+
this._orderedMapCache!.setElement(addressHex, elem)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Commits the current state of the cache (no effect on trie).
|
|
202
|
+
*/
|
|
203
|
+
commit(): void {
|
|
204
|
+
this._checkpoints -= 1
|
|
205
|
+
if (this.DEBUG) {
|
|
206
|
+
this._debug(`Commit to checkpoint ${this._checkpoints}`)
|
|
207
|
+
}
|
|
208
|
+
const diffMap = this._diffCache.pop()!
|
|
209
|
+
for (const entry of diffMap.entries()) {
|
|
210
|
+
const addressHex = entry[0]
|
|
211
|
+
const oldEntry = this._diffCache[this._checkpoints].has(addressHex)
|
|
212
|
+
if (!oldEntry) {
|
|
213
|
+
const elem = entry[1]
|
|
214
|
+
this._diffCache[this._checkpoints].set(addressHex, elem)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Marks the current state of the cache as a checkpoint, which can
|
|
221
|
+
* later be reverted or committed.
|
|
222
|
+
*/
|
|
223
|
+
checkpoint(): void {
|
|
224
|
+
this._checkpoints += 1
|
|
225
|
+
if (this.DEBUG) {
|
|
226
|
+
this._debug(`New checkpoint ${this._checkpoints}`)
|
|
227
|
+
}
|
|
228
|
+
this._diffCache.push(new Map<string, CodeCacheElement | undefined>())
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Returns the size of the cache
|
|
233
|
+
* @returns
|
|
234
|
+
*/
|
|
235
|
+
size() {
|
|
236
|
+
if (this._lruCache) {
|
|
237
|
+
return this._lruCache!.size
|
|
238
|
+
} else {
|
|
239
|
+
return this._orderedMapCache!.size()
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Returns a dictionary with cache statistics.
|
|
245
|
+
*
|
|
246
|
+
* @param reset - Whether to reset statistics after retrieval.
|
|
247
|
+
* @returns A dictionary with cache statistics.
|
|
248
|
+
*/
|
|
249
|
+
stats(reset = true): any {
|
|
250
|
+
const stats = { ...this._stats }
|
|
251
|
+
stats.size = this.size()
|
|
252
|
+
if (reset) {
|
|
253
|
+
this._stats = {
|
|
254
|
+
size: 0,
|
|
255
|
+
reads: 0,
|
|
256
|
+
hits: 0,
|
|
257
|
+
writes: 0,
|
|
258
|
+
deletions: 0,
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return stats
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Clears the cache.
|
|
266
|
+
*/
|
|
267
|
+
clear(): void {
|
|
268
|
+
if (this.DEBUG) {
|
|
269
|
+
this._debug(`Clear cache`)
|
|
270
|
+
}
|
|
271
|
+
if (this._lruCache) {
|
|
272
|
+
this._lruCache.clear()
|
|
273
|
+
} else {
|
|
274
|
+
this._orderedMapCache!.clear()
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|