Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracing private transactions feature #6161

Merged
merged 21 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
### Additions and Improvements
- Expose set finalized/safe block in plugin api BlockchainService. These method can be used by plugins to set finalized/safe block for a PoA network (such as QBFT, IBFT and Clique).[#7382](https://github.com/hyperledger/besu/pull/7382)
- In process RPC service [#7395](https://github.com/hyperledger/besu/pull/7395)
- Added support for tracing private transactions using `priv_traceTransaction` API. [#6161](https://github.com/hyperledger/besu/pull/6161)
- Wrap WorldUpdater into EVMWorldupdater [#7434](https://github.com/hyperledger/besu/pull/7434)


### Bug fixes
- Correct entrypoint in Docker evmtool [#7430](https://github.com/hyperledger/besu/pull/7430)
- Fix protocol schedule check for devnets [#7429](https://github.com/hyperledger/besu/pull/7429)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.privacy.PrivacyGroupUtil;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.condition.PrivGetTransactionReceiptTransaction;
Expand All @@ -25,6 +26,7 @@
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetCodeTransaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetLogsTransaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetTransaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivTraceTransaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.filter.PrivGetFilterChangesTransaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.filter.PrivGetFilterLogsTransaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.filter.PrivNewFilterTransaction;
Expand Down Expand Up @@ -115,6 +117,11 @@ public PrivGetLogsTransaction privGetLogs(
return new PrivGetLogsTransaction(privacyGroupId, filterParameter);
}

public PrivTraceTransaction privTraceTransaction(
final String privacyGroupId, final Hash transactionHash) {
return new PrivTraceTransaction(privacyGroupId, transactionHash);
}

public RemoveFromFlexiblePrivacyGroupTransaction removeFromPrivacyGroup(
final String privacyGroupId,
final String remover,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.tests.acceptance.dsl.transaction.privacy;

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;

import java.io.IOException;

public class PrivTraceTransaction implements Transaction<String> {

private final String privacyGroupId;
private final Hash transactionHash;

public PrivTraceTransaction(final String privacyGroupId, final Hash transactionHash) {
this.privacyGroupId = privacyGroupId;
this.transactionHash = transactionHash;
}

@Override
public String execute(final NodeRequests node) {
try {
final PrivacyRequestFactory.PrivTraceTransaction response =
node.privacy().privTraceTransaction(privacyGroupId, transactionHash).send();

assertThat(response).as("check response is not null").isNotNull();
assertThat(response.getResult()).as("check result in response is not null").isNotNull();

return response.getResult();
} catch (final IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public static class GetPrivacyPrecompileAddressResponse extends Response<Address
public static class GetPrivateTransactionResponse
extends Response<PrivateTransactionGroupResponse> {}

public static class PrivTraceTransaction extends Response<String> {}

public static class CreatePrivacyGroupResponse extends Response<String> {}

public static class DeletePrivacyGroupResponse extends Response<String> {}
Expand Down Expand Up @@ -455,6 +457,16 @@ public Request<?, EthLog> privGetLogs(
"priv_getLogs", Arrays.asList(privacyGroupId, filterParameter), web3jService, EthLog.class);
}

public Request<?, PrivTraceTransaction> privTraceTransaction(
final String privacyGroupId, final Hash transactionHash) {

return new Request<>(
"priv_traceTransaction",
Arrays.asList(privacyGroupId, transactionHash),
web3jService,
PrivTraceTransaction.class);
}

public Request<?, EthFilter> privNewFilter(
final String privacyGroupId, final LogFilterJsonParameter filterParameter) {
return new Request<>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* 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.tests.acceptance.privacy;

import static org.assertj.core.api.Assertions.assertThat;
import static org.web3j.utils.Restriction.UNRESTRICTED;

import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.ParameterizedEnclaveTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.account.PrivacyAccountResolver;
import org.hyperledger.besu.tests.web3j.generated.SimpleStorage;
import org.hyperledger.enclave.testutil.EnclaveEncryptorType;
import org.hyperledger.enclave.testutil.EnclaveType;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Optional;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.web3j.utils.Restriction;

public class PrivTraceTransactionAcceptanceTest extends ParameterizedEnclaveTestBase {

private final PrivacyNode node;

private final PrivacyNode wrongNode;

public PrivTraceTransactionAcceptanceTest(
final Restriction restriction,
final EnclaveType enclaveType,
final EnclaveEncryptorType enclaveEncryptorType)
throws IOException {

super(restriction, enclaveType, enclaveEncryptorType);

node =
privacyBesu.createPrivateTransactionEnabledMinerNode(
restriction + "-node",
PrivacyAccountResolver.ALICE.resolve(enclaveEncryptorType),
enclaveType,
Optional.empty(),
false,
false,
restriction == UNRESTRICTED);

wrongNode =
privacyBesu.createPrivateTransactionEnabledMinerNode(
restriction + "-node",
PrivacyAccountResolver.BOB.resolve(enclaveEncryptorType),
enclaveType,
Optional.empty(),
false,
false,
restriction == UNRESTRICTED);

privacyCluster.start(node);
privacyCluster.start(wrongNode);
}

@Test
public void getTransactionTrace() throws JsonProcessingException {
NickSneo marked this conversation as resolved.
Show resolved Hide resolved
final String privacyGroupId = createPrivacyGroup();
final SimpleStorage simpleStorageContract = deploySimpleStorageContract(privacyGroupId);

Hash transactionHash =
Hash.fromHexString(doTransaction(privacyGroupId, simpleStorageContract, 0));

final String result =
node.execute(privacyTransactions.privTraceTransaction(privacyGroupId, transactionHash));

assertThat(result).isNotNull();
ObjectMapper mapper = new ObjectMapper();

JsonNode rootNode = mapper.readTree(result);
JsonNode resultNode = rootNode.get("result");

assertThat(resultNode).isNotNull();
assertThat(resultNode.isArray()).isTrue();
assertThat(resultNode.size()).isGreaterThan(0);

JsonNode trace = resultNode.get(0);
assertThat(trace.get("action").get("callType").asText()).isEqualTo("call");
assertThat(trace.get("action").get("from").asText()).isEqualTo(node.getAddress().toString());
assertThat(trace.get("action").get("input").asText()).startsWith("0x60fe47b1");
assertThat(trace.get("action").get("to").asText())
.isEqualTo(simpleStorageContract.getContractAddress());
assertThat(trace.get("action").get("value").asText()).isEqualTo("0x0");
assertThat(trace.get("blockHash").asText()).isNotEmpty();
assertThat(trace.get("blockNumber").asInt()).isGreaterThan(0);
assertThat(trace.get("transactionHash").asText()).isEqualTo(transactionHash.toString());
assertThat(trace.get("type").asText()).isEqualTo("call");

final String wrongPrivacyGroupId = createWrongPrivacyGroup();

final String resultEmpty =
wrongNode.execute(
privacyTransactions.privTraceTransaction(wrongPrivacyGroupId, transactionHash));

ObjectMapper mapperEmpty = new ObjectMapper();

JsonNode rootNodeEmpty = mapperEmpty.readTree(resultEmpty);
JsonNode resultNodeEmpty = rootNodeEmpty.get("result");

assertThat(resultNodeEmpty).isNotNull();
assertThat(resultNodeEmpty.isArray()).isTrue();
assertThat(resultNodeEmpty.isEmpty()).isTrue();

final String resultWrongHash =
wrongNode.execute(privacyTransactions.privTraceTransaction(privacyGroupId, Hash.EMPTY));

ObjectMapper mapperWrongHash = new ObjectMapper();

JsonNode rootNodeWrongHash = mapperWrongHash.readTree(resultWrongHash);
JsonNode resultNodeWrongHash = rootNodeWrongHash.get("result");

assertThat(resultNodeWrongHash).isNotNull();
assertThat(resultNodeWrongHash.isArray()).isTrue();
assertThat(resultNodeWrongHash.isEmpty()).isTrue();
}

NickSneo marked this conversation as resolved.
Show resolved Hide resolved
private String createPrivacyGroup() {
return node.execute(createPrivacyGroup("myGroupName", "my group description", node));
}

private String createWrongPrivacyGroup() {
return wrongNode.execute(createPrivacyGroup("myGroupName", "my group description", wrongNode));
}

private SimpleStorage deploySimpleStorageContract(final String privacyGroupId) {
final SimpleStorage simpleStorage =
node.execute(
privateContractTransactions.createSmartContractWithPrivacyGroupId(
SimpleStorage.class,
node.getTransactionSigningKey(),
restriction,
node.getEnclaveKey(),
privacyGroupId));

privateContractVerifier
.validPrivateContractDeployed(
simpleStorage.getContractAddress(), node.getAddress().toString())
.verify(simpleStorage);

return simpleStorage;
}

private String doTransaction(
final String privacyGroupId, final SimpleStorage simpleStorageContract, final int value) {
return node.execute(
privateContractTransactions.callSmartContractWithPrivacyGroupId(
simpleStorageContract.getContractAddress(),
simpleStorageContract.set(BigInteger.valueOf(value)).encodeFunctionCall(),
node.getTransactionSigningKey(),
restriction,
node.getEnclaveKey(),
privacyGroupId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public enum RpcMethod {
PRIV_GET_FILTER_LOGS("priv_getFilterLogs"),
PRIV_SUBSCRIBE("priv_subscribe"),
PRIV_UNSUBSCRIBE("priv_unsubscribe"),
PRIV_TRACE_TRANSACTION("priv_traceTransaction"),
PRIVX_FIND_PRIVACY_GROUP_OLD("privx_findOnchainPrivacyGroup"),
PRIVX_FIND_PRIVACY_GROUP("privx_findFlexiblePrivacyGroup"),
EEA_SEND_RAW_TRANSACTION("eea_sendRawTransaction"),
Expand Down
Loading
Loading