github.com/lukso-network/go-ethereum@v1.8.22/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 ( 20 "time" 21 22 "github.com/ethereum/go-ethereum/metrics" 23 ) 24 25 // define some metrics 26 var ( 27 // All metrics are cumulative 28 29 // total amount of units credited 30 mBalanceCredit metrics.Counter 31 // total amount of units debited 32 mBalanceDebit metrics.Counter 33 // total amount of bytes credited 34 mBytesCredit metrics.Counter 35 // total amount of bytes debited 36 mBytesDebit metrics.Counter 37 // total amount of credited messages 38 mMsgCredit metrics.Counter 39 // total amount of debited messages 40 mMsgDebit metrics.Counter 41 // how many times local node had to drop remote peers 42 mPeerDrops metrics.Counter 43 // how many times local node overdrafted and dropped 44 mSelfDrops metrics.Counter 45 46 MetricsRegistry metrics.Registry 47 ) 48 49 // Prices defines how prices are being passed on to the accounting instance 50 type Prices interface { 51 // Return the Price for a message 52 Price(interface{}) *Price 53 } 54 55 type Payer bool 56 57 const ( 58 Sender = Payer(true) 59 Receiver = Payer(false) 60 ) 61 62 // Price represents the costs of a message 63 type Price struct { 64 Value uint64 65 PerByte bool // True if the price is per byte or for unit 66 Payer Payer 67 } 68 69 // For gives back the price for a message 70 // A protocol provides the message price in absolute value 71 // This method then returns the correct signed amount, 72 // depending on who pays, which is identified by the `payer` argument: 73 // `Send` will pass a `Sender` payer, `Receive` will pass the `Receiver` argument. 74 // Thus: If Sending and sender pays, amount positive, otherwise negative 75 // If Receiving, and receiver pays, amount positive, otherwise negative 76 func (p *Price) For(payer Payer, size uint32) int64 { 77 price := p.Value 78 if p.PerByte { 79 price *= uint64(size) 80 } 81 if p.Payer == payer { 82 return 0 - int64(price) 83 } 84 return int64(price) 85 } 86 87 // Balance is the actual accounting instance 88 // Balance defines the operations needed for accounting 89 // Implementations internally maintain the balance for every peer 90 type Balance interface { 91 // Adds amount to the local balance with remote node `peer`; 92 // positive amount = credit local node 93 // negative amount = debit local node 94 Add(amount int64, peer *Peer) error 95 } 96 97 // Accounting implements the Hook interface 98 // It interfaces to the balances through the Balance interface, 99 // while interfacing with protocols and its prices through the Prices interface 100 type Accounting struct { 101 Balance // interface to accounting logic 102 Prices // interface to prices logic 103 } 104 105 func NewAccounting(balance Balance, po Prices) *Accounting { 106 ah := &Accounting{ 107 Prices: po, 108 Balance: balance, 109 } 110 return ah 111 } 112 113 // SetupAccountingMetrics creates a separate registry for p2p accounting metrics; 114 // this registry should be independent of any other metrics as it persists at different endpoints. 115 // It also instantiates the given metrics and starts the persisting go-routine which 116 // at the passed interval writes the metrics to a LevelDB 117 func SetupAccountingMetrics(reportInterval time.Duration, path string) *AccountingMetrics { 118 // create an empty registry 119 MetricsRegistry = metrics.NewRegistry() 120 // instantiate the metrics 121 mBalanceCredit = metrics.NewRegisteredCounterForced("account.balance.credit", MetricsRegistry) 122 mBalanceDebit = metrics.NewRegisteredCounterForced("account.balance.debit", MetricsRegistry) 123 mBytesCredit = metrics.NewRegisteredCounterForced("account.bytes.credit", MetricsRegistry) 124 mBytesDebit = metrics.NewRegisteredCounterForced("account.bytes.debit", MetricsRegistry) 125 mMsgCredit = metrics.NewRegisteredCounterForced("account.msg.credit", MetricsRegistry) 126 mMsgDebit = metrics.NewRegisteredCounterForced("account.msg.debit", MetricsRegistry) 127 mPeerDrops = metrics.NewRegisteredCounterForced("account.peerdrops", MetricsRegistry) 128 mSelfDrops = metrics.NewRegisteredCounterForced("account.selfdrops", MetricsRegistry) 129 // create the DB and start persisting 130 return NewAccountingMetrics(MetricsRegistry, reportInterval, path) 131 } 132 133 // Send takes a peer, a size and a msg and 134 // - calculates the cost for the local node sending a msg of size to peer using the Prices interface 135 // - credits/debits local node using balance interface 136 func (ah *Accounting) Send(peer *Peer, size uint32, msg interface{}) error { 137 // get the price for a message (through the protocol spec) 138 price := ah.Price(msg) 139 // this message doesn't need accounting 140 if price == nil { 141 return nil 142 } 143 // evaluate the price for sending messages 144 costToLocalNode := price.For(Sender, size) 145 // do the accounting 146 err := ah.Add(costToLocalNode, peer) 147 // record metrics: just increase counters for user-facing metrics 148 ah.doMetrics(costToLocalNode, size, err) 149 return err 150 } 151 152 // Receive takes a peer, a size and a msg and 153 // - calculates the cost for the local node receiving a msg of size from peer using the Prices interface 154 // - credits/debits local node using balance interface 155 func (ah *Accounting) Receive(peer *Peer, size uint32, msg interface{}) error { 156 // get the price for a message (through the protocol spec) 157 price := ah.Price(msg) 158 // this message doesn't need accounting 159 if price == nil { 160 return nil 161 } 162 // evaluate the price for receiving messages 163 costToLocalNode := price.For(Receiver, size) 164 // do the accounting 165 err := ah.Add(costToLocalNode, peer) 166 // record metrics: just increase counters for user-facing metrics 167 ah.doMetrics(costToLocalNode, size, err) 168 return err 169 } 170 171 // record some metrics 172 // this is not an error handling. `err` is returned by both `Send` and `Receive` 173 // `err` will only be non-nil if a limit has been violated (overdraft), in which case the peer has been dropped. 174 // if the limit has been violated and `err` is thus not nil: 175 // * if the price is positive, local node has been credited; thus `err` implicitly signals the REMOTE has been dropped 176 // * if the price is negative, local node has been debited, thus `err` implicitly signals LOCAL node "overdraft" 177 func (ah *Accounting) doMetrics(price int64, size uint32, err error) { 178 if price > 0 { 179 mBalanceCredit.Inc(price) 180 mBytesCredit.Inc(int64(size)) 181 mMsgCredit.Inc(1) 182 if err != nil { 183 // increase the number of times a remote node has been dropped due to "overdraft" 184 mPeerDrops.Inc(1) 185 } 186 } else { 187 mBalanceDebit.Inc(price) 188 mBytesDebit.Inc(int64(size)) 189 mMsgDebit.Inc(1) 190 if err != nil { 191 // increase the number of times the local node has done an "overdraft" in respect to other nodes 192 mSelfDrops.Inc(1) 193 } 194 } 195 }