@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.
Files changed (169) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +331 -0
  3. package/dist/cjs/cache/account.d.ts +85 -0
  4. package/dist/cjs/cache/account.d.ts.map +1 -0
  5. package/dist/cjs/cache/account.js +252 -0
  6. package/dist/cjs/cache/account.js.map +1 -0
  7. package/dist/cjs/cache/cache.d.ts +23 -0
  8. package/dist/cjs/cache/cache.d.ts.map +1 -0
  9. package/dist/cjs/cache/cache.js +31 -0
  10. package/dist/cjs/cache/cache.js.map +1 -0
  11. package/dist/cjs/cache/caches.d.ts +19 -0
  12. package/dist/cjs/cache/caches.d.ts.map +1 -0
  13. package/dist/cjs/cache/caches.js +107 -0
  14. package/dist/cjs/cache/caches.js.map +1 -0
  15. package/dist/cjs/cache/code.d.ts +87 -0
  16. package/dist/cjs/cache/code.d.ts.map +1 -0
  17. package/dist/cjs/cache/code.js +258 -0
  18. package/dist/cjs/cache/code.js.map +1 -0
  19. package/dist/cjs/cache/index.d.ts +7 -0
  20. package/dist/cjs/cache/index.d.ts.map +1 -0
  21. package/dist/cjs/cache/index.js +23 -0
  22. package/dist/cjs/cache/index.js.map +1 -0
  23. package/dist/cjs/cache/originalStorageCache.d.ts +21 -0
  24. package/dist/cjs/cache/originalStorageCache.d.ts.map +1 -0
  25. package/dist/cjs/cache/originalStorageCache.js +52 -0
  26. package/dist/cjs/cache/originalStorageCache.js.map +1 -0
  27. package/dist/cjs/cache/storage.d.ts +101 -0
  28. package/dist/cjs/cache/storage.d.ts.map +1 -0
  29. package/dist/cjs/cache/storage.js +337 -0
  30. package/dist/cjs/cache/storage.js.map +1 -0
  31. package/dist/cjs/cache/types.d.ts +36 -0
  32. package/dist/cjs/cache/types.d.ts.map +1 -0
  33. package/dist/cjs/cache/types.js +8 -0
  34. package/dist/cjs/cache/types.js.map +1 -0
  35. package/dist/cjs/index.d.ts +8 -0
  36. package/dist/cjs/index.d.ts.map +1 -0
  37. package/dist/cjs/index.js +24 -0
  38. package/dist/cjs/index.js.map +1 -0
  39. package/dist/cjs/merkleStateManager.d.ts +260 -0
  40. package/dist/cjs/merkleStateManager.d.ts.map +1 -0
  41. package/dist/cjs/merkleStateManager.js +616 -0
  42. package/dist/cjs/merkleStateManager.js.map +1 -0
  43. package/dist/cjs/package.json +3 -0
  44. package/dist/cjs/proof/index.d.ts +3 -0
  45. package/dist/cjs/proof/index.d.ts.map +1 -0
  46. package/dist/cjs/proof/index.js +19 -0
  47. package/dist/cjs/proof/index.js.map +1 -0
  48. package/dist/cjs/proof/merkle.d.ts +40 -0
  49. package/dist/cjs/proof/merkle.d.ts.map +1 -0
  50. package/dist/cjs/proof/merkle.js +182 -0
  51. package/dist/cjs/proof/merkle.js.map +1 -0
  52. package/dist/cjs/proof/rpc.d.ts +10 -0
  53. package/dist/cjs/proof/rpc.d.ts.map +1 -0
  54. package/dist/cjs/proof/rpc.js +20 -0
  55. package/dist/cjs/proof/rpc.js.map +1 -0
  56. package/dist/cjs/rpcStateManager.d.ts +162 -0
  57. package/dist/cjs/rpcStateManager.d.ts.map +1 -0
  58. package/dist/cjs/rpcStateManager.js +313 -0
  59. package/dist/cjs/rpcStateManager.js.map +1 -0
  60. package/dist/cjs/simpleStateManager.d.ts +54 -0
  61. package/dist/cjs/simpleStateManager.d.ts.map +1 -0
  62. package/dist/cjs/simpleStateManager.js +125 -0
  63. package/dist/cjs/simpleStateManager.js.map +1 -0
  64. package/dist/cjs/statefulBinaryTreeStateManager.d.ts +69 -0
  65. package/dist/cjs/statefulBinaryTreeStateManager.d.ts.map +1 -0
  66. package/dist/cjs/statefulBinaryTreeStateManager.js +576 -0
  67. package/dist/cjs/statefulBinaryTreeStateManager.js.map +1 -0
  68. package/dist/cjs/types.d.ts +92 -0
  69. package/dist/cjs/types.d.ts.map +1 -0
  70. package/dist/cjs/types.js +3 -0
  71. package/dist/cjs/types.js.map +1 -0
  72. package/dist/cjs/util.d.ts +4 -0
  73. package/dist/cjs/util.d.ts.map +1 -0
  74. package/dist/cjs/util.js +21 -0
  75. package/dist/cjs/util.js.map +1 -0
  76. package/dist/esm/cache/account.d.ts +85 -0
  77. package/dist/esm/cache/account.d.ts.map +1 -0
  78. package/dist/esm/cache/account.js +248 -0
  79. package/dist/esm/cache/account.js.map +1 -0
  80. package/dist/esm/cache/cache.d.ts +23 -0
  81. package/dist/esm/cache/cache.d.ts.map +1 -0
  82. package/dist/esm/cache/cache.js +27 -0
  83. package/dist/esm/cache/cache.js.map +1 -0
  84. package/dist/esm/cache/caches.d.ts +19 -0
  85. package/dist/esm/cache/caches.d.ts.map +1 -0
  86. package/dist/esm/cache/caches.js +103 -0
  87. package/dist/esm/cache/caches.js.map +1 -0
  88. package/dist/esm/cache/code.d.ts +87 -0
  89. package/dist/esm/cache/code.d.ts.map +1 -0
  90. package/dist/esm/cache/code.js +254 -0
  91. package/dist/esm/cache/code.js.map +1 -0
  92. package/dist/esm/cache/index.d.ts +7 -0
  93. package/dist/esm/cache/index.d.ts.map +1 -0
  94. package/dist/esm/cache/index.js +7 -0
  95. package/dist/esm/cache/index.js.map +1 -0
  96. package/dist/esm/cache/originalStorageCache.d.ts +21 -0
  97. package/dist/esm/cache/originalStorageCache.d.ts.map +1 -0
  98. package/dist/esm/cache/originalStorageCache.js +48 -0
  99. package/dist/esm/cache/originalStorageCache.js.map +1 -0
  100. package/dist/esm/cache/storage.d.ts +101 -0
  101. package/dist/esm/cache/storage.d.ts.map +1 -0
  102. package/dist/esm/cache/storage.js +333 -0
  103. package/dist/esm/cache/storage.js.map +1 -0
  104. package/dist/esm/cache/types.d.ts +36 -0
  105. package/dist/esm/cache/types.d.ts.map +1 -0
  106. package/dist/esm/cache/types.js +5 -0
  107. package/dist/esm/cache/types.js.map +1 -0
  108. package/dist/esm/index.d.ts +8 -0
  109. package/dist/esm/index.d.ts.map +1 -0
  110. package/dist/esm/index.js +8 -0
  111. package/dist/esm/index.js.map +1 -0
  112. package/dist/esm/merkleStateManager.d.ts +260 -0
  113. package/dist/esm/merkleStateManager.d.ts.map +1 -0
  114. package/dist/esm/merkleStateManager.js +612 -0
  115. package/dist/esm/merkleStateManager.js.map +1 -0
  116. package/dist/esm/package.json +3 -0
  117. package/dist/esm/proof/index.d.ts +3 -0
  118. package/dist/esm/proof/index.d.ts.map +1 -0
  119. package/dist/esm/proof/index.js +3 -0
  120. package/dist/esm/proof/index.js.map +1 -0
  121. package/dist/esm/proof/merkle.d.ts +40 -0
  122. package/dist/esm/proof/merkle.d.ts.map +1 -0
  123. package/dist/esm/proof/merkle.js +175 -0
  124. package/dist/esm/proof/merkle.js.map +1 -0
  125. package/dist/esm/proof/rpc.d.ts +10 -0
  126. package/dist/esm/proof/rpc.d.ts.map +1 -0
  127. package/dist/esm/proof/rpc.js +17 -0
  128. package/dist/esm/proof/rpc.js.map +1 -0
  129. package/dist/esm/rpcStateManager.d.ts +162 -0
  130. package/dist/esm/rpcStateManager.d.ts.map +1 -0
  131. package/dist/esm/rpcStateManager.js +308 -0
  132. package/dist/esm/rpcStateManager.js.map +1 -0
  133. package/dist/esm/simpleStateManager.d.ts +54 -0
  134. package/dist/esm/simpleStateManager.d.ts.map +1 -0
  135. package/dist/esm/simpleStateManager.js +121 -0
  136. package/dist/esm/simpleStateManager.js.map +1 -0
  137. package/dist/esm/statefulBinaryTreeStateManager.d.ts +69 -0
  138. package/dist/esm/statefulBinaryTreeStateManager.d.ts.map +1 -0
  139. package/dist/esm/statefulBinaryTreeStateManager.js +572 -0
  140. package/dist/esm/statefulBinaryTreeStateManager.js.map +1 -0
  141. package/dist/esm/types.d.ts +92 -0
  142. package/dist/esm/types.d.ts.map +1 -0
  143. package/dist/esm/types.js +2 -0
  144. package/dist/esm/types.js.map +1 -0
  145. package/dist/esm/util.d.ts +4 -0
  146. package/dist/esm/util.d.ts.map +1 -0
  147. package/dist/esm/util.js +18 -0
  148. package/dist/esm/util.js.map +1 -0
  149. package/dist/tsconfig.prod.cjs.tsbuildinfo +1 -0
  150. package/dist/tsconfig.prod.esm.tsbuildinfo +1 -0
  151. package/package.json +74 -0
  152. package/src/cache/account.ts +277 -0
  153. package/src/cache/cache.ts +35 -0
  154. package/src/cache/caches.ts +125 -0
  155. package/src/cache/code.ts +277 -0
  156. package/src/cache/index.ts +6 -0
  157. package/src/cache/originalStorageCache.ts +57 -0
  158. package/src/cache/storage.ts +369 -0
  159. package/src/cache/types.ts +38 -0
  160. package/src/index.ts +7 -0
  161. package/src/merkleStateManager.ts +737 -0
  162. package/src/proof/index.ts +2 -0
  163. package/src/proof/merkle.ts +264 -0
  164. package/src/proof/rpc.ts +24 -0
  165. package/src/rpcStateManager.ts +381 -0
  166. package/src/simpleStateManager.ts +154 -0
  167. package/src/statefulBinaryTreeStateManager.ts +789 -0
  168. package/src/types.ts +103 -0
  169. 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
+ }
@@ -0,0 +1,6 @@
1
+ export * from './account.ts'
2
+ export * from './caches.ts'
3
+ export * from './code.ts'
4
+ export * from './originalStorageCache.ts'
5
+ export * from './storage.ts'
6
+ export * from './types.ts'