github.com/ethersphere/bee/v2@v2.2.0/pkg/settlement/swap/swap.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 swap 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "math/big" 12 13 "github.com/ethereum/go-ethereum/common" 14 "github.com/ethersphere/bee/v2/pkg/log" 15 "github.com/ethersphere/bee/v2/pkg/postage/postagecontract" 16 "github.com/ethersphere/bee/v2/pkg/settlement" 17 "github.com/ethersphere/bee/v2/pkg/settlement/swap/chequebook" 18 "github.com/ethersphere/bee/v2/pkg/settlement/swap/swapprotocol" 19 "github.com/ethersphere/bee/v2/pkg/storage" 20 "github.com/ethersphere/bee/v2/pkg/swarm" 21 ) 22 23 // loggerName is the tree path name of the logger for this package. 24 const loggerName = "swap" 25 26 var ( 27 // ErrWrongChequebook is the error if a peer uses a different chequebook from before. 28 ErrWrongChequebook = errors.New("wrong chequebook") 29 // ErrUnknownBeneficary is the error if a peer has never announced a beneficiary. 30 ErrUnknownBeneficary = errors.New("unknown beneficiary for peer") 31 // ErrChequeValueTooLow is the error a peer issued a cheque not covering 1 accounting credit 32 ErrChequeValueTooLow = errors.New("cheque value too low") 33 ErrNoChequebook = errors.New("no chequebook") 34 ) 35 36 type Interface interface { 37 settlement.Interface 38 // LastSentCheque returns the last sent cheque for the peer 39 LastSentCheque(peer swarm.Address) (*chequebook.SignedCheque, error) 40 // LastSentCheques returns the list of last sent cheques for all peers 41 LastSentCheques() (map[string]*chequebook.SignedCheque, error) 42 // LastReceivedCheque returns the last received cheque for the peer 43 LastReceivedCheque(peer swarm.Address) (*chequebook.SignedCheque, error) 44 // LastReceivedCheques returns the list of last received cheques for all peers 45 LastReceivedCheques() (map[string]*chequebook.SignedCheque, error) 46 // CashCheque sends a cashing transaction for the last cheque of the peer 47 CashCheque(ctx context.Context, peer swarm.Address) (common.Hash, error) 48 // CashoutStatus gets the status of the latest cashout transaction for the peers chequebook 49 CashoutStatus(ctx context.Context, peer swarm.Address) (*chequebook.CashoutStatus, error) 50 } 51 52 // Service is the implementation of the swap settlement layer. 53 type Service struct { 54 proto swapprotocol.Interface 55 logger log.Logger 56 store storage.StateStorer 57 accounting settlement.Accounting 58 metrics metrics 59 chequebook chequebook.Service 60 chequeStore chequebook.ChequeStore 61 cashout chequebook.CashoutService 62 addressbook Addressbook 63 networkID uint64 64 cashoutAddress common.Address 65 } 66 67 // New creates a new swap Service. 68 func New(proto swapprotocol.Interface, logger log.Logger, store storage.StateStorer, chequebook chequebook.Service, chequeStore chequebook.ChequeStore, addressbook Addressbook, networkID uint64, cashout chequebook.CashoutService, accounting settlement.Accounting, cashoutAddress common.Address) *Service { 69 return &Service{ 70 proto: proto, 71 logger: logger.WithName(loggerName).Register(), 72 store: store, 73 metrics: newMetrics(), 74 chequebook: chequebook, 75 chequeStore: chequeStore, 76 addressbook: addressbook, 77 networkID: networkID, 78 cashout: cashout, 79 accounting: accounting, 80 cashoutAddress: cashoutAddress, 81 } 82 } 83 84 // ReceiveCheque is called by the swap protocol if a cheque is received. 85 func (s *Service) ReceiveCheque(ctx context.Context, peer swarm.Address, cheque *chequebook.SignedCheque, exchangeRate, deduction *big.Int) (err error) { 86 // check this is the same chequebook for this peer as previously 87 expectedChequebook, known, err := s.addressbook.Chequebook(peer) 88 if err != nil { 89 return err 90 } 91 if known && expectedChequebook != cheque.Chequebook { 92 return ErrWrongChequebook 93 } 94 95 receivedAmount, err := s.chequeStore.ReceiveCheque(ctx, cheque, exchangeRate, deduction) 96 if err != nil { 97 s.metrics.ChequesRejected.Inc() 98 return fmt.Errorf("rejecting cheque: %w", err) 99 } 100 101 if deduction.Cmp(big.NewInt(0)) > 0 { 102 err = s.addressbook.AddDeductionFor(peer) 103 if err != nil { 104 return err 105 } 106 } 107 108 decreasedAmount := new(big.Int).Sub(receivedAmount, deduction) 109 amount := new(big.Int).Div(decreasedAmount, exchangeRate) 110 111 if !known { 112 err = s.addressbook.PutChequebook(peer, cheque.Chequebook) 113 if err != nil { 114 return err 115 } 116 } 117 118 tot, _ := big.NewFloat(0).SetInt(receivedAmount).Float64() 119 s.metrics.TotalReceived.Add(tot) 120 s.metrics.ChequesReceived.Inc() 121 122 return s.accounting.NotifyPaymentReceived(peer, amount) 123 } 124 125 // Pay initiates a payment to the given peer 126 func (s *Service) Pay(ctx context.Context, peer swarm.Address, amount *big.Int) { 127 var err error 128 defer func() { 129 if err != nil { 130 s.accounting.NotifyPaymentSent(peer, amount, err) 131 } 132 }() 133 if s.chequebook == nil { 134 err = ErrNoChequebook 135 return 136 } 137 beneficiary, known, err := s.addressbook.Beneficiary(peer) 138 if err != nil { 139 return 140 } 141 if !known { 142 err = ErrUnknownBeneficary 143 return 144 } 145 146 balance, err := s.proto.EmitCheque(ctx, peer, beneficiary, amount, s.chequebook.Issue) 147 148 if err != nil { 149 return 150 } 151 152 bal, _ := big.NewFloat(0).SetInt(balance).Float64() 153 s.metrics.AvailableBalance.Set(bal) 154 s.accounting.NotifyPaymentSent(peer, amount, nil) 155 amountFloat, _ := big.NewFloat(0).SetInt(amount).Float64() 156 s.metrics.TotalSent.Add(amountFloat) 157 s.metrics.ChequesSent.Inc() 158 } 159 160 func (s *Service) SetAccounting(accounting settlement.Accounting) { 161 s.accounting = accounting 162 } 163 164 // TotalSent returns the total amount sent to a peer 165 func (s *Service) TotalSent(peer swarm.Address) (totalSent *big.Int, err error) { 166 beneficiary, known, err := s.addressbook.Beneficiary(peer) 167 if err != nil { 168 return nil, err 169 } 170 if !known { 171 return nil, settlement.ErrPeerNoSettlements 172 } 173 if s.chequebook == nil { 174 return big.NewInt(0), nil 175 } 176 cheque, err := s.chequebook.LastCheque(beneficiary) 177 if err != nil { 178 if errors.Is(err, chequebook.ErrNoCheque) { 179 return nil, settlement.ErrPeerNoSettlements 180 } 181 return nil, err 182 } 183 return cheque.CumulativePayout, nil 184 } 185 186 // TotalReceived returns the total amount received from a peer 187 func (s *Service) TotalReceived(peer swarm.Address) (totalReceived *big.Int, err error) { 188 chequebookAddress, known, err := s.addressbook.Chequebook(peer) 189 if err != nil { 190 return nil, err 191 } 192 if !known { 193 return nil, settlement.ErrPeerNoSettlements 194 } 195 196 cheque, err := s.chequeStore.LastCheque(chequebookAddress) 197 if err != nil { 198 if errors.Is(err, chequebook.ErrNoCheque) { 199 return nil, settlement.ErrPeerNoSettlements 200 } 201 return nil, err 202 } 203 return cheque.CumulativePayout, nil 204 } 205 206 // SettlementsSent returns sent settlements for each individual known peer 207 func (s *Service) SettlementsSent() (map[string]*big.Int, error) { 208 result := make(map[string]*big.Int) 209 if s.chequebook == nil { 210 return result, nil 211 } 212 cheques, err := s.chequebook.LastCheques() 213 if err != nil { 214 return nil, err 215 } 216 217 for beneficiary, cheque := range cheques { 218 peer, known, err := s.addressbook.BeneficiaryPeer(beneficiary) 219 if err != nil { 220 return nil, err 221 } 222 if !known { 223 continue 224 } 225 result[peer.String()] = cheque.CumulativePayout 226 } 227 228 return result, nil 229 } 230 231 // SettlementsReceived returns received settlements for each individual known peer. 232 func (s *Service) SettlementsReceived() (map[string]*big.Int, error) { 233 result := make(map[string]*big.Int) 234 cheques, err := s.chequeStore.LastCheques() 235 if err != nil { 236 return nil, err 237 } 238 239 for chequebook, cheque := range cheques { 240 peer, known, err := s.addressbook.ChequebookPeer(chequebook) 241 if err != nil { 242 return nil, err 243 } 244 if !known { 245 continue 246 } 247 result[peer.String()] = cheque.CumulativePayout 248 } 249 return result, err 250 } 251 252 // Handshake is called by the swap protocol when a handshake is received. 253 func (s *Service) Handshake(peer swarm.Address, beneficiary common.Address) error { 254 loggerV1 := s.logger.V(1).Register() 255 256 oldPeer, known, err := s.addressbook.BeneficiaryPeer(beneficiary) 257 if err != nil { 258 return err 259 } 260 if known && !peer.Equal(oldPeer) { 261 s.logger.Debug("migrating swap addresses", "old_peer_address", oldPeer, "new_peer_address", peer) 262 return s.addressbook.MigratePeer(oldPeer, peer) 263 } 264 265 _, known, err = s.addressbook.Beneficiary(peer) 266 if err != nil { 267 return err 268 } 269 if !known { 270 loggerV1.Debug("initial swap handshake", "peer_address", peer, "beneficiary_address", beneficiary) 271 return s.addressbook.PutBeneficiary(peer, beneficiary) 272 } 273 274 return nil 275 } 276 277 // LastSentCheque returns the last sent cheque for the peer 278 func (s *Service) LastSentCheque(peer swarm.Address) (*chequebook.SignedCheque, error) { 279 280 common, known, err := s.addressbook.Beneficiary(peer) 281 282 if err != nil { 283 return nil, err 284 } 285 286 if !known { 287 return nil, chequebook.ErrNoCheque 288 } 289 290 if s.chequebook == nil { 291 return nil, ErrNoChequebook 292 } 293 294 return s.chequebook.LastCheque(common) 295 } 296 297 // LastReceivedCheque returns the last received cheque for the peer 298 func (s *Service) LastReceivedCheque(peer swarm.Address) (*chequebook.SignedCheque, error) { 299 300 common, known, err := s.addressbook.Chequebook(peer) 301 302 if err != nil { 303 return nil, err 304 } 305 306 if !known { 307 return nil, chequebook.ErrNoCheque 308 } 309 310 return s.chequeStore.LastCheque(common) 311 } 312 313 // LastSentCheques returns the list of last sent cheques for all peers 314 func (s *Service) LastSentCheques() (map[string]*chequebook.SignedCheque, error) { 315 if s.chequebook == nil { 316 return nil, ErrNoChequebook 317 } 318 lastcheques, err := s.chequebook.LastCheques() 319 if err != nil { 320 return nil, err 321 } 322 323 resultmap := make(map[string]*chequebook.SignedCheque, len(lastcheques)) 324 325 for i, j := range lastcheques { 326 addr, known, err := s.addressbook.BeneficiaryPeer(i) 327 if err == nil && known { 328 resultmap[addr.String()] = j 329 } 330 } 331 332 return resultmap, nil 333 } 334 335 // LastReceivedCheques returns the list of last received cheques for all peers 336 func (s *Service) LastReceivedCheques() (map[string]*chequebook.SignedCheque, error) { 337 lastcheques, err := s.chequeStore.LastCheques() 338 if err != nil { 339 return nil, err 340 } 341 342 resultmap := make(map[string]*chequebook.SignedCheque, len(lastcheques)) 343 344 for i, j := range lastcheques { 345 addr, known, err := s.addressbook.ChequebookPeer(i) 346 if err == nil && known { 347 resultmap[addr.String()] = j 348 } 349 } 350 351 return resultmap, nil 352 } 353 354 // CashCheque sends a cashing transaction for the last cheque of the peer 355 func (s *Service) CashCheque(ctx context.Context, peer swarm.Address) (common.Hash, error) { 356 chequebookAddress, known, err := s.addressbook.Chequebook(peer) 357 if err != nil { 358 return common.Hash{}, err 359 } 360 if !known { 361 return common.Hash{}, chequebook.ErrNoCheque 362 } 363 return s.cashout.CashCheque(ctx, chequebookAddress, s.cashoutAddress) 364 } 365 366 // CashoutStatus gets the status of the latest cashout transaction for the peers chequebook 367 func (s *Service) CashoutStatus(ctx context.Context, peer swarm.Address) (*chequebook.CashoutStatus, error) { 368 chequebookAddress, known, err := s.addressbook.Chequebook(peer) 369 if err != nil { 370 return nil, err 371 } 372 if !known { 373 return nil, chequebook.ErrNoCheque 374 } 375 return s.cashout.CashoutStatus(ctx, chequebookAddress) 376 } 377 378 func (s *Service) GetDeductionForPeer(peer swarm.Address) (bool, error) { 379 return s.addressbook.GetDeductionFor(peer) 380 } 381 382 func (s *Service) GetDeductionByPeer(peer swarm.Address) (bool, error) { 383 return s.addressbook.GetDeductionBy(peer) 384 } 385 386 func (s *Service) AddDeductionByPeer(peer swarm.Address) error { 387 return s.addressbook.AddDeductionBy(peer) 388 } 389 390 type NoOpSwap struct { 391 } 392 393 func (*NoOpSwap) TotalSent(peer swarm.Address) (totalSent *big.Int, err error) { 394 return nil, postagecontract.ErrChainDisabled 395 } 396 397 // TotalReceived returns the total amount received from a peer 398 func (*NoOpSwap) TotalReceived(peer swarm.Address) (totalSent *big.Int, err error) { 399 return nil, postagecontract.ErrChainDisabled 400 } 401 402 // SettlementsSent returns sent settlements for each individual known peer 403 func (*NoOpSwap) SettlementsSent() (map[string]*big.Int, error) { 404 return nil, postagecontract.ErrChainDisabled 405 } 406 407 // SettlementsReceived returns received settlements for each individual known peer 408 func (*NoOpSwap) SettlementsReceived() (map[string]*big.Int, error) { 409 return nil, postagecontract.ErrChainDisabled 410 } 411 412 func (*NoOpSwap) LastSentCheque(peer swarm.Address) (*chequebook.SignedCheque, error) { 413 return nil, postagecontract.ErrChainDisabled 414 } 415 416 // LastSentCheques returns the list of last sent cheques for all peers 417 func (*NoOpSwap) LastSentCheques() (map[string]*chequebook.SignedCheque, error) { 418 return nil, postagecontract.ErrChainDisabled 419 } 420 421 // LastReceivedCheque returns the last received cheque for the peer 422 func (*NoOpSwap) LastReceivedCheque(peer swarm.Address) (*chequebook.SignedCheque, error) { 423 return nil, postagecontract.ErrChainDisabled 424 } 425 426 // LastReceivedCheques returns the list of last received cheques for all peers 427 func (*NoOpSwap) LastReceivedCheques() (map[string]*chequebook.SignedCheque, error) { 428 return nil, postagecontract.ErrChainDisabled 429 } 430 431 // CashCheque sends a cashing transaction for the last cheque of the peer 432 func (*NoOpSwap) CashCheque(ctx context.Context, peer swarm.Address) (common.Hash, error) { 433 return common.Hash{}, postagecontract.ErrChainDisabled 434 } 435 436 // CashoutStatus gets the status of the latest cashout transaction for the peers chequebook 437 func (*NoOpSwap) CashoutStatus(ctx context.Context, peer swarm.Address) (*chequebook.CashoutStatus, error) { 438 return nil, postagecontract.ErrChainDisabled 439 }