Add max_fee_per_data_gas field to transaction (#4970)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/4993/head
Fabio Di Fabio 2 years ago committed by GitHub
parent 7d04ed2e13
commit 617c14a520
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 43
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/JsonRpcResponseUtils.java
  3. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java
  4. 26
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java
  5. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/graphql/AbstractEthGraphQLHttpServiceTest.java
  6. 1
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGasPriceTest.java
  7. 3
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/BaseEeaSendRawTransaction.java
  8. 230
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  9. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java
  10. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  11. 14
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java
  12. 7
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionGoQuorumTest.java
  13. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPluginPrecompiledContractTest.java
  14. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/NewPooledTransactionHashesMessageProcessorTest.java
  15. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionBroadcasterTest.java
  16. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolLegacyTest.java
  17. 1
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/EvmToolCommand.java
  18. 1
      evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java
  19. 2
      plugin-api/build.gradle
  20. 11
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/Transaction.java
  21. 13
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionType.java

@ -23,6 +23,7 @@
- Support for new DATAHASH opcode as part of EIP-4844 [#4823](https://github.com/hyperledger/besu/issues/4823)
- Send only hash announcement for blob transaction type [#4940](https://github.com/hyperledger/besu/pull/4940)
- Add `excess_data_gas` field to block header [#4958](https://github.com/hyperledger/besu/pull/4958)
- Add `max_fee_per_data_gas` field to transaction [#4970](https://github.com/hyperledger/besu/pull/4970)
### Bug Fixes
- Mitigation fix for stale bonsai code storage leading to log rolling issues on contract recreates [#4906](https://github.com/hyperledger/besu/pull/4906)

@ -172,29 +172,26 @@ public class JsonRpcResponseUtils {
final String s) {
final Transaction transaction =
new Transaction(
transactionType,
unsignedLong(nonce),
Optional.of(Wei.fromHexString(gasPrice)),
Optional.empty(),
Optional.empty(),
unsignedLong(gas),
Optional.ofNullable(address(toAddress)),
wei(value),
SignatureAlgorithmFactory.getInstance()
.createSignature(
Bytes.fromHexString(r).toUnsignedBigInteger(),
Bytes.fromHexString(s).toUnsignedBigInteger(),
Bytes.fromHexString(v)
.toUnsignedBigInteger()
.subtract(Transaction.REPLAY_UNPROTECTED_V_BASE)
.byteValueExact()),
bytes(input),
Optional.empty(),
address(fromAddress),
Optional.empty(),
Optional.of(bigInteger(v)),
Optional.empty());
Transaction.builder()
.type(transactionType)
.nonce(unsignedLong(nonce))
.gasPrice(Wei.fromHexString(gasPrice))
.gasLimit(unsignedLong(gas))
.to(address(toAddress))
.value(wei(value))
.signature(
SignatureAlgorithmFactory.getInstance()
.createSignature(
Bytes.fromHexString(r).toUnsignedBigInteger(),
Bytes.fromHexString(s).toUnsignedBigInteger(),
Bytes.fromHexString(v)
.toUnsignedBigInteger()
.subtract(Transaction.REPLAY_UNPROTECTED_V_BASE)
.byteValueExact()))
.payload(bytes(input))
.sender(address(fromAddress))
.v(bigInteger(v))
.build();
return new TransactionCompleteResult(
new TransactionWithMetadata(

@ -343,6 +343,7 @@ public class GraphQLDataFetchers {
transaction.getGasPrice(),
transaction.getMaxPriorityFeePerGas(),
transaction.getMaxFeePerGas(),
transaction.getMaxFeePerDataGas(),
transaction.getGasLimit(),
transaction.getTo(),
transaction.getValue(),
@ -351,6 +352,6 @@ public class GraphQLDataFetchers {
transaction.getSender(),
transaction.getChainId(),
Optional.ofNullable(transaction.getV()),
Optional.empty())));
transaction.getVersionedHashes())));
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
@ -33,8 +34,9 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
"from",
"gas",
"gasPrice",
"maxPriortyFeePerGas",
"maxPriorityFeePerGas",
"maxFeePerGas",
"maxFeePerDataGas",
"hash",
"input",
"nonce",
@ -43,7 +45,8 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
"value",
"v",
"r",
"s"
"s",
"blobVersionedHashes"
})
public class TransactionPendingResult implements TransactionResult {
@ -62,6 +65,9 @@ public class TransactionPendingResult implements TransactionResult {
@JsonInclude(JsonInclude.Include.NON_NULL)
private final String maxFeePerGas;
@JsonInclude(JsonInclude.Include.NON_NULL)
private final String maxFeePerDataGas;
private final String hash;
private final String input;
private final String nonce;
@ -74,6 +80,9 @@ public class TransactionPendingResult implements TransactionResult {
private final String r;
private final String s;
@JsonInclude(JsonInclude.Include.NON_NULL)
private final List<Hash> versionedHashes;
public TransactionPendingResult(final Transaction transaction) {
final TransactionType transactionType = transaction.getType();
this.accessList = transaction.getAccessList().orElse(null);
@ -83,6 +92,8 @@ public class TransactionPendingResult implements TransactionResult {
this.maxPriorityFeePerGas =
transaction.getMaxPriorityFeePerGas().map(Wei::toShortHexString).orElse(null);
this.maxFeePerGas = transaction.getMaxFeePerGas().map(Wei::toShortHexString).orElse(null);
this.maxFeePerDataGas =
transaction.getMaxFeePerDataGas().map(Wei::toShortHexString).orElse(null);
this.gasPrice = transaction.getGasPrice().map(Quantity::create).orElse(maxFeePerGas);
this.hash = transaction.getHash().toString();
this.input = transaction.getPayload().toString();
@ -100,6 +111,7 @@ public class TransactionPendingResult implements TransactionResult {
this.v = Quantity.create(transaction.getV());
this.r = Quantity.create(transaction.getR());
this.s = Quantity.create(transaction.getS());
this.versionedHashes = transaction.getVersionedHashes().orElse(null);
}
@JsonGetter(value = "accessList")
@ -137,6 +149,11 @@ public class TransactionPendingResult implements TransactionResult {
return maxFeePerGas;
}
@JsonGetter(value = "maxFeePerDataGas")
public String getMaxFeePerDataGas() {
return maxFeePerDataGas;
}
@JsonGetter(value = "hash")
public String getHash() {
return hash;
@ -206,4 +223,9 @@ public class TransactionPendingResult implements TransactionResult {
public String getTransactionIndex() {
return null;
}
@JsonGetter(value = "blobVersionedHashes")
public List<Hash> getVersionedHashes() {
return versionedHashes;
}
}

@ -151,6 +151,7 @@ public abstract class AbstractEthGraphQLHttpServiceTest {
.type(TransactionType.FRONTIER)
.nonce(42)
.gasLimit(654321)
.gasPrice(Wei.ONE)
.build(),
true,
Instant.ofEpochSecond(Integer.MAX_VALUE))));

@ -175,6 +175,7 @@ public class EthGasPriceTest {
null,
Bytes.EMPTY,
Address.ZERO,
Optional.empty(),
Optional.empty())),
List.of())));
}

@ -84,6 +84,7 @@ public class BaseEeaSendRawTransaction {
Byte.parseByte("1")),
Bytes.fromBase64String(MOCK_ORION_KEY),
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")),
Optional.empty(),
Optional.empty());
final Transaction PUBLIC_PLUGIN_TRANSACTION =
@ -103,6 +104,7 @@ public class BaseEeaSendRawTransaction {
Byte.parseByte("0")),
Bytes.fromBase64String(MOCK_ORION_KEY),
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")),
Optional.empty(),
Optional.empty());
final Transaction PUBLIC_OFF_CHAIN_TRANSACTION =
@ -122,6 +124,7 @@ public class BaseEeaSendRawTransaction {
Byte.parseByte("1")),
Bytes.fromBase64String(MOCK_ORION_KEY),
Address.wrap(Bytes.fromHexString("0x8411b12666f68ef74cace3615c9d5a377729d03f")),
Optional.empty(),
Optional.empty());
final JsonRpcRequestContext validPrivateForTransactionRequest =

@ -46,6 +46,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.primitives.Longs;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
@ -79,6 +80,7 @@ public class Transaction
private final Optional<Wei> maxPriorityFeePerGas;
private final Optional<Wei> maxFeePerGas;
private final Optional<Wei> maxFeePerDataGas;
private final long gasLimit;
@ -134,6 +136,7 @@ public class Transaction
* @param gasPrice the gas price
* @param maxPriorityFeePerGas the max priority fee per gas
* @param maxFeePerGas the max fee per gas
* @param maxFeePerDataGas the max fee per data gas
* @param gasLimit the gas limit
* @param to the transaction recipient
* @param value the value being transferred to the recipient
@ -157,6 +160,7 @@ public class Transaction
final Optional<Wei> gasPrice,
final Optional<Wei> maxPriorityFeePerGas,
final Optional<Wei> maxFeePerGas,
final Optional<Wei> maxFeePerDataGas,
final long gasLimit,
final Optional<Address> to,
final Wei value,
@ -183,26 +187,30 @@ public class Transaction
"Must not specify access list for transaction not supporting it");
}
if (gasPrice
.or(() -> maxFeePerGas)
.orElse(Wei.ZERO)
.getAsBigInteger()
.multiply(BigInteger.valueOf(gasLimit))
.bitLength()
> 256) {
throw new IllegalArgumentException("Upfront gas cost exceeds UInt256");
}
if (Objects.equals(transactionType, TransactionType.ACCESS_LIST)) {
checkArgument(
maybeAccessList.isPresent(), "Must specify access list for access list transaction");
}
if (versionedHashes.isPresent() || maxFeePerDataGas.isPresent()) {
checkArgument(
transactionType.supportsBlob(),
"Must not specify blob versioned hashes of max fee per data gas for transaction not supporting it");
}
if (transactionType.supportsBlob()) {
checkArgument(
versionedHashes.isPresent(), "Must specify blob versioned hashes for blob transaction");
checkArgument(
maxFeePerDataGas.isPresent(), "Must specify max fee per data gas for blob transaction");
}
this.transactionType = transactionType;
this.nonce = nonce;
this.gasPrice = gasPrice;
this.maxPriorityFeePerGas = maxPriorityFeePerGas;
this.maxFeePerGas = maxFeePerGas;
this.maxFeePerDataGas = maxFeePerDataGas;
this.gasLimit = gasLimit;
this.to = to;
this.value = value;
@ -213,6 +221,10 @@ public class Transaction
this.chainId = chainId;
this.v = v;
this.versionedHashes = versionedHashes;
if (isUpfrontGasCostTooHigh()) {
throw new IllegalArgumentException("Upfront gas cost exceeds UInt256");
}
}
public Transaction(
@ -220,6 +232,7 @@ public class Transaction
final Optional<Wei> gasPrice,
final Optional<Wei> maxPriorityFeePerGas,
final Optional<Wei> maxFeePerGas,
final Optional<Wei> maxFeePerDataGas,
final long gasLimit,
final Optional<Address> to,
final Wei value,
@ -235,6 +248,7 @@ public class Transaction
gasPrice,
maxPriorityFeePerGas,
maxFeePerGas,
maxFeePerDataGas,
gasLimit,
to,
value,
@ -264,6 +278,7 @@ public class Transaction
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
Optional.empty(),
gasLimit,
Optional.of(to),
value,
@ -302,12 +317,14 @@ public class Transaction
final SECPSignature signature,
final Bytes payload,
final Address sender,
final Optional<BigInteger> chainId) {
final Optional<BigInteger> chainId,
final Optional<List<Hash>> versionedHashes) {
this(
nonce,
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
Optional.empty(),
gasLimit,
to,
value,
@ -316,7 +333,7 @@ public class Transaction
sender,
chainId,
Optional.empty(),
Optional.empty());
versionedHashes);
}
/**
@ -354,6 +371,7 @@ public class Transaction
Optional.of(gasPrice),
Optional.empty(),
Optional.empty(),
Optional.empty(),
gasLimit,
to,
value,
@ -405,6 +423,16 @@ public class Transaction
return maxFeePerGas;
}
/**
* Return the transaction max fee per data gas.
*
* @return the transaction max fee per data gas
*/
@Override
public Optional<Wei> getMaxFeePerDataGas() {
return maxFeePerDataGas;
}
/**
* Boolean which indicates the transaction has associated cost data, whether gas price or 1559 fee
* market parameters.
@ -412,7 +440,9 @@ public class Transaction
* @return whether cost params are present
*/
public boolean hasCostParams() {
return Arrays.asList(getGasPrice(), getMaxFeePerGas(), getMaxPriorityFeePerGas()).stream()
return Arrays.asList(
getGasPrice(), getMaxFeePerGas(), getMaxPriorityFeePerGas(), getMaxFeePerDataGas())
.stream()
.flatMap(Optional::stream)
.map(Quantity::getAsBigInteger)
.anyMatch(q -> q.longValue() > 0L);
@ -437,6 +467,7 @@ public class Transaction
})
.orElseGet(() -> getGasPrice().orElse(Wei.ZERO));
}
/**
* Returns the transaction gas limit.
*
@ -566,11 +597,13 @@ public class Transaction
gasPrice.orElse(null),
maxPriorityFeePerGas.orElse(null),
maxFeePerGas.orElse(null),
maxFeePerDataGas.orElse(null),
gasLimit,
to,
value,
payload,
maybeAccessList,
versionedHashes.orElse(null),
chainId);
}
return hashNoSignature;
@ -673,6 +706,15 @@ public class Transaction
String.format("Transaction requires either gasPrice or maxFeePerGas"))));
}
/**
* Check if the upfront gas cost is over the max allowed
*
* @return true is upfront data cost overflow uint256 max value
*/
private boolean isUpfrontGasCostTooHigh() {
return calculateUpfrontGasCost(getMaxGasPrice()).bitLength() > 256;
}
/**
* Calculates the up-front cost for the gas the transaction can use.
*
@ -683,7 +725,9 @@ public class Transaction
if (gasPrice == null || gasPrice.isZero()) {
return Wei.ZERO;
}
var cost = BigInteger.valueOf(getGasLimit()).multiply(gasPrice.getAsBigInteger());
final var cost = calculateUpfrontGasCost(gasPrice);
if (cost.bitLength() > 256) {
return Wei.MAX_WEI;
} else {
@ -691,6 +735,10 @@ public class Transaction
}
}
private BigInteger calculateUpfrontGasCost(final Wei gasPrice) {
return new BigInteger(1, Longs.toByteArray(getGasLimit())).multiply(gasPrice.getAsBigInteger());
}
/**
* Calculates the up-front cost for the transaction.
*
@ -704,6 +752,35 @@ public class Transaction
return getUpfrontGasCost().addExact(getValue());
}
/**
* Return the maximum fee per gas the sender is willing to pay for this transaction.
*
* @return max fee per gas in wei
*/
public Wei getMaxGasPrice() {
return maxFeePerGas.orElseGet(
() ->
gasPrice.orElseThrow(
() ->
new IllegalStateException(
"Transaction requires either gasPrice or maxFeePerGas")));
}
/**
* Calculates the effectiveGasPrice of a transaction on the basis of an {@code Optional<Long>}
* baseFee and handles unwrapping Optional fee parameters. If baseFee is present, effective gas is
* calculated as:
*
* <p>min((baseFeePerGas + maxPriorityFeePerGas), maxFeePerGas)
*
* <p>Otherwise, return gasPrice for legacy transactions.
*
* @param baseFeePerGas optional baseFee from the block header, if we are post-london
* @return the effective gas price.
*/
public final Wei getEffectiveGasPrice(final Optional<Wei> baseFeePerGas) {
return getEffectivePriorityFeePerGas(baseFeePerGas).addExact(baseFeePerGas.orElse(Wei.ZERO));
}
@Override
public TransactionType getType() {
return this.transactionType;
@ -752,11 +829,13 @@ public class Transaction
final Wei gasPrice,
final Wei maxPriorityFeePerGas,
final Wei maxFeePerGas,
final Wei maxFeePerDataGas,
final long gasLimit,
final Optional<Address> to,
final Wei value,
final Bytes payload,
final Optional<List<AccessListEntry>> accessList,
final List<Hash> versionedHashes,
final Optional<BigInteger> chainId) {
if (transactionType.requiresChainId()) {
checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType);
@ -766,7 +845,6 @@ public class Transaction
case FRONTIER:
preimage = frontierPreimage(nonce, gasPrice, gasLimit, to, value, payload, chainId);
break;
case BLOB: // ToDo 4844: specialize for blob when more field will be added for it
case EIP1559:
preimage =
eip1559Preimage(
@ -780,6 +858,21 @@ public class Transaction
chainId,
accessList);
break;
case BLOB:
preimage =
blobPreimage(
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
maxFeePerDataGas,
gasLimit,
to,
value,
payload,
chainId,
accessList,
versionedHashes);
break;
case ACCESS_LIST:
preimage =
accessListPreimage(
@ -842,20 +935,78 @@ public class Transaction
RLP.encode(
rlpOutput -> {
rlpOutput.startList();
rlpOutput.writeBigIntegerScalar(chainId.orElseThrow());
rlpOutput.writeLongScalar(nonce);
rlpOutput.writeUInt256Scalar(maxPriorityFeePerGas);
rlpOutput.writeUInt256Scalar(maxFeePerGas);
rlpOutput.writeLongScalar(gasLimit);
rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY));
rlpOutput.writeUInt256Scalar(value);
rlpOutput.writeBytes(payload);
TransactionEncoder.writeAccessList(rlpOutput, accessList);
eip1559PreimageFields(
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
to,
value,
payload,
chainId,
accessList,
rlpOutput);
rlpOutput.endList();
});
return Bytes.concatenate(Bytes.of(TransactionType.EIP1559.getSerializedType()), encoded);
}
private static void eip1559PreimageFields(
final long nonce,
final Wei maxPriorityFeePerGas,
final Wei maxFeePerGas,
final long gasLimit,
final Optional<Address> to,
final Wei value,
final Bytes payload,
final Optional<BigInteger> chainId,
final Optional<List<AccessListEntry>> accessList,
final RLPOutput rlpOutput) {
rlpOutput.writeBigIntegerScalar(chainId.orElseThrow());
rlpOutput.writeLongScalar(nonce);
rlpOutput.writeUInt256Scalar(maxPriorityFeePerGas);
rlpOutput.writeUInt256Scalar(maxFeePerGas);
rlpOutput.writeLongScalar(gasLimit);
rlpOutput.writeBytes(to.map(Bytes::copy).orElse(Bytes.EMPTY));
rlpOutput.writeUInt256Scalar(value);
rlpOutput.writeBytes(payload);
TransactionEncoder.writeAccessList(rlpOutput, accessList);
}
private static Bytes blobPreimage(
final long nonce,
final Wei maxPriorityFeePerGas,
final Wei maxFeePerGas,
final Wei maxFeePerDataGas,
final long gasLimit,
final Optional<Address> to,
final Wei value,
final Bytes payload,
final Optional<BigInteger> chainId,
final Optional<List<AccessListEntry>> accessList,
final List<Hash> versionedHashes) {
final Bytes encoded =
RLP.encode(
rlpOutput -> {
rlpOutput.startList();
eip1559PreimageFields(
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
to,
value,
payload,
chainId,
accessList,
rlpOutput);
rlpOutput.writeUInt256Scalar(maxFeePerDataGas);
TransactionEncoder.writeBlobVersionedHashes(rlpOutput, versionedHashes);
rlpOutput.endList();
});
return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded);
}
private static Bytes accessListPreimage(
final long nonce,
final Wei gasPrice,
@ -887,6 +1038,7 @@ public class Transaction
&& Objects.equals(this.gasPrice, that.gasPrice)
&& Objects.equals(this.maxPriorityFeePerGas, that.maxPriorityFeePerGas)
&& Objects.equals(this.maxFeePerGas, that.maxFeePerGas)
&& Objects.equals(this.maxFeePerDataGas, that.maxFeePerDataGas)
&& this.nonce == that.nonce
&& Objects.equals(this.payload, that.payload)
&& Objects.equals(this.signature, that.signature)
@ -902,6 +1054,7 @@ public class Transaction
gasPrice,
maxPriorityFeePerGas,
maxFeePerGas,
maxFeePerDataGas,
gasLimit,
to,
value,
@ -927,6 +1080,9 @@ public class Transaction
sb.append("maxFeePerGas=")
.append(getMaxFeePerGas().map(Wei::toShortHexString).get())
.append(", ");
getMaxFeePerDataGas()
.ifPresent(
wei -> sb.append("maxFeePerDataGas=").append(wei.toShortHexString()).append(", "));
}
sb.append("gasLimit=").append(getGasLimit()).append(", ");
if (getTo().isPresent()) sb.append("to=").append(getTo().get()).append(", ");
@ -951,6 +1107,7 @@ public class Transaction
if (getMaxPriorityFeePerGas().isPresent() && getMaxFeePerGas().isPresent()) {
sb.append(getMaxPriorityFeePerGas().map(Wei::toBigInteger).get()).append(", ");
sb.append(getMaxFeePerGas().map(Wei::toBigInteger).get()).append(", ");
getMaxFeePerDataGas().ifPresent(wei -> sb.append(wei.toShortHexString()).append(", "));
}
sb.append(getGasLimit()).append(", ");
sb.append(getValue().toBigInteger()).append(", ");
@ -976,6 +1133,7 @@ public class Transaction
protected Wei maxPriorityFeePerGas;
protected Wei maxFeePerGas;
protected Wei maxFeePerDataGas;
protected long gasLimit = -1L;
@ -1026,6 +1184,11 @@ public class Transaction
return this;
}
public Builder maxFeePerDataGas(final Wei maxFeePerDataGas) {
this.maxFeePerDataGas = maxFeePerDataGas;
return this;
}
public Builder gasLimit(final long gasLimit) {
this.gasLimit = gasLimit;
return this;
@ -1096,6 +1259,7 @@ public class Transaction
Optional.ofNullable(gasPrice),
Optional.ofNullable(maxPriorityFeePerGas),
Optional.ofNullable(maxFeePerGas),
Optional.ofNullable(maxFeePerDataGas),
gasLimit,
to,
value,
@ -1125,29 +1289,15 @@ public class Transaction
gasPrice,
maxPriorityFeePerGas,
maxFeePerGas,
maxFeePerDataGas,
gasLimit,
to,
value,
payload,
accessList,
versionedHashes,
chainId),
keys);
}
}
/**
* Calculates the effectiveGasPrice of a transaction on the basis of an {@code Optional<Long>}
* baseFee and handles unwrapping Optional fee parameters. If baseFee is present, effective gas is
* calculated as:
*
* <p>min((baseFeePerGas + maxPriorityFeePerGas), maxFeePerGas)
*
* <p>Otherwise, return gasPrice for legacy transactions.
*
* @param baseFeePerGas optional baseFee from the block header, if we are post-london
* @return the effective gas price.
*/
public final Wei getEffectiveGasPrice(final Optional<Wei> baseFeePerGas) {
return getEffectivePriorityFeePerGas(baseFeePerGas).addExact(baseFeePerGas.orElse(Wei.ZERO));
}
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.core.encoding;
import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.rlp.RLP;
@ -184,6 +185,11 @@ public class TransactionEncoder {
}
}
public static void writeBlobVersionedHashes(
final RLPOutput rlpOutput, final List<Hash> versionedHashes) {
// ToDo 4844: implement
}
private static void writeSignatureAndV(final Transaction transaction, final RLPOutput out) {
out.writeBigIntegerScalar(transaction.getV());
writeSignature(transaction, out);

@ -32,8 +32,6 @@ import java.math.BigInteger;
import java.util.Optional;
import java.util.Set;
import com.google.common.primitives.Longs;
/**
* Validates a transaction based on Frontier protocol runtime requirements.
*
@ -164,19 +162,6 @@ public class MainnetTransactionValidator {
TransactionInvalidReason.NONCE_OVERFLOW, "Nonce must be less than 2^64-1");
}
if (transaction
.getGasPrice()
.or(transaction::getMaxFeePerGas)
.orElse(Wei.ZERO)
.getAsBigInteger()
.multiply(new BigInteger(1, Longs.toByteArray(transaction.getGasLimit())))
.bitLength()
> 256) {
return ValidationResult.invalid(
TransactionInvalidReason.UPFRONT_FEE_TOO_HIGH,
"gasLimit x price must be less than 2^256");
}
final long intrinsicGasCost =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation())

@ -437,8 +437,9 @@ public class BlockDataGenerator {
.value(Wei.of(positiveLong()))
.payload(payload)
.chainId(BigInteger.ONE)
.maxFeePerDataGas(Wei.of(1))
.versionedHashes(List.of(Hash.fromHexStringLenient("0x29")))
.signAndBuild(generateKeyPair());
// ToDo 4844: specialize for blob when more field will be added for it
}
private Transaction frontierTransaction(final Bytes payload, final Address to) {
@ -666,6 +667,8 @@ public class BlockDataGenerator {
private Optional<Hash> withdrawalsRoot = Optional.empty();
private Optional<Optional<Wei>> maybeMaxFeePerDataGas = Optional.empty();
public static BlockOptions create() {
return new BlockOptions();
}
@ -845,5 +848,14 @@ public class BlockDataGenerator {
this.withdrawalsRoot = Optional.of(withdrawalsRoot);
return this;
}
public Optional<Wei> getMaxFeePerDataGas(final Optional<Wei> defaultValue) {
return maybeMaxFeePerDataGas.orElse(defaultValue);
}
public BlockOptions setMaxFeePerDataGas(final Optional<Wei> maxFeePerDataGas) {
this.maybeMaxFeePerDataGas = Optional.of(maxFeePerDataGas);
return this;
}
}
}

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.core;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
@ -70,13 +71,15 @@ public class TransactionGoQuorumTest {
@Test
public void givenTransactionWithChainId_assertThatIsGoQuorumFlagIsFalse() {
final Transaction transaction = Transaction.builder().chainId(BigInteger.valueOf(0)).build();
final Transaction transaction =
Transaction.builder().chainId(BigInteger.valueOf(0)).gasPrice(Wei.ZERO).build();
assertThat(transaction.isGoQuorumPrivateTransaction(goQuorumCompatibilityMode)).isFalse();
}
@Test
public void givenTransactionWithoutChainIdAndV37_assertThatIsGoQuorumFlagIsTrue() {
final Transaction transaction = Transaction.builder().v(BigInteger.valueOf(37)).build();
final Transaction transaction =
Transaction.builder().v(BigInteger.valueOf(37)).gasPrice(Wei.ZERO).build();
assertThat(transaction.isGoQuorumPrivateTransaction(goQuorumCompatibilityMode)).isTrue();
}

@ -28,6 +28,7 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.enclave.EnclaveFactory;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
@ -178,7 +179,8 @@ public class PrivacyPluginPrecompiledContractTest {
final Bytes payload = convertPrivateTransactionToBytes(privateTransaction);
final Transaction transaction = Transaction.builder().payload(payload).build();
final Transaction transaction =
Transaction.builder().payload(payload).gasPrice(Wei.ZERO).build();
when(messageFrame.getContextVariable(KEY_TRANSACTION)).thenReturn(transaction);

@ -115,10 +115,8 @@ public class NewPooledTransactionHashesMessageProcessorTest {
@Test
public void shouldNotAddAlreadyPresentTransactions() {
when(transactionPool.getTransactionByHash(hash1))
.thenReturn(Optional.of(Transaction.builder().build()));
when(transactionPool.getTransactionByHash(hash2))
.thenReturn(Optional.of(Transaction.builder().build()));
when(transactionPool.getTransactionByHash(hash1)).thenReturn(Optional.of(transaction1));
when(transactionPool.getTransactionByHash(hash2)).thenReturn(Optional.of(transaction2));
messageHandler.processNewPooledTransactionHashesMessage(
peer1,

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.transactions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction.toTransactionList;
import static org.hyperledger.besu.plugin.data.TransactionType.BLOB;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@ -244,7 +245,7 @@ public class TransactionBroadcasterTest {
when(ethPeers.streamAvailablePeers())
.thenReturn(Stream.concat(eth65Peers.stream(), Stream.of(ethPeerNoEth65)));
List<Transaction> txs = toTransactionList(setupTransactionPool(TransactionType.BLOB, 0, 1));
List<Transaction> txs = toTransactionList(setupTransactionPool(BLOB, 0, 1));
txBroadcaster.onTransactionsAdded(txs);
@ -276,8 +277,7 @@ public class TransactionBroadcasterTest {
// 1 hash only broadcast transaction type
List<Transaction> fullBroadcastTxs =
toTransactionList(setupTransactionPool(TransactionType.EIP1559, 0, 1));
List<Transaction> hashBroadcastTxs =
toTransactionList(setupTransactionPool(TransactionType.BLOB, 0, 1));
List<Transaction> hashBroadcastTxs = toTransactionList(setupTransactionPool(BLOB, 0, 1));
List<Transaction> mixedTxs = new ArrayList<>(fullBroadcastTxs);
mixedTxs.addAll(hashBroadcastTxs);

@ -215,9 +215,9 @@ public class TransactionPoolLegacyTest extends AbstractTransactionPoolTest {
when(transactionValidator.getGoQuorumCompatibilityMode()).thenReturn(true);
final Transaction transaction37 =
Transaction.builder().v(BigInteger.valueOf(37)).value(Wei.ONE).build();
Transaction.builder().v(BigInteger.valueOf(37)).gasPrice(Wei.ZERO).value(Wei.ONE).build();
final Transaction transaction38 =
Transaction.builder().v(BigInteger.valueOf(38)).value(Wei.ONE).build();
Transaction.builder().v(BigInteger.valueOf(38)).gasPrice(Wei.ZERO).value(Wei.ONE).build();
final ValidationResult<TransactionInvalidReason> result37 =
transactionPool.addLocalTransaction(transaction37);

@ -236,6 +236,7 @@ public class EvmToolCommand implements Runnable {
null,
callData,
sender,
Optional.empty(),
Optional.empty());
final long intrinsicGasCost =

@ -1349,6 +1349,7 @@ public class MessageFrame {
private Multimap<Address, Bytes32> accessListWarmStorage = HashMultimap.create();
private Optional<List<Hash>> versionedHashes;
/**
* Sets Type.
*

@ -66,7 +66,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = '3JnYE8JbJPkPsNkmlUUFCaUU8Rk1IdfJ8lMuJOBpt5s='
knownHash = '0SzhbPysuW7BDEZzHE4szFg9gPlNu5nfdoKP68Xl4T0='
}
check.dependsOn('checkAPIChanges')

@ -76,6 +76,17 @@ public interface Transaction {
return Optional.empty();
}
/**
* A scalar value equal to the max number of Wei to be paid for data gas, as specified in
* EIP-4844.
*
* @return the quantity of Wei for fee per data gas.
*/
@Unstable
default Optional<? extends Quantity> getMaxFeePerDataGas() {
return Optional.empty();
}
/**
* A scalar value equal to the maximum amount of gas that should be used in executing this
* transaction. This is paid up-front, before any computation is done and may not be increased

@ -80,7 +80,7 @@ public enum TransactionType {
}
/**
* Does transaction type Supports access list.
* Does transaction type supports access list.
*
* @return the boolean
*/
@ -89,7 +89,7 @@ public enum TransactionType {
}
/**
* Does transaction type Supports 1559 fee market.
* Does transaction type supports EIP-1559 fee market.
*
* @return the boolean
*/
@ -105,4 +105,13 @@ public enum TransactionType {
public boolean requiresChainId() {
return !this.equals(FRONTIER);
}
/**
* Does transaction type supports data blobs.
*
* @return the boolean
*/
public boolean supportsBlob() {
return this.equals(BLOB);
}
}

Loading…
Cancel
Save