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