mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-07 21:13:56 -05:00
Add gRPC transaction validation support to send local incoming transaction data to the rln prover by extending transaction pool validator besu plugin
This commit is contained in:
@@ -61,6 +61,8 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
|
||||
"--strict-tx-replay-protection-enabled";
|
||||
private static final String TX_POOL_PRIORITY_SENDERS = "--tx-pool-priority-senders";
|
||||
private static final String TX_POOL_MIN_GAS_PRICE = "--tx-pool-min-gas-price";
|
||||
private static final String TX_POOL_GRPC_VALIDATION_ENDPOINT =
|
||||
"--tx-pool-grpc-validation-endpoint";
|
||||
|
||||
private TransactionPoolValidatorService transactionPoolValidatorService;
|
||||
|
||||
@@ -149,6 +151,15 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
|
||||
arity = "1")
|
||||
private Wei minGasPrice = TransactionPoolConfiguration.DEFAULT_TX_POOL_MIN_GAS_PRICE;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {TX_POOL_GRPC_VALIDATION_ENDPOINT},
|
||||
paramLabel = "<host:port>",
|
||||
description =
|
||||
"Enable gRPC validation for local transactions by specifying validation service endpoint (e.g., localhost:9090). "
|
||||
+ "If not specified, gRPC validation is disabled.",
|
||||
arity = "1")
|
||||
private String grpcValidationEndpoint;
|
||||
|
||||
@CommandLine.ArgGroup(
|
||||
validate = false,
|
||||
heading = "@|bold Tx Pool Layered Implementation Options|@%n")
|
||||
@@ -298,6 +309,34 @@ public class TransactionPoolOptions implements CLIOptions<TransactionPoolConfigu
|
||||
public void setPluginTransactionValidatorService(
|
||||
final TransactionPoolValidatorService transactionPoolValidatorService) {
|
||||
this.transactionPoolValidatorService = transactionPoolValidatorService;
|
||||
|
||||
// Register gRPC validator if endpoint is configured
|
||||
if (grpcValidationEndpoint != null && !grpcValidationEndpoint.trim().isEmpty()) {
|
||||
try {
|
||||
// Try to dynamically load our gRPC validator factory
|
||||
Class<?> factoryClass =
|
||||
Class.forName(
|
||||
"org.hyperledger.besu.ethereum.eth.transactions.grpc.GrpcTransactionPoolValidatorFactory");
|
||||
Object factory =
|
||||
factoryClass.getConstructor(String.class).newInstance(grpcValidationEndpoint);
|
||||
|
||||
// Register the factory with the service
|
||||
transactionPoolValidatorService
|
||||
.getClass()
|
||||
.getMethod(
|
||||
"registerPluginTransactionValidatorFactory",
|
||||
Class.forName(
|
||||
"org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory"))
|
||||
.invoke(transactionPoolValidatorService, factory);
|
||||
|
||||
// Use reflection to call the method to avoid compile-time dependency
|
||||
} catch (Exception e) {
|
||||
// Log warning if gRPC validator cannot be loaded (graceful degradation)
|
||||
System.err.println(
|
||||
"Warning: Could not load gRPC transaction validator, proceeding without gRPC validation: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -217,6 +217,7 @@ tx-pool-max-size=1234
|
||||
tx-pool-limit-by-account-percentage=0.017
|
||||
tx-pool-min-gas-price=1000
|
||||
tx-pool-min-score=100
|
||||
tx-pool-grpc-validation-endpoint="localhost:8080"
|
||||
|
||||
# Revert Reason
|
||||
revert-reason-enabled=false
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
apply plugin: 'java-library'
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'com.google.protobuf' version '0.9.4'
|
||||
}
|
||||
|
||||
jar {
|
||||
archiveBaseName = 'besu-eth'
|
||||
@@ -62,7 +65,13 @@ dependencies {
|
||||
implementation 'org.rocksdb:rocksdbjni'
|
||||
implementation 'com.github.ben-manes.caffeine:caffeine'
|
||||
implementation 'com.google.dagger:dagger'
|
||||
implementation 'io.grpc:grpc-core'
|
||||
implementation 'io.grpc:grpc-stub'
|
||||
implementation 'io.grpc:grpc-protobuf'
|
||||
implementation 'io.grpc:grpc-netty'
|
||||
implementation 'com.google.protobuf:protobuf-java-util:3.25.1'
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler'
|
||||
implementation 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
|
||||
annotationProcessor "org.immutables:value"
|
||||
implementation "org.immutables:value-annotations"
|
||||
@@ -85,6 +94,8 @@ dependencies {
|
||||
testImplementation 'org.mockito:mockito-core'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||
testImplementation 'org.openjdk.jol:jol-core'
|
||||
testImplementation 'io.grpc:grpc-inprocess:1.60.0'
|
||||
testImplementation 'io.grpc:grpc-stub:1.60.0'
|
||||
|
||||
testSupportImplementation 'org.mockito:mockito-core'
|
||||
testSupportImplementation project(':testutil')
|
||||
@@ -96,7 +107,41 @@ dependencies {
|
||||
jmhImplementation project(':plugins:rocksdb')
|
||||
}
|
||||
|
||||
javadoc {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
artifacts {
|
||||
testArtifacts testJar
|
||||
testSupportArtifacts testSupportJar
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = "com.google.protobuf:protoc:3.25.1"
|
||||
}
|
||||
plugins {
|
||||
grpc {
|
||||
artifact = 'io.grpc:protoc-gen-grpc-java:1.60.0'
|
||||
}
|
||||
}
|
||||
generateProtoTasks {
|
||||
all().each { task ->
|
||||
task.plugins {
|
||||
grpc {}
|
||||
}
|
||||
task.builtins {
|
||||
java {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs 'build/generated/source/proto/main/grpc'
|
||||
srcDirs 'build/generated/source/proto/main/java'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.eth.transactions.grpc;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.ManagedChannelBuilder;
|
||||
import net.vac.prover.Address;
|
||||
import net.vac.prover.RlnProverGrpc;
|
||||
import net.vac.prover.SendTransactionReply;
|
||||
import net.vac.prover.SendTransactionRequest;
|
||||
import net.vac.prover.U256;
|
||||
import net.vac.prover.Wei;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* gRPC-based transaction pool validator that sends local transactions to an external RLN prover
|
||||
* service for validation.
|
||||
*
|
||||
* <p>This validator demonstrates: - Only validates local transactions (isLocal=true) - Falls back
|
||||
* to default validation for peer transactions - Gracefully handles gRPC failures by falling back to
|
||||
* default validation - Logs all transaction validation attempts for verification
|
||||
*/
|
||||
public class GrpcTransactionPoolValidator implements PluginTransactionPoolValidator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GrpcTransactionPoolValidator.class);
|
||||
|
||||
private final String endpoint;
|
||||
private int validationCallCount = 0;
|
||||
private int localTransactionCount = 0;
|
||||
private int peerTransactionCount = 0;
|
||||
|
||||
private final ManagedChannel channel;
|
||||
private final RlnProverGrpc.RlnProverBlockingStub blockingStub;
|
||||
|
||||
/**
|
||||
* Creates a new gRPC transaction pool validator.
|
||||
*
|
||||
* @param endpoint the gRPC service endpoint (host:port)
|
||||
*/
|
||||
public GrpcTransactionPoolValidator(final String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
this.channel = createChannel(endpoint);
|
||||
this.blockingStub = RlnProverGrpc.newBlockingStub(channel);
|
||||
LOG.info("*** gRPC Transaction Pool Validator INITIALIZED for endpoint: {} ***", endpoint);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
|
||||
}
|
||||
|
||||
/** Creates a gRPC channel. Protected to allow overriding in tests. */
|
||||
protected ManagedChannel createChannel(final String endpoint) {
|
||||
return ManagedChannelBuilder.forTarget(endpoint).usePlaintext().build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> validateTransaction(
|
||||
final Transaction transaction, final boolean isLocal, final boolean hasPriority) {
|
||||
|
||||
validationCallCount++;
|
||||
|
||||
LOG.info("*** TRANSACTION VALIDATION #{} ***", validationCallCount);
|
||||
LOG.info("Transaction Hash: {}", transaction.getHash().toHexString());
|
||||
LOG.info("Transaction Sender: {}", transaction.getSender().toHexString());
|
||||
LOG.info(
|
||||
"Transaction Gas Price: {}", transaction.getGasPrice().map(Object::toString).orElse("N/A"));
|
||||
LOG.info("Transaction Value: {}", transaction.getValue().toHexString());
|
||||
LOG.info("Is Local: {}", isLocal);
|
||||
LOG.info("Has Priority: {}", hasPriority);
|
||||
LOG.info("gRPC Endpoint: {}", endpoint);
|
||||
|
||||
// Only validate local transactions via gRPC
|
||||
if (!isLocal) {
|
||||
peerTransactionCount++;
|
||||
LOG.debug("Skipping gRPC validation for peer transaction");
|
||||
return Optional.empty(); // Accept peer transactions without gRPC validation
|
||||
}
|
||||
|
||||
localTransactionCount++;
|
||||
LOG.debug("Processing local transaction via gRPC: {}", transaction.getHash());
|
||||
|
||||
try {
|
||||
SendTransactionRequest.Builder requestBuilder = SendTransactionRequest.newBuilder();
|
||||
|
||||
transaction
|
||||
.getGasPrice()
|
||||
.ifPresent(
|
||||
gasPrice ->
|
||||
requestBuilder.setGasPrice(
|
||||
Wei.newBuilder()
|
||||
.setValue(ByteString.copyFrom(gasPrice.getAsBigInteger().toByteArray()))
|
||||
.build()));
|
||||
|
||||
requestBuilder.setSender(
|
||||
Address.newBuilder()
|
||||
.setValue(ByteString.copyFrom(transaction.getSender().toArrayUnsafe()))
|
||||
.build());
|
||||
|
||||
transaction
|
||||
.getChainId()
|
||||
.ifPresent(
|
||||
chainId ->
|
||||
requestBuilder.setChainId(
|
||||
U256.newBuilder()
|
||||
.setValue(ByteString.copyFrom(chainId.toByteArray()))
|
||||
.build()));
|
||||
|
||||
requestBuilder.setTransactionHash(ByteString.copyFrom(transaction.getHash().toArrayUnsafe()));
|
||||
|
||||
SendTransactionRequest request = requestBuilder.build();
|
||||
|
||||
LOG.debug("Sending transaction to RLN prover: {}", request);
|
||||
SendTransactionReply reply = blockingStub.sendTransaction(request);
|
||||
|
||||
if (reply.getResult()) {
|
||||
LOG.debug("RLN prover accepted transaction {}", transaction.getHash());
|
||||
return Optional.empty(); // Transaction is valid
|
||||
} else {
|
||||
LOG.warn("RLN prover rejected transaction {}", transaction.getHash());
|
||||
return Optional.of("RLN prover rejected transaction");
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
LOG.warn(
|
||||
"gRPC validation failed for transaction {}, falling back to default validation: {}",
|
||||
transaction.getHash(),
|
||||
e.getMessage());
|
||||
// Graceful fallback: accept the transaction if gRPC fails
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/** Close the gRPC channel. */
|
||||
public void close() {
|
||||
try {
|
||||
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
LOG.warn("Interrupted while shutting down gRPC channel", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get statistics for testing/verification. */
|
||||
public int getValidationCallCount() {
|
||||
return validationCallCount;
|
||||
}
|
||||
|
||||
public int getLocalTransactionCount() {
|
||||
return localTransactionCount;
|
||||
}
|
||||
|
||||
public int getPeerTransactionCount() {
|
||||
return peerTransactionCount;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.eth.transactions.grpc;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator;
|
||||
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/** Factory for creating gRPC transaction pool validators. */
|
||||
public class GrpcTransactionPoolValidatorFactory implements PluginTransactionPoolValidatorFactory {
|
||||
|
||||
private static final Logger LOG =
|
||||
LoggerFactory.getLogger(GrpcTransactionPoolValidatorFactory.class);
|
||||
|
||||
private final String endpoint;
|
||||
private volatile GrpcTransactionPoolValidator validator;
|
||||
|
||||
/**
|
||||
* Creates a new factory.
|
||||
*
|
||||
* @param endpoint the gRPC endpoint in format "host:port"
|
||||
*/
|
||||
public GrpcTransactionPoolValidatorFactory(final String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PluginTransactionPoolValidator createTransactionValidator() {
|
||||
if (endpoint != null && !endpoint.trim().isEmpty()) {
|
||||
if (validator == null) {
|
||||
synchronized (this) {
|
||||
if (validator == null) {
|
||||
LOG.info("Creating gRPC transaction pool validator for endpoint: {}", endpoint);
|
||||
validator = new GrpcTransactionPoolValidator(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
return validator;
|
||||
} else {
|
||||
LOG.debug("No gRPC endpoint configured, using default validator");
|
||||
return PluginTransactionPoolValidator.VALIDATE_ALL;
|
||||
}
|
||||
}
|
||||
}
|
||||
120
ethereum/eth/src/main/proto/prover/RlnProver.proto
Normal file
120
ethereum/eth/src/main/proto/prover/RlnProver.proto
Normal file
@@ -0,0 +1,120 @@
|
||||
syntax = "proto3";
|
||||
package prover;
|
||||
|
||||
import "google/protobuf/descriptor.proto";
|
||||
|
||||
option java_package = "net.vac.prover";
|
||||
option java_outer_classname = "OuterSample";
|
||||
option java_multiple_files = true;
|
||||
|
||||
service RlnProver {
|
||||
|
||||
rpc SendTransaction (SendTransactionRequest) returns (SendTransactionReply);
|
||||
rpc RegisterUser (RegisterUserRequest) returns (RegisterUserReply);
|
||||
// Server side streaming RPC: 1 request -> X responses (stream)
|
||||
rpc GetProofs(RlnProofFilter) returns (stream RlnProofReply);
|
||||
|
||||
rpc GetUserTierInfo(GetUserTierInfoRequest) returns (GetUserTierInfoReply);
|
||||
rpc SetTierLimits(SetTierLimitsRequest) returns (SetTierLimitsReply);
|
||||
}
|
||||
|
||||
extend google.protobuf.FieldOptions {
|
||||
optional uint32 max_size = 50000;
|
||||
}
|
||||
|
||||
message Wei {
|
||||
bytes value = 1 [(max_size) = 32];
|
||||
}
|
||||
|
||||
message U256 {
|
||||
bytes value = 1 [(max_size) = 32];
|
||||
}
|
||||
|
||||
message Address {
|
||||
bytes value = 1 [(max_size) = 20];
|
||||
}
|
||||
|
||||
message SendTransactionRequest {
|
||||
optional Wei gasPrice = 1;
|
||||
optional Address sender = 2;
|
||||
optional U256 chainId = 3;
|
||||
bytes transactionHash = 4 [(max_size) = 32];
|
||||
}
|
||||
|
||||
message SendTransactionReply {
|
||||
bool result = 1;
|
||||
}
|
||||
|
||||
message RlnProofFilter {
|
||||
optional string address = 1;
|
||||
}
|
||||
|
||||
message RlnProofReply {
|
||||
oneof resp {
|
||||
// variant for success
|
||||
RlnProof proof = 1;
|
||||
// variant for error
|
||||
RlnProofError error = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message RlnProof {
|
||||
bytes sender = 1;
|
||||
bytes tx_hash = 2;
|
||||
bytes proof = 3;
|
||||
}
|
||||
|
||||
message RlnProofError {
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
message RegisterUserRequest {
|
||||
Address user = 14;
|
||||
}
|
||||
|
||||
enum RegistrationStatus {
|
||||
Success = 0;
|
||||
Failure = 1;
|
||||
AlreadyRegistered = 2;
|
||||
}
|
||||
|
||||
message RegisterUserReply {
|
||||
RegistrationStatus status = 1;
|
||||
}
|
||||
|
||||
message GetUserTierInfoRequest {
|
||||
Address user = 1;
|
||||
}
|
||||
|
||||
message GetUserTierInfoReply {
|
||||
oneof resp {
|
||||
UserTierInfoResult res = 1;
|
||||
UserTierInfoError error = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message UserTierInfoError {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
message UserTierInfoResult {
|
||||
sint64 current_epoch = 1;
|
||||
sint64 current_epoch_slice = 2;
|
||||
uint64 tx_count = 3;
|
||||
optional Tier tier = 4;
|
||||
}
|
||||
|
||||
message Tier {
|
||||
string name = 1;
|
||||
uint64 quota = 2;
|
||||
}
|
||||
|
||||
message SetTierLimitsRequest {
|
||||
repeated U256 karmaAmounts = 1;
|
||||
repeated Tier tiers = 2;
|
||||
}
|
||||
|
||||
message SetTierLimitsReply {
|
||||
bool status = 1;
|
||||
string error = 2;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.eth.transactions.grpc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests for GrpcTransactionPoolValidatorFactory that focus on the factory logic: - Validator
|
||||
* creation based on endpoint configuration - Singleton behavior and caching - Fallback to default
|
||||
* validator - Edge cases and error handling
|
||||
*/
|
||||
class GrpcTransactionPoolValidatorFactoryTest {
|
||||
|
||||
@Test
|
||||
void shouldReturnGrpcValidatorWhenValidEndpointProvided() {
|
||||
// Given: factory with valid endpoint
|
||||
String endpoint = "localhost:9090";
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory(endpoint);
|
||||
|
||||
// When: creating validator
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// Then: should return gRPC validator instance
|
||||
assertThat(validator).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
|
||||
GrpcTransactionPoolValidator grpcValidator = (GrpcTransactionPoolValidator) validator;
|
||||
assertThat(grpcValidator.getEndpoint()).isEqualTo(endpoint);
|
||||
|
||||
// Cleanup
|
||||
grpcValidator.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnDefaultValidatorWhenEndpointEmpty() {
|
||||
// Given: factory with empty endpoint
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory("");
|
||||
|
||||
// When: creating validator
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// Then: should return default validator
|
||||
assertThat(validator).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnDefaultValidatorWhenEndpointNull() {
|
||||
// Given: factory with null endpoint
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory(null);
|
||||
|
||||
// When: creating validator
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// Then: should return default validator
|
||||
assertThat(validator).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnDefaultValidatorWhenEndpointWhitespace() {
|
||||
// Given: factory with whitespace-only endpoint
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory(" ");
|
||||
|
||||
// When: creating validator
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// Then: should return default validator (whitespace should be treated as empty)
|
||||
assertThat(validator).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCacheValidatorInstance() {
|
||||
// Given: factory with valid endpoint
|
||||
String endpoint = "localhost:9091";
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory(endpoint);
|
||||
|
||||
// When: creating multiple validators
|
||||
PluginTransactionPoolValidator validator1 = factory.createTransactionValidator();
|
||||
PluginTransactionPoolValidator validator2 = factory.createTransactionValidator();
|
||||
PluginTransactionPoolValidator validator3 = factory.createTransactionValidator();
|
||||
|
||||
// Then: should return same instance (singleton behavior)
|
||||
assertThat(validator1).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
assertThat(validator1).isSameAs(validator2);
|
||||
assertThat(validator2).isSameAs(validator3);
|
||||
|
||||
// Cleanup
|
||||
((GrpcTransactionPoolValidator) validator1).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleVariousEndpointFormats() {
|
||||
// Test different valid endpoint formats
|
||||
String[] validEndpoints = {
|
||||
"localhost:8080", "127.0.0.1:9090", "grpc-service.example.com:443", "10.0.0.1:50051"
|
||||
};
|
||||
|
||||
for (String endpoint : validEndpoints) {
|
||||
// Given: factory with endpoint in specific format
|
||||
GrpcTransactionPoolValidatorFactory factory =
|
||||
new GrpcTransactionPoolValidatorFactory(endpoint);
|
||||
|
||||
// When: creating validator
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// Then: should create gRPC validator
|
||||
assertThat(validator).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
|
||||
GrpcTransactionPoolValidator grpcValidator = (GrpcTransactionPoolValidator) validator;
|
||||
assertThat(grpcValidator.getEndpoint()).isEqualTo(endpoint);
|
||||
|
||||
// Cleanup
|
||||
grpcValidator.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotCacheDefaultValidator() {
|
||||
// Given: factory without endpoint (returns default validator)
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory(null);
|
||||
|
||||
// When: creating multiple validators
|
||||
PluginTransactionPoolValidator validator1 = factory.createTransactionValidator();
|
||||
PluginTransactionPoolValidator validator2 = factory.createTransactionValidator();
|
||||
|
||||
// Then: should return same default validator instance (but not cached by factory)
|
||||
assertThat(validator1).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
assertThat(validator2).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
assertThat(validator1).isSameAs(validator2); // Same static instance
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateDifferentValidatorsForDifferentEndpoints() {
|
||||
// Given: factories with different endpoints
|
||||
GrpcTransactionPoolValidatorFactory factory1 =
|
||||
new GrpcTransactionPoolValidatorFactory("localhost:8080");
|
||||
GrpcTransactionPoolValidatorFactory factory2 =
|
||||
new GrpcTransactionPoolValidatorFactory("localhost:9090");
|
||||
|
||||
// When: creating validators
|
||||
PluginTransactionPoolValidator validator1 = factory1.createTransactionValidator();
|
||||
PluginTransactionPoolValidator validator2 = factory2.createTransactionValidator();
|
||||
|
||||
// Then: should create different instances
|
||||
assertThat(validator1).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
assertThat(validator2).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
assertThat(validator1).isNotSameAs(validator2);
|
||||
|
||||
GrpcTransactionPoolValidator grpcValidator1 = (GrpcTransactionPoolValidator) validator1;
|
||||
GrpcTransactionPoolValidator grpcValidator2 = (GrpcTransactionPoolValidator) validator2;
|
||||
|
||||
assertThat(grpcValidator1.getEndpoint()).isEqualTo("localhost:8080");
|
||||
assertThat(grpcValidator2.getEndpoint()).isEqualTo("localhost:9090");
|
||||
|
||||
// Cleanup
|
||||
grpcValidator1.close();
|
||||
grpcValidator2.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNormalizedEndpoints() {
|
||||
// Given: factory with endpoint that could be normalized
|
||||
String endpoint = " localhost:8080 "; // with whitespace
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory(endpoint);
|
||||
|
||||
// When: creating validator
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// Then: should still create gRPC validator (implementation may or may not trim)
|
||||
if (validator instanceof GrpcTransactionPoolValidator) {
|
||||
GrpcTransactionPoolValidator grpcValidator = (GrpcTransactionPoolValidator) validator;
|
||||
assertThat(grpcValidator.getEndpoint()).isEqualTo(endpoint); // Stores as-is
|
||||
grpcValidator.close();
|
||||
} else {
|
||||
// If implementation trims whitespace and treats as empty, default validator is returned
|
||||
assertThat(validator).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProvideDescriptiveToString() {
|
||||
// Given: factory with endpoint
|
||||
String endpoint = "test-service:8080";
|
||||
GrpcTransactionPoolValidatorFactory factory = new GrpcTransactionPoolValidatorFactory(endpoint);
|
||||
|
||||
// When: getting string representation
|
||||
String factoryString = factory.toString();
|
||||
|
||||
// Then: should be descriptive (if implemented)
|
||||
assertThat(factoryString).isNotNull();
|
||||
// Note: Actual toString implementation depends on the class implementation
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.eth.transactions.grpc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests for GrpcTransactionPoolValidator that focus on the actual implementation logic: -
|
||||
* Transaction processing behavior (local vs peer) - Error handling and fallback mechanisms -
|
||||
* Statistics tracking - Transaction data extraction and formatting
|
||||
*/
|
||||
class GrpcTransactionPoolValidatorTest {
|
||||
|
||||
private static final SignatureAlgorithm SIGNATURE_ALGORITHM =
|
||||
SignatureAlgorithmFactory.getInstance();
|
||||
|
||||
private GrpcTransactionPoolValidator validator;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (validator != null) {
|
||||
validator.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSkipPeerTransactionsCompletely() {
|
||||
// Given: validator with invalid endpoint to ensure no real gRPC calls
|
||||
validator = new GrpcTransactionPoolValidator("invalid-endpoint:9999");
|
||||
Transaction peerTransaction = createTestTransaction();
|
||||
|
||||
// When: processing peer transaction
|
||||
Optional<String> result = validator.validateTransaction(peerTransaction, false, false);
|
||||
|
||||
// Then: should accept without any gRPC processing
|
||||
assertThat(result).isEmpty(); // Accepted
|
||||
assertThat(validator.getPeerTransactionCount()).isEqualTo(1);
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(0);
|
||||
assertThat(validator.getValidationCallCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProcessLocalTransactionsViaGrpc() {
|
||||
// Given: validator with invalid endpoint to test fallback behavior
|
||||
validator = new GrpcTransactionPoolValidator("invalid-endpoint:9999");
|
||||
Transaction localTransaction = createTestTransaction();
|
||||
|
||||
// When: processing local transaction
|
||||
Optional<String> result = validator.validateTransaction(localTransaction, true, false);
|
||||
|
||||
// Then: should attempt gRPC call and fallback to acceptance
|
||||
assertThat(result).isEmpty(); // Accepted via fallback
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(1);
|
||||
assertThat(validator.getPeerTransactionCount()).isEqualTo(0);
|
||||
assertThat(validator.getValidationCallCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFallBackGracefullyWhenGrpcServiceUnavailable() {
|
||||
// Given: validator pointing to unavailable service
|
||||
validator = new GrpcTransactionPoolValidator("localhost:99999");
|
||||
Transaction localTransaction = createTestTransaction();
|
||||
|
||||
// When: attempting to validate local transaction
|
||||
Optional<String> result = validator.validateTransaction(localTransaction, true, false);
|
||||
|
||||
// Then: should fall back to accepting the transaction
|
||||
assertThat(result).isEmpty(); // Graceful fallback accepts transaction
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldTrackStatisticsAccurately() {
|
||||
// Given: validator for testing statistics
|
||||
validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
Transaction transaction = createTestTransaction();
|
||||
|
||||
// When: processing mix of local and peer transactions
|
||||
validator.validateTransaction(transaction, true, false); // local #1
|
||||
validator.validateTransaction(transaction, false, false); // peer #1
|
||||
validator.validateTransaction(transaction, true, false); // local #2
|
||||
validator.validateTransaction(transaction, false, false); // peer #2
|
||||
validator.validateTransaction(transaction, false, false); // peer #3
|
||||
validator.validateTransaction(transaction, true, false); // local #3
|
||||
|
||||
// Then: statistics should be accurate
|
||||
assertThat(validator.getValidationCallCount()).isEqualTo(6);
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(3);
|
||||
assertThat(validator.getPeerTransactionCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleTransactionsWithVariousGasPrices() {
|
||||
// Given: validator for testing different transaction types
|
||||
validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
|
||||
// When: processing transactions with different gas prices
|
||||
Transaction lowGasTransaction = createTransactionWithGasPrice(Wei.of(1_000_000_000L));
|
||||
Transaction highGasTransaction = createTransactionWithGasPrice(Wei.of(100_000_000_000L));
|
||||
Transaction zeroGasTransaction = createTransactionWithGasPrice(Wei.ZERO);
|
||||
|
||||
Optional<String> result1 = validator.validateTransaction(lowGasTransaction, true, false);
|
||||
Optional<String> result2 = validator.validateTransaction(highGasTransaction, true, false);
|
||||
Optional<String> result3 = validator.validateTransaction(zeroGasTransaction, true, false);
|
||||
|
||||
// Then: all should be processed (fallback accepts all)
|
||||
assertThat(result1).isEmpty();
|
||||
assertThat(result2).isEmpty();
|
||||
assertThat(result3).isEmpty();
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleTransactionsWithDifferentChainIds() {
|
||||
// Given: validator for testing different chain IDs
|
||||
validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
|
||||
// When: processing transactions with different chain IDs
|
||||
Transaction mainnetTx = createTransactionWithChainId(BigInteger.valueOf(1));
|
||||
Transaction testnetTx = createTransactionWithChainId(BigInteger.valueOf(3));
|
||||
Transaction customTx = createTransactionWithChainId(BigInteger.valueOf(1337));
|
||||
|
||||
Optional<String> result1 = validator.validateTransaction(mainnetTx, true, false);
|
||||
Optional<String> result2 = validator.validateTransaction(testnetTx, true, false);
|
||||
Optional<String> result3 = validator.validateTransaction(customTx, true, false);
|
||||
|
||||
// Then: all should be processed
|
||||
assertThat(result1).isEmpty();
|
||||
assertThat(result2).isEmpty();
|
||||
assertThat(result3).isEmpty();
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMaintainEndpointConfiguration() {
|
||||
// Given: validator with specific endpoint
|
||||
String testEndpoint = "grpc-service.example.com:9090";
|
||||
validator = new GrpcTransactionPoolValidator(testEndpoint);
|
||||
|
||||
// When/Then: endpoint should be stored correctly
|
||||
assertThat(validator.getEndpoint()).isEqualTo(testEndpoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandlePriorityTransactionFlag() {
|
||||
// Given: validator for testing priority handling
|
||||
validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
Transaction transaction = createTestTransaction();
|
||||
|
||||
// When: processing transactions with different priority flags
|
||||
Optional<String> normalResult = validator.validateTransaction(transaction, true, false);
|
||||
Optional<String> priorityResult = validator.validateTransaction(transaction, true, true);
|
||||
|
||||
// Then: both should be processed (priority flag passed to implementation)
|
||||
assertThat(normalResult).isEmpty();
|
||||
assertThat(priorityResult).isEmpty();
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleTransactionsWithComplexData() {
|
||||
// Given: validator for testing complex transaction data
|
||||
validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
|
||||
// When: processing transaction with complex data
|
||||
Transaction complexTransaction =
|
||||
new TransactionTestFixture()
|
||||
.gasPrice(Wei.of(50_000_000_000L))
|
||||
.gasLimit(21000)
|
||||
.value(Wei.fromEth(1))
|
||||
.chainId(Optional.of(BigInteger.valueOf(1337)))
|
||||
.to(Optional.of(Address.fromHexString("0x742d35cc6634c0532925a3b8d039135682b2e78b")))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
|
||||
Optional<String> result = validator.validateTransaction(complexTransaction, true, false);
|
||||
|
||||
// Then: should process successfully
|
||||
assertThat(result).isEmpty(); // Fallback accepts
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(1);
|
||||
|
||||
// Verify transaction has expected properties
|
||||
assertThat(complexTransaction.getGasPrice()).isPresent();
|
||||
assertThat(complexTransaction.getChainId()).isPresent();
|
||||
assertThat(complexTransaction.getSender()).isNotNull();
|
||||
assertThat(complexTransaction.getHash()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldResetStatisticsProperly() {
|
||||
// Given: validator with some processed transactions
|
||||
validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
Transaction transaction = createTestTransaction();
|
||||
|
||||
// Process some transactions
|
||||
validator.validateTransaction(transaction, true, false);
|
||||
validator.validateTransaction(transaction, false, false);
|
||||
|
||||
// When: creating new validator (simulates reset)
|
||||
validator.close();
|
||||
validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
|
||||
// Then: statistics should start fresh
|
||||
assertThat(validator.getValidationCallCount()).isEqualTo(0);
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(0);
|
||||
assertThat(validator.getPeerTransactionCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private Transaction createTestTransaction() {
|
||||
return new TransactionTestFixture()
|
||||
.gasPrice(Wei.of(10_000_000_000L))
|
||||
.chainId(Optional.of(BigInteger.valueOf(1)))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
}
|
||||
|
||||
private Transaction createTransactionWithGasPrice(final Wei gasPrice) {
|
||||
return new TransactionTestFixture()
|
||||
.gasPrice(gasPrice)
|
||||
.chainId(Optional.of(BigInteger.valueOf(1)))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
}
|
||||
|
||||
private Transaction createTransactionWithChainId(final BigInteger chainId) {
|
||||
return new TransactionTestFixture()
|
||||
.gasPrice(Wei.of(10_000_000_000L))
|
||||
.chainId(Optional.of(chainId))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* 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.eth.transactions.grpc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidator;
|
||||
import org.hyperledger.besu.plugin.services.txvalidator.PluginTransactionPoolValidatorFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Comprehensive test that validates the actual behavior of the gRPC transaction validator
|
||||
* implementation without relying on external gRPC services or complex mocking.
|
||||
*
|
||||
* <p>Tests focus on: - Core validation logic (local vs peer transaction handling) - Error handling
|
||||
* and fallback mechanisms - Transaction data processing and statistics - Integration with Besu's
|
||||
* plugin system
|
||||
*/
|
||||
class TransactionValidatorBehaviorTest {
|
||||
|
||||
private static final SignatureAlgorithm SIGNATURE_ALGORITHM =
|
||||
SignatureAlgorithmFactory.getInstance();
|
||||
|
||||
@Test
|
||||
void shouldDemonstrateCompleteValidationFlow() {
|
||||
// GIVEN: Create a gRPC validator factory (this is how Besu creates validators)
|
||||
String endpoint = "grpc-validator-service:8080";
|
||||
PluginTransactionPoolValidatorFactory factory =
|
||||
new GrpcTransactionPoolValidatorFactory(endpoint);
|
||||
|
||||
// WHEN: Besu creates a validator using the factory
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// THEN: Should get our gRPC validator
|
||||
assertThat(validator).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
|
||||
GrpcTransactionPoolValidator grpcValidator = (GrpcTransactionPoolValidator) validator;
|
||||
assertThat(grpcValidator.getEndpoint()).isEqualTo(endpoint);
|
||||
|
||||
// Cleanup
|
||||
grpcValidator.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProcessTransactionsAccordingToBusinessRules() {
|
||||
// GIVEN: gRPC validator with unreachable endpoint (tests fallback behavior)
|
||||
GrpcTransactionPoolValidator validator =
|
||||
new GrpcTransactionPoolValidator("unreachable-service:9999");
|
||||
|
||||
Transaction localTransaction =
|
||||
createTransactionWithData(
|
||||
Wei.of(50_000_000_000L), // 50 gwei gas price
|
||||
Wei.fromEth(1), // 1 ETH value
|
||||
BigInteger.valueOf(1337) // custom chain ID
|
||||
);
|
||||
|
||||
Transaction peerTransaction =
|
||||
createTransactionWithData(
|
||||
Wei.of(10_000_000_000L), // 10 gwei gas price
|
||||
Wei.fromEth(2), // 2 ETH value
|
||||
BigInteger.valueOf(1) // mainnet chain ID
|
||||
);
|
||||
|
||||
// WHEN: Processing different transaction types
|
||||
Optional<String> localResult = validator.validateTransaction(localTransaction, true, false);
|
||||
Optional<String> peerResult = validator.validateTransaction(peerTransaction, false, false);
|
||||
Optional<String> priorityResult = validator.validateTransaction(localTransaction, true, true);
|
||||
|
||||
// THEN: Business rules should be applied correctly
|
||||
|
||||
// Local transactions attempt gRPC validation, fallback to acceptance
|
||||
assertThat(localResult).isEmpty(); // Accepted via fallback
|
||||
|
||||
// Peer transactions bypass gRPC completely
|
||||
assertThat(peerResult).isEmpty(); // Accepted without gRPC call
|
||||
|
||||
// Priority transactions still go through same logic
|
||||
assertThat(priorityResult).isEmpty(); // Accepted via fallback
|
||||
|
||||
// Statistics should reflect the processing
|
||||
assertThat(validator.getValidationCallCount()).isEqualTo(3);
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(2); // local + priority
|
||||
assertThat(validator.getPeerTransactionCount()).isEqualTo(1);
|
||||
|
||||
validator.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExtractTransactionDataCorrectly() {
|
||||
// GIVEN: Validator and transaction with specific data
|
||||
GrpcTransactionPoolValidator validator = new GrpcTransactionPoolValidator("test-endpoint:8080");
|
||||
|
||||
// Create transaction with all the data fields that should be sent to gRPC
|
||||
Wei gasPrice = Wei.of(75_000_000_000L);
|
||||
BigInteger chainId = BigInteger.valueOf(42);
|
||||
|
||||
Transaction transaction =
|
||||
new TransactionTestFixture()
|
||||
.gasPrice(gasPrice)
|
||||
.gasLimit(100000)
|
||||
.value(Wei.fromEth(5))
|
||||
.chainId(Optional.of(chainId))
|
||||
.to(Optional.of(Address.fromHexString("0x742d35cc6634c0532925a3b8d039135682b2e78b")))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
|
||||
// WHEN: Processing the transaction (will attempt gRPC call and fallback)
|
||||
Optional<String> result = validator.validateTransaction(transaction, true, false);
|
||||
|
||||
// THEN: Should process successfully and extract key data
|
||||
assertThat(result).isEmpty(); // Fallback accepts
|
||||
|
||||
// Verify the transaction has the expected data that would be sent to gRPC
|
||||
assertThat(transaction.getHash()).isNotNull();
|
||||
assertThat(transaction.getSender()).isNotNull();
|
||||
assertThat(transaction.getGasPrice()).isPresent();
|
||||
assertThat(transaction.getGasPrice().get()).isEqualTo(gasPrice);
|
||||
assertThat(transaction.getChainId()).isPresent();
|
||||
assertThat(transaction.getChainId().get()).isEqualTo(chainId);
|
||||
|
||||
validator.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleFactoryBehaviorCorrectly() {
|
||||
// GIVEN: Different factory configurations
|
||||
|
||||
// Factory with valid endpoint
|
||||
GrpcTransactionPoolValidatorFactory validFactory =
|
||||
new GrpcTransactionPoolValidatorFactory("localhost:8080");
|
||||
|
||||
// Factory with no endpoint
|
||||
GrpcTransactionPoolValidatorFactory nullFactory = new GrpcTransactionPoolValidatorFactory(null);
|
||||
|
||||
GrpcTransactionPoolValidatorFactory emptyFactory = new GrpcTransactionPoolValidatorFactory("");
|
||||
|
||||
// WHEN: Creating validators
|
||||
PluginTransactionPoolValidator validValidator = validFactory.createTransactionValidator();
|
||||
PluginTransactionPoolValidator nullValidator = nullFactory.createTransactionValidator();
|
||||
PluginTransactionPoolValidator emptyValidator = emptyFactory.createTransactionValidator();
|
||||
|
||||
// THEN: Should behave according to configuration
|
||||
|
||||
// Valid endpoint creates gRPC validator
|
||||
assertThat(validValidator).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
|
||||
// Null/empty endpoints return default validator
|
||||
assertThat(nullValidator).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
assertThat(emptyValidator).isEqualTo(PluginTransactionPoolValidator.VALIDATE_ALL);
|
||||
|
||||
// Singleton behavior for same factory
|
||||
PluginTransactionPoolValidator validValidator2 = validFactory.createTransactionValidator();
|
||||
assertThat(validValidator).isSameAs(validValidator2);
|
||||
|
||||
// Cleanup
|
||||
((GrpcTransactionPoolValidator) validValidator).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldProvideMeaningfulStatistics() {
|
||||
// GIVEN: Validator for statistics testing
|
||||
GrpcTransactionPoolValidator validator = new GrpcTransactionPoolValidator("stats-test:8080");
|
||||
|
||||
Transaction tx = createTestTransaction();
|
||||
|
||||
// WHEN: Processing various transactions
|
||||
validator.validateTransaction(tx, true, false); // local
|
||||
validator.validateTransaction(tx, false, false); // peer
|
||||
validator.validateTransaction(tx, true, true); // local priority
|
||||
validator.validateTransaction(tx, false, true); // peer priority
|
||||
validator.validateTransaction(tx, true, false); // local again
|
||||
|
||||
// THEN: Statistics should be accurate and meaningful
|
||||
assertThat(validator.getValidationCallCount()).isEqualTo(5);
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(3); // 2 local + 1 priority
|
||||
assertThat(validator.getPeerTransactionCount()).isEqualTo(2);
|
||||
|
||||
// Statistics should be consistent
|
||||
assertThat(validator.getLocalTransactionCount() + validator.getPeerTransactionCount())
|
||||
.isEqualTo(validator.getValidationCallCount());
|
||||
|
||||
validator.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleEdgeCases() {
|
||||
// GIVEN: Validator for edge case testing
|
||||
GrpcTransactionPoolValidator validator =
|
||||
new GrpcTransactionPoolValidator("edge-case-test:8080");
|
||||
|
||||
// WHEN: Testing edge cases
|
||||
|
||||
// Transaction with zero gas price
|
||||
Transaction zeroGasTransaction =
|
||||
new TransactionTestFixture()
|
||||
.gasPrice(Wei.ZERO)
|
||||
.chainId(Optional.of(BigInteger.valueOf(1)))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
|
||||
// Transaction with very high gas price
|
||||
Transaction highGasTransaction =
|
||||
new TransactionTestFixture()
|
||||
.gasPrice(Wei.of(Long.MAX_VALUE))
|
||||
.chainId(Optional.of(BigInteger.valueOf(1)))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
|
||||
// Process edge case transactions
|
||||
Optional<String> zeroResult = validator.validateTransaction(zeroGasTransaction, true, false);
|
||||
Optional<String> highResult = validator.validateTransaction(highGasTransaction, true, false);
|
||||
|
||||
// THEN: Should handle edge cases gracefully
|
||||
assertThat(zeroResult).isEmpty(); // Fallback accepts
|
||||
assertThat(highResult).isEmpty(); // Fallback accepts
|
||||
|
||||
assertThat(validator.getLocalTransactionCount()).isEqualTo(2);
|
||||
|
||||
validator.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIntegrateWithBesuPluginSystem() {
|
||||
// GIVEN: Simulate how Besu's CLI would use the validator
|
||||
|
||||
// This simulates the CLI creating a factory based on configuration
|
||||
String configuredEndpoint = "rln-prover.example.com:9090";
|
||||
|
||||
// In Besu, the TransactionPoolOptions would create this factory
|
||||
PluginTransactionPoolValidatorFactory factory =
|
||||
new GrpcTransactionPoolValidatorFactory(configuredEndpoint);
|
||||
|
||||
// WHEN: Besu's transaction pool uses the factory
|
||||
PluginTransactionPoolValidator validator = factory.createTransactionValidator();
|
||||
|
||||
// THEN: Integration should work seamlessly
|
||||
assertThat(validator).isNotNull();
|
||||
assertThat(validator).isInstanceOf(GrpcTransactionPoolValidator.class);
|
||||
|
||||
GrpcTransactionPoolValidator grpcValidator = (GrpcTransactionPoolValidator) validator;
|
||||
assertThat(grpcValidator.getEndpoint()).isEqualTo(configuredEndpoint);
|
||||
|
||||
// Validator should be ready to process transactions
|
||||
Transaction testTx = createTestTransaction();
|
||||
Optional<String> result = validator.validateTransaction(testTx, true, false);
|
||||
assertThat(result).isEmpty(); // Fallback behavior
|
||||
|
||||
grpcValidator.close();
|
||||
}
|
||||
|
||||
private Transaction createTestTransaction() {
|
||||
return new TransactionTestFixture()
|
||||
.gasPrice(Wei.of(20_000_000_000L))
|
||||
.chainId(Optional.of(BigInteger.valueOf(1)))
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
}
|
||||
|
||||
private Transaction createTransactionWithData(
|
||||
final Wei gasPrice, final Wei value, final BigInteger chainId) {
|
||||
return new TransactionTestFixture()
|
||||
.gasPrice(gasPrice)
|
||||
.value(value)
|
||||
.chainId(Optional.of(chainId))
|
||||
.gasLimit(21000)
|
||||
.createTransaction(SIGNATURE_ALGORITHM.generateKeyPair());
|
||||
}
|
||||
}
|
||||
@@ -662,6 +662,14 @@
|
||||
<sha256 value="c0e547bea998888e6e25c5886a90e762272bc88b5275343dd2c05ded6ca2e360" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson" version="2.8.9">
|
||||
<artifact name="gson-2.8.9.jar">
|
||||
<sha256 value="d3999291855de495c94c743761b8ab5176cfeabe281a5ab0d8e8d45326fd703e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="gson-2.8.9.pom">
|
||||
<sha256 value="afded6e6a690fbf3ad4ae65ada397f0a90a5f630b303d1b741b9c97926fdd4de" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson" version="2.9.0">
|
||||
<artifact name="gson-2.9.0.pom">
|
||||
<sha256 value="7190d0b07f278e9f4c603f44e543940f81cf1a2559f851c6f298c9bb2be2978c" origin="Generated by Gradle"/>
|
||||
@@ -672,6 +680,11 @@
|
||||
<sha256 value="8acb1f3b72a6f026916ac0735bad9aab0293d527edb7b365327def13a9367b7a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson-parent" version="2.8.9">
|
||||
<artifact name="gson-parent-2.8.9.pom">
|
||||
<sha256 value="b16e026e63427c1972ad0fc68703ec379b1576e411ba49c32fa9a31ab0bbcffb" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson-parent" version="2.9.0">
|
||||
<artifact name="gson-parent-2.9.0.pom">
|
||||
<sha256 value="af781c9a5766ffea311a0df0536576a64decc661aa110c4de5c73ac8bf434424" origin="Generated by Gradle"/>
|
||||
@@ -717,6 +730,16 @@
|
||||
<sha256 value="efd58f6848c1805740d481ba919d455a2924ce7164927cbab7a83de6e26646d0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_annotations" version="2.18.0">
|
||||
<artifact name="error_prone_annotations-2.18.0.pom">
|
||||
<sha256 value="920135797dcca5917b5a5c017642a58d340a4cd1bcd12f56f892a5663bd7bddc" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_annotations" version="2.20.0">
|
||||
<artifact name="error_prone_annotations-2.20.0.pom">
|
||||
<sha256 value="94f50653af659be1d1a0d785f97cba768571f794d2e655fc3d22e4eee6512b39" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_annotations" version="2.21.1">
|
||||
<artifact name="error_prone_annotations-2.21.1.jar">
|
||||
<sha256 value="d1f3c66aa91ac52549e00ae3b208ba4b9af7d72d68f230643553beb38e6118ac" origin="Generated by Gradle"/>
|
||||
@@ -767,6 +790,16 @@
|
||||
<sha256 value="9484087f6a15933cabe7013c834142d9726e647628192a412b4c7d50ecccaf56" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_parent" version="2.18.0">
|
||||
<artifact name="error_prone_parent-2.18.0.pom">
|
||||
<sha256 value="47f22e99c7bf466391def16f8377985e5d3ba6f5bbcf65853644805513e15fad" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_parent" version="2.20.0">
|
||||
<artifact name="error_prone_parent-2.20.0.pom">
|
||||
<sha256 value="e0b8bc77fc794d22d50aa4851896d8202a616610161b13cbff2a148ff1e102c2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_parent" version="2.21.1">
|
||||
<artifact name="error_prone_parent-2.21.1.pom">
|
||||
<sha256 value="32bb0b5ff241fd6ba1feea448aebb9cedef1699be73cb6f319365387b82bf92c" origin="Generated by Gradle"/>
|
||||
@@ -855,6 +888,14 @@
|
||||
<sha256 value="13aaf29158343f8b9c7dd7d3f58610290b05ad29ea69c7b9504869e47fbf6319" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.gradle" name="osdetector-gradle-plugin" version="1.7.3">
|
||||
<artifact name="osdetector-gradle-plugin-1.7.3.jar">
|
||||
<sha256 value="6b4692f913a21b1fb603169ee78ba8f3e4ab2af9d762af9ca88b79126c1c0ad1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="osdetector-gradle-plugin-1.7.3.pom">
|
||||
<sha256 value="8460c950127ca3598766161e38b4ff8d63b4d69fb8310a16e00b351350010c11" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.guava" name="failureaccess" version="1.0.1">
|
||||
<artifact name="failureaccess-1.0.1.jar">
|
||||
<sha256 value="a171ee4c734dd2da837e4b16be9df4661afab72a41adaf31eb84dfdaf936ca26" origin="Generated by Gradle"/>
|
||||
@@ -992,6 +1033,11 @@
|
||||
<sha256 value="63d5c0c641797deba6c051c47d5f6923f3a103ef71e4ebc3a85d01c3e878596c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.j2objc" name="j2objc-annotations" version="2.8">
|
||||
<artifact name="j2objc-annotations-2.8.pom">
|
||||
<sha256 value="37f87798b18385113c918bfa9e1276fe50735ef8fa849b5800c519d54dbf11f8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.j2objc" name="j2objc-annotations" version="3.0.0">
|
||||
<artifact name="j2objc-annotations-3.0.0.jar">
|
||||
<sha256 value="88241573467ddca44ffd4d74aa04c2bbfd11bf7c17e0c342c94c9de7a70a7c64" origin="Generated by Gradle"/>
|
||||
@@ -1000,6 +1046,16 @@
|
||||
<sha256 value="23b3d039e168ad89dd114698e6dd7be383f4a2c577b8877d82c73a6515e74a17" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="com.google.protobuf.gradle.plugin" version="0.9.4">
|
||||
<artifact name="com.google.protobuf.gradle.plugin-0.9.4.pom">
|
||||
<sha256 value="4b03ee00dc5e3d4bbf755b26ee4d6ace2ab00c08315d3b28248910137b080edc" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-bom" version="3.25.1">
|
||||
<artifact name="protobuf-bom-3.25.1.pom">
|
||||
<sha256 value="43786970c1acb7c119c961f53ea856a4bc0bf6bf0ceffab0ad2a84f262cf1654" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-bom" version="3.25.5">
|
||||
<artifact name="protobuf-bom-3.25.5.pom">
|
||||
<sha256 value="080e2984173238b50e064c226afffbb1b0233520295c790a7fd3d6ae4593f063" origin="Generated by Gradle"/>
|
||||
@@ -1010,6 +1066,14 @@
|
||||
<sha256 value="be3baa9b418864b5ed7d39c3beb3141d83fa78d124554eaa92083243dcbfb27a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-gradle-plugin" version="0.9.4">
|
||||
<artifact name="protobuf-gradle-plugin-0.9.4.jar">
|
||||
<sha256 value="7e554bdec3202ede0a2407f20141d8ca2e9e3ab62429ffa9b21ab0c02f435223" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="protobuf-gradle-plugin-0.9.4.pom">
|
||||
<sha256 value="adba1612b20b5cdb9350e12475e25c31db59e80f937466c257cdf3f9b758e96e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-java" version="3.25.5">
|
||||
<artifact name="protobuf-java-3.25.5.jar">
|
||||
<sha256 value="8540247fad9e06baefa8fb45eb313802d019f485f14300e0f9d6b556ed88e753" origin="Generated by Gradle"/>
|
||||
@@ -1026,6 +1090,14 @@
|
||||
<sha256 value="f70bea74bd0b5325c2d2fc4d58d86163744019738d1ca436e067d733c5f7c125" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-java-util" version="3.25.1">
|
||||
<artifact name="protobuf-java-util-3.25.1.jar">
|
||||
<sha256 value="faf398ad0fe8c5a7d867f76d322e2e71bb31898fe86ec3223f787a6ed6fb4622" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="protobuf-java-util-3.25.1.pom">
|
||||
<sha256 value="c7a271d4ebdbe41e9fcc2d4584730dc644b17272f9bafd14e3e6c8fb3a233b78" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-java-util" version="3.25.5">
|
||||
<artifact name="protobuf-java-util-3.25.5.jar">
|
||||
<sha256 value="dacc58b2c3d2fa8d4bddc1acb881e78d6cf7c137dd78bc1d67f6aca732436a8d" origin="Generated by Gradle"/>
|
||||
@@ -1034,6 +1106,11 @@
|
||||
<sha256 value="a09d190eaa6a79616bc5f4b5404e94b0cab559803a98c8a090c4099962f41f92" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-parent" version="3.25.1">
|
||||
<artifact name="protobuf-parent-3.25.1.pom">
|
||||
<sha256 value="cd1be83739f036462b129b653759fa868bfad43fb9539c5e7d1de2ac3d85dcea" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protobuf-parent" version="3.25.5">
|
||||
<artifact name="protobuf-parent-3.25.5.pom">
|
||||
<sha256 value="64cc0e3ad6e85f5aec8f9dcf9341d1379e9525364ff53e23e16d8d5824673ef7" origin="Generated by Gradle"/>
|
||||
@@ -1044,6 +1121,14 @@
|
||||
<sha256 value="5364cdc058b2e5a212af6dfc1894299070dd434026a15827bbe313774489f403" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.protobuf" name="protoc" version="3.25.1">
|
||||
<artifact name="protoc-3.25.1-osx-aarch_64.exe">
|
||||
<sha256 value="b6ed65c0d20a9ab88ec6995644f747d557cb9a087eab0152fef5367c34645dc3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="protoc-3.25.1.pom">
|
||||
<sha256 value="d7fc0531c903580a1dfff9734e41eca2f3b4380ae7e652cdc1de15a91f1fef31" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.re2j" name="re2j" version="1.7">
|
||||
<artifact name="re2j-1.7.jar">
|
||||
<sha256 value="4f657af51ab8bb0909bcc3eb40862d26125af8cbcf92aaaba595fed77f947bc0" origin="Generated by Gradle"/>
|
||||
@@ -1530,6 +1615,11 @@
|
||||
<sha256 value="264fec0a1008e3e8dab9126bcb450f10a4c6e1213e8ab9f3395059715c3f1221" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-api" version="1.60.0">
|
||||
<artifact name="grpc-api-1.60.0.pom">
|
||||
<sha256 value="431dd4ff71bf1bb5983ed04de53d82716fb49610f60d568e356ddd3743aa9e49" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-api" version="1.70.0">
|
||||
<artifact name="grpc-api-1.70.0.jar">
|
||||
<sha256 value="45faf2ac1bf2791e8fdabce53684a86b62c99b84cba26fb13a5ba3f4abf80d6c" origin="Generated by Gradle"/>
|
||||
@@ -1559,6 +1649,11 @@
|
||||
<sha256 value="ae761bb574d49a40f9f7723c08ad0eb423930df408577170774db5a2e55fbf14" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-core" version="1.60.0">
|
||||
<artifact name="grpc-core-1.60.0.pom">
|
||||
<sha256 value="52cfdb9ac707b0af5d67be780e24c0643e53ab90440d7aa4320b00e702afe88b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-core" version="1.70.0">
|
||||
<artifact name="grpc-core-1.70.0.jar">
|
||||
<sha256 value="c2b5576b8b363b1b1006673c492d912500baaa1581430a7f9c05e82cc5bdfba4" origin="Generated by Gradle"/>
|
||||
@@ -1583,6 +1678,11 @@
|
||||
<sha256 value="b7c5483a6ab2c3bbf56ffede9f098eca1f11e119375584357ed5d1074991247e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-inprocess" version="1.60.0">
|
||||
<artifact name="grpc-inprocess-1.60.0.pom">
|
||||
<sha256 value="23fef188039d606f8b16250a46dbf5a2b64e9e69ce8806af9ec44224e854d8e2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-inprocess" version="1.70.0">
|
||||
<artifact name="grpc-inprocess-1.70.0.jar">
|
||||
<sha256 value="d9410b06d39383980e1489785d9b347c868839764fb69e588327471d5b73e79f" origin="Generated by Gradle"/>
|
||||
@@ -1671,6 +1771,11 @@
|
||||
<sha256 value="3691cd3280ec94a3f8c167bd1ee0c7ed5b94affef6e4f0810220739062686b6b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-stub" version="1.60.0">
|
||||
<artifact name="grpc-stub-1.60.0.pom">
|
||||
<sha256 value="78788e2485f6c932900f6d502791c244517d9a6200dc66f0a4b3a80f950e3a44" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-stub" version="1.70.0">
|
||||
<artifact name="grpc-stub-1.70.0.jar">
|
||||
<sha256 value="5adaa1ec1f744b67ae14a8dbc39c9589c010fad0fd557b0a02966202e4d23a18" origin="Generated by Gradle"/>
|
||||
@@ -1679,6 +1784,11 @@
|
||||
<sha256 value="91a91b7d0e802674a453a8079b609542ffb52230ec6bfd376d1758eec5f63ad2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-testing" version="1.60.0">
|
||||
<artifact name="grpc-testing-1.60.0.pom">
|
||||
<sha256 value="b2a3eff0c8c7d36c982f63731721694b694c5e30c82f835b090e55fb882c5836" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-testing" version="1.70.0">
|
||||
<artifact name="grpc-testing-1.70.0.jar">
|
||||
<sha256 value="4f910c09d81a863d7613cdb80d870477f396a6634a3cae2ce3bd21597cf29422" origin="Generated by Gradle"/>
|
||||
@@ -1687,6 +1797,11 @@
|
||||
<sha256 value="4a59991ee352b060b55098f4ed9c5c822fb6637f958bde02e3ce67d1ea0950f1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-util" version="1.60.0">
|
||||
<artifact name="grpc-util-1.60.0.pom">
|
||||
<sha256 value="29c1a2b30827c678a3c5e93138c70561a98208ee7ff78883de0248ad31820914" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-util" version="1.70.0">
|
||||
<artifact name="grpc-util-1.70.0.jar">
|
||||
<sha256 value="683aff93d2cabc44ff21dc9ab7794f8ae7b4c65d18748c8474535311eabe8dc4" origin="Generated by Gradle"/>
|
||||
@@ -1703,6 +1818,14 @@
|
||||
<sha256 value="3b2b9f4bdb4a4993f5509de57ae29b220dfc8c7b3a01df7e019835843fef2248" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="protoc-gen-grpc-java" version="1.60.0">
|
||||
<artifact name="protoc-gen-grpc-java-1.60.0-osx-aarch_64.exe">
|
||||
<sha256 value="bb6c0c079998ee7080e66ea122dfb66a34a602482a7ed1760b30b7324fdf8ede" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="protoc-gen-grpc-java-1.60.0.pom">
|
||||
<sha256 value="e82d7256d7230aac0fc8276f57c96e3b98ad6fcda8a074fe7d4935ee21cb7008" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-all" version="4.1.118.Final">
|
||||
<artifact name="netty-all-4.1.118.Final.jar">
|
||||
<sha256 value="44790974f057722b9d9d19dcb7afb91504eeb46d81fa2494db4f0fe01c5b7242" origin="Generated by Gradle"/>
|
||||
@@ -2739,6 +2862,14 @@
|
||||
<sha256 value="0a7b9c6327968afe0d82f7cf4facbda7e157968c5808d69bcb8019863b298dc4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="javax.annotation" name="javax.annotation-api" version="1.3.2">
|
||||
<artifact name="javax.annotation-api-1.3.2.jar">
|
||||
<sha256 value="e04ba5195bcd555dc95650f7cc614d151e4bcd52d29a10b8aa2197f3ab89ab9b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="javax.annotation-api-1.3.2.pom">
|
||||
<sha256 value="46a4a251ca406e78e4853d7a2bae83282844a4992851439ee9a1f23716f06b97" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="javax.inject" name="javax.inject" version="1">
|
||||
<artifact name="javax.inject-1.jar">
|
||||
<sha256 value="91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff" origin="Generated by Gradle"/>
|
||||
@@ -2763,6 +2894,14 @@
|
||||
<sha256 value="569b6977ee4603c965c1c46c3058fa6e969291b0160eb6964dd092cd89eadd94" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="kr.motd.maven" name="os-maven-plugin" version="1.7.1">
|
||||
<artifact name="os-maven-plugin-1.7.1.jar">
|
||||
<sha256 value="f47aeef86821e52b2b18758978bd045f03d722292e32e747082122c6228952e0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="os-maven-plugin-1.7.1.pom">
|
||||
<sha256 value="4b758004422b9633dd318f29e784f1d180bd8a5920cd50af1930861f6d6a5476" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="me.champeau.jmh" name="jmh-gradle-plugin" version="0.7.2">
|
||||
<artifact name="jmh-gradle-plugin-0.7.2.jar">
|
||||
<sha256 value="d9672099ff8fc3f9bf3d4d015864e1586f07ecbd2a8a177a66184ef0b68aba65" origin="Generated by Gradle"/>
|
||||
@@ -2797,6 +2936,11 @@
|
||||
<sha256 value="8dc519d7a3e792112a7cd841eafbec4f00dbc633d085add20c45f0ab476ca496" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="net.java" name="jvnet-parent" version="3">
|
||||
<artifact name="jvnet-parent-3.pom">
|
||||
<sha256 value="30f5789efa39ddbf96095aada3fc1260c4561faf2f714686717cb2dc5049475a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="net.java" name="jvnet-parent" version="5">
|
||||
<artifact name="jvnet-parent-5.pom">
|
||||
<sha256 value="1af699f8d9ddab67f9a0d202fbd7915eb0362a5a6dfd5ffc54cafa3465c9cb0a" origin="Generated by Gradle"/>
|
||||
|
||||
Reference in New Issue
Block a user