-
Notifications
You must be signed in to change notification settings - Fork 447
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
Build up to +65% more profitable blocks #7640
base: master
Are you sure you want to change the base?
Conversation
abc9ef8
to
8f78a67
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extreamly complicated, potentially suboptimal, might have some unforseen issues. Not sure if works at all with parallel block building.
@@ -48,7 +50,7 @@ Block[] Process( | |||
|
|||
public interface IBlockTransactionsExecutor | |||
{ | |||
TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, IReleaseSpec spec); | |||
TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, IReleaseSpec spec, CancellationToken token); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe to reduce boilerplate changes:
TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, IReleaseSpec spec, CancellationToken token); | |
TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, IReleaseSpec spec, CancellationToken token = default); |
if (token.IsCancellationRequested) return null; | ||
|
||
return _processor.Process(block, options, tracer, token); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds like : ?
@@ -123,6 +118,7 @@ public enum TxAction | |||
{ | |||
Add, | |||
Skip, | |||
Invalid, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please double check if we cover all the usages of results of this enum now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we potentially need change SimulateBridgeHelper
catch | ||
{ | ||
return false; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain?
chain.AddTransactions(BuildTransactions(chain, startingHead, TestItem.PrivateKeyC, TestItem.AddressA, 3, 10, out _, out _)); | ||
await Task.Delay(timePerSlot / 4); | ||
chain.AddTransactions(BuildTransactions(chain, startingHead, TestItem.PrivateKeyC, TestItem.AddressA, 3, 10, out _, out _)); | ||
await Task.Delay(timePerSlot / 4); | ||
chain.AddTransactions(BuildTransactions(chain, startingHead, TestItem.PrivateKeyC, TestItem.AddressA, 3, 10, out _, out _)); | ||
await Task.Delay(timePerSlot / 4); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a loop, and maybe TestCases with different loop count?
@@ -207,15 +299,32 @@ private void CleanupOldPayloads(object? sender, EventArgs e) | |||
bool currentBestBlockIsEmpty = blockContext.CurrentBestBlock?.Transactions.Length == 0; | |||
if (currentBestBlockIsEmpty && !blockContext.ImprovementTask.IsCompleted) | |||
{ | |||
await Task.WhenAny(blockContext.ImprovementTask, Task.Delay(GetPayloadWaitForFullBlockMillisecondsDelay)); | |||
await Task.WhenAny(blockContext.ImprovementTask, Task.Delay(GetPayloadWaitForFullBlockMillisecondsDelay, _tokenSource.Token)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can throw now?
int checkedTransactions = 0; | ||
int selectedTransactions = 0; | ||
using ArrayPoolList<Transaction> selectedBlobTxs = new(_eip4844Config.GetMaxBlobsPerBlock()); | ||
using ArrayPoolList<Transaction> selectedTxs = new(transactionSnapshot.Length + blobTransactionSnapshot.Length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
using ArrayPoolList<Transaction> selectedTxs = new(transactionSnapshot.Length + blobTransactionSnapshot.Length); | |
using ArrayPoolList<Transaction> selectedTxs = new(gasLimit/Transaction.BaseTxGasCost + _eip4844Config.GetMaxBlobsPerBlock()); |
.Where(tx => | ||
tx.SenderAddress is not null && // Ensure the transaction has a sender address | ||
_txFilterPipeline.Execute(tx, parent) && // Pass the transaction through the filter pipeline | ||
!tx.IsAboveInitCode(spec)) // Exclude transactions that exceed the init code size limit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is it needed here?
tx.SenderAddress is not null && // Ensure the transaction has a sender address | ||
_txFilterPipeline.Execute(tx, parent) && // Pass the transaction through the filter pipeline | ||
!tx.IsAboveInitCode(spec)) // Exclude transactions that exceed the init code size limit | ||
.DistinctBy((tx) => tx, ByHashTxComparer.Instance)); // Remove duplicate transactions based on their hash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why would there be duplicates in tx pool?
// Sort transactions from the same sender by their nonce in ascending order | ||
// Otherwise txs will be invalidated by not being in nonce order | ||
SortMultiSendersByNonce(selectedTxs, parent, spec, token); | ||
// Remove any transactions that have been invalidated or are no longer valid after sorting | ||
RemoveBlankedTransactions(selectedTxs); | ||
|
||
if (_logger.IsTrace) _logger.Trace($"Selected {tx.ToShortString()} to be potentially included in block."); | ||
// Re-sort transactions to maintain the correct order, ensuring transactions from the same sender remain in nonce order | ||
// As sorting by nonce may have put txs with lower priorty earlier | ||
selectedTxs.AsSpan().Sort((tx0, tx1) => | ||
tx0.SenderAddress == tx1.SenderAddress ? | ||
0 : // If transactions are from the same sender, keep their current order | ||
comparer.Compare(tx0, tx1)); // Otherwise, sort based on the main comparer | ||
|
||
selectedTransactions++; | ||
yield return tx; | ||
} | ||
// Remove blob transactions that cannot be included in the block (e.g. due to block limit constraints) | ||
RemoveBlobTransactions(selectedTxs.AsSpan(), parent, spec); | ||
// Remove any additional invalidated transactions after blob transaction removal | ||
RemoveBlankedTransactions(selectedTxs); | ||
|
||
if (selectedBlobTxs.Count > 0) | ||
// Convert the list of selected transactions to an array for inclusion in the block | ||
Transaction[] potentialTxs = selectedTxs.ToArray(); | ||
|
||
if (_logger.IsDebug) _logger.Debug($"Potentially selected {potentialTxs.Length} out of {checkedTransactions} pending transactions checked."); | ||
|
||
// Return the selected transactions unless the operation was cancelled | ||
return token.IsCancellationRequested ? Array.Empty<Transaction>() : potentialTxs; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really dislike the solution:
- Sort things multiple times
- Allocating memory while previous solution was lazy.
- Prioritizing GasPrice sorting over Nonce sorting first.
GetOrderedTransactions
where close to optimal, lazy and more readable solution and I would prefer to keep it.
We have a |
From PR description: |
I think this won't build optimal blocks, so for example it is better to include transactions with priority fee 10 and next 100, than the one from other sender with fee 20, but that 2nd one will win on gasBottleneck |
Right, as long as all of them are able to pay for base fee. In this example - tx with fee 10 must be able to pay base fee by itself to make it possible to process tx with fee 100 |
Current Geth block (with forced local production for PR on Nethermind)
0.0145 Eth Fees | 3.41 MGas | 67 txs
PR
0.024 Eth Fees | 4.81 MGas | 69 txs
Current Nethermind block (with forced local production for PR on Nethermind)
0.0124 Eth fees | 3.88 MGas | 75 txs
PR
0.014 Eth fees | 5.82 MGas | 94 txs
Note: Doesn't compete with builders as 50% of mainnet orderflow is private and local builder doesn't have access to include in block
Changes
Types of changes
What types of changes does your code introduce?
Testing
Requires testing
If yes, did you write tests?
Notes on testing
Should also be run on testnets where node is doing a portion of local building
Documentation
Requires documentation update
Requires explanation in Release Notes
As above