@@ -46,12 +46,17 @@ import (
4646var _ bind.ContractBackend = (* SimulatedBackend )(nil )
4747
4848var (
49- errBlockNumberUnsupported = errors .New ("simulatedBackend cannot access blocks other than the latest block" )
50- errGasEstimationFailed = errors .New ("gas required exceeds allowance or always failing transaction" )
49+ errBlockNumberUnsupported = errors .New ("simulatedBackend cannot access blocks other than the latest block" )
50+ errBlockDoesNotExist = errors .New ("block does not exist in blockchain" )
51+ errTransactionDoesNotExist = errors .New ("transaction does not exist" )
52+ errGasEstimationFailed = errors .New ("gas required exceeds allowance or always failing transaction" )
5153)
5254
5355// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
5456// the background. Its main purpose is to allow easily testing contract bindings.
57+ // Simulated backend implements the following interfaces:
58+ // ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
59+ // DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
5560type SimulatedBackend struct {
5661 database ethdb.Database // In memory database to store our testing data
5762 blockchain * core.BlockChain // Ethereum blockchain to handle the consensus
@@ -173,6 +178,9 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
173178
174179// TransactionReceipt returns the receipt of a transaction.
175180func (b * SimulatedBackend ) TransactionReceipt (ctx context.Context , txHash common.Hash ) (* types.Receipt , error ) {
181+ b .mu .Lock ()
182+ defer b .mu .Unlock ()
183+
176184 receipt , _ , _ , _ := rawdb .ReadReceipt (b .database , txHash , b .config )
177185 return receipt , nil
178186}
@@ -196,6 +204,115 @@ func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.
196204 return nil , false , ethereum .NotFound
197205}
198206
207+ // BlockByHash retrieves a block based on the block hash
208+ func (b * SimulatedBackend ) BlockByHash (ctx context.Context , hash common.Hash ) (* types.Block , error ) {
209+ b .mu .Lock ()
210+ defer b .mu .Unlock ()
211+
212+ if hash == b .pendingBlock .Hash () {
213+ return b .pendingBlock , nil
214+ }
215+
216+ block := b .blockchain .GetBlockByHash (hash )
217+ if block != nil {
218+ return block , nil
219+ }
220+
221+ return nil , errBlockDoesNotExist
222+ }
223+
224+ // BlockByNumber retrieves a block from the database by number, caching it
225+ // (associated with its hash) if found.
226+ func (b * SimulatedBackend ) BlockByNumber (ctx context.Context , number * big.Int ) (* types.Block , error ) {
227+ b .mu .Lock ()
228+ defer b .mu .Unlock ()
229+
230+ if number == nil || number .Cmp (b .pendingBlock .Number ()) == 0 {
231+ return b .blockchain .CurrentBlock (), nil
232+ }
233+
234+ block := b .blockchain .GetBlockByNumber (uint64 (number .Int64 ()))
235+ if block == nil {
236+ return nil , errBlockDoesNotExist
237+ }
238+
239+ return block , nil
240+ }
241+
242+ // HeaderByHash returns a block header from the current canonical chain.
243+ func (b * SimulatedBackend ) HeaderByHash (ctx context.Context , hash common.Hash ) (* types.Header , error ) {
244+ b .mu .Lock ()
245+ defer b .mu .Unlock ()
246+
247+ if hash == b .pendingBlock .Hash () {
248+ return b .pendingBlock .Header (), nil
249+ }
250+
251+ header := b .blockchain .GetHeaderByHash (hash )
252+ if header == nil {
253+ return nil , errBlockDoesNotExist
254+ }
255+
256+ return header , nil
257+ }
258+
259+ // HeaderByNumber returns a block header from the current canonical chain. If number is
260+ // nil, the latest known header is returned.
261+ func (b * SimulatedBackend ) HeaderByNumber (ctx context.Context , block * big.Int ) (* types.Header , error ) {
262+ b .mu .Lock ()
263+ defer b .mu .Unlock ()
264+
265+ if block == nil || block .Cmp (b .pendingBlock .Number ()) == 0 {
266+ return b .blockchain .CurrentHeader (), nil
267+ }
268+
269+ return b .blockchain .GetHeaderByNumber (uint64 (block .Int64 ())), nil
270+ }
271+
272+ // TransactionCount returns the number of transactions in a given block
273+ func (b * SimulatedBackend ) TransactionCount (ctx context.Context , blockHash common.Hash ) (uint , error ) {
274+ b .mu .Lock ()
275+ defer b .mu .Unlock ()
276+
277+ if blockHash == b .pendingBlock .Hash () {
278+ return uint (b .pendingBlock .Transactions ().Len ()), nil
279+ }
280+
281+ block := b .blockchain .GetBlockByHash (blockHash )
282+ if block == nil {
283+ return uint (0 ), errBlockDoesNotExist
284+ }
285+
286+ return uint (block .Transactions ().Len ()), nil
287+ }
288+
289+ // TransactionInBlock returns the transaction for a specific block at a specific index
290+ func (b * SimulatedBackend ) TransactionInBlock (ctx context.Context , blockHash common.Hash , index uint ) (* types.Transaction , error ) {
291+ b .mu .Lock ()
292+ defer b .mu .Unlock ()
293+
294+ if blockHash == b .pendingBlock .Hash () {
295+ transactions := b .pendingBlock .Transactions ()
296+ if uint (len (transactions )) < index + 1 {
297+ return nil , errTransactionDoesNotExist
298+ }
299+
300+ return transactions [index ], nil
301+ }
302+
303+ block := b .blockchain .GetBlockByHash (blockHash )
304+ if block == nil {
305+ return nil , errBlockDoesNotExist
306+ }
307+
308+ transactions := block .Transactions ()
309+ if uint (len (transactions )) < index + 1 {
310+ return nil , errTransactionDoesNotExist
311+ }
312+
313+ return transactions [index ], nil
314+ }
315+
199316// PendingCodeAt returns the code associated with an account in the pending state.
200317func (b * SimulatedBackend ) PendingCodeAt (ctx context.Context , contract common.Address ) ([]byte , error ) {
201318 b .mu .Lock ()
@@ -419,10 +536,38 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
419536 }), nil
420537}
421538
539+ // SubscribeNewHead returns an event subscription for a new header
540+ func (b * SimulatedBackend ) SubscribeNewHead (ctx context.Context , ch chan <- * types.Header ) (ethereum.Subscription , error ) {
541+ // subscribe to a new head
542+ sink := make (chan * types.Header )
543+ sub := b .events .SubscribeNewHeads (sink )
544+
545+ return event .NewSubscription (func (quit <- chan struct {}) error {
546+ defer sub .Unsubscribe ()
547+ for {
548+ select {
549+ case head := <- sink :
550+ select {
551+ case ch <- head :
552+ case err := <- sub .Err ():
553+ return err
554+ case <- quit :
555+ return nil
556+ }
557+ case err := <- sub .Err ():
558+ return err
559+ case <- quit :
560+ return nil
561+ }
562+ }
563+ }), nil
564+ }
565+
422566// AdjustTime adds a time shift to the simulated clock.
423567func (b * SimulatedBackend ) AdjustTime (adjustment time.Duration ) error {
424568 b .mu .Lock ()
425569 defer b .mu .Unlock ()
570+
426571 blocks , _ := core .GenerateChain (b .config , b .blockchain .CurrentBlock (), ethash .NewFaker (), b .database , 1 , func (number int , block * core.BlockGen ) {
427572 for _ , tx := range b .pendingBlock .Transactions () {
428573 block .AddTx (tx )
0 commit comments