github.com/ethersphere/bee/v2@v2.2.0/pkg/settlement/swap/chequebook/chequebook_test.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 chequebook_test
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"fmt"
    11  	"math/big"
    12  	"testing"
    13  
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/core/types"
    16  	"github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook"
    17  	erc20mock "github.com/ethersphere/bee/v2/pkg/settlement/swap/erc20/mock"
    18  	storemock "github.com/ethersphere/bee/v2/pkg/statestore/mock"
    19  	"github.com/ethersphere/bee/v2/pkg/transaction"
    20  	transactionmock "github.com/ethersphere/bee/v2/pkg/transaction/mock"
    21  )
    22  
    23  func TestChequebookAddress(t *testing.T) {
    24  	t.Parallel()
    25  
    26  	address := common.HexToAddress("0xabcd")
    27  	ownerAdress := common.HexToAddress("0xfff")
    28  	chequebookService, err := chequebook.New(
    29  		transactionmock.New(),
    30  		address,
    31  		ownerAdress,
    32  		nil,
    33  		&chequeSignerMock{},
    34  		erc20mock.New(),
    35  	)
    36  	if err != nil {
    37  		t.Fatal(err)
    38  	}
    39  
    40  	if chequebookService.Address() != address {
    41  		t.Fatalf("returned wrong address. wanted %x, got %x", address, chequebookService.Address())
    42  	}
    43  }
    44  
    45  func TestChequebookBalance(t *testing.T) {
    46  	t.Parallel()
    47  
    48  	address := common.HexToAddress("0xabcd")
    49  	ownerAdress := common.HexToAddress("0xfff")
    50  	balance := big.NewInt(10)
    51  	chequebookService, err := chequebook.New(
    52  		transactionmock.New(
    53  			transactionmock.WithABICall(&chequebookABI, address, balance.FillBytes(make([]byte, 32)), "balance"),
    54  		),
    55  		address,
    56  		ownerAdress,
    57  		nil,
    58  		&chequeSignerMock{},
    59  		erc20mock.New(),
    60  	)
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	returnedBalance, err := chequebookService.Balance(context.Background())
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  
    70  	if returnedBalance.Cmp(balance) != 0 {
    71  		t.Fatalf("returned wrong balance. wanted %d, got %d", balance, returnedBalance)
    72  	}
    73  }
    74  
    75  func TestChequebookDeposit(t *testing.T) {
    76  	t.Parallel()
    77  
    78  	address := common.HexToAddress("0xabcd")
    79  	ownerAdress := common.HexToAddress("0xfff")
    80  	balance := big.NewInt(30)
    81  	depositAmount := big.NewInt(20)
    82  	txHash := common.HexToHash("0xdddd")
    83  	chequebookService, err := chequebook.New(
    84  		transactionmock.New(),
    85  		address,
    86  		ownerAdress,
    87  		nil,
    88  		&chequeSignerMock{},
    89  		erc20mock.New(
    90  			erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) {
    91  				if address != ownerAdress {
    92  					return nil, errors.New("getting balance of wrong address")
    93  				}
    94  				return balance, nil
    95  			}),
    96  			erc20mock.WithTransferFunc(func(ctx context.Context, to common.Address, value *big.Int) (common.Hash, error) {
    97  				if to != address {
    98  					return common.Hash{}, fmt.Errorf("sending to wrong address. wanted %x, got %x", address, to)
    99  				}
   100  				if depositAmount.Cmp(value) != 0 {
   101  					return common.Hash{}, fmt.Errorf("sending wrong value. wanted %d, got %d", depositAmount, value)
   102  				}
   103  				return txHash, nil
   104  			}),
   105  		),
   106  	)
   107  	if err != nil {
   108  		t.Fatal(err)
   109  	}
   110  
   111  	returnedTxHash, err := chequebookService.Deposit(context.Background(), depositAmount)
   112  	if err != nil {
   113  		t.Fatal(err)
   114  	}
   115  
   116  	if txHash != returnedTxHash {
   117  		t.Fatalf("returned wrong transaction hash. wanted %v, got %v", txHash, returnedTxHash)
   118  	}
   119  }
   120  
   121  func TestChequebookWaitForDeposit(t *testing.T) {
   122  	t.Parallel()
   123  
   124  	address := common.HexToAddress("0xabcd")
   125  	ownerAdress := common.HexToAddress("0xfff")
   126  	txHash := common.HexToHash("0xdddd")
   127  	chequebookService, err := chequebook.New(
   128  		transactionmock.New(
   129  			transactionmock.WithWaitForReceiptFunc(func(ctx context.Context, tx common.Hash) (*types.Receipt, error) {
   130  				if tx != txHash {
   131  					t.Fatalf("waiting for wrong transaction. wanted %x, got %x", txHash, tx)
   132  				}
   133  				return &types.Receipt{
   134  					Status: 1,
   135  				}, nil
   136  			}),
   137  		),
   138  		address,
   139  		ownerAdress,
   140  		nil,
   141  		&chequeSignerMock{},
   142  		erc20mock.New(),
   143  	)
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  
   148  	err = chequebookService.WaitForDeposit(context.Background(), txHash)
   149  	if err != nil {
   150  		t.Fatal(err)
   151  	}
   152  }
   153  
   154  func TestChequebookWaitForDepositReverted(t *testing.T) {
   155  	t.Parallel()
   156  
   157  	address := common.HexToAddress("0xabcd")
   158  	ownerAdress := common.HexToAddress("0xfff")
   159  	txHash := common.HexToHash("0xdddd")
   160  	chequebookService, err := chequebook.New(
   161  		transactionmock.New(
   162  			transactionmock.WithWaitForReceiptFunc(func(ctx context.Context, tx common.Hash) (*types.Receipt, error) {
   163  				if tx != txHash {
   164  					t.Fatalf("waiting for wrong transaction. wanted %x, got %x", txHash, tx)
   165  				}
   166  				return &types.Receipt{
   167  					Status: 0,
   168  				}, nil
   169  			}),
   170  		),
   171  		address,
   172  		ownerAdress,
   173  		nil,
   174  		&chequeSignerMock{},
   175  		erc20mock.New(),
   176  	)
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	err = chequebookService.WaitForDeposit(context.Background(), txHash)
   182  	if err == nil {
   183  		t.Fatal("expected reverted error")
   184  	}
   185  	if !errors.Is(err, transaction.ErrTransactionReverted) {
   186  		t.Fatalf("wrong error. wanted %v, got %v", transaction.ErrTransactionReverted, err)
   187  	}
   188  }
   189  
   190  func TestChequebookIssue(t *testing.T) {
   191  	t.Parallel()
   192  
   193  	address := common.HexToAddress("0xabcd")
   194  	beneficiary := common.HexToAddress("0xdddd")
   195  	ownerAdress := common.HexToAddress("0xfff")
   196  	store := storemock.NewStateStore()
   197  	amount := big.NewInt(20)
   198  	amount2 := big.NewInt(30)
   199  	expectedCumulative := big.NewInt(50)
   200  	sig := common.Hex2Bytes("0xffff")
   201  	chequeSigner := &chequeSignerMock{}
   202  
   203  	chequebookService, err := chequebook.New(
   204  		transactionmock.New(
   205  			transactionmock.WithABICallSequence(
   206  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
   207  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
   208  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
   209  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
   210  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(100).FillBytes(make([]byte, 32)), "balance"),
   211  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
   212  			),
   213  		),
   214  		address,
   215  		ownerAdress,
   216  		store,
   217  		chequeSigner,
   218  		erc20mock.New(),
   219  	)
   220  	if err != nil {
   221  		t.Fatal(err)
   222  	}
   223  
   224  	// issue a cheque
   225  	expectedCheque := &chequebook.SignedCheque{
   226  		Cheque: chequebook.Cheque{
   227  			Beneficiary:      beneficiary,
   228  			CumulativePayout: amount,
   229  			Chequebook:       address,
   230  		},
   231  		Signature: sig,
   232  	}
   233  
   234  	chequeSigner.sign = func(cheque *chequebook.Cheque) ([]byte, error) {
   235  		if !cheque.Equal(&expectedCheque.Cheque) {
   236  			t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque.Cheque, cheque)
   237  		}
   238  		return sig, nil
   239  	}
   240  
   241  	_, err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error {
   242  		if !cheque.Equal(expectedCheque) {
   243  			t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
   244  		}
   245  		return nil
   246  	})
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	lastCheque, err := chequebookService.LastCheque(beneficiary)
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  
   256  	if !lastCheque.Equal(expectedCheque) {
   257  		t.Fatalf("wrong cheque stored. wanted %v got %v", expectedCheque, lastCheque)
   258  	}
   259  
   260  	// issue another cheque for the same beneficiary
   261  	expectedCheque = &chequebook.SignedCheque{
   262  		Cheque: chequebook.Cheque{
   263  			Beneficiary:      beneficiary,
   264  			CumulativePayout: expectedCumulative,
   265  			Chequebook:       address,
   266  		},
   267  		Signature: sig,
   268  	}
   269  
   270  	chequeSigner.sign = func(cheque *chequebook.Cheque) ([]byte, error) {
   271  		if !cheque.Equal(&expectedCheque.Cheque) {
   272  			t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
   273  		}
   274  		return sig, nil
   275  	}
   276  
   277  	_, err = chequebookService.Issue(context.Background(), beneficiary, amount2, func(cheque *chequebook.SignedCheque) error {
   278  		if !cheque.Equal(expectedCheque) {
   279  			t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
   280  		}
   281  		return nil
   282  	})
   283  	if err != nil {
   284  		t.Fatal(err)
   285  	}
   286  
   287  	lastCheque, err = chequebookService.LastCheque(beneficiary)
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  
   292  	if !lastCheque.Equal(expectedCheque) {
   293  		t.Fatalf("wrong cheque stored. wanted %v got %v", expectedCheque, lastCheque)
   294  	}
   295  
   296  	// issue another cheque for the different beneficiary
   297  	expectedChequeOwner := &chequebook.SignedCheque{
   298  		Cheque: chequebook.Cheque{
   299  			Beneficiary:      ownerAdress,
   300  			CumulativePayout: amount,
   301  			Chequebook:       address,
   302  		},
   303  		Signature: sig,
   304  	}
   305  
   306  	chequeSigner.sign = func(cheque *chequebook.Cheque) ([]byte, error) {
   307  		if !cheque.Equal(&expectedChequeOwner.Cheque) {
   308  			t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
   309  		}
   310  		return sig, nil
   311  	}
   312  
   313  	_, err = chequebookService.Issue(context.Background(), ownerAdress, amount, func(cheque *chequebook.SignedCheque) error {
   314  		if !cheque.Equal(expectedChequeOwner) {
   315  			t.Fatalf("wrong cheque. wanted %v got %v", expectedChequeOwner, cheque)
   316  		}
   317  		return nil
   318  	})
   319  	if err != nil {
   320  		t.Fatal(err)
   321  	}
   322  
   323  	lastCheque, err = chequebookService.LastCheque(ownerAdress)
   324  	if err != nil {
   325  		t.Fatal(err)
   326  	}
   327  
   328  	if !lastCheque.Equal(expectedChequeOwner) {
   329  		t.Fatalf("wrong cheque stored. wanted %v got %v", expectedChequeOwner, lastCheque)
   330  	}
   331  
   332  	// finally check this did not interfere with the beneficiary cheque
   333  	lastCheque, err = chequebookService.LastCheque(beneficiary)
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  
   338  	if !lastCheque.Equal(expectedCheque) {
   339  		t.Fatalf("wrong cheque stored. wanted %v got %v", expectedCheque, lastCheque)
   340  	}
   341  }
   342  
   343  func TestChequebookIssueErrorSend(t *testing.T) {
   344  	t.Parallel()
   345  
   346  	address := common.HexToAddress("0xabcd")
   347  	beneficiary := common.HexToAddress("0xdddd")
   348  	ownerAdress := common.HexToAddress("0xfff")
   349  	store := storemock.NewStateStore()
   350  	amount := big.NewInt(20)
   351  	sig := common.Hex2Bytes("0xffff")
   352  	chequeSigner := &chequeSignerMock{}
   353  
   354  	chequebookService, err := chequebook.New(
   355  		transactionmock.New(),
   356  		address,
   357  		ownerAdress,
   358  		store,
   359  		chequeSigner,
   360  		erc20mock.New(),
   361  	)
   362  	if err != nil {
   363  		t.Fatal(err)
   364  	}
   365  
   366  	chequeSigner.sign = func(cheque *chequebook.Cheque) ([]byte, error) {
   367  		return sig, nil
   368  	}
   369  
   370  	_, err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error {
   371  		return errors.New("err")
   372  	})
   373  	if err == nil {
   374  		t.Fatal("expected error")
   375  	}
   376  
   377  	// verify the cheque was not saved
   378  	_, err = chequebookService.LastCheque(beneficiary)
   379  	if !errors.Is(err, chequebook.ErrNoCheque) {
   380  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrNoCheque, err)
   381  	}
   382  }
   383  
   384  func TestChequebookIssueOutOfFunds(t *testing.T) {
   385  	t.Parallel()
   386  
   387  	address := common.HexToAddress("0xabcd")
   388  	beneficiary := common.HexToAddress("0xdddd")
   389  	ownerAdress := common.HexToAddress("0xfff")
   390  	store := storemock.NewStateStore()
   391  	amount := big.NewInt(20)
   392  
   393  	chequebookService, err := chequebook.New(
   394  		transactionmock.New(
   395  			transactionmock.WithABICallSequence(
   396  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "balance"),
   397  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
   398  			),
   399  		),
   400  		address,
   401  		ownerAdress,
   402  		store,
   403  		&chequeSignerMock{},
   404  		erc20mock.New(),
   405  	)
   406  	if err != nil {
   407  		t.Fatal(err)
   408  	}
   409  
   410  	_, err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error {
   411  		return nil
   412  	})
   413  	if !errors.Is(err, chequebook.ErrOutOfFunds) {
   414  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrOutOfFunds, err)
   415  	}
   416  
   417  	// verify the cheque was not saved
   418  	_, err = chequebookService.LastCheque(beneficiary)
   419  
   420  	if !errors.Is(err, chequebook.ErrNoCheque) {
   421  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrNoCheque, err)
   422  	}
   423  }
   424  
   425  func TestChequebookWithdraw(t *testing.T) {
   426  	t.Parallel()
   427  
   428  	address := common.HexToAddress("0xabcd")
   429  	ownerAdress := common.HexToAddress("0xfff")
   430  	balance := big.NewInt(30)
   431  	withdrawAmount := big.NewInt(20)
   432  	txHash := common.HexToHash("0xdddd")
   433  	store := storemock.NewStateStore()
   434  	chequebookService, err := chequebook.New(
   435  		transactionmock.New(
   436  			transactionmock.WithABICallSequence(
   437  				transactionmock.ABICall(&chequebookABI, address, balance.FillBytes(make([]byte, 32)), "balance"),
   438  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
   439  			),
   440  			transactionmock.WithABISend(&chequebookABI, txHash, address, big.NewInt(0), "withdraw", withdrawAmount),
   441  		),
   442  		address,
   443  		ownerAdress,
   444  		store,
   445  		&chequeSignerMock{},
   446  		erc20mock.New(),
   447  	)
   448  	if err != nil {
   449  		t.Fatal(err)
   450  	}
   451  
   452  	returnedTxHash, err := chequebookService.Withdraw(context.Background(), withdrawAmount)
   453  	if err != nil {
   454  		t.Fatal(err)
   455  	}
   456  
   457  	if txHash != returnedTxHash {
   458  		t.Fatalf("returned wrong transaction hash. wanted %v, got %v", txHash, returnedTxHash)
   459  	}
   460  }
   461  
   462  func TestChequebookWithdrawInsufficientFunds(t *testing.T) {
   463  	t.Parallel()
   464  
   465  	address := common.HexToAddress("0xabcd")
   466  	ownerAdress := common.HexToAddress("0xfff")
   467  	withdrawAmount := big.NewInt(20)
   468  	txHash := common.HexToHash("0xdddd")
   469  	store := storemock.NewStateStore()
   470  	chequebookService, err := chequebook.New(
   471  		transactionmock.New(
   472  			transactionmock.WithABISend(&chequebookABI, txHash, address, big.NewInt(0), "withdraw", withdrawAmount),
   473  			transactionmock.WithABICallSequence(
   474  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "balance"),
   475  				transactionmock.ABICall(&chequebookABI, address, big.NewInt(0).FillBytes(make([]byte, 32)), "totalPaidOut"),
   476  			),
   477  		),
   478  		address,
   479  		ownerAdress,
   480  		store,
   481  		&chequeSignerMock{},
   482  		erc20mock.New(),
   483  	)
   484  	if err != nil {
   485  		t.Fatal(err)
   486  	}
   487  
   488  	_, err = chequebookService.Withdraw(context.Background(), withdrawAmount)
   489  	if !errors.Is(err, chequebook.ErrInsufficientFunds) {
   490  		t.Fatalf("got wrong error. wanted %v, got %v", chequebook.ErrInsufficientFunds, err)
   491  	}
   492  }
   493  
   494  func TestStateStoreKeys(t *testing.T) {
   495  	t.Parallel()
   496  
   497  	address := common.HexToAddress("0xabcd")
   498  
   499  	expected := "swap_cashout_000000000000000000000000000000000000abcd"
   500  	if chequebook.CashoutActionKey(address) != expected {
   501  		t.Fatalf("wrong cashout action key. wanted %s, got %s", expected, chequebook.CashoutActionKey(address))
   502  	}
   503  
   504  	expected = "swap_chequebook_last_issued_cheque_000000000000000000000000000000000000abcd"
   505  	if chequebook.LastIssuedChequeKey(address) != expected {
   506  		t.Fatalf("wrong last issued cheque key. wanted %s, got %s", expected, chequebook.LastIssuedChequeKey(address))
   507  	}
   508  
   509  	expected = "swap_chequebook_last_received_cheque__000000000000000000000000000000000000abcd"
   510  	if chequebook.LastReceivedChequeKey(address) != expected {
   511  		t.Fatalf("wrong last received cheque key. wanted %s, got %s", expected, chequebook.LastReceivedChequeKey(address))
   512  	}
   513  }