verfifying kzg proof (#4994)

Signed-off-by: Jiri Peinlich <jiri.peinlich@gmail.com>
pull/4998/head
Jiri Peinlich 2 years ago committed by GitHub
parent a77b7fe42b
commit 8e3a912b28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      ethereum/core/build.gradle
  2. 120
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  3. 1
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java
  4. 2
      gradle/versions.gradle

@ -78,6 +78,7 @@ dependencies {
implementation files('libs/tuweni-ssz-2.4.0-SNAPSHOT.jar') implementation files('libs/tuweni-ssz-2.4.0-SNAPSHOT.jar')
implementation 'org.hyperledger.besu:bls12-381' implementation 'org.hyperledger.besu:bls12-381'
implementation 'org.immutables:value-annotations' implementation 'org.immutables:value-annotations'
implementation 'tech.pegasys:jc-kzg-4844'
implementation 'io.prometheus:simpleclient_guava' implementation 'io.prometheus:simpleclient_guava'

@ -22,6 +22,7 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionFilter; import org.hyperledger.besu.ethereum.core.TransactionFilter;
import org.hyperledger.besu.ethereum.core.encoding.ssz.TransactionNetworkPayload;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.Account;
@ -29,9 +30,15 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.plugin.data.TransactionType; import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import ethereum.ckzg4844.CKZG4844JNI;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.bouncycastle.crypto.digests.SHA256Digest;
/** /**
* Validates a transaction based on Frontier protocol runtime requirements. * Validates a transaction based on Frontier protocol runtime requirements.
* *
@ -40,6 +47,10 @@ import java.util.Set;
*/ */
public class MainnetTransactionValidator { public class MainnetTransactionValidator {
private final long MAX_DATA_GAS_PER_BLOCK = 524_288; // 2**19
private final long DATA_GAS_PER_BLOB = 131_072; // 2**17
private final byte BLOB_COMMITMENT_VERSION_KZG = 0x01;
private final GasCalculator gasCalculator; private final GasCalculator gasCalculator;
private final FeeMarket feeMarket; private final FeeMarket feeMarket;
@ -118,6 +129,14 @@ public class MainnetTransactionValidator {
if (!signatureResult.isValid()) { if (!signatureResult.isValid()) {
return signatureResult; return signatureResult;
} }
if (transaction.getType().equals(TransactionType.BLOB)
&& transaction.getBlobsWithCommitments().isPresent()) {
final ValidationResult<TransactionInvalidReason> blobsResult =
validateTransactionsBlobs(transaction);
if (!blobsResult.isValid()) {
return blobsResult;
}
}
if (goQuorumCompatibilityMode && transaction.hasCostParams()) { if (goQuorumCompatibilityMode && transaction.hasCostParams()) {
return ValidationResult.invalid( return ValidationResult.invalid(
@ -285,6 +304,107 @@ public class MainnetTransactionValidator {
return ValidationResult.valid(); return ValidationResult.valid();
} }
public ValidationResult<TransactionInvalidReason> validateTransactionsBlobs(
final Transaction transaction) {
if (transaction.getBlobsWithCommitments().isEmpty()) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_BLOBS,
"transaction blobs are empty, cannot verify without blobs");
}
Transaction.BlobsWithCommitments blobsWithCommitments =
transaction.getBlobsWithCommitments().get();
if (blobsWithCommitments.blobs.getElements().size()
> MAX_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_BLOBS,
"Too many transaction blobs ("
+ blobsWithCommitments.blobs.getElements().size()
+ ") in transaction, max is "
+ MAX_DATA_GAS_PER_BLOCK / DATA_GAS_PER_BLOB);
}
if (blobsWithCommitments.blobs.getElements().size()
!= blobsWithCommitments.kzgCommitments.getElements().size()) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_BLOBS,
"transaction blobs and commitments are not the same size");
}
List<TransactionNetworkPayload.KZGCommitment> commitments =
blobsWithCommitments.kzgCommitments.getElements();
for (TransactionNetworkPayload.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");
}
List<Hash> versionedHashes = transaction.getVersionedHashes().get();
for (int i = 0; i < versionedHashes.size(); i++) {
TransactionNetworkPayload.KZGCommitment commitment =
blobsWithCommitments.kzgCommitments.getElements().get(i);
Hash versionedHash = versionedHashes.get(i);
Hash calculatedVersionedHash = hashCommitment(commitment);
if (!calculatedVersionedHash.equals(versionedHash)) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_BLOBS,
"transaction blobs commitment hash does not match commitment");
}
}
Bytes blobs =
blobsWithCommitments.blobs.getElements().stream()
.map(
blob ->
blob.getElements().stream()
.map(sszuInt256Wrapper -> (Bytes) sszuInt256Wrapper.getData().toBytes())
.reduce(Bytes::concatenate)
.orElseThrow())
.reduce(Bytes::concatenate)
.orElseThrow();
Bytes kzgCommitments =
blobsWithCommitments.kzgCommitments.getElements().stream()
.map(commitment -> commitment.getData())
.reduce(Bytes::concatenate)
.orElseThrow();
boolean kzgVerification =
CKZG4844JNI.verifyAggregateKzgProof(
blobs.toArrayUnsafe(),
kzgCommitments.toArrayUnsafe(),
blobsWithCommitments.blobs.getElements().size(),
blobsWithCommitments.kzgProof.getBytes().toArrayUnsafe());
if (!kzgVerification) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_BLOBS,
"transaction blobs kzg proof verification failed");
}
return ValidationResult.valid();
}
private Hash hashCommitment(final TransactionNetworkPayload.KZGCommitment commitment) {
SHA256Digest digest = new SHA256Digest();
digest.update(commitment.getData().toArrayUnsafe(), 0, commitment.getData().size());
final byte[] dig = new byte[digest.getDigestSize()];
digest.doFinal(dig, 0);
dig[0] = BLOB_COMMITMENT_VERSION_KZG;
return Hash.wrap(Bytes32.wrap(dig));
}
private boolean isSenderAllowed( private boolean isSenderAllowed(
final Transaction transaction, final TransactionValidationParams validationParams) { final Transaction transaction, final TransactionValidationParams validationParams) {
if (validationParams.checkLocalPermissions() || validationParams.checkOnchainPermissions()) { if (validationParams.checkLocalPermissions() || validationParams.checkOnchainPermissions()) {

@ -54,5 +54,6 @@ public enum TransactionInvalidReason {
ETHER_VALUE_NOT_SUPPORTED, ETHER_VALUE_NOT_SUPPORTED,
UPFRONT_FEE_TOO_HIGH, UPFRONT_FEE_TOO_HIGH,
NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER, NONCE_TOO_FAR_IN_FUTURE_FOR_SENDER,
INVALID_BLOBS,
LOWER_NONCE_INVALID_TRANSACTION_EXISTS LOWER_NONCE_INVALID_TRANSACTION_EXISTS
} }

@ -153,7 +153,7 @@ dependencyManagement {
} }
dependency 'org.fusesource.jansi:jansi:2.4.0' dependency 'org.fusesource.jansi:jansi:2.4.0'
dependency 'tech.pegasys:jc-kzg-4844:0.1.0' dependency 'tech.pegasys:jc-kzg-4844:0.3.0'
dependencySet(group: 'org.hyperledger.besu', version: '0.7.1') { dependencySet(group: 'org.hyperledger.besu', version: '0.7.1') {
entry 'arithmetic' entry 'arithmetic'
entry 'ipa-multipoint' entry 'ipa-multipoint'

Loading…
Cancel
Save