github.com/cosmos/cosmos-sdk@v0.50.10/docs/architecture/adr-048-consensus-fees.md (about) 1 # ADR 048: Multi Tire Gas Price System 2 3 ## Changelog 4 5 * Dec 1, 2021: Initial Draft 6 7 ## Status 8 9 Rejected 10 11 ## Abstract 12 13 This ADR describes a flexible mechanism to maintain a consensus level gas prices, in which one can choose a multi-tier gas price system or EIP-1559 like one through configuration. 14 15 ## Context 16 17 Currently, each validator configures it's own `minimal-gas-prices` in `app.yaml`. But setting a proper minimal gas price is critical to protect network from dos attack, and it's hard for all the validators to pick a sensible value, so we propose to maintain a gas price in consensus level. 18 19 Since tendermint 0.34.20 has supported mempool prioritization, we can take advantage of that to implement more sophisticated gas fee system. 20 21 ## Multi-Tier Price System 22 23 We propose a multi-tier price system on consensus to provide maximum flexibility: 24 25 * Tier 1: a constant gas price, which could only be modified occasionally through governance proposal. 26 * Tier 2: a dynamic gas price which is adjusted according to previous block load. 27 * Tier 3: a dynamic gas price which is adjusted according to previous block load at a higher speed. 28 29 The gas price of higher tier should bigger than the lower tier. 30 31 The transaction fees are charged with the exact gas price calculated on consensus. 32 33 The parameter schema is like this: 34 35 ```protobuf 36 message TierParams { 37 uint32 priority = 1 // priority in tendermint mempool 38 Coin initial_gas_price = 2 // 39 uint32 parent_gas_target = 3 // the target saturation of block 40 uint32 change_denominator = 4 // decides the change speed 41 Coin min_gas_price = 5 // optional lower bound of the price adjustment 42 Coin max_gas_price = 6 // optional upper bound of the price adjustment 43 } 44 45 message Params { 46 repeated TierParams tiers = 1; 47 } 48 ``` 49 50 ### Extension Options 51 52 We need to allow user to specify the tier of service for the transaction, to support it in an extensible way, we add an extension option in `AuthInfo`: 53 54 ```protobuf 55 message ExtensionOptionsTieredTx { 56 uint32 fee_tier = 1 57 } 58 ``` 59 60 The value of `fee_tier` is just the index to the `tiers` parameter list. 61 62 We also change the semantic of existing `fee` field of `Tx`, instead of charging user the exact `fee` amount, we treat it as a fee cap, while the actual amount of fee charged is decided dynamically. If the `fee` is smaller than dynamic one, the transaction won't be included in current block and ideally should stay in the mempool until the consensus gas price drop. The mempool can eventually prune old transactions. 63 64 ### Tx Prioritization 65 66 Transactions are prioritized based on the tier, the higher the tier, the higher the priority. 67 68 Within the same tier, follow the default Tendermint order (currently FIFO). Be aware of that the mempool tx ordering logic is not part of consensus and can be modified by malicious validator. 69 70 This mechanism can be easily composed with prioritization mechanisms: 71 72 * we can add extra tiers out of a user control: 73 * Example 1: user can set tier 0, 10 or 20, but the protocol will create tiers 0, 1, 2 ... 29. For example IBC transactions will go to tier `user_tier + 5`: if user selected tier 1, then the transaction will go to tier 15. 74 * Example 2: we can reserve tier 4, 5, ... only for special transaction types. For example, tier 5 is reserved for evidence tx. So if submits a bank.Send transaction and set tier 5, it will be delegated to tier 3 (the max tier level available for any transaction). 75 * Example 3: we can enforce that all transactions of a sepecific type will go to specific tier. For example, tier 100 will be reserved for evidence transactions and all evidence transactions will always go to that tier. 76 77 ### `min-gas-prices` 78 79 Deprecate the current per-validator `min-gas-prices` configuration, since it would confusing for it to work together with the consensus gas price. 80 81 ### Adjust For Block Load 82 83 For tier 2 and tier 3 transactions, the gas price is adjusted according to previous block load, the logic could be similar to EIP-1559: 84 85 ```python 86 def adjust_gas_price(gas_price, parent_gas_used, tier): 87 if parent_gas_used == tier.parent_gas_target: 88 return gas_price 89 elif parent_gas_used > tier.parent_gas_target: 90 gas_used_delta = parent_gas_used - tier.parent_gas_target 91 gas_price_delta = max(gas_price * gas_used_delta // tier.parent_gas_target // tier.change_speed, 1) 92 return gas_price + gas_price_delta 93 else: 94 gas_used_delta = parent_gas_target - parent_gas_used 95 gas_price_delta = gas_price * gas_used_delta // parent_gas_target // tier.change_speed 96 return gas_price - gas_price_delta 97 ``` 98 99 ### Block Segment Reservation 100 101 Ideally we should reserve block segments for each tier, so the lower tiered transactions won't be completely squeezed out by higher tier transactions, which will force user to use higher tier, and the system degraded to a single tier. 102 103 We need help from tendermint to implement this. 104 105 ## Implementation 106 107 We can make each tier's gas price strategy fully configurable in protocol parameters, while providing a sensible default one. 108 109 Pseudocode in python-like syntax: 110 111 ```python 112 interface TieredTx: 113 def tier(self) -> int: 114 pass 115 116 def tx_tier(tx): 117 if isinstance(tx, TieredTx): 118 return tx.tier() 119 else: 120 # default tier for custom transactions 121 return 0 122 # NOTE: we can add more rules here per "Tx Prioritization" section 123 124 class TierParams: 125 'gas price strategy parameters of one tier' 126 priority: int # priority in tendermint mempool 127 initial_gas_price: Coin 128 parent_gas_target: int 129 change_speed: Decimal # 0 means don't adjust for block load. 130 131 class Params: 132 'protocol parameters' 133 tiers: List[TierParams] 134 135 class State: 136 'consensus state' 137 # total gas used in last block, None when it's the first block 138 parent_gas_used: Optional[int] 139 # gas prices of last block for all tiers 140 gas_prices: List[Coin] 141 142 def begin_block(): 143 'Adjust gas prices' 144 for i, tier in enumerate(Params.tiers): 145 if State.parent_gas_used is None: 146 # initialized gas price for the first block 147 State.gas_prices[i] = tier.initial_gas_price 148 else: 149 # adjust gas price according to gas used in previous block 150 State.gas_prices[i] = adjust_gas_price(State.gas_prices[i], State.parent_gas_used, tier) 151 152 def mempoolFeeTxHandler_checkTx(ctx, tx): 153 # the minimal-gas-price configured by validator, zero in deliver_tx context 154 validator_price = ctx.MinGasPrice() 155 consensus_price = State.gas_prices[tx_tier(tx)] 156 min_price = max(validator_price, consensus_price) 157 158 # zero means infinity for gas price cap 159 if tx.gas_price() > 0 and tx.gas_price() < min_price: 160 return 'insufficient fees' 161 return next_CheckTx(ctx, tx) 162 163 def txPriorityHandler_checkTx(ctx, tx): 164 res, err := next_CheckTx(ctx, tx) 165 # pass priority to tendermint 166 res.Priority = Params.tiers[tx_tier(tx)].priority 167 return res, err 168 169 def end_block(): 170 'Update block gas used' 171 State.parent_gas_used = block_gas_meter.consumed() 172 ``` 173 174 ### Dos attack protection 175 176 To fully saturate the blocks and prevent other transactions from executing, attacker need to use transactions of highest tier, the cost would be significantly higher than the default tier. 177 178 If attacker spam with lower tier transactions, user can mitigate by sending higher tier transactions. 179 180 ## Consequences 181 182 ### Backwards Compatibility 183 184 * New protocol parameters. 185 * New consensus states. 186 * New/changed fields in transaction body. 187 188 ### Positive 189 190 * The default tier keeps the same predictable gas price experience for client. 191 * The higher tier's gas price can adapt to block load. 192 * No priority conflict with custom priority based on transaction types, since this proposal only occupy three priority levels. 193 * Possibility to compose different priority rules with tiers 194 195 ### Negative 196 197 * Wallets & tools need to update to support the new `tier` parameter, and semantic of `fee` field is changed. 198 199 ### Neutral 200 201 ## References 202 203 * https://eips.ethereum.org/EIPS/eip-1559 204 * https://iohk.io/en/blog/posts/2021/11/26/network-traffic-and-tiered-pricing/