Note: Currently this feature is only supported by the Nethermind client.
An xDai/AuRa validator can use the prioritization feature to define transaction inclusion rules. Prioritized transactions are included at the top of a block (over other, non-prioritized, transactions) created by the validator.
This can be helpful if a network is spam-attacked or heavily loaded by a project: the prioritization helps ensure necessary transactions are included first (bridge transactions, for example).
Each validator can set their own prioritization rules that don't depend on the rules of other validators.
There are two independent ways to set prioritization rules:
On-chain (public): deploy a TxPriority smart contract and set the rules accordingly as described in https://github.com/NethermindEth/nethermind/issues/2300.
Off-chain (non-public): create a local, non-public config file with defined rules.
Both methods can be used independently of each other, or can be used in tandem: in this case, on-chain and off-chain rules are merged, with off-chain local rules taking precedence over on-chain ones if they intersect (more details here).
To define the rules (destinations and their weights), a validator first needs to deploy the TxPriority contract. The constructor accepts the address of the contract's owner. Only the contract's owner can manage it.
After the contract is deployed, its address needs to be defined in the Aura/TxPriorityContractAddress
config option in the node's config. Example config: https://github.com/poanetwork/posdao-test-setup/blob/86d6a3d057a6c1326517a0c58486c2047e130a7b/config/node1.nethermind.json#L39
After completing the config file modifications, the node must be restarted.
The prioritization rule is a transaction destination and its weight. It consists of the following fields:
tx target address (target)
function's signature (fnSignature)
priority (weight)
The TX target address
is the to
field of the transaction. It defines the address the transaction is sent to and can be either a contract or EOA. It cannot be zero address 0x00...00
.
The function's signature
is the first 4 bytes of a transaction's data
field. It is set as an actual vaue when the target
is a contract address and we want to prioritize one of its functions. For example, if we want to prioritize the transfer(address,uint256)
function of some ERC20 contract, the fnSignature
for that will be 0xa9059cbb
(see How to get Ethereum encoded function signatures).
The fnSignature
can be empty (0x00000000
) if we want to prioritize a transaction which sends native coins to a target
address (regardless of whether it's a contract, or not), i.e. when TX's data
is empty.
The target + fnSignature
is a destination
.
The weight
defines the destination's priority: a destination with a higher weight will have a higher priority than another destination with a lower weight. The weight for each destination is unique and cannot be zero.
For example, we defined the following two rules:
target = 0xaa...bbfnSignature = 0x12345678weight = 2000target = 0xaa...bbfnSignature = 0x90876543weight = 1000
In this case, if there are transactions in the TX pool with such destinations, the transactions matching the first destination (with weight = 2000) will be mined earlier than transactions matching the second one (with weight = 1000). If there is a transaction with the same to
field but with a different signature in the data
(the first 4 bytes), it won't be prioritized because there are no rules (destinations) defined for it.
If we define the following rule
target = 0xcc...ddfnSignature = 0x00000000weight = 3000
it will only apply to transactions with the empty data
field and the to
field equal to 0xcc...dd
.
To manage on-chain priority rules, there are three functions in the TxPriority
contract:
setPriority define/modify a destination with its unique weight.
removePriority remove a previously defined destination (make it non-prioritized).
getPriorities public getter to view all existing on-chain priority rules sorted by weight descending.
For convenience, a validator can verify their TxPriority
contract in Blockscout explorer and use the Read Contract
and Write Contract
tabs in Blockscout's UI to call the functions.
There can be a whitelist of from
addresses which receive top priority (higher than others defined by the setPriority
contract function).
If there is a transaction with the from
field matching one of the whitelisted addresses, such a transaction will be prioritized higher than others, including those set by the setPriority
function.
There are two functions for managing the whitelist in the TxPriority
contract:
getSendersWhitelist public getter.
In some cases, a validator may want to increase the Min Gas Price required to mine specified transactions. This is not for prioritization, but for tx filtering: if a transaction has a gasPrice
lower than the required Min Gas Price, it will not be mined by the validator.
There following functions in TxPriority
contract are used to manage Min Gas Prices:
setMinGasPrice. Note that the minGasPrice
cannot be zero and will be ignored by validator's node if it is less than the MinGasPrice global option defined in node's config.
getMinGasPrices public getter.
Please, see setMinGasPrice
description for details: https://github.com/poanetwork/posdao-contracts/blob/dd38a16d9e049295f4eca6ae74927530a082b22f/contracts/TxPriority.sol#L118-L128
If you skipped the Destinations and weights section above, please read it first to get what target
and fnSignature
fields are.
Everything described above in Defining priority rules with the smart contract can be set through a local config file. If you didn't read the section above, please read it first to understand the basic principles.
To define the config path in your filesystem, use the Aura/TxPriorityConfigFilePath
option in node's config. Example: https://github.com/poanetwork/posdao-test-setup/blob/86d6a3d057a6c1326517a0c58486c2047e130a7b/config/node1.nethermind.json#L40
Note: it is better to use an absolute path to the file.
The TxPriority config file should be JSON formatted. Example of the file with different rules:
{"whitelist": ["0x475674cb523a0a2736b7f7534390288fce16982c","0x692921b14f1b1c385cd7e0cc2ef7abe5598c1234"],"priorities": [{"target": "0x113321b14f1b1c385cd7e0cc2ef7abe5598c4321","fnSignature": "0x00010203","value": "300"},{"target": "0x76e68a8696537e4141926f3e528733af9e237d69","fnSignature": "0x00010203","value": "200"},{"target": "0x942921b14f1b1c385cd7e0cc2ef7abe5598c8358","fnSignature": "0x00000000","value": "100"}],"minGasPrices": [{"target": "0x762921b14f1b1c385cd7e0cc2ef7abe5598c8459","fnSignature": "0x00010203","value": "5000000000"},{"target": "0x83e68a8696537e4141926f3e528733af9e237e78","fnSignature": "0x00010203","value": "3000000000"},{"target": "0x552921b14f1b1c385cd7e0cc2ef7abe5598c8473","fnSignature": "0x00000000","value": "2000000000"}]}
The file with empty rules looks like this:
{"whitelist": [],"priorities": [],"minGasPrices": []}
The changes in the TxPriority config file take effect immediately without needing to restart the node.
As mentioned above, both methods (on-chain and off-chain) can be used independently of each other, or they can be used at the same time.
Rules are merged when combining on-chain and off-chain rules. In the case of intersecting information, off-chain local rules overrule the on-chain ones. For example:
We make the following call:
setPriority(0x443321b14f1b1c385cd7e0cc2ef7abe5598c0000, 0x12345678, 1000)
and we set a local (off-chain) rule for the same destination but with a different weight:
{"whitelist": [],"priorities": [{"target": "0x443321b14f1b1c385cd7e0cc2ef7abe5598c0000","fnSignature": "0x12345678","value": "2000"}],"minGasPrices": []}
The weight for the destination target = 0x443321b14f1b1c385cd7e0cc2ef7abe5598c0000, fnSignature = 0x12345678
will have the weight of 2000
because the local (off-chain) rule takes precedence over the rule from the TxPriority contract.
The same goes for minGasPrices
rules.
Note that whitelist
rules are combined from both on-chain and off-chain methods. For example we make the following call:
setSendersWhitelist([0x226621b14f1b1c385cd7e0cc2ef7abe5598c5555])
and we whitelisted another address in the local file:
{"whitelist": ["0x885521b14f1b1c385cd7e0cc2ef7abe5598c7777"],"priorities": [],"minGasPrices": []}
As a result, both addresses are whitelisted:
0x226621b14f1b1c385cd7e0cc2ef7abe5598c5555
0x885521b14f1b1c385cd7e0cc2ef7abe5598c7777
4.1. If an address sends two transactions with the same gasPrice
and nonce
, the transaction with the higher weight will be picked up (if its destination is prioritized).
4.2. If an address sends two transactions with different nonces, the transaction with a lower nonce will be picked up earlier than the second transaction with a higher nonce regardless of whether the transactions are prioritized or not.
4.3. If an address sends two transactions with the same nonce, but with different gasPrices, the transaction with a higher gas price will be picked up earlier than the second transaction having a lower gas price regardless of whether the transactions are prioritized or not.
4.4. If there are two non-prioritized transactions in the TX pool, they will be picked up according to their gas prices (a higher gas price has a higher priority).
4.5. If whitelisted addresses send multiple transactions, the transactions will be ordered according to their weights (if set).