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 }