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 }