From 373e49f347dd954ab79f1a44923e0961f8496608 Mon Sep 17 00:00:00 2001 From: avalonche Date: Tue, 21 Sep 2021 17:28:28 +1000 Subject: [PATCH] add megabundle pool --- .../Nethermind.Mev.Test/BundlePoolTests.cs | 9 +- .../Nethermind.Mev.Test/MetricsTests.cs | 4 +- .../MevRpcModuleTests.TestMevRpcBlockchain.cs | 3 +- .../Nethermind.Mev.Test/TestBundlePool.cs | 6 +- .../Data/MegabundleEventArgs.cs | 32 +++++ .../Nethermind.Mev/Data/MevBundle.cs | 4 - .../Nethermind.Mev/Data/MevMegabundle.cs | 42 ++++++ src/Nethermind/Nethermind.Mev/IMevConfig.cs | 6 +- src/Nethermind/Nethermind.Mev/Metrics.cs | 8 +- src/Nethermind/Nethermind.Mev/MevConfig.cs | 3 - src/Nethermind/Nethermind.Mev/MevPlugin.cs | 43 ++++-- src/Nethermind/Nethermind.Mev/MevRpcModule.cs | 17 +-- .../Nethermind.Mev/Source/BundlePool.cs | 132 +++++++++++++++--- .../Nethermind.Mev/Source/IBundlePool.cs | 13 +- .../Source/ISimulatedBundleSource.cs | 6 +- .../Source/MegabundleSelector.cs | 46 ++++++ 16 files changed, 315 insertions(+), 59 deletions(-) create mode 100644 src/Nethermind/Nethermind.Mev/Data/MegabundleEventArgs.cs create mode 100644 src/Nethermind/Nethermind.Mev/Data/MevMegabundle.cs create mode 100644 src/Nethermind/Nethermind.Mev/Source/MegabundleSelector.cs diff --git a/src/Nethermind/Nethermind.Mev.Test/BundlePoolTests.cs b/src/Nethermind/Nethermind.Mev.Test/BundlePoolTests.cs index dbe0af84e34..ce4e7c1e22c 100644 --- a/src/Nethermind/Nethermind.Mev.Test/BundlePoolTests.cs +++ b/src/Nethermind/Nethermind.Mev.Test/BundlePoolTests.cs @@ -343,7 +343,8 @@ public TestContext(ITimestamper? timestamper = null, IBundleSimulator? bundleSim new TxValidator(BlockTree.ChainId), new TestSpecProvider(London.Instance), config ?? new MevConfig(), - LimboLogs.Instance); + LimboLogs.Instance, + EthereumEcdsa); if (!addTestBundles) return; BundleTransaction tx1 = CreateTransaction(TestItem.PrivateKeyA); @@ -372,8 +373,10 @@ private void AddBundle(MevBundle bundle) public TestBundlePool BundlePool { get; } public ITimestamper Timestamper { get; private set; } = - new ManualTimestamper(DateTime.UnixEpoch.AddSeconds(DefaultTimestamp)); - + new ManualTimestamper(DateTime.UnixEpoch.AddSeconds(DefaultTimestamp)); + + public IEthereumEcdsa EthereumEcdsa { get; } = Substitute.For(); + private MevBundle CreateBundle(long blockNumber, params BundleTransaction[] transactions) => new(blockNumber, transactions); } } diff --git a/src/Nethermind/Nethermind.Mev.Test/MetricsTests.cs b/src/Nethermind/Nethermind.Mev.Test/MetricsTests.cs index 197de3779c9..26d932aa84d 100644 --- a/src/Nethermind/Nethermind.Mev.Test/MetricsTests.cs +++ b/src/Nethermind/Nethermind.Mev.Test/MetricsTests.cs @@ -24,6 +24,7 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; using Nethermind.Int256; using Nethermind.Logging; using Nethermind.Mev.Data; @@ -166,7 +167,8 @@ private static TestBundlePool CreateTestBundlePool() new TxValidator(blockTree.ChainId), new TestSpecProvider(London.Instance), new MevConfig(), - LimboLogs.Instance); + LimboLogs.Instance, + Substitute.For()); return bundlePool; } } diff --git a/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs b/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs index 2737c3e6a7e..6d4b72a072a 100644 --- a/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs +++ b/src/Nethermind/Nethermind.Mev.Test/MevRpcModuleTests.TestMevRpcBlockchain.cs @@ -33,6 +33,7 @@ using Nethermind.Core.Test; using Nethermind.Core.Test.Blockchain; using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; using Nethermind.Int256; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Test.Modules; @@ -186,7 +187,7 @@ protected override BlockProcessor CreateBlockProcessor() ProcessingOptions.ProducingBlock); TxBundleSimulator txBundleSimulator = new(_tracerFactory, GasLimitCalculator, Timestamper, TxPool, SpecProvider, Signer); - BundlePool = new TestBundlePool(BlockTree, txBundleSimulator, Timestamper, new TxValidator(BlockTree.ChainId), SpecProvider, _mevConfig, LogManager); + BundlePool = new TestBundlePool(BlockTree, txBundleSimulator, Timestamper, new TxValidator(BlockTree.ChainId), SpecProvider, _mevConfig, LogManager, EthereumEcdsa); return blockProcessor; } diff --git a/src/Nethermind/Nethermind.Mev.Test/TestBundlePool.cs b/src/Nethermind/Nethermind.Mev.Test/TestBundlePool.cs index d4cb31eb30e..fdc6967e1ed 100644 --- a/src/Nethermind/Nethermind.Mev.Test/TestBundlePool.cs +++ b/src/Nethermind/Nethermind.Mev.Test/TestBundlePool.cs @@ -21,6 +21,7 @@ using Nethermind.Blockchain; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Crypto; using Nethermind.Logging; using Nethermind.Mev.Data; using Nethermind.Mev.Execution; @@ -39,8 +40,9 @@ public TestBundlePool(IBlockTree blockTree, ITxValidator txValidator, ISpecProvider specProvider, IMevConfig mevConfig, - ILogManager logManager) - : base(blockTree, simulator, timestamper, txValidator, specProvider, mevConfig, logManager) + ILogManager logManager, + IEthereumEcdsa ecdsa) + : base(blockTree, simulator, timestamper, txValidator, specProvider, mevConfig, logManager, ecdsa) { } diff --git a/src/Nethermind/Nethermind.Mev/Data/MegabundleEventArgs.cs b/src/Nethermind/Nethermind.Mev/Data/MegabundleEventArgs.cs new file mode 100644 index 00000000000..0b294c8b71e --- /dev/null +++ b/src/Nethermind/Nethermind.Mev/Data/MegabundleEventArgs.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System; +using Nethermind.Core; + +namespace Nethermind.Mev.Data +{ + public class MegabundleEventArgs : BundleEventArgs + { + public Address RelayAddress { get; } + + public MegabundleEventArgs(MevBundle mevBundle, Address relayAddress): base(mevBundle) + { + RelayAddress = relayAddress; + } + } +} diff --git a/src/Nethermind/Nethermind.Mev/Data/MevBundle.cs b/src/Nethermind/Nethermind.Mev/Data/MevBundle.cs index 1d422feae1c..92e7a596be1 100644 --- a/src/Nethermind/Nethermind.Mev/Data/MevBundle.cs +++ b/src/Nethermind/Nethermind.Mev/Data/MevBundle.cs @@ -17,13 +17,9 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; -using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Int256; -using Nethermind.Mev.Source; -using Nethermind.Serialization.Rlp; namespace Nethermind.Mev.Data { diff --git a/src/Nethermind/Nethermind.Mev/Data/MevMegabundle.cs b/src/Nethermind/Nethermind.Mev/Data/MevMegabundle.cs new file mode 100644 index 00000000000..1ec26c93fbb --- /dev/null +++ b/src/Nethermind/Nethermind.Mev/Data/MevMegabundle.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System; +using Nethermind.Core.Crypto; + +namespace Nethermind.Mev.Data +{ + public class MevMegabundle: IEquatable + { + public MevMegabundle(MevBundle bundle, Signature relaySignature) + { + Bundle = bundle; + RelaySignature = relaySignature; + } + + public MevBundle Bundle { get; } + public Signature RelaySignature { get; } + + public bool Equals(MevMegabundle? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Equals(Bundle, other.Bundle) + && Equals(RelaySignature, other.RelaySignature); + } + } +} diff --git a/src/Nethermind/Nethermind.Mev/IMevConfig.cs b/src/Nethermind/Nethermind.Mev/IMevConfig.cs index 661b8627b62..05129fb0c61 100644 --- a/src/Nethermind/Nethermind.Mev/IMevConfig.cs +++ b/src/Nethermind/Nethermind.Mev/IMevConfig.cs @@ -14,10 +14,11 @@ // You should have received a copy of the GNU Lesser General Public License // along with the Nethermind. If not, see . +using System.Collections.Generic; +using System.Linq; using Nethermind.Config; using Nethermind.Core; using Nethermind.Int256; -using Nethermind.Mev.Data; namespace Nethermind.Mev { @@ -44,5 +45,8 @@ public interface IMevConfig : IConfig [ConfigItem(Description = "Defines the trusted relay addresses to receive megabundles from", DefaultValue = "")] string TrustedRelays { get; set; } + + public IEnumerable
GetTrustedRelayAddresses() => + TrustedRelays.Split(",").Select(address => new Address(address)); } } diff --git a/src/Nethermind/Nethermind.Mev/Metrics.cs b/src/Nethermind/Nethermind.Mev/Metrics.cs index ddbaaae2ab1..589f50d3c7e 100644 --- a/src/Nethermind/Nethermind.Mev/Metrics.cs +++ b/src/Nethermind/Nethermind.Mev/Metrics.cs @@ -24,10 +24,16 @@ public static class Metrics { [Description("Total number of bundles received for inclusion")] public static int BundlesReceived { get; set; } = 0; - + [Description("Total number of valid bundles received for inclusion")] public static int ValidBundlesReceived { get; set; } = 0; + [Description("Total number of megabundles received for inclusion")] + public static int MegabundlesReceived { get; set; } = 0; + + [Description("Total number of valid megabundles received for inclusion")] + public static int ValidMegabundlesReceived { get; set; } = 0; + [Description("Total number of bundles simulated")] public static int BundlesSimulated { get; set; } = 0; diff --git a/src/Nethermind/Nethermind.Mev/MevConfig.cs b/src/Nethermind/Nethermind.Mev/MevConfig.cs index dadea759b7e..d47a30b5e9d 100644 --- a/src/Nethermind/Nethermind.Mev/MevConfig.cs +++ b/src/Nethermind/Nethermind.Mev/MevConfig.cs @@ -15,10 +15,7 @@ // along with the Nethermind. If not, see . // -using Nethermind.Core; -using Nethermind.Core.Crypto; using Nethermind.Int256; -using Nethermind.Mev.Data; namespace Nethermind.Mev { diff --git a/src/Nethermind/Nethermind.Mev/MevPlugin.cs b/src/Nethermind/Nethermind.Mev/MevPlugin.cs index 0f43c913f9e..4e628dff3e1 100644 --- a/src/Nethermind/Nethermind.Mev/MevPlugin.cs +++ b/src/Nethermind/Nethermind.Mev/MevPlugin.cs @@ -16,11 +16,9 @@ // using System; -using System.Collections.Concurrent; -using System.Threading.Tasks; using System.Collections.Generic; -using System.ComponentModel.Design; using System.Linq; +using System.Threading.Tasks; using Nethermind.Api; using Nethermind.Api.Extensions; using Nethermind.Blockchain; @@ -29,15 +27,12 @@ using Nethermind.Consensus; using Nethermind.Consensus.Transactions; using Nethermind.Core; -using Nethermind.Db; -using Nethermind.Facade; using Nethermind.JsonRpc; using Nethermind.JsonRpc.Modules; using Nethermind.Logging; using Nethermind.Mev.Data; using Nethermind.Mev.Execution; using Nethermind.Mev.Source; -using Nethermind.TxPool; namespace Nethermind.Mev { @@ -91,7 +86,8 @@ private BundlePool BundlePool getFromApi.TxValidator!, getFromApi.SpecProvider!, _mevConfig, - getFromApi.LogManager); + getFromApi.LogManager, + getFromApi.EthereumEcdsa!); } return _bundlePool; @@ -162,7 +158,7 @@ public async Task InitBlockProducer(IConsensusPlugin consensusPl _nethermindApi.BlockProducerEnvFactory.TransactionsExecutorFactory = new MevBlockProducerTransactionsExecutorFactory(_nethermindApi.SpecProvider!, _nethermindApi.LogManager); List blockProducers = - new(_mevConfig.MaxMergedBundles + 1); + new(_mevConfig.MaxMergedBundles + _mevConfig.GetTrustedRelayAddresses().Count() + 1); // Add non-mev block MevBlockProducer.MevBlockProducerInfo standardProducer = await CreateProducer(consensusPlugin); @@ -176,6 +172,13 @@ public async Task InitBlockProducer(IConsensusPlugin consensusPl blockProducers.Add(bundleProducer); } + foreach (Address address in _mevConfig.GetTrustedRelayAddresses()) + { + MegabundleSelector megabundleSelector = new(BundlePool, address); + MevBlockProducer.MevBlockProducerInfo bundleProducer = await CreateProducer(consensusPlugin, address, new BundleTxSource(megabundleSelector, _nethermindApi.Timestamper)); + blockProducers.Add(bundleProducer); + } + return new MevBlockProducer(consensusPlugin.DefaultBlockProductionTrigger, _nethermindApi.LogManager, blockProducers.ToArray()); } @@ -207,6 +210,30 @@ bool BundleLimitTriggerCondition(BlockProductionEventArgs e) return new MevBlockProducer.MevBlockProducerInfo(producer, manualTrigger, new BeneficiaryTracer()); } + private async Task CreateProducer( + IConsensusPlugin consensusPlugin, + Address relayAddress, + ITxSource? additionalTxSource = null) + { + bool MegabundleTriggerCondition(BlockProductionEventArgs e) + { + BlockHeader? parent = _nethermindApi.BlockTree!.GetProducedBlockParent(e.ParentHeader); + if (parent is not null) + { + MevBundle? bundle = BundlePool.GetMegabundle(parent, _nethermindApi.Timestamper, relayAddress); + return bundle is not null; + } + + return false; + } + + IManualBlockProductionTrigger manualTrigger = new BuildBlocksWhenRequested(); + IBlockProductionTrigger trigger = new TriggerWithCondition(manualTrigger, MegabundleTriggerCondition); + + IBlockProducer producer = await consensusPlugin.InitBlockProducer(trigger, additionalTxSource); + return new MevBlockProducer.MevBlockProducerInfo(producer, manualTrigger, new BeneficiaryTracer()); + } + public bool Enabled => _mevConfig.Enabled; public ValueTask DisposeAsync() => ValueTask.CompletedTask; diff --git a/src/Nethermind/Nethermind.Mev/MevRpcModule.cs b/src/Nethermind/Nethermind.Mev/MevRpcModule.cs index 798930cc601..db7ec56624d 100644 --- a/src/Nethermind/Nethermind.Mev/MevRpcModule.cs +++ b/src/Nethermind/Nethermind.Mev/MevRpcModule.cs @@ -16,22 +16,15 @@ using System; using System.Collections.Generic; -using System.Numerics; using System.Linq; -using System.Text; using System.Threading; using Nethermind.Blockchain.Find; -using Nethermind.JsonRpc; -using Nethermind.JsonRpc.Data; -using Nethermind.Int256; -using Nethermind.Core; -using Nethermind.Facade; -using Nethermind.Logging; -using Nethermind.Blockchain; -using Nethermind.Blockchain.Tracing; using Nethermind.Consensus; +using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Specs; +using Nethermind.Int256; +using Nethermind.JsonRpc; using Nethermind.JsonRpc.Modules; using Nethermind.Mev.Data; using Nethermind.Mev.Execution; @@ -39,7 +32,6 @@ using Nethermind.Serialization.Rlp; using Nethermind.State; using Nethermind.Trie; -using Newtonsoft.Json; namespace Nethermind.Mev { @@ -88,7 +80,8 @@ public ResultWrapper eth_sendMegabundle(MevMegabundleRpc mevMegabundleRpc) { BundleTransaction[] txs = Decode(mevMegabundleRpc.Txs, mevMegabundleRpc.RevertingTxHashes?.ToHashSet()); MevBundle bundle = new(mevMegabundleRpc.BlockNumber, txs, mevMegabundleRpc.MinTimestamp, mevMegabundleRpc.MaxTimestamp); - bool result = _bundlePool.AddBundle(bundle); + MevMegabundle megabundle = new(bundle, mevMegabundleRpc.RelaySignature!); + bool result = _bundlePool.AddMegabundle(megabundle); return ResultWrapper.Success(result); } diff --git a/src/Nethermind/Nethermind.Mev/Source/BundlePool.cs b/src/Nethermind/Nethermind.Mev/Source/BundlePool.cs index d19e2d2efcf..fd10c13474b 100644 --- a/src/Nethermind/Nethermind.Mev/Source/BundlePool.cs +++ b/src/Nethermind/Nethermind.Mev/Source/BundlePool.cs @@ -16,30 +16,23 @@ // using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Drawing; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using Nethermind.Blockchain; -using Nethermind.Blockchain.Find; using Nethermind.Core; -using Nethermind.Core.Collections; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; +using Nethermind.Crypto; using Nethermind.Int256; -using Nethermind.JsonRpc; using Nethermind.Logging; using Nethermind.Mev.Data; using Nethermind.Mev.Execution; using Nethermind.TxPool; using Nethermind.TxPool.Collections; -using Org.BouncyCastle.Security; -using ILogger = Nethermind.Logging.ILogger; namespace Nethermind.Mev.Source { @@ -53,13 +46,17 @@ public class BundlePool : IBundlePool, ISimulatedBundleSource, IDisposable private readonly IBlockTree _blockTree; private readonly IBundleSimulator _simulator; private readonly BundleSortedPool _bundles; + private readonly ConcurrentDictionary _megabundles = new(); private readonly ConcurrentDictionary> _simulatedBundles = new(); private readonly ILogger _logger; + private readonly IEthereumEcdsa _ecdsa; private long HeadNumber => _blockTree.Head?.Number ?? 0; - public event EventHandler? NewReceived; - public event EventHandler? NewPending; + public event EventHandler? NewReceivedBundle; + public event EventHandler? NewPendingBundle; + public event EventHandler? NewReceivedMegabundle; + public event EventHandler? NewPendingMegabundle; public BundlePool( IBlockTree blockTree, @@ -68,7 +65,8 @@ public BundlePool( ITxValidator txValidator, ISpecProvider specProvider, IMevConfig mevConfig, - ILogManager logManager) + ILogManager logManager, + IEthereumEcdsa ecdsa) { _timestamper = timestamper; _txValidator = txValidator; @@ -77,6 +75,7 @@ public BundlePool( _blockTree = blockTree; _simulator = simulator; _logger = logManager.GetClassLogger(); + _ecdsa = ecdsa; IComparer comparer = CompareMevBundleByBlock.Default.ThenBy(CompareMevBundleByMinTimestamp.Default); _bundles = new BundleSortedPool( @@ -86,6 +85,7 @@ public BundlePool( _bundles.Removed += OnBundleRemoved; _blockTree.NewHeadBlock += OnNewBlock; + NewPendingMegabundle += OnNewPendingMegabundle; } public Task> GetBundles(BlockHeader parent, UInt256 timestamp, long gasLimit, CancellationToken token = default) => @@ -115,11 +115,40 @@ private IEnumerable GetBundles(long blockNumber, UInt256 minTimestamp } } + public Task GetMegabundle(BlockHeader parent, UInt256 timestamp, long gasLimit, + Address relayAddress, CancellationToken token = default) => + Task.FromResult(GetMegabundle(parent.Number + 1, timestamp, relayAddress, token)); + + public MevBundle? GetMegabundle(long blockNumber, UInt256 timestamp, Address relayAddress, CancellationToken token = default) => + GetMegabundle(blockNumber, timestamp, timestamp, relayAddress, token); + + private MevBundle? GetMegabundle(long blockNumber, UInt256 minTimestamp, UInt256 maxTimestamp, Address relayAddress, + CancellationToken token = default) + { + if (_megabundles.TryGetValue(relayAddress, out MevBundle? bundle)) + { + if (token.IsCancellationRequested) + { + return null; + } + + bool bundleIsInFuture = bundle.MinTimestamp != UInt256.Zero && minTimestamp < bundle.MinTimestamp; + bool bundleIsTooOld = bundle.MaxTimestamp != UInt256.Zero && maxTimestamp > bundle.MaxTimestamp; + bool bundleIsInCurrentBlock = bundle.BlockNumber == blockNumber; + if (!bundleIsInFuture && !bundleIsTooOld && bundleIsInCurrentBlock) + { + return bundle; + } + } + + return null; + } + public bool AddBundle(MevBundle bundle) { Metrics.BundlesReceived++; BundleEventArgs bundleEventArgs = new(bundle); - NewReceived?.Invoke(this, bundleEventArgs); + NewReceivedBundle?.Invoke(this, bundleEventArgs); if (ValidateBundle(bundle)) { @@ -128,7 +157,7 @@ public bool AddBundle(MevBundle bundle) if (result) { Metrics.ValidBundlesReceived++; - NewPending?.Invoke(this, bundleEventArgs); + NewPendingBundle?.Invoke(this, bundleEventArgs); if (bundle.BlockNumber == HeadNumber + 1) { TrySimulateBundle(bundle); @@ -141,6 +170,37 @@ public bool AddBundle(MevBundle bundle) return false; } + public bool AddMegabundle(MevMegabundle megabundle) + { + Metrics.MegabundlesReceived++; + Address relayAddress = _ecdsa.RecoverAddress(megabundle.RelaySignature, megabundle.Bundle.Hash)!; + MegabundleEventArgs megabundleEventArgs = new(megabundle.Bundle, relayAddress); + NewReceivedMegabundle?.Invoke(this, megabundleEventArgs); + + if (ValidateBundle(megabundle.Bundle) && IsTrustedRelay(relayAddress)) + { + _megabundles.AddOrUpdate(relayAddress, + _ => megabundle.Bundle, + (_, _) => megabundle.Bundle); + Metrics.ValidMegabundlesReceived++; + NewPendingMegabundle?.Invoke(this, megabundleEventArgs); + + if (megabundle.Bundle.BlockNumber == HeadNumber + 1) + { + TrySimulateBundle(megabundle.Bundle); + } + + return true; + } + + return false; + } + + private bool IsTrustedRelay(Address relayAddress) + { + return _mevConfig.GetTrustedRelayAddresses().Contains(relayAddress); + } + private bool ValidateBundle(MevBundle bundle) { if (HeadNumber >= bundle.BlockNumber) @@ -153,22 +213,28 @@ private bool ValidateBundle(MevBundle bundle) if (_logger.IsDebug) _logger.Debug($"Bundle rejected, because it doesn't contain transactions."); return false; } - + ulong currentTimestamp = _timestamper.UnixTime.Seconds; if (bundle.MaxTimestamp < bundle.MinTimestamp) { - if (_logger.IsDebug) _logger.Debug($"Bundle rejected, because {nameof(bundle.MaxTimestamp)} {bundle.MaxTimestamp} is < {nameof(bundle.MinTimestamp)} {bundle.MinTimestamp}."); + if (_logger.IsDebug) + _logger.Debug( + $"Bundle rejected, because {nameof(bundle.MaxTimestamp)} {bundle.MaxTimestamp} is < {nameof(bundle.MinTimestamp)} {bundle.MinTimestamp}."); return false; } else if (bundle.MaxTimestamp != 0 && bundle.MaxTimestamp < currentTimestamp) { - if (_logger.IsDebug) _logger.Debug($"Bundle rejected, because {nameof(bundle.MaxTimestamp)} {bundle.MaxTimestamp} is < current {currentTimestamp}."); + if (_logger.IsDebug) + _logger.Debug( + $"Bundle rejected, because {nameof(bundle.MaxTimestamp)} {bundle.MaxTimestamp} is < current {currentTimestamp}."); return false; } else if (bundle.MinTimestamp != 0 && bundle.MinTimestamp > currentTimestamp + _mevConfig.BundleHorizon) { - if (_logger.IsDebug) _logger.Debug($"Bundle rejected, because {nameof(bundle.MinTimestamp)} {bundle.MaxTimestamp} is further into the future than accepted horizon {_mevConfig.BundleHorizon}."); + if (_logger.IsDebug) + _logger.Debug( + $"Bundle rejected, because {nameof(bundle.MinTimestamp)} {bundle.MaxTimestamp} is further into the future than accepted horizon {_mevConfig.BundleHorizon}."); return false; } @@ -325,6 +391,12 @@ private void StopSimulation(SimulatedMevBundleContext simulation) } } + private void OnNewPendingMegabundle(object? sender, MegabundleEventArgs e) + { + _megabundles.TryRemove(e.RelayAddress, out _); + RemoveSimulation(e.MevBundle); + } + async Task> ISimulatedBundleSource.GetBundles(BlockHeader parent, UInt256 timestamp, long gasLimit, CancellationToken token) { HashSet bundles = (await GetBundles(parent, timestamp, gasLimit, token)).ToHashSet(); @@ -347,16 +419,38 @@ async Task> ISimulatedBundleSource.GetBundles(Bl return res; } - else + return (Enumerable.Empty()); + } + + async Task ISimulatedBundleSource.GetMegabundle(BlockHeader parent, UInt256 timestamp, + long gasLimit, Address relayAddress, CancellationToken token) + { + MevBundle? bundle = await GetMegabundle(parent, timestamp, gasLimit, relayAddress, token); + + if (bundle is not null && _simulatedBundles.TryGetValue(parent.Number, out ConcurrentDictionary<(MevBundle Bundle, Keccak BlockHash), SimulatedMevBundleContext>? simulatedBundlesForBlock)) { - return (Enumerable.Empty()); + if (simulatedBundlesForBlock.TryGetValue((bundle, parent.Hash!), + out SimulatedMevBundleContext? simulatedBundle)) + { + Task resultTask = simulatedBundle.Task; + await Task.WhenAny(resultTask, token.AsTask()); + + bool success = resultTask.IsCompletedSuccessfully && resultTask.Result.Success; + bool withinGasLimit = resultTask.Result.GasUsed <= gasLimit; + if (success && withinGasLimit) + { + return resultTask.Result; + } + } } + return null; } public void Dispose() { _blockTree.NewHeadBlock -= OnNewBlock; _bundles.Removed -= OnBundleRemoved; + NewPendingMegabundle -= OnNewPendingMegabundle; } protected class SimulatedMevBundleContext : IDisposable diff --git a/src/Nethermind/Nethermind.Mev/Source/IBundlePool.cs b/src/Nethermind/Nethermind.Mev/Source/IBundlePool.cs index 962802fcb57..78ea0e4da1b 100644 --- a/src/Nethermind/Nethermind.Mev/Source/IBundlePool.cs +++ b/src/Nethermind/Nethermind.Mev/Source/IBundlePool.cs @@ -26,15 +26,22 @@ namespace Nethermind.Mev.Source { public interface IBundlePool : IBundleSource { - event EventHandler NewReceived; - event EventHandler NewPending; + event EventHandler NewReceivedBundle; + event EventHandler NewPendingBundle; + event EventHandler NewReceivedMegabundle; + event EventHandler NewPendingMegabundle; bool AddBundle(MevBundle bundle); - IEnumerable GetBundles(long block, UInt256 timestamp, CancellationToken token = default); + bool AddMegabundle(MevMegabundle megabundle); + IEnumerable GetBundles(long block, UInt256 timestamp, CancellationToken token = default); + MevBundle? GetMegabundle(long block, UInt256 timestamp, Address relayAddress, CancellationToken token = default); } public static class BundlePoolExtensions { public static IEnumerable GetBundles(this IBundlePool bundleSource, BlockHeader parent, ITimestamper timestamper, CancellationToken token = default) => bundleSource.GetBundles(parent.Number + 1, timestamper.UnixTime.Seconds, token); + + public static MevBundle? GetMegabundle(this IBundlePool bundleSource, BlockHeader parent, ITimestamper timestamper, Address relayAddress, CancellationToken token = default) => + bundleSource.GetMegabundle(parent.Number + 1, timestamper.UnixTime.Seconds, relayAddress, token); } } diff --git a/src/Nethermind/Nethermind.Mev/Source/ISimulatedBundleSource.cs b/src/Nethermind/Nethermind.Mev/Source/ISimulatedBundleSource.cs index ff40ef06841..ffa26538061 100644 --- a/src/Nethermind/Nethermind.Mev/Source/ISimulatedBundleSource.cs +++ b/src/Nethermind/Nethermind.Mev/Source/ISimulatedBundleSource.cs @@ -26,6 +26,10 @@ namespace Nethermind.Mev.Source { public interface ISimulatedBundleSource { - Task> GetBundles(BlockHeader parent, UInt256 timestamp, long gasLimit, CancellationToken token = default); + Task> GetBundles(BlockHeader parent, UInt256 timestamp, long gasLimit, + CancellationToken token = default); + + Task GetMegabundle(BlockHeader parent, UInt256 timestamp, long gasLimit, Address relayAddress, + CancellationToken token = default); } } diff --git a/src/Nethermind/Nethermind.Mev/Source/MegabundleSelector.cs b/src/Nethermind/Nethermind.Mev/Source/MegabundleSelector.cs new file mode 100644 index 00000000000..f5dd4e039fe --- /dev/null +++ b/src/Nethermind/Nethermind.Mev/Source/MegabundleSelector.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2021 Demerzel Solutions Limited +// This file is part of the Nethermind library. +// +// The Nethermind library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Nethermind library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Nethermind. If not, see . +// + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Nethermind.Core; +using Nethermind.Int256; +using Nethermind.Mev.Data; + +namespace Nethermind.Mev.Source +{ + public class MegabundleSelector : IBundleSource + { + private readonly ISimulatedBundleSource _simulatedBundleSource; + private readonly Address _relayAddress; + + public MegabundleSelector(ISimulatedBundleSource simulatedBundleSource, Address relayAddress) + { + _simulatedBundleSource = simulatedBundleSource; + _relayAddress = relayAddress; + } + + public async Task> GetBundles(BlockHeader parent, UInt256 timestamp, long gasLimit, + CancellationToken token = default) + { + SimulatedMevBundle? simulatedBundle = await _simulatedBundleSource.GetMegabundle(parent, timestamp, gasLimit, _relayAddress, token); + return simulatedBundle is null ? Enumerable.Empty() : new [] {simulatedBundle.Bundle}; + } + } +}