mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 21:38:15 -05:00
EIP-7623 (#8093)
Implements EIP-7623 (increase calldata cost) Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> Signed-off-by: Simon Dudley <simon.dudley@consensys.net> Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
- Improve debug_traceBlock calls performance and reduce output size [#8076](https://github.com/hyperledger/besu/pull/8076)
|
||||
- Add support for EIP-7702 transaction in the txpool [#8018](https://github.com/hyperledger/besu/pull/8018) [#7984](https://github.com/hyperledger/besu/pull/7984)
|
||||
- Add support for `movePrecompileToAddress` in `StateOverrides` (`eth_call`)[8115](https://github.com/hyperledger/besu/pull/8115)
|
||||
- Add EIP-7623 - Increase calldata cost [#8093](https://github.com/hyperledger/besu/pull/8093)
|
||||
|
||||
### Bug fixes
|
||||
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)
|
||||
|
||||
@@ -84,6 +84,6 @@ public class PluginEeaSendRawTransaction extends AbstractEeaSendRawTransaction {
|
||||
// choose the highest of the two options
|
||||
return Math.max(
|
||||
privateTransaction.getGasLimit(),
|
||||
gasCalculator.transactionIntrinsicGasCost(Bytes.fromBase64String(pmtPayload), false));
|
||||
gasCalculator.transactionIntrinsicGasCost(Bytes.fromBase64String(pmtPayload), false, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_IS_PER
|
||||
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION_HASH;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
|
||||
import org.hyperledger.besu.collections.trie.BytesTrieSet;
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
@@ -351,22 +352,22 @@ public class MainnetTransactionProcessor {
|
||||
warmAddressList.add(miningBeneficiary);
|
||||
}
|
||||
|
||||
final long intrinsicGas =
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(), transaction.isContractCreation());
|
||||
final long accessListGas =
|
||||
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
|
||||
final long codeDelegationGas =
|
||||
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
|
||||
final long gasAvailable =
|
||||
transaction.getGasLimit() - intrinsicGas - accessListGas - codeDelegationGas;
|
||||
final long intrinsicGas =
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(),
|
||||
transaction.isContractCreation(),
|
||||
clampedAdd(accessListGas, codeDelegationGas));
|
||||
|
||||
final long gasAvailable = transaction.getGasLimit() - intrinsicGas;
|
||||
LOG.trace(
|
||||
"Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - codeDelegation)",
|
||||
"Gas available for execution {} = {} - {} (limit - intrinsic)",
|
||||
gasAvailable,
|
||||
transaction.getGasLimit(),
|
||||
intrinsicGas,
|
||||
accessListGas,
|
||||
codeDelegationGas);
|
||||
intrinsicGas);
|
||||
|
||||
final WorldUpdater worldUpdater = evmWorldUpdater.updater();
|
||||
final ImmutableMap.Builder<String, Object> contextVariablesBuilder =
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegateCodeHelper.hasDelegatedCode;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECPSignature;
|
||||
@@ -250,17 +251,22 @@ public class MainnetTransactionValidator implements TransactionValidator {
|
||||
}
|
||||
}
|
||||
|
||||
final long intrinsicGasCost =
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(), transaction.isContractCreation())
|
||||
+ (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L))
|
||||
+ gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
|
||||
if (Long.compareUnsigned(intrinsicGasCost, transaction.getGasLimit()) > 0) {
|
||||
final long baselineGas =
|
||||
clampedAdd(
|
||||
transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L),
|
||||
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize()));
|
||||
final long intrinsicGasCostOrFloor =
|
||||
Math.max(
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(), transaction.isContractCreation(), baselineGas),
|
||||
gasCalculator.transactionFloorCost(transaction.getPayload()));
|
||||
|
||||
if (Long.compareUnsigned(intrinsicGasCostOrFloor, transaction.getGasLimit()) > 0) {
|
||||
return ValidationResult.invalid(
|
||||
TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT,
|
||||
String.format(
|
||||
"intrinsic gas cost %s exceeds gas limit %s",
|
||||
intrinsicGasCost, transaction.getGasLimit()));
|
||||
intrinsicGasCostOrFloor, transaction.getGasLimit()));
|
||||
}
|
||||
|
||||
if (transaction.calculateUpfrontGasCost(transaction.getMaxGasPrice(), Wei.ZERO, 0).bitLength()
|
||||
|
||||
@@ -16,15 +16,21 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
|
||||
import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@@ -36,6 +42,8 @@ public class IntrinsicGasTest {
|
||||
public static Stream<Arguments> data() {
|
||||
final GasCalculator frontier = new FrontierGasCalculator();
|
||||
final GasCalculator istanbul = new IstanbulGasCalculator();
|
||||
final GasCalculator shanghai = new ShanghaiGasCalculator();
|
||||
final GasCalculator prague = new PragueGasCalculator();
|
||||
return Stream.of(
|
||||
// EnoughGAS
|
||||
Arguments.of(
|
||||
@@ -81,16 +89,36 @@ public class IntrinsicGasTest {
|
||||
Arguments.of(
|
||||
istanbul,
|
||||
21116L,
|
||||
"0xf87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"));
|
||||
"0xf87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"),
|
||||
// CallData Gas Increase
|
||||
Arguments.of(
|
||||
prague,
|
||||
21116L,
|
||||
"0xf87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"),
|
||||
// AccessList
|
||||
Arguments.of(
|
||||
shanghai,
|
||||
25300L,
|
||||
"0x01f89a018001826a4094095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f794a95e7baea6a6c7c4c2dfeb977efac326af552d87e1a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80a05cbd172231fc0735e0fb994dd5b1a4939170a260b36f0427a8a80866b063b948a07c230f7f578dd61785c93361b9871c0706ebfa6d06e3f4491dc9558c5202ed36"));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void validateGasCost(
|
||||
final GasCalculator gasCalculator, final long expectedGas, final String txRlp) {
|
||||
Transaction t = Transaction.readFrom(RLP.input(Bytes.fromHexString(txRlp)));
|
||||
Bytes rlp = Bytes.fromHexString(txRlp);
|
||||
|
||||
// non-frontier transactions need to be opaque for parsing to work
|
||||
if (rlp.get(0) > 0) {
|
||||
final BytesValueRLPOutput output = new BytesValueRLPOutput();
|
||||
output.writeBytes(rlp);
|
||||
rlp = output.encoded();
|
||||
}
|
||||
|
||||
Transaction t = Transaction.readFrom(RLP.input(rlp));
|
||||
Assertions.assertThat(
|
||||
gasCalculator.transactionIntrinsicGasCost(t.getPayload(), t.isContractCreation()))
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
t.getPayload(), t.isContractCreation(), baselineGas(gasCalculator, t)))
|
||||
.isEqualTo(expectedGas);
|
||||
}
|
||||
|
||||
@@ -100,4 +128,21 @@ public class IntrinsicGasTest {
|
||||
.withFailMessage("This test is here so gradle --dry-run executes this class")
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
long baselineGas(final GasCalculator gasCalculator, final Transaction transaction) {
|
||||
final List<AccessListEntry> accessListEntries = transaction.getAccessList().orElse(List.of());
|
||||
|
||||
int accessListStorageCount = 0;
|
||||
for (final var entry : accessListEntries) {
|
||||
final List<Bytes32> storageKeys = entry.storageKeys();
|
||||
accessListStorageCount += storageKeys.size();
|
||||
}
|
||||
final long accessListGas =
|
||||
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
|
||||
|
||||
final long codeDelegationGas =
|
||||
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
|
||||
|
||||
return accessListGas + codeDelegationGas;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,27 @@ public class MainnetTransactionValidatorTest {
|
||||
.gasLimit(10)
|
||||
.chainId(Optional.empty())
|
||||
.createTransaction(senderKeys);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(50L);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(50L);
|
||||
|
||||
assertThat(
|
||||
validator.validate(
|
||||
transaction, Optional.empty(), Optional.empty(), transactionValidationParams))
|
||||
.isEqualTo(
|
||||
ValidationResult.invalid(TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRejectTransactionIfFloorExceedsGasLimit_EIP_7623() {
|
||||
final TransactionValidator validator =
|
||||
createTransactionValidator(
|
||||
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
|
||||
final Transaction transaction =
|
||||
new TransactionTestFixture()
|
||||
.gasLimit(10)
|
||||
.chainId(Optional.empty())
|
||||
.createTransaction(senderKeys);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(5L);
|
||||
when(gasCalculator.transactionFloorCost(any())).thenReturn(51L);
|
||||
|
||||
assertThat(
|
||||
validator.validate(
|
||||
@@ -398,7 +418,7 @@ public class MainnetTransactionValidatorTest {
|
||||
transaction, Optional.empty(), Optional.empty(), transactionValidationParams))
|
||||
.isEqualTo(ValidationResult.invalid(INVALID_TRANSACTION_FORMAT));
|
||||
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(0L);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(0L);
|
||||
|
||||
assertThat(
|
||||
eip1559Validator.validate(
|
||||
@@ -475,7 +495,7 @@ public class MainnetTransactionValidatorTest {
|
||||
.chainId(Optional.of(BigInteger.ONE))
|
||||
.createTransaction(senderKeys);
|
||||
final Optional<Wei> basefee = Optional.of(Wei.of(150000L));
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(50L);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(50L);
|
||||
|
||||
assertThat(
|
||||
validator.validate(transaction, basefee, Optional.empty(), transactionValidationParams))
|
||||
@@ -500,7 +520,7 @@ public class MainnetTransactionValidatorTest {
|
||||
.type(TransactionType.EIP1559)
|
||||
.chainId(Optional.of(BigInteger.ONE))
|
||||
.createTransaction(senderKeys);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(50L);
|
||||
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(50L);
|
||||
|
||||
assertThat(
|
||||
validator.validate(
|
||||
|
||||
@@ -402,16 +402,20 @@ public class EvmToolCommand implements Runnable {
|
||||
|
||||
long txGas = gas;
|
||||
if (chargeIntrinsicGas) {
|
||||
final long intrinsicGasCost =
|
||||
protocolSpec
|
||||
.getGasCalculator()
|
||||
.transactionIntrinsicGasCost(tx.getPayload(), tx.isContractCreation());
|
||||
txGas -= intrinsicGasCost;
|
||||
final long accessListCost =
|
||||
tx.getAccessList()
|
||||
.map(list -> protocolSpec.getGasCalculator().accessListGasCost(list))
|
||||
.orElse(0L);
|
||||
txGas -= accessListCost;
|
||||
|
||||
final long delegateCodeCost =
|
||||
protocolSpec.getGasCalculator().delegateCodeGasCost(tx.codeDelegationListSize());
|
||||
|
||||
final long intrinsicGasCost =
|
||||
protocolSpec
|
||||
.getGasCalculator()
|
||||
.transactionIntrinsicGasCost(
|
||||
tx.getPayload(), tx.isContractCreation(), accessListCost + delegateCodeCost);
|
||||
txGas -= intrinsicGasCost;
|
||||
}
|
||||
|
||||
final EVM evm = protocolSpec.getEvm();
|
||||
|
||||
@@ -437,7 +437,7 @@ public class T8nExecutor {
|
||||
gasUsed += transactionGasUsed;
|
||||
long intrinsicGas =
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(), transaction.getTo().isEmpty());
|
||||
transaction.getPayload(), transaction.getTo().isEmpty(), 0);
|
||||
TransactionReceipt receipt =
|
||||
protocolSpec
|
||||
.getTransactionReceiptFactory()
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
},
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"nonce": "0x00",
|
||||
"balance": "0xad78ebc5ac62000000",
|
||||
"balance": "0xaa00be18c288efd690",
|
||||
"code": "0x",
|
||||
"storage": {}
|
||||
}
|
||||
@@ -194,7 +194,7 @@
|
||||
"nonce": "0x1"
|
||||
},
|
||||
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||
"balance": "0xaa00be18c288efd690",
|
||||
"balance": "0xa688906bd8afdfad20",
|
||||
"nonce": "0x2"
|
||||
}
|
||||
},
|
||||
@@ -204,7 +204,7 @@
|
||||
"requests": [
|
||||
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030100000000000000"
|
||||
],
|
||||
"stateRoot": "0x6471f6d90b87f759176a0ad62a7096f69d0d24fd873bdb6b6ced57d04a71e274",
|
||||
"stateRoot": "0xc769f83dbad9b87a209216d18c4b19cb12b61838594a2e8270898438f4e147af",
|
||||
"txRoot": "0x2b790bf82ef7259a0e4513d1b89a77d81e99672ba68758ef2ba3fde32851d023",
|
||||
"receiptsRoot": "0x9c8d7a917ecb3ff2566f264abbf39131e51b08b07eb2b69cb46989d79d985593",
|
||||
"logsHash": "0x43e31613bfefc1f55d8b3ca2b61f933f3838d523dc11cb5d7ffdd2ecf0ab5d49",
|
||||
|
||||
@@ -192,10 +192,13 @@ public class TransactionTest {
|
||||
|
||||
assertThat(transaction.getSender()).isEqualTo(expected.getSender());
|
||||
assertThat(transaction.getHash()).isEqualTo(expected.getHash());
|
||||
final long intrinsicGasCost =
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(), transaction.isContractCreation())
|
||||
+ (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L));
|
||||
final long baselineGas =
|
||||
transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L) +
|
||||
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
|
||||
final long intrinsicGasCost = gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(),
|
||||
transaction.isContractCreation(),
|
||||
baselineGas);
|
||||
assertThat(intrinsicGasCost).isEqualTo(expected.getIntrinsicGas());
|
||||
} catch (final Exception e) {
|
||||
if (expected.isSucceeds()) {
|
||||
|
||||
@@ -39,7 +39,8 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
|
||||
private static final long TX_DATA_NON_ZERO_COST = 68L;
|
||||
|
||||
private static final long TX_BASE_COST = 21_000L;
|
||||
/** Minimum base cost that every transaction needs to pay */
|
||||
protected static final long TX_BASE_COST = 21_000L;
|
||||
|
||||
private static final long TX_CREATE_EXTRA_COST = 0L;
|
||||
|
||||
@@ -129,18 +130,82 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreate) {
|
||||
public long transactionIntrinsicGasCost(
|
||||
final Bytes payload, final boolean isContractCreation, final long baselineGas) {
|
||||
final long dynamicIntrinsicGasCost =
|
||||
dynamicIntrinsicGasCost(payload, isContractCreation, baselineGas);
|
||||
|
||||
if (dynamicIntrinsicGasCost == Long.MIN_VALUE || dynamicIntrinsicGasCost == Long.MAX_VALUE) {
|
||||
return dynamicIntrinsicGasCost;
|
||||
}
|
||||
return clampedAdd(getMinimumTransactionCost(), dynamicIntrinsicGasCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the dynamic part of the intrinsic gas cost
|
||||
*
|
||||
* @param payload the call data payload
|
||||
* @param isContractCreation whether the transaction is a contract creation
|
||||
* @param baselineGas how much gas is used by access lists and code delegations
|
||||
* @return the dynamic part of the intrinsic gas cost
|
||||
*/
|
||||
protected long dynamicIntrinsicGasCost(
|
||||
final Bytes payload, final boolean isContractCreation, final long baselineGas) {
|
||||
final int payloadSize = payload.size();
|
||||
final long zeroBytes = zeroBytes(payload);
|
||||
long cost = clampedAdd(callDataCost(payloadSize, zeroBytes), baselineGas);
|
||||
|
||||
if (cost == Long.MIN_VALUE || cost == Long.MAX_VALUE) {
|
||||
return cost;
|
||||
}
|
||||
|
||||
if (isContractCreation) {
|
||||
cost = clampedAdd(cost, contractCreationCost(payloadSize));
|
||||
|
||||
if (cost == Long.MIN_VALUE || cost == Long.MAX_VALUE) {
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cost of the call data
|
||||
*
|
||||
* @param payloadSize the total size of the payload
|
||||
* @param zeroBytes the number of zero bytes in the payload
|
||||
* @return the cost of the call data
|
||||
*/
|
||||
protected long callDataCost(final long payloadSize, final long zeroBytes) {
|
||||
return clampedAdd(
|
||||
TX_DATA_NON_ZERO_COST * (payloadSize - zeroBytes), TX_DATA_ZERO_COST * zeroBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the zero bytes in the payload
|
||||
*
|
||||
* @param payload the payload
|
||||
* @return the number of zero bytes in the payload
|
||||
*/
|
||||
protected static long zeroBytes(final Bytes payload) {
|
||||
int zeros = 0;
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
if (payload.get(i) == 0) {
|
||||
++zeros;
|
||||
zeros += 1;
|
||||
}
|
||||
}
|
||||
final int nonZeros = payload.size() - zeros;
|
||||
return zeros;
|
||||
}
|
||||
|
||||
final long cost = TX_BASE_COST + TX_DATA_ZERO_COST * zeros + TX_DATA_NON_ZERO_COST * nonZeros;
|
||||
|
||||
return isContractCreate ? (cost + txCreateExtraGasCost()) : cost;
|
||||
/**
|
||||
* Returns the gas cost for contract creation transactions
|
||||
*
|
||||
* @param ignored the size of the contract creation code (ignored in Frontier)
|
||||
* @return the gas cost for contract creation transactions
|
||||
*/
|
||||
protected long contractCreationCost(final int ignored) {
|
||||
return txCreateExtraGasCost();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,6 +217,11 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
return TX_CREATE_EXTRA_COST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionFloorCost(final Bytes payload) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long codeDepositGasCost(final int codeSize) {
|
||||
return CODE_DEPOSIT_BYTE_COST * codeSize;
|
||||
@@ -558,11 +628,6 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
return clampedAdd(clampedMultiply(MEMORY_WORD_GAS_COST, length), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaximumTransactionCost(final int size) {
|
||||
return TX_BASE_COST + TX_DATA_NON_ZERO_COST * size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinimumTransactionCost() {
|
||||
return TX_BASE_COST;
|
||||
@@ -572,20 +637,21 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
public long calculateGasRefund(
|
||||
final Transaction transaction,
|
||||
final MessageFrame initialFrame,
|
||||
final long codeDelegationRefund) {
|
||||
final long selfDestructRefund =
|
||||
getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
|
||||
final long baseRefundGas =
|
||||
initialFrame.getGasRefund() + selfDestructRefund + codeDelegationRefund;
|
||||
return refunded(transaction, initialFrame.getRemainingGas(), baseRefundGas);
|
||||
final long ignoredCodeDelegationRefund) {
|
||||
|
||||
final long refundAllowance = calculateRefundAllowance(transaction, initialFrame);
|
||||
|
||||
return initialFrame.getRemainingGas() + refundAllowance;
|
||||
}
|
||||
|
||||
private long refunded(
|
||||
final Transaction transaction, final long gasRemaining, final long gasRefund) {
|
||||
private long calculateRefundAllowance(
|
||||
final Transaction transaction, final MessageFrame initialFrame) {
|
||||
final long selfDestructRefund =
|
||||
getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
|
||||
final long executionRefund = initialFrame.getGasRefund() + selfDestructRefund;
|
||||
// Integer truncation takes care of the floor calculation needed after the divide.
|
||||
final long maxRefundAllowance =
|
||||
(transaction.getGasLimit() - gasRemaining) / getMaxRefundQuotient();
|
||||
final long refundAllowance = Math.min(maxRefundAllowance, gasRefund);
|
||||
return gasRemaining + refundAllowance;
|
||||
(transaction.getGasLimit() - initialFrame.getRemainingGas()) / getMaxRefundQuotient();
|
||||
return Math.min(executionRefund, maxRefundAllowance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,10 +543,21 @@ public interface GasCalculator {
|
||||
* encoded binary representation when stored on-chain.
|
||||
*
|
||||
* @param transactionPayload The encoded transaction, as bytes
|
||||
* @param isContractCreate Is this transaction a contract creation transaction?
|
||||
* @param isContractCreation Is this transaction a contract creation transaction?
|
||||
* @param baselineGas The gas used by access lists and code delegation authorizations
|
||||
* @return the transaction's intrinsic gas cost
|
||||
*/
|
||||
long transactionIntrinsicGasCost(Bytes transactionPayload, boolean isContractCreate);
|
||||
long transactionIntrinsicGasCost(
|
||||
Bytes transactionPayload, boolean isContractCreation, long baselineGas);
|
||||
|
||||
/**
|
||||
* Returns the floor gas cost of a transaction payload, i.e. the minimum gas cost that a
|
||||
* transaction will be charged based on its calldata. Introduced in EIP-7623 in Prague.
|
||||
*
|
||||
* @param transactionPayload The encoded transaction, as bytes
|
||||
* @return the transaction's floor gas cost
|
||||
*/
|
||||
long transactionFloorCost(final Bytes transactionPayload);
|
||||
|
||||
/**
|
||||
* Returns the gas cost of the explicitly declared access list.
|
||||
@@ -580,15 +591,6 @@ public interface GasCalculator {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum Cost of a Transaction of a certain length.
|
||||
*
|
||||
* @param size the length of the transaction, in bytes
|
||||
* @return the maximum gas cost
|
||||
*/
|
||||
// what would be the gas for a PMT with hash of all non-zeros
|
||||
long getMaximumTransactionCost(int size);
|
||||
|
||||
/**
|
||||
* Minimum gas cost of a transaction.
|
||||
*
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
/** The Istanbul gas calculator. */
|
||||
@@ -24,7 +25,6 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
|
||||
private static final long TX_DATA_ZERO_COST = 4L;
|
||||
private static final long ISTANBUL_TX_DATA_NON_ZERO_COST = 16L;
|
||||
private static final long TX_BASE_COST = 21_000L;
|
||||
|
||||
private static final long SLOAD_GAS = 800L;
|
||||
private static final long BALANCE_OPERATION_GAS_COST = 700L;
|
||||
@@ -42,19 +42,9 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
public IstanbulGasCalculator() {}
|
||||
|
||||
@Override
|
||||
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreation) {
|
||||
int zeros = 0;
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
if (payload.get(i) == 0) {
|
||||
++zeros;
|
||||
}
|
||||
}
|
||||
final int nonZeros = payload.size() - zeros;
|
||||
|
||||
final long cost =
|
||||
TX_BASE_COST + (TX_DATA_ZERO_COST * zeros) + (ISTANBUL_TX_DATA_NON_ZERO_COST * nonZeros);
|
||||
|
||||
return isContractCreation ? (cost + txCreateExtraGasCost()) : cost;
|
||||
protected long callDataCost(final long payloadSize, final long zeroBytes) {
|
||||
return clampedAdd(
|
||||
ISTANBUL_TX_DATA_NON_ZERO_COST * (payloadSize - zeroBytes), TX_DATA_ZERO_COST * zeroBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,9 +126,4 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
public long extCodeHashOperationGasCost() {
|
||||
return EXTCODE_HASH_COST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaximumTransactionCost(final int size) {
|
||||
return TX_BASE_COST + (ISTANBUL_TX_DATA_NON_ZERO_COST * size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,13 @@
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/**
|
||||
* Gas Calculator for Prague
|
||||
@@ -26,6 +31,8 @@ import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
* </UL>
|
||||
*/
|
||||
public class PragueGasCalculator extends CancunGasCalculator {
|
||||
private static final long TOTAL_COST_FLOOR_PER_TOKEN = 10L;
|
||||
|
||||
final long existingAccountGasRefund;
|
||||
|
||||
/**
|
||||
@@ -68,4 +75,47 @@ public class PragueGasCalculator extends CancunGasCalculator {
|
||||
public long calculateDelegateCodeGasRefund(final long alreadyExistingAccounts) {
|
||||
return existingAccountGasRefund * alreadyExistingAccounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long calculateGasRefund(
|
||||
final Transaction transaction,
|
||||
final MessageFrame initialFrame,
|
||||
final long codeDelegationRefund) {
|
||||
|
||||
final long refundAllowance =
|
||||
calculateRefundAllowance(transaction, initialFrame, codeDelegationRefund);
|
||||
|
||||
final long executionGasUsed =
|
||||
transaction.getGasLimit() - initialFrame.getRemainingGas() - refundAllowance;
|
||||
final long transactionFloorCost = transactionFloorCost(transaction.getPayload());
|
||||
final long totalGasUsed = Math.max(executionGasUsed, transactionFloorCost);
|
||||
return transaction.getGasLimit() - totalGasUsed;
|
||||
}
|
||||
|
||||
private long calculateRefundAllowance(
|
||||
final Transaction transaction,
|
||||
final MessageFrame initialFrame,
|
||||
final long codeDelegationRefund) {
|
||||
final long selfDestructRefund =
|
||||
getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
|
||||
final long executionRefund =
|
||||
initialFrame.getGasRefund() + selfDestructRefund + codeDelegationRefund;
|
||||
// Integer truncation takes care of the floor calculation needed after the divide.
|
||||
final long maxRefundAllowance =
|
||||
(transaction.getGasLimit() - initialFrame.getRemainingGas()) / getMaxRefundQuotient();
|
||||
return Math.min(executionRefund, maxRefundAllowance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionFloorCost(final Bytes transactionPayload) {
|
||||
return clampedAdd(
|
||||
getMinimumTransactionCost(),
|
||||
tokensInCallData(transactionPayload.size(), zeroBytes(transactionPayload))
|
||||
* TOTAL_COST_FLOOR_PER_TOKEN);
|
||||
}
|
||||
|
||||
private long tokensInCallData(final long payloadSize, final long zeroBytes) {
|
||||
// as defined in https://eips.ethereum.org/EIPS/eip-7623#specification
|
||||
return clampedAdd(zeroBytes, (payloadSize - zeroBytes) * 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.internal.Words.numWords;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The Shanghai gas calculator. */
|
||||
public class ShanghaiGasCalculator extends LondonGasCalculator {
|
||||
|
||||
@@ -39,13 +36,8 @@ public class ShanghaiGasCalculator extends LondonGasCalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreation) {
|
||||
long intrinsicGasCost = super.transactionIntrinsicGasCost(payload, isContractCreation);
|
||||
if (isContractCreation) {
|
||||
return clampedAdd(intrinsicGasCost, initcodeCost(payload.size()));
|
||||
} else {
|
||||
return intrinsicGasCost;
|
||||
}
|
||||
protected long contractCreationCost(final int initCodeLength) {
|
||||
return txCreateExtraGasCost() + initcodeCost(initCodeLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -51,11 +51,11 @@ public class FrontierGasCalculatorTest {
|
||||
|
||||
// Assert
|
||||
assertThat(refund)
|
||||
.isEqualTo(6500L); // 5000 (remaining) + min(1500 (total refund), 19000 (max allowance))
|
||||
.isEqualTo(6000L); // 5000 (remaining) + min(1000 (execution refund), 47500 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCalculateRefundWithMultipleSelfDestructs() {
|
||||
void shouldCalculateRefundWithMultipleSelfDestructsAndIgnoreCodeDelegation() {
|
||||
// Arrange
|
||||
Set<Address> selfDestructs = new HashSet<>();
|
||||
selfDestructs.add(Address.wrap(Bytes.random(20)));
|
||||
@@ -71,7 +71,8 @@ public class FrontierGasCalculatorTest {
|
||||
|
||||
// Assert
|
||||
assertThat(refund)
|
||||
.isEqualTo(52500L); // 5000 (remaining) + min(47500 (total refund), 49500 (max allowance))
|
||||
.isEqualTo(
|
||||
52500L); // 5000 (remaining) + min(49500 (execution refund), 47500 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,7 +88,8 @@ public class FrontierGasCalculatorTest {
|
||||
|
||||
// Assert
|
||||
assertThat(refund)
|
||||
.isEqualTo(60000L); // 20000 (remaining) + min(101000 (total refund), 40000 (max allowance))
|
||||
.isEqualTo(
|
||||
60000L); // 20000 (remaining) + min(101000 (execution refund), 40000 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,9 +101,23 @@ public class FrontierGasCalculatorTest {
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
|
||||
// Act
|
||||
long refund = gasCalculator.calculateGasRefund(transaction, messageFrame, 0L);
|
||||
long refund =
|
||||
gasCalculator.calculateGasRefund(
|
||||
transaction,
|
||||
messageFrame,
|
||||
0L); // 0 (remaining) + min(0 (execution refund), 50000 (max allowance))
|
||||
|
||||
// Assert
|
||||
assertThat(refund).isEqualTo(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionFloorCostShouldAlwaysBeZero() {
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.EMPTY)).isEqualTo(0L);
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.random(256))).isEqualTo(0L);
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x0, Integer.MAX_VALUE)))
|
||||
.isEqualTo(0L);
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x1, Integer.MAX_VALUE)))
|
||||
.isEqualTo(0L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,22 +15,36 @@
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.assertj.core.api.AssertionsForClassTypes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PragueGasCalculatorTest {
|
||||
|
||||
private static final long TARGET_BLOB_GAS_PER_BLOCK_PRAGUE = 0xC0000;
|
||||
private final PragueGasCalculator pragueGasCalculator = new PragueGasCalculator();
|
||||
@Mock private Transaction transaction;
|
||||
@Mock private MessageFrame messageFrame;
|
||||
|
||||
@Test
|
||||
void testPrecompileSize() {
|
||||
@@ -66,4 +80,125 @@ class PragueGasCalculatorTest {
|
||||
pragueGasCalculator.getBlobGasPerBlob()),
|
||||
Arguments.of(sixBlobTargetGas, newTargetCount, sixBlobTargetGas));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCalculateRefundWithCodeDelegationAndNoSelfDestructs() {
|
||||
// Arrange
|
||||
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
|
||||
when(messageFrame.getGasRefund()).thenReturn(1000L);
|
||||
when(messageFrame.getRemainingGas()).thenReturn(5000L);
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 500L);
|
||||
|
||||
// Assert
|
||||
// execution refund = 1000 + 0 (self destructs) + 500 (code delegation) = 1500
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(6500L); // 5000 (remaining) + min(1500 (execution refund), 19000 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCalculateRefundWithMultipleSelfDestructs() {
|
||||
// Arrange
|
||||
Set<Address> selfDestructs = new HashSet<>();
|
||||
selfDestructs.add(Address.wrap(Bytes.random(20)));
|
||||
selfDestructs.add(Address.wrap(Bytes.random(20)));
|
||||
|
||||
when(messageFrame.getSelfDestructs()).thenReturn(selfDestructs);
|
||||
when(messageFrame.getGasRefund()).thenReturn(1000L);
|
||||
when(messageFrame.getRemainingGas()).thenReturn(5000L);
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 1000L);
|
||||
|
||||
// Assert
|
||||
// execution refund = 1000 + 0 (self destructs EIP-3529) + 1000 (code delegation) = 2000
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(7000L); // 5000 (remaining) + min(2000 (execution refund), 1900 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespectMaxRefundAllowance() {
|
||||
// Arrange
|
||||
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
|
||||
when(messageFrame.getGasRefund()).thenReturn(100000L);
|
||||
when(messageFrame.getRemainingGas()).thenReturn(20000L);
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 1000L);
|
||||
|
||||
// Assert
|
||||
// execution refund = 100000 + 1000 (code delegation) = 101000
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(
|
||||
36000L); // 20000 (remaining) + min(101000 (execution refund), 16000 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleZeroValuesCorrectly() {
|
||||
// Arrange
|
||||
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
|
||||
when(messageFrame.getGasRefund()).thenReturn(0L);
|
||||
when(messageFrame.getRemainingGas()).thenReturn(0L);
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund =
|
||||
pragueGasCalculator.calculateGasRefund(
|
||||
transaction,
|
||||
messageFrame,
|
||||
0L); // 0 (remaining) + min(0 (execution refund), 20000 (max allowance))
|
||||
|
||||
// Assert
|
||||
AssertionsForClassTypes.assertThat(refund).isEqualTo(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespectTransactionFloorCost() {
|
||||
// Arrange
|
||||
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
|
||||
when(messageFrame.getGasRefund()).thenReturn(100000L);
|
||||
when(messageFrame.getRemainingGas()).thenReturn(90000L);
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 1000L);
|
||||
|
||||
// Assert
|
||||
// refund allowance = 16000
|
||||
// execution gas used = 100000 (gas limit) - 20000 (remaining gas) - 16000 (refund allowance) =
|
||||
// 64000
|
||||
// floor cost = 21000 (base cost) + 0 (tokensInCallData * floor cost per token) = 21000
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(
|
||||
79000L); // 100000 (gas limit) - max(8000 (execution gas used), 21000 (floor cost))
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionFloorCostShouldBeAtLeastTransactionBaseCost() {
|
||||
// floor cost = 21000 (base cost) + 0
|
||||
AssertionsForClassTypes.assertThat(pragueGasCalculator.transactionFloorCost(Bytes.EMPTY))
|
||||
.isEqualTo(21000);
|
||||
// floor cost = 21000 (base cost) + 256 (tokensInCallData) * 10 (cost per token)
|
||||
AssertionsForClassTypes.assertThat(
|
||||
pragueGasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x0, 256)))
|
||||
.isEqualTo(23560L);
|
||||
// floor cost = 21000 (base cost) + 256 * 4 (tokensInCallData) * 10 (cost per token)
|
||||
AssertionsForClassTypes.assertThat(
|
||||
pragueGasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x1, 256)))
|
||||
.isEqualTo(31240L);
|
||||
// floor cost = 21000 (base cost) + 5 + (6 * 4) (tokensInCallData) * 10 (cost per token)
|
||||
AssertionsForClassTypes.assertThat(
|
||||
pragueGasCalculator.transactionFloorCost(
|
||||
Bytes.fromHexString("0x0001000100010001000101")))
|
||||
.isEqualTo(21290L);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user