Skip to content

Commit

Permalink
Implemented priv_debugGetStateRoot JSON RPC API. (hyperledger#1326)
Browse files Browse the repository at this point in the history
* Implemented priv_debugGetStateRoot JSON RPC API.

Signed-off-by: Mark Terry <mark.terry@consensys.net>
  • Loading branch information
mark-terry authored Aug 27, 2020
1 parent b0cdf66 commit 2691deb
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 78 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Changelog

### Additions and Improvements

* Added `priv_debugGetStateRoot` JSON-RPC API to retrieve the state root of a specified privacy group. [\#1326](https://github.com/hyperledger/besu/pull/1326)

## 1.5.3

### Additions and Improvements

* The EvmTool now processes State Tests from the Ethereum Reference Tests. [\#1311](https://github.com/hyperledger/besu/pull/1311)
* Experimental dns support added via the `Xdns-enabled` and `Xdns-update-enabled` CLI commands. [\#1247](https://github.com/hyperledger/besu/pull/1247)
* Add genesis config option `ecip1017EraRounds` for Ethereum Classic chanis. [\#1329](https://github.com/hyperledger/besu/pull/1329)
* Add genesis config option `ecip1017EraRounds` for Ethereum Classic chains. [\#1329](https://github.com/hyperledger/besu/pull/1329)

### Bug Fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public void setUp() throws Exception {

privacyController =
new DefaultPrivacyController(
blockchain, privateStateStorage, enclave, null, null, null, null, null);
blockchain, privateStateStorage, enclave, null, null, null, null, null, null);
}

@After
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public enum RpcMethod {
PRIV_CREATE_PRIVACY_GROUP("priv_createPrivacyGroup"),
PRIV_DELETE_PRIVACY_GROUP("priv_deletePrivacyGroup"),
PRIV_FIND_PRIVACY_GROUP("priv_findPrivacyGroup"),
PRIV_DEBUG_GET_STATE_ROOT("priv_debugGetStateRoot"),
PRIV_DISTRIBUTE_RAW_TRANSACTION("priv_distributeRawTransaction"),
PRIV_GET_EEA_TRANSACTION_COUNT("priv_getEeaTransactionCount"),
PRIV_GET_CODE("priv_getCode"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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 static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.FIND_PRIVACY_GROUP_ERROR;

import org.hyperledger.besu.enclave.types.PrivacyGroup;
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.AbstractBlockParameterMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
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.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.privacy.PrivacyController;

import java.util.Collections;

import org.apache.logging.log4j.Logger;

public class PrivDebugGetStateRoot extends AbstractBlockParameterMethod {

private static final Logger LOG = getLogger();

private final EnclavePublicKeyProvider enclavePublicKeyProvider;
private final PrivacyController privacyController;

public PrivDebugGetStateRoot(
final BlockchainQueries blockchainQueries,
final EnclavePublicKeyProvider enclavePublicKeyProvider,
final PrivacyController privacyController) {
super(blockchainQueries);
this.enclavePublicKeyProvider = enclavePublicKeyProvider;
this.privacyController = privacyController;
}

@Override
public String getName() {
return RpcMethod.PRIV_DEBUG_GET_STATE_ROOT.getMethodName();
}

@Override
protected BlockParameter blockParameter(final JsonRpcRequestContext request) {
return request.getRequiredParameter(1, BlockParameter.class);
}

@Override
protected Object resultByBlockNumber(
final JsonRpcRequestContext requestContext, final long blockNumber) {
final String privacyGroupId = requestContext.getRequiredParameter(0, String.class);
final String enclavePublicKey =
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser());
LOG.trace("Executing {}", this::getName);

final PrivacyGroup[] privacyGroups =
privacyController.findPrivacyGroup(
Collections.singletonList(privacyGroupId),
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));

if (privacyGroups.length == 0) {
LOG.error("Failed to fetch privacy group");
return new JsonRpcErrorResponse(
requestContext.getRequest().getId(), FIND_PRIVACY_GROUP_ERROR);
}

return privacyController
.getStateRootByBlockNumber(privacyGroupId, enclavePublicKey, blockNumber)
.<JsonRpcResponse>map(
stateRootHash ->
new JsonRpcSuccessResponse(requestContext.getRequest().getId(), stateRootHash))
.orElse(
new JsonRpcErrorResponse(
requestContext.getRequest().getId(), JsonRpcError.INVALID_PARAMS));
}

@Override
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
return (JsonRpcResponse) findResultByParamType(requestContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivUninstallFilter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivCall;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivCreatePrivacyGroup;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivDebugGetStateRoot;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivDeletePrivacyGroup;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivDistributeRawTransaction;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivFindPrivacyGroup;
Expand Down Expand Up @@ -65,57 +66,44 @@ protected RpcApi getApiGroup() {
protected Map<String, JsonRpcMethod> create(
final PrivacyController privacyController,
final EnclavePublicKeyProvider enclavePublicKeyProvider) {
if (getPrivacyParameters().isOnchainPrivacyGroupsEnabled()) {
return mapOf(
new PrivGetTransactionReceipt(
getPrivacyParameters().getPrivateStateStorage(),
privacyController,
enclavePublicKeyProvider),
new PrivGetPrivacyPrecompileAddress(getPrivacyParameters()),
new PrivGetTransactionCount(privacyController, enclavePublicKeyProvider),
new PrivGetPrivateTransaction(privacyController, enclavePublicKeyProvider),
new PrivDistributeRawTransaction(
privacyController,
enclavePublicKeyProvider,
getPrivacyParameters().isOnchainPrivacyGroupsEnabled()),
new PrivCall(getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
new PrivGetCode(getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
new PrivGetLogs(
getBlockchainQueries(),
getPrivacyQueries(),
privacyController,
enclavePublicKeyProvider),
new PrivNewFilter(filterManager, privacyController, enclavePublicKeyProvider),
new PrivUninstallFilter(filterManager, privacyController, enclavePublicKeyProvider),
new PrivGetFilterLogs(filterManager, privacyController, enclavePublicKeyProvider),
new PrivGetFilterChanges(filterManager, privacyController, enclavePublicKeyProvider));
} else {
return mapOf(
new PrivGetTransactionReceipt(
getPrivacyParameters().getPrivateStateStorage(),
privacyController,
enclavePublicKeyProvider),
new PrivCreatePrivacyGroup(privacyController, enclavePublicKeyProvider),
new PrivDeletePrivacyGroup(privacyController, enclavePublicKeyProvider),
new PrivFindPrivacyGroup(privacyController, enclavePublicKeyProvider),
new PrivGetPrivacyPrecompileAddress(getPrivacyParameters()),
new PrivGetTransactionCount(privacyController, enclavePublicKeyProvider),
new PrivGetPrivateTransaction(privacyController, enclavePublicKeyProvider),
new PrivDistributeRawTransaction(
privacyController,
enclavePublicKeyProvider,
getPrivacyParameters().isOnchainPrivacyGroupsEnabled()),
new PrivCall(getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
new PrivGetCode(getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
new PrivGetLogs(
getBlockchainQueries(),
getPrivacyQueries(),
privacyController,
enclavePublicKeyProvider),
new PrivNewFilter(filterManager, privacyController, enclavePublicKeyProvider),
new PrivUninstallFilter(filterManager, privacyController, enclavePublicKeyProvider),
new PrivGetFilterLogs(filterManager, privacyController, enclavePublicKeyProvider),
new PrivGetFilterChanges(filterManager, privacyController, enclavePublicKeyProvider));

final Map<String, JsonRpcMethod> RPC_METHODS =
mapOf(
new PrivCall(getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
new PrivDebugGetStateRoot(
getBlockchainQueries(), enclavePublicKeyProvider, privacyController),
new PrivDistributeRawTransaction(
privacyController,
enclavePublicKeyProvider,
getPrivacyParameters().isOnchainPrivacyGroupsEnabled()),
new PrivGetCode(getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
new PrivGetLogs(
getBlockchainQueries(),
getPrivacyQueries(),
privacyController,
enclavePublicKeyProvider),
new PrivGetPrivateTransaction(privacyController, enclavePublicKeyProvider),
new PrivGetPrivacyPrecompileAddress(getPrivacyParameters()),
new PrivGetTransactionCount(privacyController, enclavePublicKeyProvider),
new PrivGetTransactionReceipt(
getPrivacyParameters().getPrivateStateStorage(),
privacyController,
enclavePublicKeyProvider),
new PrivGetFilterLogs(filterManager, privacyController, enclavePublicKeyProvider),
new PrivGetFilterChanges(filterManager, privacyController, enclavePublicKeyProvider),
new PrivNewFilter(filterManager, privacyController, enclavePublicKeyProvider),
new PrivUninstallFilter(filterManager, privacyController, enclavePublicKeyProvider));

if (!getPrivacyParameters().isOnchainPrivacyGroupsEnabled()) {
final Map<String, JsonRpcMethod> OFFCHAIN_METHODS =
mapOf(
new PrivCreatePrivacyGroup(privacyController, enclavePublicKeyProvider),
new PrivDeletePrivacyGroup(privacyController, enclavePublicKeyProvider),
new PrivFindPrivacyGroup(privacyController, enclavePublicKeyProvider));
OFFCHAIN_METHODS.forEach(
(key, jsonRpcMethod) ->
RPC_METHODS.merge(key, jsonRpcMethod, (oldVal, newVal) -> newVal));
}
return RPC_METHODS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* 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.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.FIND_PRIVACY_GROUP_ERROR;
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.INVALID_PARAMS;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.enclave.types.PrivacyGroup;
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.parameters.BlockParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
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.JsonRpcResponseType;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.privacy.DefaultPrivacyController;
import org.hyperledger.besu.ethereum.privacy.PrivacyController;

import java.util.Collections;
import java.util.Optional;

import org.junit.Before;
import org.junit.Test;

public class PrivDebugGetStateRootTest {

private static final String ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
private static final PrivacyGroup PRIVACY_GROUP =
new PrivacyGroup(
"1",
PrivacyGroup.Type.LEGACY,
"group",
"Test group",
Collections.singletonList(ENCLAVE_PUBLIC_KEY));

private PrivDebugGetStateRoot method;
private final EnclavePublicKeyProvider enclavePublicKeyProvider = (user) -> ENCLAVE_PUBLIC_KEY;

private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
private final PrivacyController privacyController = mock(DefaultPrivacyController.class);

@Before
public void setUp() {
method =
new PrivDebugGetStateRoot(blockchainQueries, enclavePublicKeyProvider, privacyController);
}

@Test
public void shouldReturnCorrectMethodName() {
assertThat(method.getName()).isEqualTo("priv_debugGetStateRoot");
}

@Test
public void shouldThrowInvalidJsonRpcParametersExceptionWhenNoPrivacyGroup() {
final JsonRpcRequestContext request = request(null, "latest");

final Throwable thrown = catchThrowable(() -> method.response(request));

assertThat(thrown)
.isInstanceOf(InvalidJsonRpcParameters.class)
.hasNoCause()
.hasMessage("Missing required json rpc parameter at index 0");
}

@Test
public void shouldReturnErrorIfPrivacyGroupDoesNotExist() {
when(privacyController.findPrivacyGroup(anyList(), anyString()))
.thenReturn(new PrivacyGroup[] {});
final JsonRpcResponse response = method.response(request("invalid_group", "latest"));
assertThat(response.getType()).isEqualByComparingTo(JsonRpcResponseType.ERROR);
assertThat(((JsonRpcErrorResponse) response).getError().getMessage())
.contains(FIND_PRIVACY_GROUP_ERROR.getMessage());
}

@Test
public void shouldReturnErrorIfUnableToFindStateRoot() {
when(privacyController.findPrivacyGroup(anyList(), anyString()))
.thenReturn(new PrivacyGroup[] {PRIVACY_GROUP});
when(privacyController.getStateRootByBlockNumber(anyString(), anyString(), anyLong()))
.thenReturn(Optional.empty());

final JsonRpcErrorResponse response =
(JsonRpcErrorResponse) method.response(request(ENCLAVE_PUBLIC_KEY, "latest"));

assertThat(response.getError().getMessage()).contains(INVALID_PARAMS.getMessage());
}

@Test
public void shouldReturnSuccessWhenValidPrivacyGroupAndStateRoot() {
final Hash hash = Hash.EMPTY_LIST_HASH;

when(privacyController.findPrivacyGroup(anyList(), anyString()))
.thenReturn(new PrivacyGroup[] {PRIVACY_GROUP});
when(privacyController.getStateRootByBlockNumber(anyString(), anyString(), anyLong()))
.thenReturn(Optional.of(hash));

final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) method.response(request(ENCLAVE_PUBLIC_KEY, "latest"));
final Hash result = (Hash) response.getResult();

assertThat(result).isEqualTo(hash);
}

private JsonRpcRequestContext request(final String privacyGroupId, final String params) {
return new JsonRpcRequestContext(
new JsonRpcRequest(
"2.0", method.getName(), new Object[] {privacyGroupId, new BlockParameter(params)}));
}
}
Loading

0 comments on commit 2691deb

Please sign in to comment.