17
17
package state
18
18
19
19
import (
20
+ "fmt"
20
21
"maps"
22
+ "slices"
23
+ "sort"
21
24
22
25
"github.com/ethereum/go-ethereum/common"
26
+ "github.com/ethereum/go-ethereum/core/types"
23
27
"github.com/holiman/uint256"
24
28
)
25
29
30
+ type revision struct {
31
+ id int
32
+ journalIndex int
33
+ }
34
+
26
35
// journalEntry is a modification entry in the state change journal that can be
27
36
// reverted on demand.
28
37
type journalEntry interface {
@@ -42,6 +51,9 @@ type journalEntry interface {
42
51
type journal struct {
43
52
entries []journalEntry // Current changes tracked by the journal
44
53
dirties map [common.Address ]int // Dirty accounts and the number of changes
54
+
55
+ validRevisions []revision
56
+ nextRevisionId int
45
57
}
46
58
47
59
// newJournal creates a new initialized journal.
@@ -51,6 +63,40 @@ func newJournal() *journal {
51
63
}
52
64
}
53
65
66
+ // reset clears the journal, after this operation the journal can be used anew.
67
+ // It is semantically similar to calling 'newJournal', but the underlying slices
68
+ // can be reused.
69
+ func (j * journal ) reset () {
70
+ j .entries = j .entries [:0 ]
71
+ j .validRevisions = j .validRevisions [:0 ]
72
+ clear (j .dirties )
73
+ j .nextRevisionId = 0
74
+ }
75
+
76
+ // snapshot returns an identifier for the current revision of the state.
77
+ func (j * journal ) snapshot () int {
78
+ id := j .nextRevisionId
79
+ j .nextRevisionId ++
80
+ j .validRevisions = append (j .validRevisions , revision {id , j .length ()})
81
+ return id
82
+ }
83
+
84
+ // revertToSnapshot reverts all state changes made since the given revision.
85
+ func (j * journal ) revertToSnapshot (revid int , s * StateDB ) {
86
+ // Find the snapshot in the stack of valid snapshots.
87
+ idx := sort .Search (len (j .validRevisions ), func (i int ) bool {
88
+ return j .validRevisions [i ].id >= revid
89
+ })
90
+ if idx == len (j .validRevisions ) || j .validRevisions [idx ].id != revid {
91
+ panic (fmt .Errorf ("revision id %v cannot be reverted" , revid ))
92
+ }
93
+ snapshot := j .validRevisions [idx ].journalIndex
94
+
95
+ // Replay the journal to undo changes and remove invalidated snapshots
96
+ j .revert (s , snapshot )
97
+ j .validRevisions = j .validRevisions [:idx ]
98
+ }
99
+
54
100
// append inserts a new modification entry to the end of the change journal.
55
101
func (j * journal ) append (entry journalEntry ) {
56
102
j .entries = append (j .entries , entry )
@@ -95,11 +141,90 @@ func (j *journal) copy() *journal {
95
141
entries = append (entries , j .entries [i ].copy ())
96
142
}
97
143
return & journal {
98
- entries : entries ,
99
- dirties : maps .Clone (j .dirties ),
144
+ entries : entries ,
145
+ dirties : maps .Clone (j .dirties ),
146
+ validRevisions : slices .Clone (j .validRevisions ),
147
+ nextRevisionId : j .nextRevisionId ,
148
+ }
149
+ }
150
+
151
+ func (j * journal ) logChange (txHash common.Hash ) {
152
+ j .append (addLogChange {txhash : txHash })
153
+ }
154
+
155
+ func (j * journal ) createObject (addr common.Address ) {
156
+ j .append (createObjectChange {account : & addr })
157
+ }
158
+
159
+ func (j * journal ) createContract (addr common.Address ) {
160
+ j .append (createContractChange {account : addr })
161
+ }
162
+
163
+ func (j * journal ) destruct (addr common.Address ) {
164
+ j .append (selfDestructChange {account : & addr })
165
+ }
166
+
167
+ func (j * journal ) storageChange (addr common.Address , key , prev , origin common.Hash ) {
168
+ j .append (storageChange {
169
+ account : & addr ,
170
+ key : key ,
171
+ prevvalue : prev ,
172
+ origvalue : origin ,
173
+ })
174
+ }
175
+
176
+ func (j * journal ) transientStateChange (addr common.Address , key , prev common.Hash ) {
177
+ j .append (transientStorageChange {
178
+ account : & addr ,
179
+ key : key ,
180
+ prevalue : prev ,
181
+ })
182
+ }
183
+
184
+ func (j * journal ) refundChange (previous uint64 ) {
185
+ j .append (refundChange {prev : previous })
186
+ }
187
+
188
+ func (j * journal ) balanceChange (addr common.Address , previous * uint256.Int ) {
189
+ j .append (balanceChange {
190
+ account : & addr ,
191
+ prev : previous .Clone (),
192
+ })
193
+ }
194
+
195
+ func (j * journal ) setCode (address common.Address ) {
196
+ j .append (codeChange {account : & address })
197
+ }
198
+
199
+ func (j * journal ) nonceChange (address common.Address , prev uint64 ) {
200
+ j .append (nonceChange {
201
+ account : & address ,
202
+ prev : prev ,
203
+ })
204
+ }
205
+
206
+ func (j * journal ) touchChange (address common.Address ) {
207
+ j .append (touchChange {
208
+ account : & address ,
209
+ })
210
+ if address == ripemd {
211
+ // Explicitly put it in the dirty-cache, which is otherwise generated from
212
+ // flattened journals.
213
+ j .dirty (address )
100
214
}
101
215
}
102
216
217
+ func (j * journal ) accessListAddAccount (addr common.Address ) {
218
+ j .append (accessListAddAccountChange {& addr })
219
+ }
220
+
221
+ func (j * journal ) accessListAddSlot (addr common.Address , slot common.Hash ) {
222
+ j .append (accessListAddSlotChange {
223
+ address : & addr ,
224
+ slot : & slot ,
225
+ })
226
+ }
227
+
103
228
type (
104
229
// Changes to the account trie.
105
230
createObjectChange struct {
@@ -114,9 +239,7 @@ type (
114
239
}
115
240
116
241
selfDestructChange struct {
117
- account * common.Address
118
- prev bool // whether account had already self-destructed
119
- prevbalance * uint256.Int
242
+ account * common.Address
120
243
}
121
244
122
245
// Changes to individual accounts.
@@ -135,8 +258,7 @@ type (
135
258
origvalue common.Hash
136
259
}
137
260
codeChange struct {
138
- account * common.Address
139
- prevcode , prevhash []byte
261
+ account * common.Address
140
262
}
141
263
142
264
// Changes to other state values.
@@ -146,9 +268,6 @@ type (
146
268
addLogChange struct {
147
269
txhash common.Hash
148
270
}
149
- addPreimageChange struct {
150
- hash common.Hash
151
- }
152
271
touchChange struct {
153
272
account * common.Address
154
273
}
@@ -200,8 +319,7 @@ func (ch createContractChange) copy() journalEntry {
200
319
func (ch selfDestructChange ) revert (s * StateDB ) {
201
320
obj := s .getStateObject (* ch .account )
202
321
if obj != nil {
203
- obj .selfDestructed = ch .prev
204
- obj .setBalance (ch .prevbalance )
322
+ obj .selfDestructed = false
205
323
}
206
324
}
207
325
@@ -211,9 +329,7 @@ func (ch selfDestructChange) dirtied() *common.Address {
211
329
212
330
func (ch selfDestructChange ) copy () journalEntry {
213
331
return selfDestructChange {
214
- account : ch .account ,
215
- prev : ch .prev ,
216
- prevbalance : new (uint256.Int ).Set (ch .prevbalance ),
332
+ account : ch .account ,
217
333
}
218
334
}
219
335
@@ -263,19 +379,15 @@ func (ch nonceChange) copy() journalEntry {
263
379
}
264
380
265
381
func (ch codeChange ) revert (s * StateDB ) {
266
- s .getStateObject (* ch .account ).setCode (common . BytesToHash ( ch . prevhash ), ch . prevcode )
382
+ s .getStateObject (* ch .account ).setCode (types . EmptyCodeHash , nil )
267
383
}
268
384
269
385
func (ch codeChange ) dirtied () * common.Address {
270
386
return ch .account
271
387
}
272
388
273
389
func (ch codeChange ) copy () journalEntry {
274
- return codeChange {
275
- account : ch .account ,
276
- prevhash : common .CopyBytes (ch .prevhash ),
277
- prevcode : common .CopyBytes (ch .prevcode ),
278
- }
390
+ return codeChange {account : ch .account }
279
391
}
280
392
281
393
func (ch storageChange ) revert (s * StateDB ) {
@@ -344,20 +456,6 @@ func (ch addLogChange) copy() journalEntry {
344
456
}
345
457
}
346
458
347
- func (ch addPreimageChange ) revert (s * StateDB ) {
348
- delete (s .preimages , ch .hash )
349
- }
350
-
351
- func (ch addPreimageChange ) dirtied () * common.Address {
352
- return nil
353
- }
354
-
355
- func (ch addPreimageChange ) copy () journalEntry {
356
- return addPreimageChange {
357
- hash : ch .hash ,
358
- }
359
- }
360
-
361
459
func (ch accessListAddAccountChange ) revert (s * StateDB ) {
362
460
/*
363
461
One important invariant here, is that whenever a (addr, slot) is added, if the
0 commit comments