Skip to content

Commit fca85e3

Browse files
flcl42LukaszRozmej
authored andcommitted
EIP-4844 V6 fixes and improvements (#5745)
* Workaround for reorgs * Change DATAHASH to BLOBHASH * Add blob hashes to newpayloadv3 (CL can't verify them in RLP, sends to EL) * `To` should not be empty for blobs * Fix tx type detection * Add non-zero blobs test case to strengthen blob verification test coverage * Feature/eip 4844 v6.1 (#5784) * Added ExecutionPayload._transactions to avoid multiple RLP decoding * Simplify and refactor validation * Add test * Uglify to comply --------- Co-authored-by: Alexey Osipov <me@flcl.me> --------- Co-authored-by: Lukasz Rozmej <lukasz.rozmej@gmail.com>
1 parent 73d5389 commit fca85e3

File tree

18 files changed

+299
-44
lines changed

18 files changed

+299
-44
lines changed

src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs

+31-12
Original file line numberDiff line numberDiff line change
@@ -277,25 +277,49 @@ public void Transaction_with_init_code_above_max_value_is_rejected_when_eip3860E
277277
txValidator.IsWellFormed(tx, releaseSpec).Should().Be(expectedResult);
278278
}
279279

280+
[Test]
281+
public void ShardBlobTransactions_should_have_destination_set()
282+
{
283+
TxValidator txValidator = new(TestBlockchainIds.ChainId);
284+
285+
Transaction txWithoutTo = Build.A.Transaction
286+
.WithType(TxType.Blob)
287+
.WithTimestamp(ulong.MaxValue)
288+
.WithTo(null)
289+
.WithMaxFeePerGas(1)
290+
.WithMaxFeePerDataGas(1)
291+
.WithBlobVersionedHashes(1)
292+
.WithChainId(TestBlockchainIds.ChainId)
293+
.SignedAndResolved().TestObject;
294+
295+
Transaction txtxWithTo = Build.A.Transaction
296+
.WithType(TxType.Blob)
297+
.WithTimestamp(ulong.MaxValue)
298+
.WithTo(TestItem.AddressA)
299+
.WithMaxFeePerGas(1)
300+
.WithMaxFeePerDataGas(1)
301+
.WithBlobVersionedHashes(1)
302+
.WithChainId(TestBlockchainIds.ChainId)
303+
.SignedAndResolved().TestObject;
304+
305+
Assert.That(txValidator.IsWellFormed(txWithoutTo, Cancun.Instance), Is.False);
306+
Assert.That(txValidator.IsWellFormed(txtxWithTo, Cancun.Instance));
307+
}
308+
280309
[Timeout(Timeout.MaxTestTime)]
281310
[TestCase(TxType.EIP1559, false, ExpectedResult = true)]
282311
[TestCase(TxType.EIP1559, true, ExpectedResult = false)]
283312
[TestCase(TxType.Blob, true, ExpectedResult = true)]
284313
public bool MaxFeePerDataGas_should_be_set_for_blob_tx_only(TxType txType, bool isMaxFeePerDataGasSet)
285314
{
286-
byte[] sigData = new byte[65];
287-
sigData[31] = 1; // correct r
288-
sigData[63] = 1; // correct s
289-
sigData[64] = 27; // correct v
290-
Signature signature = new(sigData);
291315
TransactionBuilder<Transaction> txBuilder = Build.A.Transaction
292316
.WithType(txType)
293317
.WithTimestamp(ulong.MaxValue)
294318
.WithMaxFeePerGas(1)
295319
.WithMaxFeePerDataGas(isMaxFeePerDataGasSet ? 1 : null)
296320
.WithBlobVersionedHashes(txType == TxType.Blob ? Eip4844Constants.MinBlobsPerTransaction : null)
297321
.WithChainId(TestBlockchainIds.ChainId)
298-
.WithSignature(signature);
322+
.SignedAndResolved();
299323

300324
Transaction tx = txBuilder.TestObject;
301325

@@ -308,19 +332,14 @@ public bool MaxFeePerDataGas_should_be_set_for_blob_tx_only(TxType txType, bool
308332
[TestCaseSource(nameof(BlobVersionedHashValidTestCases))]
309333
public bool BlobVersionedHash_should_be_correct(byte[] hash)
310334
{
311-
byte[] sigData = new byte[65];
312-
sigData[31] = 1; // correct r
313-
sigData[63] = 1; // correct s
314-
sigData[64] = 27; // correct v
315-
Signature signature = new(sigData);
316335
Transaction tx = Build.A.Transaction
317336
.WithType(TxType.Blob)
318337
.WithTimestamp(ulong.MaxValue)
319338
.WithMaxFeePerGas(1)
320339
.WithMaxFeePerDataGas(1)
321340
.WithBlobVersionedHashes(new[] { hash })
322341
.WithChainId(TestBlockchainIds.ChainId)
323-
.WithSignature(signature).TestObject;
342+
.SignedAndResolved().TestObject;
324343

325344
TxValidator txValidator = new(TestBlockchainIds.ChainId);
326345
return txValidator.IsWellFormed(tx, Cancun.Instance);

src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ transaction.BlobVersionedHashes is null &&
116116
transaction is not { NetworkWrapper: ShardBlobNetworkWrapper };
117117
}
118118

119-
if (transaction.MaxFeePerDataGas is null ||
119+
if (transaction.To is null ||
120+
transaction.MaxFeePerDataGas is null ||
120121
transaction.BlobVersionedHashes is null ||
121122
transaction.BlobVersionedHashes!.Length > Eip4844Constants.MaxBlobsPerTransaction ||
122123
transaction.BlobVersionedHashes!.Length < Eip4844Constants.MinBlobsPerTransaction)

src/Nethermind/Nethermind.Core.Test/BytesTests.cs

+6
Original file line numberDiff line numberDiff line change
@@ -359,5 +359,11 @@ public void Or(byte[] first, byte[] second, byte[] expected)
359359
first.AsSpan().Or(second);
360360
first.Should().Equal(expected);
361361
}
362+
363+
[Test]
364+
public void NullableComparision()
365+
{
366+
Bytes.NullableEqualityComparer.Equals(null, null).Should().BeTrue();
367+
}
362368
}
363369
}

src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.TestCases.cs

+7
Large diffs are not rendered by default.

src/Nethermind/Nethermind.Core/Extensions/Bytes.cs

+14
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace Nethermind.Core.Extensions
2424
public static unsafe partial class Bytes
2525
{
2626
public static readonly IEqualityComparer<byte[]> EqualityComparer = new BytesEqualityComparer();
27+
public static readonly IEqualityComparer<byte[]?> NullableEqualityComparer = new NullableBytesEqualityComparer();
2728
public static readonly ISpanEqualityComparer<byte> SpanEqualityComparer = new SpanBytesEqualityComparer();
2829
public static readonly BytesComparer Comparer = new();
2930

@@ -40,6 +41,19 @@ public override int GetHashCode(byte[] obj)
4041
}
4142
}
4243

44+
private class NullableBytesEqualityComparer : EqualityComparer<byte[]?>
45+
{
46+
public override bool Equals(byte[]? x, byte[]? y)
47+
{
48+
return AreEqual(x, y);
49+
}
50+
51+
public override int GetHashCode(byte[]? obj)
52+
{
53+
return obj?.GetSimplifiedHashCode() ?? 0;
54+
}
55+
}
56+
4357
private class SpanBytesEqualityComparer : ISpanEqualityComparer<byte>
4458
{
4559
public bool Equals(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y) => AreEqual(x, y);

src/Nethermind/Nethermind.Evm.Test/Eip4844Tests.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,25 @@ public class Eip4844Tests : VirtualMachineTestsBase
2121
[TestCase(2, 1, Description = "Should return 0 when way out of range")]
2222
[TestCase(0, 1, Description = "Should return hash, when exists")]
2323
[TestCase(1, 3, Description = "Should return hash, when exists")]
24-
public void Test_datahash_index_in_range(int index, int datahashesCount)
24+
public void Test_blobhash_index_in_range(int index, int blobhashesCount)
2525
{
26-
byte[][] hashes = new byte[datahashesCount][];
27-
for (int i = 0; i < datahashesCount; i++)
26+
byte[][] hashes = new byte[blobhashesCount][];
27+
for (int i = 0; i < blobhashesCount; i++)
2828
{
2929
hashes[i] = new byte[32];
30-
for (int n = 0; n < datahashesCount; n++)
30+
for (int n = 0; n < blobhashesCount; n++)
3131
{
3232
hashes[i][n] = (byte)((i * 3 + 10 * 7) % 256);
3333
}
3434
}
35-
byte[] expectedOutput = datahashesCount > index ? hashes[index] : new byte[32];
35+
byte[] expectedOutput = blobhashesCount > index ? hashes[index] : new byte[32];
3636

3737
// Cost of transaction call + PUSH1 x4 + MSTORE (entry cost + 1 memory cell used)
3838
const long GasCostOfCallingWrapper = GasCostOf.Transaction + GasCostOf.VeryLow * 5 + GasCostOf.Memory;
3939

4040
byte[] code = Prepare.EvmCode
4141
.PushData(new UInt256((ulong)index))
42-
.DATAHASH()
42+
.BLOBHASH()
4343
.MSTORE(0)
4444
.Return(32, 0)
4545
.Done;
@@ -48,7 +48,7 @@ public void Test_datahash_index_in_range(int index, int datahashesCount)
4848

4949
result.StatusCode.Should().Be(StatusCode.Success);
5050
result.ReturnValue.SequenceEqual(expectedOutput);
51-
AssertGas(result, GasCostOfCallingWrapper + GasCostOf.DataHash);
51+
AssertGas(result, GasCostOfCallingWrapper + GasCostOf.BlobHash);
5252
}
5353

5454
protected override TestAllTracerWithOutput CreateTracer()

src/Nethermind/Nethermind.Evm.Test/InvalidOpcodeTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public class InvalidOpcodeTests : VirtualMachineTestsBase
106106
{
107107
Instruction.TSTORE,
108108
Instruction.TLOAD,
109-
Instruction.DATAHASH,
109+
Instruction.BLOBHASH,
110110
}
111111
).ToArray();
112112

src/Nethermind/Nethermind.Evm/ByteCodeBuilderExtensions.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ public static Prepare SELFBALANCE(this Prepare @this)
8585
=> @this.Op(Instruction.SELFBALANCE);
8686
public static Prepare BASEFEE(this Prepare @this)
8787
=> @this.Op(Instruction.BASEFEE);
88-
public static Prepare DATAHASH(this Prepare @this)
89-
=> @this.Op(Instruction.DATAHASH);
88+
public static Prepare BLOBHASH(this Prepare @this)
89+
=> @this.Op(Instruction.BLOBHASH);
9090
public static Prepare POP(this Prepare @this)
9191
=> @this.Op(Instruction.POP);
9292
public static Prepare PC(this Prepare @this)

src/Nethermind/Nethermind.Evm/GasCostOf.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static class GasCostOf
3939
public const long TxDataNonZero = 68;
4040
public const long TxDataNonZeroEip2028 = 16;
4141
public const long Transaction = 21000;
42-
public const long DataHash = 3;
42+
public const long BlobHash = 3;
4343
public const long Log = 375;
4444
public const long LogTopic = 375;
4545
public const long LogData = 8;

src/Nethermind/Nethermind.Evm/Instruction.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public enum Instruction : byte
6767
CHAINID = 0x46,
6868
SELFBALANCE = 0x47,
6969
BASEFEE = 0x48,
70-
DATAHASH = 0x49,
70+
BLOBHASH = 0x49,
7171

7272
POP = 0x50,
7373
MLOAD = 0x51,

src/Nethermind/Nethermind.Evm/VirtualMachine.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1485,11 +1485,11 @@ static void UpdateCurrentState(EvmState state, int pc, long gas, int stackHead)
14851485
stack.PushUInt256(in baseFee);
14861486
break;
14871487
}
1488-
case Instruction.DATAHASH:
1488+
case Instruction.BLOBHASH:
14891489
{
14901490
if (!spec.IsEip4844Enabled) goto InvalidInstruction;
14911491

1492-
if (!UpdateGas(GasCostOf.DataHash, ref gasAvailable)) goto OutOfGas;
1492+
if (!UpdateGas(GasCostOf.BlobHash, ref gasAvailable)) goto OutOfGas;
14931493

14941494
stack.PopUInt256(out UInt256 blobIndex);
14951495

src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.HelperFunctions.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private ExecutionPayload CreateParentBlockRequestOnHead(IBlockTree blockTree)
8484
};
8585
}
8686

87-
private static ExecutionPayload CreateBlockRequest(ExecutionPayload parent, Address miner, IList<Withdrawal>? withdrawals = null, UInt256? excessDataGas = null)
87+
private static ExecutionPayload CreateBlockRequest(ExecutionPayload parent, Address miner, IList<Withdrawal>? withdrawals = null, UInt256? excessDataGas = null, Transaction[]? transactions = null)
8888
{
8989
ExecutionPayload blockRequest = new()
9090
{
@@ -101,7 +101,7 @@ private static ExecutionPayload CreateBlockRequest(ExecutionPayload parent, Addr
101101
ExcessDataGas = excessDataGas,
102102
};
103103

104-
blockRequest.SetTransactions(Array.Empty<Transaction>());
104+
blockRequest.SetTransactions(transactions ?? Array.Empty<Transaction>());
105105
TryCalculateHash(blockRequest, out Keccak? hash);
106106
blockRequest.BlockHash = hash;
107107
return blockRequest;

0 commit comments

Comments
 (0)