-
Notifications
You must be signed in to change notification settings - Fork 382
Prioritize Substrate txns by tip-per-effective-gas #1957
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c7bd1c5
d63b5c4
85fa317
90667d9
c9a1dbd
3f08d1c
5988d66
a7eb034
5ada814
a4eabc2
4179c44
644ad65
9087f55
40bb3ca
d904ee5
27f3e83
eeb4482
68657b4
f31be56
9729146
43bcc2d
6a1914e
f32ae96
89c6547
2fe5667
2d5503f
9f6c527
ae1298f
9af007e
90b03d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -87,7 +87,7 @@ use sp_runtime::{ | |
| transaction_validity::{ | ||
| InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, | ||
| }, | ||
| ApplyExtrinsicResult, FixedPointNumber, Perbill, Permill, Perquintill, SaturatedConversion, | ||
| ApplyExtrinsicResult, FixedPointNumber, Perbill, Permill, Perquintill, | ||
| }; | ||
| use sp_std::{ | ||
| convert::{From, Into}, | ||
|
|
@@ -1397,8 +1397,10 @@ moonbeam_runtime_common::impl_runtime_apis_plus_common! { | |
| let dispatch_info = xt.get_dispatch_info(); | ||
|
|
||
| // If this is a pallet ethereum transaction, then its priority is already set | ||
| // according to gas price from pallet ethereum. If it is any other kind of transaction, | ||
| // we modify its priority. | ||
| // according to effective priority fee from pallet ethereum. If it is any other kind of | ||
| // transaction, we modify its priority. The goal is to arrive at a similar metric used | ||
| // by pallet ethereum, which means we derive a fee-per-gas from the txn's tip and | ||
| // weight. | ||
| Ok(match &xt.0.function { | ||
| RuntimeCall::Ethereum(transact { .. }) => intermediate_valid, | ||
| _ if dispatch_info.class != DispatchClass::Normal => intermediate_valid, | ||
|
|
@@ -1412,31 +1414,18 @@ moonbeam_runtime_common::impl_runtime_apis_plus_common! { | |
| } | ||
| }; | ||
|
|
||
| // Calculate the fee that will be taken by pallet transaction payment | ||
| let fee: u64 = TransactionPayment::compute_fee( | ||
| xt.encode().len() as u32, | ||
| &dispatch_info, | ||
| tip, | ||
| ).saturated_into(); | ||
|
|
||
| // Calculate how much gas this effectively uses according to the existing mapping | ||
| let effective_gas = | ||
| <Runtime as pallet_evm::Config>::GasWeightMapping::weight_to_gas( | ||
| dispatch_info.weight | ||
| ); | ||
|
|
||
| // Here we calculate an ethereum-style effective gas price using the | ||
| // current fee of the transaction. Because the weight -> gas conversion is | ||
| // lossy, we have to handle the case where a very low weight maps to zero gas. | ||
| let effective_gas_price = if effective_gas > 0 { | ||
| fee / effective_gas | ||
| let tip_per_gas = if effective_gas > 0 { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should also add an associated rust test for this validation function (if possible)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some tests, but they're not in good shape. The call to I'll revisit this again with a fresh mind, but we may also want to consider creating a task to do these tests later. In any case, I agree that they're a good idea... |
||
| tip.saturating_div(effective_gas as u128) | ||
| } else { | ||
| // If the effective gas was zero, we just act like it was 1. | ||
| fee | ||
| 0 | ||
| }; | ||
|
|
||
| // Overwrite the original prioritization with this ethereum one | ||
| intermediate_valid.priority = effective_gas_price; | ||
| intermediate_valid.priority = tip_per_gas as u64; | ||
| intermediate_valid | ||
| } | ||
| }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,13 @@ import { | |
| execTechnicalCommitteeProposal, | ||
| notePreimage, | ||
| } from "../../util/governance"; | ||
| import { GLMR, MIN_GLMR_STAKING, MIN_GLMR_DELEGATOR } from "../../util/constants"; | ||
| import { | ||
| MICROGLMR, | ||
| MILLIGLMR, | ||
| GLMR, | ||
| MIN_GLMR_STAKING, | ||
| MIN_GLMR_DELEGATOR, | ||
| } from "../../util/constants"; | ||
| import { describeDevMoonbeam, DevTestContext } from "../../util/setup-dev-tests"; | ||
| import { KeyringPair } from "@polkadot/keyring/types"; | ||
| import { expectOk } from "../../util/expect"; | ||
|
|
@@ -200,9 +206,6 @@ describeDevMoonbeam("Staking - Locks - execute revoke", (context) => { | |
| before("setup account balance", async function () { | ||
| await expectOk( | ||
| context.createBlock([ | ||
| context.polkadotApi.tx.sudo.sudo( | ||
| context.polkadotApi.tx.parachainStaking.setBlocksPerRound(5) | ||
| ), | ||
| context.polkadotApi.tx.balances.transfer( | ||
| randomAccount.address, | ||
| MIN_GLMR_DELEGATOR + 1n * GLMR | ||
|
|
@@ -258,9 +261,6 @@ describeDevMoonbeam("Staking - Locks - multiple delegations single revoke", (con | |
|
|
||
| await expectOk( | ||
| context.createBlock([ | ||
| context.polkadotApi.tx.sudo.sudo( | ||
| context.polkadotApi.tx.parachainStaking.setBlocksPerRound(5) | ||
| ), | ||
| context.polkadotApi.tx.balances.transfer(randomAccount.address, 2n * MIN_GLMR_STAKING), | ||
| context.polkadotApi.tx.parachainStaking | ||
| .joinCandidates(MIN_GLMR_STAKING, 1) | ||
|
|
@@ -516,6 +516,11 @@ describeDevMoonbeam("Staking - Locks - bottom and top delegations", (context) => | |
|
|
||
| before("setup candidate & delegations", async function () { | ||
| this.timeout(20000); | ||
| const numBottomDelegations = | ||
| await context.polkadotApi.consts.parachainStaking.maxBottomDelegationsPerCandidate.toNumber(); | ||
|
|
||
| const numTopDelegations = | ||
| await context.polkadotApi.consts.parachainStaking.maxTopDelegationsPerCandidate.toNumber(); | ||
|
|
||
| // Create the delegators to fill the lists | ||
| bottomDelegators = new Array( | ||
|
|
@@ -533,7 +538,7 @@ describeDevMoonbeam("Staking - Locks - bottom and top delegations", (context) => | |
| context.createBlock( | ||
| [...bottomDelegators, ...topDelegators].map((account, i) => | ||
| context.polkadotApi.tx.balances | ||
| .transfer(account.address, MIN_GLMR_DELEGATOR + 2n * GLMR) | ||
| .transfer(account.address, MIN_GLMR_DELEGATOR + 20n * GLMR) | ||
| .signAsync(alith, { nonce: i }) | ||
| ) | ||
| ) | ||
|
|
@@ -543,39 +548,61 @@ describeDevMoonbeam("Staking - Locks - bottom and top delegations", (context) => | |
| it("should be set for bottom and top list delegators", async function () { | ||
| await expectOk( | ||
| context.createBlock( | ||
| [...topDelegators].map((account, i) => | ||
| context.polkadotApi.tx.parachainStaking | ||
| .delegate(alith.address, MIN_GLMR_DELEGATOR + 1n * GLMR, topDelegators.length + 1, 1) | ||
| .signAsync(account) | ||
| ) | ||
| [...topDelegators].map((account, i) => { | ||
| // add a tip such that the delegation ordering will be preserved, e.g. the first txns sent | ||
| // will have the highest tip | ||
| let tip = BigInt(topDelegators.length - i + 1) * MILLIGLMR; | ||
| return context.polkadotApi.tx.parachainStaking | ||
| .delegate(alith.address, MIN_GLMR_DELEGATOR + 1n * GLMR, i + 1, 1) | ||
| .signAsync(account, { tip }); | ||
| }) | ||
|
Comment on lines
+551
to
+558
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be simplified byusing let tip = 1 * GLMR;
await expectOk(
context.createBlock(
[...topDelegators].map((account, i) => {
// add a tip such that the delegation ordering will be preserved, e.g. the first txns sent
// will have the highest tip
return context.polkadotApi.tx.parachainStaking
.delegate(alith.address, MIN_GLMR_DELEGATOR + 1n * GLMR, i + 1, 1)
.signAsync(account, { tip: tip-- });
})
)
); |
||
| ) | ||
| ); | ||
|
|
||
| // allow more block(s) for txns to be processed... | ||
| // note: this only seems necessary when a tip is added, otherwise all 300 txns make it into a | ||
| // single block. A tip is necessary if the txns are not otherwise executed in order of | ||
| // submission, which is highly dependent on txpool prioritization logic. | ||
| // TODO: it would be good to diagnose this further: why does adding a tip appear to reduce the | ||
| // number of txns included? | ||
| const numBlocksToWait = 1; | ||
| let numBlocksWaited = 0; | ||
| while (numBlocksWaited < numBlocksToWait) { | ||
| await context.createBlock(); | ||
| const topLocks = await context.polkadotApi.query.balances.locks.multi( | ||
| topDelegators.map((delegator) => delegator.address) | ||
| ); | ||
| let numDelegatorLocks = topLocks.filter((lockSet) => | ||
| lockSet.find((lock) => lock.id.toHuman().toString() == "stkngdel") | ||
| ).length; | ||
|
|
||
| if (numDelegatorLocks < topDelegators.length) { | ||
| numBlocksWaited += 1; | ||
| expect(numBlocksWaited).to.be.lt( | ||
| numBlocksToWait, | ||
| "Top delegation extrinsics not included in time" | ||
| ); | ||
| } else { | ||
| expect(numDelegatorLocks).to.eq(topDelegators.length, "More delegations than expected"); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| await expectOk( | ||
| context.createBlock( | ||
| [...bottomDelegators].map((account, i) => | ||
| context.polkadotApi.tx.parachainStaking | ||
| .delegate( | ||
| alith.address, | ||
| MIN_GLMR_DELEGATOR, | ||
| topDelegators.length + bottomDelegators.length + 1, | ||
| 1 | ||
| ) | ||
| .signAsync(account) | ||
| ) | ||
| [...bottomDelegators].map((account, i) => { | ||
| // add a tip such that the delegation ordering will be preserved, e.g. the first txns sent | ||
| // will have the highest tip | ||
| let tip = BigInt(topDelegators.length + (bottomDelegators.length - i) + 1) * MILLIGLMR; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
| return context.polkadotApi.tx.parachainStaking | ||
| .delegate(alith.address, MIN_GLMR_DELEGATOR, topDelegators.length + i + 1, 1) | ||
| .signAsync(account, { tip }); | ||
| }) | ||
| ) | ||
| ); | ||
|
|
||
| const topLocks = await context.polkadotApi.query.balances.locks.multi( | ||
| topDelegators.map((delegator) => delegator.address) | ||
| ); | ||
| expect( | ||
| topLocks.filter((lockSet) => | ||
| lockSet.find((lock) => lock.id.toHuman().toString() == "stkngdel") | ||
| ).length | ||
| ).to.equal( | ||
| context.polkadotApi.consts.parachainStaking.maxTopDelegationsPerCandidate.toNumber() | ||
| ); | ||
|
|
||
| // note that we don't need to wait for further blocks here because bottom delegations is much | ||
| // smaller than top delegations, so all txns reliably fit within one block. | ||
| const bottomLocks = await context.polkadotApi.query.balances.locks.multi( | ||
| bottomDelegators.map((delegator) => delegator.address) | ||
| ); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.