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 }