GraphQL schema + adapter updates to handle GoQuorum transaction fields (#2490)

* Added graphQL schema update and associated Adapter methods.

Signed-off-by: Mark Terry <mark.terry@consensys.net>
pull/2506/head
mark-terry 3 years ago committed by GitHub
parent f2b09be408
commit 7ed88b70f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
  2. 4
      build.gradle
  3. 57
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/GraphQLDataFetchers.java
  4. 12
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/graphql/internal/pojoadapter/TransactionAdapter.java
  5. 19
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/GoQuorumEthGetQuorumPayload.java
  6. 4
      ethereum/api/src/main/resources/schema.graphqls
  7. 16
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/goquorum/GoQuorumEthGetQuorumPayloadTest.java

@ -568,7 +568,9 @@ public class RunnerBuilder {
Optional<GraphQLHttpService> graphQLHttpService = Optional.empty();
if (graphQLConfiguration.isEnabled()) {
final GraphQLDataFetchers fetchers = new GraphQLDataFetchers(supportedCapabilities);
final GraphQLDataFetchers fetchers =
new GraphQLDataFetchers(
supportedCapabilities, privacyParameters.getGoQuorumPrivacyParameters());
final GraphQLDataFetcherContextImpl dataFetcherContext =
new GraphQLDataFetcherContextImpl(
blockchainQueries,

@ -663,11 +663,11 @@ task acceptanceTestsQuorum {
* Not available features in Besu: privacy-enhancements-disabled, extension, mps
* Not available RPC methods in Besu: async, storage-root, get-quorum-payload, personal-api-signed
*
* Ignored for now (privacy-polishing): graphql, eth-api-signed, spam, nosupport
* Ignored for now (privacy-polishing): eth-api-signed, spam, nosupport
*
* LOGGING_LEVEL_COM_QUORUM_GAUGE=DEBUG -- enables HTTP JSON-RPC logging
*/
def tags = "(basic && !nosupport && !mps && !spam && !eth-api-signed && !privacy-enhancements-disabled && !graphql && !async && !extension && !storage-root && !get-quorum-payload && !personal-api-signed) || networks/typical-besu::ibft2"
def tags = "(basic && !nosupport && !mps && !spam && !eth-api-signed && !privacy-enhancements-disabled && !async && !extension && !storage-root && !get-quorum-payload && !personal-api-signed) || networks/typical-besu::ibft2"
doLast {
exec {

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.api.graphql;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.AccountAdapter;
import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.EmptyAccountAdapter;
import org.hyperledger.besu.ethereum.api.graphql.internal.pojoadapter.LogAdapter;
@ -30,6 +31,7 @@ import org.hyperledger.besu.ethereum.api.query.LogsQuery;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.LogTopic;
import org.hyperledger.besu.ethereum.core.LogWithMetadata;
@ -56,10 +58,24 @@ import java.util.stream.Collectors;
import com.google.common.base.Preconditions;
import graphql.schema.DataFetcher;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class GraphQLDataFetchers {
private static final Logger LOG = LogManager.getLogger();
private Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters = Optional.empty();
public GraphQLDataFetchers(
final Set<Capability> supportedCapabilities,
final Optional<GoQuorumPrivacyParameters> goQuorumPrivacyParameters) {
this(supportedCapabilities);
this.goQuorumPrivacyParameters = goQuorumPrivacyParameters;
}
public GraphQLDataFetchers(final Set<Capability> supportedCapabilities) {
final OptionalInt version =
supportedCapabilities.stream()
@ -259,7 +275,46 @@ public class GraphQLDataFetchers {
((GraphQLDataFetcherContext) dataFetchingEnvironment.getContext()).getBlockchainQueries();
final Bytes32 hash = dataFetchingEnvironment.getArgument("hash");
final Optional<TransactionWithMetadata> tran = blockchain.transactionByHash(Hash.wrap(hash));
return tran.map(TransactionAdapter::new);
return tran.map(this::getTransactionAdapter);
};
}
private TransactionAdapter getTransactionAdapter(
final TransactionWithMetadata transactionWithMetadata) {
final Transaction transaction = transactionWithMetadata.getTransaction();
return goQuorumPrivacyParameters.isPresent() && transaction.isGoQuorumPrivateTransaction()
? updatePrivatePayload(transaction)
: new TransactionAdapter(transactionWithMetadata);
}
private TransactionAdapter updatePrivatePayload(final Transaction transaction) {
final GoQuorumEnclave enclave = goQuorumPrivacyParameters.get().enclave();
Bytes enclavePayload;
try {
// Retrieve the payload from the enclave
enclavePayload =
Bytes.wrap(enclave.receive(transaction.getPayload().toBase64String()).getPayload());
} catch (final Exception ex) {
LOG.debug("An error occurred while retrieving the GoQuorum transaction payload: ", ex);
enclavePayload = Bytes.EMPTY;
}
// Return a new transaction containing the retrieved payload
return new TransactionAdapter(
new TransactionWithMetadata(
new Transaction(
transaction.getNonce(),
transaction.getGasPrice(),
transaction.getMaxPriorityFeePerGas(),
transaction.getMaxFeePerGas(),
transaction.getGasLimit(),
transaction.getTo(),
transaction.getValue(),
transaction.getSignature(),
enclavePayload,
transaction.getSender(),
transaction.getChainId(),
Optional.ofNullable(transaction.getV()))));
}
}

@ -185,4 +185,16 @@ public class TransactionAdapter extends AdapterBase {
}
return results;
}
public boolean getIsPrivate() {
return transactionWithMetadata.getTransaction().isGoQuorumPrivateTransaction();
}
public Optional<Bytes> getPrivateInputData() {
final Transaction transaction = transactionWithMetadata.getTransaction();
if (transaction.isGoQuorumPrivateTransaction()) {
return Optional.ofNullable(transaction.getPayload());
}
return Optional.of(Bytes.EMPTY);
}
}

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
@ -60,8 +61,20 @@ public class GoQuorumEthGetQuorumPayload implements JsonRpcMethod {
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
}
final GoQuorumReceiveResponse receive = enclave.receive(bytes.toBase64String());
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(), Bytes.wrap(receive.getPayload()).toHexString());
try {
final GoQuorumReceiveResponse receive = enclave.receive(bytes.toBase64String());
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(), Bytes.wrap(receive.getPayload()).toHexString());
} catch (final EnclaveClientException ex) {
if (ex.getStatusCode() == 404) {
return new JsonRpcSuccessResponse(
requestContext.getRequest().getId(), Bytes.EMPTY.toHexString());
} else {
LOG.debug("Error retrieving enclave payload: ", ex);
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INTERNAL_ERROR);
}
}
}
}

@ -96,6 +96,10 @@ type Transaction {
# Logs is a list of log entries emitted by this transaction. If the
# transaction has not yet been mined, this field will be null.
logs: [Log!]
# IsPrivate is an indicator of a GoQuorum private transaction
isPrivate: Boolean
# PrivateInputData is the actual payload of a GoQuorum private transaction
privateInputData: Bytes
}
# BlockFilterCriteria encapsulates log filter criteria for a filter applied

@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
@ -130,4 +131,19 @@ public class GoQuorumEthGetQuorumPayloadTest {
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
assertThat(response.toString()).contains("INVALID_PARAMS");
}
@Test
public void enclave404ReturnsEmptyBytesString() {
final String hexString = Bytes.wrap(new byte[64]).toHexString();
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString}));
when(enclave.receive(any())).thenThrow(new EnclaveClientException(404, null));
final JsonRpcResponse response = method.response(request);
assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
assertThat(((JsonRpcSuccessResponse) response).getResult())
.isEqualTo(Bytes.EMPTY.toHexString());
}
}

Loading…
Cancel
Save