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

Disable NonceGap check for internal transactions #4926

Merged
merged 42 commits into from
Feb 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
ad504ec
Unit test
deffrian Nov 17, 2022
dfc29c3
check if transaction is not local
deffrian Nov 18, 2022
ab62123
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Nov 22, 2022
5db71ff
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Nov 22, 2022
299e39f
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Nov 27, 2022
be5df46
Add nonce manager
deffrian Nov 27, 2022
6447baa
Fix tests
deffrian Nov 27, 2022
1f83a79
Refactor reserve nonce
deffrian Nov 28, 2022
4c00f11
Implement NonceManager
deffrian Dec 1, 2022
4d7fd1b
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Dec 5, 2022
34e93b2
Fix nonce gap
deffrian Dec 5, 2022
35ca01f
Fix test
deffrian Dec 6, 2022
6eefad7
Tests
deffrian Dec 6, 2022
441312a
Remove comment
deffrian Dec 6, 2022
c4bd2fd
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Dec 7, 2022
340273c
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Dec 9, 2022
a013d86
fix dosdao
deffrian Dec 11, 2022
3741a79
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Dec 16, 2022
78e22a1
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Dec 18, 2022
fccd40f
Fix posdao master
deffrian Dec 19, 2022
06ce751
Cleanup
deffrian Dec 19, 2022
4e2812b
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Dec 20, 2022
e7fe506
Add tx reinserted test
deffrian Dec 28, 2022
74534f0
Fix spaces
deffrian Dec 28, 2022
1ee7706
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Jan 6, 2023
1de59bf
Fix test
deffrian Jan 6, 2023
0424c75
Release nonces
deffrian Jan 16, 2023
5c26b2f
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Jan 16, 2023
4fdaf4b
Make AccountLocker
deffrian Jan 20, 2023
01c402f
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Jan 20, 2023
1c16ea5
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Jan 20, 2023
7733adf
Fix tests
deffrian Jan 20, 2023
36b1a60
Fix tests
deffrian Jan 20, 2023
071b82a
Implement dispose
deffrian Jan 22, 2023
9bfe8c6
Add nonceManager to api
deffrian Jan 22, 2023
7e9b85b
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Jan 23, 2023
f743fc1
Fix posdao tests
deffrian Jan 23, 2023
a08b797
Merge branch 'master' into 4845-nonce-increment-bugs
deffrian Feb 6, 2023
420e1bb
API improvements -> move to more contextual locking
LukaszRozmej Feb 8, 2023
eb33be4
fix test
LukaszRozmej Feb 8, 2023
ff42da2
Add test & fix bug
deffrian Feb 9, 2023
14233d6
Rollback reservedNOnce out parameter
deffrian Feb 9, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Api/IApiWithBlockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public interface IApiWithBlockchain : IApiWithStores, IBlockchainBridgeFactory
ITransactionProcessor? TransactionProcessor { get; set; }
ITrieStore? TrieStore { get; set; }
ITxSender? TxSender { get; set; }
INonceManager? NonceManager { get; set; }
ITxPool? TxPool { get; set; }
ITxPoolInfoProvider? TxPoolInfoProvider { get; set; }
IWitnessCollector? WitnessCollector { get; set; }
Expand Down
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Api/NethermindApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ public ISealEngine SealEngine
public ITrieStore? TrieStore { get; set; }
public IReadOnlyTrieStore? ReadOnlyTrieStore { get; set; }
public ITxSender? TxSender { get; set; }
public INonceManager? NonceManager { get; set; }
public ITxPool? TxPool { get; set; }
public ITxPoolInfoProvider? TxPoolInfoProvider { get; set; }
public IHealthHintService? HealthHintService { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ private IAuRaValidator CreateAuRaValidator(IBlockProcessor processor, IReadOnlyT
if (_api.BlockTree is null) throw new StepDependencyException(nameof(_api.BlockTree));
if (_api.EngineSigner is null) throw new StepDependencyException(nameof(_api.EngineSigner));
if (_api.SpecProvider is null) throw new StepDependencyException(nameof(_api.SpecProvider));
if (_api.NonceManager is null) throw new StepDependencyException(nameof(_api.NonceManager));

var chainSpecAuRa = _api.ChainSpec.AuRa;

Expand All @@ -136,8 +137,7 @@ private IAuRaValidator CreateAuRaValidator(IBlockProcessor processor, IReadOnlyT
_api.ReceiptStorage,
_api.ValidatorStore,
_api.FinalizationManager,
new TxPoolSender(_api.TxPool,
new NonceReservingTxSealer(_api.EngineSigner, _api.Timestamper, _api.TxPool, _api.EthereumEcdsa)),
new TxPoolSender(_api.TxPool, new TxSealer(_api.EngineSigner, _api.Timestamper), _api.NonceManager, _api.EthereumEcdsa),
_api.TxPool,
NethermindApi.Config<IBlocksConfig>(),
_api.LogManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public class TestBlockchain : IDisposable
public const int DefaultTimeout = 4000;
public IStateReader StateReader { get; private set; } = null!;
public IEthereumEcdsa EthereumEcdsa { get; private set; } = null!;
public INonceManager NonceManager { get; private set; } = null!;
public TransactionProcessor TxProcessor { get; set; } = null!;
public IStorageProvider Storage { get; set; } = null!;
public IReceiptStorage ReceiptStorage { get; set; } = null!;
Expand Down Expand Up @@ -142,6 +143,11 @@ protected virtual async Task<TestBlockchain> Build(ISpecProvider? specProvider =
TransactionComparerProvider = new TransactionComparerProvider(SpecProvider, BlockTree);
TxPool = CreateTxPool();

IChainHeadInfoProvider chainHeadInfoProvider =
new ChainHeadInfoProvider(SpecProvider, BlockTree, StateReader);

NonceManager = new NonceManager(chainHeadInfoProvider.AccountStateProvider);

_trieStoreWatcher = new TrieStoreBoundaryWatcher(TrieStore, BlockTree, LogManager);

ReceiptStorage = new InMemoryReceiptStorage();
Expand Down
9 changes: 7 additions & 2 deletions src/Nethermind/Nethermind.Facade.Test/TxPoolBridgeTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Threading;
using FluentAssertions;
using Nethermind.Consensus;
using Nethermind.Core;
Expand All @@ -19,19 +20,23 @@ public class TxPoolBridgeTests
private ITxSender _txSender;
private ITxPool _txPool;
private ITxSigner _txSigner;
private INonceManager _nonceManager;
private IEthereumEcdsa _ecdsa;

[SetUp]
public void SetUp()
{
_txPool = Substitute.For<ITxPool>();
_txSigner = Substitute.For<ITxSigner>();
_txSender = new TxPoolSender(_txPool, new TxSealer(_txSigner, Timestamper.Default));
_nonceManager = new NonceManager(Substitute.For<IAccountStateProvider>());
_ecdsa = Substitute.For<IEthereumEcdsa>();
_txSender = new TxPoolSender(_txPool, new TxSealer(_txSigner, Timestamper.Default), _nonceManager, _ecdsa);
}

[Test]
public void Timestamp_is_set_on_transactions()
{
Transaction tx = Build.A.Transaction.Signed(new EthereumEcdsa(TestBlockchainIds.ChainId, LimboLogs.Instance), TestItem.PrivateKeyA).TestObject;
Transaction tx = Build.A.Transaction.WithSenderAddress(TestItem.AddressA).Signed(new EthereumEcdsa(TestBlockchainIds.ChainId, LimboLogs.Instance), TestItem.PrivateKeyA).TestObject;
_txSender.SendTransaction(tx, TxHandlingOptions.PersistentBroadcast);
_txPool.Received().SubmitTx(Arg.Is<Transaction>(tx => tx.Timestamp != UInt256.Zero), TxHandlingOptions.PersistentBroadcast);
}
Expand Down
15 changes: 9 additions & 6 deletions src/Nethermind/Nethermind.Init/Steps/InitializeBlockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,15 @@ private Task InitBlockchain()

IChainHeadInfoProvider chainHeadInfoProvider =
new ChainHeadInfoProvider(getApi.SpecProvider, getApi.BlockTree, stateReader);

// TODO: can take the tx sender from plugin here maybe
ITxSigner txSigner = new WalletTxSigner(getApi.Wallet, getApi.SpecProvider.ChainId);
TxSealer nonceReservingTxSealer =
new(txSigner, getApi.Timestamper);
INonceManager nonceManager = new NonceManager(chainHeadInfoProvider.AccountStateProvider);
setApi.NonceManager = nonceManager;
setApi.TxSender = new TxPoolSender(txPool, nonceReservingTxSealer, nonceManager, getApi.EthereumEcdsa!);

setApi.TxPoolInfoProvider = new TxPoolInfoProvider(chainHeadInfoProvider.AccountStateProvider, txPool);
setApi.GasPriceOracle = new GasPriceOracle(getApi.BlockTree, getApi.SpecProvider, _api.LogManager, blocksConfig.MinGasPrice);
IBlockProcessor mainBlockProcessor = setApi.MainBlockProcessor = CreateBlockProcessor();
Expand All @@ -265,12 +274,6 @@ private Task InitBlockchain()
setApi.BlockchainProcessor = blockchainProcessor;
setApi.EthSyncingInfo = new EthSyncingInfo(getApi.BlockTree, getApi.ReceiptStorage!, syncConfig, getApi.LogManager);

// TODO: can take the tx sender from plugin here maybe
ITxSigner txSigner = new WalletTxSigner(getApi.Wallet, getApi.SpecProvider.ChainId);
NonceReservingTxSealer nonceReservingTxSealer =
new(txSigner, getApi.Timestamper, txPool, getApi.EthereumEcdsa!);
setApi.TxSender = new TxPoolSender(txPool, nonceReservingTxSealer);

IFilterStore filterStore = setApi.FilterStore = new FilterStore();
setApi.FilterManager = new FilterManager(filterStore, mainBlockProcessor, txPool, getApi.LogManager);
setApi.HealthHintService = CreateHealthHintService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,8 @@ public async Task Send_transaction_with_signature_will_not_try_to_sign()
public async Task Send_raw_transaction_will_send_transaction(string rawTransaction)
{
using Context ctx = await Context.Create();
ITxSender txSender = Substitute.ForPartsOf<TxPoolSender>(ctx.Test.TxPool, ctx.Test.TxSealer);
ITxSender txSender = Substitute.ForPartsOf<TxPoolSender>(ctx.Test.TxPool, ctx.Test.TxSealer,
ctx.Test.NonceManager, ctx.Test.EthereumEcdsa);
IBlockchainBridge bridge = Substitute.For<IBlockchainBridge>();
ctx.Test = await TestRpcBlockchain.ForTest(SealEngineType.NethDev).WithBlockchainBridge(bridge).WithTxSender(txSender).Build();
string serialized = ctx.Test.TestEthRpc("eth_sendRawTransaction", rawTransaction);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,8 @@ protected override async Task<TestBlockchain> Build(ISpecProvider? specProvider


ITxSigner txSigner = new WalletTxSigner(TestWallet, specProvider.ChainId);
TxSealer = new NonceReservingTxSealer(txSigner, Timestamper, TxPool, EthereumEcdsa ?? new EthereumEcdsa(specProvider.ChainId, LogManager));
TxSender ??= new TxPoolSender(TxPool, TxSealer);
TxSealer = new TxSealer(txSigner, Timestamper);
TxSender ??= new TxPoolSender(TxPool, TxSealer, NonceManager, EthereumEcdsa ?? new EthereumEcdsa(specProvider.ChainId, LogManager));
GasPriceOracle ??= new GasPriceOracle(BlockFinder, SpecProvider, LogManager);
FeeHistoryOracle ??= new FeeHistoryOracle(BlockFinder, ReceiptStorage, SpecProvider);
ISyncConfig syncConfig = new SyncConfig();
Expand Down
225 changes: 225 additions & 0 deletions src/Nethermind/Nethermind.TxPool.Test/NonceManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Nethermind.Blockchain;
using Nethermind.Core;
using Nethermind.Core.Specs;
using Nethermind.Core.Test.Builders;
using Nethermind.Db;
using Nethermind.Int256;
using Nethermind.Logging;
using Nethermind.Specs;
using Nethermind.State;
using Nethermind.Trie.Pruning;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.TxPool.Test;

public class NonceManagerTests
{
private ISpecProvider _specProvider;
private IStateProvider _stateProvider;
private IBlockTree _blockTree;
private ChainHeadInfoProvider _headInfo;
private INonceManager _nonceManager;

[SetUp]
public void Setup()
{
ILogManager logManager = LimboLogs.Instance;
_specProvider = RopstenSpecProvider.Instance;
var trieStore = new TrieStore(new MemDb(), logManager);
var codeDb = new MemDb();
_stateProvider = new StateProvider(trieStore, codeDb, logManager);
_blockTree = Substitute.For<IBlockTree>();
Block block = Build.A.Block.WithNumber(0).TestObject;
_blockTree.Head.Returns(block);
_blockTree.FindBestSuggestedHeader().Returns(Build.A.BlockHeader.WithNumber(10000000).TestObject);

_headInfo = new ChainHeadInfoProvider(_specProvider, _blockTree, _stateProvider);
_nonceManager = new NonceManager(_headInfo.AccountStateProvider);
}

[Test]
public void should_increment_own_transaction_nonces_locally_when_requesting_reservations()
{
using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(0);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(1);
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(1);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressB, out UInt256 nonce))
{
nonce.Should().Be(0);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressB, out UInt256 nonce))
{
nonce.Should().Be(1);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressB, out UInt256 nonce))
{
nonce.Should().Be(2);
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressB, out UInt256 nonce))
{
nonce.Should().Be(2);
locker.Accept();
}
}

[Test]
[Repeat(10)]
public void should_increment_own_transaction_nonces_locally_when_requesting_reservations_in_parallel()
{
const int reservationsCount = 1000;

ConcurrentQueue<UInt256> nonces = new();

var result = Parallel.For(0, reservationsCount, i =>
{
using NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce);
locker.Accept();
nonces.Enqueue(nonce);
});

result.IsCompleted.Should().BeTrue();
using NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce);
nonces.Enqueue(nonce);
nonce.Should().Be(new UInt256(reservationsCount));
nonces.OrderBy(n => n).Should().BeEquivalentTo(Enumerable.Range(0, reservationsCount + 1).Select(i => new UInt256((uint)i)));
}

[Test]
public void should_pick_account_nonce_as_initial_value()
{
IAccountStateProvider accountStateProvider = Substitute.For<IAccountStateProvider>();
Account account = new(0);
accountStateProvider.GetAccount(TestItem.AddressA).Returns(account);
_nonceManager = new NonceManager(accountStateProvider);
using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(0);
}

accountStateProvider.GetAccount(TestItem.AddressA).Returns(account.WithChangedNonce(10));
using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(10);
}
}

[Test]
public void ReserveNonce_should_skip_nonce_if_TxWithNonceReceived()
{
using (NonceLocker locker = _nonceManager.TxWithNonceReceived(TestItem.AddressA, 4))
{
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(0);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(1);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.TxWithNonceReceived(TestItem.AddressA, 2))
{
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(3);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(5);
locker.Accept();
}
}

[Test]
public void should_reuse_nonce_if_tx_rejected()
{
using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(0);
}

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(0);
locker.Accept();
}

using (NonceLocker locker = _nonceManager.TxWithNonceReceived(TestItem.AddressA, 1)) { }

using (NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce))
{
nonce.Should().Be(1);
locker.Accept();
}
}

[Test]
[Repeat(10)]
public void should_lock_on_same_account()
{
using NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce);
nonce.Should().Be(0);
Task task = Task.Run(() =>
{
using NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 _);
});
TimeSpan ts = TimeSpan.FromMilliseconds(1000);
task.Wait(ts);
task.IsCompleted.Should().Be(false);
}

[Test]
[Repeat(10)]
public void should_not_lock_on_different_accounts()
{
using NonceLocker locker = _nonceManager.ReserveNonce(TestItem.AddressA, out UInt256 nonce);
nonce.Should().Be(0);
Task task = Task.Run(() =>
{
using NonceLocker locker2 = _nonceManager.ReserveNonce(TestItem.AddressB, out UInt256 nonce2);
nonce2.Should().Be(0);
});
TimeSpan ts = TimeSpan.FromMilliseconds(1000);
task.Wait(ts);
task.IsCompleted.Should().Be(true);
}
}
Loading