Skip to content

Commit

Permalink
Hunting allocations 3 - introduce AccountStruct (#6605)
Browse files Browse the repository at this point in the history
  • Loading branch information
LukaszRozmej authored Jan 26, 2024
1 parent ed0a2fd commit 4440998
Show file tree
Hide file tree
Showing 38 changed files with 273 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public async Task Should_execute_well_formed_op_successfully_if_codehash_not_cha
chain.SendUserOperation(entryPointAddress[0], op);
if (changeCodeHash)
{
chain.State.InsertCode(walletAddress[0]!, Bytes.Concat(chain.State.GetCode(walletAddress[0]!), 0x00), chain.SpecProvider.GenesisSpec);
chain.State.InsertCode(walletAddress[0]!, Bytes.Concat(chain.State.GetCode(walletAddress[0]!)!, 0x00), chain.SpecProvider.GenesisSpec);
chain.State.Commit(chain.SpecProvider.GenesisSpec);
chain.State.RecalculateStateRoot();
chain.State.CommitTree(chain.BlockTree.Head!.Number);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ public void Should_make_sure_external_contract_extcodehashes_stays_same_after_si
.Op(Instruction.STOP)
.Done;

Hash256 initialCodeHash = TestState.GetCodeHash(externalContractAddress);
(UserOperationTxTracer tracer, _, _) = ExecuteAndTraceAccessCall(SenderRecipientAndMiner.Default, code, whitelisted);
ValueHash256 initialCodeHash = TestState.GetCodeHash(externalContractAddress);
ExecuteAndTraceAccessCall(SenderRecipientAndMiner.Default, code, whitelisted);
if (shouldMatch)
{
TestState.GetCodeHash(externalContractAddress).Should().Be(initialCodeHash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public ResultWrapper<Hash256> Simulate(UserOperation userOperation,
foreach (KeyValuePair<Address, Hash256> kv in userOperation.AddressesToCodeHashes)
{
(Address address, Hash256 expectedCodeHash) = kv;
Hash256 codeHash = _stateProvider.GetCodeHash(address);
ValueHash256 codeHash = _stateProvider.GetCodeHash(address);
if (codeHash != expectedCodeHash)
{
return ResultWrapper<Hash256>.Fail($"codehash of address {address} changed since initial simulation");
Expand Down Expand Up @@ -148,7 +148,7 @@ private UserOperationSimulationResult SimulateValidation(
IDictionary<Address, Hash256> addressesToCodeHashes = new Dictionary<Address, Hash256>();
foreach (Address accessedAddress in txTracer.AccessedAddresses)
{
addressesToCodeHashes[accessedAddress] = _stateProvider.GetCodeHash(accessedAddress);
addressesToCodeHashes[accessedAddress] = new Hash256(_stateProvider.GetCodeHash(accessedAddress));
}

return new UserOperationSimulationResult()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void transactions_are_addable_to_block_after_sealing()
Address nodeAddress = TestItem.AddressA;

UInt256 expectedNonce = 10;
stateReader.GetAccount(blockHeader.StateRoot, nodeAddress).Returns(Account.TotallyEmpty.WithChangedNonce(expectedNonce));
stateReader.GetAccount(blockHeader.StateRoot, nodeAddress).Returns(new AccountStruct(expectedNonce, UInt256.Zero));

ulong expectedTimeStamp = 100;
timestamper.UnixTime.Returns(UnixTime.FromSeconds(expectedTimeStamp));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,26 @@ public SpecificBlockReadOnlyStateProvider(IStateReader stateReader, Hash256? sta

public virtual Hash256 StateRoot { get; }

public Account GetAccount(Address address) => _stateReader.GetAccount(StateRoot, address) ?? Account.TotallyEmpty;
public AccountStruct GetAccount(Address address) => _stateReader.GetAccount(StateRoot, address) ?? default;

public bool IsContract(Address address) => GetAccount(address).IsContract;

public UInt256 GetNonce(Address address) => GetAccount(address).Nonce;

public UInt256 GetBalance(Address address) => GetAccount(address).Balance;

public Hash256? GetStorageRoot(Address address) => GetAccount(address).StorageRoot;
public ValueHash256 GetStorageRoot(Address address) => GetAccount(address).StorageRoot;

public byte[] GetCode(Address address)
public byte[]? GetCode(Address address)
{
Account account = GetAccount(address);
if (!account.HasCode)
{
return Array.Empty<byte>();
}

return _stateReader.GetCode(account.CodeHash);
AccountStruct account = GetAccount(address);
return !account.HasCode ? Array.Empty<byte>() : _stateReader.GetCode(account.CodeHash);
}

public byte[] GetCode(Hash256 codeHash) => _stateReader.GetCode(codeHash);
public byte[]? GetCode(Hash256 codeHash) => _stateReader.GetCode(codeHash);
public byte[]? GetCode(ValueHash256 codeHash) => _stateReader.GetCode(codeHash);

public Hash256 GetCodeHash(Address address)
{
Account account = GetAccount(address);
return account.CodeHash;
}
public ValueHash256 GetCodeHash(Address address) => GetAccount(address).CodeHash;

public void Accept(ITreeVisitor visitor, Hash256 stateRoot, VisitingOptions? visitingOptions)
{
Expand All @@ -59,15 +51,8 @@ public void Accept(ITreeVisitor visitor, Hash256 stateRoot, VisitingOptions? vis
public bool AccountExists(Address address) => _stateReader.GetAccount(StateRoot, address) is not null;

public bool IsEmptyAccount(Address address) => GetAccount(address).IsEmpty;
public bool HasStateForRoot(Hash256 stateRoot)
{
return _stateReader.HasStateForRoot(stateRoot);
}
public bool HasStateForRoot(Hash256 stateRoot) => _stateReader.HasStateForRoot(stateRoot);

public bool IsDeadAccount(Address address)
{
Account account = GetAccount(address);
return account.IsEmpty;
}
public bool IsDeadAccount(Address address) => GetAccount(address).IsEmpty;
}
}
41 changes: 40 additions & 1 deletion src/Nethermind/Nethermind.Core/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Nethermind.Core
{
public class Account
{
public static Account TotallyEmpty = new();
public static readonly Account TotallyEmpty = new();

private readonly Hash256? _codeHash;
private readonly Hash256? _storageRoot;
Expand Down Expand Up @@ -100,5 +100,44 @@ public Account WithChangedCodeHash(Hash256 newCodeHash)
{
return new(newCodeHash, this);
}

public AccountStruct ToStruct() => new(Nonce, Balance, StorageRoot, CodeHash);
}

public readonly struct AccountStruct
{
private readonly ValueHash256 _codeHash = Keccak.OfAnEmptyString.ValueHash256;
private readonly ValueHash256 _storageRoot = Keccak.EmptyTreeHash.ValueHash256;

public AccountStruct(in UInt256 nonce, in UInt256 balance, in ValueHash256 storageRoot, in ValueHash256 codeHash)
{
_codeHash = codeHash;
_storageRoot = storageRoot;
Nonce = nonce;
Balance = balance;
}

public AccountStruct(in UInt256 nonce, in UInt256 balance)
{
Nonce = nonce;
Balance = balance;
}

public AccountStruct(in UInt256 balance)
{
Balance = balance;
}

public bool HasCode => _codeHash != Keccak.OfAnEmptyString.ValueHash256;

public bool HasStorage => _storageRoot != Keccak.EmptyTreeHash.ValueHash256;

public UInt256 Nonce { get; }
public UInt256 Balance { get; }
public ValueHash256 StorageRoot => _storageRoot;
public ValueHash256 CodeHash => _codeHash;
public bool IsTotallyEmpty => _storageRoot == Keccak.EmptyTreeHash.ValueHash256 && IsEmpty;
public bool IsEmpty => _codeHash == Keccak.OfAnEmptyString.ValueHash256 && Balance.IsZero && Nonce.IsZero;
public bool IsContract => _codeHash != Keccak.OfAnEmptyString.ValueHash256;
}
}
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Core/Crypto/Hash256.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace Nethermind.Core.Crypto
{
[DebuggerStepThrough]
[DebuggerDisplay("{ToString()}")]
[JsonConverter(typeof(ValueHash256Converter))]
public readonly struct ValueHash256 : IEquatable<ValueHash256>, IComparable<ValueHash256>, IEquatable<Hash256>
{
private readonly Vector256<byte> _bytes;
Expand Down
29 changes: 29 additions & 0 deletions src/Nethermind/Nethermind.Core/Crypto/ValueHash256Converter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using Nethermind.Core.Crypto;

namespace Nethermind.Serialization.Json;

public class ValueHash256Converter : JsonConverter<ValueHash256>
{
public override ValueHash256 Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
byte[]? bytes = ByteArrayConverter.Convert(ref reader);
return bytes is null ? null : new ValueHash256(bytes);
}

public override void Write(
Utf8JsonWriter writer,
ValueHash256 keccak,
JsonSerializerOptions options)
{
ByteArrayConverter.Convert(writer, keccak.Bytes, skipLeadingZeros: false);
}
}
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Core/IAccountStateProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace Nethermind.Core
{
public interface IAccountStateProvider
{
Account GetAccount(Address address);
AccountStruct GetAccount(Address address);
}
}
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Evm.Test/Eip1014Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public void Test_out_of_gas_existing_account_with_storage()
TestState.Commit(Spec);
TestState.CommitTree(0);

Hash256 storageRoot = TestState.GetAccount(expectedAddress).StorageRoot;
ValueHash256 storageRoot = TestState.GetAccount(expectedAddress).StorageRoot;
storageRoot.Should().NotBe(PatriciaTree.EmptyTreeHash);

TestState.CreateAccount(TestItem.AddressC, 1.Ether());
Expand Down
9 changes: 5 additions & 4 deletions src/Nethermind/Nethermind.Evm/VirtualMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using System.Runtime.Intrinsics;
using static Nethermind.Evm.VirtualMachine;
using static System.Runtime.CompilerServices.Unsafe;
using ValueHash256 = Nethermind.Core.Crypto.ValueHash256;

#if DEBUG
using Nethermind.Evm.Tracing.Debugger;
Expand Down Expand Up @@ -533,16 +534,16 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR
}

CodeInfo cachedCodeInfo = null;
Hash256 codeHash = worldState.GetCodeHash(codeSource);
if (ReferenceEquals(codeHash, Keccak.OfAnEmptyString))
ValueHash256 codeHash = worldState.GetCodeHash(codeSource);
if (codeHash == Keccak.OfAnEmptyString.ValueHash256)
{
cachedCodeInfo = CodeInfo.Empty;
}

cachedCodeInfo ??= CodeCache.Get(codeHash);
if (cachedCodeInfo is null)
{
byte[] code = worldState.GetCode(codeHash);
byte[]? code = worldState.GetCode(codeHash);

if (code is null)
{
Expand All @@ -563,7 +564,7 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR

[DoesNotReturn]
[StackTraceHidden]
static void MissingCode(Address codeSource, Hash256 codeHash)
static void MissingCode(Address codeSource, in ValueHash256 codeHash)
{
throw new NullReferenceException($"Code {codeHash} missing in the state for address {codeSource}");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -823,9 +823,9 @@ public async Task Trace_replayBlockTransactions_stateDiff()
.WithGasLimit(50000)
.WithGasPrice(10).SignedAndResolved(TestItem.PrivateKeyA).TestObject;

Account accountA = blockchain.State.GetAccount(TestItem.AddressA);
Account accountD = blockchain.State.GetAccount(TestItem.AddressD);
Account accountF = blockchain.State.GetAccount(TestItem.AddressF);
AccountStruct accountA = blockchain.State.GetAccount(TestItem.AddressA);
AccountStruct accountD = blockchain.State.GetAccount(TestItem.AddressD);
AccountStruct accountF = blockchain.State.GetAccount(TestItem.AddressF);

string[] traceTypes = { "stateDiff" };

Expand Down
20 changes: 13 additions & 7 deletions src/Nethermind/Nethermind.JsonRpc/Data/AccountForRpc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@

namespace Nethermind.JsonRpc.Data;

public class AccountForRpc
public struct AccountForRpc
{
private Account _Account { get; set; }
private readonly Account? _account;
private readonly AccountStruct? _accountStruct;
public AccountForRpc(Account account)
{
_Account = account;
_account = account;
}

public Hash256 CodeHash => _Account.CodeHash;
public Hash256 StorageRoot => _Account.StorageRoot;
public UInt256 Balance => _Account.Balance;
public UInt256 Nonce => _Account.Nonce;
public AccountForRpc(in AccountStruct? account)
{
_accountStruct = account;
}

public ValueHash256 CodeHash => (_accountStruct?.CodeHash ?? _account?.CodeHash.ValueHash256)!.Value;
public ValueHash256 StorageRoot => (_accountStruct?.StorageRoot ?? _account?.StorageRoot.ValueHash256)!.Value;
public UInt256 Balance => (_accountStruct?.Balance ?? _account?.Balance)!.Value;
public UInt256 Nonce => (_accountStruct?.Nonce ?? _account?.Nonce)!.Value;

}
24 changes: 9 additions & 15 deletions src/Nethermind/Nethermind.JsonRpc/Modules/Eth/EthRpcModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public ResultWrapper<IEnumerable<Address>> eth_accounts()
return Task.FromResult(GetStateFailureResult<UInt256?>(header));
}

Account account = _stateReader.GetAccount(header!.StateRoot!, address);
AccountStruct? account = _stateReader.GetAccount(header!.StateRoot!, address);
return Task.FromResult(ResultWrapper<UInt256?>.Success(account?.Balance ?? UInt256.Zero));
}

Expand Down Expand Up @@ -200,7 +200,7 @@ public Task<ResultWrapper<UInt256>> eth_getTransactionCount(Address address, Blo
return Task.FromResult(GetStateFailureResult<UInt256>(header));
}

Account account = _stateReader.GetAccount(header!.StateRoot!, address);
AccountStruct? account = _stateReader.GetAccount(header!.StateRoot!, address);
UInt256 nonce = account?.Nonce ?? 0;

return Task.FromResult(ResultWrapper<UInt256>.Success(nonce));
Expand Down Expand Up @@ -257,14 +257,8 @@ public ResultWrapper<byte[]> eth_getCode(Address address, BlockParameter? blockP
return GetStateFailureResult<byte[]>(header);
}

Account account = _stateReader.GetAccount(header!.StateRoot!, address);
if (account is null)
{
return ResultWrapper<byte[]>.Success(Array.Empty<byte>());
}

byte[]? code = _stateReader.GetCode(account.CodeHash);
return ResultWrapper<byte[]>.Success(code);
AccountStruct? account = _stateReader.GetAccount(header!.StateRoot!, address);
return ResultWrapper<byte[]>.Success(account is null ? Array.Empty<byte>() : _stateReader.GetCode(account.Value.CodeHash));
}

public ResultWrapper<byte[]> eth_sign(Address addressData, byte[] message)
Expand Down Expand Up @@ -701,22 +695,22 @@ private static IEnumerable<FilterLog> GetLogs(IEnumerable<FilterLog> logs, Cance
}
}

public ResultWrapper<AccountForRpc> eth_getAccount(Address accountAddress, BlockParameter? blockParameter)
public ResultWrapper<AccountForRpc?> eth_getAccount(Address accountAddress, BlockParameter? blockParameter)
{
SearchResult<BlockHeader> searchResult = _blockFinder.SearchForHeader(blockParameter);
if (searchResult.IsError)
{
return GetFailureResult<AccountForRpc, BlockHeader>(searchResult, _ethSyncingInfo.SyncMode.HaveNotSyncedHeadersYet());
return GetFailureResult<AccountForRpc?, BlockHeader>(searchResult, _ethSyncingInfo.SyncMode.HaveNotSyncedHeadersYet());
}

BlockHeader header = searchResult.Object;
if (!HasStateForBlock(_blockchainBridge, header!))
{
return GetStateFailureResult<AccountForRpc>(header);
return GetStateFailureResult<AccountForRpc?>(header);
}

Account account = _stateReader.GetAccount(header!.StateRoot!, accountAddress);
return ResultWrapper<AccountForRpc>.Success(account is null ? null : new AccountForRpc(account));
AccountStruct? account = _stateReader.GetAccount(header!.StateRoot!, accountAddress);
return ResultWrapper<AccountForRpc?>.Success(account is null ? null : new AccountForRpc(account.Value));
}

private static ResultWrapper<TResult> GetFailureResult<TResult, TSearch>(SearchResult<TSearch> searchResult, bool isTemporary) where TSearch : class =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,6 @@ ResultWrapper<TransactionForRpc> eth_getTransactionByBlockNumberAndIndex(
ResultWrapper<AccountProof> eth_getProof([JsonRpcParameter(ExampleValue = "[\"0x7F0d15C7FAae65896648C8273B6d7E43f58Fa842\",[ \"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\" ],\"latest\"]")] Address accountAddress, UInt256[] hashRate, BlockParameter blockParameter);

[JsonRpcMethod(IsImplemented = true, Description = "Retrieves Accounts via Address and Blocknumber", IsSharable = true)]
ResultWrapper<AccountForRpc> eth_getAccount([JsonRpcParameter(ExampleValue = "[\"0xaa00000000000000000000000000000000000000\", \"latest\"]")] Address accountAddress, BlockParameter blockParameter = null);
ResultWrapper<AccountForRpc?> eth_getAccount([JsonRpcParameter(ExampleValue = "[\"0xaa00000000000000000000000000000000000000\", \"latest\"]")] Address accountAddress, BlockParameter blockParameter = null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,16 @@ private void AssertExecutionStatusChanged(IBlockFinder blockFinder, Hash256 head
private (UInt256, UInt256) AddTransactions(MergeTestBlockchain chain, ExecutionPayload executePayloadRequest,
PrivateKey from, Address to, uint count, int value, out BlockHeader parentHeader)
{
Transaction[] transactions = BuildTransactions(chain, executePayloadRequest.ParentHash, from, to, count, value, out Account accountFrom, out parentHeader);
Transaction[] transactions = BuildTransactions(chain, executePayloadRequest.ParentHash, from, to, count, value, out AccountStruct accountFrom, out parentHeader);
executePayloadRequest.SetTransactions(transactions);
UInt256 totalValue = ((int)(count * value)).GWei();
return (accountFrom.Balance - totalValue, chain.StateReader.GetBalance(parentHeader.StateRoot!, to) + totalValue);
}

private Transaction[] BuildTransactions(MergeTestBlockchain chain, Hash256 parentHash, PrivateKey from,
Address to, uint count, int value, out Account accountFrom, out BlockHeader parentHeader, int blobCountPerTx = 0)
Address to, uint count, int value, out AccountStruct accountFrom, out BlockHeader parentHeader, int blobCountPerTx = 0)
{
Transaction BuildTransaction(uint index, Account senderAccount) =>
Transaction BuildTransaction(uint index, AccountStruct senderAccount) =>
Build.A.Transaction.WithNonce(senderAccount.Nonce + index)
.WithTimestamp(Timestamper.UnixTime.Seconds)
.WithTo(to)
Expand All @@ -66,7 +66,7 @@ Transaction BuildTransaction(uint index, Account senderAccount) =>
.SignedAndResolved(from).TestObject;

parentHeader = chain.BlockTree.FindHeader(parentHash, BlockTreeLookupOptions.None)!;
Account account = chain.StateReader.GetAccount(parentHeader.StateRoot!, from.Address)!;
AccountStruct account = chain.StateReader.GetAccount(parentHeader.StateRoot!, from.Address)!.Value;
accountFrom = account;

return Enumerable.Range(0, (int)count).Select(i => BuildTransaction((uint)i, account)).ToArray();
Expand Down
Loading

0 comments on commit 4440998

Please sign in to comment.