Commit 273f120e authored by Ralph Pichler's avatar Ralph Pichler Committed by GitHub

swap, accounting: move to bigint (#1187)

parent 7ac406d3
......@@ -193,15 +193,15 @@ func (c *command) setAllFlags(cmd *cobra.Command) {
cmd.Flags().String(optionNameVerbosity, "info", "log verbosity level 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=trace")
cmd.Flags().String(optionWelcomeMessage, "", "send a welcome message string during handshakes")
cmd.Flags().Bool(optionNameGlobalPinningEnabled, false, "enable global pinning")
cmd.Flags().Uint64(optionNamePaymentThreshold, 100000, "threshold in BZZ where you expect to get paid from your peers")
cmd.Flags().Uint64(optionNamePaymentTolerance, 10000, "excess debt above payment threshold in BZZ where you disconnect from your peer")
cmd.Flags().Uint64(optionNamePaymentEarly, 10000, "amount in BZZ below the peers payment threshold when we initiate settlement")
cmd.Flags().String(optionNamePaymentThreshold, "100000", "threshold in BZZ where you expect to get paid from your peers")
cmd.Flags().String(optionNamePaymentTolerance, "10000", "excess debt above payment threshold in BZZ where you disconnect from your peer")
cmd.Flags().String(optionNamePaymentEarly, "10000", "amount in BZZ below the peers payment threshold when we initiate settlement")
cmd.Flags().StringSlice(optionNameResolverEndpoints, []string{}, "ENS compatible API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url")
cmd.Flags().Bool(optionNameGatewayMode, false, "disable a set of sensitive features in the api")
cmd.Flags().Bool(optionNameClefSignerEnable, false, "enable clef signer")
cmd.Flags().String(optionNameClefSignerEndpoint, "", "clef signer endpoint")
cmd.Flags().String(optionNameSwapEndpoint, "http://localhost:8545", "swap ethereum blockchain endpoint")
cmd.Flags().String(optionNameSwapFactoryAddress, "", "swap factory address")
cmd.Flags().Uint64(optionNameSwapInitialDeposit, 100000000, "initial deposit if deploying a new chequebook")
cmd.Flags().String(optionNameSwapInitialDeposit, "100000000", "initial deposit if deploying a new chequebook")
cmd.Flags().Bool(optionNameSwapEnable, true, "enable swap")
}
......@@ -137,14 +137,14 @@ Welcome to the Swarm.... Bzzz Bzzzz Bzzzz
TracingServiceName: c.config.GetString(optionNameTracingServiceName),
Logger: logger,
GlobalPinningEnabled: c.config.GetBool(optionNameGlobalPinningEnabled),
PaymentThreshold: c.config.GetUint64(optionNamePaymentThreshold),
PaymentTolerance: c.config.GetUint64(optionNamePaymentTolerance),
PaymentEarly: c.config.GetUint64(optionNamePaymentEarly),
PaymentThreshold: c.config.GetString(optionNamePaymentThreshold),
PaymentTolerance: c.config.GetString(optionNamePaymentTolerance),
PaymentEarly: c.config.GetString(optionNamePaymentEarly),
ResolverConnectionCfgs: resolverCfgs,
GatewayMode: c.config.GetBool(optionNameGatewayMode),
SwapEndpoint: c.config.GetString(optionNameSwapEndpoint),
SwapFactoryAddress: c.config.GetString(optionNameSwapFactoryAddress),
SwapInitialDeposit: c.config.GetUint64(optionNameSwapInitialDeposit),
SwapInitialDeposit: c.config.GetString(optionNameSwapInitialDeposit),
SwapEnable: c.config.GetBool(optionNameSwapEnable),
})
if err != nil {
......
......@@ -8,7 +8,7 @@ import (
"context"
"errors"
"fmt"
"math"
"math/big"
"strings"
"sync"
"time"
......@@ -42,22 +42,22 @@ type Interface interface {
// Debit increases the balance we have with the peer (we get "paid" back).
Debit(peer swarm.Address, price uint64) error
// Balance returns the current balance for the given peer.
Balance(peer swarm.Address) (int64, error)
Balance(peer swarm.Address) (*big.Int, error)
// SurplusBalance returns the current surplus balance for the given peer.
SurplusBalance(peer swarm.Address) (int64, error)
SurplusBalance(peer swarm.Address) (*big.Int, error)
// Balances returns balances for all known peers.
Balances() (map[string]int64, error)
Balances() (map[string]*big.Int, error)
// CompensatedBalance returns the current balance deducted by current surplus balance for the given peer.
CompensatedBalance(peer swarm.Address) (int64, error)
CompensatedBalance(peer swarm.Address) (*big.Int, error)
// CompensatedBalances returns the compensated balances for all known peers.
CompensatedBalances() (map[string]int64, error)
CompensatedBalances() (map[string]*big.Int, error)
}
// accountingPeer holds all in-memory accounting information for one peer.
type accountingPeer struct {
lock sync.Mutex // lock to be held during any accounting action for this peer
reservedBalance uint64 // amount currently reserved for active peer interaction
paymentThreshold uint64 // the threshold at which the peer expects us to pay
reservedBalance *big.Int // amount currently reserved for active peer interaction
paymentThreshold *big.Int // the threshold at which the peer expects us to pay
}
// Accounting is the main implementation of the accounting interface.
......@@ -68,11 +68,11 @@ type Accounting struct {
logger logging.Logger
store storage.StateStorer
// The payment threshold in BZZ we communicate to our peers.
paymentThreshold uint64
paymentThreshold *big.Int
// The amount in BZZ we let peers exceed the payment threshold before we
// disconnect them.
paymentTolerance uint64
earlyPayment uint64
paymentTolerance *big.Int
earlyPayment *big.Int
settlement settlement.Interface
pricing pricing.Interface
metrics metrics
......@@ -95,16 +95,12 @@ var (
func NewAccounting(
PaymentThreshold,
PaymentTolerance,
EarlyPayment uint64,
EarlyPayment *big.Int,
Logger logging.Logger,
Store storage.StateStorer,
Settlement settlement.Interface,
Pricing pricing.Interface,
) (*Accounting, error) {
if PaymentTolerance+PaymentThreshold > math.MaxInt64 {
return nil, fmt.Errorf("tolerance plus threshold too big: %w", ErrOverflow)
}
return &Accounting{
accountingPeers: make(map[string]*accountingPeer),
paymentThreshold: PaymentThreshold,
......@@ -135,31 +131,22 @@ func (a *Accounting) Reserve(ctx context.Context, peer swarm.Address, price uint
}
}
// Check for safety of increase of reservedBalance by price
if accountingPeer.reservedBalance+price < accountingPeer.reservedBalance {
return ErrOverflow
}
bigPrice := new(big.Int).SetUint64(price)
nextReserved := new(big.Int).Add(accountingPeer.reservedBalance, bigPrice)
nextReserved := accountingPeer.reservedBalance + price
// Subtract already reserved amount from actual balance, to get expected balance
expectedBalance, err := subtractI64mU64(currentBalance, nextReserved)
if err != nil {
return err
}
expectedBalance := new(big.Int).Sub(currentBalance, nextReserved)
// Determine if we will owe anything to the peer, if we owe less than 0, we conclude we owe nothing
// This conversion is made safe by previous subtractI64mU64 not allowing MinInt64
expectedDebt := -expectedBalance
if expectedDebt < 0 {
expectedDebt = 0
expectedDebt := new(big.Int).Neg(expectedBalance)
if expectedDebt.Cmp(big.NewInt(0)) < 0 {
expectedDebt.SetInt64(0)
}
threshold := accountingPeer.paymentThreshold
if threshold > a.earlyPayment {
threshold -= a.earlyPayment
threshold := new(big.Int).Set(accountingPeer.paymentThreshold)
if threshold.Cmp(a.earlyPayment) > 0 {
threshold.Sub(threshold, a.earlyPayment)
} else {
threshold = 0
threshold.SetInt64(0)
}
additionalDebt, err := a.SurplusBalance(peer)
......@@ -168,35 +155,29 @@ func (a *Accounting) Reserve(ctx context.Context, peer swarm.Address, price uint
}
// uint64 conversion of surplusbalance is safe because surplusbalance is always positive
if additionalDebt < 0 {
if additionalDebt.Cmp(big.NewInt(0)) < 0 {
return ErrInvalidValue
}
increasedExpectedDebt, err := addI64pU64(expectedDebt, uint64(additionalDebt))
if err != nil {
return err
}
increasedExpectedDebt := new(big.Int).Add(expectedDebt, additionalDebt)
// If our expected debt is less than earlyPayment away from our payment threshold
// and we are actually in debt, trigger settlement.
// we pay early to avoid needlessly blocking request later when concurrent requests occur and we are already close to the payment threshold.
if increasedExpectedDebt >= int64(threshold) && currentBalance < 0 {
if increasedExpectedDebt.Cmp(threshold) >= 0 && currentBalance.Cmp(big.NewInt(0)) < 0 {
err = a.settle(ctx, peer, accountingPeer)
if err != nil {
return fmt.Errorf("failed to settle with peer %v: %v", peer, err)
}
// if we settled successfully our balance is back at 0
// and the expected debt therefore equals next reserved amount
expectedDebt = int64(nextReserved)
increasedExpectedDebt, err = addI64pU64(expectedDebt, uint64(additionalDebt))
if err != nil {
return err
}
expectedDebt = nextReserved
increasedExpectedDebt = new(big.Int).Add(expectedDebt, additionalDebt)
}
// if expectedDebt would still exceed the paymentThreshold at this point block this request
// this can happen if there is a large number of concurrent requests to the same peer
if increasedExpectedDebt > int64(accountingPeer.paymentThreshold) {
if increasedExpectedDebt.Cmp(accountingPeer.paymentThreshold) > 0 {
a.metrics.AccountingBlocksCount.Inc()
return ErrOverdraft
}
......@@ -216,12 +197,14 @@ func (a *Accounting) Release(peer swarm.Address, price uint64) {
accountingPeer.lock.Lock()
defer accountingPeer.lock.Unlock()
bigPrice := new(big.Int).SetUint64(price)
// NOTE: this should never happen if Reserve and Release calls are paired.
if price > accountingPeer.reservedBalance {
if bigPrice.Cmp(accountingPeer.reservedBalance) > 0 {
a.logger.Error("attempting to release more balance than was reserved for peer")
accountingPeer.reservedBalance = 0
accountingPeer.reservedBalance.SetUint64(0)
} else {
accountingPeer.reservedBalance -= price
accountingPeer.reservedBalance.Sub(accountingPeer.reservedBalance, bigPrice)
}
}
......@@ -244,10 +227,7 @@ func (a *Accounting) Credit(peer swarm.Address, price uint64) error {
}
// Calculate next balance by safely decreasing current balance with the price we credit
nextBalance, err := subtractI64mU64(currentBalance, price)
if err != nil {
return err
}
nextBalance := new(big.Int).Sub(currentBalance, new(big.Int).SetUint64(price))
a.logger.Tracef("crediting peer %v with price %d, new balance is %d", peer, price, nextBalance)
......@@ -274,23 +254,17 @@ func (a *Accounting) settle(ctx context.Context, peer swarm.Address, balance *ac
// Don't do anything if there is no actual debt.
// This might be the case if the peer owes us and the total reserve for a
// peer exceeds the payment treshold.
if oldBalance >= 0 {
if oldBalance.Cmp(big.NewInt(0)) >= 0 {
return nil
}
// check safety of the following -1 * int64 conversion, all negative int64 have positive int64 equals except MinInt64
if oldBalance == math.MinInt64 {
return ErrOverflow
}
// This is safe because of the earlier check for oldbalance < 0 and the check for != MinInt64
paymentAmount := uint64(-oldBalance)
nextBalance := 0
paymentAmount := new(big.Int).Neg(oldBalance)
// Try to save the next balance first.
// Otherwise we might pay and then not be able to save, forcing us to pay
// again after restart.
err = a.store.Put(peerBalanceKey(peer), nextBalance)
err = a.store.Put(peerBalanceKey(peer), big.NewInt(0))
if err != nil {
return fmt.Errorf("failed to persist balance: %w", err)
}
......@@ -320,22 +294,19 @@ func (a *Accounting) Debit(peer swarm.Address, price uint64) error {
accountingPeer.lock.Lock()
defer accountingPeer.lock.Unlock()
cost := price
cost := new(big.Int).SetUint64(price)
// see if peer has surplus balance to deduct this transaction of
surplusBalance, err := a.SurplusBalance(peer)
if err != nil {
return fmt.Errorf("failed to get surplus balance: %w", err)
}
if surplusBalance > 0 {
if surplusBalance.Cmp(big.NewInt(0)) > 0 {
// get new surplus balance after deduct
newSurplusBalance, err := subtractI64mU64(surplusBalance, price)
if err != nil {
return err
}
newSurplusBalance := new(big.Int).Sub(surplusBalance, cost)
// if nothing left for debiting, store new surplus balance and return from debit
if newSurplusBalance >= 0 {
if newSurplusBalance.Cmp(big.NewInt(0)) >= 0 {
a.logger.Tracef("surplus debiting peer %v with value %d, new surplus balance is %d", peer, price, newSurplusBalance)
err = a.store.Put(peerSurplusBalanceKey(peer), newSurplusBalance)
......@@ -349,22 +320,19 @@ func (a *Accounting) Debit(peer swarm.Address, price uint64) error {
}
// if surplus balance didn't cover full transaction, let's continue with leftover part as cost
debitIncrease, err := subtractU64mI64(price, surplusBalance)
if err != nil {
return err
}
debitIncrease := new(big.Int).Sub(new(big.Int).SetUint64(price), surplusBalance)
// conversion to uint64 is safe because we know the relationship between the values by now, but let's make a sanity check
if debitIncrease <= 0 {
if debitIncrease.Cmp(big.NewInt(0)) <= 0 {
return fmt.Errorf("sanity check failed for partial debit after surplus balance drawn")
}
cost = uint64(debitIncrease)
cost.Set(debitIncrease)
// if we still have something to debit, than have run out of surplus balance,
// let's store 0 as surplus balance
a.logger.Tracef("surplus debiting peer %v with value %d, new surplus balance is 0", peer, debitIncrease)
err = a.store.Put(peerSurplusBalanceKey(peer), 0)
err = a.store.Put(peerSurplusBalanceKey(peer), big.NewInt(0))
if err != nil {
return fmt.Errorf("failed to persist surplus balance: %w", err)
}
......@@ -379,10 +347,7 @@ func (a *Accounting) Debit(peer swarm.Address, price uint64) error {
}
// Get nextBalance by safely increasing current balance with price
nextBalance, err := addI64pU64(currentBalance, cost)
if err != nil {
return err
}
nextBalance := new(big.Int).Add(currentBalance, cost)
a.logger.Tracef("debiting peer %v with price %d, new balance is %d", peer, price, nextBalance)
......@@ -394,7 +359,7 @@ func (a *Accounting) Debit(peer swarm.Address, price uint64) error {
a.metrics.TotalDebitedAmount.Add(float64(price))
a.metrics.DebitEventsCount.Inc()
if nextBalance >= int64(a.paymentThreshold+a.paymentTolerance) {
if nextBalance.Cmp(new(big.Int).Add(a.paymentThreshold, a.paymentTolerance)) >= 0 {
// peer too much in debt
a.metrics.AccountingDisconnectsCount.Inc()
return p2p.NewBlockPeerError(10000*time.Hour, ErrDisconnectThresholdExceeded)
......@@ -404,61 +369,58 @@ func (a *Accounting) Debit(peer swarm.Address, price uint64) error {
}
// Balance returns the current balance for the given peer.
func (a *Accounting) Balance(peer swarm.Address) (balance int64, err error) {
func (a *Accounting) Balance(peer swarm.Address) (balance *big.Int, err error) {
err = a.store.Get(peerBalanceKey(peer), &balance)
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
return 0, ErrPeerNoBalance
return big.NewInt(0), ErrPeerNoBalance
}
return 0, err
return nil, err
}
return balance, nil
}
// SurplusBalance returns the current balance for the given peer.
func (a *Accounting) SurplusBalance(peer swarm.Address) (balance int64, err error) {
func (a *Accounting) SurplusBalance(peer swarm.Address) (balance *big.Int, err error) {
err = a.store.Get(peerSurplusBalanceKey(peer), &balance)
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
return 0, nil
return big.NewInt(0), nil
}
return 0, err
return nil, err
}
return balance, nil
}
// CompensatedBalance returns balance decreased by surplus balance
func (a *Accounting) CompensatedBalance(peer swarm.Address) (compensated int64, err error) {
func (a *Accounting) CompensatedBalance(peer swarm.Address) (compensated *big.Int, err error) {
surplus, err := a.SurplusBalance(peer)
if err != nil {
return 0, err
return nil, err
}
if surplus < 0 {
return 0, ErrInvalidValue
if surplus.Cmp(big.NewInt(0)) < 0 {
return nil, ErrInvalidValue
}
balance, err := a.Balance(peer)
if err != nil {
if !errors.Is(err, ErrPeerNoBalance) {
return 0, err
return nil, err
}
}
// if surplus is 0 and peer has no balance, propagate ErrPeerNoBalance
if surplus == 0 && errors.Is(err, ErrPeerNoBalance) {
return 0, err
if surplus.Cmp(big.NewInt(0)) == 0 && errors.Is(err, ErrPeerNoBalance) {
return nil, err
}
// Compensated balance is balance decreased by surplus balance
compensated, err = subtractI64mU64(balance, uint64(surplus))
if err != nil {
return 0, err
}
compensated = new(big.Int).Sub(balance, surplus)
return compensated, nil
}
......@@ -482,7 +444,7 @@ func (a *Accounting) getAccountingPeer(peer swarm.Address) (*accountingPeer, err
peerData, ok := a.accountingPeers[peer.String()]
if !ok {
peerData = &accountingPeer{
reservedBalance: 0,
reservedBalance: big.NewInt(0),
// initially assume the peer has the same threshold as us
paymentThreshold: a.paymentThreshold,
}
......@@ -493,8 +455,8 @@ func (a *Accounting) getAccountingPeer(peer swarm.Address) (*accountingPeer, err
}
// Balances gets balances for all peers from store.
func (a *Accounting) Balances() (map[string]int64, error) {
s := make(map[string]int64)
func (a *Accounting) Balances() (map[string]*big.Int, error) {
s := make(map[string]*big.Int)
err := a.store.Iterate(balancesPrefix, func(key, val []byte) (stop bool, err error) {
addr, err := balanceKeyPeer(key)
......@@ -503,7 +465,7 @@ func (a *Accounting) Balances() (map[string]int64, error) {
}
if _, ok := s[addr.String()]; !ok {
var storevalue int64
var storevalue *big.Int
err = a.store.Get(peerBalanceKey(addr), &storevalue)
if err != nil {
return false, fmt.Errorf("get peer %s balance: %v", addr.String(), err)
......@@ -523,8 +485,8 @@ func (a *Accounting) Balances() (map[string]int64, error) {
}
// Balances gets balances for all peers from store.
func (a *Accounting) CompensatedBalances() (map[string]int64, error) {
s := make(map[string]int64)
func (a *Accounting) CompensatedBalances() (map[string]*big.Int, error) {
s := make(map[string]*big.Int)
err := a.store.Iterate(balancesPrefix, func(key, val []byte) (stop bool, err error) {
addr, err := balanceKeyPeer(key)
......@@ -605,7 +567,7 @@ func surplusBalanceKeyPeer(key []byte) (swarm.Address, error) {
}
// NotifyPayment is called by Settlement when we receive a payment.
func (a *Accounting) NotifyPayment(peer swarm.Address, amount uint64) error {
func (a *Accounting) NotifyPayment(peer swarm.Address, amount *big.Int) error {
accountingPeer, err := a.getAccountingPeer(peer)
if err != nil {
return err
......@@ -622,15 +584,12 @@ func (a *Accounting) NotifyPayment(peer swarm.Address, amount uint64) error {
}
// if balance is already negative or zero, we credit full amount received to surplus balance and terminate early
if currentBalance <= 0 {
if currentBalance.Cmp(big.NewInt(0)) <= 0 {
surplus, err := a.SurplusBalance(peer)
if err != nil {
return fmt.Errorf("failed to get surplus balance: %w", err)
}
increasedSurplus, err := addI64pU64(surplus, amount)
if err != nil {
return err
}
increasedSurplus := new(big.Int).Add(surplus, amount)
a.logger.Tracef("surplus crediting peer %v with amount %d due to payment, new surplus balance is %d", peer, amount, increasedSurplus)
......@@ -643,17 +602,14 @@ func (a *Accounting) NotifyPayment(peer swarm.Address, amount uint64) error {
}
// if current balance is positive, let's make a partial credit to
newBalance, err := subtractI64mU64(currentBalance, amount)
if err != nil {
return err
}
newBalance := new(big.Int).Sub(currentBalance, amount)
// Don't allow a payment to put us into debt
// This is to prevent another node tricking us into settling by settling
// first (e.g. send a bouncing cheque to trigger an honest cheque in swap).
nextBalance := newBalance
if newBalance < 0 {
nextBalance = 0
if newBalance.Cmp(big.NewInt(0)) < 0 {
nextBalance = big.NewInt(0)
}
a.logger.Tracef("crediting peer %v with amount %d due to payment, new balance is %d", peer, amount, nextBalance)
......@@ -666,20 +622,14 @@ func (a *Accounting) NotifyPayment(peer swarm.Address, amount uint64) error {
// If payment would have put us into debt, rather, let's add to surplusBalance,
// so as that an oversettlement attempt creates balance for future forwarding services
// charges to be deducted of
if newBalance < 0 {
surplusGrowth, err := subtractU64mI64(amount, currentBalance)
if err != nil {
return err
}
if newBalance.Cmp(big.NewInt(0)) < 0 {
surplusGrowth := new(big.Int).Sub(amount, currentBalance)
surplus, err := a.SurplusBalance(peer)
if err != nil {
return fmt.Errorf("failed to get surplus balance: %w", err)
}
increasedSurplus := surplus + surplusGrowth
if increasedSurplus < surplus {
return ErrOverflow
}
increasedSurplus := new(big.Int).Add(surplus, surplusGrowth)
a.logger.Tracef("surplus crediting peer %v with amount %d due to payment, new surplus balance is %d", peer, surplusGrowth, increasedSurplus)
......@@ -694,7 +644,7 @@ func (a *Accounting) NotifyPayment(peer swarm.Address, amount uint64) error {
// AsyncNotifyPayment calls notify payment in a go routine.
// This is needed when accounting needs to be notified but the accounting lock is already held.
func (a *Accounting) AsyncNotifyPayment(peer swarm.Address, amount uint64) error {
func (a *Accounting) AsyncNotifyPayment(peer swarm.Address, amount *big.Int) error {
go func() {
err := a.NotifyPayment(peer, amount)
if err != nil {
......@@ -704,67 +654,8 @@ func (a *Accounting) AsyncNotifyPayment(peer swarm.Address, amount uint64) error
return nil
}
// subtractI64mU64 is a helper function for safe subtraction of Int64 - Uint64
// It checks for
// - overflow safety in conversion of uint64 to int64
// - safety of the arithmetic
// - whether ( -1 * result ) is still Int64, as MinInt64 in absolute sense is 1 larger than MaxInt64
// If result is MinInt64, we also return overflow error, for two reasons:
// - in some cases we are going to use -1 * result in the following operations, which is secured by this check
// - we also do not want to possibly store this value as balance, even if ( -1 * result ) is not used immediately afterwards, because it could
// disable settleing for this amount as the value would create overflow
func subtractI64mU64(base int64, subtracted uint64) (result int64, err error) {
if subtracted > math.MaxInt64 {
return 0, ErrOverflow
}
result = base - int64(subtracted)
if result > base {
return 0, ErrOverflow
}
if result == math.MinInt64 {
return 0, ErrOverflow
}
return result, nil
}
func subtractU64mI64(base uint64, subtracted int64) (result int64, err error) {
if base > math.MaxInt64 {
return 0, ErrOverflow
}
// base is positive, overflow can happen by subtracting negative number
result = int64(base) - subtracted
if subtracted < 0 {
if result < int64(base) {
return 0, ErrOverflow
}
}
return result, nil
}
// addI64pU64 is a helper function for safe addition of Int64 + Uint64
// It checks for
// - overflow safety in conversion of uint64 to int64
// - safety of the arithmetic
func addI64pU64(a int64, b uint64) (result int64, err error) {
if b > math.MaxInt64 {
return 0, ErrOverflow
}
result = a + int64(b)
if result < a {
return 0, ErrOverflow
}
return result, nil
}
// NotifyPaymentThreshold should be called to notify accounting of changes in the payment threshold
func (a *Accounting) NotifyPaymentThreshold(peer swarm.Address, paymentThreshold uint64) error {
func (a *Accounting) NotifyPaymentThreshold(peer swarm.Address, paymentThreshold *big.Int) error {
accountingPeer, err := a.getAccountingPeer(peer)
if err != nil {
return err
......@@ -773,6 +664,6 @@ func (a *Accounting) NotifyPaymentThreshold(peer swarm.Address, paymentThreshold
accountingPeer.lock.Lock()
defer accountingPeer.lock.Unlock()
accountingPeer.paymentThreshold = paymentThreshold
accountingPeer.paymentThreshold.Set(paymentThreshold)
return nil
}
......@@ -8,22 +8,25 @@ import (
"context"
"errors"
"io/ioutil"
"math"
"math/big"
"testing"
"github.com/ethersphere/bee/pkg/accounting"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p"
mockSettlement "github.com/ethersphere/bee/pkg/settlement/pseudosettle/mock"
mockSettlement "github.com/ethersphere/bee/pkg/settlement/swap/mock"
"github.com/ethersphere/bee/pkg/statestore/mock"
"github.com/ethersphere/bee/pkg/swarm"
)
const (
testPaymentThreshold = 10000
testPaymentThresholdLarge = math.MaxInt64 - 1
testPaymentTolerance = 1000
testPrice = uint64(10)
testPrice = uint64(10)
)
var (
testPaymentTolerance = big.NewInt(1000)
testPaymentEarly = big.NewInt(1000)
testPaymentThreshold = big.NewInt(10000)
)
// booking represents an accounting action and the expected result afterwards
......@@ -40,7 +43,7 @@ func TestAccountingAddBalance(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
acc, err := accounting.NewAccounting(testPaymentThreshold, 1000, 1000, logger, store, nil, nil)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
......@@ -86,7 +89,7 @@ func TestAccountingAddBalance(t *testing.T) {
t.Fatal(err)
}
if balance != booking.expectedBalance {
if balance.Int64() != booking.expectedBalance {
t.Fatalf("balance for peer %v not as expected after booking %d. got %d, wanted %d", booking.peer.String(), i, balance, booking.expectedBalance)
}
}
......@@ -101,7 +104,7 @@ func TestAccountingAdd_persistentBalances(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
acc, err := accounting.NewAccounting(testPaymentThreshold, 1000, 1000, logger, store, nil, nil)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
......@@ -128,7 +131,7 @@ func TestAccountingAdd_persistentBalances(t *testing.T) {
t.Fatal(err)
}
acc, err = accounting.NewAccounting(testPaymentThreshold, 1000, 1000, logger, store, nil, nil)
acc, err = accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
......@@ -138,7 +141,7 @@ func TestAccountingAdd_persistentBalances(t *testing.T) {
t.Fatal(err)
}
if peer1Balance != int64(peer1DebitAmount) {
if peer1Balance.Uint64() != peer1DebitAmount {
t.Fatalf("peer1Balance not loaded correctly. got %d, wanted %d", peer1Balance, peer1DebitAmount)
}
......@@ -147,7 +150,7 @@ func TestAccountingAdd_persistentBalances(t *testing.T) {
t.Fatal(err)
}
if peer2Balance != -int64(peer2CreditAmount) {
if peer2Balance.Int64() != -int64(peer2CreditAmount) {
t.Fatalf("peer2Balance not loaded correctly. got %d, wanted %d", peer2Balance, -int64(peer2CreditAmount))
}
}
......@@ -159,7 +162,7 @@ func TestAccountingReserve(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
acc, err := accounting.NewAccounting(testPaymentThreshold, 1000, 1000, logger, store, nil, nil)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
......@@ -170,7 +173,7 @@ func TestAccountingReserve(t *testing.T) {
}
// it should allow to cross the threshold one time
err = acc.Reserve(context.Background(), peer1Addr, testPaymentThreshold+1)
err = acc.Reserve(context.Background(), peer1Addr, testPaymentThreshold.Uint64()+1)
if err == nil {
t.Fatal("expected error from reserve")
}
......@@ -180,225 +183,6 @@ func TestAccountingReserve(t *testing.T) {
}
}
func TestAccountingOverflowReserve(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mock.NewStateStore()
defer store.Close()
settlement := mockSettlement.NewSettlement()
acc, err := accounting.NewAccounting(testPaymentThresholdLarge, 0, 0, logger, store, settlement, nil)
if err != nil {
t.Fatal(err)
}
peer1Addr, err := swarm.ParseHexAddress("00112233")
if err != nil {
t.Fatal(err)
}
err = acc.Reserve(context.Background(), peer1Addr, testPaymentThresholdLarge)
if err != nil {
t.Fatal(err)
}
err = acc.Reserve(context.Background(), peer1Addr, math.MaxInt64)
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatalf("expected overflow error from Reserve, got %v", err)
}
acc.Release(peer1Addr, testPaymentThresholdLarge)
// Try crediting near maximal value for peer
err = acc.Credit(peer1Addr, math.MaxInt64)
if err != nil {
t.Fatal(err)
}
// Try reserving further value, should overflow
err = acc.Reserve(context.Background(), peer1Addr, 1)
// If we had other error, assert fail
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatalf("expected overflow error from Reserve, got %v", err)
}
}
func TestAccountingOverflowSurplusBalance(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mock.NewStateStore()
defer store.Close()
settlement := mockSettlement.NewSettlement()
acc, err := accounting.NewAccounting(testPaymentThresholdLarge, 0, 0, logger, store, settlement, nil)
if err != nil {
t.Fatal(err)
}
peer1Addr, err := swarm.ParseHexAddress("00112233")
if err != nil {
t.Fatal(err)
}
// Try Debiting a large amount to peer so balance is large positive
err = acc.Debit(peer1Addr, testPaymentThresholdLarge-1)
if err != nil {
t.Fatal(err)
}
// Notify of incoming payment from same peer, so balance goes to 0 with surplusbalance 2
err = acc.NotifyPayment(peer1Addr, math.MaxInt64)
if err != nil {
t.Fatal("Unexpected overflow from NotifyPayment")
}
// sanity check surplus balance
val, err := acc.SurplusBalance(peer1Addr)
if err != nil {
t.Fatal("Error checking Surplusbalance")
}
if val != 2 {
t.Fatal("Not expected surplus balance")
}
// sanity check balance
val, err = acc.Balance(peer1Addr)
if err != nil {
t.Fatal("Error checking Balance")
}
if val != 0 {
t.Fatal("Unexpected balance")
}
// Notify of incoming payment from same peer, further decreasing balance, this should overflow
err = acc.NotifyPayment(peer1Addr, math.MaxInt64)
if err == nil {
t.Fatal("Expected overflow from NotifyPayment")
}
// If we had other error, assert fail
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatal("Expected overflow error from NotifyPayment")
}
}
func TestAccountingOverflowNotifyPayment(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mock.NewStateStore()
defer store.Close()
settlement := mockSettlement.NewSettlement()
acc, err := accounting.NewAccounting(testPaymentThresholdLarge, 0, 0, logger, store, settlement, nil)
if err != nil {
t.Fatal(err)
}
peer1Addr, err := swarm.ParseHexAddress("00112233")
if err != nil {
t.Fatal(err)
}
// Try Crediting a large amount to peer so balance is negative
err = acc.Credit(peer1Addr, math.MaxInt64)
if err != nil {
t.Fatal(err)
}
// NotifyPayment for peer should now fill the surplus balance
err = acc.NotifyPayment(peer1Addr, math.MaxInt64)
if err != nil {
t.Fatalf("Expected no error but got one: %v", err)
}
// Notify of incoming payment from same peer, further increasing the surplus balance into an overflow
err = acc.NotifyPayment(peer1Addr, 1)
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatalf("expected overflow error from Debit, got %v", err)
}
}
func TestAccountingOverflowDebit(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mock.NewStateStore()
defer store.Close()
acc, err := accounting.NewAccounting(testPaymentThresholdLarge, 0, 0, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
peer1Addr, err := swarm.ParseHexAddress("00112233")
if err != nil {
t.Fatal(err)
}
// Try increasing peer debit with near maximal value
err = acc.Debit(peer1Addr, math.MaxInt64-2)
if err != nil {
t.Fatal(err)
}
// Try further increasing peer debit with near maximal value, this should fail
err = acc.Debit(peer1Addr, math.MaxInt64-2)
if err == nil {
t.Fatal("Expected error from overflow Debit")
}
// If we had other error, assert fail
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatalf("expected overflow error from Credit, got %v", err)
}
// Try further increasing peer debit with near maximal value, this should fail
err = acc.Debit(peer1Addr, math.MaxInt64)
if err == nil {
t.Fatal("Expected error from overflow Debit")
}
// If we had other error, assert fail
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatalf("expected overflow error from Debit, got %v", err)
}
}
func TestAccountingOverflowCredit(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mock.NewStateStore()
defer store.Close()
acc, err := accounting.NewAccounting(testPaymentThresholdLarge, 0, 0, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
peer1Addr, err := swarm.ParseHexAddress("00112233")
if err != nil {
t.Fatal(err)
}
// Try increasing peer credit with near maximal value
err = acc.Credit(peer1Addr, math.MaxInt64-2)
if err != nil {
t.Fatal(err)
}
// Try increasing with a further near maximal value, this must overflow
err = acc.Credit(peer1Addr, math.MaxInt64-2)
if err == nil {
t.Fatal("Expected error from overflow Credit")
}
// Try increasing with a small amount, which should also overflow
err = acc.Credit(peer1Addr, 3)
if err == nil {
t.Fatal("Expected error from overflow Credit")
}
// If we had other error, assert fail
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatalf("expected overflow error from Credit, got %v", err)
}
// Try increasing with maximal value
err = acc.Credit(peer1Addr, math.MaxInt64)
if err == nil {
t.Fatal("Expected error from overflow Credit")
}
// If we had other error, assert fail
if !errors.Is(err, accounting.ErrOverflow) {
t.Fatalf("expected overflow error from Credit, got %v", err)
}
}
// TestAccountingDisconnect tests that exceeding the disconnect threshold with Debit returns a p2p.DisconnectError
func TestAccountingDisconnect(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
......@@ -406,7 +190,7 @@ func TestAccountingDisconnect(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
acc, err := accounting.NewAccounting(testPaymentThreshold, 1000, 1000, logger, store, nil, nil)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
......@@ -417,7 +201,7 @@ func TestAccountingDisconnect(t *testing.T) {
}
// put the peer 1 unit away from disconnect
err = acc.Debit(peer1Addr, testPaymentThreshold+testPaymentTolerance-1)
err = acc.Debit(peer1Addr, testPaymentThreshold.Uint64()+testPaymentTolerance.Uint64()-1)
if err != nil {
t.Fatal("expected no error while still within tolerance")
}
......@@ -441,9 +225,9 @@ func TestAccountingCallSettlement(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
settlement := mockSettlement.NewSettlement()
settlement := mockSettlement.New()
acc, err := accounting.NewAccounting(testPaymentThreshold, 1000, 1000, logger, store, settlement, nil)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, settlement, nil)
if err != nil {
t.Fatal(err)
}
......@@ -453,18 +237,18 @@ func TestAccountingCallSettlement(t *testing.T) {
t.Fatal(err)
}
err = acc.Reserve(context.Background(), peer1Addr, testPaymentThreshold)
err = acc.Reserve(context.Background(), peer1Addr, testPaymentThreshold.Uint64())
if err != nil {
t.Fatal(err)
}
// Credit until payment treshold
err = acc.Credit(peer1Addr, testPaymentThreshold)
err = acc.Credit(peer1Addr, testPaymentThreshold.Uint64())
if err != nil {
t.Fatal(err)
}
acc.Release(peer1Addr, testPaymentThreshold)
acc.Release(peer1Addr, testPaymentThreshold.Uint64())
// try another request
err = acc.Reserve(context.Background(), peer1Addr, 1)
......@@ -479,7 +263,7 @@ func TestAccountingCallSettlement(t *testing.T) {
t.Fatal(err)
}
if totalSent != testPaymentThreshold {
if totalSent.Cmp(testPaymentThreshold) != 0 {
t.Fatalf("paid wrong amount. got %d wanted %d", totalSent, testPaymentThreshold)
}
......@@ -487,7 +271,7 @@ func TestAccountingCallSettlement(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if balance != 0 {
if balance.Int64() != 0 {
t.Fatalf("expected balance to be reset. got %d", balance)
}
......@@ -498,7 +282,7 @@ func TestAccountingCallSettlement(t *testing.T) {
}
// Credit until the expected debt exceeeds payment threshold
expectedAmount := uint64(testPaymentThreshold - 100)
expectedAmount := testPaymentThreshold.Uint64() - 100
err = acc.Reserve(context.Background(), peer1Addr, expectedAmount)
if err != nil {
t.Fatal(err)
......@@ -524,8 +308,8 @@ func TestAccountingCallSettlement(t *testing.T) {
t.Fatal(err)
}
if totalSent != expectedAmount+testPaymentThreshold {
t.Fatalf("paid wrong amount. got %d wanted %d", totalSent, expectedAmount+testPaymentThreshold)
if totalSent.Cmp(new(big.Int).Add(new(big.Int).SetUint64(expectedAmount), testPaymentThreshold)) != 0 {
t.Fatalf("paid wrong amount. got %d wanted %d", totalSent, new(big.Int).Add(new(big.Int).SetUint64(expectedAmount), testPaymentThreshold))
}
acc.Release(peer1Addr, 100)
......@@ -538,9 +322,9 @@ func TestAccountingCallSettlementEarly(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
settlement := mockSettlement.NewSettlement()
settlement := mockSettlement.New()
debt := uint64(500)
earlyPayment := uint64(1000)
earlyPayment := big.NewInt(1000)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, earlyPayment, logger, store, settlement, nil)
if err != nil {
......@@ -557,7 +341,7 @@ func TestAccountingCallSettlementEarly(t *testing.T) {
t.Fatal(err)
}
payment := testPaymentThreshold - earlyPayment
payment := testPaymentThreshold.Uint64() - earlyPayment.Uint64()
err = acc.Reserve(context.Background(), peer1Addr, payment)
if err != nil {
t.Fatal(err)
......@@ -570,7 +354,7 @@ func TestAccountingCallSettlementEarly(t *testing.T) {
t.Fatal(err)
}
if totalSent != debt {
if totalSent.Cmp(new(big.Int).SetUint64(debt)) != 0 {
t.Fatalf("paid wrong amount. got %d wanted %d", totalSent, testPaymentThreshold)
}
......@@ -578,7 +362,7 @@ func TestAccountingCallSettlementEarly(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if balance != 0 {
if balance.Int64() != 0 {
t.Fatalf("expected balance to be reset. got %d", balance)
}
}
......@@ -589,9 +373,9 @@ func TestAccountingSurplusBalance(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
settlement := mockSettlement.NewSettlement()
settlement := mockSettlement.New()
acc, err := accounting.NewAccounting(testPaymentThreshold, 0, 0, logger, store, settlement, nil)
acc, err := accounting.NewAccounting(testPaymentThreshold, big.NewInt(0), big.NewInt(0), logger, store, settlement, nil)
if err != nil {
t.Fatal(err)
}
......@@ -600,12 +384,12 @@ func TestAccountingSurplusBalance(t *testing.T) {
t.Fatal(err)
}
// Try Debiting a large amount to peer so balance is large positive
err = acc.Debit(peer1Addr, testPaymentThreshold-1)
err = acc.Debit(peer1Addr, testPaymentThreshold.Uint64()-1)
if err != nil {
t.Fatal(err)
}
// Notify of incoming payment from same peer, so balance goes to 0 with surplusbalance 2
err = acc.NotifyPayment(peer1Addr, testPaymentThreshold+1)
err = acc.NotifyPayment(peer1Addr, new(big.Int).Add(testPaymentThreshold, big.NewInt(1)))
if err != nil {
t.Fatal("Unexpected overflow from doable NotifyPayment")
}
......@@ -614,7 +398,7 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Surplusbalance")
}
if val != 2 {
if val.Int64() != 2 {
t.Fatal("Not expected surplus balance")
}
//sanity check balance
......@@ -622,7 +406,7 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Balance")
}
if val != 0 {
if val.Int64() != 0 {
t.Fatal("Not expected balance")
}
// Notify of incoming payment from same peer, so balance goes to 0 with surplusbalance 10002 (testpaymentthreshold+2)
......@@ -635,7 +419,7 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Surplusbalance")
}
if val != testPaymentThreshold+2 {
if val.Int64() != testPaymentThreshold.Int64()+2 {
t.Fatal("Unexpected surplus balance")
}
//sanity check balance
......@@ -643,11 +427,11 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Balance")
}
if val != 0 {
if val.Int64() != 0 {
t.Fatal("Not expected balance, expected 0")
}
// Debit for same peer, so balance stays 0 with surplusbalance decreasing to 2
err = acc.Debit(peer1Addr, testPaymentThreshold)
err = acc.Debit(peer1Addr, testPaymentThreshold.Uint64())
if err != nil {
t.Fatal("Unexpected error from Credit")
}
......@@ -656,7 +440,7 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Surplusbalance")
}
if val != 2 {
if val.Int64() != 2 {
t.Fatal("Unexpected surplus balance")
}
//sanity check balance
......@@ -664,11 +448,11 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Balance")
}
if val != 0 {
if val.Int64() != 0 {
t.Fatal("Not expected balance, expected 0")
}
// Debit for same peer, so balance goes to 9998 (testpaymentthreshold - 2) with surplusbalance decreasing to 0
err = acc.Debit(peer1Addr, testPaymentThreshold)
err = acc.Debit(peer1Addr, testPaymentThreshold.Uint64())
if err != nil {
t.Fatal("Unexpected error from NotifyPayment")
}
......@@ -677,7 +461,7 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Surplusbalance")
}
if val != 0 {
if val.Int64() != 0 {
t.Fatal("Unexpected surplus balance")
}
//sanity check balance
......@@ -685,7 +469,7 @@ func TestAccountingSurplusBalance(t *testing.T) {
if err != nil {
t.Fatal("Error checking Balance")
}
if val != testPaymentThreshold-2 {
if val.Int64() != testPaymentThreshold.Int64()-2 {
t.Fatal("Not expected balance, expected 0")
}
}
......@@ -697,7 +481,7 @@ func TestAccountingNotifyPayment(t *testing.T) {
store := mock.NewStateStore()
defer store.Close()
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, 1000, logger, store, nil, nil)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, nil, nil)
if err != nil {
t.Fatal(err)
}
......@@ -708,12 +492,12 @@ func TestAccountingNotifyPayment(t *testing.T) {
}
debtAmount := uint64(100)
err = acc.Debit(peer1Addr, debtAmount+testPaymentTolerance)
err = acc.Debit(peer1Addr, debtAmount+testPaymentTolerance.Uint64())
if err != nil {
t.Fatal(err)
}
err = acc.NotifyPayment(peer1Addr, debtAmount+testPaymentTolerance)
err = acc.NotifyPayment(peer1Addr, new(big.Int).SetUint64(debtAmount+testPaymentTolerance.Uint64()))
if err != nil {
t.Fatal(err)
}
......@@ -723,7 +507,7 @@ func TestAccountingNotifyPayment(t *testing.T) {
t.Fatal(err)
}
err = acc.NotifyPayment(peer1Addr, debtAmount+testPaymentTolerance+1)
err = acc.NotifyPayment(peer1Addr, new(big.Int).SetUint64(debtAmount+testPaymentTolerance.Uint64()+1))
if err != nil {
t.Fatal(err)
}
......@@ -732,10 +516,10 @@ func TestAccountingNotifyPayment(t *testing.T) {
type pricingMock struct {
called bool
peer swarm.Address
paymentThreshold uint64
paymentThreshold *big.Int
}
func (p *pricingMock) AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold uint64) error {
func (p *pricingMock) AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold *big.Int) error {
p.called = true
p.peer = peer
p.paymentThreshold = paymentThreshold
......@@ -750,7 +534,7 @@ func TestAccountingConnected(t *testing.T) {
pricing := &pricingMock{}
_, err := accounting.NewAccounting(testPaymentThreshold, 1000, 1000, logger, store, nil, pricing)
_, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, testPaymentEarly, logger, store, nil, pricing)
if err != nil {
t.Fatal(err)
}
......@@ -785,9 +569,9 @@ func TestAccountingNotifyPaymentThreshold(t *testing.T) {
defer store.Close()
pricing := &pricingMock{}
settlement := mockSettlement.NewSettlement()
settlement := mockSettlement.New()
acc, err := accounting.NewAccounting(testPaymentThreshold, 1000, 0, logger, store, settlement, pricing)
acc, err := accounting.NewAccounting(testPaymentThreshold, testPaymentTolerance, big.NewInt(0), logger, store, settlement, pricing)
if err != nil {
t.Fatal(err)
}
......@@ -800,7 +584,7 @@ func TestAccountingNotifyPaymentThreshold(t *testing.T) {
debt := uint64(50)
lowerThreshold := uint64(100)
err = acc.NotifyPaymentThreshold(peer1Addr, lowerThreshold)
err = acc.NotifyPaymentThreshold(peer1Addr, new(big.Int).SetUint64(lowerThreshold))
if err != nil {
t.Fatal(err)
}
......@@ -820,7 +604,7 @@ func TestAccountingNotifyPaymentThreshold(t *testing.T) {
t.Fatal(err)
}
if totalSent != debt {
if totalSent.Cmp(new(big.Int).SetUint64(debt)) != 0 {
t.Fatalf("paid wrong amount. got %d wanted %d", totalSent, debt)
}
}
......@@ -6,6 +6,7 @@ package mock
import (
"context"
"math/big"
"sync"
"github.com/ethersphere/bee/pkg/accounting"
......@@ -15,17 +16,17 @@ import (
// Service is the mock Accounting service.
type Service struct {
lock sync.Mutex
balances map[string]int64
balances map[string]*big.Int
reserveFunc func(ctx context.Context, peer swarm.Address, price uint64) error
releaseFunc func(peer swarm.Address, price uint64)
creditFunc func(peer swarm.Address, price uint64) error
debitFunc func(peer swarm.Address, price uint64) error
balanceFunc func(swarm.Address) (int64, error)
balancesFunc func() (map[string]int64, error)
compensatedBalanceFunc func(swarm.Address) (int64, error)
compensatedBalancesFunc func() (map[string]int64, error)
balanceFunc func(swarm.Address) (*big.Int, error)
balancesFunc func() (map[string]*big.Int, error)
compensatedBalanceFunc func(swarm.Address) (*big.Int, error)
compensatedBalancesFunc func() (map[string]*big.Int, error)
balanceSurplusFunc func(swarm.Address) (int64, error)
balanceSurplusFunc func(swarm.Address) (*big.Int, error)
}
// WithReserveFunc sets the mock Reserve function
......@@ -57,35 +58,35 @@ func WithDebitFunc(f func(peer swarm.Address, price uint64) error) Option {
}
// WithBalanceFunc sets the mock Balance function
func WithBalanceFunc(f func(swarm.Address) (int64, error)) Option {
func WithBalanceFunc(f func(swarm.Address) (*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.balanceFunc = f
})
}
// WithBalancesFunc sets the mock Balances function
func WithBalancesFunc(f func() (map[string]int64, error)) Option {
func WithBalancesFunc(f func() (map[string]*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.balancesFunc = f
})
}
// WithCompensatedBalanceFunc sets the mock Balance function
func WithCompensatedBalanceFunc(f func(swarm.Address) (int64, error)) Option {
func WithCompensatedBalanceFunc(f func(swarm.Address) (*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.compensatedBalanceFunc = f
})
}
// WithCompensatedBalancesFunc sets the mock Balances function
func WithCompensatedBalancesFunc(f func() (map[string]int64, error)) Option {
func WithCompensatedBalancesFunc(f func() (map[string]*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.compensatedBalancesFunc = f
})
}
// WithBalanceSurplusFunc sets the mock SurplusBalance function
func WithBalanceSurplusFunc(f func(swarm.Address) (int64, error)) Option {
func WithBalanceSurplusFunc(f func(swarm.Address) (*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.balanceSurplusFunc = f
})
......@@ -94,7 +95,7 @@ func WithBalanceSurplusFunc(f func(swarm.Address) (int64, error)) Option {
// NewAccounting creates the mock accounting implementation
func NewAccounting(opts ...Option) accounting.Interface {
mock := new(Service)
mock.balances = make(map[string]int64)
mock.balances = make(map[string]*big.Int)
for _, o := range opts {
o.apply(mock)
}
......@@ -123,7 +124,12 @@ func (s *Service) Credit(peer swarm.Address, price uint64) error {
}
s.lock.Lock()
defer s.lock.Unlock()
s.balances[peer.String()] -= int64(price)
if bal, ok := s.balances[peer.String()]; ok {
s.balances[peer.String()] = new(big.Int).Sub(bal, new(big.Int).SetUint64(price))
} else {
s.balances[peer.String()] = big.NewInt(-int64(price))
}
return nil
}
......@@ -134,22 +140,31 @@ func (s *Service) Debit(peer swarm.Address, price uint64) error {
}
s.lock.Lock()
defer s.lock.Unlock()
s.balances[peer.String()] += int64(price)
if bal, ok := s.balances[peer.String()]; ok {
s.balances[peer.String()] = new(big.Int).Add(bal, new(big.Int).SetUint64(price))
} else {
s.balances[peer.String()] = new(big.Int).SetUint64(price)
}
return nil
}
// Balance is the mock function wrapper that calls the set implementation
func (s *Service) Balance(peer swarm.Address) (int64, error) {
func (s *Service) Balance(peer swarm.Address) (*big.Int, error) {
if s.balanceFunc != nil {
return s.balanceFunc(peer)
}
s.lock.Lock()
defer s.lock.Unlock()
return s.balances[peer.String()], nil
if bal, ok := s.balances[peer.String()]; ok {
return bal, nil
} else {
return big.NewInt(0), nil
}
}
// Balances is the mock function wrapper that calls the set implementation
func (s *Service) Balances() (map[string]int64, error) {
func (s *Service) Balances() (map[string]*big.Int, error) {
if s.balancesFunc != nil {
return s.balancesFunc()
}
......@@ -157,7 +172,7 @@ func (s *Service) Balances() (map[string]int64, error) {
}
// CompensatedBalance is the mock function wrapper that calls the set implementation
func (s *Service) CompensatedBalance(peer swarm.Address) (int64, error) {
func (s *Service) CompensatedBalance(peer swarm.Address) (*big.Int, error) {
if s.compensatedBalanceFunc != nil {
return s.compensatedBalanceFunc(peer)
}
......@@ -167,7 +182,7 @@ func (s *Service) CompensatedBalance(peer swarm.Address) (int64, error) {
}
// CompensatedBalances is the mock function wrapper that calls the set implementation
func (s *Service) CompensatedBalances() (map[string]int64, error) {
func (s *Service) CompensatedBalances() (map[string]*big.Int, error) {
if s.compensatedBalancesFunc != nil {
return s.compensatedBalancesFunc()
}
......@@ -175,13 +190,13 @@ func (s *Service) CompensatedBalances() (map[string]int64, error) {
}
//
func (s *Service) SurplusBalance(peer swarm.Address) (int64, error) {
func (s *Service) SurplusBalance(peer swarm.Address) (*big.Int, error) {
if s.balanceFunc != nil {
return s.balanceSurplusFunc(peer)
}
s.lock.Lock()
defer s.lock.Unlock()
return 0, nil
return big.NewInt(0), nil
}
// Option is the option passed to the mock accounting service
......
......@@ -6,6 +6,7 @@ package debugapi
import (
"errors"
"math/big"
"net/http"
"github.com/ethersphere/bee/pkg/accounting"
......@@ -22,8 +23,8 @@ var (
)
type balanceResponse struct {
Peer string `json:"peer"`
Balance int64 `json:"balance"`
Peer string `json:"peer"`
Balance *big.Int `json:"balance"`
}
type balancesResponse struct {
......
......@@ -6,6 +6,7 @@ package debugapi_test
import (
"errors"
"math/big"
"net/http"
"reflect"
"testing"
......@@ -19,11 +20,11 @@ import (
)
func TestBalances(t *testing.T) {
compensatedBalancesFunc := func() (ret map[string]int64, err error) {
ret = make(map[string]int64)
ret["DEAD"] = 1000000000000000000
ret["BEEF"] = -100000000000000000
ret["PARTY"] = 0
compensatedBalancesFunc := func() (ret map[string]*big.Int, err error) {
ret = make(map[string]*big.Int)
ret["DEAD"] = big.NewInt(1000000000000000000)
ret["BEEF"] = big.NewInt(-100000000000000000)
ret["PARTY"] = big.NewInt(0)
return ret, err
}
testServer := newTestServer(t, testServerOptions{
......@@ -34,15 +35,15 @@ func TestBalances(t *testing.T) {
[]debugapi.BalanceResponse{
{
Peer: "DEAD",
Balance: 1000000000000000000,
Balance: big.NewInt(1000000000000000000),
},
{
Peer: "BEEF",
Balance: -100000000000000000,
Balance: big.NewInt(-100000000000000000),
},
{
Peer: "PARTY",
Balance: 0,
Balance: big.NewInt(0),
},
},
}
......@@ -61,7 +62,7 @@ func TestBalances(t *testing.T) {
func TestBalancesError(t *testing.T) {
wantErr := errors.New("ASDF")
compensatedBalancesFunc := func() (ret map[string]int64, err error) {
compensatedBalancesFunc := func() (ret map[string]*big.Int, err error) {
return nil, wantErr
}
testServer := newTestServer(t, testServerOptions{
......@@ -78,8 +79,8 @@ func TestBalancesError(t *testing.T) {
func TestBalancesPeers(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
compensatedBalanceFunc := func(swarm.Address) (int64, error) {
return 1000000000000000000, nil
compensatedBalanceFunc := func(swarm.Address) (*big.Int, error) {
return big.NewInt(100000000000000000), nil
}
testServer := newTestServer(t, testServerOptions{
AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)},
......@@ -88,7 +89,7 @@ func TestBalancesPeers(t *testing.T) {
jsonhttptest.Request(t, testServer.Client, http.MethodGet, "/balances/"+peer, http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(debugapi.BalanceResponse{
Peer: peer,
Balance: 1000000000000000000,
Balance: big.NewInt(100000000000000000),
}),
)
}
......@@ -96,8 +97,8 @@ func TestBalancesPeers(t *testing.T) {
func TestBalancesPeersError(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
wantErr := errors.New("Error")
compensatedBalanceFunc := func(swarm.Address) (int64, error) {
return 0, wantErr
compensatedBalanceFunc := func(swarm.Address) (*big.Int, error) {
return nil, wantErr
}
testServer := newTestServer(t, testServerOptions{
AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)},
......@@ -113,8 +114,8 @@ func TestBalancesPeersError(t *testing.T) {
func TestBalancesPeersNoBalance(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
compensatedBalanceFunc := func(swarm.Address) (int64, error) {
return 0, accounting.ErrPeerNoBalance
compensatedBalanceFunc := func(swarm.Address) (*big.Int, error) {
return nil, accounting.ErrPeerNoBalance
}
testServer := newTestServer(t, testServerOptions{
AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)},
......@@ -172,11 +173,11 @@ func equalBalances(a, b *debugapi.BalancesResponse) bool {
}
func TestConsumedBalances(t *testing.T) {
balancesFunc := func() (ret map[string]int64, err error) {
ret = make(map[string]int64)
ret["DEAD"] = 1000000000000000000
ret["BEEF"] = -100000000000000000
ret["PARTY"] = 0
balancesFunc := func() (ret map[string]*big.Int, err error) {
ret = make(map[string]*big.Int)
ret["DEAD"] = big.NewInt(1000000000000000000)
ret["BEEF"] = big.NewInt(-100000000000000000)
ret["PARTY"] = big.NewInt(0)
return ret, err
}
testServer := newTestServer(t, testServerOptions{
......@@ -187,15 +188,15 @@ func TestConsumedBalances(t *testing.T) {
[]debugapi.BalanceResponse{
{
Peer: "DEAD",
Balance: 1000000000000000000,
Balance: big.NewInt(1000000000000000000),
},
{
Peer: "BEEF",
Balance: -100000000000000000,
Balance: big.NewInt(-100000000000000000),
},
{
Peer: "PARTY",
Balance: 0,
Balance: big.NewInt(0),
},
},
}
......@@ -214,7 +215,7 @@ func TestConsumedBalances(t *testing.T) {
func TestConsumedError(t *testing.T) {
wantErr := errors.New("ASDF")
balancesFunc := func() (ret map[string]int64, err error) {
balancesFunc := func() (ret map[string]*big.Int, err error) {
return nil, wantErr
}
testServer := newTestServer(t, testServerOptions{
......@@ -231,8 +232,8 @@ func TestConsumedError(t *testing.T) {
func TestConsumedPeers(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
balanceFunc := func(swarm.Address) (int64, error) {
return 1000000000000000000, nil
balanceFunc := func(swarm.Address) (*big.Int, error) {
return big.NewInt(1000000000000000000), nil
}
testServer := newTestServer(t, testServerOptions{
AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)},
......@@ -241,7 +242,7 @@ func TestConsumedPeers(t *testing.T) {
jsonhttptest.Request(t, testServer.Client, http.MethodGet, "/consumed/"+peer, http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(debugapi.BalanceResponse{
Peer: peer,
Balance: 1000000000000000000,
Balance: big.NewInt(1000000000000000000),
}),
)
}
......@@ -249,8 +250,8 @@ func TestConsumedPeers(t *testing.T) {
func TestConsumedPeersError(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
wantErr := errors.New("Error")
balanceFunc := func(swarm.Address) (int64, error) {
return 0, wantErr
balanceFunc := func(swarm.Address) (*big.Int, error) {
return nil, wantErr
}
testServer := newTestServer(t, testServerOptions{
AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)},
......@@ -266,8 +267,8 @@ func TestConsumedPeersError(t *testing.T) {
func TestConsumedPeersNoBalance(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
balanceFunc := func(swarm.Address) (int64, error) {
return 0, accounting.ErrPeerNoBalance
balanceFunc := func(swarm.Address) (*big.Int, error) {
return nil, accounting.ErrPeerNoBalance
}
testServer := newTestServer(t, testServerOptions{
AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)},
......
......@@ -19,7 +19,6 @@ import (
p2pmock "github.com/ethersphere/bee/pkg/p2p/mock"
"github.com/ethersphere/bee/pkg/pingpong"
"github.com/ethersphere/bee/pkg/resolver"
settlementmock "github.com/ethersphere/bee/pkg/settlement/pseudosettle/mock"
chequebookmock "github.com/ethersphere/bee/pkg/settlement/swap/chequebook/mock"
swapmock "github.com/ethersphere/bee/pkg/settlement/swap/mock"
"github.com/ethersphere/bee/pkg/storage"
......@@ -42,7 +41,7 @@ type testServerOptions struct {
TopologyOpts []topologymock.Option
Tags *tags.Tags
AccountingOpts []accountingmock.Option
SettlementOpts []settlementmock.Option
SettlementOpts []swapmock.Option
ChequebookOpts []chequebookmock.Option
SwapOpts []swapmock.Option
}
......@@ -55,7 +54,7 @@ type testServer struct {
func newTestServer(t *testing.T, o testServerOptions) *testServer {
topologyDriver := topologymock.NewTopologyDriver(o.TopologyOpts...)
acc := accountingmock.NewAccounting(o.AccountingOpts...)
settlement := settlementmock.NewSettlement(o.SettlementOpts...)
settlement := swapmock.New(o.SettlementOpts...)
chequebook := chequebookmock.NewChequebook(o.ChequebookOpts...)
swapserv := swapmock.NewApiInterface(o.SwapOpts...)
s := debugapi.New(o.Overlay, o.PublicKey, o.PSSPublicKey, o.EthereumAddress, o.P2P, o.Pingpong, topologyDriver, o.Storer, logging.New(ioutil.Discard, 0), nil, o.Tags, acc, settlement, true, swapserv, chequebook)
......
......@@ -21,9 +21,9 @@ var (
)
type settlementResponse struct {
Peer string `json:"peer"`
SettlementReceived uint64 `json:"received"`
SettlementSent uint64 `json:"sent"`
Peer string `json:"peer"`
SettlementReceived *big.Int `json:"received"`
SettlementSent *big.Int `json:"sent"`
}
type settlementsResponse struct {
......@@ -58,9 +58,9 @@ func (s *server) settlementsHandler(w http.ResponseWriter, r *http.Request) {
settlementResponses[a] = settlementResponse{
Peer: a,
SettlementSent: b,
SettlementReceived: 0,
SettlementReceived: big.NewInt(0),
}
totalSent.Add(big.NewInt(int64(b)), totalSent)
totalSent.Add(b, totalSent)
}
for a, b := range settlementsReceived {
......@@ -71,11 +71,11 @@ func (s *server) settlementsHandler(w http.ResponseWriter, r *http.Request) {
} else {
settlementResponses[a] = settlementResponse{
Peer: a,
SettlementSent: 0,
SettlementSent: big.NewInt(0),
SettlementReceived: b,
}
}
totalReceived.Add(big.NewInt(int64(b)), totalReceived)
totalReceived.Add(b, totalReceived)
}
settlementResponsesArray := make([]settlementResponse, len(settlementResponses))
......
......@@ -14,23 +14,23 @@ import (
"github.com/ethersphere/bee/pkg/debugapi"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/settlement/pseudosettle/mock"
"github.com/ethersphere/bee/pkg/settlement/swap/mock"
"github.com/ethersphere/bee/pkg/swarm"
)
func TestSettlements(t *testing.T) {
settlementsSentFunc := func() (ret map[string]uint64, err error) {
ret = make(map[string]uint64)
ret["DEAD"] = 10000
ret["BEEF"] = 20000
ret["FFFF"] = 50000
settlementsSentFunc := func() (ret map[string]*big.Int, err error) {
ret = make(map[string]*big.Int)
ret["DEAD"] = big.NewInt(10000)
ret["BEEF"] = big.NewInt(20000)
ret["FFFF"] = big.NewInt(50000)
return ret, err
}
settlementsRecvFunc := func() (ret map[string]uint64, err error) {
ret = make(map[string]uint64)
ret["BEEF"] = 10000
ret["EEEE"] = 5000
settlementsRecvFunc := func() (ret map[string]*big.Int, err error) {
ret = make(map[string]*big.Int)
ret["BEEF"] = big.NewInt(10000)
ret["EEEE"] = big.NewInt(5000)
return ret, err
}
......@@ -44,23 +44,23 @@ func TestSettlements(t *testing.T) {
Settlements: []debugapi.SettlementResponse{
{
Peer: "DEAD",
SettlementReceived: 0,
SettlementSent: 10000,
SettlementReceived: big.NewInt(0),
SettlementSent: big.NewInt(10000),
},
{
Peer: "BEEF",
SettlementReceived: 10000,
SettlementSent: 20000,
SettlementReceived: big.NewInt(10000),
SettlementSent: big.NewInt(20000),
},
{
Peer: "FFFF",
SettlementReceived: 0,
SettlementSent: 50000,
SettlementReceived: big.NewInt(0),
SettlementSent: big.NewInt(50000),
},
{
Peer: "EEEE",
SettlementReceived: 5000,
SettlementSent: 0,
SettlementReceived: big.NewInt(5000),
SettlementSent: big.NewInt(0),
},
},
}
......@@ -79,7 +79,7 @@ func TestSettlements(t *testing.T) {
func TestSettlementsError(t *testing.T) {
wantErr := errors.New("New errors")
settlementsSentFunc := func() (map[string]uint64, error) {
settlementsSentFunc := func() (map[string]*big.Int, error) {
return nil, wantErr
}
testServer := newTestServer(t, testServerOptions{
......@@ -96,8 +96,8 @@ func TestSettlementsError(t *testing.T) {
func TestSettlementsPeers(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
settlementSentFunc := func(swarm.Address) (uint64, error) {
return 1000000000000000000, nil
settlementSentFunc := func(swarm.Address) (*big.Int, error) {
return big.NewInt(1000000000000000000), nil
}
testServer := newTestServer(t, testServerOptions{
SettlementOpts: []mock.Option{mock.WithSettlementSentFunc(settlementSentFunc)},
......@@ -106,8 +106,8 @@ func TestSettlementsPeers(t *testing.T) {
jsonhttptest.Request(t, testServer.Client, http.MethodGet, "/settlements/"+peer, http.StatusOK,
jsonhttptest.WithExpectedJSONResponse(debugapi.SettlementResponse{
Peer: peer,
SettlementSent: 1000000000000000000,
SettlementReceived: 0,
SettlementSent: big.NewInt(1000000000000000000),
SettlementReceived: big.NewInt(0),
}),
)
}
......@@ -115,8 +115,8 @@ func TestSettlementsPeers(t *testing.T) {
func TestSettlementsPeersError(t *testing.T) {
peer := "bff2c89e85e78c38bd89fca1acc996afb876c21bf5a8482ad798ce15f1c223fa"
wantErr := errors.New("Error")
settlementSentFunc := func(swarm.Address) (uint64, error) {
return 0, wantErr
settlementSentFunc := func(swarm.Address) (*big.Int, error) {
return nil, wantErr
}
testServer := newTestServer(t, testServerOptions{
SettlementOpts: []mock.Option{mock.WithSettlementSentFunc(settlementSentFunc)},
......
......@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"log"
"math/big"
"net"
"net/http"
"path/filepath"
......@@ -97,14 +98,14 @@ type Options struct {
TracingEndpoint string
TracingServiceName string
GlobalPinningEnabled bool
PaymentThreshold uint64
PaymentTolerance uint64
PaymentEarly uint64
PaymentThreshold string
PaymentTolerance string
PaymentEarly string
ResolverConnectionCfgs []multiresolver.ConnectionConfig
GatewayMode bool
SwapEndpoint string
SwapFactoryAddress string
SwapInitialDeposit uint64
SwapInitialDeposit string
SwapEnable bool
}
......@@ -198,12 +199,16 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
}
}
swapInitialDeposit, ok := new(big.Int).SetString(o.SwapInitialDeposit, 10)
if !ok {
return nil, fmt.Errorf("invalid initial deposit: %s", swapInitialDeposit)
}
// initialize chequebook logic
chequebookService, err = chequebook.Init(p2pCtx,
chequebookFactory,
stateStore,
logger,
o.SwapInitialDeposit,
swapInitialDeposit,
transactionService,
swapBackend,
chainID.Int64(),
......@@ -300,12 +305,32 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
settlement = pseudosettleService
}
pricing := pricing.New(p2ps, logger, o.PaymentThreshold)
paymentThreshold, ok := new(big.Int).SetString(o.PaymentThreshold, 10)
if !ok {
return nil, fmt.Errorf("invalid payment threshold: %s", paymentThreshold)
}
pricing := pricing.New(p2ps, logger, paymentThreshold)
if err = p2ps.AddProtocol(pricing.Protocol()); err != nil {
return nil, fmt.Errorf("pricing service: %w", err)
}
acc, err := accounting.NewAccounting(o.PaymentThreshold, o.PaymentTolerance, o.PaymentEarly, logger, stateStore, settlement, pricing)
paymentTolerance, ok := new(big.Int).SetString(o.PaymentTolerance, 10)
if !ok {
return nil, fmt.Errorf("invalid payment tolerance: %s", paymentTolerance)
}
paymentEarly, ok := new(big.Int).SetString(o.PaymentEarly, 10)
if !ok {
return nil, fmt.Errorf("invalid payment early: %s", paymentEarly)
}
acc, err := accounting.NewAccounting(
paymentThreshold,
paymentTolerance,
paymentEarly,
logger,
stateStore,
settlement,
pricing,
)
if err != nil {
return nil, fmt.Errorf("accounting: %w", err)
}
......
......@@ -23,7 +23,7 @@ var _ = math.Inf
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type AnnouncePaymentThreshold struct {
PaymentThreshold uint64 `protobuf:"varint,1,opt,name=PaymentThreshold,proto3" json:"PaymentThreshold,omitempty"`
PaymentThreshold []byte `protobuf:"bytes,1,opt,name=PaymentThreshold,proto3" json:"PaymentThreshold,omitempty"`
}
func (m *AnnouncePaymentThreshold) Reset() { *m = AnnouncePaymentThreshold{} }
......@@ -59,11 +59,11 @@ func (m *AnnouncePaymentThreshold) XXX_DiscardUnknown() {
var xxx_messageInfo_AnnouncePaymentThreshold proto.InternalMessageInfo
func (m *AnnouncePaymentThreshold) GetPaymentThreshold() uint64 {
func (m *AnnouncePaymentThreshold) GetPaymentThreshold() []byte {
if m != nil {
return m.PaymentThreshold
}
return 0
return nil
}
func init() {
......@@ -78,10 +78,10 @@ var fileDescriptor_ec4cc93d045d43d0 = []byte{
0xce, 0xcc, 0x4b, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x72, 0x95, 0xdc, 0xb8,
0x24, 0x1c, 0xf3, 0xf2, 0xf2, 0x4b, 0xf3, 0x92, 0x53, 0x03, 0x12, 0x2b, 0x73, 0x53, 0xf3, 0x4a,
0x42, 0x32, 0x8a, 0x52, 0x8b, 0x33, 0xf2, 0x73, 0x52, 0x84, 0xb4, 0xb8, 0x04, 0xd0, 0xc5, 0x24,
0x18, 0x15, 0x18, 0x35, 0x58, 0x82, 0x30, 0xc4, 0x9d, 0x64, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0,
0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x30, 0xc4, 0x9d, 0x64, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0,
0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8,
0xf1, 0x58, 0x8e, 0x21, 0x8a, 0xa9, 0x20, 0x29, 0x89, 0x0d, 0x6c, 0xab, 0x31, 0x20, 0x00, 0x00,
0xff, 0xff, 0x70, 0x59, 0x58, 0xcf, 0x86, 0x00, 0x00, 0x00,
0xff, 0xff, 0x50, 0xca, 0x0e, 0x0a, 0x86, 0x00, 0x00, 0x00,
}
func (m *AnnouncePaymentThreshold) Marshal() (dAtA []byte, err error) {
......@@ -104,10 +104,12 @@ func (m *AnnouncePaymentThreshold) MarshalToSizedBuffer(dAtA []byte) (int, error
_ = i
var l int
_ = l
if m.PaymentThreshold != 0 {
i = encodeVarintPricing(dAtA, i, uint64(m.PaymentThreshold))
if len(m.PaymentThreshold) > 0 {
i -= len(m.PaymentThreshold)
copy(dAtA[i:], m.PaymentThreshold)
i = encodeVarintPricing(dAtA, i, uint64(len(m.PaymentThreshold)))
i--
dAtA[i] = 0x8
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
......@@ -129,8 +131,9 @@ func (m *AnnouncePaymentThreshold) Size() (n int) {
}
var l int
_ = l
if m.PaymentThreshold != 0 {
n += 1 + sovPricing(uint64(m.PaymentThreshold))
l = len(m.PaymentThreshold)
if l > 0 {
n += 1 + l + sovPricing(uint64(l))
}
return n
}
......@@ -171,10 +174,10 @@ func (m *AnnouncePaymentThreshold) Unmarshal(dAtA []byte) error {
}
switch fieldNum {
case 1:
if wireType != 0 {
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PaymentThreshold", wireType)
}
m.PaymentThreshold = 0
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPricing
......@@ -184,11 +187,26 @@ func (m *AnnouncePaymentThreshold) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
m.PaymentThreshold |= uint64(b&0x7F) << shift
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthPricing
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthPricing
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.PaymentThreshold = append(m.PaymentThreshold[:0], dAtA[iNdEx:postIndex]...)
if m.PaymentThreshold == nil {
m.PaymentThreshold = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPricing(dAtA[iNdEx:])
......
......@@ -9,5 +9,5 @@ package pricing;
option go_package = "pb";
message AnnouncePaymentThreshold {
uint64 PaymentThreshold = 1;
bytes PaymentThreshold = 1;
}
......@@ -7,6 +7,7 @@ package pricing
import (
"context"
"fmt"
"math/big"
"time"
"github.com/ethersphere/bee/pkg/logging"
......@@ -26,22 +27,22 @@ var _ Interface = (*Service)(nil)
// Interface is the main interface of the pricing protocol
type Interface interface {
AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold uint64) error
AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold *big.Int) error
}
// PaymentThresholdObserver is used for being notified of payment threshold updates
type PaymentThresholdObserver interface {
NotifyPaymentThreshold(peer swarm.Address, paymentThreshold uint64) error
NotifyPaymentThreshold(peer swarm.Address, paymentThreshold *big.Int) error
}
type Service struct {
streamer p2p.Streamer
logger logging.Logger
paymentThreshold uint64
paymentThreshold *big.Int
paymentThresholdObserver PaymentThresholdObserver
}
func New(streamer p2p.Streamer, logger logging.Logger, paymentThreshold uint64) *Service {
func New(streamer p2p.Streamer, logger logging.Logger, paymentThreshold *big.Int) *Service {
return &Service{
streamer: streamer,
logger: logger,
......@@ -81,7 +82,7 @@ func (s *Service) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) (e
}
s.logger.Tracef("received payment threshold announcement from peer %v of %d", p.Address, req.PaymentThreshold)
return s.paymentThresholdObserver.NotifyPaymentThreshold(p.Address, req.PaymentThreshold)
return s.paymentThresholdObserver.NotifyPaymentThreshold(p.Address, big.NewInt(0).SetBytes(req.PaymentThreshold))
}
func (s *Service) init(ctx context.Context, p p2p.Peer) error {
......@@ -93,7 +94,7 @@ func (s *Service) init(ctx context.Context, p p2p.Peer) error {
}
// AnnouncePaymentThreshold announces the payment threshold to per
func (s *Service) AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold uint64) error {
func (s *Service) AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold *big.Int) error {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
......@@ -112,7 +113,7 @@ func (s *Service) AnnouncePaymentThreshold(ctx context.Context, peer swarm.Addre
s.logger.Tracef("sending payment threshold announcement to peer %v of %d", peer, paymentThreshold)
w := protobuf.NewWriter(stream)
err = w.WriteMsgWithContext(ctx, &pb.AnnouncePaymentThreshold{
PaymentThreshold: paymentThreshold,
PaymentThreshold: paymentThreshold.Bytes(),
})
return err
......
......@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"io/ioutil"
"math/big"
"testing"
"github.com/ethersphere/bee/pkg/logging"
......@@ -21,10 +22,10 @@ import (
type testObserver struct {
called bool
peer swarm.Address
paymentThreshold uint64
paymentThreshold *big.Int
}
func (t *testObserver) NotifyPaymentThreshold(peer swarm.Address, paymentThreshold uint64) error {
func (t *testObserver) NotifyPaymentThreshold(peer swarm.Address, paymentThreshold *big.Int) error {
t.called = true
t.peer = peer
t.paymentThreshold = paymentThreshold
......@@ -33,7 +34,7 @@ func (t *testObserver) NotifyPaymentThreshold(peer swarm.Address, paymentThresho
func TestAnnouncePaymentThreshold(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
testThreshold := uint64(100000)
testThreshold := big.NewInt(100000)
observer := &testObserver{}
recipient := pricing.New(nil, logger, testThreshold)
......@@ -46,7 +47,7 @@ func TestAnnouncePaymentThreshold(t *testing.T) {
payer := pricing.New(recorder, logger, testThreshold)
peerID := swarm.MustParseHexAddress("9ee7add7")
paymentThreshold := uint64(10000)
paymentThreshold := big.NewInt(10000)
err := payer.AnnouncePaymentThreshold(context.Background(), peerID, paymentThreshold)
if err != nil {
......@@ -76,8 +77,8 @@ func TestAnnouncePaymentThreshold(t *testing.T) {
t.Fatalf("got %v messages, want %v", len(messages), 1)
}
sentPaymentThreshold := messages[0].(*pb.AnnouncePaymentThreshold).PaymentThreshold
if sentPaymentThreshold != paymentThreshold {
sentPaymentThreshold := big.NewInt(0).SetBytes(messages[0].(*pb.AnnouncePaymentThreshold).PaymentThreshold)
if sentPaymentThreshold.Cmp(paymentThreshold) != 0 {
t.Fatalf("got message with amount %v, want %v", sentPaymentThreshold, paymentThreshold)
}
......@@ -85,7 +86,7 @@ func TestAnnouncePaymentThreshold(t *testing.T) {
t.Fatal("expected observer to be called")
}
if observer.paymentThreshold != paymentThreshold {
if observer.paymentThreshold.Cmp(paymentThreshold) != 0 {
t.Fatalf("observer called with wrong paymentThreshold. got %d, want %d", observer.paymentThreshold, paymentThreshold)
}
......
......@@ -76,7 +76,7 @@ func TestSendChunkAndReceiveReceipt(t *testing.T) {
t.Fatal(err)
}
if balance != -int64(fixedPrice) {
if balance.Int64() != -int64(fixedPrice) {
t.Fatalf("unexpected balance on pivot. want %d got %d", -int64(fixedPrice), balance)
}
......@@ -85,7 +85,7 @@ func TestSendChunkAndReceiveReceipt(t *testing.T) {
t.Fatal(err)
}
if balance != int64(fixedPrice) {
if balance.Int64() != int64(fixedPrice) {
t.Fatalf("unexpected balance on peer. want %d got %d", int64(fixedPrice), balance)
}
}
......@@ -155,7 +155,7 @@ func TestPushChunkToClosest(t *testing.T) {
t.Fatal(err)
}
if balance != -int64(fixedPrice) {
if balance.Int64() != -int64(fixedPrice) {
t.Fatalf("unexpected balance on pivot. want %d got %d", -int64(fixedPrice), balance)
}
......@@ -164,7 +164,7 @@ func TestPushChunkToClosest(t *testing.T) {
t.Fatal(err)
}
if balance != int64(fixedPrice) {
if balance.Int64() != int64(fixedPrice) {
t.Fatalf("unexpected balance on peer. want %d got %d", int64(fixedPrice), balance)
}
......@@ -273,7 +273,7 @@ func TestPushChunkToNextClosest(t *testing.T) {
t.Fatal(err)
}
if balance != -int64(fixedPrice) {
if balance.Int64() != -int64(fixedPrice) {
t.Fatalf("unexpected balance on pivot. want %d got %d", -int64(fixedPrice), balance)
}
......@@ -282,7 +282,7 @@ func TestPushChunkToNextClosest(t *testing.T) {
t.Fatal(err)
}
if balance2 != int64(fixedPrice) {
if balance2.Int64() != int64(fixedPrice) {
t.Fatalf("unexpected balance on peer2. want %d got %d", int64(fixedPrice), balance2)
}
......@@ -291,7 +291,7 @@ func TestPushChunkToNextClosest(t *testing.T) {
t.Fatal(err)
}
if balance1 != 0 {
if balance1.Int64() != 0 {
t.Fatalf("unexpected balance on peer1. want %d got %d", 0, balance1)
}
}
......@@ -354,7 +354,7 @@ func TestHandler(t *testing.T) {
t.Fatal(err)
}
if balance != -int64(fixedPrice) {
if balance.Int64() != -int64(fixedPrice) {
t.Fatalf("unexpected balance on trigger. want %d got %d", -int64(fixedPrice), balance)
}
......@@ -364,7 +364,7 @@ func TestHandler(t *testing.T) {
t.Fatal(err)
}
if balance != int64(fixedPrice) {
if balance.Int64() != int64(fixedPrice) {
t.Fatalf("unexpected balance on pivot. want %d got %d", int64(fixedPrice), balance)
}
......@@ -373,7 +373,7 @@ func TestHandler(t *testing.T) {
t.Fatal(err)
}
if balance != -int64(fixedPrice) {
if balance.Int64() != -int64(fixedPrice) {
t.Fatalf("unexpected balance on pivot. want %d got %d", -int64(fixedPrice), balance)
}
......@@ -382,7 +382,7 @@ func TestHandler(t *testing.T) {
t.Fatal(err)
}
if balance != int64(fixedPrice) {
if balance.Int64() != int64(fixedPrice) {
t.Fatalf("unexpected balance on closest. want %d got %d", int64(fixedPrice), balance)
}
}
......
......@@ -120,12 +120,12 @@ func TestDelivery(t *testing.T) {
}
clientBalance, _ := clientMockAccounting.Balance(peerID)
if clientBalance != -int64(price) {
if clientBalance.Int64() != -int64(price) {
t.Fatalf("unexpected balance on client. want %d got %d", -price, clientBalance)
}
serverBalance, _ := serverMockAccounting.Balance(peerID)
if serverBalance != int64(price) {
if serverBalance.Int64() != int64(price) {
t.Fatalf("unexpected balance on server. want %d got %d", price, serverBalance)
}
}
......@@ -418,12 +418,12 @@ func TestRetrievePreemptiveRetry(t *testing.T) {
}
clientServer1Balance, _ := clientMockAccounting.Balance(serverAddress1)
if clientServer1Balance != 0 {
if clientServer1Balance.Int64() != 0 {
t.Fatalf("unexpected balance on client. want %d got %d", -price, clientServer1Balance)
}
clientServer2Balance, _ := clientMockAccounting.Balance(serverAddress2)
if clientServer2Balance != -int64(price) {
if clientServer2Balance.Int64() != -int64(price) {
t.Fatalf("unexpected balance on client. want %d got %d", -price, clientServer2Balance)
}
......@@ -432,12 +432,12 @@ func TestRetrievePreemptiveRetry(t *testing.T) {
time.Sleep(2 * time.Second)
clientServer1Balance, _ = clientMockAccounting.Balance(serverAddress1)
if clientServer1Balance != -int64(price) {
if clientServer1Balance.Int64() != -int64(price) {
t.Fatalf("unexpected balance on client. want %d got %d", -price, clientServer1Balance)
}
clientServer2Balance, _ = clientMockAccounting.Balance(serverAddress2)
if clientServer2Balance != -int64(price) {
if clientServer2Balance.Int64() != -int64(price) {
t.Fatalf("unexpected balance on client. want %d got %d", -price, clientServer2Balance)
}
})
......
......@@ -7,6 +7,7 @@ package settlement
import (
"context"
"errors"
"math/big"
"github.com/ethersphere/bee/pkg/swarm"
)
......@@ -19,18 +20,18 @@ var (
type Interface interface {
// Pay initiates a payment to the given peer
// It should return without error it is likely that the payment worked
Pay(ctx context.Context, peer swarm.Address, amount uint64) error
Pay(ctx context.Context, peer swarm.Address, amount *big.Int) error
// TotalSent returns the total amount sent to a peer
TotalSent(peer swarm.Address) (totalSent uint64, err error)
TotalSent(peer swarm.Address) (totalSent *big.Int, err error)
// TotalReceived returns the total amount received from a peer
TotalReceived(peer swarm.Address) (totalSent uint64, err error)
TotalReceived(peer swarm.Address) (totalSent *big.Int, err error)
// SettlementsSent returns sent settlements for each individual known peer
SettlementsSent() (map[string]uint64, error)
SettlementsSent() (map[string]*big.Int, error)
// SettlementsReceived returns received settlements for each individual known peer
SettlementsReceived() (map[string]uint64, error)
SettlementsReceived() (map[string]*big.Int, error)
// SetNotifyPaymentFunc sets the NotifyPaymentFunc to notify
SetNotifyPaymentFunc(notifyPaymentFunc NotifyPaymentFunc)
}
// NotifyPaymentFunc is called when a payment from peer was successfully received
type NotifyPaymentFunc func(peer swarm.Address, amount uint64) error
type NotifyPaymentFunc func(peer swarm.Address, amount *big.Int) error
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mock
import (
"context"
"sync"
"github.com/ethersphere/bee/pkg/settlement"
"github.com/ethersphere/bee/pkg/swarm"
)
// Service is the mock settlement service.
type Service struct {
lock sync.Mutex
settlementsSent map[string]uint64
settlementsRecv map[string]uint64
settlementSentFunc func(swarm.Address) (uint64, error)
settlementRecvFunc func(swarm.Address) (uint64, error)
settlementsSentFunc func() (map[string]uint64, error)
settlementsRecvFunc func() (map[string]uint64, error)
payFunc func(context.Context, swarm.Address, uint64) error
}
// WithsettlementFunc sets the mock settlement function
func WithSettlementSentFunc(f func(swarm.Address) (uint64, error)) Option {
return optionFunc(func(s *Service) {
s.settlementSentFunc = f
})
}
func WithSettlementRecvFunc(f func(swarm.Address) (uint64, error)) Option {
return optionFunc(func(s *Service) {
s.settlementRecvFunc = f
})
}
// WithsettlementsFunc sets the mock settlements function
func WithSettlementsSentFunc(f func() (map[string]uint64, error)) Option {
return optionFunc(func(s *Service) {
s.settlementsSentFunc = f
})
}
func WithSettlementsRecvFunc(f func() (map[string]uint64, error)) Option {
return optionFunc(func(s *Service) {
s.settlementsRecvFunc = f
})
}
func WithPayFunc(f func(context.Context, swarm.Address, uint64) error) Option {
return optionFunc(func(s *Service) {
s.payFunc = f
})
}
// Newsettlement creates the mock settlement implementation
func NewSettlement(opts ...Option) settlement.Interface {
mock := new(Service)
mock.settlementsSent = make(map[string]uint64)
mock.settlementsRecv = make(map[string]uint64)
for _, o := range opts {
o.apply(mock)
}
return mock
}
func (s *Service) Pay(c context.Context, peer swarm.Address, amount uint64) error {
if s.payFunc != nil {
return s.payFunc(c, peer, amount)
}
s.settlementsSent[peer.String()] += amount
return nil
}
func (s *Service) TotalSent(peer swarm.Address) (totalSent uint64, err error) {
if s.settlementSentFunc != nil {
return s.settlementSentFunc(peer)
}
s.lock.Lock()
defer s.lock.Unlock()
return s.settlementsSent[peer.String()], nil
}
func (s *Service) TotalReceived(peer swarm.Address) (totalSent uint64, err error) {
if s.settlementRecvFunc != nil {
return s.settlementRecvFunc(peer)
}
s.lock.Lock()
defer s.lock.Unlock()
return s.settlementsRecv[peer.String()], nil
}
// settlements is the mock function wrapper that calls the set implementation
func (s *Service) SettlementsSent() (map[string]uint64, error) {
if s.settlementsSentFunc != nil {
return s.settlementsSentFunc()
}
s.lock.Lock()
defer s.lock.Unlock()
return s.settlementsSent, nil
}
func (s *Service) SettlementsReceived() (map[string]uint64, error) {
if s.settlementsRecvFunc != nil {
return s.settlementsRecvFunc()
}
s.lock.Lock()
defer s.lock.Unlock()
return s.settlementsRecv, nil
}
func (s *Service) SetNotifyPaymentFunc(settlement.NotifyPaymentFunc) {
}
// Option is the option passed to the mock settlement service
type Option interface {
apply(*Service)
}
type optionFunc func(*Service)
func (f optionFunc) apply(r *Service) { f(r) }
......@@ -8,6 +8,7 @@ import (
"context"
"errors"
"fmt"
"math/big"
"strings"
"time"
......@@ -97,19 +98,19 @@ func (s *Service) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) (e
if !errors.Is(err, settlement.ErrPeerNoSettlements) {
return err
}
totalReceived = 0
totalReceived = big.NewInt(0)
}
err = s.store.Put(totalKey(p.Address, SettlementReceivedPrefix), totalReceived+req.Amount)
err = s.store.Put(totalKey(p.Address, SettlementReceivedPrefix), totalReceived.Add(totalReceived, new(big.Int).SetUint64(req.Amount)))
if err != nil {
return err
}
return s.notifyPaymentFunc(p.Address, req.Amount)
return s.notifyPaymentFunc(p.Address, new(big.Int).SetUint64(req.Amount))
}
// Pay initiates a payment to the given peer
func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) error {
func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount *big.Int) error {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
......@@ -128,7 +129,7 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er
s.logger.Tracef("sending payment message to peer %v of %d", peer, amount)
w := protobuf.NewWriter(stream)
err = w.WriteMsgWithContext(ctx, &pb.Payment{
Amount: amount,
Amount: amount.Uint64(),
})
if err != nil {
return err
......@@ -138,13 +139,15 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er
if !errors.Is(err, settlement.ErrPeerNoSettlements) {
return err
}
totalSent = 0
totalSent = big.NewInt(0)
}
err = s.store.Put(totalKey(peer, SettlementSentPrefix), totalSent+amount)
err = s.store.Put(totalKey(peer, SettlementSentPrefix), totalSent.Add(totalSent, amount))
if err != nil {
return err
}
s.metrics.TotalSentPseudoSettlements.Add(float64(amount))
amountFloat, _ := new(big.Float).SetInt(amount).Float64()
s.metrics.TotalSentPseudoSettlements.Add(amountFloat)
return nil
}
......@@ -154,41 +157,41 @@ func (s *Service) SetNotifyPaymentFunc(notifyPaymentFunc settlement.NotifyPaymen
}
// TotalSent returns the total amount sent to a peer
func (s *Service) TotalSent(peer swarm.Address) (totalSent uint64, err error) {
func (s *Service) TotalSent(peer swarm.Address) (totalSent *big.Int, err error) {
key := totalKey(peer, SettlementSentPrefix)
err = s.store.Get(key, &totalSent)
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
return 0, settlement.ErrPeerNoSettlements
return nil, settlement.ErrPeerNoSettlements
}
return 0, err
return nil, err
}
return totalSent, nil
}
// TotalReceived returns the total amount received from a peer
func (s *Service) TotalReceived(peer swarm.Address) (totalReceived uint64, err error) {
func (s *Service) TotalReceived(peer swarm.Address) (totalReceived *big.Int, err error) {
key := totalKey(peer, SettlementReceivedPrefix)
err = s.store.Get(key, &totalReceived)
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
return 0, settlement.ErrPeerNoSettlements
return nil, settlement.ErrPeerNoSettlements
}
return 0, err
return nil, err
}
return totalReceived, nil
}
// AllSettlements returns all stored settlement values for a given type of prefix (sent or received)
func (s *Service) SettlementsSent() (map[string]uint64, error) {
sent := make(map[string]uint64)
// SettlementsSent returns all stored sent settlement values for a given type of prefix
func (s *Service) SettlementsSent() (map[string]*big.Int, error) {
sent := make(map[string]*big.Int)
err := s.store.Iterate(SettlementSentPrefix, func(key, val []byte) (stop bool, err error) {
addr, err := totalKeyPeer(key, SettlementSentPrefix)
if err != nil {
return false, fmt.Errorf("parse address from key: %s: %w", string(key), err)
}
if _, ok := sent[addr.String()]; !ok {
var storevalue uint64
var storevalue *big.Int
err = s.store.Get(totalKey(addr, SettlementSentPrefix), &storevalue)
if err != nil {
return false, fmt.Errorf("get peer %s settlement balance: %w", addr.String(), err)
......@@ -204,15 +207,16 @@ func (s *Service) SettlementsSent() (map[string]uint64, error) {
return sent, nil
}
func (s *Service) SettlementsReceived() (map[string]uint64, error) {
received := make(map[string]uint64)
// SettlementsReceived returns all stored received settlement values for a given type of prefix
func (s *Service) SettlementsReceived() (map[string]*big.Int, error) {
received := make(map[string]*big.Int)
err := s.store.Iterate(SettlementReceivedPrefix, func(key, val []byte) (stop bool, err error) {
addr, err := totalKeyPeer(key, SettlementReceivedPrefix)
if err != nil {
return false, fmt.Errorf("parse address from key: %s: %w", string(key), err)
}
if _, ok := received[addr.String()]; !ok {
var storevalue uint64
var storevalue *big.Int
err = s.store.Get(totalKey(addr, SettlementReceivedPrefix), &storevalue)
if err != nil {
return false, fmt.Errorf("get peer %s settlement balance: %w", addr.String(), err)
......
......@@ -8,6 +8,7 @@ import (
"bytes"
"context"
"io/ioutil"
"math/big"
"testing"
"time"
......@@ -23,7 +24,7 @@ import (
type testObserver struct {
called chan struct{}
peer swarm.Address
amount uint64
amount *big.Int
}
func newTestObserver() *testObserver {
......@@ -32,7 +33,7 @@ func newTestObserver() *testObserver {
}
}
func (t *testObserver) NotifyPayment(peer swarm.Address, amount uint64) error {
func (t *testObserver) NotifyPayment(peer swarm.Address, amount *big.Int) error {
close(t.called)
t.peer = peer
t.amount = amount
......@@ -59,7 +60,7 @@ func TestPayment(t *testing.T) {
payer := pseudosettle.New(recorder, logger, storePayer)
peerID := swarm.MustParseHexAddress("9ee7add7")
amount := uint64(10000)
amount := big.NewInt(10000)
err := payer.Pay(context.Background(), peerID, amount)
if err != nil {
......@@ -94,7 +95,7 @@ func TestPayment(t *testing.T) {
}
sentAmount := messages[0].(*pb.Payment).Amount
if sentAmount != amount {
if sentAmount != amount.Uint64() {
t.Fatalf("got message with amount %v, want %v", sentAmount, amount)
}
......@@ -104,7 +105,7 @@ func TestPayment(t *testing.T) {
t.Fatal("expected observer to be called")
}
if observer.amount != amount {
if observer.amount.Cmp(amount) != 0 {
t.Fatalf("observer called with wrong amount. got %d, want %d", observer.amount, amount)
}
......@@ -117,7 +118,7 @@ func TestPayment(t *testing.T) {
t.Fatal(err)
}
if totalSent != sentAmount {
if totalSent.Cmp(new(big.Int).SetUint64(sentAmount)) != 0 {
t.Fatalf("stored wrong totalSent. got %d, want %d", totalSent, sentAmount)
}
......@@ -126,7 +127,7 @@ func TestPayment(t *testing.T) {
t.Fatal(err)
}
if totalReceived != sentAmount {
if totalReceived.Cmp(new(big.Int).SetUint64(sentAmount)) != 0 {
t.Fatalf("stored wrong totalReceived. got %d, want %d", totalReceived, sentAmount)
}
}
......@@ -22,7 +22,7 @@ const chequebookKey = "chequebook"
func checkBalance(
ctx context.Context,
logger logging.Logger,
swapInitialDeposit uint64,
swapInitialDeposit *big.Int,
swapBackend transaction.Backend,
chainId int64,
overlayEthAddress common.Address,
......@@ -47,7 +47,7 @@ func checkBalance(
return err
}
if balance.Cmp(big.NewInt(int64(swapInitialDeposit))) < 0 {
if balance.Cmp(swapInitialDeposit) < 0 {
logger.Warningf("cannot continue until there is sufficient ETH and BZZ available on %x", overlayEthAddress)
if chainId == 5 {
logger.Warningf("on the test network you can get both Goerli ETH and Goerli BZZ from https://faucet.ethswarm.org?address=%x", overlayEthAddress)
......@@ -70,7 +70,7 @@ func Init(
chequebookFactory Factory,
stateStore storage.StateStorer,
logger logging.Logger,
swapInitialDeposit uint64,
swapInitialDeposit *big.Int,
transactionService transaction.Service,
swapBackend transaction.Backend,
chainId int64,
......@@ -97,7 +97,7 @@ func Init(
}
logger.Info("no chequebook found, deploying new one.")
if swapInitialDeposit != 0 {
if swapInitialDeposit.Cmp(big.NewInt(0)) != 0 {
err = checkBalance(ctx, logger, swapInitialDeposit, swapBackend, chainId, overlayEthAddress, erc20BindingFunc, erc20Address, 20*time.Second, 10)
if err != nil {
return nil, err
......@@ -130,9 +130,9 @@ func Init(
return nil, err
}
if swapInitialDeposit != 0 {
if swapInitialDeposit.Cmp(big.NewInt(0)) != 0 {
logger.Infof("depositing %d token into new chequebook", swapInitialDeposit)
depositHash, err := chequebookService.Deposit(ctx, big.NewInt(int64(swapInitialDeposit)))
depositHash, err := chequebookService.Deposit(ctx, swapInitialDeposit)
if err != nil {
return nil, err
}
......
......@@ -6,6 +6,7 @@ package mock
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
......@@ -16,14 +17,17 @@ import (
)
type Service struct {
settlementSentFunc func(swarm.Address) (uint64, error)
settlementRecvFunc func(swarm.Address) (uint64, error)
settlementsSent map[string]*big.Int
settlementsRecv map[string]*big.Int
settlementsSentFunc func() (map[string]uint64, error)
settlementsRecvFunc func() (map[string]uint64, error)
settlementSentFunc func(swarm.Address) (*big.Int, error)
settlementRecvFunc func(swarm.Address) (*big.Int, error)
settlementsSentFunc func() (map[string]*big.Int, error)
settlementsRecvFunc func() (map[string]*big.Int, error)
receiveChequeFunc func(context.Context, swarm.Address, *chequebook.SignedCheque) error
payFunc func(context.Context, swarm.Address, uint64) error
payFunc func(context.Context, swarm.Address, *big.Int) error
setNotifyPaymentFunc settlement.NotifyPaymentFunc
handshakeFunc func(swarm.Address, common.Address) error
lastSentChequeFunc func(swarm.Address) (*chequebook.SignedCheque, error)
......@@ -37,26 +41,26 @@ type Service struct {
}
// WithsettlementFunc sets the mock settlement function
func WithSettlementSentFunc(f func(swarm.Address) (uint64, error)) Option {
func WithSettlementSentFunc(f func(swarm.Address) (*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.settlementSentFunc = f
})
}
func WithSettlementRecvFunc(f func(swarm.Address) (uint64, error)) Option {
func WithSettlementRecvFunc(f func(swarm.Address) (*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.settlementRecvFunc = f
})
}
// WithsettlementsFunc sets the mock settlements function
func WithSettlementsSentFunc(f func() (map[string]uint64, error)) Option {
func WithSettlementsSentFunc(f func() (map[string]*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.settlementsSentFunc = f
})
}
func WithSettlementsRecvFunc(f func() (map[string]uint64, error)) Option {
func WithSettlementsRecvFunc(f func() (map[string]*big.Int, error)) Option {
return optionFunc(func(s *Service) {
s.settlementsRecvFunc = f
})
......@@ -68,7 +72,7 @@ func WithReceiveChequeFunc(f func(context.Context, swarm.Address, *chequebook.Si
})
}
func WithPayFunc(f func(context.Context, swarm.Address, uint64) error) Option {
func WithPayFunc(f func(context.Context, swarm.Address, *big.Int) error) Option {
return optionFunc(func(s *Service) {
s.payFunc = f
})
......@@ -126,6 +130,8 @@ func WithCashoutStatusFunc(f func(ctx context.Context, peer swarm.Address) (*che
// New creates the mock swap implementation
func New(opts ...Option) settlement.Interface {
mock := new(Service)
mock.settlementsSent = make(map[string]*big.Int)
mock.settlementsRecv = make(map[string]*big.Int)
for _, o := range opts {
o.apply(mock)
}
......@@ -149,10 +155,15 @@ func (s *Service) ReceiveCheque(ctx context.Context, peer swarm.Address, cheque
}
// Pay is the mock Pay function of swap.
func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) error {
func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount *big.Int) error {
if s.payFunc != nil {
return s.payFunc(ctx, peer, amount)
}
if settlement, ok := s.settlementsSent[peer.String()]; ok {
s.settlementsSent[peer.String()] = big.NewInt(0).Add(settlement, amount)
} else {
s.settlementsSent[peer.String()] = amount
}
return nil
}
......@@ -163,37 +174,41 @@ func (s *Service) SetNotifyPaymentFunc(f settlement.NotifyPaymentFunc) {
}
// TotalSent is the mock TotalSent function of swap.
func (s *Service) TotalSent(peer swarm.Address) (totalSent uint64, err error) {
func (s *Service) TotalSent(peer swarm.Address) (totalSent *big.Int, err error) {
if s.settlementSentFunc != nil {
return s.settlementSentFunc(peer)
}
return 0, nil
if v, ok := s.settlementsSent[peer.String()]; ok {
return v, nil
}
return big.NewInt(0), nil
}
// TotalReceived is the mock TotalReceived function of swap.
func (s *Service) TotalReceived(peer swarm.Address) (totalReceived uint64, err error) {
func (s *Service) TotalReceived(peer swarm.Address) (totalReceived *big.Int, err error) {
if s.settlementRecvFunc != nil {
return s.settlementRecvFunc(peer)
}
return 0, nil
if v, ok := s.settlementsRecv[peer.String()]; ok {
return v, nil
}
return big.NewInt(0), nil
}
// SettlementsSent is the mock SettlementsSent function of swap.
func (s *Service) SettlementsSent() (map[string]uint64, error) {
func (s *Service) SettlementsSent() (map[string]*big.Int, error) {
if s.settlementsSentFunc != nil {
return s.settlementsSentFunc()
}
result := make(map[string]uint64)
return result, nil
return s.settlementsSent, nil
}
// SettlementsReceived is the mock SettlementsReceived function of swap.
func (s *Service) SettlementsReceived() (map[string]uint64, error) {
func (s *Service) SettlementsReceived() (map[string]*big.Int, error) {
if s.settlementsRecvFunc != nil {
return s.settlementsRecvFunc()
}
result := make(map[string]uint64)
return result, nil
return s.settlementsRecv, nil
}
// Handshake is called by the swap protocol when a handshake is received.
......
......@@ -102,11 +102,11 @@ func (s *Service) ReceiveCheque(ctx context.Context, peer swarm.Address, cheque
s.metrics.TotalReceived.Add(float64(amount.Uint64()))
return s.notifyPaymentFunc(peer, amount.Uint64())
return s.notifyPaymentFunc(peer, amount)
}
// Pay initiates a payment to the given peer
func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) error {
func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount *big.Int) error {
beneficiary, known, err := s.addressbook.Beneficiary(peer)
if err != nil {
return err
......@@ -119,7 +119,7 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er
}
return ErrUnknownBeneficary
}
balance, err := s.chequebook.Issue(ctx, beneficiary, big.NewInt(int64(amount)), func(signedCheque *chequebook.SignedCheque) error {
balance, err := s.chequebook.Issue(ctx, beneficiary, amount, func(signedCheque *chequebook.SignedCheque) error {
return s.proto.EmitCheque(ctx, peer, signedCheque)
})
if err != nil {
......@@ -127,7 +127,8 @@ func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount uint64) er
}
bal, _ := big.NewFloat(0).SetInt(balance).Float64()
s.metrics.AvailableBalance.Set(bal)
s.metrics.TotalSent.Add(float64(amount))
amountFloat, _ := big.NewFloat(0).SetInt(amount).Float64()
s.metrics.TotalSent.Add(amountFloat)
return nil
}
......@@ -137,47 +138,47 @@ func (s *Service) SetNotifyPaymentFunc(notifyPaymentFunc settlement.NotifyPaymen
}
// TotalSent returns the total amount sent to a peer
func (s *Service) TotalSent(peer swarm.Address) (totalSent uint64, err error) {
func (s *Service) TotalSent(peer swarm.Address) (totalSent *big.Int, err error) {
beneficiary, known, err := s.addressbook.Beneficiary(peer)
if err != nil {
return 0, err
return nil, err
}
if !known {
return 0, settlement.ErrPeerNoSettlements
return nil, settlement.ErrPeerNoSettlements
}
cheque, err := s.chequebook.LastCheque(beneficiary)
if err != nil {
if err == chequebook.ErrNoCheque {
return 0, settlement.ErrPeerNoSettlements
return nil, settlement.ErrPeerNoSettlements
}
return 0, err
return nil, err
}
return cheque.CumulativePayout.Uint64(), nil
return cheque.CumulativePayout, nil
}
// TotalReceived returns the total amount received from a peer
func (s *Service) TotalReceived(peer swarm.Address) (totalReceived uint64, err error) {
func (s *Service) TotalReceived(peer swarm.Address) (totalReceived *big.Int, err error) {
chequebookAddress, known, err := s.addressbook.Chequebook(peer)
if err != nil {
return 0, err
return nil, err
}
if !known {
return 0, settlement.ErrPeerNoSettlements
return nil, settlement.ErrPeerNoSettlements
}
cheque, err := s.chequeStore.LastCheque(chequebookAddress)
if err != nil {
if err == chequebook.ErrNoCheque {
return 0, settlement.ErrPeerNoSettlements
return nil, settlement.ErrPeerNoSettlements
}
return 0, err
return nil, err
}
return cheque.CumulativePayout.Uint64(), nil
return cheque.CumulativePayout, nil
}
// SettlementsSent returns sent settlements for each individual known peer
func (s *Service) SettlementsSent() (map[string]uint64, error) {
result := make(map[string]uint64)
func (s *Service) SettlementsSent() (map[string]*big.Int, error) {
result := make(map[string]*big.Int)
cheques, err := s.chequebook.LastCheques()
if err != nil {
return nil, err
......@@ -191,15 +192,15 @@ func (s *Service) SettlementsSent() (map[string]uint64, error) {
if !known {
continue
}
result[peer.String()] = cheque.CumulativePayout.Uint64()
result[peer.String()] = cheque.CumulativePayout
}
return result, nil
}
// SettlementsReceived returns received settlements for each individual known peer.
func (s *Service) SettlementsReceived() (map[string]uint64, error) {
result := make(map[string]uint64)
func (s *Service) SettlementsReceived() (map[string]*big.Int, error) {
result := make(map[string]*big.Int)
cheques, err := s.chequeStore.LastCheques()
if err != nil {
return nil, err
......@@ -213,7 +214,7 @@ func (s *Service) SettlementsReceived() (map[string]uint64, error) {
if !known {
continue
}
result[peer.String()] = cheque.CumulativePayout.Uint64()
result[peer.String()] = cheque.CumulativePayout
}
return result, err
}
......
......@@ -37,10 +37,10 @@ func (m *swapProtocolMock) EmitCheque(ctx context.Context, peer swarm.Address, c
type testObserver struct {
called bool
peer swarm.Address
amount uint64
amount *big.Int
}
func (t *testObserver) NotifyPayment(peer swarm.Address, amount uint64) error {
func (t *testObserver) NotifyPayment(peer swarm.Address, amount *big.Int) error {
t.called = true
t.peer = peer
t.amount = amount
......@@ -155,7 +155,7 @@ func TestReceiveCheque(t *testing.T) {
t.Fatal("expected observer to be called")
}
if observer.amount != amount.Uint64() {
if observer.amount.Cmp(amount) != 0 {
t.Fatalf("observer called with wrong amount. got %d, want %d", observer.amount, amount)
}
......@@ -278,7 +278,7 @@ func TestPay(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mockstore.NewStateStore()
amount := uint64(50)
amount := big.NewInt(50)
beneficiary := common.HexToAddress("0xcd")
var cheque chequebook.SignedCheque
......@@ -289,7 +289,7 @@ func TestPay(t *testing.T) {
if b != beneficiary {
t.Fatalf("issuing cheque for wrong beneficiary. wanted %v, got %v", beneficiary, b)
}
if a.Uint64() != amount {
if a.Cmp(amount) != 0 {
t.Fatalf("issuing cheque with wrong amount. wanted %d, got %d", amount, a)
}
chequebookCalled = true
......@@ -349,7 +349,7 @@ func TestPayIssueError(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mockstore.NewStateStore()
amount := uint64(50)
amount := big.NewInt(50)
beneficiary := common.HexToAddress("0xcd")
peer := swarm.MustParseHexAddress("abcd")
......@@ -392,7 +392,7 @@ func TestPayUnknownBeneficiary(t *testing.T) {
logger := logging.New(ioutil.Discard, 0)
store := mockstore.NewStateStore()
amount := uint64(50)
amount := big.NewInt(50)
peer := swarm.MustParseHexAddress("abcd")
networkID := uint64(1)
addressbook := &addressbookMock{
......
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