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

Ban zero blob transactions #5425

Merged
merged 9 commits into from
May 5, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### Additions and Improvements
- "Big-EOF" (the EOF version initially slotted for Shanghai) has been moved from Cancun to FutureEIPs [#5429](https://github.com/hyperledger/besu/pull/5429)
- EIP-4844: Zero blob transactions are invalid [#5425](https://github.com/hyperledger/besu/pull/5425)

### Bug Fixes
- Fix eth_feeHistory response for the case in which blockCount is higher than highestBlock requested. [#5397](https://github.com/hyperledger/besu/pull/5397)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void eip1559TransactionWithShortWeiVals() {
new TransactionCompleteResult(
new TransactionWithMetadata(
new TransactionTestFixture()
.type(TransactionType.EIP1559)
.maxFeePerGas(Optional.of(Wei.ONE))
.maxPriorityFeePerGas(Optional.of(Wei.ZERO))
.createTransaction(gen.generateKeyPair()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ public Transaction(
if (transactionType.supportsBlob()) {
checkArgument(
versionedHashes.isPresent(), "Must specify blob versioned hashes for blob transaction");
checkArgument(
!versionedHashes.get().isEmpty(), "Blob transaction must have at least one blob");
checkArgument(
maxFeePerDataGas.isPresent(), "Must specify max fee per data gas for blob transaction");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,29 @@

import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.AccessListEntry;
import org.hyperledger.besu.plugin.data.TransactionType;

import java.math.BigInteger;
import java.util.List;
import java.util.Optional;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;

public class TransactionTestFixture {
private static final Hash DEFAULT_VERSIONED_HASH =
Hash.wrap(
Bytes32.wrap(
Bytes.concatenate(Bytes.fromHexString("0x01"), Bytes.repeat((byte) 42, 31))));

private TransactionType transactionType = TransactionType.FRONTIER;

private long nonce = 0;

private Wei gasPrice = Wei.of(5000);
private Optional<Wei> gasPrice = Optional.empty();

private long gasLimit = 5000;

Expand All @@ -45,6 +53,10 @@ public class TransactionTestFixture {

private Optional<Wei> maxPriorityFeePerGas = Optional.empty();
private Optional<Wei> maxFeePerGas = Optional.empty();
private Optional<Wei> maxFeePerDataGas = Optional.empty();

private Optional<List<AccessListEntry>> accessListEntries = Optional.empty();
private Optional<List<Hash>> versionedHashes = Optional.empty();

private Optional<BigInteger> v = Optional.empty();

Expand All @@ -53,18 +65,35 @@ public Transaction createTransaction(final KeyPair keys) {
builder
.type(transactionType)
.gasLimit(gasLimit)
.gasPrice(gasPrice)
.nonce(nonce)
.payload(payload)
.value(value)
.sender(sender);

switch (transactionType) {
case FRONTIER:
builder.gasPrice(gasPrice.orElse(Wei.of(5000)));
break;
case ACCESS_LIST:
builder.gasPrice(gasPrice.orElse(Wei.of(5000)));
builder.accessList(accessListEntries.orElse(List.of()));
break;
case EIP1559:
builder.maxPriorityFeePerGas(maxPriorityFeePerGas.orElse(Wei.of(500)));
builder.maxFeePerGas(maxFeePerGas.orElse(Wei.of(5000)));
builder.accessList(accessListEntries.orElse(List.of()));
break;
case BLOB:
builder.maxPriorityFeePerGas(maxPriorityFeePerGas.orElse(Wei.of(500)));
builder.maxFeePerGas(maxFeePerGas.orElse(Wei.of(5000)));
builder.accessList(accessListEntries.orElse(List.of()));
builder.maxFeePerDataGas(maxFeePerDataGas.orElse(Wei.ONE));
builder.versionedHashes(versionedHashes.orElse(List.of(DEFAULT_VERSIONED_HASH)));
break;
}

to.ifPresent(builder::to);
chainId.ifPresent(builder::chainId);

maxPriorityFeePerGas.ifPresent(builder::maxPriorityFeePerGas);
maxFeePerGas.ifPresent(builder::maxFeePerGas);

v.ifPresent(builder::v);

return builder.signAndBuild(keys);
Expand All @@ -81,7 +110,7 @@ public TransactionTestFixture nonce(final long nonce) {
}

public TransactionTestFixture gasPrice(final Wei gasPrice) {
this.gasPrice = gasPrice;
this.gasPrice = Optional.ofNullable(gasPrice);
return this;
}

Expand Down Expand Up @@ -125,6 +154,21 @@ public TransactionTestFixture maxFeePerGas(final Optional<Wei> maxFeePerGas) {
return this;
}

public TransactionTestFixture maxFeePerDataGas(final Optional<Wei> maxFeePerDataGas) {
this.maxFeePerDataGas = maxFeePerDataGas;
return this;
}

public TransactionTestFixture accessList(final List<AccessListEntry> accessListEntries) {
this.accessListEntries = Optional.ofNullable(accessListEntries);
return this;
}

public TransactionTestFixture versionedHashes(final List<Hash> versionedHashes) {
this.versionedHashes = Optional.ofNullable(versionedHashes);
return this;
}

public TransactionTestFixture v(final Optional<BigInteger> v) {
this.v = v;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,29 @@

import static java.util.stream.Collectors.toUnmodifiableSet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;

import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.AccessListEntry;
import org.hyperledger.besu.plugin.data.TransactionType;

import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;

import com.google.common.base.Suppliers;
import org.junit.Test;

public class TransactionBuilderTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair();

@Test
public void guessTypeCanGuessAllTypes() {
Expand All @@ -50,4 +61,18 @@ public void guessTypeCanGuessAllTypes() {
TransactionType.FRONTIER, TransactionType.ACCESS_LIST, TransactionType.EIP1559
});
}

@Test
public void zeroBlobTransactionIsInvalid() {
try {
new TransactionTestFixture()
.type(TransactionType.BLOB)
.chainId(Optional.of(BigInteger.ONE))
.versionedHashes(List.of())
.createTransaction(senderKeys);
fail();
} catch (IllegalArgumentException iea) {
assertThat(iea).hasMessage("Blob transaction must have at least one blob");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,29 @@ public void shouldRejectTooLargeInitcode() {
.isEqualTo("Initcode size of 49153 exceeds maximum size of 49152");
}

@Test
public void shouldAcceptTransactionWithAtLeastOneBlob() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
GasLimitCalculator.constant(),
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.FRONTIER, TransactionType.EIP1559, TransactionType.BLOB),
0xc000);

var blobTx =
new TransactionTestFixture()
.type(TransactionType.BLOB)
.chainId(Optional.of(BigInteger.ONE))
.createTransaction(senderKeys);
var validationResult =
validator.validate(blobTx, Optional.empty(), transactionValidationParams);

assertThat(validationResult.isValid()).isTrue();
}

private Account accountWithNonce(final long nonce) {
return account(basicTransaction.getUpfrontCost(0L), nonce);
}
Expand Down