Commit 5b44ec7f authored by Ralph Pichler's avatar Ralph Pichler Committed by GitHub

check available balance when issuing cheques (#739)

parent 04075a19
...@@ -16,7 +16,8 @@ var ( ...@@ -16,7 +16,8 @@ var (
) )
type chequebookBalanceResponse struct { type chequebookBalanceResponse struct {
Balance *big.Int `json:"balance"` TotalBalance *big.Int `json:"totalBalance"`
AvailableBalance *big.Int `json:"availableBalance"`
} }
type chequebookAddressResponse struct { type chequebookAddressResponse struct {
...@@ -32,7 +33,15 @@ func (s *server) chequebookBalanceHandler(w http.ResponseWriter, r *http.Request ...@@ -32,7 +33,15 @@ func (s *server) chequebookBalanceHandler(w http.ResponseWriter, r *http.Request
return return
} }
jsonhttp.OK(w, chequebookBalanceResponse{Balance: balance}) availableBalance, err := s.Chequebook.AvailableBalance(r.Context())
if err != nil {
jsonhttp.InternalServerError(w, errChequebookBalance)
s.Logger.Debugf("debug api: chequebook availableBalance: %v", err)
s.Logger.Error("debug api: cannot get chequebook availableBalance")
return
}
jsonhttp.OK(w, chequebookBalanceResponse{TotalBalance: balance, AvailableBalance: availableBalance})
} }
func (s *server) chequebookAddressHandler(w http.ResponseWriter, r *http.Request) { func (s *server) chequebookAddressHandler(w http.ResponseWriter, r *http.Request) {
......
...@@ -21,18 +21,26 @@ import ( ...@@ -21,18 +21,26 @@ import (
func TestChequebookBalance(t *testing.T) { func TestChequebookBalance(t *testing.T) {
returnedBalance := big.NewInt(9000) returnedBalance := big.NewInt(9000)
returnedAvailableBalance := big.NewInt(1000)
chequebookBalanceFunc := func(context.Context) (ret *big.Int, err error) { chequebookBalanceFunc := func(context.Context) (ret *big.Int, err error) {
return returnedBalance, nil return returnedBalance, nil
} }
chequebookAvailableBalanceFunc := func(context.Context) (ret *big.Int, err error) {
return returnedAvailableBalance, nil
}
testServer := newTestServer(t, testServerOptions{ testServer := newTestServer(t, testServerOptions{
ChequebookOpts: []mock.Option{mock.WithChequebookBalanceFunc(chequebookBalanceFunc)}, ChequebookOpts: []mock.Option{
mock.WithChequebookBalanceFunc(chequebookBalanceFunc),
mock.WithChequebookAvailableBalanceFunc(chequebookAvailableBalanceFunc),
},
}) })
expected := &debugapi.ChequebookBalanceResponse{ expected := &debugapi.ChequebookBalanceResponse{
Balance: returnedBalance, TotalBalance: returnedBalance,
AvailableBalance: returnedAvailableBalance,
} }
// We expect a list of items unordered by peer: // We expect a list of items unordered by peer:
var got *debugapi.ChequebookBalanceResponse var got *debugapi.ChequebookBalanceResponse
...@@ -64,6 +72,30 @@ func TestChequebookBalanceError(t *testing.T) { ...@@ -64,6 +72,30 @@ func TestChequebookBalanceError(t *testing.T) {
) )
} }
func TestChequebookAvailableBalanceError(t *testing.T) {
chequebookBalanceFunc := func(context.Context) (ret *big.Int, err error) {
return big.NewInt(0), nil
}
chequebookAvailableBalanceFunc := func(context.Context) (ret *big.Int, err error) {
return nil, errors.New("New errors")
}
testServer := newTestServer(t, testServerOptions{
ChequebookOpts: []mock.Option{
mock.WithChequebookBalanceFunc(chequebookBalanceFunc),
mock.WithChequebookAvailableBalanceFunc(chequebookAvailableBalanceFunc),
},
})
jsonhttptest.Request(t, testServer.Client, http.MethodGet, "/chequebook/balance", http.StatusInternalServerError,
jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
Message: debugapi.ErrChequebookBalance,
Code: http.StatusInternalServerError,
}),
)
}
func TestChequebookAddress(t *testing.T) { func TestChequebookAddress(t *testing.T) {
chequebookAddressFunc := func() common.Address { chequebookAddressFunc := func() common.Address {
return common.HexToAddress("0xfffff") return common.HexToAddress("0xfffff")
......
...@@ -17,6 +17,7 @@ import ( ...@@ -17,6 +17,7 @@ import (
type SimpleSwapBinding interface { type SimpleSwapBinding interface {
Balance(*bind.CallOpts) (*big.Int, error) Balance(*bind.CallOpts) (*big.Int, error)
Issuer(*bind.CallOpts) (common.Address, error) Issuer(*bind.CallOpts) (common.Address, error)
TotalPaidOut(*bind.CallOpts) (*big.Int, error)
} }
type SimpleSwapBindingFunc = func(common.Address, bind.ContractBackend) (SimpleSwapBinding, error) type SimpleSwapBindingFunc = func(common.Address, bind.ContractBackend) (SimpleSwapBinding, error)
......
...@@ -24,6 +24,12 @@ type SendChequeFunc func(cheque *SignedCheque) error ...@@ -24,6 +24,12 @@ type SendChequeFunc func(cheque *SignedCheque) error
const ( const (
lastIssuedChequeKeyPrefix = "chequebook_last_issued_cheque_" lastIssuedChequeKeyPrefix = "chequebook_last_issued_cheque_"
totalIssuedKey = "chequebook_total_issued_"
)
var (
// ErrOutOfFunds is the error when the chequebook has not enough free funds for a cheque
ErrOutOfFunds = errors.New("chequebook out of funds")
) )
// Service is the main interface for interacting with the nodes chequebook. // Service is the main interface for interacting with the nodes chequebook.
...@@ -34,10 +40,12 @@ type Service interface { ...@@ -34,10 +40,12 @@ type Service interface {
WaitForDeposit(ctx context.Context, txHash common.Hash) error WaitForDeposit(ctx context.Context, txHash common.Hash) error
// Balance returns the token balance of the chequebook. // Balance returns the token balance of the chequebook.
Balance(ctx context.Context) (*big.Int, error) Balance(ctx context.Context) (*big.Int, error)
// AvailableBalance returns the token balance of the chequebook which is not yet used for uncashed cheques.
AvailableBalance(ctx context.Context) (*big.Int, error)
// Address returns the address of the used chequebook contract. // Address returns the address of the used chequebook contract.
Address() common.Address Address() common.Address
// Issue a new cheque for the beneficiary with an cumulativePayout amount higher than the last. // Issue a new cheque for the beneficiary with an cumulativePayout amount higher than the last.
Issue(beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) error Issue(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) error
// LastCheque returns the last cheque we issued for the beneficiary. // LastCheque returns the last cheque we issued for the beneficiary.
LastCheque(beneficiary common.Address) (*SignedCheque, error) LastCheque(beneficiary common.Address) (*SignedCheque, error)
// LastCheque returns the last cheques for all beneficiaries. // LastCheque returns the last cheques for all beneficiaries.
...@@ -146,6 +154,32 @@ func (s *service) Balance(ctx context.Context) (*big.Int, error) { ...@@ -146,6 +154,32 @@ func (s *service) Balance(ctx context.Context) (*big.Int, error) {
}) })
} }
// AvailableBalance returns the token balance of the chequebook which is not yet used for uncashed cheques.
func (s *service) AvailableBalance(ctx context.Context) (*big.Int, error) {
totalIssued, err := s.totalIssued()
if err != nil {
return nil, err
}
balance, err := s.Balance(ctx)
if err != nil {
return nil, err
}
totalPaidOut, err := s.chequebookInstance.TotalPaidOut(&bind.CallOpts{
Context: ctx,
})
if err != nil {
return nil, err
}
// balance plus totalPaidOut is the total amount ever put into the chequebook (ignoring deposits and withdrawals which cancelled out)
// minus the total amount we issued from this chequebook this gives use the portion of the balance not covered by any cheques
availableBalance := big.NewInt(0).Add(balance, totalPaidOut)
availableBalance = availableBalance.Sub(availableBalance, totalIssued)
return availableBalance, nil
}
// WaitForDeposit waits for the deposit transaction to confirm and verifies the result. // WaitForDeposit waits for the deposit transaction to confirm and verifies the result.
func (s *service) WaitForDeposit(ctx context.Context, txHash common.Hash) error { func (s *service) WaitForDeposit(ctx context.Context, txHash common.Hash) error {
receipt, err := s.transactionService.WaitForReceipt(ctx, txHash) receipt, err := s.transactionService.WaitForReceipt(ctx, txHash)
...@@ -165,11 +199,20 @@ func lastIssuedChequeKey(beneficiary common.Address) string { ...@@ -165,11 +199,20 @@ func lastIssuedChequeKey(beneficiary common.Address) string {
// Issue issues a new cheque and passes it to sendChequeFunc // Issue issues a new cheque and passes it to sendChequeFunc
// if sendChequeFunc succeeds the cheque is considered sent and saved // if sendChequeFunc succeeds the cheque is considered sent and saved
func (s *service) Issue(beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) error { func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) error {
// don't allow concurrent issuing of cheques // don't allow concurrent issuing of cheques
// this would be sufficient on a per beneficiary basis
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
availableBalance, err := s.AvailableBalance(ctx)
if err != nil {
return err
}
if amount.Cmp(availableBalance) > 0 {
return ErrOutOfFunds
}
var cumulativePayout *big.Int var cumulativePayout *big.Int
lastCheque, err := s.LastCheque(beneficiary) lastCheque, err := s.LastCheque(beneficiary)
if err != nil { if err != nil {
...@@ -209,7 +252,29 @@ func (s *service) Issue(beneficiary common.Address, amount *big.Int, sendChequeF ...@@ -209,7 +252,29 @@ func (s *service) Issue(beneficiary common.Address, amount *big.Int, sendChequeF
return err return err
} }
return s.store.Put(lastIssuedChequeKey(beneficiary), cheque) err = s.store.Put(lastIssuedChequeKey(beneficiary), cheque)
if err != nil {
return err
}
totalIssued, err := s.totalIssued()
if err != nil {
return err
}
totalIssued = totalIssued.Add(totalIssued, amount)
return s.store.Put(totalIssuedKey, totalIssued)
}
// returns the total amount in cheques issued so far
func (s *service) totalIssued() (totalIssued *big.Int, err error) {
err = s.store.Get(totalIssuedKey, &totalIssued)
if err != nil {
if err != storage.ErrNotFound {
return nil, err
}
return big.NewInt(0), nil
}
return totalIssued, nil
} }
// LastCheque returns the last cheque we issued for the beneficiary. // LastCheque returns the last cheque we issued for the beneficiary.
......
...@@ -259,7 +259,14 @@ func TestChequebookIssue(t *testing.T) { ...@@ -259,7 +259,14 @@ func TestChequebookIssue(t *testing.T) {
ownerAdress, ownerAdress,
store, store,
chequeSigner, chequeSigner,
&simpleSwapBindingMock{}, &simpleSwapBindingMock{
balance: func(*bind.CallOpts) (*big.Int, error) {
return big.NewInt(100), nil
},
totalPaidOut: func(*bind.CallOpts) (*big.Int, error) {
return big.NewInt(0), nil
},
},
&erc20BindingMock{}) &erc20BindingMock{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -282,7 +289,7 @@ func TestChequebookIssue(t *testing.T) { ...@@ -282,7 +289,7 @@ func TestChequebookIssue(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(beneficiary, amount, func(cheque *chequebook.SignedCheque) error { err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error {
if !cheque.Equal(expectedCheque) { if !cheque.Equal(expectedCheque) {
t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque) t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
} }
...@@ -318,7 +325,7 @@ func TestChequebookIssue(t *testing.T) { ...@@ -318,7 +325,7 @@ func TestChequebookIssue(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(beneficiary, amount2, func(cheque *chequebook.SignedCheque) error { err = chequebookService.Issue(context.Background(), beneficiary, amount2, func(cheque *chequebook.SignedCheque) error {
if !cheque.Equal(expectedCheque) { if !cheque.Equal(expectedCheque) {
t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque) t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
} }
...@@ -354,7 +361,7 @@ func TestChequebookIssue(t *testing.T) { ...@@ -354,7 +361,7 @@ func TestChequebookIssue(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(ownerAdress, amount, func(cheque *chequebook.SignedCheque) error { err = chequebookService.Issue(context.Background(), ownerAdress, amount, func(cheque *chequebook.SignedCheque) error {
if !cheque.Equal(expectedChequeOwner) { if !cheque.Equal(expectedChequeOwner) {
t.Fatalf("wrong cheque. wanted %v got %v", expectedChequeOwner, cheque) t.Fatalf("wrong cheque. wanted %v got %v", expectedChequeOwner, cheque)
} }
...@@ -403,7 +410,14 @@ func TestChequebookIssueErrorSend(t *testing.T) { ...@@ -403,7 +410,14 @@ func TestChequebookIssueErrorSend(t *testing.T) {
ownerAdress, ownerAdress,
store, store,
chequeSigner, chequeSigner,
&simpleSwapBindingMock{}, &simpleSwapBindingMock{
balance: func(*bind.CallOpts) (*big.Int, error) {
return amount, nil
},
totalPaidOut: func(*bind.CallOpts) (*big.Int, error) {
return big.NewInt(0), nil
},
},
&erc20BindingMock{}) &erc20BindingMock{})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -413,7 +427,7 @@ func TestChequebookIssueErrorSend(t *testing.T) { ...@@ -413,7 +427,7 @@ func TestChequebookIssueErrorSend(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(beneficiary, amount, func(cheque *chequebook.SignedCheque) error { err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error {
return errors.New("err") return errors.New("err")
}) })
if err == nil { if err == nil {
...@@ -422,9 +436,51 @@ func TestChequebookIssueErrorSend(t *testing.T) { ...@@ -422,9 +436,51 @@ func TestChequebookIssueErrorSend(t *testing.T) {
// verify the cheque was not saved // verify the cheque was not saved
_, err = chequebookService.LastCheque(beneficiary) _, err = chequebookService.LastCheque(beneficiary)
if err == nil { if !errors.Is(err, chequebook.ErrNoCheque) {
t.Fatal("expected error") t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrNoCheque, err)
} }
}
func TestChequebookIssueOutOfFunds(t *testing.T) {
address := common.HexToAddress("0xabcd")
erc20address := common.HexToAddress("0xefff")
beneficiary := common.HexToAddress("0xdddd")
ownerAdress := common.HexToAddress("0xfff")
store := storemock.NewStateStore()
amount := big.NewInt(20)
chequebookService, err := newTestChequebook(
t,
&backendMock{},
&transactionServiceMock{},
address,
erc20address,
ownerAdress,
store,
&chequeSignerMock{},
&simpleSwapBindingMock{
balance: func(*bind.CallOpts) (*big.Int, error) {
return big.NewInt(0), nil
},
totalPaidOut: func(*bind.CallOpts) (*big.Int, error) {
return big.NewInt(0), nil
},
},
&erc20BindingMock{})
if err != nil {
t.Fatal(err)
}
err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error {
return nil
})
if !errors.Is(err, chequebook.ErrOutOfFunds) {
t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrOutOfFunds, err)
}
// verify the cheque was not saved
_, err = chequebookService.LastCheque(beneficiary)
if !errors.Is(err, chequebook.ErrNoCheque) { if !errors.Is(err, chequebook.ErrNoCheque) {
t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrNoCheque, err) t.Fatalf("wrong error. wanted %v, got %v", chequebook.ErrNoCheque, err)
} }
......
...@@ -98,8 +98,9 @@ func (m *simpleSwapFactoryBindingMock) ERC20Address(o *bind.CallOpts) (common.Ad ...@@ -98,8 +98,9 @@ func (m *simpleSwapFactoryBindingMock) ERC20Address(o *bind.CallOpts) (common.Ad
} }
type simpleSwapBindingMock struct { type simpleSwapBindingMock struct {
balance func(*bind.CallOpts) (*big.Int, error) balance func(*bind.CallOpts) (*big.Int, error)
issuer func(*bind.CallOpts) (common.Address, error) issuer func(*bind.CallOpts) (common.Address, error)
totalPaidOut func(o *bind.CallOpts) (*big.Int, error)
} }
func (m *simpleSwapBindingMock) Balance(o *bind.CallOpts) (*big.Int, error) { func (m *simpleSwapBindingMock) Balance(o *bind.CallOpts) (*big.Int, error) {
...@@ -110,6 +111,10 @@ func (m *simpleSwapBindingMock) Issuer(o *bind.CallOpts) (common.Address, error) ...@@ -110,6 +111,10 @@ func (m *simpleSwapBindingMock) Issuer(o *bind.CallOpts) (common.Address, error)
return m.issuer(o) return m.issuer(o)
} }
func (m *simpleSwapBindingMock) TotalPaidOut(o *bind.CallOpts) (*big.Int, error) {
return m.totalPaidOut(o)
}
type erc20BindingMock struct { type erc20BindingMock struct {
balanceOf func(*bind.CallOpts, common.Address) (*big.Int, error) balanceOf func(*bind.CallOpts, common.Address) (*big.Int, error)
} }
......
...@@ -15,9 +15,10 @@ import ( ...@@ -15,9 +15,10 @@ import (
// Service is the mock chequebook service. // Service is the mock chequebook service.
type Service struct { type Service struct {
chequebookBalanceFunc func(context.Context) (*big.Int, error) chequebookBalanceFunc func(context.Context) (*big.Int, error)
chequebookAddressFunc func() common.Address chequebookAvailableBalanceFunc func(context.Context) (*big.Int, error)
chequebookIssueFunc func(beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) error chequebookAddressFunc func() common.Address
chequebookIssueFunc func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) error
} }
// WithChequebook*Functions set the mock chequebook functions // WithChequebook*Functions set the mock chequebook functions
...@@ -27,13 +28,19 @@ func WithChequebookBalanceFunc(f func(ctx context.Context) (*big.Int, error)) Op ...@@ -27,13 +28,19 @@ func WithChequebookBalanceFunc(f func(ctx context.Context) (*big.Int, error)) Op
}) })
} }
func WithChequebookAvailableBalanceFunc(f func(ctx context.Context) (*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.chequebookAvailableBalanceFunc = f
})
}
func WithChequebookAddressFunc(f func() common.Address) Option { func WithChequebookAddressFunc(f func() common.Address) Option {
return optionFunc(func(s *Service) { return optionFunc(func(s *Service) {
s.chequebookAddressFunc = f s.chequebookAddressFunc = f
}) })
} }
func WithChequebookIssueFunc(f func(beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) error) Option { func WithChequebookIssueFunc(f func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) error) Option {
return optionFunc(func(s *Service) { return optionFunc(func(s *Service) {
s.chequebookIssueFunc = f s.chequebookIssueFunc = f
}) })
...@@ -56,6 +63,13 @@ func (s *Service) Balance(ctx context.Context) (bal *big.Int, err error) { ...@@ -56,6 +63,13 @@ func (s *Service) Balance(ctx context.Context) (bal *big.Int, err error) {
return big.NewInt(0), errors.New("Error") return big.NewInt(0), errors.New("Error")
} }
func (s *Service) AvailableBalance(ctx context.Context) (bal *big.Int, err error) {
if s.chequebookAvailableBalanceFunc != nil {
return s.chequebookAvailableBalanceFunc(ctx)
}
return big.NewInt(0), errors.New("Error")
}
// Deposit mocks the chequebook .Deposit function // Deposit mocks the chequebook .Deposit function
func (s *Service) Deposit(ctx context.Context, amount *big.Int) (hash common.Hash, err error) { func (s *Service) Deposit(ctx context.Context, amount *big.Int) (hash common.Hash, err error) {
return common.Hash{}, errors.New("Error") return common.Hash{}, errors.New("Error")
...@@ -74,9 +88,9 @@ func (s *Service) Address() common.Address { ...@@ -74,9 +88,9 @@ func (s *Service) Address() common.Address {
return common.Address{} return common.Address{}
} }
func (s *Service) Issue(beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) error { func (s *Service) Issue(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) error {
if s.chequebookIssueFunc != nil { if s.chequebookIssueFunc != nil {
return s.chequebookIssueFunc(beneficiary, amount, sendChequeFunc) return s.chequebookIssueFunc(ctx, beneficiary, amount, sendChequeFunc)
} }
return nil return nil
} }
......
...@@ -92,7 +92,7 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er ...@@ -92,7 +92,7 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er
if !known { if !known {
return ErrUnknownBeneficary return ErrUnknownBeneficary
} }
err = s.chequebook.Issue(beneficiary, big.NewInt(int64(amount)), func(signedCheque *chequebook.SignedCheque) error { err = s.chequebook.Issue(ctx, beneficiary, big.NewInt(int64(amount)), func(signedCheque *chequebook.SignedCheque) error {
return s.proto.EmitCheque(ctx, peer, signedCheque) return s.proto.EmitCheque(ctx, peer, signedCheque)
}) })
if err != nil { if err != nil {
......
...@@ -266,7 +266,7 @@ func TestPay(t *testing.T) { ...@@ -266,7 +266,7 @@ func TestPay(t *testing.T) {
peer := swarm.MustParseHexAddress("abcd") peer := swarm.MustParseHexAddress("abcd")
var chequebookCalled bool var chequebookCalled bool
chequebookService := mockchequebook.NewChequebook( chequebookService := mockchequebook.NewChequebook(
mockchequebook.WithChequebookIssueFunc(func(b common.Address, a *big.Int, sendChequeFunc chequebook.SendChequeFunc) error { mockchequebook.WithChequebookIssueFunc(func(ctx context.Context, b common.Address, a *big.Int, sendChequeFunc chequebook.SendChequeFunc) error {
if b != beneficiary { if b != beneficiary {
t.Fatalf("issuing cheque for wrong beneficiary. wanted %v, got %v", beneficiary, b) t.Fatalf("issuing cheque for wrong beneficiary. wanted %v, got %v", beneficiary, b)
} }
...@@ -334,7 +334,7 @@ func TestPayIssueError(t *testing.T) { ...@@ -334,7 +334,7 @@ func TestPayIssueError(t *testing.T) {
peer := swarm.MustParseHexAddress("abcd") peer := swarm.MustParseHexAddress("abcd")
errReject := errors.New("reject") errReject := errors.New("reject")
chequebookService := mockchequebook.NewChequebook( chequebookService := mockchequebook.NewChequebook(
mockchequebook.WithChequebookIssueFunc(func(b common.Address, a *big.Int, sendChequeFunc chequebook.SendChequeFunc) error { mockchequebook.WithChequebookIssueFunc(func(ctx context.Context, b common.Address, a *big.Int, sendChequeFunc chequebook.SendChequeFunc) error {
return errReject return errReject
}), }),
) )
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment