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  }