Skip to content

Commit

Permalink
Add SSZ encoding and hashing for Blob txs
Browse files Browse the repository at this point in the history
  • Loading branch information
flcl42 committed Apr 20, 2023
1 parent 0ed97aa commit 8aef27a
Show file tree
Hide file tree
Showing 48 changed files with 1,233 additions and 371 deletions.
17 changes: 2 additions & 15 deletions src/Nethermind/Nethermind.Cli/Console/ColorfulCliConsole.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
// Copyright (c) 2022 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/>.
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Drawing;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using Nethermind.Core;
using Nethermind.Int256;

namespace Nethermind.Consensus.Transactions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using Nethermind.Core;
using Nethermind.Int256;
using Nethermind.Logging;
using Nethermind.TxPool;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Collections.Generic;
using Nethermind.Core;
using Nethermind.Int256;

namespace Nethermind.Consensus.Transactions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Linq;
using Nethermind.Core;
using Nethermind.Int256;

namespace Nethermind.Consensus.Transactions
{
Expand Down
27 changes: 13 additions & 14 deletions src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,12 @@ private bool ValidateSignature(Signature? signature, IReleaseSpec spec)

private static bool Validate4844Fields(Transaction transaction)
{
// Execution-payload version part
// Execution-payload version verification
if (transaction.Type != TxType.Blob)
{
return transaction.MaxFeePerDataGas is null &&
transaction.BlobVersionedHashes is null &&
transaction.BlobKzgs is null &&
transaction.Blobs is null &&
transaction.BlobProofs is null;
transaction is not { NetworkWrapper: ShardBlobNetworkWrapper };
}

if (transaction.MaxFeePerDataGas is null ||
Expand All @@ -130,32 +128,33 @@ transaction.BlobVersionedHashes is null ||
}
}

// And mempool version part if presents
if (transaction.BlobVersionedHashes!.Length > 0 && (transaction.Blobs is not null ||
transaction.BlobKzgs is not null ||
transaction.BlobProofs is not null))
// Mempool version verification if presents
if (transaction.NetworkWrapper is ShardBlobNetworkWrapper wrapper)
{
if (transaction.BlobKzgs is null)
int blobCount = wrapper.Blobs.Length / Ckzg.Ckzg.BytesPerBlob;
if (transaction.BlobVersionedHashes.Length != blobCount ||
(wrapper.BlobKzgs.Length / Ckzg.Ckzg.BytesPerCommitment) != blobCount ||
(wrapper.BlobProofs.Length / Ckzg.Ckzg.BytesPerProof) != blobCount)
{
return false;
}

Span<byte> hash = stackalloc byte[32];
Span<byte> commitements = transaction.BlobKzgs;
Span<byte> commitements = wrapper.BlobKzgs;
for (int i = 0, n = 0;
i < transaction.BlobVersionedHashes!.Length;
i < transaction.BlobVersionedHashes.Length;
i++, n += Ckzg.Ckzg.BytesPerCommitment)
{
if (!KzgPolynomialCommitments.TryComputeCommitmentHashV1(
commitements[n..(n + Ckzg.Ckzg.BytesPerCommitment)], hash) ||
!hash.SequenceEqual(transaction.BlobVersionedHashes![i]))
!hash.SequenceEqual(transaction.BlobVersionedHashes[i]))
{
return false;
}
}

return KzgPolynomialCommitments.AreProofsValid(transaction.Blobs!,
transaction.BlobKzgs!, transaction.BlobProofs!);
return KzgPolynomialCommitments.AreProofsValid(wrapper.Blobs,
wrapper.BlobKzgs, wrapper.BlobProofs);
}

return true;
Expand Down
29 changes: 18 additions & 11 deletions src/Nethermind/Nethermind.Core.Test/Builders/Build.Transaction.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

namespace Nethermind.Core.Test.Builders
using System.Collections.Generic;
using System.Linq;
using Nethermind.Core.Eip2930;
using Nethermind.Core.Extensions;
using Nethermind.Crypto;
using Nethermind.Int256;
using Nethermind.Logging;

namespace Nethermind.Core.Test.Builders;

public partial class Build
{
public partial class Build
{
public TransactionBuilder<Transaction> Transaction => new();
public TransactionBuilder<SystemTransaction> SystemTransaction => new();
public TransactionBuilder<GeneratedTransaction> GeneratedTransaction => new();
public TransactionBuilder<T> TypedTransaction<T>() where T : Transaction, new() => new();
public TransactionBuilder<Transaction> Transaction => new();
public TransactionBuilder<SystemTransaction> SystemTransaction => new();
public TransactionBuilder<GeneratedTransaction> GeneratedTransaction => new();
public TransactionBuilder<T> TypedTransaction<T>() where T : Transaction, new() => new();

public TransactionBuilder<NamedTransaction> NamedTransaction(string name)
{
return new() { TestObjectInternal = { Name = name } };
}
public TransactionBuilder<NamedTransaction> NamedTransaction(string name)
{
return new() { TestObjectInternal = { Name = name } };
}
}
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Core.Test/Builders/TestItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace Nethermind.Core.Test.Builders
{
public static partial class TestItem
{
public static Random Random { get; } = new();
public static Random Random { get; } = new(1337);
private static readonly AccountDecoder _accountDecoder = new();

static TestItem()
Expand Down
69 changes: 36 additions & 33 deletions src/Nethermind/Nethermind.Core.Test/Builders/TransactionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,7 @@ public TransactionBuilder<T> WithBlobVersionedHashes(int? count)
return this;
}

public TransactionBuilder<T> WithBlobs(byte[] blobs)
{
TestObjectInternal.Blobs = blobs;

return this;
}

public TransactionBuilder<T> WithShardBlobTxTypeAndFields(int blobCount)
public TransactionBuilder<T> WithShardBlobTxTypeAndFields(int blobCount, bool isMempoolTx = true)
{
if (blobCount is 0)
{
Expand All @@ -179,34 +172,44 @@ public TransactionBuilder<T> WithShardBlobTxTypeAndFields(int blobCount)

TestObjectInternal.Type = TxType.Blob;
TestObjectInternal.MaxFeePerDataGas ??= 1;
TestObjectInternal.Blobs = new byte[Ckzg.Ckzg.BytesPerBlob * blobCount];
TestObjectInternal.BlobKzgs = new byte[Ckzg.Ckzg.BytesPerCommitment * blobCount];
TestObjectInternal.BlobProofs = new byte[Ckzg.Ckzg.BytesPerProof * blobCount];
TestObjectInternal.BlobVersionedHashes = new byte[blobCount][];
for (int i = 0; i < blobCount; i++)

if (isMempoolTx)
{
TestObjectInternal.BlobVersionedHashes[i] = new byte[32];
TestObjectInternal.Blobs[Ckzg.Ckzg.BytesPerBlob * i] = 1;
KzgPolynomialCommitments.KzgifyBlob(
TestObjectInternal.Blobs.AsSpan(Ckzg.Ckzg.BytesPerBlob * i, Ckzg.Ckzg.BytesPerBlob * (i + 1)),
TestObjectInternal.BlobKzgs.AsSpan(Ckzg.Ckzg.BytesPerCommitment * i, Ckzg.Ckzg.BytesPerCommitment * (i + 1)),
TestObjectInternal.BlobProofs.AsSpan(Ckzg.Ckzg.BytesPerProof * i, Ckzg.Ckzg.BytesPerProof * (i + 1)),
TestObjectInternal.BlobVersionedHashes[i].AsSpan());
TestObjectInternal.BlobVersionedHashes = new byte[blobCount][];
ShardBlobNetworkWrapper wrapper = new(
blobs: new byte[Ckzg.Ckzg.BytesPerBlob * blobCount],
blobKzgs: new byte[Ckzg.Ckzg.BytesPerCommitment * blobCount],
blobProofs: new byte[Ckzg.Ckzg.BytesPerProof * blobCount]
);

for (int i = 0; i < blobCount; i++)
{
TestObjectInternal.BlobVersionedHashes[i] = new byte[32];
wrapper.Blobs[Ckzg.Ckzg.BytesPerBlob * i] = (byte)(i % 256);
if (KzgPolynomialCommitments.IsInitialized)
{
KzgPolynomialCommitments.KzgifyBlob(
wrapper.Blobs.AsSpan(Ckzg.Ckzg.BytesPerBlob * i, Ckzg.Ckzg.BytesPerBlob * (i + 1)),
wrapper.BlobKzgs.AsSpan(Ckzg.Ckzg.BytesPerCommitment * i,
Ckzg.Ckzg.BytesPerCommitment * (i + 1)),
wrapper.BlobProofs.AsSpan(Ckzg.Ckzg.BytesPerProof * i,
Ckzg.Ckzg.BytesPerProof * (i + 1)),
TestObjectInternal.BlobVersionedHashes[i].AsSpan());
}
else
{
wrapper.BlobKzgs[Ckzg.Ckzg.BytesPerCommitment * i] = (byte)(i % 256);
wrapper.BlobProofs[Ckzg.Ckzg.BytesPerProof * i] = (byte)(i % 256);
}
}

TestObjectInternal.NetworkWrapper = wrapper;
}
else
{
return WithBlobVersionedHashes(blobCount);
}


return this;
}

public TransactionBuilder<T> WithBlobKzgs(byte[] blobKzgs)
{
TestObjectInternal.BlobKzgs = blobKzgs;
return this;
}

public TransactionBuilder<T> WithProofs(byte[] proofs)
{
TestObjectInternal.BlobProofs = proofs;
return this;
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Test.Builders;
using Nethermind.Crypto;
using Nethermind.Logging;
using Nethermind.Serialization.Rlp;
using NUnit.Framework;

namespace Nethermind.Core.Test.Encoding;

[TestFixture]
public partial class ShardBlobTxDecoderTests
{
private readonly TxDecoder _txDecoder = new();

public static IEnumerable<(Transaction, string)> TestCaseSource() =>
TxDecoderTests.TestObjectsSource().Select(tos => (tos.Item1
.WithChainId(TestBlockchainIds.ChainId)
.WithShardBlobTxTypeAndFields(2, false)
.SignedAndResolved()
.TestObject, tos.Item2));

[TestCaseSource(nameof(TestCaseSource))]
public void Roundtrip_ExecutionPayloadForm_for_shard_blobs((Transaction Tx, string Description) testCase)
{
RlpStream rlpStream = new RlpStream(_txDecoder.GetLength(testCase.Tx));
_txDecoder.Encode(rlpStream, testCase.Tx);
rlpStream.Position = 0;
Transaction? decoded = _txDecoder.Decode(rlpStream);
decoded!.SenderAddress =
new EthereumEcdsa(TestBlockchainIds.ChainId, LimboLogs.Instance).RecoverAddress(decoded);
decoded.Hash = decoded.CalculateHash();
decoded.Should().BeEquivalentTo(testCase.Tx, testCase.Description);
}

[TestCaseSource(nameof(TestCaseSource))]
public void Roundtrip_ValueDecoderContext_ExecutionPayloadForm_for_shard_blobs((Transaction Tx, string Description) testCase)
{
RlpStream rlpStream = new(10000);
_txDecoder.Encode(rlpStream, testCase.Tx);

Span<byte> spanIncomingTxRlp = rlpStream.Data.AsSpan();
Rlp.ValueDecoderContext decoderContext = new(spanIncomingTxRlp);
rlpStream.Position = 0;
Transaction? decoded = _txDecoder.Decode(ref decoderContext);
decoded!.SenderAddress =
new EthereumEcdsa(TestBlockchainIds.ChainId, LimboLogs.Instance).RecoverAddress(decoded);
decoded.Hash = decoded.CalculateHash();
decoded.Should().BeEquivalentTo(testCase.Tx, testCase.Description);
}

[TestCaseSource(nameof(ShardBlobTxTests))]
public void NetworkWrapper_is_decoded_correctly(string rlp, Keccak signedHash, RlpBehaviors rlpBehaviors)
{
RlpStream incomingTxRlp = Bytes.FromHexString(rlp).AsRlpStream();
byte[] spanIncomingTxRlp = Bytes.FromHexString(rlp);
Rlp.ValueDecoderContext decoderContext = new(spanIncomingTxRlp.AsSpan());

Transaction? decoded = _txDecoder.Decode(incomingTxRlp, rlpBehaviors);
Transaction? decodedByValueDecoderContext = _txDecoder.Decode(ref decoderContext, rlpBehaviors);

Assert.That(decoded!.Hash, Is.EqualTo(signedHash));
Assert.That(decodedByValueDecoderContext!.Hash, Is.EqualTo(signedHash));

Rlp encoded = _txDecoder.Encode(decoded!, rlpBehaviors);
Rlp encodedWithDecodedByValueDecoderContext = _txDecoder.Encode(decodedByValueDecoderContext!, rlpBehaviors);
Assert.That(encoded.Bytes, Is.EquivalentTo(spanIncomingTxRlp));
Assert.That(encodedWithDecodedByValueDecoderContext.Bytes, Is.EquivalentTo(spanIncomingTxRlp));
}
}
Loading

0 comments on commit 8aef27a

Please sign in to comment.