github.com/ethersphere/bee/v2@v2.2.0/pkg/settlement/swap/swapprotocol/swapprotocol_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 swapprotocol_test
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"errors"
    12  	"math/big"
    13  	"testing"
    14  
    15  	"github.com/ethereum/go-ethereum/common"
    16  
    17  	"github.com/ethersphere/bee/v2/pkg/log"
    18  	"github.com/ethersphere/bee/v2/pkg/p2p"
    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/swap/chequebook"
    22  	swapmock "github.com/ethersphere/bee/v2/pkg/settlement/swap/mock"
    23  	priceoraclemock "github.com/ethersphere/bee/v2/pkg/settlement/swap/priceoracle/mock"
    24  	"github.com/ethersphere/bee/v2/pkg/settlement/swap/swapprotocol"
    25  	"github.com/ethersphere/bee/v2/pkg/settlement/swap/swapprotocol/pb"
    26  	"github.com/ethersphere/bee/v2/pkg/swarm"
    27  )
    28  
    29  func TestEmitCheques(t *testing.T) {
    30  	t.Parallel()
    31  
    32  	// Test negotiating / sending cheques
    33  
    34  	logger := log.Noop
    35  	commonAddr := common.HexToAddress("0xab")
    36  	peerID := swarm.MustParseHexAddress("9ee7add7")
    37  	swapReceiver := swapmock.NewSwap()
    38  	swapInitiator := swapmock.NewSwap()
    39  
    40  	// mocked exchange rate and deduction
    41  	priceOracle := priceoraclemock.New(big.NewInt(50), big.NewInt(500))
    42  
    43  	swappReceiver := swapprotocol.New(nil, logger, commonAddr, priceOracle)
    44  	swappReceiver.SetSwap(swapReceiver)
    45  	recorder := streamtest.New(
    46  		streamtest.WithProtocols(swappReceiver.Protocol()),
    47  		streamtest.WithBaseAddr(peerID),
    48  	)
    49  	commonAddr2 := common.HexToAddress("0xdc")
    50  	swappInitiator := swapprotocol.New(recorder, logger, commonAddr2, priceOracle)
    51  	swappInitiator.SetSwap(swapInitiator)
    52  	peer := p2p.Peer{Address: peerID}
    53  
    54  	// amount in accounting credits cheque should cover
    55  	chequeAmount := big.NewInt(1250)
    56  
    57  	issueFunc := func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) (*big.Int, error) {
    58  		cheque := &chequebook.SignedCheque{
    59  			Cheque: chequebook.Cheque{
    60  				Beneficiary: commonAddr,
    61  				// CumulativePayout only contains value of last cheque
    62  				CumulativePayout: amount,
    63  				Chequebook:       common.Address{},
    64  			},
    65  			Signature: []byte{},
    66  		}
    67  		_ = sendChequeFunc(cheque)
    68  		return big.NewInt(13750), nil
    69  	}
    70  
    71  	// in this case we try to send a cheque covering amount * exchange rate + initial deduction
    72  
    73  	if _, err := swappInitiator.EmitCheque(context.Background(), peer.Address, commonAddr, chequeAmount, issueFunc); err != nil {
    74  		t.Fatalf("expected no error, got %v", err)
    75  	}
    76  	records, err := recorder.Records(peerID, "swap", "1.0.0", "swap")
    77  	if err != nil {
    78  		t.Fatal(err)
    79  	}
    80  	if l := len(records); l != 1 {
    81  		t.Fatalf("got %v records, want %v", l, 1)
    82  	}
    83  	record := records[0]
    84  	messages, err := protobuf.ReadMessages(
    85  		bytes.NewReader(record.In()),
    86  		func() protobuf.Message { return new(pb.EmitCheque) },
    87  	)
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	if len(messages) != 1 {
    92  		t.Fatalf("got %v messages, want %v", len(messages), 1)
    93  	}
    94  
    95  	gotCheque := messages[0].(*pb.EmitCheque)
    96  
    97  	var gotSignedCheque *chequebook.SignedCheque
    98  	err = json.Unmarshal(gotCheque.Cheque, &gotSignedCheque)
    99  	if err != nil {
   100  		t.Fatal(err)
   101  	}
   102  
   103  	// cumulative payout expected to be 50 * 1250 + 500
   104  
   105  	if gotSignedCheque.CumulativePayout.Cmp(big.NewInt(63000)) != 0 {
   106  		t.Fatalf("Unexpected cheque amount, expected %v, got %v", 63000, gotSignedCheque.CumulativePayout)
   107  	}
   108  
   109  	// attempt to send second cheque covering same amount, this time deduction is not applicable
   110  
   111  	if _, err := swappInitiator.EmitCheque(context.Background(), peer.Address, commonAddr, chequeAmount, issueFunc); err != nil {
   112  		t.Fatalf("expected no error, got %v", err)
   113  	}
   114  	records, err = recorder.Records(peerID, "swap", "1.0.0", "swap")
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	if l := len(records); l != 2 {
   119  		t.Fatalf("got %v records, want %v", l, 2)
   120  	}
   121  	record = records[1]
   122  	messages, err = protobuf.ReadMessages(
   123  		bytes.NewReader(record.In()),
   124  		func() protobuf.Message { return new(pb.EmitCheque) },
   125  	)
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	if len(messages) != 1 {
   130  		t.Fatalf("got %v messages, want %v", len(messages), 1)
   131  	}
   132  
   133  	gotCheque = messages[0].(*pb.EmitCheque)
   134  
   135  	err = json.Unmarshal(gotCheque.Cheque, &gotSignedCheque)
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  
   140  	// cumulative payout only contains last cheque value in this case because of mocks
   141  	// cumulative payout expected to be 50 * 1250 + 0
   142  
   143  	if gotSignedCheque.CumulativePayout.Cmp(big.NewInt(62500)) != 0 {
   144  		t.Fatalf("Unexpected cheque amount, expected %v, got %v", 62500, gotSignedCheque.CumulativePayout)
   145  	}
   146  }
   147  
   148  func TestCantEmitChequeRateMismatch(t *testing.T) {
   149  	t.Parallel()
   150  
   151  	logger := log.Noop
   152  	commonAddr := common.HexToAddress("0xab")
   153  	peerID := swarm.MustParseHexAddress("9ee7add7")
   154  	swapReceiver := swapmock.NewSwap()
   155  	swapInitiator := swapmock.NewSwap()
   156  
   157  	// mock different information for the receiver and sender received from oracle
   158  
   159  	priceOracle := priceoraclemock.New(big.NewInt(50), big.NewInt(500))
   160  	priceOracle2 := priceoraclemock.New(big.NewInt(52), big.NewInt(560))
   161  	swappReceiver := swapprotocol.New(nil, logger, commonAddr, priceOracle)
   162  	swappReceiver.SetSwap(swapReceiver)
   163  	recorder := streamtest.New(
   164  		streamtest.WithProtocols(swappReceiver.Protocol()),
   165  		streamtest.WithBaseAddr(peerID),
   166  	)
   167  	commonAddr2 := common.HexToAddress("0xdc")
   168  	swappInitiator := swapprotocol.New(recorder, logger, commonAddr2, priceOracle2)
   169  	swappInitiator.SetSwap(swapInitiator)
   170  	peer := p2p.Peer{Address: peerID}
   171  
   172  	chequeAmount := big.NewInt(1250)
   173  
   174  	issueFunc := func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) (*big.Int, error) {
   175  		cheque := &chequebook.SignedCheque{
   176  			Cheque: chequebook.Cheque{
   177  				Beneficiary:      commonAddr,
   178  				CumulativePayout: amount,
   179  				Chequebook:       common.Address{},
   180  			},
   181  			Signature: []byte{},
   182  		}
   183  		_ = sendChequeFunc(cheque)
   184  		return big.NewInt(13750), nil
   185  	}
   186  
   187  	// try to send cheque, this should fail because of the different known exchange rates on the sender and receiver
   188  	// expect rate negotiation error
   189  
   190  	if _, err := swappInitiator.EmitCheque(context.Background(), peer.Address, commonAddr, chequeAmount, issueFunc); !errors.Is(err, swapprotocol.ErrNegotiateRate) {
   191  		t.Fatalf("expected error %v, got %v", swapprotocol.ErrNegotiateRate, err)
   192  	}
   193  	records, err := recorder.Records(peerID, "swap", "1.0.0", "swap")
   194  	if err != nil {
   195  		t.Fatal(err)
   196  	}
   197  	if l := len(records); l != 1 {
   198  		t.Fatalf("got %v records, want %v", l, 1)
   199  	}
   200  	record := records[0]
   201  	messages, err := protobuf.ReadMessages(
   202  		bytes.NewReader(record.In()),
   203  		func() protobuf.Message { return new(pb.EmitCheque) },
   204  	)
   205  	if err != nil {
   206  		t.Fatal(err)
   207  	}
   208  	if len(messages) != 0 {
   209  		t.Fatalf("got %v messages, want %v", len(messages), 0)
   210  	}
   211  }
   212  
   213  func TestCantEmitChequeDeductionMismatch(t *testing.T) {
   214  	t.Parallel()
   215  
   216  	logger := log.Noop
   217  	commonAddr := common.HexToAddress("0xab")
   218  	peerID := swarm.MustParseHexAddress("9ee7add7")
   219  	swapReceiver := swapmock.NewSwap()
   220  	swapInitiator := swapmock.NewSwap()
   221  
   222  	// mock different deduction values for receiver and sender received from the oracle
   223  
   224  	priceOracle := priceoraclemock.New(big.NewInt(50), big.NewInt(500))
   225  	priceOracle2 := priceoraclemock.New(big.NewInt(50), big.NewInt(560))
   226  	swappReceiver := swapprotocol.New(nil, logger, commonAddr, priceOracle)
   227  	swappReceiver.SetSwap(swapReceiver)
   228  	recorder := streamtest.New(
   229  		streamtest.WithProtocols(swappReceiver.Protocol()),
   230  		streamtest.WithBaseAddr(peerID),
   231  	)
   232  	commonAddr2 := common.HexToAddress("0xdc")
   233  	swappInitiator := swapprotocol.New(recorder, logger, commonAddr2, priceOracle2)
   234  	swappInitiator.SetSwap(swapInitiator)
   235  	peer := p2p.Peer{Address: peerID}
   236  
   237  	chequeAmount := big.NewInt(1250)
   238  
   239  	issueFunc := func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) (*big.Int, error) {
   240  		cheque := &chequebook.SignedCheque{
   241  			Cheque: chequebook.Cheque{
   242  				Beneficiary:      commonAddr,
   243  				CumulativePayout: amount,
   244  				Chequebook:       common.Address{},
   245  			},
   246  			Signature: []byte{},
   247  		}
   248  		_ = sendChequeFunc(cheque)
   249  		return big.NewInt(13750), nil
   250  	}
   251  
   252  	// attempt negotiating rates, expect deduction negotiation error
   253  
   254  	if _, err := swappInitiator.EmitCheque(context.Background(), peer.Address, commonAddr, chequeAmount, issueFunc); !errors.Is(err, swapprotocol.ErrNegotiateDeduction) {
   255  		t.Fatalf("expected error %v, got %v", swapprotocol.ErrNegotiateDeduction, err)
   256  	}
   257  
   258  	records, err := recorder.Records(peerID, "swap", "1.0.0", "swap")
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  	if l := len(records); l != 1 {
   263  		t.Fatalf("got %v records, want %v", l, 1)
   264  	}
   265  	record := records[0]
   266  	messages, err := protobuf.ReadMessages(
   267  		bytes.NewReader(record.In()),
   268  		func() protobuf.Message { return new(pb.EmitCheque) },
   269  	)
   270  	if err != nil {
   271  		t.Fatal(err)
   272  	}
   273  	if len(messages) != 0 {
   274  		t.Fatalf("got %v messages, want %v", len(messages), 0)
   275  	}
   276  }
   277  
   278  func TestCantEmitChequeIneligibleDeduction(t *testing.T) {
   279  	t.Parallel()
   280  
   281  	logger := log.Noop
   282  	commonAddr := common.HexToAddress("0xab")
   283  	peerID := swarm.MustParseHexAddress("9ee7add7")
   284  	swapReceiver := swapmock.NewSwap()
   285  	swapInitiator := swapmock.NewSwap()
   286  
   287  	// mock exactly the same rates for exchange and deduction for receiver and sender
   288  
   289  	priceOracle := priceoraclemock.New(big.NewInt(50), big.NewInt(500))
   290  	priceOracle2 := priceoraclemock.New(big.NewInt(50), big.NewInt(500))
   291  	swappReceiver := swapprotocol.New(nil, logger, commonAddr, priceOracle)
   292  	swappReceiver.SetSwap(swapReceiver)
   293  	recorder := streamtest.New(
   294  		streamtest.WithProtocols(swappReceiver.Protocol()),
   295  		streamtest.WithBaseAddr(peerID),
   296  	)
   297  	commonAddr2 := common.HexToAddress("0xdc")
   298  	swappInitiator := swapprotocol.New(recorder, logger, commonAddr2, priceOracle2)
   299  	swappInitiator.SetSwap(swapInitiator)
   300  	peer := p2p.Peer{Address: peerID}
   301  
   302  	chequeAmount := big.NewInt(1250)
   303  
   304  	issueFunc := func(ctx context.Context, beneficiary common.Address, amount *big.Int, sendChequeFunc chequebook.SendChequeFunc) (*big.Int, error) {
   305  		cheque := &chequebook.SignedCheque{
   306  			Cheque: chequebook.Cheque{
   307  				Beneficiary:      commonAddr,
   308  				CumulativePayout: amount,
   309  				Chequebook:       common.Address{},
   310  			},
   311  			Signature: []byte{},
   312  		}
   313  		_ = sendChequeFunc(cheque)
   314  		return big.NewInt(13750), nil
   315  	}
   316  
   317  	// mock that the initiator believes deduction was applied previously, but the accepting peer does not
   318  
   319  	err := swapInitiator.AddDeductionByPeer(peerID)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  
   324  	// attempt settling anyway, expect it to fail by ineligible deduction error
   325  
   326  	if _, err := swappInitiator.EmitCheque(context.Background(), peer.Address, commonAddr, chequeAmount, issueFunc); !errors.Is(err, swapprotocol.ErrHaveDeduction) {
   327  		t.Fatalf("expected error %v, got %v", swapprotocol.ErrHaveDeduction, err)
   328  	}
   329  
   330  	records, err := recorder.Records(peerID, "swap", "1.0.0", "swap")
   331  	if err != nil {
   332  		t.Fatal(err)
   333  	}
   334  	if l := len(records); l != 1 {
   335  		t.Fatalf("got %v records, want %v", l, 1)
   336  	}
   337  	record := records[0]
   338  	messages, err := protobuf.ReadMessages(
   339  		bytes.NewReader(record.In()),
   340  		func() protobuf.Message { return new(pb.EmitCheque) },
   341  	)
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  	if len(messages) != 0 {
   346  		t.Fatalf("got %v messages, want %v", len(messages), 0)
   347  	}
   348  }