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 {
// Address returns the address of the used chequebook contract.
Address() common.Address
// 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(beneficiary common.Address) (*SignedCheque, error)
// LastCheque returns the last cheques for all beneficiaries.
......@@ -204,21 +204,21 @@ func lastIssuedChequeKey(beneficiary common.Address) string {
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()
defer s.lock.Unlock()
availableBalance, err := s.AvailableBalance(ctx)
if err != nil {
return err
return nil, err
}
if amount.Cmp(big.NewInt(0).Sub(availableBalance, s.totalIssuedReserved)) > 0 {
return ErrOutOfFunds
return nil, ErrOutOfFunds
}
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) {
......@@ -227,12 +227,14 @@ func (s *service) unreserveTotalIssued(amount *big.Int) {
s.totalIssuedReserved = s.totalIssuedReserved.Sub(s.totalIssuedReserved, amount)
}
// Issue issues a new cheque and passes it to sendChequeFunc
// if sendChequeFunc succeeds the cheque is considered sent and saved
func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc SendChequeFunc) error {
err := s.reserveTotalIssued(ctx, amount)
// Issue issues a new cheque and passes it to sendChequeFunc.
// The cheque is considered sent and saved when sendChequeFunc succeeds.
// The available balance which is available after sending the cheque is passed
// 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 {
return err
return nil, err
}
defer s.unreserveTotalIssued(amount)
......@@ -240,7 +242,7 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount
lastCheque, err := s.LastCheque(beneficiary)
if err != nil {
if err != ErrNoCheque {
return err
return nil, err
}
cumulativePayout = big.NewInt(0)
} else {
......@@ -263,7 +265,7 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount
Beneficiary: beneficiary,
})
if err != nil {
return err
return nil, err
}
// 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
Signature: sig,
})
if err != nil {
return err
return nil, err
}
err = s.store.Put(lastIssuedChequeKey(beneficiary), cheque)
if err != nil {
return err
return nil, err
}
s.lock.Lock()
......@@ -285,10 +287,10 @@ func (s *service) Issue(ctx context.Context, beneficiary common.Address, amount
totalIssued, err := s.totalIssued()
if err != nil {
return err
return nil, err
}
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
......
......@@ -292,7 +292,7 @@ func TestChequebookIssue(t *testing.T) {
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) {
t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
}
......@@ -328,7 +328,7 @@ func TestChequebookIssue(t *testing.T) {
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) {
t.Fatalf("wrong cheque. wanted %v got %v", expectedCheque, cheque)
}
......@@ -364,7 +364,7 @@ func TestChequebookIssue(t *testing.T) {
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) {
t.Fatalf("wrong cheque. wanted %v got %v", expectedChequeOwner, cheque)
}
......@@ -430,7 +430,7 @@ func TestChequebookIssueErrorSend(t *testing.T) {
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")
})
if err == nil {
......@@ -474,7 +474,7 @@ func TestChequebookIssueOutOfFunds(t *testing.T) {
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
})
if !errors.Is(err, chequebook.ErrOutOfFunds) {
......
......@@ -18,7 +18,7 @@ type Service struct {
chequebookBalanceFunc func(context.Context) (*big.Int, error)
chequebookAvailableBalanceFunc func(context.Context) (*big.Int, error)
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)
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
})
}
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) {
s.chequebookIssueFunc = f
})
......@@ -105,11 +105,11 @@ func (s *Service) Address() 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 {
return s.chequebookIssueFunc(ctx, beneficiary, amount, sendChequeFunc)
}
return nil
return big.NewInt(0), nil
}
func (s *Service) LastCheque(beneficiary common.Address) (*chequebook.SignedCheque, error) {
......
......@@ -10,12 +10,10 @@ import (
)
type metrics struct {
// all metrics fields must be exported
// to be able to return them by Metrics()
// using reflection
TotalReceived prometheus.Counter
TotalSent prometheus.Counter
ChequesRejected prometheus.Counter
AvailableBalance prometheus.Gauge
}
func newMetrics() metrics {
......@@ -40,6 +38,12 @@ func newMetrics() metrics {
Name: "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
}
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)
})
if err != nil {
return err
}
bal, _ := big.NewFloat(0).SetInt(balance).Float64()
s.metrics.AvailableBalance.Set(bal)
s.metrics.TotalSent.Add(float64(amount))
return nil
}
......
......@@ -285,7 +285,7 @@ func TestPay(t *testing.T) {
peer := swarm.MustParseHexAddress("abcd")
var chequebookCalled bool
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 {
t.Fatalf("issuing cheque for wrong beneficiary. wanted %v, got %v", beneficiary, b)
}
......@@ -293,7 +293,7 @@ func TestPay(t *testing.T) {
t.Fatalf("issuing cheque with wrong amount. wanted %d, got %d", amount, a)
}
chequebookCalled = true
return sendChequeFunc(&cheque)
return big.NewInt(0), sendChequeFunc(&cheque)
}),
)
......@@ -355,8 +355,8 @@ func TestPayIssueError(t *testing.T) {
peer := swarm.MustParseHexAddress("abcd")
errReject := errors.New("reject")
chequebookService := mockchequebook.NewChequebook(
mockchequebook.WithChequebookIssueFunc(func(ctx context.Context, b common.Address, a *big.Int, sendChequeFunc chequebook.SendChequeFunc) error {
return errReject
mockchequebook.WithChequebookIssueFunc(func(ctx context.Context, b common.Address, a *big.Int, sendChequeFunc chequebook.SendChequeFunc) (*big.Int, error) {
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