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

Implement shard blob transactions (EIP-4844) #4858

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ public bool MaxFeePerDataGas_should_be_set_for_blob_tx_only(TxType txType, bool
.WithMaxFeePerGas(1)
.WithMaxFeePerDataGas(isMaxFeePerDataGasSet ? 1 : null)
.WithChainId(TestBlockchainIds.ChainId)
.WithBlobVersionedHashes(1)
.WithSignature(signature).TestObject;

TxValidator txValidator = new(TestBlockchainIds.ChainId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,13 @@ bool IBlockProducer.IsProducingBlocks(ulong? maxProducingInterval)
);
header.TxRoot = new TxTrie(block.Transactions).RootHash;
block.Header.Author = _sealer.Address;

if (spec.IsEip4844Enabled)
{
header.ExcessDataGas = Evm.IntrinsicGasCalculator.CalculateExcessDataGas(parentBlock.ExcessDataGas,
block.Transactions.Sum(x => x.BlobVersionedHashes?.Length ?? 0), spec);
header.ParentExcessDataGas = parentHeader.ExcessDataGas ?? 0;
}
return block;
}

Expand Down
11 changes: 10 additions & 1 deletion src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Nethermind.Blockchain;
using Nethermind.Blockchain.Receipts;
Expand Down Expand Up @@ -239,8 +240,15 @@ protected virtual TxReceipt[] ProcessBlock(
_stateProvider.RecalculateStateRoot();

block.Header.StateRoot = _stateProvider.StateRoot;
block.Header.Hash = block.Header.CalculateHash();


if (spec.IsEip4844Enabled)
{
block.Header.ExcessDataGas = Evm.IntrinsicGasCalculator.CalculateExcessDataGas(block.Header.ParentExcessDataGas,
block.GetTransactions().Sum(x => x.BlobVersionedHashes?.Length ?? 0), spec);
}

block.Header.Hash = block.Header.CalculateHash();
return receipts;
}

Expand Down Expand Up @@ -280,6 +288,7 @@ private Block PrepareBlockForProcessing(Block suggestedBlock)
BaseFeePerGas = bh.BaseFeePerGas,
WithdrawalsRoot = bh.WithdrawalsRoot,
IsPostMerge = bh.IsPostMerge,
ParentExcessDataGas = bh.ParentExcessDataGas,
};

return suggestedBlock.CreateCopy(headerForProcessing);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,11 +567,14 @@ private ProcessingBranch PrepareProcessingBranch(Block suggestedBlock, Processin
$"To be processed (of {suggestedBlock.ToString(Block.Format.Short)}) is {toBeProcessed?.ToString(Block.Format.Short)}");
if (toBeProcessed.IsGenesis)
{
toBeProcessed.Header.ParentExcessDataGas = 0;
break;
}

branchingPoint = _blockTree.FindParentHeader(toBeProcessed.Header,
BlockTreeLookupOptions.TotalDifficultyNotNeeded);
toBeProcessed.Header.ParentExcessDataGas = branchingPoint?.ExcessDataGas.GetValueOrDefault() ?? 0;

if (branchingPoint is null)
{
// genesis block
Expand Down Expand Up @@ -658,7 +661,7 @@ private ProcessingBranch PrepareProcessingBranch(Block suggestedBlock, Processin
Keccak stateRoot = branchingPoint?.StateRoot;
if (_logger.IsTrace) _logger.Trace($"State root lookup: {stateRoot}");
blocksToBeAddedToMain.Reverse();
return new ProcessingBranch(stateRoot, blocksToBeAddedToMain);
return new ProcessingBranch(stateRoot, blocksToBeAddedToMain, branchingPoint);
}

[Todo(Improve.Refactor, "This probably can be made conditional (in DEBUG only)")]
Expand Down Expand Up @@ -715,16 +718,18 @@ public void Dispose()
[DebuggerDisplay("Root: {Root}, Length: {BlocksToProcess.Count}")]
private readonly struct ProcessingBranch
{
public ProcessingBranch(Keccak root, List<Block> blocks)
public ProcessingBranch(Keccak root, List<Block> blocks, BlockHeader parentHeader)
{
Root = root;
Blocks = blocks;
BlocksToProcess = new List<Block>();
ParentHeader = parentHeader;
}

public Keccak Root { get; }
public List<Block> Blocks { get; }
public List<Block> BlocksToProcess { get; }
public BlockHeader ParentHeader { get; }
}

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -14,6 +15,7 @@
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Evm;
using Nethermind.Evm.Tracing;
using Nethermind.Int256;
using Nethermind.Logging;
Expand Down Expand Up @@ -44,7 +46,7 @@ public abstract class BlockProducerBase : IBlockProducer
private IStateProvider StateProvider { get; }
private readonly IGasLimitCalculator _gasLimitCalculator;
private readonly IDifficultyCalculator _difficultyCalculator;
private readonly ISpecProvider _specProvider;
protected readonly ISpecProvider _specProvider;
private readonly ITxSource _txSource;
private readonly IBlockProductionTrigger _trigger;
private bool _isRunning;
Expand Down Expand Up @@ -286,7 +288,8 @@ protected virtual BlockHeader PrepareBlockHeader(BlockHeader parent,
_blocksConfig.GetExtraDataBytes())
{
Author = blockAuthor,
MixHash = payloadAttributes?.PrevRandao
MixHash = payloadAttributes?.PrevRandao,
ParentExcessDataGas = parent.ExcessDataGas.GetValueOrDefault(),
};

UInt256 difficulty = _difficultyCalculator.Calculate(header, parent);
Expand All @@ -304,10 +307,10 @@ protected virtual Block PrepareBlock(BlockHeader parent, PayloadAttributes? payl

IEnumerable<Transaction> transactions = GetTransactions(parent);

if (_specProvider.GetSpec(header).IsEip4844Enabled)
IReleaseSpec releaseSpec = _specProvider.GetSpec(header.Number, header.Timestamp);
if (releaseSpec.IsEip4844Enabled)
{
// TODO: Calculate ExcessDataGas depending on parent ExcessDataGas and number of blobs in txs
header.ExcessDataGas = 0;
header.ParentExcessDataGas = parent.ExcessDataGas ?? 0;
}

return new BlockToProduce(header, transactions, Array.Empty<BlockHeader>(), payloadAttributes?.Withdrawals);
Expand Down
14 changes: 14 additions & 0 deletions src/Nethermind/Nethermind.Consensus/Producers/TxPoolTxSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public IEnumerable<Transaction> GetTransactions(BlockHeader parent, long gasLimi

// TODO: removing transactions from TX pool here seems to be a bad practice since they will
// not come back if the block is ignored?
int blobsCounter = 0;
flcl42 marked this conversation as resolved.
Show resolved Hide resolved
foreach (Transaction tx in transactions)
{
i++;
Expand All @@ -73,6 +74,19 @@ public IEnumerable<Transaction> GetTransactions(BlockHeader parent, long gasLimi
bool success = _txFilterPipeline.Execute(tx, parent);
if (success)
{
if (tx.Type == TxType.Blob)
{
if ((blobsCounter + tx.BlobVersionedHashes.Length) > 4)
flcl42 marked this conversation as resolved.
Show resolved Hide resolved
{
if (_logger.IsTrace) _logger.Trace($"Declining {tx.ToShortString()}, no more blob space.");
continue;
}
else
{
blobsCounter += tx.BlobVersionedHashes.Length;
if (_logger.IsTrace) _logger.Trace($"Including blob tx {tx.ToShortString()}, total blobs included: {blobsCounter}.");
}
}
if (_logger.IsTrace) _logger.Trace($"Selected {tx.ToShortString()} to be potentially included in block.");

selectedTransactions++;
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Consensus/Signer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public Signature Sign(Keccak message)

public ValueTask Sign(Transaction tx)
{
Keccak hash = Keccak.Compute(Rlp.Encode(tx, true, true, _chainId).Bytes);
Keccak hash = Keccak.Compute(Rlp.EncodeForSigning(tx, true, _chainId));
tx.Signature = Sign(hash);
tx.Signature.V = tx.Signature.V + 8 + 2 * _chainId;
return default;
Expand Down
26 changes: 26 additions & 0 deletions src/Nethermind/Nethermind.Consensus/Validators/BlockValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Linq;
using Nethermind.Blockchain;
using Nethermind.Core;
using Nethermind.Core.Crypto;
Expand Down Expand Up @@ -99,6 +100,9 @@ public bool ValidateSuggestedBlock(Block block)
if (!ValidateWithdrawals(block, spec, out _))
return false;

if (!ValidateBlobs(block, spec, out _))
return false;

return true;
}

Expand Down Expand Up @@ -189,6 +193,28 @@ private bool ValidateWithdrawals(Block block, IReleaseSpec spec, out string? err
return true;
}

private bool ValidateBlobs(Block block, IReleaseSpec spec, out string? error)
{
const int maxBlobsPerBlock = 4;

if (spec.IsEip4844Enabled && block.ExcessDataGas is null)
{
error = $"ExcessDataGas cannot be null in block {block.Hash} when EIP-4844 activated.";
if (_logger.IsWarn) _logger.Warn(error);
return false;
}

if (spec.IsEip4844Enabled && block.Transactions.Sum(tr => tr?.BlobVersionedHashes?.Length) > maxBlobsPerBlock)
{
error = $"A block cannot contain more than {maxBlobsPerBlock} blobs.";
if (_logger.IsWarn) _logger.Warn(error);
return false;
}

error = null;
return true;
}

public static bool ValidateTxRootMatchesTxs(Block block, out Keccak txRoot)
{
txRoot = new TxTrie(block.Transactions).RootHash;
Expand Down
33 changes: 31 additions & 2 deletions src/Nethermind/Nethermind.Consensus/Validators/TxValidator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Linq;
using System.Numerics;
using Nethermind.Core;
using Nethermind.Core.Crypto;
Expand Down Expand Up @@ -115,8 +117,35 @@ private bool ValidateSignature(Signature? signature, IReleaseSpec spec)

private bool Validate4844Fields(Transaction transaction)
{
// TODO: Add blobs validation
return transaction.Type == TxType.Blob ^ transaction.MaxFeePerDataGas is null;
const int maxBlobsPerTransaction = 4;

if (transaction.Type != TxType.Blob)
{
return true;
}

if (transaction.MaxFeePerDataGas is null ||
transaction.BlobVersionedHashes is null ||
transaction.BlobVersionedHashes?.Length > maxBlobsPerTransaction)
{
return false;
}

if (transaction.BlobKzgs is not null)
{
Span<byte> hash = stackalloc byte[32];
for (int i = 0; i < transaction.BlobVersionedHashes!.Length; i++)
{
if (!KzgPolynomialCommitments.TryComputeCommitmentV1(transaction.BlobKzgs[i], hash) ||
!hash.SequenceEqual(transaction.BlobVersionedHashes![i]))
{
return false;
}
}
}

return KzgPolynomialCommitments.IsAggregatedProofValid(transaction.Proof, transaction.Blobs,
transaction.BlobKzgs);
}
}
}
15 changes: 15 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/AddressTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections;
using Nethermind.Blockchain;
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Specs.Forks;
using Nethermind.Core.Test.Builders;
using Nethermind.Int256;
Expand Down Expand Up @@ -175,6 +177,19 @@ public void Of_contract(long nonce, string expectedAddress)
Assert.AreEqual(address, new Address(expectedAddress));
}

[TestCaseSource(nameof(PointEvaluationPrecompileTestCases))]
public bool Is_PointEvaluationPrecompile_properly_activated(IReleaseSpec spec) =>
Address.FromNumber(0x14).IsPrecompile(spec);

public static IEnumerable PointEvaluationPrecompileTestCases
{
get
{
yield return new TestCaseData(Shanghai.Instance) { ExpectedResult = false, TestName = nameof(Shanghai) };
yield return new TestCaseData(Cancun.Instance) { ExpectedResult = true, TestName = nameof(Cancun) };
}
}

[Test]
public void There_are_no_duplicates_in_known_addresses()
{
Expand Down
6 changes: 6 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/Builders/BlockBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ public BlockBuilder WithWithdrawals(int count)
return WithWithdrawals(withdrawals);
}

public BlockBuilder WithExcessDataGas(UInt256 excessDataGas)
{
TestObjectInternal.Header.ExcessDataGas = excessDataGas;
return this;
}

public BlockBuilder WithWithdrawals(Withdrawal[]? withdrawals)
{
TestObjectInternal = TestObjectInternal
Expand Down
52 changes: 52 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/Builders/Build.Transaction.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

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
Expand All @@ -14,5 +22,49 @@ public TransactionBuilder<NamedTransaction> NamedTransaction(string name)
{
return new() { TestObjectInternal = { Name = name } };
}
public Transaction[] BunchOfTransactions(bool isSigned = true)
{
Address to = Build.An.Address.FromNumber(1).TestObject;
byte[] blobData = new byte[4096 * 32];
blobData[0] = 0xff;
blobData[1] = 0x15;

AccessList accessList = new AccessListBuilder()
.AddAddress(new Address("0x1000000000000000000000000000000000000007"))
.AddStorage(new UInt256(8))
.AddStorage(new UInt256(8 << 25))
.ToAccessList();

TransactionBuilder<Transaction> simpleEmptyTx = Build.A.Transaction;
TransactionBuilder<Transaction> simpleTx = Build.A.Transaction;

TransactionBuilder<Transaction> networkBlobTx = Build.A.Transaction.WithTo(to).WithType(TxType.Blob)
.WithChainId(1)
.WithNonce(2)
.WithGasLimit(3)
.WithMaxFeePerGas(4)
.WithGasPrice(5)
.WithMaxFeePerDataGas(6)
.WithValue(7)
.WithTo(new Address("0xffb38a7a99e3e2335be83fc74b7faa19d5531243"))
.WithAccessList(accessList)
.WithData(new byte[] { 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 })
.WithBlobVersionedHashes(new byte[][] { Bytes.FromHexString("0x018a3fa172b27f3240007eb2770906ff4f4c87bb0d2118e263a4f5ef94e4683c") })
.WithBlobs(new byte[][] { blobData })
.WithBlobKzgs(new byte[][] { Bytes.FromHexString("0xb46608161d1f715b8c838da2e4fb20e5a739b0a6bb41f27847f0535975c8b9430bf32bff9ffc91cb12480f1adb1d48a2") })
.WithProof(Bytes.FromHexString("0x88033d1744e765ab78d5fa22af022bb5d3608dcf7c0b84515897f76b1d141a2054b2772eabbb8bd8979148164959bdc1"));

IEnumerable<TransactionBuilder<Transaction>> txs = new TransactionBuilder<Transaction>[] {
simpleEmptyTx,
networkBlobTx,
};

if (isSigned)
{
txs = txs.Select(x => x.SignedAndResolved(new EthereumEcdsa(BlockchainIds.Ropsten, LimboLogs.Instance), TestItem.PrivateKeyA).WithSenderAddress(null));
}

return txs.Select(tx => tx.TestObject).ToArray();
}
}
}
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
Loading