github.com/insight-chain/inb-go@v1.1.3-0.20191221022159-da049980ae38/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/insight-chain/inb-go/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 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 creates 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 instantiates the given metrics and 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 an empty registry 117 registry := metrics.NewRegistry() 118 //instantiate the metrics 119 mBalanceCredit = metrics.NewRegisteredCounterForced("account.balance.credit", registry) 120 mBalanceDebit = metrics.NewRegisteredCounterForced("account.balance.debit", registry) 121 mBytesCredit = metrics.NewRegisteredCounterForced("account.bytes.credit", registry) 122 mBytesDebit = metrics.NewRegisteredCounterForced("account.bytes.debit", registry) 123 mMsgCredit = metrics.NewRegisteredCounterForced("account.msg.credit", registry) 124 mMsgDebit = metrics.NewRegisteredCounterForced("account.msg.debit", registry) 125 mPeerDrops = metrics.NewRegisteredCounterForced("account.peerdrops", registry) 126 mSelfDrops = metrics.NewRegisteredCounterForced("account.selfdrops", registry) 127 //create the DB and start persisting 128 return NewAccountingMetrics(registry, reportInterval, path) 129 } 130 131 //Implement Hook.Send 132 // Send takes a peer, a size and a msg and 133 // - calculates the cost for the local node sending a msg of size to peer using the Prices interface 134 // - credits/debits local node using balance interface 135 func (ah *Accounting) Send(peer *Peer, size uint32, msg interface{}) error { 136 //get the price for a message (through the protocol spec) 137 price := ah.Price(msg) 138 //this message doesn't need accounting 139 if price == nil { 140 return nil 141 } 142 //evaluate the price for sending messages 143 costToLocalNode := price.For(Sender, size) 144 //do the accounting 145 err := ah.Add(costToLocalNode, peer) 146 //record metrics: just increase counters for user-facing metrics 147 ah.doMetrics(costToLocalNode, size, err) 148 return err 149 } 150 151 //Implement Hook.Receive 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 }