diff --git a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs index 6fba1d025d9..0229abb316e 100644 --- a/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs +++ b/src/Nethermind/Nethermind.JsonRpc.Test/Modules/TestRpcBlockchain.cs @@ -105,12 +105,36 @@ public Builder WithConfig(IJsonRpcConfig config) return this; } + public Builder WithEthRpcModule(Func builder) + { + _blockchain._ethRpcModuleBuilder = builder; + return this; + } + public async Task Build( ISpecProvider? specProvider = null, UInt256? initialValues = null) => (T)(await _blockchain.Build(specProvider, initialValues, true)); } + private Func _ethRpcModuleBuilder = @this => new EthRpcModule( + @this.RpcConfig, + @this.Bridge, + @this.BlockFinder, + @this.ReceiptFinder, + @this.StateReader, + @this.TxPool, + @this.TxSender, + @this.TestWallet, + LimboLogs.Instance, + @this.SpecProvider, + @this.GasPriceOracle, + new EthSyncingInfo(@this.BlockTree, @this.ReceiptStorage, new SyncConfig(), + new StaticSelector(SyncMode.All), Substitute.For(), @this.LogManager), + @this.FeeHistoryOracle ?? + new FeeHistoryOracle(@this.BlockTree, @this.ReceiptStorage, @this.SpecProvider), + new BlocksConfig().SecondsPerSlot); + protected override async Task Build( ISpecProvider? specProvider = null, UInt256? initialValues = null, @@ -145,23 +169,7 @@ protected override async Task Build( TxSender ??= new TxPoolSender(TxPool, TxSealer, NonceManager, EthereumEcdsa ?? new EthereumEcdsa(specProvider.ChainId, LogManager)); GasPriceOracle ??= new GasPriceOracle(BlockFinder, SpecProvider, LogManager); FeeHistoryOracle ??= new FeeHistoryOracle(BlockTree, ReceiptStorage, SpecProvider); - ISyncConfig syncConfig = new SyncConfig(); - EthRpcModule = new EthRpcModule( - RpcConfig, - Bridge, - BlockFinder, - ReceiptFinder, - StateReader, - TxPool, - TxSender, - TestWallet, - LimboLogs.Instance, - SpecProvider, - GasPriceOracle, - new EthSyncingInfo(BlockTree, ReceiptStorage, syncConfig, - new StaticSelector(SyncMode.All), Substitute.For(), LogManager), - FeeHistoryOracle, - blocksConfig.SecondsPerSlot); + EthRpcModule = _ethRpcModuleBuilder(this); return this; } diff --git a/src/Nethermind/Nethermind.Optimism.Test/Nethermind.Optimism.Test.csproj b/src/Nethermind/Nethermind.Optimism.Test/Nethermind.Optimism.Test.csproj index b2d4b0a3d49..bf83b032cc6 100644 --- a/src/Nethermind/Nethermind.Optimism.Test/Nethermind.Optimism.Test.csproj +++ b/src/Nethermind/Nethermind.Optimism.Test/Nethermind.Optimism.Test.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismEthRpcModuleTest.cs b/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismEthRpcModuleTest.cs new file mode 100644 index 00000000000..9005a5906b4 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/Rpc/OptimismEthRpcModuleTest.cs @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System.Threading.Tasks; +using Nethermind.Blockchain.Synchronization; +using Nethermind.Config; +using Nethermind.Core; +using Nethermind.Core.Extensions; +using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; +using Nethermind.Facade; +using Nethermind.Facade.Eth; +using Nethermind.JsonRpc.Client; +using Nethermind.JsonRpc.Modules.Eth.FeeHistory; +using Nethermind.JsonRpc.Test.Modules; +using Nethermind.Logging; +using Nethermind.Optimism.Rpc; +using Nethermind.Serialization.Rlp; +using Nethermind.Synchronization.ParallelSync; +using Nethermind.TxPool; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Optimism.Test.Rpc; + +public class OptimismEthRpcModuleTest +{ + [Test] + public async Task Sequencer_send_transaction_with_signature_will_not_try_to_sign() + { + IBlockchainBridge bridge = Substitute.For(); + ITxSender txSender = Substitute.For(); + txSender.SendTransaction(tx: Arg.Any(), txHandlingOptions: TxHandlingOptions.PersistentBroadcast) + .Returns(returnThis: (TestItem.KeccakA, AcceptTxResult.Accepted)); + + EthereumEcdsa ethereumEcdsa = new EthereumEcdsa(chainId: TestBlockchainIds.ChainId, logManager: LimboLogs.Instance); + TestRpcBlockchain rpcBlockchain = await TestRpcBlockchain + .ForTest(sealEngineType: SealEngineType.Optimism) + .WithBlockchainBridge(bridge) + .WithTxSender(txSender) + .WithOptimismEthRpcModule( + sequencerRpcClient: null /* explicitly using null to behave as Sequencer */, + accountStateProvider: Substitute.For(), + ecdsa: new OptimismEthereumEcdsa(ethereumEcdsa), + sealer: Substitute.For(), + opSpecHelper: Substitute.For()) + .Build(); + + Transaction tx = Build.A.Transaction + .Signed(ecdsa: ethereumEcdsa, privateKey: TestItem.PrivateKeyA) + .TestObject; + string serialized = await rpcBlockchain.TestEthRpc("eth_sendRawTransaction", Rlp.Encode(item: tx, behaviors: RlpBehaviors.None).Bytes.ToHexString()); + + await txSender.Received().SendTransaction(tx: Arg.Any(), txHandlingOptions: TxHandlingOptions.PersistentBroadcast); + Assert.That(actual: serialized, expression: Is.EqualTo(expected: $$"""{"jsonrpc":"2.0","result":"{{TestItem.KeccakA.Bytes.ToHexString(withZeroX: true)}}","id":67}""")); + } +} + +internal static class TestRpcBlockchainExt +{ + public static TestRpcBlockchain.Builder WithOptimismEthRpcModule( + this TestRpcBlockchain.Builder @this, + IJsonRpcClient? sequencerRpcClient, + IAccountStateProvider accountStateProvider, + IEthereumEcdsa ecdsa, + ITxSealer sealer, + IOptimismSpecHelper opSpecHelper) + { + return @this.WithEthRpcModule(blockchain => new OptimismEthRpcModule( + blockchain.RpcConfig, + blockchain.Bridge, + blockchain.BlockFinder, + blockchain.ReceiptFinder, + blockchain.StateReader, + blockchain.TxPool, + blockchain.TxSender, + blockchain.TestWallet, + LimboLogs.Instance, + blockchain.SpecProvider, + blockchain.GasPriceOracle, + new EthSyncingInfo(blockchain.BlockTree, blockchain.ReceiptStorage, new SyncConfig(), + new StaticSelector(SyncMode.All), Substitute.For(), blockchain.LogManager), + blockchain.FeeHistoryOracle ?? + new FeeHistoryOracle(blockchain.BlockTree, blockchain.ReceiptStorage, blockchain.SpecProvider), + new BlocksConfig().SecondsPerSlot, + + sequencerRpcClient, accountStateProvider, ecdsa, sealer, opSpecHelper + )); + } +} diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthModuleFactory.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthModuleFactory.cs index 9604bbdc78b..6d575a75d0c 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthModuleFactory.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthModuleFactory.cs @@ -56,7 +56,6 @@ IOptimismSpecHelper opSpecHelper private readonly IGasPriceOracle _gasPriceOracle = gasPriceOracle ?? throw new ArgumentNullException(nameof(gasPriceOracle)); private readonly IEthSyncingInfo _ethSyncingInfo = ethSyncingInfo ?? throw new ArgumentNullException(nameof(ethSyncingInfo)); private readonly IFeeHistoryOracle _feeHistoryOracle = feeHistoryOracle ?? throw new ArgumentNullException(nameof(feeHistoryOracle)); - private readonly IJsonRpcClient? _sequencerRpcClient = sequencerRpcClient ?? throw new ArgumentNullException(nameof(sequencerRpcClient)); private readonly IAccountStateProvider _accountStateProvider = accountStateProvider ?? throw new ArgumentNullException(nameof(accountStateProvider)); private readonly IEthereumEcdsa _ecdsa = ecdsa ?? throw new ArgumentNullException(nameof(ecdsa)); private readonly ITxSealer _sealer = sealer ?? throw new ArgumentNullException(nameof(sealer)); @@ -82,7 +81,7 @@ public override IOptimismEthRpcModule Create() _feeHistoryOracle, secondsPerSlot, - _sequencerRpcClient, + sequencerRpcClient, _accountStateProvider, _ecdsa, _sealer, diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs index 6302ab92643..3cc73945633 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/OptimismEthRpcModule.cs @@ -130,13 +130,15 @@ public override async Task> eth_sendRawTransaction(byte[] { if (_sequencerRpcClient is null) { - return ResultWrapper.Fail("No sequencer url in the config"); + return await base.eth_sendRawTransaction(transaction); } + Hash256? result = await _sequencerRpcClient.Post(nameof(eth_sendRawTransaction), transaction); if (result is null) { return ResultWrapper.Fail("Failed to forward transaction"); } + return ResultWrapper.Success(result); } diff --git a/src/Nethermind/Nethermind.Optimism/Rpc/RegisterOptimismRpcModules.cs b/src/Nethermind/Nethermind.Optimism/Rpc/RegisterOptimismRpcModules.cs index 2b509d129c0..360ccaa1eda 100644 --- a/src/Nethermind/Nethermind.Optimism/Rpc/RegisterOptimismRpcModules.cs +++ b/src/Nethermind/Nethermind.Optimism/Rpc/RegisterOptimismRpcModules.cs @@ -52,7 +52,7 @@ protected override void RegisterEthRpcModule(IRpcModuleProvider rpcModuleProvide if (_config.SequencerUrl is null && _logger.IsWarn) { - _logger.Warn($"SequencerUrl is not set."); + _logger.Warn($"SequencerUrl is not set. Nethermind will behave as a Sequencer"); } BasicJsonRpcClient? sequencerJsonRpcClient = _config.SequencerUrl is null