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

Broadcast local txs only if MaxFeePerGas is equal at least 70% of current base fee #6350

Merged
merged 12 commits into from
Dec 14, 2023
63 changes: 63 additions & 0 deletions src/Nethermind/Nethermind.TxPool.Test/TxBroadcasterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,36 @@ public void should_not_pick_txs_with_GasPrice_lower_than_CurrentBaseFee([Values(
expectedTxs.Should().BeEquivalentTo(pickedTxs);
}

[TestCase(0, false)]
[TestCase(69, false)]
[TestCase(70, true)]
[TestCase(100, true)]
[TestCase(150, true)]
public void should_not_broadcast_tx_with_MaxFeePerGas_lower_than_70_percent_of_CurrentBaseFee(int maxFeePerGas, bool shouldBroadcast)
{
_headInfo.CurrentBaseFee.Returns((UInt256)100);

_broadcaster = new TxBroadcaster(_comparer, TimerFactory.Default, _txPoolConfig, _headInfo, _logManager);

ITxPoolPeer peer = Substitute.For<ITxPoolPeer>();
peer.Id.Returns(TestItem.PublicKeyA);
_broadcaster.AddPeer(peer);

Transaction transaction = Build.A.Transaction
.WithType(TxType.EIP1559)
.WithMaxFeePerGas((UInt256)maxFeePerGas)
.SignedAndResolved(_ethereumEcdsa, TestItem.PrivateKeyA)
.TestObject;

_broadcaster.Broadcast(transaction, true);

// 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);
}

[Test]
public void should_not_pick_1559_txs_with_MaxFeePerGas_lower_than_CurrentBaseFee([Values(1, 2, 99, 100, 101, 1000)] int threshold)
{
Expand Down Expand Up @@ -657,6 +687,39 @@ public void should_rebroadcast_all_persistent_transactions_if_PeerNotificationTh
}
}

[TestCase(0, 0)]
[TestCase(2, 1)]
[TestCase(3, 2)]
[TestCase(7, 4)]
[TestCase(8, 5)]
[TestCase(100, 70)]
[TestCase(9999, 6999)]
[TestCase(10000, 7000)]
public void should_calculate_baseFeeThreshold_correctly(int baseFee, int expectedThreshold)
{
_headInfo.CurrentBaseFee.Returns((UInt256)baseFee);
_broadcaster = new TxBroadcaster(_comparer, TimerFactory.Default, _txPoolConfig, _headInfo, _logManager);
_broadcaster.CalculateBaseFeeThreshold().Should().Be((UInt256)expectedThreshold);
}

[Test]
public void calculation_of_baseFeeThreshold_should_handle_overflow_correctly([Values(0, 70, 100, 101, 500)] int threshold, [Values(2, 3, 4, 5, 6, 7, 8, 9, 10, 11)] int divisor)
{
UInt256.Divide(UInt256.MaxValue, (UInt256)divisor, out UInt256 baseFee);
_headInfo.CurrentBaseFee.Returns(baseFee);

_txPoolConfig = new TxPoolConfig() { MinBaseFeeThreshold = threshold };
_broadcaster = new TxBroadcaster(_comparer, TimerFactory.Default, _txPoolConfig, _headInfo, _logManager);

UInt256.Divide(baseFee, 100, out UInt256 onePercentOfBaseFee);
bool overflow = UInt256.MultiplyOverflow(onePercentOfBaseFee, (UInt256)threshold, out UInt256 lessAccurateBaseFeeThreshold);

_broadcaster.CalculateBaseFeeThreshold().Should().Be(
UInt256.MultiplyOverflow(baseFee, (UInt256)threshold, out UInt256 baseFeeThreshold)
? overflow ? UInt256.MaxValue : lessAccurateBaseFeeThreshold
: baseFeeThreshold);
}

private (IList<Transaction> expectedTxs, IList<Hash256> expectedHashes) GetTxsAndHashesExpectedToBroadcast(Transaction[] transactions, int expectedCountTotal)
{
List<Transaction> expectedTxs = new();
Expand Down
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.TxPool/ITxPoolConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ public interface ITxPoolConfig : IConfig
[ConfigItem(DefaultValue = "5", Description = "The average percentage of transaction hashes from persistent broadcast sent to a peer together with hashes of the last added transactions.")]
int PeerNotificationThreshold { get; set; }

[ConfigItem(DefaultValue = "70", Description = "Minimal percentage of the current base fee which must be surpassed by MaxFeePerGas for the transaction to be broadcasted.")]
marcindsobczak marked this conversation as resolved.
Show resolved Hide resolved
int MinBaseFeeThreshold { get; set; }

[ConfigItem(DefaultValue = "2048", Description = "The max number of transactions held in the mempool (the more transactions in the mempool, the more memory used).")]
int Size { get; set; }

Expand Down
42 changes: 39 additions & 3 deletions src/Nethermind/Nethermind.TxPool/TxBroadcaster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,18 @@ internal class TxBroadcaster : IDisposable
/// </summary>
private ResettableList<Transaction> _txsToSend;


/// <summary>
/// Minimal value of MaxFeePerGas of local tx to be broadcasted immediately after receiving it
/// </summary>
private UInt256 _baseFeeThreshold;

/// <summary>
/// Used to throttle tx broadcast. Particularly during forward sync where the head changes a lot which triggers
/// a lot of broadcast. There are no transaction in pool but its quite spammy on the log.
/// </summary>
private DateTimeOffset _lastPersistedTxBroadcast = DateTimeOffset.UnixEpoch;

private readonly TimeSpan _minTimeBetweenPersistedTxBroadcast = TimeSpan.FromSeconds(1);

private readonly ILogger _logger;
Expand All @@ -80,6 +87,8 @@ public TxBroadcaster(IComparer<Transaction> comparer,
_timer.Elapsed += TimerOnElapsed;
_timer.AutoReset = false;
_timer.Start();

_baseFeeThreshold = CalculateBaseFeeThreshold();
}

// only for testing reasons
Expand All @@ -99,7 +108,13 @@ public void Broadcast(Transaction tx, bool isPersistent)

private void StartBroadcast(Transaction tx)
{
NotifyPeersAboutLocalTx(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())
{
NotifyPeersAboutLocalTx(tx);
}

if (tx.Hash is not null)
{
_persistentTxs.TryInsert(tx.Hash, tx.SupportsBlobs ? new LightTransaction(tx) : tx);
Expand All @@ -122,7 +137,29 @@ public void AnnounceOnce(ITxPoolPeer peer, Transaction[] txs)
}
}

public void BroadcastPersistentTxs()
public void OnNewHead()
{
_baseFeeThreshold = CalculateBaseFeeThreshold();
BroadcastPersistentTxs();
}

internal UInt256 CalculateBaseFeeThreshold()
{
bool overflow = UInt256.MultiplyOverflow(_headInfo.CurrentBaseFee, (UInt256)_txPoolConfig.MinBaseFeeThreshold, out UInt256 baseFeeThreshold);
UInt256.Divide(baseFeeThreshold, 100, out baseFeeThreshold);

if (overflow)
{
UInt256.Divide(_headInfo.CurrentBaseFee, 100, out baseFeeThreshold);
overflow = UInt256.MultiplyOverflow(baseFeeThreshold, (UInt256)_txPoolConfig.MinBaseFeeThreshold, out baseFeeThreshold);
}

// if there is still an overflow, it means that MinBaseFeeThreshold > 100
// we are returning max possible value of UInt256.MaxValue
return overflow ? UInt256.MaxValue : baseFeeThreshold;
}

internal void BroadcastPersistentTxs()
{
if (_persistentTxs.Count == 0)
{
Expand Down Expand Up @@ -274,7 +311,6 @@ private void Notify(ITxPoolPeer peer, IEnumerable<Transaction> txs, bool sendFul
{
try
{

peer.SendNewTransactions(txs.Where(t => _txGossipPolicy.ShouldGossipTransaction(t)), sendFullTx);
if (_logger.IsTrace) _logger.Trace($"Notified {peer} about transactions.");
}
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.TxPool/TxPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ private void ProcessNewHeads()
ReAddReorganisedTransactions(args.PreviousBlock);
RemoveProcessedTransactions(args.Block.Transactions);
UpdateBuckets();
_broadcaster.BroadcastPersistentTxs();
_broadcaster.OnNewHead();
Metrics.TransactionCount = _transactions.Count;
Metrics.BlobTransactionCount = _blobTransactions.Count;
}
Expand Down
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.TxPool/TxPoolConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Nethermind.TxPool
public class TxPoolConfig : ITxPoolConfig
{
public int PeerNotificationThreshold { get; set; } = 5;
public int MinBaseFeeThreshold { get; set; } = 70;
public int Size { get; set; } = 2048;
public bool BlobSupportEnabled { get; set; } = false;
public bool PersistentBlobStorageEnabled { get; set; } = false;
Expand Down
Loading