@@ -86,7 +86,7 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis
86
86
config : genesis .Config ,
87
87
events : filters .NewEventSystem (& filterBackend {database , blockchain }, false ),
88
88
}
89
- backend .rollback ()
89
+ backend .rollback (blockchain . CurrentBlock () )
90
90
return backend
91
91
}
92
92
@@ -112,30 +112,59 @@ func (b *SimulatedBackend) Commit() {
112
112
if _ , err := b .blockchain .InsertChain ([]* types.Block {b .pendingBlock }); err != nil {
113
113
panic (err ) // This cannot happen unless the simulator is wrong, fail in that case
114
114
}
115
- b .rollback ()
115
+ // Using the last inserted block here makes it possible to build on a side
116
+ // chain after a fork.
117
+ b .rollback (b .pendingBlock )
116
118
}
117
119
118
120
// Rollback aborts all pending transactions, reverting to the last committed state.
119
121
func (b * SimulatedBackend ) Rollback () {
120
122
b .mu .Lock ()
121
123
defer b .mu .Unlock ()
122
124
123
- b .rollback ()
125
+ b .rollback (b . blockchain . CurrentBlock () )
124
126
}
125
127
126
- func (b * SimulatedBackend ) rollback () {
127
- blocks , _ := core .GenerateChain (b .config , b . blockchain . CurrentBlock () , ethash .NewFaker (), b .database , 1 , func (int , * core.BlockGen ) {})
128
+ func (b * SimulatedBackend ) rollback (parent * types. Block ) {
129
+ blocks , _ := core .GenerateChain (b .config , parent , ethash .NewFaker (), b .database , 1 , func (int , * core.BlockGen ) {})
128
130
129
131
b .pendingBlock = blocks [0 ]
130
132
b .pendingState , _ = state .New (b .pendingBlock .Root (), b .blockchain .StateCache (), nil )
131
133
}
132
134
135
+ // Fork creates a side-chain that can be used to simulate reorgs.
136
+ //
137
+ // This function should be called with the ancestor block where the new side
138
+ // chain should be started. Transactions (old and new) can then be applied on
139
+ // top and Commit-ed.
140
+ //
141
+ // Note, the side-chain will only become canonical (and trigger the events) when
142
+ // it becomes longer. Until then CallContract will still operate on the current
143
+ // canonical chain.
144
+ //
145
+ // There is a % chance that the side chain becomes canonical at the same length
146
+ // to simulate live network behavior.
147
+ func (b * SimulatedBackend ) Fork (ctx context.Context , parent common.Hash ) error {
148
+ b .mu .Lock ()
149
+ defer b .mu .Unlock ()
150
+
151
+ if len (b .pendingBlock .Transactions ()) != 0 {
152
+ return errors .New ("pending block dirty" )
153
+ }
154
+ block , err := b .blockByHash (ctx , parent )
155
+ if err != nil {
156
+ return err
157
+ }
158
+ b .rollback (block )
159
+ return nil
160
+ }
161
+
133
162
// stateByBlockNumber retrieves a state by a given blocknumber.
134
163
func (b * SimulatedBackend ) stateByBlockNumber (ctx context.Context , blockNumber * big.Int ) (* state.StateDB , error ) {
135
164
if blockNumber == nil || blockNumber .Cmp (b .blockchain .CurrentBlock ().Number ()) == 0 {
136
165
return b .blockchain .State ()
137
166
}
138
- block , err := b .blockByNumberNoLock (ctx , blockNumber )
167
+ block , err := b .blockByNumber (ctx , blockNumber )
139
168
if err != nil {
140
169
return nil , err
141
170
}
@@ -228,6 +257,11 @@ func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*
228
257
b .mu .Lock ()
229
258
defer b .mu .Unlock ()
230
259
260
+ return b .blockByHash (ctx , hash )
261
+ }
262
+
263
+ // blockByHash retrieves a block based on the block hash without Locking.
264
+ func (b * SimulatedBackend ) blockByHash (ctx context.Context , hash common.Hash ) (* types.Block , error ) {
231
265
if hash == b .pendingBlock .Hash () {
232
266
return b .pendingBlock , nil
233
267
}
@@ -246,12 +280,12 @@ func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (
246
280
b .mu .Lock ()
247
281
defer b .mu .Unlock ()
248
282
249
- return b .blockByNumberNoLock (ctx , number )
283
+ return b .blockByNumber (ctx , number )
250
284
}
251
285
252
- // blockByNumberNoLock retrieves a block from the database by number, caching it
286
+ // blockByNumber retrieves a block from the database by number, caching it
253
287
// (associated with its hash) if found without Lock.
254
- func (b * SimulatedBackend ) blockByNumberNoLock (ctx context.Context , number * big.Int ) (* types.Block , error ) {
288
+ func (b * SimulatedBackend ) blockByNumber (ctx context.Context , number * big.Int ) (* types.Block , error ) {
255
289
if number == nil || number .Cmp (b .pendingBlock .Number ()) == 0 {
256
290
return b .blockchain .CurrentBlock (), nil
257
291
}
@@ -559,8 +593,12 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
559
593
b .mu .Lock ()
560
594
defer b .mu .Unlock ()
561
595
562
- // Check transaction validity.
563
- block := b .blockchain .CurrentBlock ()
596
+ // Get the last block
597
+ block , err := b .blockByHash (ctx , b .pendingBlock .ParentHash ())
598
+ if err != nil {
599
+ panic ("could not fetch parent" )
600
+ }
601
+ // Check transaction validity
564
602
signer := types .MakeSigner (b .blockchain .Config (), block .Number ())
565
603
sender , err := types .Sender (signer , tx )
566
604
if err != nil {
@@ -570,8 +608,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
570
608
if tx .Nonce () != nonce {
571
609
panic (fmt .Errorf ("invalid transaction nonce: got %d, want %d" , tx .Nonce (), nonce ))
572
610
}
573
-
574
- // Include tx in chain.
611
+ // Include tx in chain
575
612
blocks , _ := core .GenerateChain (b .config , block , ethash .NewFaker (), b .database , 1 , func (number int , block * core.BlockGen ) {
576
613
for _ , tx := range b .pendingBlock .Transactions () {
577
614
block .AddTxWithChain (b .blockchain , tx )
0 commit comments