github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/p2p/protocols/accounting.go (about)

     1  // Copyright 2018 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package protocols
    18  
    19  import "github.com/ethereum/go-ethereum/metrics"
    20  
    21  //define some metrics
    22  var (
    23  	//NOTE: these metrics just define the interfaces and are currently *NOT persisted* over sessions
    24  	//All metrics are cumulative
    25  
    26  	//total amount of units credited
    27  	mBalanceCredit = metrics.NewRegisteredCounterForced("account.balance.credit", nil)
    28  	//total amount of units debited
    29  	mBalanceDebit = metrics.NewRegisteredCounterForced("account.balance.debit", nil)
    30  	//total amount of bytes credited
    31  	mBytesCredit = metrics.NewRegisteredCounterForced("account.bytes.credit", nil)
    32  	//total amount of bytes debited
    33  	mBytesDebit = metrics.NewRegisteredCounterForced("account.bytes.debit", nil)
    34  	//total amount of credited messages
    35  	mMsgCredit = metrics.NewRegisteredCounterForced("account.msg.credit", nil)
    36  	//total amount of debited messages
    37  	mMsgDebit = metrics.NewRegisteredCounterForced("account.msg.debit", nil)
    38  	//how many times local node had to drop remote peers
    39  	mPeerDrops = metrics.NewRegisteredCounterForced("account.peerdrops", nil)
    40  	//how many times local node overdrafted and dropped
    41  	mSelfDrops = metrics.NewRegisteredCounterForced("account.selfdrops", nil)
    42  )
    43  
    44  //Prices defines how prices are being passed on to the accounting instance
    45  type Prices interface {
    46  	//Return the Price for a message
    47  	Price(interface{}) *Price
    48  }
    49  
    50  type Payer bool
    51  
    52  const (
    53  	Sender   = Payer(true)
    54  	Receiver = Payer(false)
    55  )
    56  
    57  //Price represents the costs of a message
    58  type Price struct {
    59  	Value   uint64 //
    60  	PerByte bool   //True if the price is per byte or for unit
    61  	Payer   Payer
    62  }
    63  
    64  //For gives back the price for a message
    65  //A protocol provides the message price in absolute value
    66  //This method then returns the correct signed amount,
    67  //depending on who pays, which is identified by the `payer` argument:
    68  //`Send` will pass a `Sender` payer, `Receive` will pass the `Receiver` argument.
    69  //Thus: If Sending and sender pays, amount positive, otherwise negative
    70  //If Receiving, and receiver pays, amount positive, otherwise negative
    71  func (p *Price) For(payer Payer, size uint32) int64 {
    72  	price := p.Value
    73  	if p.PerByte {
    74  		price *= uint64(size)
    75  	}
    76  	if p.Payer == payer {
    77  		return 0 - int64(price)
    78  	}
    79  	return int64(price)
    80  }
    81  
    82  //Balance is the actual accounting instance
    83  //Balance defines the operations needed for accounting
    84  //Implementations internally maintain the balance for every peer
    85  type Balance interface {
    86  	//Adds amount to the local balance with remote node `peer`;
    87  	//positive amount = credit local node
    88  	//negative amount = debit local node
    89  	Add(amount int64, peer *Peer) error
    90  }
    91  
    92  //Accounting implements the Hook interface
    93  //It interfaces to the balances through the Balance interface,
    94  //while interfacing with protocols and its prices through the Prices interface
    95  type Accounting struct {
    96  	Balance //interface to accounting logic
    97  	Prices  //interface to prices logic
    98  }
    99  
   100  func NewAccounting(balance Balance, po Prices) *Accounting {
   101  	ah := &Accounting{
   102  		Prices:  po,
   103  		Balance: balance,
   104  	}
   105  	return ah
   106  }
   107  
   108  //Implement Hook.Send
   109  // Send takes a peer, a size and a msg and
   110  // - calculates the cost for the local node sending a msg of size to peer using the Prices interface
   111  // - credits/debits local node using balance interface
   112  func (ah *Accounting) Send(peer *Peer, size uint32, msg interface{}) error {
   113  	//get the price for a message (through the protocol spec)
   114  	price := ah.Price(msg)
   115  	//this message doesn't need accounting
   116  	if price == nil {
   117  		return nil
   118  	}
   119  	//evaluate the price for sending messages
   120  	costToLocalNode := price.For(Sender, size)
   121  	//do the accounting
   122  	err := ah.Add(costToLocalNode, peer)
   123  	//record metrics: just increase counters for user-facing metrics
   124  	ah.doMetrics(costToLocalNode, size, err)
   125  	return err
   126  }
   127  
   128  //Implement Hook.Receive
   129  // Receive takes a peer, a size and a msg and
   130  // - calculates the cost for the local node receiving a msg of size from peer using the Prices interface
   131  // - credits/debits local node using balance interface
   132  func (ah *Accounting) Receive(peer *Peer, size uint32, msg interface{}) error {
   133  	//get the price for a message (through the protocol spec)
   134  	price := ah.Price(msg)
   135  	//this message doesn't need accounting
   136  	if price == nil {
   137  		return nil
   138  	}
   139  	//evaluate the price for receiving messages
   140  	costToLocalNode := price.For(Receiver, size)
   141  	//do the accounting
   142  	err := ah.Add(costToLocalNode, peer)
   143  	//record metrics: just increase counters for user-facing metrics
   144  	ah.doMetrics(costToLocalNode, size, err)
   145  	return err
   146  }
   147  
   148  //record some metrics
   149  //this is not an error handling. `err` is returned by both `Send` and `Receive`
   150  //`err` will only be non-nil if a limit has been violated (overdraft), in which case the peer has been dropped.
   151  //if the limit has been violated and `err` is thus not nil:
   152  // * if the price is positive, local node has been credited; thus `err` implicitly signals the REMOTE has been dropped
   153  // * if the price is negative, local node has been debited, thus `err` implicitly signals LOCAL node "overdraft"
   154  func (ah *Accounting) doMetrics(price int64, size uint32, err error) {
   155  	if price > 0 {
   156  		mBalanceCredit.Inc(price)
   157  		mBytesCredit.Inc(int64(size))
   158  		mMsgCredit.Inc(1)
   159  		if err != nil {
   160  			//increase the number of times a remote node has been dropped due to "overdraft"
   161  			mPeerDrops.Inc(1)
   162  		}
   163  	} else {
   164  		mBalanceDebit.Inc(price)
   165  		mBytesDebit.Inc(int64(size))
   166  		mMsgDebit.Inc(1)
   167  		if err != nil {
   168  			//increase the number of times the local node has done an "overdraft" in respect to other nodes
   169  			mSelfDrops.Inc(1)
   170  		}
   171  	}
   172  }