github.com/ethersphere/bee/v2@v2.2.0/pkg/pricing/pricing.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 pricing 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "math/big" 12 "time" 13 14 "github.com/ethersphere/bee/v2/pkg/log" 15 "github.com/ethersphere/bee/v2/pkg/p2p" 16 "github.com/ethersphere/bee/v2/pkg/p2p/protobuf" 17 "github.com/ethersphere/bee/v2/pkg/pricing/pb" 18 "github.com/ethersphere/bee/v2/pkg/swarm" 19 ) 20 21 // loggerName is the tree path name of the logger for this package. 22 const loggerName = "pricing" 23 24 const ( 25 protocolName = "pricing" 26 protocolVersion = "1.0.0" 27 streamName = "pricing" 28 ) 29 30 var ( 31 // ErrThresholdTooLow says that the proposed payment threshold is too low for even a single reserve. 32 ErrThresholdTooLow = errors.New("threshold too low") 33 ) 34 35 var _ Interface = (*Service)(nil) 36 37 // Interface is the main interface of the pricing protocol 38 type Interface interface { 39 AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold *big.Int) error 40 } 41 42 // PaymentThresholdObserver is used for being notified of payment threshold updates 43 type PaymentThresholdObserver interface { 44 NotifyPaymentThreshold(peer swarm.Address, paymentThreshold *big.Int) error 45 } 46 47 type Service struct { 48 streamer p2p.Streamer 49 logger log.Logger 50 paymentThreshold *big.Int 51 lightPaymentThreshold *big.Int 52 minPaymentThreshold *big.Int 53 paymentThresholdObserver PaymentThresholdObserver 54 } 55 56 func New(streamer p2p.Streamer, logger log.Logger, paymentThreshold, lightPaymentThreshold, minThreshold *big.Int) *Service { 57 return &Service{ 58 streamer: streamer, 59 logger: logger.WithName(loggerName).Register(), 60 paymentThreshold: paymentThreshold, 61 lightPaymentThreshold: lightPaymentThreshold, 62 minPaymentThreshold: minThreshold, 63 } 64 } 65 66 func (s *Service) Protocol() p2p.ProtocolSpec { 67 return p2p.ProtocolSpec{ 68 Name: protocolName, 69 Version: protocolVersion, 70 StreamSpecs: []p2p.StreamSpec{ 71 { 72 Name: streamName, 73 Handler: s.handler, 74 }, 75 }, 76 ConnectIn: s.init, 77 ConnectOut: s.init, 78 } 79 } 80 81 func (s *Service) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) (err error) { 82 loggerV1 := s.logger.V(1).Register() 83 84 r := protobuf.NewReader(stream) 85 defer func() { 86 if err != nil { 87 _ = stream.Reset() 88 } else { 89 _ = stream.FullClose() 90 } 91 }() 92 93 var req pb.AnnouncePaymentThreshold 94 if err := r.ReadMsgWithContext(ctx, &req); err != nil { 95 s.logger.Debug("could not receive payment threshold and/or price table announcement from peer", "peer_address", p.Address) 96 return fmt.Errorf("read request from peer %v: %w", p.Address, err) 97 } 98 99 paymentThreshold := big.NewInt(0).SetBytes(req.PaymentThreshold) 100 loggerV1.Debug("received payment threshold announcement from peer", "peer_address", p.Address, "payment_threshold", paymentThreshold) 101 102 if paymentThreshold.Cmp(s.minPaymentThreshold) < 0 { 103 loggerV1.Debug("payment threshold from peer too small, need at least min payment threshold", "peer_address", p.Address, "payment_threshold", paymentThreshold, "min_payment_threshold", s.minPaymentThreshold) 104 return p2p.NewDisconnectError(ErrThresholdTooLow) 105 } 106 107 if paymentThreshold.Cmp(big.NewInt(0)) == 0 { 108 return err 109 } 110 return s.paymentThresholdObserver.NotifyPaymentThreshold(p.Address, paymentThreshold) 111 } 112 113 func (s *Service) init(ctx context.Context, p p2p.Peer) error { 114 115 threshold := s.paymentThreshold 116 if !p.FullNode { 117 threshold = s.lightPaymentThreshold 118 } 119 120 err := s.AnnouncePaymentThreshold(ctx, p.Address, threshold) 121 if err != nil { 122 s.logger.Warning("could not send payment threshold announcement to peer", "peer_address", p.Address) 123 } 124 return err 125 } 126 127 // AnnouncePaymentThreshold announces the payment threshold to per 128 func (s *Service) AnnouncePaymentThreshold(ctx context.Context, peer swarm.Address, paymentThreshold *big.Int) error { 129 loggerV1 := s.logger.V(1).Register() 130 131 ctx, cancel := context.WithTimeout(ctx, 5*time.Second) 132 defer cancel() 133 134 stream, err := s.streamer.NewStream(ctx, peer, nil, protocolName, protocolVersion, streamName) 135 if err != nil { 136 return err 137 } 138 defer func() { 139 if err != nil { 140 _ = stream.Reset() 141 } else { 142 stream.FullClose() 143 } 144 }() 145 146 loggerV1.Debug("sending payment threshold announcement to peer", "peer_address", peer, "payment_threshold", paymentThreshold) 147 w := protobuf.NewWriter(stream) 148 err = w.WriteMsgWithContext(ctx, &pb.AnnouncePaymentThreshold{ 149 PaymentThreshold: paymentThreshold.Bytes(), 150 }) 151 152 return err 153 } 154 155 // SetPaymentThresholdObserver sets the PaymentThresholdObserver to be used when receiving a new payment threshold 156 func (s *Service) SetPaymentThresholdObserver(observer PaymentThresholdObserver) { 157 s.paymentThresholdObserver = observer 158 }