Skip to content

Commit

Permalink
Extract evm version of AbstractRecordingCreateOperation (#5112)
Browse files Browse the repository at this point in the history
Signed-off-by: Stoyan Stoyanov <stoyan.stoyanov@limechain.tech>
Signed-off-by: Cody Littley <cody.littley@swirldslabs.com>
Signed-off-by: Kim Rader <kim.rader@swirldslabs.com>
Signed-off-by: Artem Ananev <artem.ananev@swirldslabs.com>
Signed-off-by: beeradb <294617+beeradb@users.noreply.github.com>
Signed-off-by: Neeharika-Sompalli <neeharika.sompalli@swirldslabs.com>
Signed-off-by: Michael Tinker <michael.tinker@swirldslabs.com>
Signed-off-by: Neeha <52669918+Neeharika-Sompalli@users.noreply.github.com>
Signed-off-by: Cody Littley <cody@swirldslabs.com>
Signed-off-by: tannerjfco <tannerjfco@gmail.com>
Signed-off-by: Austin Littley <austin.littley@swirldslabs.com>
Signed-off-by: Austin Littley <austin@swirldslabs.com>
Signed-off-by: Kelly Greco <kelly.greco@swirldslabs.com>
Signed-off-by: Edward Wertz <edward@swirldslabs.com>
Signed-off-by: Lazar Petrovic <lpetrovic05@gmail.com>
Signed-off-by: Nathan Klick <nathan@swirldslabs.com>
Signed-off-by: David Bakin <117694041+david-bakin-sl@users.noreply.github.com>
Signed-off-by: Kelly Greco <82919061+poulok@users.noreply.github.com>
Signed-off-by: Lev Povolotsky <lev@swirldslabs.com>
Signed-off-by: Miroslav Gatsanoga <miroslav.gatsanoga@limechain.tech>
Co-authored-by: Cody Littley <56973212+cody-littley@users.noreply.github.com>
Co-authored-by: Kim Rader <kim.rader@swirldslabs.com>
Co-authored-by: artemananiev <33361937+artemananiev@users.noreply.github.com>
Co-authored-by: Brad Bowman <294617+beeradb@users.noreply.github.com>
Co-authored-by: Neeha <52669918+Neeharika-Sompalli@users.noreply.github.com>
Co-authored-by: Michael Tinker <michael.tinker@swirldslabs.com>
Co-authored-by: Neeharika-Sompalli <neeharika.sompalli@swirldslabs.com>
Co-authored-by: Quan Nguyen <quan.nguyen@swirldslabs.com>
Co-authored-by: Tanner J Ferguson <tannerjfco@gmail.com>
Co-authored-by: Austin Littley <102969658+alittley@users.noreply.github.com>
Co-authored-by: Kelly Greco <82919061+poulok@users.noreply.github.com>
Co-authored-by: Edward Wertz <123979964+edward-swirldslabs@users.noreply.github.com>
Co-authored-by: Lazar Petrovic <lpetrovic05@gmail.com>
Co-authored-by: Nathan Klick <nathan@swirldslabs.com>
Co-authored-by: David Bakin <117694041+david-bakin-sl@users.noreply.github.com>
Co-authored-by: Cody Littley <cody@swirldslabs.com>
Co-authored-by: Lev Povolotsky <16233475+povolev15@users.noreply.github.com>
Co-authored-by: Miroslav Gatsanoga <miroslav.gatsanoga@limechain.tech>
  • Loading branch information
19 people committed Apr 4, 2023
1 parent b644cc2 commit f56f7a7
Show file tree
Hide file tree
Showing 27 changed files with 753 additions and 1,130 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,11 @@ public interface EvmProperties {
int maxGasRefundPercentage();

boolean isRedirectTokenCallsEnabled();

boolean isLazyCreationEnabled();

/**
* Enables or disables Create2 operation.
*/
boolean isCreate2Enabled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import static org.hyperledger.besu.evm.frame.MessageFrame.State.REVERT;

import com.hedera.node.app.service.evm.store.contracts.AbstractLedgerEvmWorldUpdater;
import com.hedera.node.app.service.evm.store.contracts.HederaEvmStackedWorldStateUpdater;
import com.hedera.node.app.service.evm.store.contracts.precompile.EvmHTSPrecompiledContract;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -84,7 +83,7 @@ public void start(final MessageFrame frame, final OperationTracer operationTrace
protected void executeHederaPrecompile(
final PrecompiledContract contract, final MessageFrame frame, final OperationTracer operationTracer) {
if (contract instanceof EvmHTSPrecompiledContract htsPrecompile) {
var updater = (HederaEvmStackedWorldStateUpdater) frame.getWorldUpdater();
var updater = (AbstractLedgerEvmWorldUpdater) frame.getWorldUpdater();
final var costedResult = htsPrecompile.computeCosted(
frame.getInputData(),
frame,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,11 @@
* limitations under the License.
*/

package com.hedera.node.app.service.mono.contracts.operation;

import static com.hedera.node.app.service.mono.context.BasicTransactionContext.EMPTY_KEY;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.ETHEREUM_NONCE;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.IS_SMART_CONTRACT;
import static com.hedera.node.app.service.mono.ledger.properties.AccountProperty.KEY;
import static com.hedera.node.app.service.mono.state.EntityCreator.EMPTY_MEMO;
import static com.hedera.node.app.service.mono.state.EntityCreator.NO_CUSTOM_FEES;
import static com.hedera.node.app.service.mono.txns.contract.ContractCreateTransitionLogic.STANDIN_CONTRACT_ID_KEY;
import static com.hedera.node.app.service.mono.utils.EntityIdUtils.accountIdFromEvmAddress;
package com.hedera.node.app.service.evm.contracts.operations;

import static org.hyperledger.besu.evm.frame.ExceptionalHaltReason.ILLEGAL_STATE_CHANGE;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;

import com.hedera.node.app.service.mono.context.SideEffectsTracker;
import com.hedera.node.app.service.mono.context.properties.GlobalDynamicProperties;
import com.hedera.node.app.service.mono.records.RecordsHistorian;
import com.hedera.node.app.service.mono.state.EntityCreator;
import com.hedera.node.app.service.mono.store.contracts.HederaStackedWorldStateUpdater;
import com.hedera.node.app.service.mono.store.contracts.precompile.SyntheticTxnFactory;
import com.hedera.node.app.service.mono.utils.EntityIdUtils;
import com.hedera.node.app.service.mono.utils.SidecarUtils;
import com.hedera.services.stream.proto.SidecarType;
import com.hederahashgraph.api.proto.java.AccountID;
import com.hederahashgraph.api.proto.java.ContractID;
import java.util.Collections;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.hyperledger.besu.datatypes.Address;
Expand All @@ -55,35 +34,29 @@
import org.hyperledger.besu.evm.operation.AbstractOperation;
import org.hyperledger.besu.evm.operation.Operation;

public abstract class AbstractRecordingCreateOperation extends AbstractOperation {
private static final int MAX_STACK_DEPTH = 1024;
/**
* Common logic for Hedera Create or Create2 operation.
* <p>Externalizing child records for newly created contract as well as sidecar creation using a {@link CreateOperationExternalizer}
*/
public abstract class AbstractEvmRecordingCreateOperation extends AbstractOperation {
protected static final int MAX_STACK_DEPTH = 1024;

protected static final Operation.OperationResult INVALID_RESPONSE =
new OperationResult(0L, ExceptionalHaltReason.INVALID_OPERATION);
protected static final Operation.OperationResult UNDERFLOW_RESPONSE =
new Operation.OperationResult(0, ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
private final CreateOperationExternalizer createOperationExternalizer;

protected final GlobalDynamicProperties dynamicProperties;
private final EntityCreator creator;
private final SyntheticTxnFactory syntheticTxnFactory;
private final RecordsHistorian recordsHistorian;

protected AbstractRecordingCreateOperation(
protected AbstractEvmRecordingCreateOperation(
final int opcode,
final String name,
final int stackItemsConsumed,
final int stackItemsProduced,
final int opSize,
final GasCalculator gasCalculator,
final EntityCreator creator,
final SyntheticTxnFactory syntheticTxnFactory,
final RecordsHistorian recordsHistorian,
final GlobalDynamicProperties dynamicProperties) {
final CreateOperationExternalizer createOperationExternalizer) {
super(opcode, name, stackItemsConsumed, stackItemsProduced, opSize, gasCalculator);
this.creator = creator;
this.recordsHistorian = recordsHistorian;
this.syntheticTxnFactory = syntheticTxnFactory;
this.dynamicProperties = dynamicProperties;
this.createOperationExternalizer = createOperationExternalizer;
}

@Override
Expand Down Expand Up @@ -121,7 +94,7 @@ public Operation.OperationResult execute(final MessageFrame frame, final EVM evm
return new Operation.OperationResult(cost, null);
}

static Operation.OperationResult haltWith(final long cost, final ExceptionalHaltReason reason) {
public static Operation.OperationResult haltWith(final long cost, final ExceptionalHaltReason reason) {
return new Operation.OperationResult(cost, reason);
}

Expand All @@ -131,7 +104,7 @@ static Operation.OperationResult haltWith(final long cost, final ExceptionalHalt

protected abstract Address targetContractAddress(MessageFrame frame);

private void fail(final MessageFrame frame) {
protected void fail(final MessageFrame frame) {
final long inputOffset = clampedToLong(frame.getStackItem(1));
final long inputSize = clampedToLong(frame.getStackItem(2));
frame.readMutableMemory(inputOffset, inputSize);
Expand All @@ -157,14 +130,9 @@ private void spawnChildMessage(final MessageFrame frame) {

final Address contractAddress = targetContractAddress(frame);

if (!dynamicProperties.isLazyCreationEnabled()) {
final var hollowAccountID =
matchingHollowAccountId((HederaStackedWorldStateUpdater) frame.getWorldUpdater(), contractAddress);

if (hollowAccountID != null) {
fail(frame);
return;
}
if (createOperationExternalizer.shouldFailBasedOnLazyCreation(frame, contractAddress)) {
fail(frame);
return;
}

final long childGasStipend = gasCalculator().gasAvailableForChildCreate(frame.getRemainingGas());
Expand Down Expand Up @@ -210,40 +178,7 @@ private void complete(final MessageFrame frame, final MessageFrame childFrame) {
if (childFrame.getState() == MessageFrame.State.COMPLETED_SUCCESS) {
frame.mergeWarmedUpFields(childFrame);
frame.pushStackItem(Words.fromAddress(childFrame.getContractAddress()));

// Add an in-progress record so that if everything succeeds, we can externalize the
// newly
// created contract in the record stream with both its 0.0.X id and its EVM address.
// C.f. https://github.com/hashgraph/hedera-services/issues/2807
final var updater = (HederaStackedWorldStateUpdater) frame.getWorldUpdater();
final var sideEffects = new SideEffectsTracker();

ContractID createdContractId;
final var hollowAccountID = matchingHollowAccountId(updater, childFrame.getContractAddress());

// if a hollow account exists at the alias address, finalize it to a contract
if (hollowAccountID != null) {
finalizeHollowAccountIntoContract(hollowAccountID, updater);
createdContractId = EntityIdUtils.asContract(hollowAccountID);
} else {
createdContractId = updater.idOfLastNewAddress();
}

sideEffects.trackNewContract(createdContractId, childFrame.getContractAddress());
final var childRecord = creator.createSuccessfulSyntheticRecord(NO_CUSTOM_FEES, sideEffects, EMPTY_MEMO);
childRecord.onlyExternalizeIfSuccessful();
final var opCustomizer = updater.customizerForPendingCreation();
final var syntheticOp = syntheticTxnFactory.contractCreation(opCustomizer);
if (dynamicProperties.enabledSidecars().contains(SidecarType.CONTRACT_BYTECODE)) {
final var contractBytecodeSidecar = SidecarUtils.createContractBytecodeSidecarFrom(
createdContractId,
childFrame.getCode().getContainerBytes().toArrayUnsafe(),
updater.get(childFrame.getContractAddress()).getCode().toArrayUnsafe());
updater.manageInProgressRecord(
recordsHistorian, childRecord, syntheticOp, List.of(contractBytecodeSidecar));
} else {
updater.manageInProgressRecord(recordsHistorian, childRecord, syntheticOp, Collections.emptyList());
}
createOperationExternalizer.externalize(frame, childFrame);
} else {
frame.setReturnData(childFrame.getOutputData());
frame.pushStackItem(UInt256.ZERO);
Expand All @@ -252,29 +187,4 @@ private void complete(final MessageFrame frame, final MessageFrame childFrame) {
final int currentPC = frame.getPC();
frame.setPC(currentPC + 1);
}

private AccountID matchingHollowAccountId(HederaStackedWorldStateUpdater updater, Address contract) {
final var accountID = accountIdFromEvmAddress(updater.aliases().resolveForEvm(contract));
final var trackingAccounts = updater.trackingAccounts();
if (trackingAccounts.contains(accountID)) {
final var accountKey = updater.trackingAccounts().get(accountID, KEY);
return EMPTY_KEY.equals(accountKey) ? accountID : null;
} else {
return null;
}
}

private void finalizeHollowAccountIntoContract(AccountID hollowAccountID, HederaStackedWorldStateUpdater updater) {
// reclaim the id for the contract
updater.reclaimLatestContractId();

// update the hollow account to be a contract
updater.trackingAccounts().set(hollowAccountID, IS_SMART_CONTRACT, true);

// update the hollow account key to be the default contract key
updater.trackingAccounts().set(hollowAccountID, KEY, STANDIN_CONTRACT_ID_KEY);

// set initial contract nonce to 1
updater.trackingAccounts().set(hollowAccountID, ETHEREUM_NONCE, 1L);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.hedera.node.app.service.evm.contracts.operations;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.MessageFrame;

/**
* Handles side effects for {@link AbstractEvmRecordingCreateOperation}
*/
public interface CreateOperationExternalizer {
/**
* Handle external side effects
* @param frame current message frame
* @param childFrame child message frame to be created
*/
void externalize(MessageFrame frame, MessageFrame childFrame);

/**
* Should lazy creation fail based on environment
* @param frame current message frame
* @param contractAddress the target contract address
* @return should it fail
*/
boolean shouldFailBasedOnLazyCreation(MessageFrame frame, Address contractAddress);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2021-2023 Hedera Hashgraph, LLC
* Copyright (C) 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -14,64 +14,57 @@
* limitations under the License.
*/

package com.hedera.node.app.service.mono.contracts.operation;
package com.hedera.node.app.service.evm.contracts.operations;

import static com.hedera.node.app.service.mono.sigs.utils.MiscCryptoUtils.keccak256DigestOf;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;

import com.hedera.node.app.service.mono.context.properties.GlobalDynamicProperties;
import com.hedera.node.app.service.mono.records.RecordsHistorian;
import com.hedera.node.app.service.mono.state.EntityCreator;
import com.hedera.node.app.service.mono.store.contracts.HederaStackedWorldStateUpdater;
import com.hedera.node.app.service.mono.store.contracts.precompile.SyntheticTxnFactory;
import com.hedera.node.app.service.evm.contracts.execution.EvmProperties;
import com.hedera.node.app.service.evm.store.contracts.HederaEvmStackedWorldUpdater;
import javax.inject.Inject;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

public class HederaCreate2Operation extends AbstractRecordingCreateOperation {
/**
* Hedera adapted version of Create2Operation.
*
* <p>Can be disabled using dynamic properties.
*/
public class HederaEvmCreate2Operation extends AbstractEvmRecordingCreateOperation {
protected final EvmProperties evmProperties;

private static final Bytes PREFIX = Bytes.fromHexString("0xFF");

@Inject
public HederaCreate2Operation(
public HederaEvmCreate2Operation(
final GasCalculator gasCalculator,
final EntityCreator creator,
final SyntheticTxnFactory syntheticTxnFactory,
final RecordsHistorian recordsHistorian,
final GlobalDynamicProperties dynamicProperties) {
super(
0xF5,
"ħCREATE2",
4,
1,
1,
gasCalculator,
creator,
syntheticTxnFactory,
recordsHistorian,
dynamicProperties);
final EvmProperties evmProperties,
final CreateOperationExternalizer createOperationExternalizer) {
super(0xF5, "ħCREATE2", 4, 1, 1, gasCalculator, createOperationExternalizer);
this.evmProperties = evmProperties;
}

@Override
protected long cost(final MessageFrame frame) {
return gasCalculator().create2OperationGasCost(frame);
public boolean isEnabled() {
return evmProperties.isCreate2Enabled();
}

@Override
protected boolean isEnabled() {
return dynamicProperties.isCreate2Enabled();
public long cost(final MessageFrame frame) {
return gasCalculator().create2OperationGasCost(frame);
}

@Override
protected Address targetContractAddress(final MessageFrame frame) {
public Address targetContractAddress(final MessageFrame frame) {
final var sourceAddressOrAlias = frame.getRecipientAddress();
final var offset = clampedToLong(frame.getStackItem(1));
final var length = clampedToLong(frame.getStackItem(2));

final var updater = (HederaStackedWorldStateUpdater) frame.getWorldUpdater();
final var updater = (HederaEvmStackedWorldUpdater) frame.getWorldUpdater();
final var source = updater.priorityAddress(sourceAddressOrAlias);

final Bytes32 salt = UInt256.fromBytes(frame.getStackItem(3));
Expand All @@ -88,4 +81,8 @@ protected Address targetContractAddress(final MessageFrame frame) {
private static Bytes32 keccak256(final Bytes input) {
return Bytes32.wrap(keccak256DigestOf(input.toArrayUnsafe()));
}

private static byte[] keccak256DigestOf(final byte[] msg) {
return new Keccak.Digest256().digest(msg);
}
}
Loading

0 comments on commit f56f7a7

Please sign in to comment.