github.com/ethersphere/bee/v2@v2.2.0/pkg/transaction/mock/transaction.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mock
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"math/big"
    13  
    14  	"github.com/ethereum/go-ethereum/accounts/abi"
    15  	"github.com/ethereum/go-ethereum/common"
    16  	"github.com/ethereum/go-ethereum/core/types"
    17  	"github.com/ethersphere/bee/v2/pkg/transaction"
    18  )
    19  
    20  type transactionServiceMock struct {
    21  	send                 func(ctx context.Context, request *transaction.TxRequest, boost int) (txHash common.Hash, err error)
    22  	waitForReceipt       func(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error)
    23  	watchSentTransaction func(txHash common.Hash) (chan types.Receipt, chan error, error)
    24  	call                 func(ctx context.Context, request *transaction.TxRequest) (result []byte, err error)
    25  	pendingTransactions  func() ([]common.Hash, error)
    26  	resendTransaction    func(ctx context.Context, txHash common.Hash) error
    27  	storedTransaction    func(txHash common.Hash) (*transaction.StoredTransaction, error)
    28  	cancelTransaction    func(ctx context.Context, originalTxHash common.Hash) (common.Hash, error)
    29  	transactionFee       func(ctx context.Context, txHash common.Hash) (*big.Int, error)
    30  }
    31  
    32  func (m *transactionServiceMock) Send(ctx context.Context, request *transaction.TxRequest, boostPercent int) (txHash common.Hash, err error) {
    33  	if m.send != nil {
    34  		return m.send(ctx, request, boostPercent)
    35  	}
    36  	return common.Hash{}, errors.New("not implemented")
    37  }
    38  
    39  func (m *transactionServiceMock) WaitForReceipt(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error) {
    40  	if m.waitForReceipt != nil {
    41  		return m.waitForReceipt(ctx, txHash)
    42  	}
    43  	return nil, errors.New("not implemented")
    44  }
    45  
    46  func (m *transactionServiceMock) WatchSentTransaction(txHash common.Hash) (<-chan types.Receipt, <-chan error, error) {
    47  	if m.watchSentTransaction != nil {
    48  		return m.watchSentTransaction(txHash)
    49  	}
    50  	return nil, nil, errors.New("not implemented")
    51  }
    52  
    53  func (m *transactionServiceMock) Call(ctx context.Context, request *transaction.TxRequest) (result []byte, err error) {
    54  	if m.call != nil {
    55  		return m.call(ctx, request)
    56  	}
    57  	return nil, errors.New("not implemented")
    58  }
    59  
    60  func (m *transactionServiceMock) PendingTransactions() ([]common.Hash, error) {
    61  	if m.pendingTransactions != nil {
    62  		return m.pendingTransactions()
    63  	}
    64  	return nil, errors.New("not implemented")
    65  }
    66  
    67  func (m *transactionServiceMock) ResendTransaction(ctx context.Context, txHash common.Hash) error {
    68  	if m.resendTransaction != nil {
    69  		return m.resendTransaction(ctx, txHash)
    70  	}
    71  	return errors.New("not implemented")
    72  }
    73  
    74  func (m *transactionServiceMock) StoredTransaction(txHash common.Hash) (*transaction.StoredTransaction, error) {
    75  	if m.storedTransaction != nil {
    76  		return m.storedTransaction(txHash)
    77  	}
    78  	return nil, errors.New("not implemented")
    79  }
    80  
    81  func (m *transactionServiceMock) CancelTransaction(ctx context.Context, originalTxHash common.Hash) (common.Hash, error) {
    82  	if m.cancelTransaction != nil {
    83  		return m.cancelTransaction(ctx, originalTxHash)
    84  	}
    85  	return common.Hash{}, errors.New("not implemented")
    86  }
    87  
    88  func (m *transactionServiceMock) Close() error {
    89  	return nil
    90  }
    91  
    92  // TransactionFee returns fee of transaction
    93  func (m *transactionServiceMock) TransactionFee(ctx context.Context, txHash common.Hash) (*big.Int, error) {
    94  	if m.transactionFee != nil {
    95  		return m.transactionFee(ctx, txHash)
    96  	}
    97  	return big.NewInt(0), nil
    98  }
    99  
   100  func (m *transactionServiceMock) UnwrapABIError(_ context.Context, _ *transaction.TxRequest, err error, _ map[string]abi.Error) error {
   101  	return err
   102  }
   103  
   104  // Option is the option passed to the mock Chequebook service
   105  type Option interface {
   106  	apply(*transactionServiceMock)
   107  }
   108  
   109  type optionFunc func(*transactionServiceMock)
   110  
   111  func (f optionFunc) apply(r *transactionServiceMock) { f(r) }
   112  
   113  func WithSendFunc(f func(context.Context, *transaction.TxRequest, int) (txHash common.Hash, err error)) Option {
   114  	return optionFunc(func(s *transactionServiceMock) {
   115  		s.send = f
   116  	})
   117  }
   118  
   119  func WithWaitForReceiptFunc(f func(ctx context.Context, txHash common.Hash) (receipt *types.Receipt, err error)) Option {
   120  	return optionFunc(func(s *transactionServiceMock) {
   121  		s.waitForReceipt = f
   122  	})
   123  }
   124  
   125  func WithCallFunc(f func(ctx context.Context, request *transaction.TxRequest) (result []byte, err error)) Option {
   126  	return optionFunc(func(s *transactionServiceMock) {
   127  		s.call = f
   128  	})
   129  }
   130  
   131  func WithStoredTransactionFunc(f func(txHash common.Hash) (*transaction.StoredTransaction, error)) Option {
   132  	return optionFunc(func(s *transactionServiceMock) {
   133  		s.storedTransaction = f
   134  	})
   135  }
   136  
   137  func WithPendingTransactionsFunc(f func() ([]common.Hash, error)) Option {
   138  	return optionFunc(func(s *transactionServiceMock) {
   139  		s.pendingTransactions = f
   140  	})
   141  }
   142  
   143  func WithResendTransactionFunc(f func(ctx context.Context, txHash common.Hash) error) Option {
   144  	return optionFunc(func(s *transactionServiceMock) {
   145  		s.resendTransaction = f
   146  	})
   147  }
   148  
   149  func WithCancelTransactionFunc(f func(ctx context.Context, originalTxHash common.Hash) (common.Hash, error)) Option {
   150  	return optionFunc(func(s *transactionServiceMock) {
   151  		s.cancelTransaction = f
   152  	})
   153  }
   154  
   155  func WithTransactionFeeFunc(f func(ctx context.Context, txHash common.Hash) (*big.Int, error)) Option {
   156  	return optionFunc(func(s *transactionServiceMock) {
   157  		s.transactionFee = f
   158  	})
   159  }
   160  
   161  func New(opts ...Option) transaction.Service {
   162  	mock := new(transactionServiceMock)
   163  	for _, o := range opts {
   164  		o.apply(mock)
   165  	}
   166  	return mock
   167  }
   168  
   169  type Call struct {
   170  	abi    *abi.ABI
   171  	to     common.Address
   172  	result []byte
   173  	method string
   174  	params []interface{}
   175  }
   176  
   177  func ABICall(abi *abi.ABI, to common.Address, result []byte, method string, params ...interface{}) Call {
   178  	return Call{
   179  		to:     to,
   180  		abi:    abi,
   181  		result: result,
   182  		method: method,
   183  		params: params,
   184  	}
   185  }
   186  
   187  func WithABICallSequence(calls ...Call) Option {
   188  	return optionFunc(func(s *transactionServiceMock) {
   189  		s.call = func(ctx context.Context, request *transaction.TxRequest) ([]byte, error) {
   190  			if len(calls) == 0 {
   191  				return nil, errors.New("unexpected call")
   192  			}
   193  
   194  			call := calls[0]
   195  
   196  			data, err := call.abi.Pack(call.method, call.params...)
   197  			if err != nil {
   198  				return nil, err
   199  			}
   200  
   201  			if !bytes.Equal(data, request.Data) {
   202  				return nil, fmt.Errorf("wrong data. wanted %x, got %x", data, request.Data)
   203  			}
   204  
   205  			if request.To == nil {
   206  				return nil, errors.New("call with no recipient")
   207  			}
   208  			if *request.To != call.to {
   209  				return nil, fmt.Errorf("wrong recipient. wanted %x, got %x", call.to, *request.To)
   210  			}
   211  
   212  			calls = calls[1:]
   213  
   214  			return call.result, nil
   215  		}
   216  	})
   217  }
   218  
   219  func WithABICall(abi *abi.ABI, to common.Address, result []byte, method string, params ...interface{}) Option {
   220  	return WithABICallSequence(ABICall(abi, to, result, method, params...))
   221  }
   222  
   223  func WithABISend(abi *abi.ABI, txHash common.Hash, expectedAddress common.Address, expectedValue *big.Int, method string, params ...interface{}) Option {
   224  	return optionFunc(func(s *transactionServiceMock) {
   225  		s.send = func(ctx context.Context, request *transaction.TxRequest, boost int) (common.Hash, error) {
   226  			data, err := abi.Pack(method, params...)
   227  			if err != nil {
   228  				return common.Hash{}, err
   229  			}
   230  
   231  			if !bytes.Equal(data, request.Data) {
   232  				return common.Hash{}, fmt.Errorf("wrong data. wanted %x, got %x", data, request.Data)
   233  			}
   234  
   235  			if request.To != nil && *request.To != expectedAddress {
   236  				return common.Hash{}, fmt.Errorf("sending to wrong contract. wanted %x, got %x", expectedAddress, request.To)
   237  			}
   238  			if request.Value.Cmp(expectedValue) != 0 {
   239  				return common.Hash{}, fmt.Errorf("sending with wrong value. wanted %d, got %d", expectedValue, request.Value)
   240  			}
   241  
   242  			return txHash, nil
   243  		}
   244  	})
   245  }