@@ -30,11 +30,15 @@ import (
30
30
"github.com/ethereum/go-ethereum/common/math"
31
31
"github.com/ethereum/go-ethereum/consensus/ethash"
32
32
"github.com/ethereum/go-ethereum/core"
33
+ "github.com/ethereum/go-ethereum/core/bloombits"
33
34
"github.com/ethereum/go-ethereum/core/state"
34
35
"github.com/ethereum/go-ethereum/core/types"
35
36
"github.com/ethereum/go-ethereum/core/vm"
37
+ "github.com/ethereum/go-ethereum/eth/filters"
36
38
"github.com/ethereum/go-ethereum/ethdb"
39
+ "github.com/ethereum/go-ethereum/event"
37
40
"github.com/ethereum/go-ethereum/params"
41
+ "github.com/ethereum/go-ethereum/rpc"
38
42
)
39
43
40
44
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
@@ -53,6 +57,8 @@ type SimulatedBackend struct {
53
57
pendingBlock * types.Block // Currently pending block that will be imported on request
54
58
pendingState * state.StateDB // Currently pending state that will be the active on on request
55
59
60
+ events * filters.EventSystem // Event system for filtering log events live
61
+
56
62
config * params.ChainConfig
57
63
}
58
64
@@ -62,8 +68,14 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
62
68
database , _ := ethdb .NewMemDatabase ()
63
69
genesis := core.Genesis {Config : params .AllEthashProtocolChanges , Alloc : alloc }
64
70
genesis .MustCommit (database )
65
- blockchain , _ := core .NewBlockChain (database , genesis .Config , ethash .NewFaker (), vm.Config {})
66
- backend := & SimulatedBackend {database : database , blockchain : blockchain , config : genesis .Config }
71
+ blockchain , _ := core .NewBlockChain (database , nil , genesis .Config , ethash .NewFaker (), vm.Config {})
72
+
73
+ backend := & SimulatedBackend {
74
+ database : database ,
75
+ blockchain : blockchain ,
76
+ config : genesis .Config ,
77
+ events : filters .NewEventSystem (new (event.TypeMux ), & filterBackend {database , blockchain }, false ),
78
+ }
67
79
backend .rollback ()
68
80
return backend
69
81
}
@@ -90,8 +102,10 @@ func (b *SimulatedBackend) Rollback() {
90
102
91
103
func (b * SimulatedBackend ) rollback () {
92
104
blocks , _ := core .GenerateChain (b .config , b .blockchain .CurrentBlock (), ethash .NewFaker (), b .database , 1 , func (int , * core.BlockGen ) {})
105
+ statedb , _ := b .blockchain .State ()
106
+
93
107
b .pendingBlock = blocks [0 ]
94
- b .pendingState , _ = state .New (b .pendingBlock .Root (), state . NewDatabase ( b . database ))
108
+ b .pendingState , _ = state .New (b .pendingBlock .Root (), statedb . Database ( ))
95
109
}
96
110
97
111
// CodeAt returns the code associated with a certain account in the blockchain.
@@ -248,7 +262,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
248
262
return hi , nil
249
263
}
250
264
251
- // callContract implemens common code between normal and pending contract calls.
265
+ // callContract implements common code between normal and pending contract calls.
252
266
// state is modified during execution, make sure to copy it if necessary.
253
267
func (b * SimulatedBackend ) callContract (ctx context.Context , call ethereum.CallMsg , block * types.Block , statedb * state.StateDB ) ([]byte , uint64 , bool , error ) {
254
268
// Ensure message is initialized properly.
@@ -297,12 +311,76 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
297
311
}
298
312
block .AddTx (tx )
299
313
})
314
+ statedb , _ := b .blockchain .State ()
315
+
300
316
b .pendingBlock = blocks [0 ]
301
- b .pendingState , _ = state .New (b .pendingBlock .Root (), state . NewDatabase ( b . database ))
317
+ b .pendingState , _ = state .New (b .pendingBlock .Root (), statedb . Database ( ))
302
318
return nil
303
319
}
304
320
305
- // JumpTimeInSeconds adds skip seconds to the clock
321
+ // FilterLogs executes a log filter operation, blocking during execution and
322
+ // returning all the results in one batch.
323
+ //
324
+ // TODO(karalabe): Deprecate when the subscription one can return past data too.
325
+ func (b * SimulatedBackend ) FilterLogs (ctx context.Context , query ethereum.FilterQuery ) ([]types.Log , error ) {
326
+ // Initialize unset filter boundaried to run from genesis to chain head
327
+ from := int64 (0 )
328
+ if query .FromBlock != nil {
329
+ from = query .FromBlock .Int64 ()
330
+ }
331
+ to := int64 (- 1 )
332
+ if query .ToBlock != nil {
333
+ to = query .ToBlock .Int64 ()
334
+ }
335
+ // Construct and execute the filter
336
+ filter := filters .New (& filterBackend {b .database , b .blockchain }, from , to , query .Addresses , query .Topics )
337
+
338
+ logs , err := filter .Logs (ctx )
339
+ if err != nil {
340
+ return nil , err
341
+ }
342
+ res := make ([]types.Log , len (logs ))
343
+ for i , log := range logs {
344
+ res [i ] = * log
345
+ }
346
+ return res , nil
347
+ }
348
+
349
+ // SubscribeFilterLogs creates a background log filtering operation, returning a
350
+ // subscription immediately, which can be used to stream the found events.
351
+ func (b * SimulatedBackend ) SubscribeFilterLogs (ctx context.Context , query ethereum.FilterQuery , ch chan <- types.Log ) (ethereum.Subscription , error ) {
352
+ // Subscribe to contract events
353
+ sink := make (chan []* types.Log )
354
+
355
+ sub , err := b .events .SubscribeLogs (query , sink )
356
+ if err != nil {
357
+ return nil , err
358
+ }
359
+ // Since we're getting logs in batches, we need to flatten them into a plain stream
360
+ return event .NewSubscription (func (quit <- chan struct {}) error {
361
+ defer sub .Unsubscribe ()
362
+ for {
363
+ select {
364
+ case logs := <- sink :
365
+ for _ , log := range logs {
366
+ select {
367
+ case ch <- * log :
368
+ case err := <- sub .Err ():
369
+ return err
370
+ case <- quit :
371
+ return nil
372
+ }
373
+ }
374
+ case err := <- sub .Err ():
375
+ return err
376
+ case <- quit :
377
+ return nil
378
+ }
379
+ }
380
+ }), nil
381
+ }
382
+
383
+ // AdjustTime adds a time shift to the simulated clock.
306
384
func (b * SimulatedBackend ) AdjustTime (adjustment time.Duration ) error {
307
385
b .mu .Lock ()
308
386
defer b .mu .Unlock ()
@@ -312,8 +390,10 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
312
390
}
313
391
block .OffsetTime (int64 (adjustment .Seconds ()))
314
392
})
393
+ statedb , _ := b .blockchain .State ()
394
+
315
395
b .pendingBlock = blocks [0 ]
316
- b .pendingState , _ = state .New (b .pendingBlock .Root (), state . NewDatabase ( b . database ))
396
+ b .pendingState , _ = state .New (b .pendingBlock .Root (), statedb . Database ( ))
317
397
318
398
return nil
319
399
}
@@ -331,3 +411,44 @@ func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
331
411
func (m callmsg ) Gas () uint64 { return m .CallMsg .Gas }
332
412
func (m callmsg ) Value () * big.Int { return m .CallMsg .Value }
333
413
func (m callmsg ) Data () []byte { return m .CallMsg .Data }
414
+
415
+ // filterBackend implements filters.Backend to support filtering for logs without
416
+ // taking bloom-bits acceleration structures into account.
417
+ type filterBackend struct {
418
+ db ethdb.Database
419
+ bc * core.BlockChain
420
+ }
421
+
422
+ func (fb * filterBackend ) ChainDb () ethdb.Database { return fb .db }
423
+ func (fb * filterBackend ) EventMux () * event.TypeMux { panic ("not supported" ) }
424
+
425
+ func (fb * filterBackend ) HeaderByNumber (ctx context.Context , block rpc.BlockNumber ) (* types.Header , error ) {
426
+ if block == rpc .LatestBlockNumber {
427
+ return fb .bc .CurrentHeader (), nil
428
+ }
429
+ return fb .bc .GetHeaderByNumber (uint64 (block .Int64 ())), nil
430
+ }
431
+ func (fb * filterBackend ) GetReceipts (ctx context.Context , hash common.Hash ) (types.Receipts , error ) {
432
+ return core .GetBlockReceipts (fb .db , hash , core .GetBlockNumber (fb .db , hash )), nil
433
+ }
434
+
435
+ func (fb * filterBackend ) SubscribeTxPreEvent (ch chan <- core.TxPreEvent ) event.Subscription {
436
+ return event .NewSubscription (func (quit <- chan struct {}) error {
437
+ <- quit
438
+ return nil
439
+ })
440
+ }
441
+ func (fb * filterBackend ) SubscribeChainEvent (ch chan <- core.ChainEvent ) event.Subscription {
442
+ return fb .bc .SubscribeChainEvent (ch )
443
+ }
444
+ func (fb * filterBackend ) SubscribeRemovedLogsEvent (ch chan <- core.RemovedLogsEvent ) event.Subscription {
445
+ return fb .bc .SubscribeRemovedLogsEvent (ch )
446
+ }
447
+ func (fb * filterBackend ) SubscribeLogsEvent (ch chan <- []* types.Log ) event.Subscription {
448
+ return fb .bc .SubscribeLogsEvent (ch )
449
+ }
450
+
451
+ func (fb * filterBackend ) BloomStatus () (uint64 , uint64 ) { return 4096 , 0 }
452
+ func (fb * filterBackend ) ServiceFilter (ctx context.Context , ms * bloombits.MatcherSession ) {
453
+ panic ("not supported" )
454
+ }
0 commit comments