Skip to content
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

Fix/6790 return accepted for rejected tx added to broadcast #6889

Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Nethermind/Nethermind.TxPool.Test/TxBroadcasterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ public void should_not_broadcast_tx_with_MaxFeePerGas_lower_than_70_percent_of_C
// tx should be immediately broadcasted only if MaxFeePerGas is equal at least 70% of current base fee
peer.Received(shouldBroadcast ? 1 : 0).SendNewTransaction(Arg.Any<Transaction>());

// tx should always be added to persistent collection, without any fee restrictions
_broadcaster.GetSnapshot().Length.Should().Be(1);
// tx should only be added to persistent collection, if it is above the fee restriction
_broadcaster.GetSnapshot().Length.Should().Be(shouldBroadcast ? 1 : 0);
}

[Test]
Expand Down
50 changes: 47 additions & 3 deletions src/Nethermind/Nethermind.TxPool.Test/TxPoolTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ public void should_add_underpaid_txs_to_full_TxPool_only_if_local(bool isLocal)
AcceptTxResult result = _txPool.SubmitTx(tx, txHandlingOptions);
_txPool.GetPendingTransactionsCount().Should().Be(30);
_txPool.GetOwnPendingTransactions().Length.Should().Be(isLocal ? 1 : 0);
result.ToString().Should().Contain(isLocal ? nameof(AcceptTxResult.FeeTooLowToCompete) : nameof(AcceptTxResult.FeeTooLow));
result.ToString().Should().Contain(isLocal ? nameof(AcceptTxResult.Accepted) : nameof(AcceptTxResult.FeeTooLow));
}

[TestCase(0)]
Expand Down Expand Up @@ -1381,7 +1381,7 @@ public void should_increase_nonce_when_transaction_not_included_in_txPool_but_br
.WithMaxFeePerGas(1)
.WithMaxPriorityFeePerGas(1)
.SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject;
_txPool.SubmitTx(cheapTx, TxHandlingOptions.PersistentBroadcast).Should().Be(AcceptTxResult.FeeTooLowToCompete);
_txPool.SubmitTx(cheapTx, TxHandlingOptions.PersistentBroadcast).Should().Be(AcceptTxResult.Accepted);
_txPool.GetPendingTransactions().Should().NotContain(cheapTx);
_txPool.GetOwnPendingTransactions().Should().Contain(cheapTx);
peer.Received().SendNewTransaction(cheapTx);
Expand All @@ -1393,7 +1393,7 @@ public void should_increase_nonce_when_transaction_not_included_in_txPool_but_br
.WithMaxFeePerGas(1)
.WithMaxPriorityFeePerGas(1)
.SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject;
_txPool.SubmitTx(fourthTx, TxHandlingOptions.PersistentBroadcast).Should().Be(AcceptTxResult.FeeTooLowToCompete);
_txPool.SubmitTx(fourthTx, TxHandlingOptions.PersistentBroadcast).Should().Be(AcceptTxResult.Accepted);
_txPool.GetPendingTransactions().Should().NotContain(fourthTx);
_txPool.GetOwnPendingTransactions().Should().Contain(fourthTx);
peer.Received().SendNewTransaction(fourthTx);
Expand Down Expand Up @@ -1638,6 +1638,50 @@ public void Should_not_add_underpaid_tx_even_if_lower_nonces_are_expensive(int g
result.Should().Be(expectedResult ? AcceptTxResult.Accepted : AcceptTxResult.FeeTooLowToCompete);
}

[TestCase(TxType.Legacy, true)]
[TestCase(TxType.EIP1559, true)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and [TestCase(TxType.AccessList, true)] to have all types

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better to have TestCaseSource and generate test cases for all enum items, by default with true and just have exception for blobs. In that case if we add new tx type we will by default have a test that expects true here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the test accordingly to check all the values of TxType.

[TestCase(TxType.Blob, false)]
public void Should_correctly_add_tx_to_local_pool_when_underpaid(TxType txType, bool expectedResult)
{
// Should only add legacy and 1559 local transactions to local pool when underpaid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

access list type too and probably all future ones. The best is to write sth like Should only add local transactions other than blob-type to local pool when underpaid

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applied

ISpecProvider specProvider = GetCancunSpecProvider();
TxPoolConfig txPoolConfig = new TxPoolConfig { Size = 30, PersistentBlobStorageSize = 0 };
_txPool = CreatePool(txPoolConfig, specProvider);

Transaction[] transactions = GetTransactions(GetPeers(3), true, false);

foreach (Address address in transactions.Select(t => t.SenderAddress).Distinct())
{
EnsureSenderBalance(address, UInt256.MaxValue);
}

// setup full tx pool
foreach (Transaction transaction in transactions)
{
transaction.GasPrice = 10.GWei();
_txPool.SubmitTx(transaction, TxHandlingOptions.None);
}

_txPool.GetPendingTransactionsCount().Should().Be(30);

Transaction testTx = Build.A.Transaction
.WithNonce(0)
.WithType(txType)
.WithShardBlobTxTypeAndFieldsIfBlobTx()
.WithMaxFeePerGas(9.GWei())
.WithMaxPriorityFeePerGas(9.GWei())
.WithTo(TestItem.AddressB)
.SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA).TestObject;

EnsureSenderBalance(TestItem.PrivateKeyA.Address, UInt256.MaxValue);

AcceptTxResult result = _txPool.SubmitTx(testTx, TxHandlingOptions.PersistentBroadcast);
result.Should().Be(expectedResult ? AcceptTxResult.Accepted : AcceptTxResult.FeeTooLowToCompete);
_txPool.GetOwnPendingTransactions().Length.Should().Be(expectedResult ? 1 : 0);
_txPool.GetPendingBlobTransactionsCount().Should().Be(0);
_txPool.GetPendingTransactions().Should().NotContain(testTx);
}

private IDictionary<ITxPoolPeer, PrivateKey> GetPeers(int limit = 100)
{
var peers = new Dictionary<ITxPoolPeer, PrivateKey>();
Expand Down
26 changes: 14 additions & 12 deletions src/Nethermind/Nethermind.TxPool/TxBroadcaster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,31 +97,33 @@ internal class TxBroadcaster : IDisposable
// only for testing reasons
internal Transaction[] GetSnapshot() => _persistentTxs.GetSnapshot();

public void Broadcast(Transaction tx, bool isPersistent)
public bool Broadcast(Transaction tx, bool isPersistent)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor cosmetic: would be good to rename method, right now we are not calling it if we want to broadcast (void), but when we want to try to broadcast (bool return depending on success). So I recommend renaming it to TryBroadcast and TryStartBroadcast

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth to consider before merging

{
if (isPersistent)
{
StartBroadcast(tx);
}
else
{
BroadcastOnce(tx);
return StartBroadcast(tx);
}

BroadcastOnce(tx);

return true;
}

private void StartBroadcast(Transaction tx)
private bool StartBroadcast(Transaction tx)
{
// broadcast local tx only if MaxFeePerGas is not lower than configurable percent of current base fee
// (70% by default). Otherwise only add to persistent txs and broadcast when tx will be ready for inclusion
if (tx.MaxFeePerGas >= _baseFeeThreshold || tx.IsFree())

if (tx is not null
&& (tx.MaxFeePerGas >= _baseFeeThreshold || tx.IsFree())
&& _persistentTxs.TryInsert(tx.Hash, tx.SupportsBlobs ? new LightTransaction(tx) : tx, out Transaction? removed)
&& removed?.Hash != tx.Hash)
{
NotifyPeersAboutLocalTx(tx);
return true;
}

if (tx.Hash is not null)
{
_persistentTxs.TryInsert(tx.Hash, tx.SupportsBlobs ? new LightTransaction(tx) : tx);
}
return false;
}

private void BroadcastOnce(Transaction tx)
Expand Down
11 changes: 7 additions & 4 deletions src/Nethermind/Nethermind.TxPool/TxPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -450,14 +450,17 @@ private AcceptTxResult AddCore(Transaction tx, TxFilteringState state, bool isPe
if (tx.Hash == removed?.Hash)
{
// it means it was added and immediately evicted - pool was full of better txs
if (isPersistentBroadcast)
if (!isPersistentBroadcast || tx.SupportsBlobs || !_broadcaster.Broadcast(tx, true))
{
// we are adding only to persistent broadcast - not good enough for standard pool,
// but can be good enough for TxBroadcaster pool - for local txs only
_broadcaster.Broadcast(tx, isPersistentBroadcast);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happened to _broadcaster.Broadcast call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the if branch now has the opposite condition, if the tx is persistent (and not a blob tx), it would run to the end of the function (line 462~483).

_broadcaster.Broadcast will be run at line 477.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if running through all the normal flow here is best choice as this is a special case, when something we are not adding to main pool will have broadcasting, will wait for @marcindsobczak opinion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a good option, we definitely don't want to run through all the normal flow here

Metrics.PendingTransactionsPassedFiltersButCannotCompeteOnFees++;
return AcceptTxResult.FeeTooLowToCompete;
}
else
{
return AcceptTxResult.Accepted;
}
Metrics.PendingTransactionsPassedFiltersButCannotCompeteOnFees++;
return AcceptTxResult.FeeTooLowToCompete;
}

relevantPool.UpdateGroup(tx.SenderAddress!, state.SenderAccount, UpdateBucketWithAddedTransaction);
Expand Down