Some message types may be more important than others and should have priority over them. That is, the more significant a message type is, the more quickly it should be included in a block, before other messages are.
Failing to prioritize message types allows attackers to front-run them, possibly gaining unfair advantage. Moreover, during high network congestion, the message may be simply not included in a block for a long period, causing the system to malfunction.
In the Cosmos's mempool, transactions are ordered in first-in-first-out (FIFO) manner by default. Especially, there is no fee-based ordering.
An example application implements a lending platform. It uses a price oracle mechanism, where privileged entities can vote on new assets' prices. The mechanism is implemented as standard messages.
service Msg {
rpc Lend(MsgLend) returns (MsgLendResponse);
rpc Borrow(MsgBorrow) returns (MsgBorrowResponse);
rpc Liquidate(MsgLiquidate) returns (MsgLiquidateResponse);
rpc OracleCommitPrice(MsgOracleCommitPrice) returns (MsgOracleCommitPriceResponse);
rpc OracleRevealPrice(MsgOracleRevealPrice) returns (MsgOracleRevealPriceResponse);
}
Prices ought to be updated (committed and revealed) after every voting period. However, an attacker can spam the network with low-cost transactions to completely fill blocks, leaving no space for price updates. He can then profit from the fact that the system uses outdated, stale prices.
Lets consider a liquidity pool application that implements the following message types:
service Msg {
rpc CreatePool(MsgCreatePool) returns (MsgCreatePoolResponse);
rpc Deposit(MsgDeposit) returns (MsgDepositResponse);
rpc Withdraw(MsgWithdraw) returns (MsgWithdrawResponse);
rpc Swap(MsgSwap) returns (MsgSwapResponse);
rpc Pause(MsgPause) returns (MsgPauseResponse);
rpc Resume(MsgResume) returns (MsgResumeResponse);
}
There is the Pause
message, which allows privileged users to stop the pool.
Once a bug in pool's implementation is discovered, attackers and the pool's operators will compete for whose message is first executed (Swap
vs Pause
). Prioritizing Pause
messages will help pool's operators to prevent exploitation, but in this case it won't stop the attackers completely. They can outrun the Pause
message by order of magnitude - so the priority will not matter - or even cooperate with a malicious validator node - who can order his mempool in an arbitrary way.
- Use
CheckTx
'spriority
return value to prioritize messages. Please note that this feature has a transaction (not a message) granularity - users can send multiple messages in a single transaction, and it is the transaction that will have to be prioritized. - Perform authorization for prioritized transactions as early as possible. That is, during the
CheckTx
phase. This will prevent attackers from filling whole blocks with invalid, but prioritized transactions. In other words, implement a mechanism that will prevent validators from accepting not-authorized, prioritized messages into a mempool. - Alternatively, charge a high fee for prioritized transactions to disincentivize attackers.
- Terra Money's oracle messages were not prioritized (search for "priority"). It was fixed with modifications to Tendermint.
- Umee oracle and orchestrator messages were not prioritized (search for finding TOB-UMEE-20 and TOB-UMEE-31).