add eth_getQuorumPayload (#2470)

* add eth_getQuorumPayload

Signed-off-by: Stefan Pingel <stefan.pingel@consensys.net>
pull/2475/head
Stefan Pingel 3 years ago committed by GitHub
parent b55b076a91
commit fd18b0a6fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/ContainerTests.java
  2. 18
      container-tests/tests/src/test/java/org/hyperledger/besu/tests/container/helpers/ContractOperations.java
  3. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java
  4. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractBlockParameterOrBlockHashMethod.java
  5. 67
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/GoQuorumEthGetQuorumPayload.java
  6. 9
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/GoQuorumJsonRpcPrivacyMethods.java
  7. 3
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java
  8. 133
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/goquorum/GoQuorumEthGetQuorumPayloadTest.java

@ -18,7 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.Assertions.fail;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.deployContractAndReturnAddress; import static org.hyperledger.besu.tests.container.helpers.ContractOperations.deployContractAndReturnAddress;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.generateHexString; import static org.hyperledger.besu.tests.container.helpers.ContractOperations.generate64BytesHexString;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.getCode; import static org.hyperledger.besu.tests.container.helpers.ContractOperations.getCode;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.getTransactionLog; import static org.hyperledger.besu.tests.container.helpers.ContractOperations.getTransactionLog;
import static org.hyperledger.besu.tests.container.helpers.ContractOperations.sendLogEventAndReturnTransactionHash; import static org.hyperledger.besu.tests.container.helpers.ContractOperations.sendLogEventAndReturnTransactionHash;
@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import org.jetbrains.annotations.NotNull;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.web3j.abi.EventEncoder; import org.web3j.abi.EventEncoder;
@ -43,10 +44,12 @@ import org.web3j.crypto.Credentials;
import org.web3j.protocol.core.DefaultBlockParameterName; import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.EthFilter; import org.web3j.protocol.core.methods.request.EthFilter;
import org.web3j.protocol.core.methods.response.EthLog; import org.web3j.protocol.core.methods.response.EthLog;
import org.web3j.protocol.core.methods.response.EthTransaction;
import org.web3j.protocol.exceptions.TransactionException; import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.quorum.enclave.Enclave; import org.web3j.quorum.enclave.Enclave;
import org.web3j.quorum.enclave.Tessera; import org.web3j.quorum.enclave.Tessera;
import org.web3j.quorum.enclave.protocol.EnclaveService; import org.web3j.quorum.enclave.protocol.EnclaveService;
import org.web3j.quorum.methods.response.PrivatePayload;
import org.web3j.quorum.tx.QuorumTransactionManager; import org.web3j.quorum.tx.QuorumTransactionManager;
import org.web3j.tx.response.PollingTransactionReceiptProcessor; import org.web3j.tx.response.PollingTransactionReceiptProcessor;
@ -103,7 +106,7 @@ public class ContainerTests extends ContainerTestBase {
goQuorumPollingTransactionReceiptProcessor); goQuorumPollingTransactionReceiptProcessor);
// Generate a random value to insert into the log // Generate a random value to insert into the log
final String logValue = generateHexString(98765L); final String logValue = generate64BytesHexString(98765L);
// Send the transaction and get the transaction hash // Send the transaction and get the transaction hash
final String transactionHash = final String transactionHash =
@ -131,6 +134,19 @@ public class ContainerTests extends ContainerTestBase {
final String codeValueGoQuorum = getCode(goQuorumWeb3j, contractAddress); final String codeValueGoQuorum = getCode(goQuorumWeb3j, contractAddress);
assertThat(codeValueGoQuorum).startsWith(CONTRACT_PREFIX); assertThat(codeValueGoQuorum).startsWith(CONTRACT_PREFIX);
assertThat(codeValueBesu).isEqualTo(codeValueGoQuorum); assertThat(codeValueBesu).isEqualTo(codeValueGoQuorum);
// Assert that the private payloads returned are the same
final String enclaveKey = getEnclaveKey(transactionHash);
final PrivatePayload goQuorumPayload = goQuorumWeb3j.quorumGetPrivatePayload(enclaveKey).send();
final PrivatePayload besuPayload = besuWeb3j.quorumGetPrivatePayload(enclaveKey).send();
assertThat(goQuorumPayload.getPrivatePayload()).isEqualTo(besuPayload.getPrivatePayload());
}
@NotNull
private String getEnclaveKey(final String transactionHash) throws IOException {
final EthTransaction send = besuWeb3j.ethGetTransactionByHash(transactionHash).send();
return send.getTransaction().get().getInput();
} }
@Test @Test
@ -155,7 +171,7 @@ public class ContainerTests extends ContainerTestBase {
besuPollingTransactionReceiptProcessor); besuPollingTransactionReceiptProcessor);
// Generate a random value to insert into the log // Generate a random value to insert into the log
final String logValue = generateHexString(192837L); final String logValue = generate64BytesHexString(192837L);
// Send the transaction and get the transaction hash // Send the transaction and get the transaction hash
final String transactionHash = final String transactionHash =
@ -216,7 +232,7 @@ public class ContainerTests extends ContainerTestBase {
ethFilterSubscription.addSingleTopic(eventEncoded); ethFilterSubscription.addSingleTopic(eventEncoded);
// Generate a value to insert into the log // Generate a value to insert into the log
final String logValue = generateHexString((1234567L)); final String logValue = generate64BytesHexString(1234567L);
final AtomicBoolean checked = new AtomicBoolean(false); final AtomicBoolean checked = new AtomicBoolean(false);
final Disposable subscribe = final Disposable subscribe =

@ -21,6 +21,7 @@ import java.math.BigInteger;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.web3j.abi.FunctionEncoder; import org.web3j.abi.FunctionEncoder;
import org.web3j.crypto.Credentials; import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction; import org.web3j.crypto.RawTransaction;
@ -138,13 +139,20 @@ public class ContractOperations {
return logReceiptResult.getTransactionHash(); return logReceiptResult.getTransactionHash();
} }
public static String generateHexString(final long number) { public static String generate64BytesHexString(final long number) {
final StringBuilder randomValue = new StringBuilder(Long.toHexString(number)); final String str = Long.toHexString(number);
while (randomValue.length() < 64) { return prependZeroesToPadHexStringToGivenLength(str, 64);
randomValue.insert(0, '0');
} }
return randomValue.toString();
@NotNull
public static String prependZeroesToPadHexStringToGivenLength(
final String hexString, final int lenRequested) {
final StringBuilder sb = new StringBuilder(hexString);
while (sb.length() < lenRequested) {
sb.insert(0, '0');
}
return sb.toString();
} }
public static int getNonce(final Quorum quorum, final Credentials credentials) public static int getNonce(final Quorum quorum, final Credentials credentials)

@ -43,6 +43,7 @@ public enum RpcMethod {
DEBUG_TRACE_TRANSACTION("debug_traceTransaction"), DEBUG_TRACE_TRANSACTION("debug_traceTransaction"),
DEBUG_BATCH_RAW_TRANSACTION("debug_batchSendRawTransaction"), DEBUG_BATCH_RAW_TRANSACTION("debug_batchSendRawTransaction"),
DEBUG_GET_BAD_BLOCKS("debug_getBadBlocks"), DEBUG_GET_BAD_BLOCKS("debug_getBadBlocks"),
GOQUORUM_ETH_GET_QUORUM_PAYLOAD("eth_getQuorumPayload"),
GOQUORUM_STORE_RAW("goquorum_storeRaw"), GOQUORUM_STORE_RAW("goquorum_storeRaw"),
PRIV_CALL("priv_call"), PRIV_CALL("priv_call"),
PRIV_GET_PRIVATE_TRANSACTION("priv_getPrivateTransaction"), PRIV_GET_PRIVATE_TRANSACTION("priv_getPrivateTransaction"),

@ -72,7 +72,7 @@ public abstract class AbstractBlockParameterOrBlockHashMethod implements JsonRpc
} else if (blockParameterOrBlockHash.isPending()) { } else if (blockParameterOrBlockHash.isPending()) {
result = pendingResult(requestContext); result = pendingResult(requestContext);
} else if (blockParameterOrBlockHash.isNumeric() || blockParameterOrBlockHash.isEarliest()) { } else if (blockParameterOrBlockHash.isNumeric() || blockParameterOrBlockHash.isEarliest()) {
OptionalLong blockNumber = blockParameterOrBlockHash.getNumber(); final OptionalLong blockNumber = blockParameterOrBlockHash.getNumber();
if (blockNumber.isEmpty() || blockNumber.getAsLong() < 0) { if (blockNumber.isEmpty() || blockNumber.getAsLong() < 0) {
return new JsonRpcErrorResponse( return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS); requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS);

@ -0,0 +1,67 @@
/*
* Copyright ConsenSys AG.
*
* 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.api.jsonrpc.internal.privacy.methods.priv;
import static org.apache.logging.log4j.LogManager.getLogger;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
public class GoQuorumEthGetQuorumPayload implements JsonRpcMethod {
private static final Logger LOG = getLogger();
private final GoQuorumEnclave enclave;
public GoQuorumEthGetQuorumPayload(final GoQuorumEnclave enclave) {
this.enclave = enclave;
}
@Override
public String getName() {
return RpcMethod.GOQUORUM_ETH_GET_QUORUM_PAYLOAD.getMethodName();
}
@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
final String key = requestContext.getRequiredParameter(0, String.class);
final Bytes bytes;
try {
bytes = Bytes.fromHexString(key);
} catch (final IllegalArgumentException e) {
LOG.debug("Enclave key contains invalid hex character {}", key);
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
}
if (bytes.size() != 64) {
LOG.debug("Enclave key expected length 64, but length is {}", bytes.size());
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());
}
}

@ -14,10 +14,12 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.methods; package org.hyperledger.besu.ethereum.api.jsonrpc.methods;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumEthGetQuorumPayload;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumSendRawPrivateTransaction; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumSendRawPrivateTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumStoreRawPrivateTransaction; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumStoreRawPrivateTransaction;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
@ -48,11 +50,14 @@ public class GoQuorumJsonRpcPrivacyMethods extends PrivacyApiGroupJsonRpcMethods
protected Map<String, JsonRpcMethod> create( protected Map<String, JsonRpcMethod> create(
final PrivacyController privacyController, final PrivacyController privacyController,
final EnclavePublicKeyProvider enclavePublicKeyProvider) { final EnclavePublicKeyProvider enclavePublicKeyProvider) {
if (goQuorumParameters.isPresent()) { if (goQuorumParameters.isPresent()) {
final GoQuorumEnclave enclave = goQuorumParameters.get().enclave();
return mapOf( return mapOf(
new GoQuorumSendRawPrivateTransaction( new GoQuorumSendRawPrivateTransaction(
goQuorumParameters.get().enclave(), getTransactionPool(), enclavePublicKeyProvider), enclave, getTransactionPool(), enclavePublicKeyProvider),
new GoQuorumStoreRawPrivateTransaction(goQuorumParameters.get().enclave())); new GoQuorumStoreRawPrivateTransaction(enclave),
new GoQuorumEthGetQuorumPayload(enclave));
} else { } else {
return Collections.emptyMap(); return Collections.emptyMap();
} }

@ -152,7 +152,8 @@ public abstract class PrivacyApiGroupJsonRpcMethods extends ApiGroupJsonRpcMetho
final PrivacyParameters privacyParameters, final JsonRpcMethod rpcMethod) { final PrivacyParameters privacyParameters, final JsonRpcMethod rpcMethod) {
final String methodName = rpcMethod.getName(); final String methodName = rpcMethod.getName();
if (methodName.equals(RpcMethod.ETH_SEND_RAW_PRIVATE_TRANSACTION.getMethodName()) if (methodName.equals(RpcMethod.ETH_SEND_RAW_PRIVATE_TRANSACTION.getMethodName())
|| methodName.equals(RpcMethod.GOQUORUM_STORE_RAW.getMethodName())) { || methodName.equals(RpcMethod.GOQUORUM_STORE_RAW.getMethodName())
|| methodName.equals(RpcMethod.GOQUORUM_ETH_GET_QUORUM_PAYLOAD.getMethodName())) {
return rpcMethod; return rpcMethod;
} else if (privacyParameters.isEnabled() && privacyParameters.isMultiTenancyEnabled()) { } else if (privacyParameters.isEnabled() && privacyParameters.isMultiTenancyEnabled()) {
return new MultiTenancyRpcMethodDecorator(rpcMethod); return new MultiTenancyRpcMethodDecorator(rpcMethod);

@ -0,0 +1,133 @@
/*
* Copyright ConsenSys AG.
*
* 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.api.jsonrpc.internal.privacy.methods.goquorum;
import static org.assertj.core.api.Assertions.assertThat;
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.GoQuorumEnclave;
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.GoQuorumEthGetQuorumPayload;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class GoQuorumEthGetQuorumPayloadTest {
@Mock GoQuorumEthGetQuorumPayload method;
@Mock GoQuorumEnclave enclave;
@Before
public void before() {
method = new GoQuorumEthGetQuorumPayload(enclave);
}
@Test
public void correctRequest() {
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()))
.thenReturn(new GoQuorumReceiveResponse(new byte[10], 0, null, null));
final JsonRpcResponse response = method.response(request);
assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
assertThat(((JsonRpcSuccessResponse) response).getResult().toString())
.isEqualTo("0x00000000000000000000");
}
@Test
public void requestIsMissingParameter() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {}));
assertThatThrownBy(() -> method.response(request))
.isInstanceOf(InvalidJsonRpcParameters.class)
.hasMessage("Missing required json rpc parameter at index 0");
}
@Test
public void requestHasNullObjectParameter() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "eth_getQuorumPayload", null));
assertThatThrownBy(() -> method.response(request))
.isInstanceOf(InvalidJsonRpcParameters.class)
.hasMessage("Missing required json rpc parameter at index 0");
}
@Test
public void requestHasNullArrayParameter() {
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {null}));
assertThatThrownBy(() -> method.response(request))
.isInstanceOf(InvalidJsonRpcParameters.class)
.hasMessage("Missing required json rpc parameter at index 0");
}
@Test
public void requestHasShortHex() {
final String hexString = Bytes.wrap(new byte[63]).toHexString();
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString}));
final JsonRpcResponse response = method.response(request);
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
assertThat(response.toString()).contains("INVALID_PARAMS");
}
@Test
public void requestHasLongHex() {
final String hexString = Bytes.wrap(new byte[65]).toHexString();
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString}));
final JsonRpcResponse response = method.response(request);
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
assertThat(response.toString()).contains("INVALID_PARAMS");
}
@Test
public void requestNonHexString() {
final String hexString = Bytes.wrap(new byte[63]).toHexString() + "f";
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("2.0", "eth_getQuorumPayload", new String[] {hexString}));
final JsonRpcResponse response = method.response(request);
assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
assertThat(response.toString()).contains("INVALID_PARAMS");
}
}
Loading…
Cancel
Save