Skip to content

Commit

Permalink
Merge branch 'master' into feature/eip-7742
Browse files Browse the repository at this point in the history
  • Loading branch information
yerke26 committed Nov 5, 2024
2 parents d8e088c + 8bc8d8f commit 793d085
Show file tree
Hide file tree
Showing 16 changed files with 518 additions and 73 deletions.
2 changes: 1 addition & 1 deletion src/Nethermind/Ethereum.Test.Base/GeneralTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ protected EthereumTestResult RunTest(GeneralStateTest test, ITxTracer txTracer)
return testResult;
}

private static void InitializeTestState(Dictionary<Address, AccountState> preState, WorldState stateProvider, ISpecProvider specProvider)
public static void InitializeTestState(Dictionary<Address, AccountState> preState, WorldState stateProvider, ISpecProvider specProvider)
{
foreach (KeyValuePair<Address, AccountState> accountState in preState)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ void IThreadPoolWorkItem.Execute()
if (block is null) return;

Transaction[] txs = block.Transactions;
Address beneficiary = block.Header.GasBeneficiary;
Address beneficiary = block.Header.GasBeneficiary ?? Address.Zero;
Transaction lastTx = txs.Length > 0 ? txs[^1] : null;
bool isMev = false;
if (lastTx is not null && (lastTx.SenderAddress == beneficiary || _alternateMevPayees.Contains(lastTx.SenderAddress)))
Expand Down
23 changes: 23 additions & 0 deletions tools/Evm/T8n/Errors/GethErrorMappings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

namespace Evm.T8n.Errors;

public class GethErrorMappings
{
private const string WrongTransactionNonceError = "wrong transaction nonce";
private const string WrongTransactionNonceGethError = "nonce too low: address {0}, tx: {1} state: {2}";

private const string MissingTxToError = "TxMissingTo: Must be set.";
private const string MissingTxToGethError = "rlp: input string too short for common.Address, decoding into (types.Transaction)(types.BlobTx).To";

public static string GetErrorMapping(string error, params object[] arguments)
{
return error switch
{
WrongTransactionNonceError => string.Format(WrongTransactionNonceGethError, arguments),
MissingTxToError => string.Format(MissingTxToGethError),
_ => error
};
}
}
12 changes: 6 additions & 6 deletions tools/Evm/T8n/JsonTypes/EnvJson.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

namespace Evm.T8n.JsonTypes;

public class EnvJson
public class EnvJson(Address currentCoinbase, long currentGasLimit, long currentNumber, ulong currentTimestamp)
{
public Address? CurrentCoinbase { get; set; }
public long CurrentGasLimit { get; set; }
public long CurrentNumber { get; set; }
public ulong CurrentTimestamp { get; set; }
public Withdrawal[]? Withdrawals { get; set; }
public Address CurrentCoinbase { get; set; } = currentCoinbase;
public long CurrentGasLimit { get; set; } = currentGasLimit;
public long CurrentNumber { get; set; } = currentNumber;
public ulong CurrentTimestamp { get; set; } = currentTimestamp;

public Withdrawal[]? Withdrawals { get; set; }
public UInt256? CurrentRandom { get; set; }
public ulong ParentTimestamp { get; set; }
public UInt256? ParentDifficulty { get; set; }
Expand Down
7 changes: 6 additions & 1 deletion tools/Evm/T8n/JsonTypes/InputData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class InputData
public TransactionMetaData[]? TransactionMetaDataList { get; set; }
public string? TxRlp { get; set; }

public Transaction[] GetTransactions(TxDecoder decoder)
public Transaction[] GetTransactions(TxDecoder decoder, ulong chainId)
{
List<Transaction> transactions = [];
if (TxRlp is not null)
Expand All @@ -29,14 +29,19 @@ public Transaction[] GetTransactions(TxDecoder decoder)
}
else if (Txs is not null && TransactionMetaDataList is not null)
{
var ecdsa = new EthereumEcdsa(chainId);

for (int i = 0; i < Txs.Length; i++)
{
var transaction = Txs[i].ToTransaction();
transaction.SenderAddress = null; // t8n does not accept SenderAddress from input, so need to reset senderAddress

SignTransaction(transaction, TransactionMetaDataList[i], (LegacyTransactionForRpc) Txs[i]);

transaction.ChainId ??= chainId;
transaction.SenderAddress ??= ecdsa.RecoverAddress(transaction);
transaction.Hash = transaction.CalculateHash();

transactions.Add(transaction);
}
}
Expand Down
6 changes: 1 addition & 5 deletions tools/Evm/T8n/JsonTypes/Ommer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,4 @@

namespace Evm.T8n.JsonTypes;

public class Ommer(int delta, Address address)
{
public int Delta { get; set; } = delta;
public Address Address { get; set; } = address;
}
public readonly record struct Ommer(int Delta, Address Address);
6 changes: 6 additions & 0 deletions tools/Evm/T8n/JsonTypes/RejectedTx.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

namespace Evm.T8n.JsonTypes;

public readonly record struct RejectedTx(int Index, string Error);
129 changes: 129 additions & 0 deletions tools/Evm/T8n/JsonTypes/T8nResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System.Text.Json.Serialization;
using Ethereum.Test.Base;
using Nethermind.Core;
using Nethermind.Core.Collections;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Evm;
using Nethermind.Evm.Tracing;
using Nethermind.Int256;
using Nethermind.Serialization.Rlp;
using Nethermind.State;
using Nethermind.State.Proofs;

namespace Evm.T8n.JsonTypes;

public class T8nResult
{
public Hash256? StateRoot { get; set; }
public Hash256? TxRoot { get; set; }
public Hash256? ReceiptsRoot { get; set; }
public Hash256? WithdrawalsRoot { get; set; }
public Hash256? LogsHash { get; set; }
public Bloom? LogsBloom { get; set; }
public TxReceipt[]? Receipts { get; set; }
public RejectedTx[]? Rejected { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public UInt256? CurrentDifficulty { get; set; }
public UInt256? GasUsed { get; set; }
public UInt256? CurrentBaseFee { get; set; }
public UInt256? CurrentExcessBlobGas { get; set; }
public UInt256? BlobGasUsed { get; set; }
public Dictionary<Address, AccountState> Accounts { get; set; } = [];
public byte[] TransactionsRlp { get; set; } = [];


public static T8nResult ConstructT8nResult(WorldState stateProvider,
Block block,
T8nTest test,
StorageTxTracer storageTracer,
BlockReceiptsTracer blockReceiptsTracer,
ISpecProvider specProvider,
TransactionExecutionReport txReport)
{
IReceiptSpec receiptSpec = specProvider.GetSpec(block.Header);
Hash256 txRoot = TxTrie.CalculateRoot(txReport.SuccessfulTransactions.ToArray());
Hash256 receiptsRoot = ReceiptTrie<TxReceipt>.CalculateRoot(receiptSpec,
txReport.SuccessfulTransactionReceipts.ToArray(), new ReceiptMessageDecoder());
LogEntry[] logEntries = txReport.SuccessfulTransactionReceipts
.SelectMany(receipt => receipt.Logs ?? Enumerable.Empty<LogEntry>())
.ToArray();
var bloom = new Bloom(logEntries);
var gasUsed = blockReceiptsTracer.TxReceipts.Count == 0 ? 0 : (ulong)blockReceiptsTracer.LastReceipt.GasUsedTotal;
ulong? blobGasUsed = test.Spec.IsEip4844Enabled ? BlobGasCalculator.CalculateBlobGas(txReport.ValidTransactions.ToArray()) : null;

T8nResult t8NResult = new()
{
StateRoot = stateProvider.StateRoot,
TxRoot = txRoot,
ReceiptsRoot = receiptsRoot,
LogsBloom = bloom,
LogsHash = Keccak.Compute(Rlp.OfEmptySequence.Bytes),
Receipts = txReport.SuccessfulTransactionReceipts.ToArray(),
Rejected = txReport.RejectedTransactionReceipts.Count == 0
? null
: txReport.RejectedTransactionReceipts.ToArray(),
CurrentDifficulty = test.CurrentDifficulty,
GasUsed = new UInt256(gasUsed),
CurrentBaseFee = test.CurrentBaseFee,
WithdrawalsRoot = block.WithdrawalsRoot,
CurrentExcessBlobGas = block.ExcessBlobGas,
BlobGasUsed = blobGasUsed,
TransactionsRlp = Rlp.Encode(txReport.SuccessfulTransactions.ToArray()).Bytes,
Accounts = CollectAccounts(test, stateProvider, storageTracer, block),
};

return t8NResult;
}

private static Dictionary<Address, AccountState> CollectAccounts(T8nTest test, WorldState stateProvider, StorageTxTracer storageTracer, Block block)
{
Dictionary<Address, AccountState?> accounts = test.Alloc.Keys.ToDictionary(address => address,
address => GetAccountState(address, stateProvider, storageTracer));

accounts.AddRange(test.Ommers.ToDictionary(ommer => ommer.Address,
ommer => GetAccountState(ommer.Address, stateProvider, storageTracer)));

if (block.Beneficiary is not null)
{
accounts[block.Beneficiary] = GetAccountState(block.Beneficiary, stateProvider, storageTracer);
}

foreach (Transaction tx in test.Transactions)
{
if (tx.To is not null && !accounts.ContainsKey(tx.To))
{
accounts[tx.To] = GetAccountState(tx.To, stateProvider, storageTracer);
}
if (tx.SenderAddress is not null && !accounts.ContainsKey(tx.SenderAddress))
{
accounts[tx.SenderAddress] = GetAccountState(tx.SenderAddress, stateProvider, storageTracer);
}
}

return accounts
.Where(addressAndAccount => addressAndAccount.Value is not null)
.ToDictionary(addressAndAccount => addressAndAccount.Key, addressAndAccount => addressAndAccount.Value!);
}

private static AccountState? GetAccountState(Address address, WorldState stateProvider, StorageTxTracer storageTxTracer)
{
if (!stateProvider.AccountExists(address)) return null;

Account account = stateProvider.GetAccount(address);
var code = stateProvider.GetCode(address);
var accountState = new AccountState
{
Nonce = account.Nonce,
Balance = account.Balance,
Code = code
};

accountState.Storage = storageTxTracer.GetStorage(address) ?? [];

return accountState;
}
}
49 changes: 0 additions & 49 deletions tools/Evm/T8n/JsonTypes/T8nTest.cs

This file was deleted.

15 changes: 15 additions & 0 deletions tools/Evm/T8n/JsonTypes/TransactionExecutionReport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;

namespace Evm.T8n.JsonTypes;


public class TransactionExecutionReport
{
public List<RejectedTx> RejectedTransactionReceipts { get; set; } = [];
public List<Transaction> ValidTransactions { get; set; } = [];
public List<Transaction> SuccessfulTransactions { get; set; } = [];
public List<TxReceipt> SuccessfulTransactionReceipts { get; set; } = [];
}
6 changes: 1 addition & 5 deletions tools/Evm/T8n/JsonTypes/TransactionMetaData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,4 @@

namespace Evm.T8n.JsonTypes;

public class TransactionMetaData
{
public bool? Protected { get; set; }
public byte[]? SecretKey { get; set; }
}
public readonly record struct TransactionMetaData(bool? Protected, byte[]? SecretKey);
42 changes: 42 additions & 0 deletions tools/Evm/T8n/StorageTxTracer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Nethermind.Core;
using Nethermind.Evm.Tracing;
using Nethermind.Int256;

namespace Evm.T8n;

public class StorageTxTracer : TxTracer, IBlockTracer
{
private readonly Dictionary<Address, Dictionary<UInt256, byte[]>> _storages = new();
public bool IsTracingRewards => false;
public override bool IsTracingOpLevelStorage => true;

public override void SetOperationStorage(Address address, UInt256 storageIndex, ReadOnlySpan<byte> newValue,
ReadOnlySpan<byte> currentValue)
{
if (!_storages.TryGetValue(address, out _))
{
_storages[address] = [];
}

_storages[address][storageIndex] = newValue.ToArray();
}

public Dictionary<UInt256, byte[]>? GetStorage(Address address)
{
_storages.TryGetValue(address, out Dictionary<UInt256, byte[]>? storage);
return storage;
}

public void ReportReward(Address author, string rewardType, UInt256 rewardValue) { }

public void StartNewBlockTrace(Block block) { }

public ITxTracer StartNewTxTrace(Transaction? tx) => this;

public void EndTxTrace() { }

public void EndBlockTrace() { }
}
33 changes: 33 additions & 0 deletions tools/Evm/T8n/T8nBlockHashProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using Evm.T8n.Errors;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Evm;

namespace Evm.T8n;

public class T8nBlockHashProvider : IBlockhashProvider
{
private readonly Dictionary<long, Hash256?> _blockHashes = new();
private static readonly int _maxDepth = 256;

public Hash256? GetBlockhash(BlockHeader currentBlock, in long number)
{
long current = currentBlock.Number;
if (number >= current || number < current - Math.Min(current, _maxDepth))
{
return null;
}

return _blockHashes.GetValueOrDefault(number, null) ??
throw new T8nException($"BlockHash for block {number} not provided",
T8nErrorCodes.ErrorMissingBlockhash);
}

public void Insert(Hash256 blockHash, long number)
{
_blockHashes[number] = blockHash;
}
}
Loading

0 comments on commit 793d085

Please sign in to comment.