Skip to content

Commit

Permalink
refactor txprocessor to using accessed addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
ak88 committed Aug 29, 2024
1 parent 369d510 commit 73b86bd
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public class RecoverSignatures : IBlockPreprocessorStep
private readonly IEthereumEcdsa _ecdsa;
private readonly ITxPool _txPool;
private readonly ISpecProvider _specProvider;
private readonly AuthorizationTupleDecoder _authorizationTupleDecoder = new();
private readonly ILogger _logger;

/// <summary>
Expand Down
40 changes: 23 additions & 17 deletions src/Nethermind/Nethermind.Evm.Test/CodeInfoRepositoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ public void InsertFromAuthorizations_AuthorityTupleIsCorrect_CodeIsInserted()
{
CreateAuthorizationTuple(authority, 1, TestItem.AddressB, 0),
};
CodeInsertResult result = sut.InsertFromAuthorizations(Substitute.For<IWorldState>(), tuples, Substitute.For<IReleaseSpec>());
HashSet<Address> accessedAddresses = new ();
int result = sut.InsertFromAuthorizations(Substitute.For<IWorldState>(), tuples, accessedAddresses, Substitute.For<IReleaseSpec>());

result.AccessedAddresses.Should().BeEquivalentTo([authority.Address]);
accessedAddresses.Should().BeEquivalentTo([authority.Address]);
}

public static IEnumerable<object[]> AuthorizationCases()
Expand Down Expand Up @@ -70,10 +71,10 @@ public void InsertFromAuthorizations_MixOfCorrectAndWrongChainIdAndNonce_Inserts
TrieStore trieStore = new(stateDb, LimboLogs.Instance);
IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance);
CodeInfoRepository sut = new(1);
HashSet<Address> accessedAddresses = new ();
sut.InsertFromAuthorizations(stateProvider, [tuple], accessedAddresses, Substitute.For<IReleaseSpec>());

CodeInsertResult result = sut.InsertFromAuthorizations(stateProvider, [tuple], Substitute.For<IReleaseSpec>());

Assert.That(stateProvider.HasCode(result.AccessedAddresses.First()), Is.EqualTo(shouldInsert));
Assert.That(stateProvider.HasCode(tuple.Authority), Is.EqualTo(shouldInsert));
}

[Test]
Expand All @@ -89,8 +90,9 @@ public void InsertFromAuthorizations_AuthorityHasCode_NoCodeIsInserted()
{
CreateAuthorizationTuple(authority, 1, codeSource, 0),
};
HashSet<Address> accessedAddresses = new();

sut.InsertFromAuthorizations(mockWorldState, tuples, Substitute.For<IReleaseSpec>());
sut.InsertFromAuthorizations(mockWorldState, tuples, accessedAddresses, Substitute.For<IReleaseSpec>());

mockWorldState.DidNotReceive().InsertCode(Arg.Any<Address>(), Arg.Any<ReadOnlyMemory<byte>>(), Arg.Any<IReleaseSpec>());
}
Expand All @@ -100,20 +102,23 @@ public void InsertFromAuthorizations_AuthorityHasDelegatedCode_CodeIsInserted()
{
PrivateKey authority = TestItem.PrivateKeyA;
Address codeSource = TestItem.AddressB;
IWorldState mockWorldState = Substitute.For<IWorldState>();
mockWorldState.HasCode(authority.Address).Returns(true);
IDb stateDb = new MemDb();
IDb codeDb = new MemDb();
TrieStore trieStore = new(stateDb, LimboLogs.Instance);
IWorldState stateProvider = new WorldState(trieStore, codeDb, LimboLogs.Instance);
byte[] code = new byte[23];
Eip7702Constants.DelegationHeader.CopyTo(code);
mockWorldState.GetCode(authority.Address).Returns(code);
stateProvider.CreateAccount(authority.Address, 0);
stateProvider.InsertCode(authority.Address,Keccak.Compute(code), code, Substitute.For<IReleaseSpec>());
CodeInfoRepository sut = new(1);
var tuples = new[]
{
CreateAuthorizationTuple(authority, 1, codeSource, 0),
};

sut.InsertFromAuthorizations(mockWorldState, tuples, Substitute.For<IReleaseSpec>());
sut.InsertFromAuthorizations(stateProvider, tuples, Substitute.For<ISet<Address>>(), Substitute.For<IReleaseSpec>());

mockWorldState.Received().InsertCode(authority.Address, Arg.Any<Hash256>(), Arg.Any<ReadOnlyMemory<byte>>(), Arg.Any<IReleaseSpec>(), Arg.Any<bool>());
Assert.That(stateProvider.GetCode(authority.Address).Slice(3), Is.EqualTo(codeSource.Bytes));
}

[Test]
Expand All @@ -132,13 +137,13 @@ public void InsertFromAuthorizations_AuthorityHasZeroNonce_NonceIsIncrementedByO
CreateAuthorizationTuple(authority, 1, codeSource, 0),
};

sut.InsertFromAuthorizations(stateProvider, tuples, Substitute.For<IReleaseSpec>());
sut.InsertFromAuthorizations(stateProvider, tuples, Substitute.For<ISet<Address>>(), Substitute.For<IReleaseSpec>());

Assert.That(stateProvider.GetNonce(authority.Address), Is.EqualTo((UInt256)1));
}

[Test]
public void InsertFromAuthorizations_FourAuthorizationInTotalButTwoAreInvalid_ResultContainsAllFour()
public void InsertFromAuthorizations_FourAuthorizationInTotalButOneHasInvalidNonce_ResultContainsThreeAddresses()
{
CodeInfoRepository sut = new(1);
var tuples = new[]
Expand All @@ -148,9 +153,10 @@ public void InsertFromAuthorizations_FourAuthorizationInTotalButTwoAreInvalid_Re
CreateAuthorizationTuple(TestItem.PrivateKeyC, 2, TestItem.AddressF, 0),
CreateAuthorizationTuple(TestItem.PrivateKeyD, 1, TestItem.AddressF, 1),
};
CodeInsertResult result = sut.InsertFromAuthorizations(Substitute.For<IWorldState>(), tuples, Substitute.For<IReleaseSpec>());
HashSet<Address> addresses = new();
sut.InsertFromAuthorizations(Substitute.For<IWorldState>(), tuples, addresses, Substitute.For<IReleaseSpec>());

result.AccessedAddresses.Should().BeEquivalentTo([TestItem.AddressA, TestItem.AddressB, TestItem.AddressC, TestItem.AddressD]);
addresses.Should().BeEquivalentTo([TestItem.AddressA, TestItem.AddressB, TestItem.AddressD]);
}

[Test]
Expand All @@ -168,9 +174,9 @@ public void InsertFromAuthorizations_AuthorizationsHasOneExistingAccount_ResultH
};
stateProvider.CreateAccount(TestItem.AddressA, 0);

CodeInsertResult result = sut.InsertFromAuthorizations(stateProvider, tuples, Substitute.For<IReleaseSpec>());
int refunds = sut.InsertFromAuthorizations(stateProvider, tuples, Substitute.For<ISet<Address>>(), Substitute.For<IReleaseSpec>());

result.Refunds.Should().Be(1);
refunds.Should().Be(1);
}

private static AuthorizationTuple CreateAuthorizationTuple(PrivateKey signer, ulong chainId, Address codeAddress, ulong nonce)
Expand Down
31 changes: 12 additions & 19 deletions src/Nethermind/Nethermind.Evm/CodeInfoRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ public CodeInfoRepository(ulong chainId, ConcurrentDictionary<PreBlockCaches.Pre

public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec)
{
return GetCachedCodeInfo(worldState, codeSource, vmSpec, out _);
}
public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec, out Address? delegationAddress)
{
delegationAddress = null;
if (codeSource.IsPrecompile(vmSpec))
{
return _localPrecompiles[codeSource];
Expand All @@ -118,7 +123,9 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR

if (Eip7702Constants.IsDelegatedCode(cachedCodeInfo.MachineCode.Span))
{
cachedCodeInfo = InternalGetCachedCode(worldState, ParseDelegatedAddress(cachedCodeInfo.MachineCode.Span));
Address delegatedAddress = ParseDelegatedAddress(cachedCodeInfo.MachineCode.Span);
cachedCodeInfo = InternalGetCachedCode(worldState, delegationAddress);
delegationAddress = delegatedAddress;
}

return cachedCodeInfo;
Expand Down Expand Up @@ -190,12 +197,12 @@ public void InsertCode(IWorldState state, ReadOnlyMemory<byte> code, Address cod
/// and return all authority addresses that was accessed and amount of autorization refunds.
/// eip-7702
/// </summary>
public CodeInsertResult InsertFromAuthorizations(
public int InsertFromAuthorizations(
IWorldState worldState,
AuthorizationTuple?[] authorizations,
ISet<Address> accessedAddresses,
IReleaseSpec spec)
{
HashSet<Address> accessedAddresses = new();
int refunds = 0;
//TODO optimize
foreach (AuthorizationTuple? authTuple in authorizations)
Expand All @@ -216,7 +223,7 @@ public CodeInsertResult InsertFromAuthorizations(
InsertDelegationCode(worldState, authTuple.CodeAddress, authTuple.Authority, spec);
worldState.IncrementNonce(authTuple.Authority);
}
return new CodeInsertResult(accessedAddresses, refunds);
return refunds;

void InsertDelegationCode(IWorldState state, Address codeSource, Address authority, IReleaseSpec spec)
{
Expand Down Expand Up @@ -257,7 +264,7 @@ private bool IsValidForExecution(
AuthorizationTuple authorizationTuple,
IWorldState stateProvider,
ulong chainId,
HashSet<Address> accessedAddresses,
ISet<Address> accessedAddresses,
[NotNullWhen(false)] out string? error)
{
if (authorizationTuple.Authority is null)
Expand Down Expand Up @@ -345,18 +352,4 @@ private class CachedPrecompile(
}
}
}
public readonly struct CodeInsertResult
{
public CodeInsertResult(IEnumerable<Address> accessedAddresses, int refunds)
{
AccessedAddresses = accessedAddresses;
Refunds = refunds;
}
public CodeInsertResult()
{
AccessedAddresses = Array.Empty<Address>();
}
public readonly IEnumerable<Address> AccessedAddresses;
public readonly int Refunds;
}

3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Evm/ICodeInfoRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ namespace Nethermind.Evm;
public interface ICodeInfoRepository
{
CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec);
CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec, out Address? delegationAddress);
ValueHash256 GetCodeHash(IWorldState worldState, Address address);
CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan<byte> initCode);
void InsertCode(IWorldState state, ReadOnlyMemory<byte> code, Address codeOwner, IReleaseSpec spec, bool isSystemEnv);
CodeInsertResult InsertFromAuthorizations(IWorldState worldState, AuthorizationTuple?[] authorizations, IReleaseSpec spec);
int InsertFromAuthorizations(IWorldState worldState, AuthorizationTuple?[] authorizations, ISet<Address> accessedAddresses, IReleaseSpec spec);
bool IsDelegation(IWorldState worldState, Address address, [NotNullWhen(true)] out Address? delegatedAddress);
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,19 +104,20 @@ protected virtual TransactionResult Execute(Transaction tx, in BlockExecutionCon

if (commit) WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullTxTracer.Instance, commitStorageRoots: false);

CodeInsertResult codeInsertResult = new();
HashSet<Address> accessedAddresses = new();
int delegationRefunds = 0;
if (spec.IsEip7702Enabled)
{
if (tx.HasAuthorizationList)
{
codeInsertResult = _codeInfoRepository.InsertFromAuthorizations(WorldState, tx.AuthorizationList, spec);
delegationRefunds = _codeInfoRepository.InsertFromAuthorizations(WorldState, tx.AuthorizationList, accessedAddresses, spec);
}
}

ExecutionEnvironment env = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, _codeInfoRepository);
ExecutionEnvironment env = BuildExecutionEnvironment(tx, in blCtx, spec, effectiveGasPrice, _codeInfoRepository, accessedAddresses);

long gasAvailable = tx.GasLimit - intrinsicGas;
ExecuteEvmCall(tx, header, spec, tracer, opts, codeInsertResult, gasAvailable, env, out TransactionSubstate? substate, out long spentGas, out byte statusCode);
ExecuteEvmCall(tx, header, spec, tracer, opts, delegationRefunds, accessedAddresses, gasAvailable, env, out TransactionSubstate? substate, out long spentGas, out byte statusCode);
PayFees(tx, header, spec, tracer, substate, spentGas, premiumPerGas, statusCode);

// Finalize
Expand Down Expand Up @@ -393,16 +394,23 @@ protected ExecutionEnvironment BuildExecutionEnvironment(
in BlockExecutionContext blCtx,
IReleaseSpec spec,
in UInt256 effectiveGasPrice,
ICodeInfoRepository codeInfoRepository)
ICodeInfoRepository codeInfoRepository,
HashSet<Address> accessedAddresses)
{
Address recipient = tx.GetRecipient(tx.IsContractCreation ? WorldState.GetNonce(tx.SenderAddress) : 0);
if (recipient is null) ThrowInvalidDataException("Recipient has not been resolved properly before tx execution");

TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, codeInfoRepository);
accessedAddresses.Add(recipient);
accessedAddresses.Add(tx.SenderAddress);

TxExecutionContext executionContext = new(in blCtx, tx.SenderAddress, effectiveGasPrice, tx.BlobVersionedHashes, codeInfoRepository);
Address? delegationAddress = null;
CodeInfo codeInfo = tx.IsContractCreation
? new(tx.Data ?? Memory<byte>.Empty)
: codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec);
: codeInfoRepository.GetCachedCodeInfo(WorldState, recipient, spec, out delegationAddress);

if (delegationAddress is not null)
accessedAddresses.Add(delegationAddress);

codeInfo.AnalyseInBackgroundIfRequired();

Expand All @@ -428,7 +436,8 @@ protected void ExecuteEvmCall(
IReleaseSpec spec,
ITxTracer tracer,
ExecutionOptions opts,
CodeInsertResult codeInsertResult,
int delegationRefunds,
IEnumerable<Address> accessedAddresses,
in long gasAvailable,
in ExecutionEnvironment env,
out TransactionSubstate? substate,
Expand Down Expand Up @@ -461,7 +470,7 @@ protected void ExecuteEvmCall(

using (EvmState state = new(unspentGas, env, executionType, true, snapshot, false))
{
WarmUp(tx, header, spec, env, state, codeInsertResult.AccessedAddresses);
WarmUp(tx, header, spec, state, accessedAddresses);

substate = !tracer.IsTracingActions
? VirtualMachine.Run<NotTracing>(state, WorldState, tracer)
Expand Down Expand Up @@ -522,7 +531,7 @@ protected void ExecuteEvmCall(
statusCode = StatusCode.Success;
}

spentGas = Refund(tx, header, spec, opts, substate, unspentGas, env.TxExecutionContext.GasPrice, codeInsertResult.Refunds);
spentGas = Refund(tx, header, spec, opts, substate, unspentGas, env.TxExecutionContext.GasPrice, delegationRefunds);
}
catch (Exception ex) when (ex is EvmException or OverflowException) // TODO: OverflowException? still needed? hope not
{
Expand All @@ -534,31 +543,25 @@ protected void ExecuteEvmCall(
header.GasUsed += spentGas;
}

private void WarmUp(Transaction tx, BlockHeader header, IReleaseSpec spec, ExecutionEnvironment env, EvmState state, IEnumerable<Address> authorities)
private void WarmUp(Transaction tx, BlockHeader header, IReleaseSpec spec, EvmState state, IEnumerable<Address> accessedAddresses)
{
if (spec.UseTxAccessLists)
{
state.WarmUp(tx.AccessList); // eip-2930
}

if (spec.UseHotAndColdStorage)
if (spec.UseHotAndColdStorage) // eip-2929
{
state.WarmUp(tx.SenderAddress); // eip-2929
state.WarmUp(env.ExecutingAccount); // eip-2929
foreach (Address authorized in accessedAddresses)
{
state.WarmUp(authorized);
}
}

if (spec.AddCoinbaseToTxAccessList)
{
state.WarmUp(header.GasBeneficiary!);
}

if (spec.IsEip7702Enabled)
{
foreach (Address authorized in authorities)
{
state.WarmUp(authorized);
}
}
}

protected virtual void PayFees(Transaction tx, BlockHeader header, IReleaseSpec spec, ITxTracer tracer, in TransactionSubstate substate, in long spentGas, in UInt256 premiumPerGas, in byte statusCode)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IR
_codeOverwrites.TryGetValue(codeSource, out CodeInfo result)
? result
: codeInfoRepository.GetCachedCodeInfo(worldState, codeSource, vmSpec);
public CodeInfo GetCachedCodeInfo(IWorldState worldState, Address codeSource, IReleaseSpec vmSpec, out Address? delegationAddress)
{
delegationAddress = null;
return _codeOverwrites.TryGetValue(codeSource, out CodeInfo result)
? result
: codeInfoRepository.GetCachedCodeInfo(worldState, codeSource, vmSpec);
}

public CodeInfo GetOrAdd(ValueHash256 codeHash, ReadOnlySpan<byte> initCode) => codeInfoRepository.GetOrAdd(codeHash, initCode);

Expand All @@ -47,9 +54,9 @@ public void ClearOverwrites()
_codeOverwrites.Clear();
}

public CodeInsertResult InsertFromAuthorizations(IWorldState worldState, AuthorizationTuple?[] authorizations, IReleaseSpec spec)
public int InsertFromAuthorizations(IWorldState worldState, AuthorizationTuple?[] authorizations, ISet<Address> accessedAddresses, IReleaseSpec spec)
{
return codeInfoRepository.InsertFromAuthorizations(worldState, authorizations, spec);
return codeInfoRepository.InsertFromAuthorizations(worldState, authorizations, accessedAddresses, spec);
}

public bool IsDelegation(IWorldState worldState, Address address, [NotNullWhen(true)] out Address? delegatedAddress)
Expand Down

0 comments on commit 73b86bd

Please sign in to comment.