Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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 @@ -112,6 +112,7 @@
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
Expand Down Expand Up @@ -139,8 +140,7 @@ public abstract class MainnetProtocolSpecs {
// A consensus bug at Ethereum mainnet transaction 0xcf416c53
// deleted an empty account even when the message execution scope
// failed, but the transaction itself succeeded.
private static final Set<Address> SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES =
Set.of(RIPEMD160_PRECOMPILE);
private static final HashSet<Address> SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES;

private static final Wei FRONTIER_BLOCK_REWARD = Wei.fromEth(5);

Expand All @@ -151,6 +151,11 @@ public abstract class MainnetProtocolSpecs {
private static final Logger LOG = LoggerFactory.getLogger(MainnetProtocolSpecs.class);
private static final int POW_SLOT_TIME_ESTIMATION = 13;

static {
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES = new HashSet<>();
SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES.add(RIPEMD160_PRECOMPILE);
}

private MainnetProtocolSpecs() {}

public static ProtocolSpecBuilder frontierDefinition(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void setUp() {
originalRegistry = new PrecompileContractRegistry();
originalRegistry.put(ORIGINAL_ADDRESS_1, originalPrecompiledContract);
originalRegistry.put(ORIGINAL_ADDRESS_2, originalPrecompiledContract);
originalProcessor = new MessageCallProcessor(null, originalRegistry, null);
originalProcessor = new MessageCallProcessor(null, originalRegistry);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ private ContractCreationProcessor thisContractCreationProcessor() {
evmSpec.getEvm(),
evmSpec.isRequireDeposit(),
evmSpec.getContractValidationRules(),
evmSpec.getInitialNonce(),
evmSpec.getForceCommitAddresses()));
evmSpec.getInitialNonce()));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1631,11 +1631,13 @@ public MessageFrame build() {
TxValues newTxValues;

if (parentMessageFrame == null) {
HashSet<Address> warmedUpAddresses = new HashSet<>();
warmedUpAddresses.add(contract);
newTxValues =
new TxValues(
blockHashLookup,
maxStackSize,
UndoSet.of(new HashSet<>(Address.SIZE)),
UndoSet.of(warmedUpAddresses),
UndoTable.of(HashBasedTable.create()),
originator,
gasPrice,
Expand All @@ -1645,8 +1647,8 @@ public MessageFrame build() {
miningBeneficiary,
versionedHashes,
UndoTable.of(HashBasedTable.create()),
UndoSet.of(new HashSet<>(Address.SIZE)),
UndoSet.of(new HashSet<>(Address.SIZE)),
UndoSet.of(new HashSet<>()),
UndoSet.of(new HashSet<>()),
new UndoScalar<>(0L));
updater = worldUpdater;
newStatic = isStatic;
Expand Down Expand Up @@ -1677,7 +1679,6 @@ public MessageFrame build() {
eip7928AccessList);
newTxValues.messageFrameStack().addFirst(messageFrame);
messageFrame.warmUpAddress(sender);
messageFrame.warmUpAddress(contract);
for (Address a : eip2930AccessListWarmAddresses) {
messageFrame.warmUpAddress(a);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,15 @@ public void complete(final MessageFrame frame, final MessageFrame childFrame) {
}

frame.setReturnData(outputData);
frame.addLogs(childFrame.getLogs());
frame.addSelfDestructs(childFrame.getSelfDestructs());
frame.addCreates(childFrame.getCreates());
if (!childFrame.getLogs().isEmpty()) {
frame.addLogs(childFrame.getLogs());
}
if (!childFrame.getSelfDestructs().isEmpty()) {
frame.addSelfDestructs(childFrame.getSelfDestructs());
}
if (!childFrame.getCreates().isEmpty()) {
frame.addCreates(childFrame.getCreates());
}

final long gasRemaining = childFrame.getRemainingGas();
frame.incrementRemainingGas(gasRemaining);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.ModificationNotAllowedException;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.AccountState;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.tracing.OperationTracer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.Set;

import org.apache.tuweni.bytes.Bytes;

Expand Down Expand Up @@ -69,7 +67,7 @@ public abstract class AbstractMessageProcessor {

// List of addresses to force delete when they are touched but empty
// when the state changes in the message are were not meant to be committed.
private final Collection<? super Address> forceDeleteAccountsWhenEmpty;
private final Set<? super Address> forceDeleteAccountsWhenEmpty;
final EVM evm;

/**
Expand All @@ -78,7 +76,7 @@ public abstract class AbstractMessageProcessor {
* @param evm the evm
* @param forceDeleteAccountsWhenEmpty the force delete accounts when empty
*/
AbstractMessageProcessor(final EVM evm, final Collection<Address> forceDeleteAccountsWhenEmpty) {
AbstractMessageProcessor(final EVM evm, final Set<Address> forceDeleteAccountsWhenEmpty) {
this.evm = evm;
this.forceDeleteAccountsWhenEmpty = forceDeleteAccountsWhenEmpty;
}
Expand All @@ -100,19 +98,35 @@ public abstract class AbstractMessageProcessor {
protected abstract void codeSuccess(MessageFrame frame, final OperationTracer operationTracer);

private void clearAccumulatedStateBesidesGasAndOutput(final MessageFrame frame) {
ArrayList<Address> addresses =
frame.getWorldUpdater().getTouchedAccounts().stream()
.filter(AccountState::isEmpty)
.map(Account::getAddress)
.filter(forceDeleteAccountsWhenEmpty::contains)
.collect(Collectors.toCollection(ArrayList::new));

// Clear any pending changes.
frame.getWorldUpdater().revert();

// Force delete any requested accounts and commit the changes.
((Collection<Address>) addresses).forEach(h -> frame.getWorldUpdater().deleteAccount(h));
frame.getWorldUpdater().commit();
final var worldUpdater = frame.getWorldUpdater();
final var touchedAccounts = worldUpdater.getTouchedAccounts();

if (touchedAccounts.isEmpty() || forceDeleteAccountsWhenEmpty.isEmpty()) {
// Fast path: no touched accounts or no force-delete targets.
// Just revert and commit without the stream pipeline overhead.
worldUpdater.revert();
worldUpdater.commit();
} else {
// Full path: find empty accounts that need force-deletion
ArrayList<Address> addresses = new ArrayList<>();
for (final Account account : touchedAccounts) {
if (account.isEmpty()) {
Address address = account.getAddress();
if (forceDeleteAccountsWhenEmpty.contains(address)) {
addresses.add(address);
}
}
}

// Clear any pending changes.
worldUpdater.revert();

// Force delete any requested accounts and commit the changes.
for (final Address address : addresses) {
worldUpdater.deleteAccount(address);
}
worldUpdater.commit();
}

frame.clearLogs();
frame.clearGasRefund();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import org.hyperledger.besu.evm.log.TransferLogEmitter;
import org.hyperledger.besu.evm.tracing.OperationTracer;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -62,7 +62,7 @@ public ContractCreationProcessor(
final boolean requireCodeDepositToSucceed,
final List<ContractValidationRule> contractValidationRules,
final long initialContractNonce,
final Collection<Address> forceCommitAddresses) {
final Set<Address> forceCommitAddresses) {
this(
evm,
requireCodeDepositToSucceed,
Expand Down Expand Up @@ -90,7 +90,7 @@ public ContractCreationProcessor(
requireCodeDepositToSucceed,
contractValidationRules,
initialContractNonce,
Set.of(),
Collections.emptySet(),
TransferLogEmitter.NOOP);
}

Expand All @@ -109,7 +109,7 @@ public ContractCreationProcessor(
final boolean requireCodeDepositToSucceed,
final List<ContractValidationRule> contractValidationRules,
final long initialContractNonce,
final Collection<Address> forceCommitAddresses,
final Set<Address> forceCommitAddresses,
final TransferLogEmitter transferLogEmitter) {
super(evm, forceCommitAddresses);
this.requireCodeDepositToSucceed = requireCodeDepositToSucceed;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.hyperledger.besu.evm.precompile.PrecompiledContract;
import org.hyperledger.besu.evm.tracing.OperationTracer;

import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -56,7 +56,7 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
public MessageCallProcessor(
final EVM evm,
final PrecompileContractRegistry precompiles,
final Collection<Address> forceCommitAddresses) {
final Set<Address> forceCommitAddresses) {
this(evm, precompiles, forceCommitAddresses, TransferLogEmitter.NOOP);
}

Expand All @@ -67,7 +67,7 @@ public MessageCallProcessor(
* @param precompiles the precompiles
*/
public MessageCallProcessor(final EVM evm, final PrecompileContractRegistry precompiles) {
this(evm, precompiles, Set.of(), TransferLogEmitter.NOOP);
this(evm, precompiles, Collections.emptySet(), TransferLogEmitter.NOOP);
}

/**
Expand All @@ -81,7 +81,7 @@ public MessageCallProcessor(final EVM evm, final PrecompileContractRegistry prec
public MessageCallProcessor(
final EVM evm,
final PrecompileContractRegistry precompiles,
final Collection<Address> forceCommitAddresses,
final Set<Address> forceCommitAddresses,
final TransferLogEmitter transferLogEmitter) {
super(evm, forceCommitAddresses);
this.precompiles = precompiles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

/**
* The Stacked updater.
Expand Down Expand Up @@ -59,8 +60,12 @@ protected UpdateTrackingAccount<A> getForMutation(final Address address) {
return account == null ? null : new UpdateTrackingAccount<>(account);
}

@SuppressWarnings("MixedMutabilityReturnType")
@Override
public Collection<? extends Account> getTouchedAccounts() {
if (getUpdatedAccounts().isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<>(getUpdatedAccounts());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ private void executeOperation(final Bytes contract, final EVM evm) {

operation.execute(messageFrame, evm);
final MessageFrame createFrame = messageFrameStack.peek();
final ContractCreationProcessor ccp =
new ContractCreationProcessor(evm, false, List.of(), 0, List.of());
final ContractCreationProcessor ccp = new ContractCreationProcessor(evm, false, List.of(), 0);
ccp.process(createFrame, OperationTracer.NO_TRACING);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,7 @@ void shanghaiMaxInitCodeSizeCreate() {
final EVM myEVM = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, myEVM);
final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek();
final ContractCreationProcessor ccp =
new ContractCreationProcessor(myEVM, false, List.of(), 0, List.of());
final ContractCreationProcessor ccp = new ContractCreationProcessor(myEVM, false, List.of(), 0);
ccp.process(createFrame, OperationTracer.NO_TRACING);

final Log log = createFrame.getLogs().get(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,7 @@ void createFromMemoryMutationSafe() {
final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT);
operation.execute(messageFrame, evm);
final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek();
final ContractCreationProcessor ccp =
new ContractCreationProcessor(evm, false, List.of(), 0, List.of());
final ContractCreationProcessor ccp = new ContractCreationProcessor(evm, false, List.of(), 0);
ccp.process(createFrame, OperationTracer.NO_TRACING);

final Log log = createFrame.getLogs().get(0);
Expand Down Expand Up @@ -183,8 +182,7 @@ void shanghaiMaxInitCodeSizeCreate() {
final EVM evm = MainnetEVMs.shanghai(DEV_NET_CHAIN_ID, EvmConfiguration.DEFAULT);
var result = operation.execute(messageFrame, evm);
final MessageFrame createFrame = messageFrame.getMessageFrameStack().peek();
final ContractCreationProcessor ccp =
new ContractCreationProcessor(evm, false, List.of(), 0, List.of());
final ContractCreationProcessor ccp = new ContractCreationProcessor(evm, false, List.of(), 0);
ccp.process(createFrame, OperationTracer.NO_TRACING);

final Log log = createFrame.getLogs().get(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ class ContractCreationProcessorTest
@Test
void shouldThrowAnExceptionWhenCodeContractFormatInvalidPreEOF() {
processor =
new ContractCreationProcessor(
evm, true, Collections.singletonList(PrefixCodeRule.of()), 1, Collections.emptyList());
new ContractCreationProcessor(evm, true, Collections.singletonList(PrefixCodeRule.of()), 1);
final Bytes contractCode = Bytes.fromHexString("EF01010101010101");
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
Expand All @@ -65,8 +64,7 @@ void shouldThrowAnExceptionWhenCodeContractFormatInvalidPreEOF() {
@Test
void shouldNotThrowAnExceptionWhenCodeContractIsValid() {
processor =
new ContractCreationProcessor(
evm, true, Collections.singletonList(PrefixCodeRule.of()), 1, Collections.emptyList());
new ContractCreationProcessor(evm, true, Collections.singletonList(PrefixCodeRule.of()), 1);
final Bytes contractCode = Bytes.fromHexString("0101010101010101");
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
Expand All @@ -78,9 +76,7 @@ void shouldNotThrowAnExceptionWhenCodeContractIsValid() {

@Test
void shouldNotThrowAnExceptionWhenPrefixCodeRuleNotAdded() {
processor =
new ContractCreationProcessor(
evm, true, Collections.emptyList(), 1, Collections.emptyList());
processor = new ContractCreationProcessor(evm, true, Collections.emptyList(), 1);
final Bytes contractCode = Bytes.fromHexString("0F01010101010101");
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
Expand All @@ -98,8 +94,7 @@ void shouldThrowAnExceptionWhenCodeContractTooLarge() {
true,
Collections.singletonList(
MaxCodeSizeRule.from(EvmSpecVersion.SPURIOUS_DRAGON, EvmConfiguration.DEFAULT)),
1,
Collections.emptyList());
1);
final Bytes contractCode =
Bytes.fromHexString("00".repeat(EvmSpecVersion.SPURIOUS_DRAGON.getMaxCodeSize() + 1));
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
Expand All @@ -120,8 +115,7 @@ void shouldNotThrowAnExceptionWhenCodeContractTooLarge() {
true,
Collections.singletonList(
MaxCodeSizeRule.from(EvmSpecVersion.SPURIOUS_DRAGON, EvmConfiguration.DEFAULT)),
1,
Collections.emptyList());
1);
final Bytes contractCode =
Bytes.fromHexString("00".repeat(EvmSpecVersion.SPURIOUS_DRAGON.getMaxCodeSize()));
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
Expand All @@ -134,9 +128,7 @@ void shouldNotThrowAnExceptionWhenCodeContractTooLarge() {

@Test
void shouldNotThrowAnExceptionWhenCodeSizeRuleNotAdded() {
processor =
new ContractCreationProcessor(
evm, true, Collections.emptyList(), 1, Collections.emptyList());
processor = new ContractCreationProcessor(evm, true, Collections.emptyList(), 1);
final Bytes contractCode = Bytes.fromHexString("00".repeat(24 * 1024 + 1));
final MessageFrame messageFrame = new TestMessageFrameBuilder().build();
messageFrame.setOutputData(contractCode);
Expand All @@ -148,7 +140,6 @@ void shouldNotThrowAnExceptionWhenCodeSizeRuleNotAdded() {

@Override
protected ContractCreationProcessor getAbstractMessageProcessor() {
return new ContractCreationProcessor(
evm, true, Collections.emptyList(), 1, Collections.emptyList());
return new ContractCreationProcessor(evm, true, Collections.emptyList(), 1);
}
}
Loading