Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move EIP-4762 gas charging for contract account creation from code su… #7869

Merged
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
2 changes: 1 addition & 1 deletion ethereum/referencetests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ dependencies {
}
verkleRefTestImplemention dependencies.create('ethereum:execution-spec-tests') {
version {
strictly 'verkle@v0.0.5'
strictly 'verkle@v0.0.8'
}
artifact {
name = 'fixtures'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@

public final class AccessEvents {

private static final long WITNESS_BRANCH_COST = 1900;
private static final long WITNESS_CHUNK_COST = 200;
private static final long SUBTREE_EDIT_COST = 3000;
private static final long CHUNK_EDIT_COST = 500;
private static final long CHUNK_FILL_COST = 6200;

public static final short NONE = 0;
public static final short BRANCH_READ = 1;
public static final short BRANCH_WRITE = 2;
Expand Down Expand Up @@ -44,4 +50,24 @@ public static boolean isLeafReset(final short accessEvents) {
public static boolean isLeafSet(final short accessEvents) {
return (accessEvents & LEAF_SET) != 0;
}

public static long getBranchReadCost() {
return WITNESS_BRANCH_COST;
}

public static long getLeafReadCost() {
return WITNESS_CHUNK_COST;
}

public static long getBranchWriteCost() {
return SUBTREE_EDIT_COST;
}

public static long getLeafResetCost() {
return CHUNK_EDIT_COST;
}

public static long getLeafSetCost() {
return CHUNK_FILL_COST;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ public class Eip4762AccessWitness implements AccessWitness {

private static final Logger LOG = LoggerFactory.getLogger(Eip4762AccessWitness.class);
private static final TrieKeyAdapter TRIE_KEY_ADAPTER = new TrieKeyAdapter(new PedersenHasher());
private static final long WITNESS_BRANCH_COST = 1900;
private static final long WITNESS_CHUNK_COST = 200;
private static final long SUBTREE_EDIT_COST = 3000;
private static final long CHUNK_EDIT_COST = 500;
private static final long CHUNK_FILL_COST = 6200;

private static final UInt256 zeroTreeIndex = UInt256.ZERO;
private final Map<LeafAccessKey, Integer> leaves;
Expand Down Expand Up @@ -289,89 +284,57 @@ public long touchAddressAndChargeGas(
final short accessEvents = touchAddress(address, treeIndex, subIndex, accessMode);
long gas = 0;
if (AccessEvents.isBranchRead(accessEvents)) {
gas = clampedAdd(gas, WITNESS_BRANCH_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas WITNESS_BRANCH_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
gas = clampedAdd(gas, AccessEvents.getBranchReadCost());
}
if (AccessEvents.isLeafRead(accessEvents)) {
gas = clampedAdd(gas, WITNESS_CHUNK_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas WITNESS_CHUNK_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
gas = clampedAdd(gas, AccessEvents.getLeafReadCost());
}
if (AccessEvents.isBranchWrite(accessEvents)) {
gas = clampedAdd(gas, SUBTREE_EDIT_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas SUBTREE_EDIT_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
gas = clampedAdd(gas, AccessEvents.getBranchWriteCost());
}
if (AccessEvents.isLeafReset(accessEvents)) {
gas = clampedAdd(gas, CHUNK_EDIT_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas CHUNK_EDIT_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
gas = clampedAdd(gas, AccessEvents.getLeafResetCost());
}
if (AccessEvents.isLeafSet(accessEvents)) {
gas = clampedAdd(gas, CHUNK_FILL_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas CHUNK_FILL_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
gas = clampedAdd(gas, AccessEvents.getLeafSetCost());
}

final long gasView = gas;
LOG.atDebug().log(
() ->
"touch witness "
+ address
+ " "
+ treeIndex.toShortHexString()
+ " "
+ subIndex.toShortHexString()
+ "\ntotal charges "
+ gasView
+ costSchedulePrettyPrint(accessEvents));

return gas;
}

private static String costSchedulePrettyPrint(final short accessEvents) {
String message = "";
if (AccessEvents.isBranchRead(accessEvents)) {
message += "\n\tWITNESS_BRANCH_COST " + AccessEvents.getBranchReadCost();
}
if (AccessEvents.isLeafRead(accessEvents)) {
message += "\n\tWITNESS_CHUNK_COST " + AccessEvents.getLeafReadCost();
}
if (AccessEvents.isBranchWrite(accessEvents)) {
message += "\n\tSUBTREE_EDIT_COST " + AccessEvents.getBranchWriteCost();
}
if (AccessEvents.isLeafReset(accessEvents)) {
message += "\n\tCHUNK_EDIT_COST " + AccessEvents.getLeafResetCost();
}
if (AccessEvents.isLeafSet(accessEvents)) {
message += "\n\tCHUNK_FILL_COST " + AccessEvents.getLeafSetCost();
}
return message;
}

public short touchAddress(
final Address addr, final UInt256 treeIndex, final UInt256 leafIndex, final int accessMode) {
short accessEvents = AccessEvents.NONE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;

import org.apache.tuweni.bytes.Bytes;
import org.slf4j.Logger;
Expand Down Expand Up @@ -101,7 +102,17 @@ public void start(final MessageFrame frame, final OperationTracer operationTrace
final MutableAccount contract = frame.getWorldUpdater().getOrCreate(contractAddress);
long statelessGasCost =
evm.getGasCalculator().proofOfAbsenceCost(frame, contract.getAddress());
if (handleInsufficientGas(
frame,
statelessGasCost,
() ->
String.format(
"Not enough gas to cover proof of absence fee for %s: remaining gas = %d < %d = creation fee",
frame.getContractAddress(), frame.getRemainingGas(), statelessGasCost))) {
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to return or set the state of the frame as we do here ?
frame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it Illegal state change? I think it is insufficient gas. It's already done inside handleInsufficientGas

}
frame.decrementRemainingGas(statelessGasCost);

if (accountExists(contract)) {
LOG.trace(
"Contract creation error: account has already been created for address {}",
Expand All @@ -111,6 +122,20 @@ public void start(final MessageFrame frame, final OperationTracer operationTrace
operationTracer.traceAccountCreationResult(
frame, Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE));
} else {
final long accountCreationFee =
evm.getGasCalculator().completedCreateContractGasCost(frame);
if (handleInsufficientGas(
frame,
accountCreationFee,
() ->
String.format(
"Not enough gas to pay the contract creation fee for %s: "
+ "remaining gas = %d < %d = creation fee",
frame.getContractAddress(), frame.getRemainingGas(), accountCreationFee))) {
return;
}
frame.decrementRemainingGas(accountCreationFee);

frame.addCreate(contractAddress);
contract.incrementBalance(frame.getValue());
contract.setNonce(initialContractNonce);
Expand All @@ -132,65 +157,62 @@ public void codeSuccess(final MessageFrame frame, final OperationTracer operatio

final long depositFee = evm.getGasCalculator().codeDepositGasCost(frame, contractCode.size());

if (frame.getRemainingGas() < depositFee) {
LOG.trace(
"Not enough gas to pay the code deposit fee for {}: "
+ "remaining gas = {} < {} = deposit fee",
frame.getContractAddress(),
frame.getRemainingGas(),
depositFee);
if (handleInsufficientGas(
frame,
depositFee,
() ->
String.format(
"Not enough gas to pay the code deposit fee for %s: "
+ "remaining gas = %d < %d = deposit fee",
frame.getContractAddress(), frame.getRemainingGas(), depositFee))) {

if (requireCodeDepositToSucceed) {
LOG.trace("Contract creation error: insufficient funds for code deposit");
frame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
operationTracer.traceAccountCreationResult(
frame, Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
} else {
frame.setState(MessageFrame.State.COMPLETED_SUCCESS);
}
} else {
final var invalidReason =
contractValidationRules.stream()
.map(rule -> rule.validate(contractCode, frame, evm))
.filter(Optional::isPresent)
.findFirst();
if (invalidReason.isEmpty()) {
frame.decrementRemainingGas(depositFee);

final long statelessContractCompletionFee =
evm.getGasCalculator().completedCreateContractGasCost(frame);
if (frame.getRemainingGas() < statelessContractCompletionFee) {
LOG.trace(
"Not enough gas to pay the contract creation completion fee for {}: "
+ "remaining gas = {} < {} = deposit fee",
frame.getContractAddress(),
frame.getRemainingGas(),
statelessContractCompletionFee);
frame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
} else {
frame.decrementRemainingGas(statelessContractCompletionFee);
// Finalize contract creation, setting the contract code.
final MutableAccount contract =
frame.getWorldUpdater().getOrCreate(frame.getContractAddress());
contract.setCode(contractCode);
LOG.trace(
"Successful creation of contract {} with code of size {} (Gas remaining: {})",
frame.getContractAddress(),
contractCode.size(),
frame.getRemainingGas());
frame.setState(MessageFrame.State.COMPLETED_SUCCESS);
}
return;
}

if (operationTracer.isExtendedTracing()) {
operationTracer.traceAccountCreationResult(frame, Optional.empty());
}
} else {
final Optional<ExceptionalHaltReason> exceptionalHaltReason = invalidReason.get();
frame.setExceptionalHaltReason(exceptionalHaltReason);
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
operationTracer.traceAccountCreationResult(frame, exceptionalHaltReason);
}
final var invalidReason =
contractValidationRules.stream()
.map(rule -> rule.validate(contractCode, frame, evm))
.filter(Optional::isPresent)
.findFirst();
if (invalidReason.isPresent()) {
final Optional<ExceptionalHaltReason> exceptionalHaltReason = invalidReason.get();
frame.setExceptionalHaltReason(exceptionalHaltReason);
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
operationTracer.traceAccountCreationResult(frame, exceptionalHaltReason);
return;
}

frame.decrementRemainingGas(depositFee);

// Finalize contract creation, setting the contract code.
final MutableAccount contract = frame.getWorldUpdater().getOrCreate(frame.getContractAddress());
contract.setCode(contractCode);
LOG.trace(
"Successful creation of contract {} with code of size {} (Gas remaining: {})",
frame.getContractAddress(),
contractCode.size(),
frame.getRemainingGas());
frame.setState(MessageFrame.State.COMPLETED_SUCCESS);

if (operationTracer.isExtendedTracing()) {
operationTracer.traceAccountCreationResult(frame, Optional.empty());
}
}

private static boolean handleInsufficientGas(
final MessageFrame frame, final long gasFee, final Supplier<String> message) {
if (frame.getRemainingGas() < gasFee) {
LOG.trace(message.get());
frame.setExceptionalHaltReason(Optional.of(ExceptionalHaltReason.INSUFFICIENT_GAS));
frame.setState(MessageFrame.State.EXCEPTIONAL_HALT);
return true;
}
return false;
}
}
Loading