diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java index cb14101acd9..c41bf0730d5 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Hash.java @@ -61,6 +61,10 @@ public static Hash hash(final Bytes value) { return new Hash(keccak256(value)); } + public static Hash sha256(final Bytes value) { + return new Hash(org.hyperledger.besu.crypto.Hash.sha256(value)); + } + /** * Wrap bytes to hash. * diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java index 7c2f22ed1aa..8b1d2893c8f 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionPendingResult.java @@ -15,9 +15,9 @@ 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.core.blobs.VersionedHash; import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder; import org.hyperledger.besu.evm.AccessListEntry; import org.hyperledger.besu.plugin.data.TransactionType; @@ -81,7 +81,7 @@ public class TransactionPendingResult implements TransactionResult { private final String s; @JsonInclude(JsonInclude.Include.NON_NULL) - private final List versionedHashes; + private final List versionedHashes; public TransactionPendingResult(final Transaction transaction) { final TransactionType transactionType = transaction.getType(); @@ -223,7 +223,7 @@ public String getTransactionIndex() { } @JsonGetter(value = "blobVersionedHashes") - public List getVersionedHashes() { + public List getVersionedHashes() { return versionedHashes; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java index 30a35389afb..8aa06e4bf05 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java @@ -23,6 +23,6 @@ static GasLimitCalculator constant() { } default long currentDataGasLimit() { - return 0L; + return 1 << 19; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index a2dd818c91b..e7df83ed1ee 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.ethereum.core.blobs.BlobsWithCommitments; import org.hyperledger.besu.ethereum.core.blobs.KZGCommitment; import org.hyperledger.besu.ethereum.core.blobs.KZGProof; +import org.hyperledger.besu.ethereum.core.blobs.VersionedHash; import org.hyperledger.besu.ethereum.core.encoding.BlobTransactionDecoder; import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder; import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder; @@ -112,7 +113,7 @@ public class Transaction private final TransactionType transactionType; private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithmFactory.getInstance(); - private final Optional> versionedHashes; + private final Optional> versionedHashes; private final Optional blobsWithCommitments; @@ -166,7 +167,7 @@ public Transaction( final Optional> maybeAccessList, final Address sender, final Optional chainId, - final Optional> versionedHashes, + final Optional> versionedHashes, final Optional blobsWithCommitments) { if (transactionType.requiresChainId()) { @@ -235,7 +236,7 @@ public Transaction( final Bytes payload, final Address sender, final Optional chainId, - final Optional> versionedHashes, + final Optional> versionedHashes, final Optional blobsWithCommitments) { this( TransactionType.FRONTIER, @@ -265,7 +266,7 @@ public Transaction( final SECPSignature signature, final Bytes payload, final Optional chainId, - final Optional> versionedHashes, + final Optional> versionedHashes, final Optional blobsWithCommitments) { this( TransactionType.FRONTIER, @@ -313,7 +314,7 @@ public Transaction( final Bytes payload, final Address sender, final Optional chainId, - final Optional> versionedHashes) { + final Optional> versionedHashes) { this( nonce, Optional.of(gasPrice), @@ -359,7 +360,7 @@ public Transaction( final Address sender, final Optional chainId, final Optional maxFeePerDataGas, - final Optional> versionedHashes, + final Optional> versionedHashes, final Optional blobsWithCommitments) { this( nonce, @@ -805,7 +806,7 @@ public TransactionType getType() { return this.transactionType; } - public Optional> getVersionedHashes() { + public Optional> getVersionedHashes() { return versionedHashes; } @@ -849,7 +850,7 @@ private static Bytes32 computeSenderRecoveryHash( final Wei value, final Bytes payload, final Optional> accessList, - final List versionedHashes, + final List versionedHashes, final Optional chainId) { if (transactionType.requiresChainId()) { checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType); @@ -998,7 +999,7 @@ private static Bytes blobPreimage( final Bytes payload, final Optional chainId, final Optional> accessList, - final List versionedHashes) { + final List versionedHashes) { final Bytes encoded = RLP.encode( @@ -1181,7 +1182,7 @@ public static class Builder { protected Optional chainId = Optional.empty(); - protected List versionedHashes = null; + protected List versionedHashes = null; private BlobsWithCommitments blobsWithCommitments; public Builder type(final TransactionType transactionType) { @@ -1257,7 +1258,7 @@ public Builder signature(final SECPSignature signature) { return this; } - public Builder versionedHashes(final List versionedHashes) { + public Builder versionedHashes(final List versionedHashes) { this.versionedHashes = versionedHashes; return this; } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/blobs/VersionedHash.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/blobs/VersionedHash.java new file mode 100644 index 00000000000..76616cf492e --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/blobs/VersionedHash.java @@ -0,0 +1,69 @@ +/* + * Copyright Hyperledger Besu Contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.hyperledger.besu.ethereum.core.blobs; + +import org.hyperledger.besu.datatypes.Hash; + +import java.util.Objects; + +import org.apache.tuweni.bytes.Bytes32; + +public class VersionedHash { + + private final byte versionId; + Bytes32 hashish; + + public VersionedHash(final byte versionId, final Hash hash) { + if (versionId != 1) { + throw new IllegalArgumentException("Only supported hash version is 0x01, sha256 hash."); + } + + this.versionId = versionId; + this.hashish = hash; + } + + public VersionedHash(final Bytes32 typedHash) { + byte versionId = typedHash.get(0); + if (versionId != 1) { + throw new IllegalArgumentException("Only supported hash version is 0x01, sha256 hash."); + } + this.versionId = versionId; + this.hashish = typedHash; + } + + public Bytes32 toBytes() { + byte[] bytes = hashish.toArray(); + bytes[0] = versionId; + return Bytes32.wrap(bytes); + } + + public byte getVersionId() { + return versionId; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + VersionedHash that = (VersionedHash) o; + return getVersionId() == that.getVersionId() && Objects.equals(this.toBytes(), that.toBytes()); + } + + @Override + public int hashCode() { + return Objects.hash(getVersionId(), hashish); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionDecoder.java index 1c6a3989ce3..f6525e26613 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionDecoder.java @@ -17,12 +17,12 @@ import org.hyperledger.besu.crypto.SignatureAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; 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.core.blobs.Blob; import org.hyperledger.besu.ethereum.core.blobs.KZGCommitment; import org.hyperledger.besu.ethereum.core.blobs.KZGProof; +import org.hyperledger.besu.ethereum.core.blobs.VersionedHash; import org.hyperledger.besu.ethereum.rlp.RLPInput; import org.hyperledger.besu.ethereum.rlp.RLPOutput; import org.hyperledger.besu.evm.AccessListEntry; @@ -82,7 +82,7 @@ private static void readTransactionPayloadInner( })) .maxFeePerDataGas(Wei.of(input.readUInt256Scalar())) .versionedHashes( - input.readList(versionedHashes -> Hash.wrap(versionedHashes.readBytes32()))); + input.readList(versionedHashes -> new VersionedHash(versionedHashes.readBytes32()))); final byte recId = (byte) input.readIntScalar(); builder.signature( @@ -109,7 +109,7 @@ private static Transaction readNetworkWrapperInner(final RLPInput input) { } public static void writeBlobVersionedHashes( - final RLPOutput rlpOutput, final List versionedHashes) { - rlpOutput.writeList(versionedHashes, (h, out) -> out.writeBytes(h)); + final RLPOutput rlpOutput, final List versionedHashes) { + rlpOutput.writeList(versionedHashes, (h, out) -> out.writeBytes(h.toBytes())); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionEncoder.java index 6f3dd68113e..a6d23941e2c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/BlobTransactionEncoder.java @@ -41,7 +41,13 @@ public static void encodeEIP4844(final Transaction transaction, final RLPOutput TransactionEncoder.writeAccessList(out, transaction.getAccessList()); out.writeUInt256Scalar(transaction.getMaxFeePerDataGas().orElseThrow()); out.startList(); - transaction.getVersionedHashes().get().forEach(out::writeBytes); + transaction + .getVersionedHashes() + .get() + .forEach( + vh -> { + out.writeBytes(vh.toBytes()); + }); out.endList(); TransactionEncoder.writeSignatureAndRecoveryId(transaction, out); out.endList(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index c040d896e27..9853a7ff4b3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.blobs.VersionedHash; import org.hyperledger.besu.ethereum.core.feemarket.CoinbaseFeePriceCalculator; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater; @@ -377,8 +378,15 @@ public TransactionProcessingResult processTransaction( .blockHashLookup(blockHashLookup) .contextVariables(contextVariablesBuilder.build()) .accessListWarmAddresses(addressList) - .accessListWarmStorage(storageList) - .versionedHashes(transaction.getVersionedHashes()); + .accessListWarmStorage(storageList); + + if (transaction.getVersionedHashes().isPresent()) { + commonMessageFrameBuilder.versionedHashes( + Optional.of( + transaction.getVersionedHashes().get().stream() + .map(VersionedHash::toBytes) + .toList())); + } final MessageFrame initialFrame; if (transaction.isContractCreation()) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index f18c9d8f3b3..1459fce8df1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -1,5 +1,5 @@ /* - * Copyright ConsenSys AG. + * Copyright Hyperledger Besu Contributors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.blobs.BlobsWithCommitments; import org.hyperledger.besu.ethereum.core.blobs.KZGCommitment; import org.hyperledger.besu.ethereum.core.blobs.KZGProof; +import org.hyperledger.besu.ethereum.core.blobs.VersionedHash; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.evm.account.Account; @@ -142,14 +143,6 @@ public ValidationResult validate( } } - if (transaction.getType().supportsBlob() && transaction.getBlobsWithCommitments().isPresent()) { - final ValidationResult blobsResult = - validateTransactionsBlobs(transaction); - if (!blobsResult.isValid()) { - return blobsResult; - } - } - final TransactionType transactionType = transaction.getType(); if (!acceptedTransactionTypes.contains(transactionType)) { return ValidationResult.invalid( @@ -367,35 +360,27 @@ public ValidationResult validateTransactionsBlobs( "transaction blobs and commitments are not the same size"); } - List commitments = blobsWithCommitments.getKzgCommitments(); - for (KZGCommitment commitment : commitments) { - if (commitment.getData().get(0) != BLOB_COMMITMENT_VERSION_KZG) { - return ValidationResult.invalid( - TransactionInvalidReason.INVALID_BLOBS, - "transaction blobs commitment version is not supported"); - } - } if (transaction.getVersionedHashes().isEmpty()) { return ValidationResult.invalid( TransactionInvalidReason.INVALID_BLOBS, "transaction versioned hashes are empty, cannot verify without versioned hashes"); } - final List versionedHashes = transaction.getVersionedHashes().get(); + final List versionedHashes = transaction.getVersionedHashes().get(); for (int i = 0; i < versionedHashes.size(); i++) { final KZGCommitment commitment = blobsWithCommitments.getKzgCommitments().get(i); - final Hash versionedHash = versionedHashes.get(i); + final VersionedHash versionedHash = versionedHashes.get(i); - if (versionedHash.get(0) != BLOB_COMMITMENT_VERSION_KZG) { + if (versionedHash.getVersionId() != BLOB_COMMITMENT_VERSION_KZG) { return ValidationResult.invalid( TransactionInvalidReason.INVALID_BLOBS, "transaction blobs commitment version is not supported. Expected " + BLOB_COMMITMENT_VERSION_KZG + ", found " - + versionedHash.get(0)); + + versionedHash.getVersionId()); } - final Hash calculatedVersionedHash = hashCommitment(commitment); + final VersionedHash calculatedVersionedHash = hashCommitment(commitment); if (!calculatedVersionedHash.equals(versionedHash)) { return ValidationResult.invalid( TransactionInvalidReason.INVALID_BLOBS, @@ -422,11 +407,11 @@ public ValidationResult validateTransactionsBlobs( .orElseThrow(); final boolean kzgVerification = - CKZG4844JNI.verifyAggregateKzgProof( + CKZG4844JNI.verifyBlobKzgProofBatch( blobs.toArrayUnsafe(), kzgCommitments.toArrayUnsafe(), - blobsWithCommitments.getBlobs().size(), - kzgProofs.toArrayUnsafe()); + kzgProofs.toArrayUnsafe(), + blobsWithCommitments.getBlobs().size()); if (!kzgVerification) { return ValidationResult.invalid( @@ -437,7 +422,7 @@ public ValidationResult validateTransactionsBlobs( return ValidationResult.valid(); } - private Hash hashCommitment(final KZGCommitment commitment) { + private VersionedHash hashCommitment(final KZGCommitment commitment) { final SHA256Digest digest = new SHA256Digest(); digest.update(commitment.getData().toArrayUnsafe(), 0, commitment.getData().size()); @@ -446,7 +431,7 @@ private Hash hashCommitment(final KZGCommitment commitment) { digest.doFinal(dig, 0); dig[0] = BLOB_COMMITMENT_VERSION_KZG; - return Hash.wrap(Bytes32.wrap(dig)); + return new VersionedHash(Bytes32.wrap(dig)); } private boolean isSenderAllowed( diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index 000d2e45e4c..45086d320f8 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.core.blobs.VersionedHash; import org.hyperledger.besu.ethereum.mainnet.BodyValidation; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; @@ -442,7 +443,7 @@ private Transaction blobTransaction(final Bytes payload, final Address to) { .payload(payload) .chainId(BigInteger.ONE) .maxFeePerDataGas(Wei.of(1)) - .versionedHashes(List.of(Hash.fromHexStringLenient("0x29"))) + .versionedHashes(List.of(new VersionedHash(Bytes32.fromHexStringLenient("0x29")))) .signAndBuild(generateKeyPair()); } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java index 2bc133807f8..f9219eac17d 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/TransactionTestFixture.java @@ -16,8 +16,9 @@ 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.ethereum.core.blobs.BlobsWithCommitments; +import org.hyperledger.besu.ethereum.core.blobs.VersionedHash; import org.hyperledger.besu.evm.AccessListEntry; import org.hyperledger.besu.plugin.data.TransactionType; @@ -49,8 +50,12 @@ public class TransactionTestFixture { private Optional maxPriorityFeePerGas = Optional.empty(); private Optional maxFeePerGas = Optional.empty(); + private Optional maxFeePerDataGas = Optional.empty(); + private List accessList = null; - private Optional> versionedHashes = Optional.empty(); + private Optional> versionedHashes = Optional.empty(); + + private Optional blobs = Optional.empty(); public Transaction createTransaction(final KeyPair keys) { final Transaction.Builder builder = Transaction.builder(); @@ -69,8 +74,12 @@ public Transaction createTransaction(final KeyPair keys) { maxPriorityFeePerGas.ifPresent(builder::maxPriorityFeePerGas); maxFeePerGas.ifPresent(builder::maxFeePerGas); + maxFeePerDataGas.ifPresent(builder::maxFeePerDataGas); versionedHashes.ifPresent(builder::versionedHashes); - + blobs.ifPresent( + bwc -> { + builder.kzgBlobs(bwc.getKzgCommitments(), bwc.getBlobs(), bwc.getKzgProofs()); + }); return builder.signAndBuild(keys); } @@ -129,12 +138,23 @@ public TransactionTestFixture maxFeePerGas(final Optional maxFeePerGas) { return this; } + public TransactionTestFixture maxFeePerDataGas(final Optional maxFeePerDataGas) { + this.maxFeePerDataGas = maxFeePerDataGas; + return this; + } + + public TransactionTestFixture blobsWithCommitments(final Optional blobs) { + this.blobs = blobs; + return this; + } + public TransactionTestFixture accessList(final List accessList) { this.accessList = accessList; return this; } - public TransactionTestFixture versionedHashes(final Optional> versionedHashes) { + public TransactionTestFixture versionedHashes( + final Optional> versionedHashes) { this.versionedHashes = versionedHashes; return this; } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java index eea4abe4665..82a27fb529d 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionBuilderTest.java @@ -67,6 +67,9 @@ public void zeroBlobTransactionIsInvalid() { .type(TransactionType.BLOB) .chainId(Optional.of(BigInteger.ONE)) .versionedHashes(Optional.of(List.of())) + .maxFeePerGas(Optional.of(Wei.of(5))) + .maxPriorityFeePerGas(Optional.of(Wei.of(5))) + .maxFeePerDataGas(Optional.of(Wei.of(5))) .createTransaction(senderKeys); fail(); } catch (IllegalArgumentException iea) { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/BodyValidationTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/BodyValidationTest.java index 266d992ba9d..7255d53918f 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/BodyValidationTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/BodyValidationTest.java @@ -30,7 +30,7 @@ public final class BodyValidationTest { @Test public void calculateTransactionsRoot() throws IOException { - for (final int block : Arrays.asList(300006, 4400002, 62717)) { + for (final int block : Arrays.asList(300006, 4400002)) { final BlockHeader header = ValidationTestUtils.readHeader(block); final BlockBody body = ValidationTestUtils.readBody(block); final Bytes32 transactionRoot = BodyValidation.transactionsRoot(body.getTransactions()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 868c39d5370..a8f40c0b7d5 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -15,12 +15,14 @@ package org.hyperledger.besu.ethereum.mainnet; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.INVALID_TRANSACTION_FORMAT; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.MAX_PRIORITY_FEE_PER_GAS_EXCEEDS_MAX_FEE_PER_GAS; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -36,19 +38,29 @@ import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.TransactionFilter; import org.hyperledger.besu.ethereum.core.TransactionTestFixture; +import org.hyperledger.besu.ethereum.core.blobs.Blob; +import org.hyperledger.besu.ethereum.core.blobs.BlobsWithCommitments; +import org.hyperledger.besu.ethereum.core.blobs.KZGCommitment; +import org.hyperledger.besu.ethereum.core.blobs.KZGProof; +import org.hyperledger.besu.ethereum.core.blobs.VersionedHash; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.plugin.data.TransactionType; +import java.io.IOException; +import java.io.InputStream; import java.math.BigInteger; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import com.google.common.base.Suppliers; +import ethereum.ckzg4844.CKZG4844JNI; import org.apache.tuweni.bytes.Bytes; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -505,7 +517,9 @@ public void shouldRejectTooLargeInitcode() { } @Test + @Ignore("This test is ignored because it requires a native library to be loaded") public void shouldAcceptTransactionWithAtLeastOneBlob() { + when(gasCalculator.dataGasCost(anyInt())).thenReturn(2L); final MainnetTransactionValidator validator = new MainnetTransactionValidator( gasCalculator, @@ -516,13 +530,49 @@ public void shouldAcceptTransactionWithAtLeastOneBlob() { Set.of(TransactionType.FRONTIER, TransactionType.EIP1559, TransactionType.BLOB), 0xc000); + byte[] rawMaterial = {}; + try (InputStream readme = + this.getClass() + .getResourceAsStream( + "/org/hyperledger/besu/ethereum/core/encoding/BlobDataFixture.bin")) { + rawMaterial = readme.readAllBytes(); + } catch (IOException e) { + fail("Failed to read blob file", e); + } + + Bytes commitment = Bytes.EMPTY; + try { + CKZG4844JNI.loadNativeLibrary(CKZG4844JNI.Preset.MAINNET); + CKZG4844JNI.loadTrustedSetupFromResource("/kzg-trusted-setups/mainnet.txt", this.getClass()); + commitment = Bytes.wrap(CKZG4844JNI.blobToKzgCommitment(rawMaterial)); + } catch (Exception e) { + fail("Failed to compute commitment", e); + } + assertThat(commitment.size()).isEqualTo(48); + Bytes proof = Bytes.wrap(CKZG4844JNI.computeBlobKzgProof(rawMaterial, commitment.toArray())); + VersionedHash versionedHash = new VersionedHash((byte) 1, Hash.sha256(commitment)); + BlobsWithCommitments bwc = + new BlobsWithCommitments( + List.of(new KZGCommitment(commitment)), + List.of(new Blob(Bytes.wrap(rawMaterial))), + List.of(new KZGProof(proof))); + var blobTx = new TransactionTestFixture() .type(TransactionType.BLOB) .chainId(Optional.of(BigInteger.ONE)) + .maxFeePerGas(Optional.of(Wei.of(15))) + .maxFeePerDataGas(Optional.of(Wei.of(128))) + .maxPriorityFeePerGas(Optional.of(Wei.of(1))) + .blobsWithCommitments(Optional.of(bwc)) + .versionedHashes(Optional.of(List.of(versionedHash))) .createTransaction(senderKeys); var validationResult = validator.validate(blobTx, Optional.empty(), transactionValidationParams); + if (!validationResult.isValid()) { + System.out.println( + validationResult.getInvalidReason() + " " + validationResult.getErrorMessage()); + } assertThat(validationResult.isValid()).isTrue(); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index 94902bc7ae9..78e9125133f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -240,7 +240,7 @@ public enum Type { private Optional revertReason; private final Map contextVariables; - private final Optional> versionedHashes; + private final Optional> versionedHashes; private final Table transientStorage = HashBasedTable.create(); @@ -285,7 +285,7 @@ private MessageFrame( final int maxStackSize, final Set
accessListWarmAddresses, final Multimap accessListWarmStorage, - final Optional> versionedHashes) { + final Optional> versionedHashes) { this.type = type; this.messageFrameStack = messageFrameStack; this.parentMessageFrame = messageFrameStack.peek(); @@ -1359,7 +1359,7 @@ public void commitTransientStorage() { * * @return optional list of hashes */ - public Optional> getVersionedHashes() { + public Optional> getVersionedHashes() { return versionedHashes; } @@ -1397,7 +1397,7 @@ public static class Builder { private Set
accessListWarmAddresses = emptySet(); private Multimap accessListWarmStorage = HashMultimap.create(); - private Optional> versionedHashes; + private Optional> versionedHashes; /** * Sets Type. @@ -1669,7 +1669,7 @@ public Builder accessListWarmStorage(final Multimap accessList * @param versionedHashes the Optional list of versioned hashes * @return the builder */ - public Builder versionedHashes(final Optional> versionedHashes) { + public Builder versionedHashes(final Optional> versionedHashes) { this.versionedHashes = versionedHashes; return this; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataHashOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataHashOperation.java index e6fbe9b3f81..cc4c3f073e2 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/DataHashOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/DataHashOperation.java @@ -14,7 +14,6 @@ */ package org.hyperledger.besu.evm.operation; -import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; @@ -22,6 +21,7 @@ import java.util.List; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; /** @@ -50,9 +50,9 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { Bytes blobIndex = frame.popStackItem(); UInt256 blobIndexUInt256 = UInt256.fromBytes(blobIndex); if (frame.getVersionedHashes().isPresent()) { - List versionedHashes = frame.getVersionedHashes().get(); + List versionedHashes = frame.getVersionedHashes().get(); if (blobIndexUInt256.lessThan(UInt256.valueOf(versionedHashes.size()))) { - Hash requested = versionedHashes.get(blobIndexUInt256.toBigInteger().intValue()); + Bytes32 requested = versionedHashes.get(blobIndexUInt256.toBigInteger().intValue()); frame.pushStackItem(requested); } else { frame.pushStackItem(Bytes.EMPTY); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operations/DataHashOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operations/DataHashOperationTest.java index 6d2e49e0cd7..c0edd1e0c0f 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operations/DataHashOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operations/DataHashOperationTest.java @@ -43,7 +43,7 @@ public class DataHashOperationTest { @Test public void putsHashOnStack() { Hash version0Hash = Hash.fromHexStringLenient("0xcafebabeb0b0facedeadbeef"); - List versionedHashes = Arrays.asList(version0Hash); + List versionedHashes = Arrays.asList(version0Hash); DataHashOperation getHash = new DataHashOperation(new LondonGasCalculator()); MessageFrame frame = mock(MessageFrame.class); when(frame.popStackItem()).thenReturn(Bytes.of(0)); @@ -80,7 +80,7 @@ public void pushesZeroOnBloblessTx() { @Test public void pushZeroOnVersionIndexOutOFBounds() { Hash version0Hash = Hash.fromHexStringLenient("0xcafebabeb0b0facedeadbeef"); - List versionedHashes = Arrays.asList(version0Hash); + List versionedHashes = Arrays.asList(version0Hash); DataHashOperation getHash = new DataHashOperation(new ShanghaiGasCalculator()); MessageFrame frame = mock(MessageFrame.class); when(frame.popStackItem()).thenReturn(Bytes.of(1)); @@ -95,7 +95,7 @@ public void pushZeroOnVersionIndexOutOFBounds() { @Test public void pushZeroWhenPopsMissingUint256SizedIndex() { Hash version0Hash = Hash.fromHexStringLenient("0xcafebabeb0b0facedeadbeef"); - List versionedHashes = Arrays.asList(version0Hash); + List versionedHashes = Arrays.asList(version0Hash); DataHashOperation getHash = new DataHashOperation(new ShanghaiGasCalculator()); MessageFrame frame = mock(MessageFrame.class); when(frame.popStackItem()).thenReturn(Bytes32.repeat((byte) 0x2C)); diff --git a/evm/src/test/java/org/hyperledger/besu/evm/precompile/KZGPointEvalPrecompileContractTest.java b/evm/src/test/java/org/hyperledger/besu/evm/precompile/KZGPointEvalPrecompileContractTest.java deleted file mode 100644 index e56ed49e5ed..00000000000 --- a/evm/src/test/java/org/hyperledger/besu/evm/precompile/KZGPointEvalPrecompileContractTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Hyperledger Besu Contributors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.evm.precompile; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -import org.hyperledger.besu.evm.frame.MessageFrame; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import org.apache.tuweni.bytes.Bytes; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -public class KZGPointEvalPrecompileContractTest { - private static KZGPointEvalPrecompiledContract contract; - private final MessageFrame toRun = mock(MessageFrame.class); - - @BeforeAll - public static void init() { - KZGPointEvalPrecompiledContract.init("foo"); - contract = new KZGPointEvalPrecompiledContract(); - } - - @AfterAll - public static void tearDown() { - contract.tearDown(); - } - - @ParameterizedTest(name = "{index}") - @MethodSource("getPointEvaluationPrecompileTestVectors") - public void testComputePrecompile(final PrecompileTestParameters parameters) { - PrecompiledContract.PrecompileContractResult result = - contract.computePrecompile(parameters.input, toRun); - if (parameters.valid) { - assertThat(result.getState()).isEqualTo(MessageFrame.State.COMPLETED_SUCCESS); - assertThat(result.getOutput()).isEqualTo(parameters.returnValue); - } else { - assertThat(result.getState()).isNotEqualTo(MessageFrame.State.COMPLETED_SUCCESS); - } - } - - public static List getPointEvaluationPrecompileTestVectors() - throws IOException { - final JsonNode jsonNode; - try (final InputStream testVectors = - KZGPointEvalPrecompileContractTest.class.getResourceAsStream( - "pointEvaluationPrecompile.json")) { - jsonNode = new ObjectMapper().readTree(testVectors); - } - final ArrayNode testCases = (ArrayNode) jsonNode.get("TestCases"); - final Bytes returnValue = Bytes.fromHexString(jsonNode.get("PrecompileReturnValue").asText()); - return IntStream.range(0, testCases.size()) - .mapToObj( - i -> { - final JsonNode testCase = testCases.get(i); - final Bytes input = Bytes.fromHexString(testCase.get("Input").asText()); - final boolean valid = testCase.get("Valid").asBoolean(); - return new PrecompileTestParameters(input, valid, returnValue); - }) - .collect(Collectors.toList()); - } - - record PrecompileTestParameters(Bytes input, boolean valid, Bytes returnValue) {} -} diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 951ad1c9577..77cd15b567c 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -5592,15 +5592,15 @@ - - - + + + - - + + - - + + diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 8abdc6c6191..4e6629ab29a 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -163,7 +163,7 @@ dependencyManagement { dependency 'org.fusesource.jansi:jansi:2.4.0' dependency 'org.openjdk.jol:jol-core:0.17' - dependency 'tech.pegasys:jc-kzg-4844:0.4.0' + dependency 'tech.pegasys:jc-kzg-4844:0.7.0' dependencySet(group: 'org.hyperledger.besu', version: '0.7.1') { entry 'arithmetic' diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index 0544e1dbac0..88da5182b08 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -69,7 +69,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 = 'vfKliYwHKucwcFF5w5hLnplIPn8OqkxdmzGnaKE+fMs=' + knownHash = 'qPEwwqc/bjDvFqYBVq+qjxoQbQilRiyIoG0ltRDqItY=' } check.dependsOn('checkAPIChanges')