accounting.go 6.7 KB

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