Skip to content

Commit

Permalink
GraphQL schema + adapter updates to handle GoQuorum transaction fields (
Browse files Browse the repository at this point in the history
#2490)

* Added graphQL schema update and associated Adapter methods.

Signed-off-by: Mark Terry <mark.terry@consensys.net>
  • Loading branch information
mark-terry authored Jul 1, 2021
1 parent f2b09be commit 7ed88b7
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 7 deletions.
4 changes: 3 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,9 @@ public Runner build() {

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,
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

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;
Expand All @@ -30,6 +31,7 @@
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;
Expand All @@ -56,10 +58,24 @@

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()
Expand Down Expand Up @@ -259,7 +275,46 @@ DataFetcher<Optional<TransactionAdapter>> getTransactionDataFetcher() {
((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()))));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,16 @@ public List<LogAdapter> getLogs(final DataFetchingEnvironment environment) {
}
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

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;
Expand Down Expand Up @@ -60,8 +61,20 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
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);
}
}
}
}
4 changes: 4 additions & 0 deletions ethereum/api/src/main/resources/schema.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand Down Expand Up @@ -130,4 +131,19 @@ public void requestNonHexString() {
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());
}
}

0 comments on commit 7ed88b7

Please sign in to comment.