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

Add MEV v0.4 spec #3423

Merged
merged 23 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
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
394 changes: 300 additions & 94 deletions src/Nethermind/Nethermind.Mev.Test/BundlePoolTests.cs

Large diffs are not rendered by default.

74 changes: 69 additions & 5 deletions src/Nethermind/Nethermind.Mev.Test/MetricsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -58,7 +59,7 @@ public void Should_count_valid_bundles()
bundlePool.AddBundle(new MevBundle(1, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}, 0, 0));
bundlePool.AddBundle(new MevBundle(3, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}, 0, 0));
bundlePool.AddBundle(new MevBundle(4, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}, 0, 0));

int deltaBundlesReceived = Metrics.BundlesReceived - beforeBundlesReceived;
int deltaValidBundlesReceived = Metrics.ValidBundlesReceived - beforeValidBundlesReceived;
int deltaBundlesSimulated = Metrics.BundlesSimulated - beforeBundlesSimulated;
Expand All @@ -68,6 +69,32 @@ public void Should_count_valid_bundles()
deltaBundlesSimulated.Should().Be(2); // only first two bundles are at current head
}

[Test]
public void Should_count_valid_megabundles()
{
var ecdsa = Substitute.For<IEthereumEcdsa>();
ecdsa.RecoverAddress(Arg.Any<Signature>(), Arg.Any<Keccak>()).Returns(TestItem.AddressB);

TestBundlePool bundlePool = CreateTestBundlePool(ecdsa);

int beforeMegabundlesReceived = Metrics.MegabundlesReceived;
int beforeValidMegabundlesReceived = Metrics.ValidMegabundlesReceived;
int beforeBundlesSimulated = Metrics.BundlesSimulated;

bundlePool.AddMegabundle(new MevMegabundle(1, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}));
bundlePool.AddMegabundle(new MevMegabundle(1, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}));
bundlePool.AddMegabundle(new MevMegabundle(3, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}));
bundlePool.AddMegabundle(new MevMegabundle(4, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}));

int deltaMegabundlesReceived = Metrics.MegabundlesReceived - beforeMegabundlesReceived;
int deltaValidMegabundlesReceived = Metrics.ValidMegabundlesReceived - beforeValidMegabundlesReceived;
int deltaMegabundlesSimulated = Metrics.BundlesSimulated - beforeBundlesSimulated;

deltaMegabundlesReceived.Should().Be(4);
deltaValidMegabundlesReceived.Should().Be(4);
deltaMegabundlesSimulated.Should().Be(2); // first two megeabundles are at current head
}

[Test]
[Explicit("Fails on CI")]
public void Should_count_invalid_bundles()
Expand Down Expand Up @@ -100,6 +127,42 @@ public void Should_count_invalid_bundles()
deltaBundlesSimulated.Should().Be(0); // should not simulate invalid bundle
}

[Test]
public void Should_count_invalid_megabundles()
{
var ecdsa = Substitute.For<IEthereumEcdsa>();

MevMegabundle[] bundles = new[]
{
new MevMegabundle(1, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}, minTimestamp: 5, maxTimestamp: 0), // invalid
new MevMegabundle(2, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}, minTimestamp: 0, maxTimestamp: 0), // invalid relay address
new MevMegabundle(3, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}, minTimestamp: 0, maxTimestamp: long.MaxValue), // invalid
new MevMegabundle(4, new []{Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(TestItem.PrivateKeyA).TestObject}, minTimestamp: 0, maxTimestamp: 0)
};

ecdsa.RecoverAddress(Arg.Any<Signature>(), bundles[2].Hash).Returns(TestItem.AddressC); // not in list of trusted relay addresses
ecdsa.RecoverAddress(Arg.Any<Signature>(), bundles[3].Hash).Returns(TestItem.AddressB); // trusted relay address

TestBundlePool bundlePool = CreateTestBundlePool(ecdsa);

int beforeMegabundlesReceived = Metrics.MegabundlesReceived;
int beforeValidMegabundlesReceived = Metrics.ValidMegabundlesReceived;
int beforeMegabundlesSimulated = Metrics.BundlesSimulated;

foreach (MevMegabundle mevMegabundle in bundles)
{
bundlePool.AddMegabundle(mevMegabundle);
}

int deltaBundlesReceived = Metrics.MegabundlesReceived - beforeMegabundlesReceived;
int deltaValidBundlesReceived = Metrics.ValidMegabundlesReceived - beforeValidMegabundlesReceived;
int deltaBundlesSimulated = Metrics.BundlesSimulated - beforeMegabundlesSimulated;

deltaBundlesReceived.Should().Be(4);
deltaValidBundlesReceived.Should().Be(1);
deltaBundlesSimulated.Should().Be(0); // should not simulate invalid bundle
}

[Test]
public async Task Should_count_total_coinbase_payments()
{
Expand Down Expand Up @@ -140,7 +203,7 @@ public async Task Should_count_total_coinbase_payments()
deltaCoinbasePayments.Should().Be(100000000000);
}

private static TestBundlePool CreateTestBundlePool()
private static TestBundlePool CreateTestBundlePool(IEthereumEcdsa? ecdsa = null, MevConfig? config = null)
{
var blockTree = Substitute.For<IBlockTree>();
blockTree.ChainId.Returns((ulong)ChainId.Mainnet);
Expand All @@ -158,15 +221,16 @@ private static TestBundlePool CreateTestBundlePool()

blockTree.Head.Returns(head);
//blockTree.FindLevel(0).ReturnsForAnyArgs(info);

TestBundlePool bundlePool = new(
blockTree,
Substitute.For<IBundleSimulator>(),
new ManualTimestamper(DateTimeOffset.UnixEpoch.DateTime),
new TxValidator(blockTree.ChainId),
new TestSpecProvider(London.Instance),
new MevConfig(),
LimboLogs.Instance);
config ?? new MevConfig() { TrustedRelays = $"{TestItem.AddressA},{TestItem.AddressB}" },
LimboLogs.Instance,
ecdsa ?? Substitute.For<IEthereumEcdsa>());
return bundlePool;
}
}
Expand Down
36 changes: 27 additions & 9 deletions src/Nethermind/Nethermind.Mev.Test/MevBundleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public static IEnumerable BundleTests
{
BundleTransaction BuildTransaction(PrivateKey sender, bool canRevert = false)
{
BundleTransaction tx = Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(sender).TestObject;
BundleTransaction tx = Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(sender)
.TestObject;
tx.CanRevert = canRevert;
return tx;
}
Expand All @@ -43,19 +44,36 @@ BundleTransaction BuildTransaction(PrivateKey sender, bool canRevert = false)
BundleTransaction tx2 = BuildTransaction(TestItem.PrivateKeyB);
MevBundle bundle = new(1, new[] {tx1, tx2}, UInt256.One, UInt256.One);

yield return new TestCaseData(bundle, new MevBundle(1, new[] {tx1, tx2})) {ExpectedResult = true, TestName = "timestamps don't matter"};
yield return new TestCaseData(bundle, new MevBundle(1, new[] {BuildTransaction(TestItem.PrivateKeyA, true), tx2})) {ExpectedResult = true, TestName = "reverting hashes don't matter"};
yield return new TestCaseData(bundle, new MevBundle(1, new[] {tx2, tx1})) {ExpectedResult = false, TestName = "transaction order matters"};
yield return new TestCaseData(bundle, new MevBundle(2, new[] {tx1, tx2})) {ExpectedResult = false, TestName = "block number matters"};

yield return new TestCaseData(bundle, new MevBundle(1, new[] {tx1, tx2}))
{
ExpectedResult = true, TestName = "timestamps don't matter"
};
yield return new TestCaseData(bundle,
new MevBundle(1, new[] {BuildTransaction(TestItem.PrivateKeyA, true), tx2}))
{
ExpectedResult = true, TestName = "reverting hashes don't matter"
};
yield return new TestCaseData(bundle, new MevBundle(1, new[] {tx2, tx1}))
{
ExpectedResult = false, TestName = "transaction order matters"
};
yield return new TestCaseData(bundle, new MevBundle(2, new[] {tx1, tx2}))
{
ExpectedResult = false, TestName = "block number matters"
};

BundleTransaction tx3 = BuildTransaction(TestItem.PrivateKeyC);
BundleTransaction tx4 = BuildTransaction(TestItem.PrivateKeyD);
yield return new TestCaseData(bundle, new MevBundle(2, new[] {tx3, tx4})) {ExpectedResult = false, TestName = "transactions matter"};
yield return new TestCaseData(bundle, new MevBundle(2, new[] {tx3, tx4}))
{
ExpectedResult = false, TestName = "transactions matter"
};
}
}

[TestCaseSource(nameof(BundleTests))]
public bool bundles_are_identified_by_block_number_and_transactions(MevBundle bundle1, MevBundle bundle2) => bundle1.Equals(bundle2);
public bool bundles_are_identified_by_block_number_and_transactions(MevBundle bundle1, MevBundle bundle2) =>
bundle1.Equals(bundle2);

[Test]
public void bundles_are_sequenced()
Expand Down
104 changes: 104 additions & 0 deletions src/Nethermind/Nethermind.Mev.Test/MevMegabundleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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 <http://www.gnu.org/licenses/>.
//

using System.Collections;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Test.Builders;
using Nethermind.Crypto;
using Nethermind.Int256;
using Nethermind.Logging;
using Nethermind.Mev.Data;
using NUnit.Framework;

namespace Nethermind.Mev.Test
{
public class MevMegabundleTests
{
public static IEnumerable MegabundleTests
{
get
{
BundleTransaction BuildTransaction(PrivateKey sender, bool canRevert = false)
{
BundleTransaction tx = Build.A.TypedTransaction<BundleTransaction>().SignedAndResolved(sender)
.TestObject;
tx.CanRevert = canRevert;
return tx;
}

EthereumEcdsa ecdsa = new(ChainId.Mainnet, LimboLogs.Instance);

BundleTransaction tx = BuildTransaction(TestItem.PrivateKeyB);
BundleTransaction revertingTx = BuildTransaction(TestItem.PrivateKeyA, true);
MevMegabundle megabundle = new(1, new[] {revertingTx, tx}, new[] {revertingTx.Hash!}, null, UInt256.One,
UInt256.One);
Signature relaySignature = ecdsa.Sign(TestItem.PrivateKeyA, megabundle.Hash);
megabundle.RelaySignature = relaySignature;

yield return new TestCaseData(megabundle,
new MevMegabundle(1, new[] {BuildTransaction(TestItem.PrivateKeyA), tx}, new[] {revertingTx.Hash!},
relaySignature, UInt256.One, UInt256.One))
{
ExpectedResult = true, TestName = "reverting tx don't matter"
};
yield return new TestCaseData(megabundle,
new MevMegabundle(1, new[] {revertingTx, tx}, relaySignature: relaySignature,
minTimestamp: UInt256.One, maxTimestamp: UInt256.One))
{
ExpectedResult = false, TestName = "reverting tx hashes matters"
};
yield return new TestCaseData(megabundle,
new MevMegabundle(1, new[] {revertingTx, tx}, new[] {revertingTx.Hash!}, relaySignature, UInt256.One))
{
ExpectedResult = false, TestName = "max timestamp matters"
};
yield return new TestCaseData(megabundle,
new MevMegabundle(1, new[] {revertingTx, tx}, new[] {revertingTx.Hash!}, relaySignature, maxTimestamp: UInt256.One))
{
ExpectedResult = false, TestName = "min timestamp matters"
};
yield return new TestCaseData(megabundle,
new MevMegabundle(1, new[] {revertingTx, tx}, new[] {revertingTx.Hash!}, minTimestamp: UInt256.One, maxTimestamp: UInt256.One))
{
ExpectedResult = false, TestName = "relay signature matters"
};
yield return new TestCaseData(megabundle,
new MevMegabundle(1, new[] {tx, revertingTx}, new[] {revertingTx.Hash!}, relaySignature, UInt256.One, UInt256.One))
{
ExpectedResult = false, TestName = "transaction order matters"
};
yield return new TestCaseData(megabundle,
new MevMegabundle(2, new[] {revertingTx, tx}, new[] {revertingTx.Hash!}, relaySignature, UInt256.One, UInt256.One))
{
ExpectedResult = false, TestName = "block number matters"
};

BundleTransaction tx3 = BuildTransaction(TestItem.PrivateKeyC);
BundleTransaction tx4 = BuildTransaction(TestItem.PrivateKeyD);
yield return new TestCaseData(megabundle, new MevMegabundle(2, new[] {tx3, tx4}, new[] {revertingTx.Hash!}, relaySignature, UInt256.One, UInt256.One))
{
ExpectedResult = false, TestName = "transactions matter"
};
}
}

[TestCaseSource(nameof(MegabundleTests))]
public bool megabundles_are_identified_by_block_number_and_transactions(MevMegabundle bundle1,
MevMegabundle bundle2) => bundle1.Equals(bundle2);
}
}
Loading