github.com/ethersphere/bee/v2@v2.2.0/pkg/settlement/pseudosettle/pseudosettle_test.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package pseudosettle_test
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"io"
    12  	"math/big"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/ethersphere/bee/v2/pkg/log"
    17  	"github.com/ethersphere/bee/v2/pkg/p2p"
    18  	mockp2p "github.com/ethersphere/bee/v2/pkg/p2p/mock"
    19  	"github.com/ethersphere/bee/v2/pkg/p2p/protobuf"
    20  	"github.com/ethersphere/bee/v2/pkg/p2p/streamtest"
    21  	"github.com/ethersphere/bee/v2/pkg/settlement/pseudosettle"
    22  	"github.com/ethersphere/bee/v2/pkg/settlement/pseudosettle/pb"
    23  	"github.com/ethersphere/bee/v2/pkg/statestore/mock"
    24  	"github.com/ethersphere/bee/v2/pkg/storage"
    25  	"github.com/ethersphere/bee/v2/pkg/swarm"
    26  	"github.com/ethersphere/bee/v2/pkg/util/testutil"
    27  )
    28  
    29  type testObserver struct {
    30  	receivedCalled    chan notifyPaymentReceivedCall
    31  	sentCalled        chan notifyPaymentSentCall
    32  	refreshSentCalled chan notifyRefreshSentCall
    33  	peerDebts         map[string]*big.Int
    34  }
    35  
    36  type notifyPaymentReceivedCall struct {
    37  	peer   swarm.Address
    38  	amount *big.Int
    39  }
    40  
    41  type notifyPaymentSentCall struct {
    42  	peer   swarm.Address
    43  	amount *big.Int
    44  	err    error
    45  }
    46  
    47  type notifyRefreshSentCall struct {
    48  	peer      swarm.Address
    49  	attempted *big.Int
    50  	amount    *big.Int
    51  	timestamp int64
    52  	interval  int64
    53  	err       error
    54  }
    55  
    56  func newTestObserver(debtAmounts, shadowBalanceAmounts map[string]*big.Int) *testObserver {
    57  	return &testObserver{
    58  		receivedCalled:    make(chan notifyPaymentReceivedCall, 1),
    59  		sentCalled:        make(chan notifyPaymentSentCall, 1),
    60  		refreshSentCalled: make(chan notifyRefreshSentCall, 1),
    61  		peerDebts:         debtAmounts,
    62  	}
    63  }
    64  
    65  func (t *testObserver) setPeerDebt(peer swarm.Address, debt *big.Int) {
    66  	t.peerDebts[peer.String()] = debt
    67  }
    68  
    69  func (t *testObserver) PeerDebt(peer swarm.Address) (*big.Int, error) {
    70  	if debt, ok := t.peerDebts[peer.String()]; ok {
    71  		return debt, nil
    72  	}
    73  
    74  	return nil, errors.New("Peer not listed")
    75  }
    76  
    77  func (t *testObserver) Connect(peer swarm.Address, full bool) {
    78  
    79  }
    80  
    81  func (t *testObserver) Disconnect(peer swarm.Address) {
    82  
    83  }
    84  
    85  func (t *testObserver) NotifyRefreshmentSent(peer swarm.Address, attemptedAmount, amount *big.Int, timestamp int64, allegedInterval int64, receivedError error) {
    86  	t.refreshSentCalled <- notifyRefreshSentCall{
    87  		peer:      peer,
    88  		attempted: attemptedAmount,
    89  		amount:    amount,
    90  		timestamp: timestamp,
    91  		interval:  allegedInterval,
    92  		err:       receivedError,
    93  	}
    94  }
    95  
    96  func (t *testObserver) NotifyRefreshmentReceived(peer swarm.Address, amount *big.Int, time int64) error {
    97  	t.receivedCalled <- notifyPaymentReceivedCall{
    98  		peer:   peer,
    99  		amount: amount,
   100  	}
   101  	return nil
   102  }
   103  
   104  func (t *testObserver) NotifyPaymentReceived(peer swarm.Address, amount *big.Int) error {
   105  	return nil
   106  }
   107  
   108  func (t *testObserver) NotifyPaymentSent(peer swarm.Address, amount *big.Int, err error) {
   109  	t.sentCalled <- notifyPaymentSentCall{
   110  		peer:   peer,
   111  		amount: amount,
   112  		err:    err,
   113  	}
   114  }
   115  
   116  func (t *testObserver) Reserve(ctx context.Context, peer swarm.Address, amount uint64) error {
   117  	return nil
   118  }
   119  
   120  func (t *testObserver) Release(peer swarm.Address, amount uint64) {
   121  }
   122  
   123  var testRefreshRate = int64(10000)
   124  var testRefreshRateLight = int64(1000)
   125  
   126  func testCaseNotAccepted(t *testing.T, recorder *streamtest.Recorder, payerObserver, receiverObserver *testObserver, payer, recipient *pseudosettle.Service, peerID swarm.Address, payerTime, recipientTime int64, recordsLength int, debtAmount, amount *big.Int, expectedError error) {
   127  	t.Helper()
   128  
   129  	payer.SetTime(payerTime)
   130  	recipient.SetTime(recipientTime)
   131  	receiverObserver.setPeerDebt(peerID, debtAmount)
   132  
   133  	payer.Pay(context.Background(), peerID, amount)
   134  	select {
   135  	case sent := <-payerObserver.refreshSentCalled:
   136  		if !errors.Is(sent.err, expectedError) {
   137  			t.Fatalf("expected error %v, got %v", expectedError, sent.err)
   138  		}
   139  	case <-time.After(1 * time.Second):
   140  		t.Fatalf("expected refresh sent called")
   141  	}
   142  
   143  	records, err := recorder.Records(peerID, "pseudosettle", "1.0.0", "pseudosettle")
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  
   148  	if l := len(records); l != recordsLength {
   149  		t.Fatalf("got %v records, want %v", l, recordsLength)
   150  	}
   151  
   152  	select {
   153  	case <-receiverObserver.receivedCalled:
   154  		t.Fatal("unexpected observer to be called")
   155  
   156  	case <-time.After(1 * time.Second):
   157  
   158  	}
   159  }
   160  
   161  func testCaseAccepted(t *testing.T, recorder *streamtest.Recorder, payerObserver, receiverObserver *testObserver, payer, recipient *pseudosettle.Service, peerID swarm.Address, payerTime, recipientTime int64, recordsLength, msgLength, recMsgLength int, debtAmount, amount, accepted, totalAmount *big.Int) {
   162  	t.Helper()
   163  
   164  	payer.SetTime(payerTime)
   165  	recipient.SetTime(recipientTime)
   166  
   167  	// set debt shown by accounting (observer)
   168  	receiverObserver.setPeerDebt(peerID, debtAmount)
   169  
   170  	payer.Pay(context.Background(), peerID, amount)
   171  	acceptedAmount := big.NewInt(0)
   172  	select {
   173  	case sent := <-payerObserver.refreshSentCalled:
   174  		if sent.err != nil {
   175  			t.Fatal(sent.err)
   176  		}
   177  		acceptedAmount.Set(sent.amount)
   178  	case <-time.After(1 * time.Second):
   179  		t.Fatalf("waiting for refresh to be called")
   180  	}
   181  
   182  	if acceptedAmount.Cmp(accepted) != 0 {
   183  		t.Fatalf("expected amount not accepted. wanted %d, got %d", acceptedAmount, accepted)
   184  	}
   185  
   186  	records, err := recorder.Records(peerID, "pseudosettle", "1.0.0", "pseudosettle")
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  
   191  	if l := len(records); l != recordsLength {
   192  		t.Fatalf("got %v records, want %v", l, recordsLength)
   193  	}
   194  
   195  	record := records[recordsLength-1]
   196  
   197  	if err := record.Err(); err != nil {
   198  		t.Fatalf("record error: %v", err)
   199  	}
   200  
   201  	messages, err := protobuf.ReadMessages(
   202  		bytes.NewReader(record.In()),
   203  		func() protobuf.Message { return new(pb.Payment) },
   204  	)
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  
   209  	receivedMessages, err := protobuf.ReadMessages(
   210  		bytes.NewReader(record.Out()),
   211  		func() protobuf.Message { return new(pb.PaymentAck) },
   212  	)
   213  	if err != nil {
   214  		t.Fatal(err)
   215  	}
   216  
   217  	if len(messages) != msgLength || len(receivedMessages) != recMsgLength {
   218  		t.Fatalf("got %v/%v messages, want %v/%v", len(messages), len(receivedMessages), msgLength, recMsgLength)
   219  	}
   220  
   221  	sentAmount := big.NewInt(0).SetBytes(messages[0].(*pb.Payment).Amount)
   222  	receivedAmount := big.NewInt(0).SetBytes(receivedMessages[0].(*pb.PaymentAck).Amount)
   223  	if sentAmount.Cmp(amount) != 0 {
   224  		t.Fatalf("got message with amount %v, want %v", sentAmount, amount)
   225  	}
   226  
   227  	if receivedAmount.Cmp(accepted) != 0 {
   228  		t.Fatalf("wrong settlement amount, got %v, want %v", receivedAmount, accepted)
   229  	}
   230  
   231  	select {
   232  	case call := <-receiverObserver.receivedCalled:
   233  		if call.amount.Cmp(accepted) != 0 {
   234  			t.Fatalf("observer called with wrong amount. got %d, want %d", call.amount, accepted)
   235  		}
   236  
   237  		if !call.peer.Equal(peerID) {
   238  			t.Fatalf("observer called with wrong peer. got %v, want %v", call.peer, peerID)
   239  		}
   240  
   241  	case <-time.After(1 * time.Second):
   242  		t.Fatal("expected observer to be called")
   243  	}
   244  
   245  	totalSent, err := payer.TotalSent(peerID)
   246  	if err != nil {
   247  		t.Fatal(err)
   248  	}
   249  
   250  	if totalSent.Cmp(totalAmount) != 0 {
   251  		t.Fatalf("stored wrong totalSent. got %d, want %d", totalSent, sentAmount)
   252  	}
   253  
   254  	totalReceived, err := recipient.TotalReceived(peerID)
   255  	if err != nil {
   256  		t.Fatal(err)
   257  	}
   258  
   259  	if totalReceived.Cmp(totalAmount) != 0 {
   260  		t.Fatalf("stored wrong totalReceived. got %d, want %d", totalReceived, sentAmount)
   261  	}
   262  }
   263  
   264  func TestPayment(t *testing.T) {
   265  	t.Parallel()
   266  
   267  	logger := log.Noop
   268  
   269  	storeRecipient := newStateStore(t)
   270  
   271  	peerID := swarm.MustParseHexAddress("9ee7add7")
   272  	peer := p2p.Peer{Address: peerID, FullNode: true}
   273  
   274  	debt := int64(10000)
   275  
   276  	payerObserver := newTestObserver(map[string]*big.Int{peerID.String(): big.NewInt(debt)}, map[string]*big.Int{})
   277  	receiverObserver := newTestObserver(map[string]*big.Int{peerID.String(): big.NewInt(debt)}, map[string]*big.Int{})
   278  	recipient := pseudosettle.New(nil, logger, storeRecipient, receiverObserver, big.NewInt(testRefreshRate), big.NewInt(testRefreshRateLight), mockp2p.New())
   279  	recipient.SetAccounting(receiverObserver)
   280  	err := recipient.Init(context.Background(), peer)
   281  	if err != nil {
   282  		t.Fatal(err)
   283  	}
   284  
   285  	recorder := streamtest.New(
   286  		streamtest.WithProtocols(recipient.Protocol()),
   287  		streamtest.WithBaseAddr(peerID),
   288  	)
   289  
   290  	storePayer := newStateStore(t)
   291  
   292  	payer := pseudosettle.New(recorder, logger, storePayer, payerObserver, big.NewInt(testRefreshRate), big.NewInt(testRefreshRateLight), mockp2p.New())
   293  	payer.SetAccounting(payerObserver)
   294  	// set time to non-zero, attempt payment based on debt, expect full amount to be accepted
   295  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 30, 30, 1, 1, 1, big.NewInt(debt), big.NewInt(debt), big.NewInt(debt), big.NewInt(debt))
   296  }
   297  
   298  func TestTimeLimitedPayment(t *testing.T) {
   299  	t.Parallel()
   300  
   301  	logger := log.Noop
   302  
   303  	storeRecipient := newStateStore(t)
   304  
   305  	peerID := swarm.MustParseHexAddress("9ee7add7")
   306  	peer := p2p.Peer{Address: peerID, FullNode: true}
   307  
   308  	debt := testRefreshRate
   309  
   310  	payerObserver := newTestObserver(map[string]*big.Int{peerID.String(): big.NewInt(debt)}, map[string]*big.Int{})
   311  	receiverObserver := newTestObserver(map[string]*big.Int{peerID.String(): big.NewInt(debt)}, map[string]*big.Int{})
   312  	recipient := pseudosettle.New(nil, logger, storeRecipient, receiverObserver, big.NewInt(testRefreshRate), big.NewInt(testRefreshRateLight), mockp2p.New())
   313  	recipient.SetAccounting(receiverObserver)
   314  	err := recipient.Init(context.Background(), peer)
   315  	if err != nil {
   316  		t.Fatal(err)
   317  	}
   318  
   319  	recorder := streamtest.New(
   320  		streamtest.WithProtocols(recipient.Protocol()),
   321  		streamtest.WithBaseAddr(peerID),
   322  	)
   323  
   324  	storePayer := newStateStore(t)
   325  
   326  	payer := pseudosettle.New(recorder, logger, storePayer, payerObserver, big.NewInt(testRefreshRate), big.NewInt(testRefreshRateLight), mockp2p.New())
   327  	payer.SetAccounting(payerObserver)
   328  
   329  	// Set time to 10000, attempt payment based on debt, expect full amount accepted
   330  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10000, 10000, 1, 1, 1, big.NewInt(debt), big.NewInt(debt), big.NewInt(debt), big.NewInt(debt))
   331  
   332  	// Set time 3 seconds later, attempt settlement below time based refreshment rate, expect full amount accepted
   333  	sentSum := big.NewInt(debt + testRefreshRate*3/2)
   334  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10003, 10003, 2, 1, 1, big.NewInt(testRefreshRate*3/2), big.NewInt(testRefreshRate*3/2), big.NewInt(testRefreshRate*3/2), sentSum)
   335  
   336  	// set time 1 seconds later, attempt settlement over the time-based allowed limit, expect partial amount accepted
   337  	sentSum = big.NewInt(debt + testRefreshRate*3/2 + testRefreshRate)
   338  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10004, 10004, 3, 1, 1, big.NewInt(testRefreshRate*3), big.NewInt(testRefreshRate*3), big.NewInt(testRefreshRate), sentSum)
   339  
   340  	// set time to same second as previous case on recipient, 1 second later on payer, attempt settlement, expect sent but failed
   341  	testCaseNotAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10005, 10004, 4, big.NewInt(2*testRefreshRate), big.NewInt(2*testRefreshRate), io.EOF)
   342  
   343  	// set time 6 seconds later, attempt with debt over time based allowance, expect partial accept
   344  	sentSum = big.NewInt(debt + testRefreshRate*3/2 + testRefreshRate + 6*testRefreshRate)
   345  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10010, 10010, 5, 1, 1, big.NewInt(9*testRefreshRate), big.NewInt(9*testRefreshRate), big.NewInt(6*testRefreshRate), sentSum)
   346  
   347  	// set time 10 seconds later, attempt with debt below time based allowance, expect full amount accepted
   348  	sentSum = big.NewInt(debt + testRefreshRate*3/2 + testRefreshRate + 6*testRefreshRate + 5*testRefreshRate)
   349  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10020, 10020, 6, 1, 1, big.NewInt(5*testRefreshRate), big.NewInt(5*testRefreshRate), big.NewInt(5*testRefreshRate), sentSum)
   350  }
   351  
   352  func TestTimeLimitedPaymentLight(t *testing.T) {
   353  	t.Parallel()
   354  
   355  	logger := log.Noop
   356  
   357  	storeRecipient := newStateStore(t)
   358  
   359  	peerID := swarm.MustParseHexAddress("9ee7add7")
   360  	peer := p2p.Peer{Address: peerID, FullNode: false}
   361  
   362  	debt := testRefreshRate
   363  
   364  	payerObserver := newTestObserver(map[string]*big.Int{peerID.String(): big.NewInt(debt)}, map[string]*big.Int{})
   365  	receiverObserver := newTestObserver(map[string]*big.Int{peerID.String(): big.NewInt(debt)}, map[string]*big.Int{})
   366  	recipient := pseudosettle.New(nil, logger, storeRecipient, receiverObserver, big.NewInt(testRefreshRate), big.NewInt(testRefreshRateLight), mockp2p.New())
   367  	recipient.SetAccounting(receiverObserver)
   368  	err := recipient.Init(context.Background(), peer)
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  
   373  	recorder := streamtest.New(
   374  		streamtest.WithProtocols(recipient.Protocol()),
   375  		streamtest.WithBaseAddr(peerID),
   376  	)
   377  
   378  	storePayer := newStateStore(t)
   379  
   380  	payer := pseudosettle.New(recorder, logger, storePayer, payerObserver, big.NewInt(testRefreshRateLight), big.NewInt(testRefreshRateLight), mockp2p.New())
   381  	payer.SetAccounting(payerObserver)
   382  
   383  	// Set time to 10000, attempt payment based on debt, expect full amount accepted
   384  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10000, 10000, 1, 1, 1, big.NewInt(debt), big.NewInt(debt), big.NewInt(debt), big.NewInt(debt))
   385  	// Set time 3 seconds later, attempt settlement below time based light refreshment rate, expect full amount accepted
   386  	sentSum := big.NewInt(debt + testRefreshRateLight*3)
   387  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10003, 10003, 2, 1, 1, big.NewInt(testRefreshRate*3/2), big.NewInt(testRefreshRate*3/2), big.NewInt(testRefreshRateLight*3), sentSum)
   388  	// set time 1 seconds later, attempt settlement over the time-based light allowed limit, expect partial amount accepted
   389  	sentSum = big.NewInt(debt + testRefreshRateLight*3 + testRefreshRateLight)
   390  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10004, 10004, 3, 1, 1, big.NewInt(testRefreshRate*3), big.NewInt(testRefreshRate*3), big.NewInt(testRefreshRateLight), sentSum)
   391  	// set time to same second as previous case on recipient, 1 second later on payer, attempt settlement, expect sent but failed
   392  	testCaseNotAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10005, 10004, 4, big.NewInt(2*testRefreshRate), big.NewInt(2*testRefreshRate), io.EOF)
   393  	// set time 6 seconds later, attempt with debt over time based light allowance, expect partial accept
   394  	sentSum = big.NewInt(debt + testRefreshRateLight*3 + testRefreshRateLight + 6*testRefreshRateLight)
   395  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10010, 10010, 5, 1, 1, big.NewInt(9*testRefreshRate), big.NewInt(9*testRefreshRate), big.NewInt(6*testRefreshRateLight), sentSum)
   396  	// set time 100 seconds later, attempt with debt below time based allowance, expect full amount accepted
   397  	sentSum = big.NewInt(debt + testRefreshRateLight*3 + testRefreshRateLight + 6*testRefreshRateLight + 50*testRefreshRateLight)
   398  	testCaseAccepted(t, recorder, payerObserver, receiverObserver, payer, recipient, peerID, 10110, 10110, 6, 1, 1, big.NewInt(50*testRefreshRateLight), big.NewInt(50*testRefreshRateLight), big.NewInt(50*testRefreshRateLight), sentSum)
   399  }
   400  
   401  func newStateStore(t *testing.T) storage.StateStorer {
   402  	t.Helper()
   403  
   404  	storePayer := mock.NewStateStore()
   405  	testutil.CleanupCloser(t, storePayer)
   406  
   407  	return storePayer
   408  }