github.com/ethersphere/bee/v2@v2.2.0/pkg/settlement/swap/chequebook/chequestore_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  	"math/big"
    11  	"testing"
    12  
    13  	"github.com/ethereum/go-ethereum/common"
    14  	"github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook"
    15  	storemock "github.com/ethersphere/bee/v2/pkg/statestore/mock"
    16  	transactionmock "github.com/ethersphere/bee/v2/pkg/transaction/mock"
    17  )
    18  
    19  func TestReceiveCheque(t *testing.T) {
    20  	t.Parallel()
    21  
    22  	store := storemock.NewStateStore()
    23  	beneficiary := common.HexToAddress("0xffff")
    24  	issuer := common.HexToAddress("0xbeee")
    25  	cumulativePayout := big.NewInt(101)
    26  	cumulativePayout2 := big.NewInt(201)
    27  	chequebookAddress := common.HexToAddress("0xeeee")
    28  	sig := make([]byte, 65)
    29  	chainID := int64(1)
    30  	exchangeRate := big.NewInt(10)
    31  	deduction := big.NewInt(1)
    32  
    33  	cheque := &chequebook.SignedCheque{
    34  		Cheque: chequebook.Cheque{
    35  			Beneficiary:      beneficiary,
    36  			CumulativePayout: cumulativePayout,
    37  			Chequebook:       chequebookAddress,
    38  		},
    39  		Signature: sig,
    40  	}
    41  
    42  	var verifiedWithFactory bool
    43  	factory := &factoryMock{
    44  		verifyChequebook: func(ctx context.Context, address common.Address) error {
    45  			if address != chequebookAddress {
    46  				t.Fatal("verifying wrong chequebook")
    47  			}
    48  			verifiedWithFactory = true
    49  			return nil
    50  		},
    51  	}
    52  
    53  	chequestore := chequebook.NewChequeStore(
    54  		store,
    55  		factory,
    56  		chainID,
    57  		beneficiary,
    58  		transactionmock.New(
    59  			transactionmock.WithABICallSequence(
    60  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
    61  				transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout2.FillBytes(make([]byte, 32)), "balance"),
    62  				transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
    63  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
    64  				transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout2.FillBytes(make([]byte, 32)), "balance"),
    65  				transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
    66  			),
    67  		),
    68  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
    69  			if cid != chainID {
    70  				t.Fatalf("recovery with wrong chain id. wanted %d, got %d", chainID, cid)
    71  			}
    72  			if !cheque.Equal(c) {
    73  				t.Fatalf("recovery with wrong cheque. wanted %v, got %v", cheque, c)
    74  			}
    75  			return issuer, nil
    76  		})
    77  
    78  	received, err := chequestore.ReceiveCheque(context.Background(), cheque, exchangeRate, deduction)
    79  	if err != nil {
    80  		t.Fatal(err)
    81  	}
    82  
    83  	if !verifiedWithFactory {
    84  		t.Fatal("did not verify with factory")
    85  	}
    86  
    87  	if received.Cmp(cumulativePayout) != 0 {
    88  		t.Fatalf("calculated wrong received cumulativePayout. wanted %d, got %d", cumulativePayout, received)
    89  	}
    90  
    91  	lastCheque, err := chequestore.LastCheque(chequebookAddress)
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  
    96  	if !cheque.Equal(lastCheque) {
    97  		t.Fatalf("stored wrong cheque. wanted %v, got %v", cheque, lastCheque)
    98  	}
    99  
   100  	cheque = &chequebook.SignedCheque{
   101  		Cheque: chequebook.Cheque{
   102  			Beneficiary:      beneficiary,
   103  			CumulativePayout: cumulativePayout2,
   104  			Chequebook:       chequebookAddress,
   105  		},
   106  		Signature: sig,
   107  	}
   108  
   109  	verifiedWithFactory = false
   110  	received, err = chequestore.ReceiveCheque(context.Background(), cheque, exchangeRate, deduction)
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  
   115  	if verifiedWithFactory {
   116  		t.Fatal("needlessly verify with factory")
   117  	}
   118  
   119  	expectedReceived := big.NewInt(0).Sub(cumulativePayout2, cumulativePayout)
   120  	if received.Cmp(expectedReceived) != 0 {
   121  		t.Fatalf("calculated wrong received cumulativePayout. wanted %d, got %d", expectedReceived, received)
   122  	}
   123  }
   124  
   125  func TestReceiveChequeInvalidBeneficiary(t *testing.T) {
   126  	t.Parallel()
   127  
   128  	store := storemock.NewStateStore()
   129  	beneficiary := common.HexToAddress("0xffff")
   130  	issuer := common.HexToAddress("0xbeee")
   131  	cumulativePayout := big.NewInt(10)
   132  	chequebookAddress := common.HexToAddress("0xeeee")
   133  	sig := make([]byte, 65)
   134  	chainID := int64(1)
   135  
   136  	cheque := &chequebook.SignedCheque{
   137  		Cheque: chequebook.Cheque{
   138  			Beneficiary:      issuer,
   139  			CumulativePayout: cumulativePayout,
   140  			Chequebook:       chequebookAddress,
   141  		},
   142  		Signature: sig,
   143  	}
   144  
   145  	chequestore := chequebook.NewChequeStore(
   146  		store,
   147  		&factoryMock{},
   148  		chainID,
   149  		beneficiary,
   150  		transactionmock.New(),
   151  		nil,
   152  	)
   153  
   154  	_, err := chequestore.ReceiveCheque(context.Background(), cheque, cumulativePayout, big.NewInt(0))
   155  	if err == nil {
   156  		t.Fatal("accepted cheque with wrong beneficiary")
   157  	}
   158  	if !errors.Is(err, chequebook.ErrWrongBeneficiary) {
   159  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrWrongBeneficiary, err)
   160  	}
   161  }
   162  
   163  func TestReceiveChequeInvalidAmount(t *testing.T) {
   164  	t.Parallel()
   165  
   166  	store := storemock.NewStateStore()
   167  	beneficiary := common.HexToAddress("0xffff")
   168  	issuer := common.HexToAddress("0xbeee")
   169  	cumulativePayout := big.NewInt(10)
   170  	cumulativePayoutLower := big.NewInt(5)
   171  	chequebookAddress := common.HexToAddress("0xeeee")
   172  	sig := make([]byte, 65)
   173  	chainID := int64(1)
   174  
   175  	chequestore := chequebook.NewChequeStore(
   176  		store,
   177  		&factoryMock{
   178  			verifyChequebook: func(ctx context.Context, address common.Address) error {
   179  				return nil
   180  			},
   181  		},
   182  		chainID,
   183  		beneficiary,
   184  		transactionmock.New(
   185  			transactionmock.WithABICallSequence(
   186  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
   187  				transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
   188  				transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
   189  			),
   190  		),
   191  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
   192  			return issuer, nil
   193  		})
   194  
   195  	_, err := chequestore.ReceiveCheque(context.Background(), &chequebook.SignedCheque{
   196  		Cheque: chequebook.Cheque{
   197  			Beneficiary:      beneficiary,
   198  			CumulativePayout: cumulativePayout,
   199  			Chequebook:       chequebookAddress,
   200  		},
   201  		Signature: sig,
   202  	}, cumulativePayout, big.NewInt(0))
   203  	if err != nil {
   204  		t.Fatal(err)
   205  	}
   206  
   207  	_, err = chequestore.ReceiveCheque(context.Background(), &chequebook.SignedCheque{
   208  		Cheque: chequebook.Cheque{
   209  			Beneficiary:      beneficiary,
   210  			CumulativePayout: cumulativePayoutLower,
   211  			Chequebook:       chequebookAddress,
   212  		},
   213  		Signature: sig,
   214  	}, cumulativePayout, big.NewInt(0))
   215  	if err == nil {
   216  		t.Fatal("accepted lower amount cheque")
   217  	}
   218  	if !errors.Is(err, chequebook.ErrChequeNotIncreasing) {
   219  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrChequeNotIncreasing, err)
   220  	}
   221  }
   222  
   223  func TestReceiveChequeInvalidChequebook(t *testing.T) {
   224  	t.Parallel()
   225  
   226  	store := storemock.NewStateStore()
   227  	beneficiary := common.HexToAddress("0xffff")
   228  	issuer := common.HexToAddress("0xbeee")
   229  	cumulativePayout := big.NewInt(10)
   230  	chequebookAddress := common.HexToAddress("0xeeee")
   231  	sig := make([]byte, 65)
   232  	chainID := int64(1)
   233  
   234  	chequestore := chequebook.NewChequeStore(
   235  		store,
   236  		&factoryMock{
   237  			verifyChequebook: func(ctx context.Context, address common.Address) error {
   238  				return chequebook.ErrNotDeployedByFactory
   239  			},
   240  		},
   241  		chainID,
   242  		beneficiary,
   243  		transactionmock.New(
   244  			transactionmock.WithABICallSequence(
   245  				transactionmock.ABICall(&chequebookABI, chequebookAddress, issuer.Bytes(), "issuer"),
   246  				transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
   247  			),
   248  		),
   249  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
   250  			return issuer, nil
   251  		})
   252  
   253  	_, err := chequestore.ReceiveCheque(context.Background(), &chequebook.SignedCheque{
   254  		Cheque: chequebook.Cheque{
   255  			Beneficiary:      beneficiary,
   256  			CumulativePayout: cumulativePayout,
   257  			Chequebook:       chequebookAddress,
   258  		},
   259  		Signature: sig,
   260  	}, cumulativePayout, big.NewInt(0))
   261  	if !errors.Is(err, chequebook.ErrNotDeployedByFactory) {
   262  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrNotDeployedByFactory, err)
   263  	}
   264  }
   265  
   266  func TestReceiveChequeInvalidSignature(t *testing.T) {
   267  	t.Parallel()
   268  
   269  	store := storemock.NewStateStore()
   270  	beneficiary := common.HexToAddress("0xffff")
   271  	issuer := common.HexToAddress("0xbeee")
   272  	cumulativePayout := big.NewInt(10)
   273  	chequebookAddress := common.HexToAddress("0xeeee")
   274  	sig := make([]byte, 65)
   275  	chainID := int64(1)
   276  
   277  	chequestore := chequebook.NewChequeStore(
   278  		store,
   279  		&factoryMock{
   280  			verifyChequebook: func(ctx context.Context, address common.Address) error {
   281  				return nil
   282  			},
   283  		},
   284  		chainID,
   285  		beneficiary,
   286  		transactionmock.New(
   287  			transactionmock.WithABICallSequence(
   288  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
   289  			),
   290  		),
   291  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
   292  			return common.Address{}, nil
   293  		})
   294  
   295  	_, err := chequestore.ReceiveCheque(context.Background(), &chequebook.SignedCheque{
   296  		Cheque: chequebook.Cheque{
   297  			Beneficiary:      beneficiary,
   298  			CumulativePayout: cumulativePayout,
   299  			Chequebook:       chequebookAddress,
   300  		},
   301  		Signature: sig,
   302  	}, cumulativePayout, big.NewInt(0))
   303  	if !errors.Is(err, chequebook.ErrChequeInvalid) {
   304  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrChequeInvalid, err)
   305  	}
   306  }
   307  
   308  func TestReceiveChequeInsufficientBalance(t *testing.T) {
   309  	t.Parallel()
   310  
   311  	store := storemock.NewStateStore()
   312  	beneficiary := common.HexToAddress("0xffff")
   313  	issuer := common.HexToAddress("0xbeee")
   314  	cumulativePayout := big.NewInt(10)
   315  	chequebookAddress := common.HexToAddress("0xeeee")
   316  	sig := make([]byte, 65)
   317  	chainID := int64(1)
   318  
   319  	chequestore := chequebook.NewChequeStore(
   320  		store,
   321  		&factoryMock{
   322  			verifyChequebook: func(ctx context.Context, address common.Address) error {
   323  				return nil
   324  			},
   325  		},
   326  		chainID,
   327  		beneficiary,
   328  		transactionmock.New(
   329  			transactionmock.WithABICallSequence(
   330  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
   331  				transactionmock.ABICall(&chequebookABI, chequebookAddress, new(big.Int).Sub(cumulativePayout, big.NewInt(1)).FillBytes(make([]byte, 32)), "balance"),
   332  				transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
   333  			),
   334  		),
   335  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
   336  			return issuer, nil
   337  		})
   338  
   339  	_, err := chequestore.ReceiveCheque(context.Background(), &chequebook.SignedCheque{
   340  		Cheque: chequebook.Cheque{
   341  			Beneficiary:      beneficiary,
   342  			CumulativePayout: cumulativePayout,
   343  			Chequebook:       chequebookAddress,
   344  		},
   345  		Signature: sig,
   346  	}, cumulativePayout, big.NewInt(0))
   347  	if !errors.Is(err, chequebook.ErrBouncingCheque) {
   348  		t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrBouncingCheque, err)
   349  	}
   350  }
   351  
   352  func TestReceiveChequeSufficientBalancePaidOut(t *testing.T) {
   353  	t.Parallel()
   354  
   355  	store := storemock.NewStateStore()
   356  	beneficiary := common.HexToAddress("0xffff")
   357  	issuer := common.HexToAddress("0xbeee")
   358  	cumulativePayout := big.NewInt(10)
   359  	chequebookAddress := common.HexToAddress("0xeeee")
   360  	sig := make([]byte, 65)
   361  	chainID := int64(1)
   362  
   363  	chequestore := chequebook.NewChequeStore(
   364  		store,
   365  		&factoryMock{
   366  			verifyChequebook: func(ctx context.Context, address common.Address) error {
   367  				return nil
   368  			},
   369  		},
   370  		chainID,
   371  		beneficiary,
   372  		transactionmock.New(
   373  			transactionmock.WithABICallSequence(
   374  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
   375  				transactionmock.ABICall(&chequebookABI, chequebookAddress, new(big.Int).Sub(cumulativePayout, big.NewInt(100)).FillBytes(make([]byte, 32)), "balance"),
   376  				transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
   377  			),
   378  		),
   379  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
   380  			return issuer, nil
   381  		})
   382  
   383  	_, err := chequestore.ReceiveCheque(context.Background(), &chequebook.SignedCheque{
   384  		Cheque: chequebook.Cheque{
   385  			Beneficiary:      beneficiary,
   386  			CumulativePayout: cumulativePayout,
   387  			Chequebook:       chequebookAddress,
   388  		},
   389  		Signature: sig,
   390  	}, cumulativePayout, big.NewInt(0))
   391  	if err != nil {
   392  		t.Fatal(err)
   393  	}
   394  }
   395  
   396  func TestReceiveChequeNotEnoughValue(t *testing.T) {
   397  	t.Parallel()
   398  
   399  	store := storemock.NewStateStore()
   400  	beneficiary := common.HexToAddress("0xffff")
   401  	issuer := common.HexToAddress("0xbeee")
   402  	cumulativePayout := big.NewInt(100)
   403  	chequebookAddress := common.HexToAddress("0xeeee")
   404  	sig := make([]byte, 65)
   405  	chainID := int64(1)
   406  	exchangeRate := big.NewInt(101)
   407  	deduction := big.NewInt(0)
   408  
   409  	cheque := &chequebook.SignedCheque{
   410  		Cheque: chequebook.Cheque{
   411  			Beneficiary:      beneficiary,
   412  			CumulativePayout: cumulativePayout,
   413  			Chequebook:       chequebookAddress,
   414  		},
   415  		Signature: sig,
   416  	}
   417  
   418  	factory := &factoryMock{
   419  		verifyChequebook: func(ctx context.Context, address common.Address) error {
   420  			if address != chequebookAddress {
   421  				t.Fatal("verifying wrong chequebook")
   422  			}
   423  			return nil
   424  		},
   425  	}
   426  
   427  	chequestore := chequebook.NewChequeStore(
   428  		store,
   429  		factory,
   430  		chainID,
   431  		beneficiary,
   432  		transactionmock.New(
   433  			transactionmock.WithABICallSequence(
   434  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
   435  				transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
   436  				transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
   437  			),
   438  		),
   439  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
   440  			if cid != chainID {
   441  				t.Fatalf("recovery with wrong chain id. wanted %d, got %d", chainID, cid)
   442  			}
   443  			if !cheque.Equal(c) {
   444  				t.Fatalf("recovery with wrong cheque. wanted %v, got %v", cheque, c)
   445  			}
   446  			return issuer, nil
   447  		})
   448  
   449  	_, err := chequestore.ReceiveCheque(context.Background(), cheque, exchangeRate, deduction)
   450  	if !errors.Is(err, chequebook.ErrChequeValueTooLow) {
   451  		t.Fatalf("got wrong error. wanted %v, got %v", chequebook.ErrChequeValueTooLow, err)
   452  	}
   453  }
   454  
   455  func TestReceiveChequeNotEnoughValueAfterDeduction(t *testing.T) {
   456  	t.Parallel()
   457  
   458  	store := storemock.NewStateStore()
   459  	beneficiary := common.HexToAddress("0xffff")
   460  	issuer := common.HexToAddress("0xbeee")
   461  	cumulativePayout := big.NewInt(100)
   462  	chequebookAddress := common.HexToAddress("0xeeee")
   463  	sig := make([]byte, 65)
   464  	chainID := int64(1)
   465  
   466  	// cheque needs to cover initial deduction (if applicable) plus one times the exchange rate
   467  	// in order to amount to at least 1 accounting credit and be accepted
   468  	// in this test cheque amount is just not enough to cover that therefore we expect
   469  
   470  	exchangeRate := big.NewInt(100)
   471  	deduction := big.NewInt(1)
   472  
   473  	cheque := &chequebook.SignedCheque{
   474  		Cheque: chequebook.Cheque{
   475  			Beneficiary:      beneficiary,
   476  			CumulativePayout: cumulativePayout,
   477  			Chequebook:       chequebookAddress,
   478  		},
   479  		Signature: sig,
   480  	}
   481  
   482  	factory := &factoryMock{
   483  		verifyChequebook: func(ctx context.Context, address common.Address) error {
   484  			if address != chequebookAddress {
   485  				t.Fatal("verifying wrong chequebook")
   486  			}
   487  			return nil
   488  		},
   489  	}
   490  
   491  	chequestore := chequebook.NewChequeStore(
   492  		store,
   493  		factory,
   494  		chainID,
   495  		beneficiary,
   496  		transactionmock.New(
   497  			transactionmock.WithABICallSequence(
   498  				transactionmock.ABICall(&chequebookABI, chequebookAddress, common.BytesToHash(issuer.Bytes()).Bytes(), "issuer"),
   499  				transactionmock.ABICall(&chequebookABI, chequebookAddress, cumulativePayout.FillBytes(make([]byte, 32)), "balance"),
   500  				transactionmock.ABICall(&chequebookABI, chequebookAddress, big.NewInt(0).FillBytes(make([]byte, 32)), "paidOut", beneficiary),
   501  			),
   502  		),
   503  		func(c *chequebook.SignedCheque, cid int64) (common.Address, error) {
   504  			if cid != chainID {
   505  				t.Fatalf("recovery with wrong chain id. wanted %d, got %d", chainID, cid)
   506  			}
   507  			if !cheque.Equal(c) {
   508  				t.Fatalf("recovery with wrong cheque. wanted %v, got %v", cheque, c)
   509  			}
   510  			return issuer, nil
   511  		})
   512  
   513  	_, err := chequestore.ReceiveCheque(context.Background(), cheque, exchangeRate, deduction)
   514  	if !errors.Is(err, chequebook.ErrChequeValueTooLow) {
   515  		t.Fatalf("got wrong error. wanted %v, got %v", chequebook.ErrChequeValueTooLow, err)
   516  	}
   517  }