Commit 7aa2db90 authored by acud's avatar acud Committed by GitHub

settlement: add available balance metric (#1172)

parent 1bad8c23
...@@ -50,7 +50,7 @@ type Service interface { ...@@ -50,7 +50,7 @@ type Service interface {
// 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(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) error Issue(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) (*big.Int, 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.
...@@ -204,21 +204,21 @@ func lastIssuedChequeKey(beneficiary common.Address) string { ...@@ -204,21 +204,21 @@ func lastIssuedChequeKey(beneficiary common.Address) string {
return fmt.Sprintf("chequebook_last_issued_cheque_%x", beneficiary) return fmt.Sprintf("chequebook_last_issued_cheque_%x", beneficiary)
} }
func (s *service) reserveTotalIssued(ctx context.Context, amount *big.Int) error { func (s *service) reserveTotalIssued(ctx context.Context, amount *big.Int) (*big.Int, error) {
s.lock.Lock() s.lock.Lock()
defer s.lock.Unlock() defer s.lock.Unlock()
availableBalance, err := s.AvailableBalance(ctx) availableBalance, err := s.AvailableBalance(ctx)
if err != nil { if err != nil {
return err return nil, err
} }
if amount.Cmp(big.NewInt(0).Sub(availableBalance, s.totalIssuedReserved)) > 0 { if amount.Cmp(big.NewInt(0).Sub(availableBalance, s.totalIssuedReserved)) > 0 {
return ErrOutOfFunds return nil, ErrOutOfFunds
} }
s.totalIssuedReserved = s.totalIssuedReserved.Add(s.totalIssuedReserved, amount) s.totalIssuedReserved = s.totalIssuedReserved.Add(s.totalIssuedReserved, amount)
return nil return big.NewInt(0).Sub(availableBalance, amount), nil
} }
func (s *service) unreserveTotalIssued(amount *big.Int) { func (s *service) unreserveTotalIssued(amount *big.Int) {
...@@ -227,12 +227,14 @@ func (s *service) unreserveTotalIssued(amount *big.Int) { ...@@ -227,12 +227,14 @@ func (s *service) unreserveTotalIssued(amount *big.Int) {
s.totalIssuedReserved = s.totalIssuedReserved.Sub(s.totalIssuedReserved, amount) s.totalIssuedReserved = s.totalIssuedReserved.Sub(s.totalIssuedReserved, amount)
} }
// 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 // The cheque is considered sent and saved when sendChequeFunc succeeds.
func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) error { // The available balance which is available after sending the cheque is passed
err := s.reserveTotalIssued(ctx, amount) // to the caller for it to be communicated over metrics.
func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) (*big.Int, error) {
availableBalance, err := s.reserveTotalIssued(ctx, amount)
if err != nil { if err != nil {
return err return nil, err
} }
defer s.unreserveTotalIssued(amount) defer s.unreserveTotalIssued(amount)
...@@ -240,7 +242,7 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount ...@@ -240,7 +242,7 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount
lastCheque, err := s.LastCheque(beneficiary) lastCheque, err := s.LastCheque(beneficiary)
if err != nil { if err != nil {
if err != ErrNoCheque { if err != ErrNoCheque {
return err return nil, err
} }
cumulativePayout = big.NewInt(0) cumulativePayout = big.NewInt(0)
} else { } else {
...@@ -263,7 +265,7 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount ...@@ -263,7 +265,7 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount
Beneficiary: beneficiary, Beneficiary: beneficiary,
}) })
if err != nil { if err != nil {
return err return nil, err
} }
// actually send the check before saving to avoid double payment // actually send the check before saving to avoid double payment
...@@ -272,12 +274,12 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount ...@@ -272,12 +274,12 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount
Signature: sig, Signature: sig,
}) })
if err != nil { if err != nil {
return err return nil, err
} }
err = s.store.Put(lastIssuedChequeKey(beneficiary), cheque) err = s.store.Put(lastIssuedChequeKey(beneficiary), cheque)
if err != nil { if err != nil {
return err return nil, err
} }
s.lock.Lock() s.lock.Lock()
...@@ -285,10 +287,10 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount ...@@ -285,10 +287,10 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount
totalIssued, err := s.totalIssued() totalIssued, err := s.totalIssued()
if err != nil { if err != nil {
return err return nil, err
} }
totalIssued = totalIssued.Add(totalIssued, amount) totalIssued = totalIssued.Add(totalIssued, amount)
return s.store.Put(totalIssuedKey, totalIssued) return availableBalance, s.store.Put(totalIssuedKey, totalIssued)
} }
// returns the total amount in cheques issued so far // returns the total amount in cheques issued so far
......
...@@ -292,7 +292,7 @@ func TestChequebookIssue(t *testing.T) { ...@@ -292,7 +292,7 @@ func TestChequebookIssue(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(context.Background(), 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)
} }
...@@ -328,7 +328,7 @@ func TestChequebookIssue(t *testing.T) { ...@@ -328,7 +328,7 @@ func TestChequebookIssue(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(context.Background(), 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)
} }
...@@ -364,7 +364,7 @@ func TestChequebookIssue(t *testing.T) { ...@@ -364,7 +364,7 @@ func TestChequebookIssue(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(context.Background(), 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)
} }
...@@ -430,7 +430,7 @@ func TestChequebookIssueErrorSend(t *testing.T) { ...@@ -430,7 +430,7 @@ func TestChequebookIssueErrorSend(t *testing.T) {
return sig, nil return sig, nil
} }
err = chequebookService.Issue(context.Background(), 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 {
...@@ -474,7 +474,7 @@ func TestChequebookIssueOutOfFunds(t *testing.T) { ...@@ -474,7 +474,7 @@ func TestChequebookIssueOutOfFunds(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error { _, err = chequebookService.Issue(context.Background(), beneficiary, amount, func(cheque *chequebook.SignedCheque) error {
return nil return nil
}) })
if !errors.Is(err, chequebook.ErrOutOfFunds) { if !errors.Is(err, chequebook.ErrOutOfFunds) {
......
...@@ -18,7 +18,7 @@ type Service struct { ...@@ -18,7 +18,7 @@ type Service struct {
chequebookBalanceFunc func(context.Context) (*big.Int, error) chequebookBalanceFunc func(context.Context) (*big.Int, error)
chequebookAvailableBalanceFunc func(context.Context) (*big.Int, error) chequebookAvailableBalanceFunc func(context.Context) (*big.Int, error)
chequebookAddressFunc func() common.Address chequebookAddressFunc func() common.Address
chequebookIssueFunc func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) error chequebookIssueFunc func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) (*big.Int, error)
chequebookWithdrawFunc func(ctx context.Context, amount *big.Int) (hash common.Hash, err error) chequebookWithdrawFunc func(ctx context.Context, amount *big.Int) (hash common.Hash, err error)
chequebookDepositFunc func(ctx context.Context, amount *big.Int) (hash common.Hash, err error) chequebookDepositFunc func(ctx context.Context, amount *big.Int) (hash common.Hash, err error)
} }
...@@ -48,7 +48,7 @@ func WithChequebookDepositFunc(f func(ctx context.Context, amount *big.Int) (has ...@@ -48,7 +48,7 @@ func WithChequebookDepositFunc(f func(ctx context.Context, amount *big.Int) (has
}) })
} }
func WithChequebookIssueFunc(f func(ctx context.Context, 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) (*big.Int, error)) Option {
return optionFunc(func(s *Service) { return optionFunc(func(s *Service) {
s.chequebookIssueFunc = f s.chequebookIssueFunc = f
}) })
...@@ -105,11 +105,11 @@ func (s *Service) Address() common.Address { ...@@ -105,11 +105,11 @@ func (s *Service) Address() common.Address {
return common.Address{} return common.Address{}
} }
func (s *Service) Issue(ctx context.Context, 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) (*big.Int, error) {
if s.chequebookIssueFunc != nil { if s.chequebookIssueFunc != nil {
return s.chequebookIssueFunc(ctx, beneficiary, amount, sendChequeFunc) return s.chequebookIssueFunc(ctx, beneficiary, amount, sendChequeFunc)
} }
return nil return big.NewInt(0), nil
} }
func (s *Service) LastCheque(beneficiary common.Address) (*chequebook.SignedCheque, error) { func (s *Service) LastCheque(beneficiary common.Address) (*chequebook.SignedCheque, error) {
......
...@@ -10,12 +10,10 @@ import ( ...@@ -10,12 +10,10 @@ import (
) )
type metrics struct { type metrics struct {
// all metrics fields must be exported TotalReceived prometheus.Counter
// to be able to return them by Metrics() TotalSent prometheus.Counter
// using reflection ChequesRejected prometheus.Counter
TotalReceived prometheus.Counter AvailableBalance prometheus.Gauge
TotalSent prometheus.Counter
ChequesRejected prometheus.Counter
} }
func newMetrics() metrics { func newMetrics() metrics {
...@@ -40,6 +38,12 @@ func newMetrics() metrics { ...@@ -40,6 +38,12 @@ func newMetrics() metrics {
Name: "cheques_rejected", Name: "cheques_rejected",
Help: "Number of cheques rejected", Help: "Number of cheques rejected",
}), }),
AvailableBalance: prometheus.NewGauge(prometheus.GaugeOpts{
Namespace: m.Namespace,
Subsystem: subsystem,
Name: "available_balance",
Help: "Currently availeble chequebook balance.",
}),
} }
} }
......
...@@ -119,12 +119,14 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er ...@@ -119,12 +119,14 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er
} }
return ErrUnknownBeneficary return ErrUnknownBeneficary
} }
err = s.chequebook.Issue(ctx, beneficiary, big.NewInt(int64(amount)), func(signedCheque *chequebook.SignedCheque) error { balance, 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 {
return err return err
} }
bal, _ := big.NewFloat(0).SetInt(balance).Float64()
s.metrics.AvailableBalance.Set(bal)
s.metrics.TotalSent.Add(float64(amount)) s.metrics.TotalSent.Add(float64(amount))
return nil return nil
} }
......
...@@ -285,7 +285,7 @@ func TestPay(t *testing.T) { ...@@ -285,7 +285,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(ctx context.Context, 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) (*big.Int, 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)
} }
...@@ -293,7 +293,7 @@ func TestPay(t *testing.T) { ...@@ -293,7 +293,7 @@ func TestPay(t *testing.T) {
t.Fatalf("issuing cheque with wrong amount. wanted %d, got %d", amount, a) t.Fatalf("issuing cheque with wrong amount. wanted %d, got %d", amount, a)
} }
chequebookCalled = true chequebookCalled = true
return sendChequeFunc(&cheque) return big.NewInt(0), sendChequeFunc(&cheque)
}), }),
) )
...@@ -355,8 +355,8 @@ func TestPayIssueError(t *testing.T) { ...@@ -355,8 +355,8 @@ 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(ctx context.Context, 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) (*big.Int, error) {
return errReject return big.NewInt(0), 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