Skip to content
Merged
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 @@ -20,6 +20,7 @@
import org.hyperledger.besu.datatypes.CodeDelegation;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.block.access.list.AccessLocationTracker;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.CodeDelegationService;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
Expand Down Expand Up @@ -119,32 +120,43 @@ private void processCodeDelegation(

LOG.trace("Set code delegation for authority: {}", authorizer.get());

final Optional<MutableAccount> maybeAuthorityAccount =
Optional.ofNullable(worldUpdater.getAccount(authorizer.get()));
// Use read-only get() to avoid marking the account as touched during validation.
// getAccount() would mark it as touched, causing empty accounts to be incorrectly
// deleted by clearAccountsThatAreEmpty() even when authorization is invalid/skipped.
final Optional<Account> maybeExistingAccount =
Optional.ofNullable(worldUpdater.get(authorizer.get()));
eip7928AccessList.ifPresent(t -> t.addTouchedAccount(authorizer.get()));
result.addAccessedDelegatorAddress(authorizer.get());

MutableAccount authority;
boolean authorityDoesAlreadyExist = false;
if (maybeAuthorityAccount.isEmpty()) {
if (maybeExistingAccount.isEmpty()) {
// only create an account if nonce is valid
if (codeDelegation.nonce() != 0) {
return;
}
authority = worldUpdater.createAccount(authorizer.get());
eip7928AccessList.ifPresent(t -> t.addTouchedAccount(authority.getAddress()));
} else {
authority = maybeAuthorityAccount.get();
eip7928AccessList.ifPresent(t -> t.addTouchedAccount(authority.getAddress()));
if (!codeDelegationService.canSetCodeDelegation(maybeExistingAccount.get())) {
return;
}

if (!codeDelegationService.canSetCodeDelegation(authority)) {
if (codeDelegation.nonce() != maybeExistingAccount.get().getNonce()) {
LOG.trace(
"Invalid nonce for code delegation. Expected: {}, Actual: {}",
maybeExistingAccount.get().getNonce(),
codeDelegation.nonce());
return;
}

// Validation passed — now get the mutable account for mutation
authority = worldUpdater.getAccount(authorizer.get());
eip7928AccessList.ifPresent(t -> t.addTouchedAccount(authority.getAddress()));
authorityDoesAlreadyExist = true;
}

if (codeDelegation.nonce() != authority.getNonce()) {
if (!authorityDoesAlreadyExist && codeDelegation.nonce() != authority.getNonce()) {
LOG.trace(
"Invalid nonce for code delegation. Expected: {}, Actual: {}",
authority.getNonce(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ void shouldProcessValidDelegationForNewAccount() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 0L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(null);
when(worldUpdater.get(any())).thenReturn(null);
when(worldUpdater.createAccount(any())).thenReturn(authority);
when(authority.getNonce()).thenReturn(0L);

Expand All @@ -117,7 +117,7 @@ void shouldNotCreateAccountIfNonceIsInvalid() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 1L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(null);
when(worldUpdater.get(any())).thenReturn(null);

// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction, Optional.empty());
Expand All @@ -133,6 +133,7 @@ void shouldProcessValidDelegationForExistingAccount() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 1L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.get(any())).thenReturn(authority);
when(worldUpdater.getAccount(any())).thenReturn(authority);
when(authority.getNonce()).thenReturn(1L);
when(codeDelegationService.canSetCodeDelegation(any())).thenReturn(true);
Expand All @@ -152,14 +153,16 @@ void shouldRejectDelegationWithInvalidNonce() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 2L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(authority);
when(worldUpdater.get(any())).thenReturn(authority);
when(authority.getNonce()).thenReturn(1L);
when(codeDelegationService.canSetCodeDelegation(any())).thenReturn(true);

// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction, Optional.empty());

// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(worldUpdater, never()).getAccount(any());
verify(authority, never()).incrementNonce();
verify(codeDelegationService, never()).processCodeDelegation(any(), any());
}
Expand Down Expand Up @@ -194,7 +197,7 @@ void shouldSkipOverInvalidMultipleInvalidNonceDelegationsForSameAuthorityForNewA
when(transaction.getCodeDelegationList())
.thenReturn(Optional.of(List.of(cd1_invalid, cd2_valid, cd3_invalid)));

when(worldUpdater.getAccount(any())).thenReturn(null).thenReturn(null).thenReturn(authority);
when(worldUpdater.get(any())).thenReturn(null).thenReturn(null).thenReturn(authority);
when(worldUpdater.createAccount(any())).thenReturn(authority);
when(authority.getNonce()).thenReturn(0L).thenReturn(1L);
when(codeDelegationService.canSetCodeDelegation(any())).thenReturn(true);
Expand Down Expand Up @@ -267,14 +270,15 @@ void shouldRejectDelegationWhenCannotSetCodeDelegation() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 1L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(authority);
when(worldUpdater.get(any())).thenReturn(authority);
when(codeDelegationService.canSetCodeDelegation(any())).thenReturn(false);

// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction, Optional.empty());

// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(worldUpdater, never()).getAccount(any());
verify(authority, never()).incrementNonce();
verify(codeDelegationService, never()).processCodeDelegation(any(), any());
}
Expand Down