mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 21:38:15 -05:00
on-chain-privacy-groups (#423)
Introduce on-chain privacy groups with add and remove functionality. Signed-off-by: Ivaylo Kirilov <iikirilov@gmail.com>
This commit is contained in:
@@ -18,6 +18,9 @@ import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.miner.MiningStatusCondition;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthTransactions;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
public class EthConditions {
|
||||
|
||||
private final EthTransactions transactions;
|
||||
@@ -68,4 +71,10 @@ public class EthConditions {
|
||||
public Condition miningStatus(final boolean isMining) {
|
||||
return new MiningStatusCondition(transactions.mining(), isMining);
|
||||
}
|
||||
|
||||
public Condition expectNewPendingTransactions(
|
||||
final BigInteger filterId, final List<String> transactionHashes) {
|
||||
return new NewPendingTransactionFilterChangesCondition(
|
||||
transactions.filterChanges(filterId), transactionHashes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.condition.eth;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.WaitUtils;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.Node;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.eth.EthFilterChangesTransaction;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.web3j.protocol.core.methods.response.EthLog;
|
||||
|
||||
public class NewPendingTransactionFilterChangesCondition implements Condition {
|
||||
|
||||
private final EthFilterChangesTransaction filterChanges;
|
||||
private final List<String> transactionHashes;
|
||||
|
||||
public NewPendingTransactionFilterChangesCondition(
|
||||
final EthFilterChangesTransaction filterChanges, final List<String> transactionHashes) {
|
||||
|
||||
this.filterChanges = filterChanges;
|
||||
this.transactionHashes = transactionHashes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(final Node node) {
|
||||
WaitUtils.waitFor(
|
||||
() -> {
|
||||
final EthLog response = node.execute(filterChanges);
|
||||
assertThat(response).isNotNull();
|
||||
assertThat(response.getResult().size()).isEqualTo(transactionHashes.size());
|
||||
for (int i = 0; i < transactionHashes.size(); ++i) {
|
||||
assertThat(response.getLogs().get(0).get()).isEqualTo(transactionHashes.get(0));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,23 @@ import org.hyperledger.orion.testutil.OrionKeyConfiguration;
|
||||
|
||||
public class PrivacyNodeConfiguration {
|
||||
|
||||
private final int privacyAddress;
|
||||
private final BesuNodeConfiguration besuConfig;
|
||||
private final OrionKeyConfiguration orionConfig;
|
||||
|
||||
PrivacyNodeConfiguration(
|
||||
final BesuNodeConfiguration besuConfig, final OrionKeyConfiguration orionConfig) {
|
||||
final int privacyAddress,
|
||||
final BesuNodeConfiguration besuConfig,
|
||||
final OrionKeyConfiguration orionConfig) {
|
||||
this.privacyAddress = privacyAddress;
|
||||
this.besuConfig = besuConfig;
|
||||
this.orionConfig = orionConfig;
|
||||
}
|
||||
|
||||
public int getPrivacyAddress() {
|
||||
return privacyAddress;
|
||||
}
|
||||
|
||||
public BesuNodeConfiguration getBesuConfig() {
|
||||
return besuConfig;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.tests.acceptance.dsl.node.configuration.privacy;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConfigurationBuilder;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.NodeConfigurationFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory;
|
||||
@@ -41,8 +42,15 @@ public class PrivacyNodeFactory {
|
||||
|
||||
public PrivacyNode createPrivateTransactionEnabledMinerNode(
|
||||
final String name, final PrivacyAccount privacyAccount) throws IOException {
|
||||
return createPrivateTransactionEnabledMinerNode(name, privacyAccount, Address.PRIVACY);
|
||||
}
|
||||
|
||||
public PrivacyNode createPrivateTransactionEnabledMinerNode(
|
||||
final String name, final PrivacyAccount privacyAccount, final int privacyAddress)
|
||||
throws IOException {
|
||||
return create(
|
||||
new PrivacyNodeConfiguration(
|
||||
privacyAddress,
|
||||
new BesuNodeConfigurationBuilder()
|
||||
.name(name)
|
||||
.miningEnabled()
|
||||
@@ -57,8 +65,15 @@ public class PrivacyNodeFactory {
|
||||
|
||||
public PrivacyNode createPrivateTransactionEnabledNode(
|
||||
final String name, final PrivacyAccount privacyAccount) throws IOException {
|
||||
return createPrivateTransactionEnabledNode(name, privacyAccount, Address.PRIVACY);
|
||||
}
|
||||
|
||||
public PrivacyNode createPrivateTransactionEnabledNode(
|
||||
final String name, final PrivacyAccount privacyAccount, final int privacyAddress)
|
||||
throws IOException {
|
||||
return create(
|
||||
new PrivacyNodeConfiguration(
|
||||
privacyAddress,
|
||||
new BesuNodeConfigurationBuilder()
|
||||
.name(name)
|
||||
.jsonRpcEnabled()
|
||||
@@ -72,8 +87,15 @@ public class PrivacyNodeFactory {
|
||||
|
||||
public PrivacyNode createIbft2NodePrivacyEnabled(
|
||||
final String name, final PrivacyAccount privacyAccount) throws IOException {
|
||||
return createIbft2NodePrivacyEnabled(name, privacyAccount, Address.PRIVACY);
|
||||
}
|
||||
|
||||
public PrivacyNode createIbft2NodePrivacyEnabled(
|
||||
final String name, final PrivacyAccount privacyAccount, final int privacyAddress)
|
||||
throws IOException {
|
||||
return create(
|
||||
new PrivacyNodeConfiguration(
|
||||
privacyAddress,
|
||||
new BesuNodeConfigurationBuilder()
|
||||
.name(name)
|
||||
.miningEnabled()
|
||||
|
||||
@@ -69,6 +69,7 @@ public class PrivacyNode implements AutoCloseable {
|
||||
private final OrionTestHarness orion;
|
||||
private final BesuNode besu;
|
||||
private final Vertx vertx;
|
||||
private final Integer privacyAddress;
|
||||
|
||||
public PrivacyNode(final PrivacyNodeConfiguration privacyConfiguration, final Vertx vertx)
|
||||
throws IOException {
|
||||
@@ -78,6 +79,8 @@ public class PrivacyNode implements AutoCloseable {
|
||||
|
||||
final BesuNodeConfiguration besuConfig = privacyConfiguration.getBesuConfig();
|
||||
|
||||
privacyAddress = privacyConfiguration.getPrivacyAddress();
|
||||
|
||||
this.besu =
|
||||
new BesuNode(
|
||||
besuConfig.getName(),
|
||||
@@ -167,6 +170,7 @@ public class PrivacyNode implements AutoCloseable {
|
||||
.setStorageProvider(createKeyValueStorageProvider(dataDir, dbDir))
|
||||
.setPrivateKeyPath(KeyPairUtil.getDefaultKeyFile(besu.homeDirectory()).toPath())
|
||||
.setEnclaveFactory(new EnclaveFactory(vertx))
|
||||
.setPrivacyAddress(privacyAddress)
|
||||
.build();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException();
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.privacy.condition;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.PrivacyTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
public class ExpectValidOnChainPrivacyGroupCreated implements PrivateCondition {
|
||||
|
||||
private final PrivacyTransactions transactions;
|
||||
private final PrivacyRequestFactory.OnChainPrivacyGroup expected;
|
||||
|
||||
public ExpectValidOnChainPrivacyGroupCreated(
|
||||
final PrivacyTransactions transactions,
|
||||
final PrivacyRequestFactory.OnChainPrivacyGroup expected) {
|
||||
this.transactions = transactions;
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(final PrivacyNode node) {
|
||||
Awaitility.await()
|
||||
.untilAsserted(
|
||||
() -> {
|
||||
final List<PrivacyRequestFactory.OnChainPrivacyGroup> groups =
|
||||
node.execute(
|
||||
transactions.findOnChainPrivacyGroup(
|
||||
Base64String.unwrapList(expected.getMembers())));
|
||||
assertThat(groups).contains(expected);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,8 @@ public class ExpectValidPrivateContractDeployedReceipt implements PrivateContrac
|
||||
assertThat(receipt.isPresent()).isTrue();
|
||||
final TransactionReceipt transactionReceipt = receipt.get();
|
||||
|
||||
assertThat(transactionReceipt.isStatusOK()).isTrue();
|
||||
|
||||
// Contract transaction has no 'to' address or contract address
|
||||
assertThat(transactionReceipt.getTo()).isNull();
|
||||
|
||||
|
||||
@@ -24,21 +24,34 @@ import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
public class ExpectValidPrivateTransactionReceipt implements PrivateCondition {
|
||||
private final PrivacyTransactions transactions;
|
||||
private final String transactionHash;
|
||||
private final PrivateTransactionReceipt receipt;
|
||||
private final PrivateTransactionReceipt expectedReceipt;
|
||||
|
||||
public ExpectValidPrivateTransactionReceipt(
|
||||
final PrivacyTransactions transactions,
|
||||
final String transactionHash,
|
||||
final PrivateTransactionReceipt receipt) {
|
||||
final PrivateTransactionReceipt expectedReceipt) {
|
||||
|
||||
this.transactions = transactions;
|
||||
this.transactionHash = transactionHash;
|
||||
this.receipt = receipt;
|
||||
this.expectedReceipt = expectedReceipt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(final PrivacyNode node) {
|
||||
assertThat(node.execute(transactions.getPrivateTransactionReceipt(transactionHash)))
|
||||
.isEqualToIgnoringGivenFields(receipt, "commitmentHash");
|
||||
final PrivateTransactionReceipt actualReceipt =
|
||||
node.execute(transactions.getPrivateTransactionReceipt(transactionHash));
|
||||
assertThat(actualReceipt)
|
||||
.usingRecursiveComparison()
|
||||
.ignoringFields("commitmentHash", "logs")
|
||||
.isEqualTo(expectedReceipt);
|
||||
|
||||
assertThat(actualReceipt.getLogs().size()).isEqualTo(expectedReceipt.getLogs().size());
|
||||
|
||||
for (int i = 0; i < expectedReceipt.getLogs().size(); i++) {
|
||||
assertThat(actualReceipt.getLogs().get(i))
|
||||
.usingRecursiveComparison()
|
||||
.ignoringFields("blockHash", "blockNumber")
|
||||
.isEqualTo(expectedReceipt.getLogs().get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class PrivGetTransactionReceiptTransaction
|
||||
public PrivateTransactionReceipt execute(final NodeRequests node) {
|
||||
final Besu besu = node.privacy().getBesuClient();
|
||||
final PollingPrivateTransactionReceiptProcessor receiptProcessor =
|
||||
new PollingPrivateTransactionReceiptProcessor(besu, 15000, 3);
|
||||
new PollingPrivateTransactionReceiptProcessor(besu, 1000, 15);
|
||||
try {
|
||||
final PrivateTransactionReceipt result =
|
||||
receiptProcessor.waitForTransactionReceipt(transactionHash);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.tests.acceptance.dsl.privacy.condition;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.PrivacyTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
|
||||
import org.web3j.protocol.besu.response.privacy.PrivacyGroup;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
@@ -41,6 +42,11 @@ public class PrivateTransactionVerifier {
|
||||
return new ExpectValidPrivacyGroupCreated(transactions, expected);
|
||||
}
|
||||
|
||||
public ExpectValidOnChainPrivacyGroupCreated validOnChainPrivacyGroupExists(
|
||||
final PrivacyRequestFactory.OnChainPrivacyGroup expected) {
|
||||
return new ExpectValidOnChainPrivacyGroupCreated(transactions, expected);
|
||||
}
|
||||
|
||||
public ExpectInternalErrorPrivateTransactionReceipt internalErrorPrivateTransactionReceipt(
|
||||
final String transactionHash) {
|
||||
return new ExpectInternalErrorPrivateTransactionReceipt(transactions, transactionHash);
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.privacy.contract;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.besu.Besu;
|
||||
import org.web3j.tx.BesuPrivateTransactionManager;
|
||||
import org.web3j.tx.PrivateTransactionManager;
|
||||
import org.web3j.tx.gas.BesuPrivacyGasProvider;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
public class CallOnChianPermissioningPrivateSmartContractFunction implements Transaction<String> {
|
||||
|
||||
private static final BesuPrivacyGasProvider GAS_PROVIDER =
|
||||
new BesuPrivacyGasProvider(BigInteger.valueOf(1000));
|
||||
private final String contractAddress;
|
||||
private final String encodedFunction;
|
||||
private final Credentials senderCredentials;
|
||||
private final long chainId;
|
||||
private final Base64String privateFrom;
|
||||
private final Base64String privacyGroupId;
|
||||
|
||||
public CallOnChianPermissioningPrivateSmartContractFunction(
|
||||
final String contractAddress,
|
||||
final String encodedFunction,
|
||||
final String transactionSigningKey,
|
||||
final long chainId,
|
||||
final String privateFrom,
|
||||
final String privacyGroupId) {
|
||||
|
||||
this.contractAddress = contractAddress;
|
||||
this.encodedFunction = encodedFunction;
|
||||
this.senderCredentials = Credentials.create(transactionSigningKey);
|
||||
this.chainId = chainId;
|
||||
this.privateFrom = Base64String.wrap(privateFrom);
|
||||
this.privacyGroupId = Base64String.wrap(privacyGroupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(final NodeRequests node) {
|
||||
final Besu besu = node.privacy().getBesuClient();
|
||||
|
||||
final PrivateTransactionManager privateTransactionManager =
|
||||
new BesuPrivateTransactionManager(
|
||||
besu, GAS_PROVIDER, senderCredentials, chainId, privateFrom, privacyGroupId);
|
||||
|
||||
try {
|
||||
return privateTransactionManager
|
||||
.sendTransaction(
|
||||
GAS_PROVIDER.getGasPrice(),
|
||||
GAS_PROVIDER.getGasLimit(),
|
||||
contractAddress,
|
||||
encodedFunction,
|
||||
null)
|
||||
.getTransactionHash();
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,4 +106,20 @@ public class PrivateContractTransactions {
|
||||
return new LoadPrivateSmartContractTransaction<>(
|
||||
contractAddress, clazz, transactionSigningKey, chainId, privateFrom, privateFor);
|
||||
}
|
||||
|
||||
public CallOnChianPermissioningPrivateSmartContractFunction callOnChainPermissioningSmartContract(
|
||||
final String contractAddress,
|
||||
final String encodedFunction,
|
||||
final String transactionSigningKey,
|
||||
final long chainId,
|
||||
final String privateFrom,
|
||||
final String privacyGroupId) {
|
||||
return new CallOnChianPermissioningPrivateSmartContractFunction(
|
||||
contractAddress,
|
||||
encodedFunction,
|
||||
transactionSigningKey,
|
||||
chainId,
|
||||
privateFrom,
|
||||
privacyGroupId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.privacy.transaction;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.web3j.protocol.exceptions.TransactionException;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
public class AddToOnChainPrivacyGroupTransaction implements Transaction<String> {
|
||||
private final Base64String privacyGroupId;
|
||||
private final PrivacyNode adder;
|
||||
private final List<String> addresses;
|
||||
|
||||
public AddToOnChainPrivacyGroupTransaction(
|
||||
final String privacyGroupId, final PrivacyNode adder, final PrivacyNode... nodes) {
|
||||
this.privacyGroupId = Base64String.wrap(privacyGroupId);
|
||||
this.adder = adder;
|
||||
this.addresses =
|
||||
Arrays.stream(nodes)
|
||||
.map(n -> n.getOrion().getDefaultPublicKey())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(final NodeRequests node) {
|
||||
try {
|
||||
return node.privacy().privxAddToPrivacyGroup(privacyGroupId, adder, addresses);
|
||||
} catch (IOException | TransactionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.privacy.transaction;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory.PrivxCreatePrivacyGroupResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CreateOnChainPrivacyGroupTransaction
|
||||
implements Transaction<PrivxCreatePrivacyGroupResponse> {
|
||||
private final PrivacyNode creator;
|
||||
private final List<String> addresses;
|
||||
|
||||
public CreateOnChainPrivacyGroupTransaction(
|
||||
final PrivacyNode creator, final PrivacyNode... nodes) {
|
||||
this.creator = creator;
|
||||
this.addresses =
|
||||
Arrays.stream(nodes)
|
||||
.map(n -> n.getOrion().getDefaultPublicKey())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivxCreatePrivacyGroupResponse execute(final NodeRequests node) {
|
||||
try {
|
||||
return node.privacy().privxCreatePrivacyGroup(creator, addresses);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.privacy.transaction;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
public class FindOnChainPrivacyGroupTransaction
|
||||
implements Transaction<List<PrivacyRequestFactory.OnChainPrivacyGroup>> {
|
||||
private final List<Base64String> nodes;
|
||||
|
||||
public FindOnChainPrivacyGroupTransaction(final List<String> nodeEnclaveKeys) {
|
||||
this.nodes = nodeEnclaveKeys.stream().map(Base64String::wrap).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrivacyRequestFactory.OnChainPrivacyGroup> execute(final NodeRequests node) {
|
||||
try {
|
||||
return node.privacy().privxFindOnChainPrivacyGroup(nodes).send().getGroups();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.privacy.transaction;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.web3j.protocol.exceptions.TransactionException;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
public class LockOnChainPrivacyGroupTransaction implements Transaction<String> {
|
||||
private final Base64String privacyGroupId;
|
||||
private final PrivacyNode locker;
|
||||
|
||||
public LockOnChainPrivacyGroupTransaction(final String privacyGroupId, final PrivacyNode locker) {
|
||||
this.privacyGroupId = Base64String.wrap(privacyGroupId);
|
||||
this.locker = locker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(final NodeRequests node) {
|
||||
try {
|
||||
return node.privacy().privxLockPrivacyGroup(locker, privacyGroupId);
|
||||
} catch (IOException | TransactionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,29 @@ public class PrivacyTransactions {
|
||||
return new CreatePrivacyGroupTransaction(name, description, nodes);
|
||||
}
|
||||
|
||||
public CreateOnChainPrivacyGroupTransaction createOnChainPrivacyGroup(
|
||||
final PrivacyNode creator, final PrivacyNode... nodes) {
|
||||
return new CreateOnChainPrivacyGroupTransaction(creator, nodes);
|
||||
}
|
||||
|
||||
public AddToOnChainPrivacyGroupTransaction addToPrivacyGroup(
|
||||
final String privacyGroupId, final PrivacyNode adder, final PrivacyNode... nodes) {
|
||||
return new AddToOnChainPrivacyGroupTransaction(privacyGroupId, adder, nodes);
|
||||
}
|
||||
|
||||
public LockOnChainPrivacyGroupTransaction privxLockPrivacyGroup(
|
||||
final String privacyGroupId, final PrivacyNode locker) {
|
||||
return new LockOnChainPrivacyGroupTransaction(privacyGroupId, locker);
|
||||
}
|
||||
|
||||
public FindPrivacyGroupTransaction findPrivacyGroup(final List<String> nodes) {
|
||||
return new FindPrivacyGroupTransaction(nodes);
|
||||
}
|
||||
|
||||
public FindOnChainPrivacyGroupTransaction findOnChainPrivacyGroup(final List<String> nodes) {
|
||||
return new FindOnChainPrivacyGroupTransaction(nodes);
|
||||
}
|
||||
|
||||
public PrivDistributeTransactionTransaction privDistributeTransaction(
|
||||
final String signedPrivateTransaction) {
|
||||
return new PrivDistributeTransactionTransaction(signedPrivateTransaction);
|
||||
|
||||
@@ -18,6 +18,9 @@ import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
@@ -33,28 +36,70 @@ public class DeploySmartContractTransaction<T extends Contract> implements Trans
|
||||
Credentials.create(Accounts.GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
|
||||
|
||||
private final Class<T> clazz;
|
||||
private final Object[] args;
|
||||
|
||||
public DeploySmartContractTransaction(final Class<T> clazz) {
|
||||
public DeploySmartContractTransaction(final Class<T> clazz, final Object... args) {
|
||||
this.clazz = clazz;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T execute(final NodeRequests node) {
|
||||
try {
|
||||
final Method method =
|
||||
clazz.getMethod(
|
||||
"deploy", Web3j.class, Credentials.class, BigInteger.class, BigInteger.class);
|
||||
if (args != null && args.length != 0) {
|
||||
final ArrayList<Object> parameterObjects = new ArrayList<>();
|
||||
parameterObjects.addAll(
|
||||
Arrays.asList(node.eth(), BENEFACTOR_ONE, DEFAULT_GAS_PRICE, DEFAULT_GAS_LIMIT));
|
||||
parameterObjects.addAll(Arrays.asList(args));
|
||||
|
||||
final Object invoked =
|
||||
method.invoke(
|
||||
METHOD_IS_STATIC, node.eth(), BENEFACTOR_ONE, DEFAULT_GAS_PRICE, DEFAULT_GAS_LIMIT);
|
||||
final Method method =
|
||||
Arrays.stream(clazz.getMethods())
|
||||
.filter(
|
||||
i ->
|
||||
i.getName().equals("deploy")
|
||||
&& parameterTypesAreEqual(i.getParameterTypes(), parameterObjects))
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
|
||||
final Object invoked = method.invoke(METHOD_IS_STATIC, parameterObjects.toArray());
|
||||
|
||||
return cast(invoked).send();
|
||||
} else {
|
||||
final Method method =
|
||||
clazz.getMethod(
|
||||
"deploy", Web3j.class, Credentials.class, BigInteger.class, BigInteger.class);
|
||||
|
||||
final Object invoked =
|
||||
method.invoke(
|
||||
METHOD_IS_STATIC, node.eth(), BENEFACTOR_ONE, DEFAULT_GAS_PRICE, DEFAULT_GAS_LIMIT);
|
||||
|
||||
return cast(invoked).send();
|
||||
}
|
||||
|
||||
return cast(invoked).send();
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private boolean parameterTypesAreEqual(
|
||||
final Class<?>[] expectedTypes, final ArrayList<Object> actualObjects) {
|
||||
if (expectedTypes.length != actualObjects.size()) {
|
||||
return false;
|
||||
}
|
||||
final ArrayList<Class> actualTypes =
|
||||
actualObjects.stream()
|
||||
.map(Object::getClass)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
|
||||
for (int i = 0; i < expectedTypes.length; i++) {
|
||||
if (!expectedTypes[i].isAssignableFrom(actualTypes.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private RemoteCall<T> cast(final Object invokedMethod) {
|
||||
return (RemoteCall<T>) invokedMethod;
|
||||
|
||||
@@ -26,6 +26,11 @@ public class ContractTransactions {
|
||||
return new DeploySmartContractTransaction<>(clazz);
|
||||
}
|
||||
|
||||
public <T extends Contract> DeploySmartContractTransaction<T> createSmartContract(
|
||||
final Class<T> clazz, final Object... args) {
|
||||
return new DeploySmartContractTransaction<>(clazz, args);
|
||||
}
|
||||
|
||||
public CallSmartContractFunction callSmartContract(
|
||||
final String functionName, final String contractAddress) {
|
||||
return new CallSmartContractFunction(functionName, contractAddress);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.transaction.eth;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.web3j.protocol.core.methods.response.EthLog;
|
||||
|
||||
public class EthFilterChangesTransaction implements Transaction<EthLog> {
|
||||
private final BigInteger filterId;
|
||||
|
||||
public EthFilterChangesTransaction(final BigInteger filterId) {
|
||||
this.filterId = filterId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EthLog execute(final NodeRequests node) {
|
||||
try {
|
||||
final EthLog response = node.eth().ethGetFilterChanges(filterId).send();
|
||||
assertThat(response.getLogs()).isNotNull();
|
||||
return response;
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.transaction.eth;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.web3j.protocol.core.methods.response.EthFilter;
|
||||
|
||||
public class EthNewPendingTransactionFilterTransaction implements Transaction<BigInteger> {
|
||||
@Override
|
||||
public BigInteger execute(final NodeRequests node) {
|
||||
try {
|
||||
final EthFilter response = node.eth().ethNewPendingTransactionFilter().send();
|
||||
assertThat(response.getFilterId()).isNotNull();
|
||||
return response.getFilterId();
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ package org.hyperledger.besu.tests.acceptance.dsl.transaction.eth;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.web3j.protocol.core.DefaultBlockParameter;
|
||||
import org.web3j.protocol.core.DefaultBlockParameterName;
|
||||
|
||||
@@ -65,4 +67,12 @@ public class EthTransactions {
|
||||
public EthMiningTransaction mining() {
|
||||
return new EthMiningTransaction();
|
||||
}
|
||||
|
||||
public EthNewPendingTransactionFilterTransaction newPendingTransactionsFilter() {
|
||||
return new EthNewPendingTransactionFilterTransaction();
|
||||
}
|
||||
|
||||
public EthFilterChangesTransaction filterChanges(final BigInteger filterId) {
|
||||
return new EthFilterChangesTransaction(filterId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,26 +14,44 @@
|
||||
*/
|
||||
package org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import org.hyperledger.besu.crypto.SecureRandomProvider;
|
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.parameters.CreatePrivacyGroupParameter;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivateTransactionGroupResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3jService;
|
||||
import org.web3j.protocol.besu.Besu;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.Request;
|
||||
import org.web3j.protocol.core.Response;
|
||||
import org.web3j.protocol.eea.crypto.PrivateTransactionEncoder;
|
||||
import org.web3j.protocol.eea.crypto.RawPrivateTransaction;
|
||||
import org.web3j.protocol.exceptions.TransactionException;
|
||||
import org.web3j.tx.response.PollingPrivateTransactionReceiptProcessor;
|
||||
import org.web3j.utils.Base64String;
|
||||
import org.web3j.utils.Numeric;
|
||||
|
||||
public class PrivacyRequestFactory {
|
||||
private final SecureRandom secureRandom;
|
||||
|
||||
public static class GetPrivacyPrecompileAddressResponse extends Response<Address> {}
|
||||
|
||||
@@ -81,6 +99,7 @@ public class PrivacyRequestFactory {
|
||||
public PrivacyRequestFactory(final Web3jService web3jService) {
|
||||
this.web3jService = web3jService;
|
||||
this.besuClient = Besu.build(web3jService);
|
||||
this.secureRandom = SecureRandomProvider.createSecureRandom();
|
||||
}
|
||||
|
||||
public Besu getBesuClient() {
|
||||
@@ -96,6 +115,126 @@ public class PrivacyRequestFactory {
|
||||
}
|
||||
}
|
||||
|
||||
public String privxAddToPrivacyGroup(
|
||||
final Base64String privacyGroupId, final PrivacyNode adder, final List<String> addresses)
|
||||
throws IOException, TransactionException {
|
||||
|
||||
final BigInteger nonce =
|
||||
besuClient
|
||||
.privGetTransactionCount(adder.getAddress().toHexString(), privacyGroupId)
|
||||
.send()
|
||||
.getTransactionCount();
|
||||
|
||||
final Bytes payload =
|
||||
encodeAddToGroupFunctionCall(
|
||||
Bytes.fromBase64String(adder.getEnclaveKey()),
|
||||
addresses.stream().map(Bytes::fromBase64String).collect(Collectors.toList()));
|
||||
|
||||
final RawPrivateTransaction privateTransaction =
|
||||
RawPrivateTransaction.createTransaction(
|
||||
nonce,
|
||||
BigInteger.valueOf(1000),
|
||||
BigInteger.valueOf(3000000),
|
||||
Address.PRIVACY_PROXY.toHexString(),
|
||||
payload.toHexString(),
|
||||
Base64String.wrap(adder.getEnclaveKey()),
|
||||
privacyGroupId,
|
||||
org.web3j.utils.Restriction.RESTRICTED);
|
||||
|
||||
return besuClient
|
||||
.eeaSendRawTransaction(
|
||||
Numeric.toHexString(
|
||||
PrivateTransactionEncoder.signMessage(
|
||||
privateTransaction, Credentials.create(adder.getTransactionSigningKey()))))
|
||||
.send()
|
||||
.getTransactionHash();
|
||||
}
|
||||
|
||||
public String privxLockPrivacyGroup(final PrivacyNode locker, final Base64String privacyGroupId)
|
||||
throws IOException, TransactionException {
|
||||
final BigInteger nonce =
|
||||
besuClient
|
||||
.privGetTransactionCount(locker.getAddress().toHexString(), privacyGroupId)
|
||||
.send()
|
||||
.getTransactionCount();
|
||||
|
||||
final RawPrivateTransaction privateTransaction =
|
||||
RawPrivateTransaction.createTransaction(
|
||||
nonce,
|
||||
BigInteger.valueOf(1000),
|
||||
BigInteger.valueOf(3000000),
|
||||
Address.PRIVACY_PROXY.toHexString(),
|
||||
OnChainGroupManagement.LOCK_GROUP_METHOD_SIGNATURE.toHexString(),
|
||||
Base64String.wrap(locker.getEnclaveKey()),
|
||||
privacyGroupId,
|
||||
org.web3j.utils.Restriction.RESTRICTED);
|
||||
|
||||
final String transactionHash =
|
||||
besuClient
|
||||
.eeaSendRawTransaction(
|
||||
Numeric.toHexString(
|
||||
PrivateTransactionEncoder.signMessage(
|
||||
privateTransaction, Credentials.create(locker.getTransactionSigningKey()))))
|
||||
.send()
|
||||
.getTransactionHash();
|
||||
|
||||
return new PollingPrivateTransactionReceiptProcessor(besuClient, 3000, 10)
|
||||
.waitForTransactionReceipt(transactionHash)
|
||||
.getcommitmentHash();
|
||||
}
|
||||
|
||||
public PrivxCreatePrivacyGroupResponse privxCreatePrivacyGroup(
|
||||
final PrivacyNode creator, final List<String> addresses) throws IOException {
|
||||
|
||||
final byte[] bytes = new byte[32];
|
||||
secureRandom.nextBytes(bytes);
|
||||
final Bytes privacyGroupId = Bytes.wrap(bytes);
|
||||
|
||||
final BigInteger nonce =
|
||||
besuClient
|
||||
.privGetTransactionCount(
|
||||
creator.getAddress().toHexString(),
|
||||
Base64String.wrap(privacyGroupId.toArrayUnsafe()))
|
||||
.send()
|
||||
.getTransactionCount();
|
||||
|
||||
final Bytes payload =
|
||||
encodeAddToGroupFunctionCall(
|
||||
Bytes.fromBase64String(creator.getEnclaveKey()),
|
||||
addresses.stream().map(Bytes::fromBase64String).collect(Collectors.toList()));
|
||||
|
||||
final RawPrivateTransaction privateTransaction =
|
||||
RawPrivateTransaction.createTransaction(
|
||||
nonce,
|
||||
BigInteger.valueOf(1000),
|
||||
BigInteger.valueOf(3000000),
|
||||
Address.PRIVACY_PROXY.toHexString(),
|
||||
payload.toHexString(),
|
||||
Base64String.wrap(creator.getEnclaveKey()),
|
||||
Base64String.wrap(privacyGroupId.toArrayUnsafe()),
|
||||
org.web3j.utils.Restriction.RESTRICTED);
|
||||
|
||||
final String transactionHash =
|
||||
besuClient
|
||||
.eeaSendRawTransaction(
|
||||
Numeric.toHexString(
|
||||
PrivateTransactionEncoder.signMessage(
|
||||
privateTransaction,
|
||||
Credentials.create(creator.getTransactionSigningKey()))))
|
||||
.send()
|
||||
.getTransactionHash();
|
||||
return new PrivxCreatePrivacyGroupResponse(privacyGroupId.toBase64String(), transactionHash);
|
||||
}
|
||||
|
||||
public Request<?, PrivxFindPrivacyGroupResponse> privxFindOnChainPrivacyGroup(
|
||||
final List<Base64String> nodes) {
|
||||
return new Request<>(
|
||||
"privx_findOnChainPrivacyGroup",
|
||||
singletonList(nodes),
|
||||
web3jService,
|
||||
PrivxFindPrivacyGroupResponse.class);
|
||||
}
|
||||
|
||||
public Request<?, GetPrivacyPrecompileAddressResponse> privGetPrivacyPrecompileAddress() {
|
||||
return new Request<>(
|
||||
"priv_getPrivacyPrecompileAddress",
|
||||
@@ -179,4 +318,131 @@ public class PrivacyRequestFactory {
|
||||
web3jService,
|
||||
GetCodeResponse.class);
|
||||
}
|
||||
|
||||
public static class PrivxFindPrivacyGroupResponse extends Response<List<OnChainPrivacyGroup>> {
|
||||
public List<OnChainPrivacyGroup> getGroups() {
|
||||
return getResult();
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnChainPrivacyGroup {
|
||||
private final Base64String privacyGroupId;
|
||||
private final List<Base64String> members;
|
||||
private final String name;
|
||||
private final String description;
|
||||
|
||||
public enum Type {
|
||||
ONCHAIN
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public OnChainPrivacyGroup(
|
||||
@JsonProperty(value = "privacyGroupId") final String privacyGroupId,
|
||||
@JsonProperty(value = "type") final Type type,
|
||||
@JsonProperty(value = "name") final String name,
|
||||
@JsonProperty(value = "description") final String description,
|
||||
@JsonProperty(value = "members") final List<Base64String> members) {
|
||||
this(privacyGroupId, members);
|
||||
}
|
||||
|
||||
public OnChainPrivacyGroup(final String privacyGroupId, final List<Base64String> members) {
|
||||
this.privacyGroupId = Base64String.wrap(privacyGroupId);
|
||||
this.name = "";
|
||||
this.description = "";
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
public Base64String getPrivacyGroupId() {
|
||||
return privacyGroupId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return Type.ONCHAIN;
|
||||
}
|
||||
|
||||
public List<Base64String> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final OnChainPrivacyGroup that = (OnChainPrivacyGroup) o;
|
||||
return getPrivacyGroupId().equals(that.getPrivacyGroupId())
|
||||
&& getName().equals(that.getName())
|
||||
&& getDescription().equals(that.getDescription())
|
||||
&& getType() == that.getType()
|
||||
&& getMembers().equals(that.getMembers());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
getPrivacyGroupId(), getName(), getDescription(), getType(), getMembers());
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrivxCreatePrivacyGroupResponse {
|
||||
final String privacyGroupId;
|
||||
final String transactionHash;
|
||||
|
||||
@JsonCreator
|
||||
public PrivxCreatePrivacyGroupResponse(
|
||||
@JsonProperty("privacyGroupId") final String privacyGroupId,
|
||||
@JsonProperty("transactionHash") final String transactionHash) {
|
||||
this.privacyGroupId = privacyGroupId;
|
||||
this.transactionHash = transactionHash;
|
||||
}
|
||||
|
||||
public String getPrivacyGroupId() {
|
||||
return privacyGroupId;
|
||||
}
|
||||
|
||||
public String getTransactionHash() {
|
||||
return transactionHash;
|
||||
}
|
||||
}
|
||||
|
||||
private Bytes encodeAddToGroupFunctionCall(
|
||||
final Bytes privateFrom, final List<Bytes> participants) {
|
||||
return Bytes.concatenate(
|
||||
OnChainGroupManagement.ADD_TO_GROUP_METHOD_SIGNATURE,
|
||||
privateFrom,
|
||||
encodeList(participants));
|
||||
}
|
||||
|
||||
private Bytes encodeList(final List<Bytes> participants) {
|
||||
final Bytes dynamicParameterOffset = encodeLong(64);
|
||||
final Bytes length = encodeLong(participants.size());
|
||||
return Bytes.concatenate(
|
||||
dynamicParameterOffset,
|
||||
length,
|
||||
Bytes.fromHexString(
|
||||
participants.stream()
|
||||
.map(Bytes::toUnprefixedHexString)
|
||||
.collect(Collectors.joining(""))));
|
||||
}
|
||||
|
||||
// long to uint256, 8 bytes big endian, so left padded by 24 bytes
|
||||
private static Bytes encodeLong(final long l) {
|
||||
checkArgument(l >= 0, "Unsigned value must be positive");
|
||||
final byte[] longBytes = new byte[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
longBytes[i] = (byte) ((l >> ((7 - i) * 8)) & 0xFF);
|
||||
}
|
||||
return Bytes.concatenate(Bytes.wrap(new byte[24]), Bytes.wrap(longBytes));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ dependencies {
|
||||
testImplementation project(':ethereum:api')
|
||||
testImplementation project(':ethereum:core')
|
||||
testImplementation project(':ethereum:rlp')
|
||||
testImplementation project(':privacy-contracts')
|
||||
testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts')
|
||||
testImplementation project(':ethereum:permissioning')
|
||||
testImplementation project(':plugin-api')
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
|
||||
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.hyperledger.besu.ethereum.core.Address.DEFAULT_PRIVACY;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
@@ -56,6 +57,11 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
private Cluster multiTenancyCluster;
|
||||
|
||||
private static final SECP256K1.KeyPair TEST_KEY =
|
||||
SECP256K1.KeyPair.create(
|
||||
SECP256K1.PrivateKey.create(
|
||||
new BigInteger(
|
||||
"853d7f0010fd86d0d7811c1f9d968ea89a24484a8127b4a483ddf5d2cfec766d", 16)));
|
||||
private static final String PRIVACY_GROUP_ID = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
|
||||
private static final String ENCLAVE_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
|
||||
private static final String KEY1 = "sgFkVOyFndZe/5SAZJO5UYbrl7pezHetveriBBWWnE8=";
|
||||
@@ -99,9 +105,9 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
final PrivateTransaction validSignedPrivateTransaction =
|
||||
getValidSignedPrivateTransaction(senderAddress);
|
||||
|
||||
receiveEnclaveStub(getRLPOutput(validSignedPrivateTransaction));
|
||||
receiveEnclaveStub(validSignedPrivateTransaction);
|
||||
retrievePrivacyGroupEnclaveStub();
|
||||
sendEnclaveStub("testKey");
|
||||
sendEnclaveStub(KEY1);
|
||||
|
||||
final Hash transactionHash =
|
||||
node.execute(
|
||||
@@ -133,9 +139,9 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
throws JsonProcessingException {
|
||||
final List<PrivacyGroup> groupMembership =
|
||||
List.of(
|
||||
testPrivacyGroup(emptyList(), PrivacyGroup.Type.PANTHEON),
|
||||
testPrivacyGroup(emptyList(), PrivacyGroup.Type.PANTHEON),
|
||||
testPrivacyGroup(emptyList(), PrivacyGroup.Type.PANTHEON));
|
||||
testPrivacyGroup(singletonList(ENCLAVE_KEY), PrivacyGroup.Type.PANTHEON),
|
||||
testPrivacyGroup(singletonList(ENCLAVE_KEY), PrivacyGroup.Type.PANTHEON),
|
||||
testPrivacyGroup(singletonList(ENCLAVE_KEY), PrivacyGroup.Type.PANTHEON));
|
||||
|
||||
findPrivacyGroupEnclaveStub(groupMembership);
|
||||
|
||||
@@ -149,8 +155,8 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
getValidSignedPrivateTransaction(senderAddress);
|
||||
|
||||
retrievePrivacyGroupEnclaveStub();
|
||||
sendEnclaveStub("testKey");
|
||||
receiveEnclaveStub(getRLPOutput(validSignedPrivateTransaction));
|
||||
sendEnclaveStub(KEY1);
|
||||
receiveEnclaveStub(validSignedPrivateTransaction);
|
||||
|
||||
node.verify(
|
||||
priv.eeaSendRawTransaction(
|
||||
@@ -166,8 +172,8 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction);
|
||||
|
||||
retrievePrivacyGroupEnclaveStub();
|
||||
sendEnclaveStub("testKey");
|
||||
receiveEnclaveStub(rlpOutput);
|
||||
sendEnclaveStub(KEY1);
|
||||
receiveEnclaveStub(validSignedPrivateTransaction);
|
||||
|
||||
node.verify(priv.getTransactionCount(accountAddress, PRIVACY_GROUP_ID, 0));
|
||||
final Hash transactionReceipt =
|
||||
@@ -180,14 +186,10 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
@Test
|
||||
public void privDistributeRawTransactionSuccessShouldReturnEnclaveKey()
|
||||
throws JsonProcessingException {
|
||||
final String enclaveResponseKey = "TestKey";
|
||||
final String enclaveResponseKeyBase64 =
|
||||
Base64.encode(Bytes.wrap(enclaveResponseKey.getBytes(UTF_8)));
|
||||
final String enclaveResponseKeyBytes =
|
||||
Bytes.wrap(Bytes.fromBase64String(enclaveResponseKeyBase64)).toString();
|
||||
final String enclaveResponseKeyBytes = Bytes.wrap(Bytes.fromBase64String(KEY1)).toString();
|
||||
|
||||
retrievePrivacyGroupEnclaveStub();
|
||||
sendEnclaveStub(enclaveResponseKeyBase64);
|
||||
sendEnclaveStub(KEY1);
|
||||
|
||||
node.verify(
|
||||
priv.distributeRawTransaction(
|
||||
@@ -203,8 +205,8 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction);
|
||||
|
||||
retrievePrivacyGroupEnclaveStub();
|
||||
sendEnclaveStub("testKey");
|
||||
receiveEnclaveStub(rlpOutput);
|
||||
sendEnclaveStub(KEY1);
|
||||
receiveEnclaveStub(validSignedPrivateTransaction);
|
||||
|
||||
final Hash transactionReceipt =
|
||||
node.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
|
||||
@@ -224,8 +226,8 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
List.of(testPrivacyGroup(emptyList(), PrivacyGroup.Type.LEGACY));
|
||||
|
||||
retrievePrivacyGroupEnclaveStub();
|
||||
sendEnclaveStub("testKey");
|
||||
receiveEnclaveStub(rlpOutput);
|
||||
sendEnclaveStub(KEY1);
|
||||
receiveEnclaveStub(validSignedPrivateTransaction);
|
||||
findPrivacyGroupEnclaveStub(groupMembership);
|
||||
|
||||
node.verify(priv.getTransactionCount(accountAddress, PRIVACY_GROUP_ID, 0));
|
||||
@@ -268,8 +270,8 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
stubFor(post("/send").willReturn(ok(sendResponse)));
|
||||
}
|
||||
|
||||
private void receiveEnclaveStub(final BytesValueRLPOutput rlpOutput)
|
||||
throws JsonProcessingException {
|
||||
private void receiveEnclaveStub(final PrivateTransaction privTx) throws JsonProcessingException {
|
||||
final BytesValueRLPOutput rlpOutput = getRLPOutputForReceiveResponse(privTx);
|
||||
final String senderKey = "QTFhVnRNeExDVUhtQlZIWG9aenpCZ1BiVy93ajVheERwVzlYOGw5MVNHbz0=";
|
||||
final String receiveResponse =
|
||||
mapper.writeValueAsString(
|
||||
@@ -278,9 +280,16 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
stubFor(post("/receive").willReturn(ok(receiveResponse)));
|
||||
}
|
||||
|
||||
private BytesValueRLPOutput getRLPOutput(final PrivateTransaction validSignedPrivateTransaction) {
|
||||
private BytesValueRLPOutput getRLPOutputForReceiveResponse(
|
||||
final PrivateTransaction privateTransaction) {
|
||||
final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
|
||||
validSignedPrivateTransaction.writeTo(bvrlpo);
|
||||
privateTransaction.writeTo(bvrlpo);
|
||||
return bvrlpo;
|
||||
}
|
||||
|
||||
private BytesValueRLPOutput getRLPOutput(final PrivateTransaction privateTransaction) {
|
||||
final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
|
||||
privateTransaction.writeTo(bvrlpo);
|
||||
return bvrlpo;
|
||||
}
|
||||
|
||||
@@ -302,10 +311,6 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase {
|
||||
.privateFrom(Bytes.fromBase64String(ENCLAVE_KEY))
|
||||
.restriction(Restriction.RESTRICTED)
|
||||
.privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID))
|
||||
.signAndBuild(
|
||||
SECP256K1.KeyPair.create(
|
||||
SECP256K1.PrivateKey.create(
|
||||
new BigInteger(
|
||||
"853d7f0010fd86d0d7811c1f9d968ea89a24484a8127b4a483ddf5d2cfec766d", 16))));
|
||||
.signAndBuild(TEST_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* 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.tests.web3j.privacy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.eth.EthConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory.OnChainPrivacyGroup;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory.PrivxCreatePrivacyGroupResponse;
|
||||
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
|
||||
import org.web3j.protocol.core.methods.response.Log;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
|
||||
private static final long POW_CHAIN_ID = 2018;
|
||||
|
||||
private PrivacyNode alice;
|
||||
private PrivacyNode bob;
|
||||
private PrivacyNode charlie;
|
||||
|
||||
private final MinerTransactions minerTransactions = new MinerTransactions();
|
||||
private final EthConditions ethConditions = new EthConditions(ethTransactions);
|
||||
|
||||
private static final String EXPECTED_STORE_OUTPUT_DATA =
|
||||
"0x000000000000000000000000f17f52151ebef6c7334fad080c5704d77216b7320000000000000000000000000000000000000000000000000000000000000539";
|
||||
private static final String EXPECTED_STORE_EVENT_TOPIC =
|
||||
"0xc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
alice =
|
||||
privacyBesu.createPrivateTransactionEnabledMinerNode(
|
||||
"node1", privacyAccountResolver.resolve(0), Address.PRIVACY);
|
||||
bob =
|
||||
privacyBesu.createPrivateTransactionEnabledNode(
|
||||
"node2", privacyAccountResolver.resolve(1), Address.PRIVACY);
|
||||
charlie =
|
||||
privacyBesu.createPrivateTransactionEnabledNode(
|
||||
"node3", privacyAccountResolver.resolve(2), Address.PRIVACY);
|
||||
privacyCluster.start(alice, bob, charlie);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeCanCreatePrivacyGroup() {
|
||||
final PrivxCreatePrivacyGroupResponse privxCreatePrivacyGroupResponse =
|
||||
alice.execute(privacyTransactions.createOnChainPrivacyGroup(alice, alice, bob));
|
||||
|
||||
assertThat(privxCreatePrivacyGroupResponse).isNotNull();
|
||||
|
||||
final OnChainPrivacyGroup expectedGroup =
|
||||
new OnChainPrivacyGroup(
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(),
|
||||
Base64String.wrapList(alice.getEnclaveKey(), bob.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroup));
|
||||
|
||||
bob.verify(privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroup));
|
||||
|
||||
final String getParticipantsCallHash =
|
||||
alice.execute(
|
||||
privateContractTransactions.callOnChainPermissioningSmartContract(
|
||||
Address.PRIVACY_PROXY.toHexString(),
|
||||
"0x0b0235be" // get participants method signature
|
||||
+ Bytes.fromBase64String(alice.getEnclaveKey()).toUnprefixedHexString(),
|
||||
alice.getTransactionSigningKey(),
|
||||
POW_CHAIN_ID,
|
||||
alice.getEnclaveKey(),
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId()));
|
||||
|
||||
final PrivateTransactionReceipt expectedReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
null,
|
||||
alice.getAddress().toHexString(),
|
||||
Address.PRIVACY_PROXY.toHexString(),
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000020" // dynamic
|
||||
// array offset
|
||||
+ "0000000000000000000000000000000000000000000000000000000000000002" // length
|
||||
// of array
|
||||
+ Bytes.fromBase64String(alice.getEnclaveKey()).toUnprefixedHexString() // first
|
||||
// element
|
||||
+ Bytes.fromBase64String(bob.getEnclaveKey()).toUnprefixedHexString(), // second
|
||||
// element
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
null,
|
||||
alice.getEnclaveKey(),
|
||||
null,
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(),
|
||||
"0x1",
|
||||
null);
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
getParticipantsCallHash, expectedReceipt));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
getParticipantsCallHash, expectedReceipt));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deployingMustGiveValidReceipt() {
|
||||
final PrivxCreatePrivacyGroupResponse privxCreatePrivacyGroupResponse =
|
||||
alice.execute(privacyTransactions.createOnChainPrivacyGroup(alice));
|
||||
|
||||
final OnChainPrivacyGroup expectedGroup =
|
||||
new OnChainPrivacyGroup(
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(),
|
||||
Base64String.wrapList(alice.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroup));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
POW_CHAIN_ID,
|
||||
alice.getEnclaveKey(),
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canAddParticipantToGroup() {
|
||||
final PrivxCreatePrivacyGroupResponse privxCreatePrivacyGroupResponse =
|
||||
alice.execute(privacyTransactions.createOnChainPrivacyGroup(alice, alice, bob));
|
||||
|
||||
assertThat(privxCreatePrivacyGroupResponse).isNotNull();
|
||||
|
||||
final OnChainPrivacyGroup expectedGroup =
|
||||
new OnChainPrivacyGroup(
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(),
|
||||
Base64String.wrapList(alice.getEnclaveKey(), bob.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroup));
|
||||
|
||||
bob.verify(privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroup));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
POW_CHAIN_ID,
|
||||
alice.getEnclaveKey(),
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId()));
|
||||
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
alice.execute(
|
||||
privacyTransactions.privxLockPrivacyGroup(
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(), alice));
|
||||
|
||||
alice.execute(
|
||||
privacyTransactions.addToPrivacyGroup(
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(), alice, charlie));
|
||||
|
||||
final OnChainPrivacyGroup expectedGroupAfterCharlieIsAdded =
|
||||
new OnChainPrivacyGroup(
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(),
|
||||
Base64String.wrapList(
|
||||
alice.getEnclaveKey(), bob.getEnclaveKey(), charlie.getEnclaveKey()));
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(
|
||||
expectedGroupAfterCharlieIsAdded));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(
|
||||
expectedGroupAfterCharlieIsAdded));
|
||||
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(
|
||||
expectedGroupAfterCharlieIsAdded));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bobCanAddCharlieAfterBeingAddedByAlice() {
|
||||
final PrivxCreatePrivacyGroupResponse privxCreatePrivacyGroupResponse =
|
||||
alice.execute(privacyTransactions.createOnChainPrivacyGroup(alice, alice));
|
||||
|
||||
assertThat(privxCreatePrivacyGroupResponse).isNotNull();
|
||||
|
||||
final String privacyGroupId = privxCreatePrivacyGroupResponse.getPrivacyGroupId();
|
||||
final OnChainPrivacyGroup expectedGroup =
|
||||
new OnChainPrivacyGroup(privacyGroupId, Base64String.wrapList(alice.getEnclaveKey()));
|
||||
|
||||
alice.verify(privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroup));
|
||||
|
||||
final EventEmitter eventEmitter =
|
||||
alice.execute(
|
||||
privateContractTransactions.createSmartContractWithPrivacyGroupId(
|
||||
EventEmitter.class,
|
||||
alice.getTransactionSigningKey(),
|
||||
POW_CHAIN_ID,
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
privateContractVerifier
|
||||
.validPrivateContractDeployed(
|
||||
eventEmitter.getContractAddress(), alice.getAddress().toString())
|
||||
.verify(eventEmitter);
|
||||
|
||||
final String aliceLockHash =
|
||||
alice.execute(privacyTransactions.privxLockPrivacyGroup(privacyGroupId, alice));
|
||||
|
||||
alice.execute(privacyTransactions.addToPrivacyGroup(privacyGroupId, alice, bob));
|
||||
|
||||
final OnChainPrivacyGroup expectedGroupAfterBobIsAdded =
|
||||
new OnChainPrivacyGroup(
|
||||
privacyGroupId, Base64String.wrapList(alice.getEnclaveKey(), bob.getEnclaveKey()));
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroupAfterBobIsAdded));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(expectedGroupAfterBobIsAdded));
|
||||
|
||||
bob.execute(privacyTransactions.privxLockPrivacyGroup(privacyGroupId, bob));
|
||||
|
||||
alice.execute(minerTransactions.minerStop());
|
||||
|
||||
alice.getBesu().verify(ethConditions.miningStatus(false));
|
||||
|
||||
final BigInteger pendingTransactionFilterId =
|
||||
alice.execute(ethTransactions.newPendingTransactionsFilter());
|
||||
|
||||
final String callHash =
|
||||
alice.execute(
|
||||
privateContractTransactions.callOnChainPermissioningSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.value().encodeFunctionCall(),
|
||||
alice.getTransactionSigningKey(),
|
||||
POW_CHAIN_ID,
|
||||
alice.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
final String bobAddHash =
|
||||
bob.execute(privacyTransactions.addToPrivacyGroup(privacyGroupId, bob, charlie));
|
||||
|
||||
alice
|
||||
.getBesu()
|
||||
.verify(
|
||||
ethConditions.expectNewPendingTransactions(
|
||||
pendingTransactionFilterId, Arrays.asList(callHash, bobAddHash)));
|
||||
|
||||
alice.execute(minerTransactions.minerStart());
|
||||
|
||||
alice.getBesu().verify(ethConditions.miningStatus(true));
|
||||
|
||||
final OnChainPrivacyGroup expectedGroupAfterCharlieIsAdded =
|
||||
new OnChainPrivacyGroup(
|
||||
privacyGroupId,
|
||||
Base64String.wrapList(
|
||||
alice.getEnclaveKey(), bob.getEnclaveKey(), charlie.getEnclaveKey()));
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(
|
||||
expectedGroupAfterCharlieIsAdded));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(
|
||||
expectedGroupAfterCharlieIsAdded));
|
||||
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validOnChainPrivacyGroupExists(
|
||||
expectedGroupAfterCharlieIsAdded));
|
||||
|
||||
final Optional<TransactionReceipt> aliceAddReceipt =
|
||||
alice.execute(ethTransactions.getTransactionReceipt(bobAddHash));
|
||||
assertThat(aliceAddReceipt.get().getStatus())
|
||||
.isEqualTo("0x1"); // this means the PMT for the "add" succeeded which is what we expect
|
||||
|
||||
final Optional<TransactionReceipt> alicePublicReceipt =
|
||||
alice.execute(ethTransactions.getTransactionReceipt(callHash));
|
||||
if (alicePublicReceipt.isPresent()) {
|
||||
assertThat(alicePublicReceipt.get().getBlockHash())
|
||||
.isEqualTo(
|
||||
aliceAddReceipt
|
||||
.get()
|
||||
.getBlockHash()); // ensure that "add" and "call" are in the same block
|
||||
assertThat(alicePublicReceipt.get().getStatus())
|
||||
.isEqualTo(
|
||||
"0x1"); // this means the PMT for the "call" succeeded which is what we expect because
|
||||
// it is in the same block as the "add" and there is no way to tell that this
|
||||
// will happen before the block is mined
|
||||
}
|
||||
|
||||
final PrivateTransactionReceipt aliceReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(callHash));
|
||||
assertThat(aliceReceipt.getStatus())
|
||||
.isEqualTo(
|
||||
"0x0"); // this means the "call" failed which is what we expect because the group was
|
||||
// locked!
|
||||
final PrivateTransactionReceipt bobReceipt =
|
||||
alice.execute(privacyTransactions.getPrivateTransactionReceipt(callHash));
|
||||
assertThat(bobReceipt.getStatus())
|
||||
.isEqualTo(
|
||||
"0x0"); // this means the "call" failed which is what we expect because the group was
|
||||
// locked!
|
||||
|
||||
// assert charlie can access private transaction information from before he was added
|
||||
final PrivateTransactionReceipt expectedAliceLockReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
null,
|
||||
alice.getAddress().toHexString(),
|
||||
Address.PRIVACY_PROXY.toHexString(),
|
||||
"0x",
|
||||
Collections.emptyList(),
|
||||
null,
|
||||
null,
|
||||
alice.getEnclaveKey(),
|
||||
null,
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(),
|
||||
"0x1",
|
||||
null);
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceLockHash, expectedAliceLockReceipt));
|
||||
|
||||
final String aliceStoreHash =
|
||||
charlie.execute(
|
||||
privateContractTransactions.callOnChainPermissioningSmartContract(
|
||||
eventEmitter.getContractAddress(),
|
||||
eventEmitter.store(BigInteger.valueOf(1337)).encodeFunctionCall(),
|
||||
charlie.getTransactionSigningKey(),
|
||||
POW_CHAIN_ID,
|
||||
charlie.getEnclaveKey(),
|
||||
privacyGroupId));
|
||||
|
||||
final PrivateTransactionReceipt expectedStoreReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
null,
|
||||
charlie.getAddress().toHexString(),
|
||||
eventEmitter.getContractAddress(),
|
||||
"0x",
|
||||
Collections.singletonList(
|
||||
new Log(
|
||||
false,
|
||||
"0x0",
|
||||
"0x0",
|
||||
aliceStoreHash,
|
||||
null,
|
||||
null,
|
||||
eventEmitter.getContractAddress(),
|
||||
EXPECTED_STORE_OUTPUT_DATA,
|
||||
null,
|
||||
Collections.singletonList(EXPECTED_STORE_EVENT_TOPIC))),
|
||||
null,
|
||||
null,
|
||||
charlie.getEnclaveKey(),
|
||||
null,
|
||||
privxCreatePrivacyGroupResponse.getPrivacyGroupId(),
|
||||
"0x1",
|
||||
null);
|
||||
|
||||
alice.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceStoreHash, expectedStoreReceipt));
|
||||
|
||||
bob.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceStoreHash, expectedStoreReceipt));
|
||||
|
||||
charlie.verify(
|
||||
privateTransactionVerifier.validPrivateTransactionReceipt(
|
||||
aliceStoreHash, expectedStoreReceipt));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.tests.web3j.privacy.contracts;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.privacy.contracts.generated.PrivacyGroup;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.web3j.protocol.exceptions.TransactionException;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class PrivacyGroupTest extends AcceptanceTestBase {
|
||||
|
||||
private final Base64String firstParticipant =
|
||||
Base64String.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=");
|
||||
private final Base64String secondParticipant =
|
||||
Base64String.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=");
|
||||
private final Base64String thirdParticipant =
|
||||
Base64String.wrap("Jo2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=");
|
||||
private PrivacyGroup privacyGroup;
|
||||
|
||||
private static final String RAW_FIRST_PARTICIPANT =
|
||||
"0x0b0235be035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486a";
|
||||
private static final String RAW_ADD_PARTICIPANT =
|
||||
"0xf744b089035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000012a8d9b56a0fe9cd94d60be4413bcb721d3a7be27ed8e28b3a6346df874ee141b";
|
||||
private static final String RAW_LOCK = "0xf83d08ba";
|
||||
private static final String RAW_UNLOCK = "0xa69df4b5";
|
||||
private static final String RAW_CAN_EXECUTE = "0x78b90337";
|
||||
private static final String RAW_GET_VERSION = "0x0d8e6e2c";
|
||||
|
||||
private BesuNode minerNode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
minerNode = besu.createMinerNode("node");
|
||||
cluster.start(minerNode);
|
||||
privacyGroup = minerNode.execute(contractTransactions.createSmartContract(PrivacyGroup.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rlp() throws Exception {
|
||||
final String contractAddress = "0x42699a7612a82f1d9c36148af9c77354759b210b";
|
||||
assertThat(privacyGroup.isValid()).isEqualTo(true);
|
||||
contractVerifier.validTransactionReceipt(contractAddress).verify(privacyGroup);
|
||||
// 0x0b0235be
|
||||
assertThat(RAW_FIRST_PARTICIPANT)
|
||||
.isEqualTo(privacyGroup.getParticipants(firstParticipant.raw()).encodeFunctionCall());
|
||||
// 0xf744b089
|
||||
assertThat(RAW_ADD_PARTICIPANT)
|
||||
.isEqualTo(
|
||||
privacyGroup
|
||||
.addParticipants(
|
||||
firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.encodeFunctionCall());
|
||||
assertThat(RAW_LOCK).isEqualTo(privacyGroup.lock().encodeFunctionCall());
|
||||
assertThat(RAW_UNLOCK).isEqualTo(privacyGroup.unlock().encodeFunctionCall());
|
||||
assertThat(RAW_CAN_EXECUTE).isEqualTo(privacyGroup.canExecute().encodeFunctionCall());
|
||||
assertThat(RAW_GET_VERSION).isEqualTo(privacyGroup.getVersion().encodeFunctionCall());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canInitiallyAddParticipants() throws Exception {
|
||||
privacyGroup
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.send();
|
||||
final List<byte[]> participants = privacyGroup.getParticipants(firstParticipant.raw()).send();
|
||||
assertThat(participants.size()).isEqualTo(2);
|
||||
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
|
||||
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
|
||||
}
|
||||
|
||||
@Test(expected = TransactionException.class)
|
||||
public void cannotAddToContractWhenNotLocked() throws Exception {
|
||||
privacyGroup
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw()))
|
||||
.send();
|
||||
|
||||
privacyGroup
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.send();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureContractIsNotLockedAfterDeploy() throws Exception {
|
||||
privacyGroup.unlock().send();
|
||||
assertThat(privacyGroup.canExecute().send()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canAddTwiceToContractWhenCallLock() throws Exception {
|
||||
privacyGroup
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw()))
|
||||
.send();
|
||||
privacyGroup.lock().send();
|
||||
privacyGroup
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.send();
|
||||
final BigInteger privacyGroupVersion = privacyGroup.getVersion().send();
|
||||
assertThat(privacyGroupVersion).isEqualTo(BigInteger.TWO);
|
||||
|
||||
final List<byte[]> participants = privacyGroup.getParticipants(firstParticipant.raw()).send();
|
||||
assertThat(participants.size()).isEqualTo(3);
|
||||
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
|
||||
assertThat(thirdParticipant.raw()).isEqualTo(participants.get(1));
|
||||
assertThat(secondParticipant.raw()).isEqualTo(participants.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void versionStartsAtZero() throws Exception {
|
||||
final BigInteger privacyGroupVersion = privacyGroup.getVersion().send();
|
||||
assertThat(privacyGroupVersion).isEqualTo(BigInteger.ZERO);
|
||||
}
|
||||
|
||||
@Test(expected = TransactionException.class)
|
||||
public void cannotLockTwice() throws Exception {
|
||||
privacyGroup
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw()))
|
||||
.send();
|
||||
privacyGroup.lock().send();
|
||||
privacyGroup.lock().send();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* 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.tests.web3j.privacy.contracts;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.privacy.contracts.generated.PrivacyGroup;
|
||||
import org.hyperledger.besu.privacy.contracts.generated.PrivacyProxy;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.web3j.utils.Base64String;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class PrivacyProxyTest extends AcceptanceTestBase {
|
||||
|
||||
private final Base64String firstParticipant =
|
||||
Base64String.wrap("93Ky7lXwFkMc7+ckoFgUMku5bpr9tz4zhmWmk9RlNng=");
|
||||
private final Base64String secondParticipant =
|
||||
Base64String.wrap("9iaJ6OObl6TUWYjXAOyZsL0VaDPwF+tRFkMwwYSeqqw=");
|
||||
private final Base64String thirdParticipant =
|
||||
Base64String.wrap("Jo2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=");
|
||||
private PrivacyProxy privacyProxy;
|
||||
|
||||
private static final String RAW_FIRST_PARTICIPANT =
|
||||
"0x0b0235bef772b2ee55f016431cefe724a05814324bb96e9afdb73e338665a693d4653678";
|
||||
private static final String RAW_ADD_PARTICIPANT =
|
||||
"0xf744b089f772b2ee55f016431cefe724a05814324bb96e9afdb73e338665a693d465367800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
private BesuNode minerNode;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
minerNode = besu.createMinerNode("node");
|
||||
cluster.start(minerNode);
|
||||
PrivacyGroup privacyGroup =
|
||||
minerNode.execute(contractTransactions.createSmartContract(PrivacyGroup.class));
|
||||
privacyProxy =
|
||||
minerNode.execute(
|
||||
contractTransactions.createSmartContract(
|
||||
PrivacyProxy.class, privacyGroup.getContractAddress()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rlp() throws Exception {
|
||||
assertThat(privacyProxy.isValid()).isEqualTo(true);
|
||||
contractVerifier
|
||||
.validTransactionReceipt(privacyProxy.getContractAddress())
|
||||
.verify(privacyProxy);
|
||||
// 0x0b0235be
|
||||
assertThat(RAW_FIRST_PARTICIPANT)
|
||||
.isEqualTo(privacyProxy.getParticipants(firstParticipant.raw()).encodeFunctionCall());
|
||||
// 0xf744b089
|
||||
assertThat(RAW_ADD_PARTICIPANT)
|
||||
.isEqualTo(
|
||||
privacyProxy
|
||||
.addParticipants(firstParticipant.raw(), Collections.emptyList())
|
||||
.encodeFunctionCall());
|
||||
}
|
||||
|
||||
@Ignore("return 0x which causes web3j to throw exception instead of return empty list")
|
||||
@Test
|
||||
public void deploysWithNoParticipant() throws Exception {
|
||||
final List<byte[]> participants = privacyProxy.getParticipants(firstParticipant.raw()).send();
|
||||
assertThat(participants.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canAddParticipants() throws Exception {
|
||||
privacyProxy
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.send();
|
||||
final List<byte[]> participants = privacyProxy.getParticipants(firstParticipant.raw()).send();
|
||||
assertThat(participants.size()).isEqualTo(2);
|
||||
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
|
||||
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canUpgrade() throws Exception {
|
||||
privacyProxy
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.send();
|
||||
final List<byte[]> participants = privacyProxy.getParticipants(firstParticipant.raw()).send();
|
||||
assertThat(participants.size()).isEqualTo(2);
|
||||
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
|
||||
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
|
||||
|
||||
final PrivacyGroup upgradedContract =
|
||||
minerNode.execute(contractTransactions.createSmartContract(PrivacyGroup.class));
|
||||
|
||||
privacyProxy.upgradeTo(upgradedContract.getContractAddress()).send();
|
||||
privacyProxy
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.send();
|
||||
final List<byte[]> participantsAfterUpgrade =
|
||||
privacyProxy.getParticipants(firstParticipant.raw()).send();
|
||||
assertThat(participantsAfterUpgrade.size()).isEqualTo(2);
|
||||
assertThat(firstParticipant.raw()).isEqualTo(participantsAfterUpgrade.get(0));
|
||||
assertThat(secondParticipant.raw()).isEqualTo(participantsAfterUpgrade.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canAddTwiceToContractWhenCallLock() throws Exception {
|
||||
privacyProxy
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw()))
|
||||
.send();
|
||||
privacyProxy.lock().send();
|
||||
privacyProxy
|
||||
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
|
||||
.send();
|
||||
final List<byte[]> participants = privacyProxy.getParticipants(firstParticipant.raw()).send();
|
||||
assertThat(participants.size()).isEqualTo(3);
|
||||
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
|
||||
assertThat(thirdParticipant.raw()).isEqualTo(participants.get(1));
|
||||
assertThat(secondParticipant.raw()).isEqualTo(participants.get(2));
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
<Logger name="org.hyperledger.besu.SubProcessLog" level="INFO" additivity="false">
|
||||
<AppenderRef ref="SubProcessConsole" />
|
||||
</Logger>
|
||||
<Logger name="net.consensys.orion" level="OFF" additivity="false">
|
||||
</Logger>
|
||||
<Root level="${sys:root.log.level}">
|
||||
<AppenderRef ref="Console" />
|
||||
</Root>
|
||||
|
||||
@@ -219,6 +219,9 @@ public abstract class BesuControllerBuilder<C> {
|
||||
this::createConsensusContext);
|
||||
validateContext(protocolContext);
|
||||
|
||||
protocolSchedule.setPublicWorldStateArchiveForPrivacyBlockProcessor(
|
||||
protocolContext.getWorldStateArchive());
|
||||
|
||||
final MutableBlockchain blockchain = protocolContext.getBlockchain();
|
||||
|
||||
Optional<Pruner> maybePruner = Optional.empty();
|
||||
|
||||
@@ -82,6 +82,7 @@ public class PrivacyGroup implements Serializable {
|
||||
|
||||
public enum Type {
|
||||
LEGACY,
|
||||
ONCHAIN,
|
||||
PANTHEON
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.hyperledger.besu.ethereum.privacy.DefaultPrivacyController;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.Restriction;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.orion.testutil.OrionKeyConfiguration;
|
||||
import org.hyperledger.orion.testutil.OrionTestHarness;
|
||||
@@ -56,7 +57,6 @@ import io.vertx.core.Vertx;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
@@ -74,13 +74,14 @@ public class PrivGetPrivateTransactionIntegrationTest {
|
||||
private final TransactionWithMetadata returnedTransaction = mock(TransactionWithMetadata.class);
|
||||
|
||||
private final Transaction justTransaction = mock(Transaction.class);
|
||||
private final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class);
|
||||
|
||||
private static final Vertx vertx = Vertx.vertx();
|
||||
|
||||
private final EnclavePublicKeyProvider enclavePublicKeyProvider = (user) -> ENCLAVE_PUBLIC_KEY;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpOnce() throws Exception {
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
folder.create();
|
||||
|
||||
testHarness =
|
||||
@@ -92,7 +93,9 @@ public class PrivGetPrivateTransactionIntegrationTest {
|
||||
final EnclaveFactory factory = new EnclaveFactory(vertx);
|
||||
enclave = factory.createVertxEnclave(testHarness.clientUrl());
|
||||
|
||||
privacyController = new DefaultPrivacyController(enclave, null, null, null, null, null);
|
||||
privacyController =
|
||||
new DefaultPrivacyController(
|
||||
null, privateStateStorage, enclave, null, null, null, null, null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
@@ -151,11 +154,13 @@ public class PrivGetPrivateTransactionIntegrationTest {
|
||||
public void returnsStoredPrivateTransaction() {
|
||||
|
||||
final PrivGetPrivateTransaction privGetPrivateTransaction =
|
||||
new PrivGetPrivateTransaction(blockchain, privacyController, enclavePublicKeyProvider);
|
||||
new PrivGetPrivateTransaction(
|
||||
blockchain, privacyController, privateStateStorage, enclavePublicKeyProvider);
|
||||
|
||||
when(blockchain.transactionByHash(any(Hash.class)))
|
||||
.thenReturn(Optional.of(returnedTransaction));
|
||||
when(returnedTransaction.getTransaction()).thenReturn(justTransaction);
|
||||
when(returnedTransaction.getBlockHash()).thenReturn(Optional.of(Hash.ZERO));
|
||||
|
||||
final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput();
|
||||
privateTransaction.writeTo(bvrlp);
|
||||
|
||||
@@ -47,6 +47,7 @@ public enum RpcMethod {
|
||||
PRIV_DISTRIBUTE_RAW_TRANSACTION("priv_distributeRawTransaction"),
|
||||
PRIV_GET_EEA_TRANSACTION_COUNT("priv_getEeaTransactionCount"),
|
||||
PRIV_GET_CODE("priv_getCode"),
|
||||
PRIVX_FIND_PRIVACY_GROUP("privx_findOnChainPrivacyGroup"),
|
||||
EEA_SEND_RAW_TRANSACTION("eea_sendRawTransaction"),
|
||||
ETH_ACCOUNTS("eth_accounts"),
|
||||
ETH_BLOCK_NUMBER("eth_blockNumber"),
|
||||
|
||||
@@ -20,6 +20,8 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter.co
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.DECODE_ERROR;
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.ENCLAVE_ERROR;
|
||||
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException;
|
||||
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.JsonRpcMethod;
|
||||
@@ -27,6 +29,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.Enclav
|
||||
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.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason;
|
||||
@@ -37,6 +40,8 @@ import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -70,6 +75,10 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
|
||||
final PrivateTransaction privateTransaction =
|
||||
PrivateTransaction.readFrom(RLP.input(Bytes.fromHexString(rawPrivateTransaction)));
|
||||
|
||||
final Optional<String> addPayloadEnclaveKey =
|
||||
privacyController.buildAndSendAddPayload(
|
||||
privateTransaction, enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));
|
||||
|
||||
final String enclavePublicKey =
|
||||
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser());
|
||||
final ValidationResult<TransactionInvalidReason> validationResult =
|
||||
@@ -81,8 +90,34 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
|
||||
|
||||
final String enclaveKey =
|
||||
privacyController.sendTransaction(privateTransaction, enclavePublicKey);
|
||||
final Transaction privacyMarkerTransaction =
|
||||
privacyController.createPrivacyMarkerTransaction(enclaveKey, privateTransaction);
|
||||
final Transaction privacyMarkerTransaction;
|
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) {
|
||||
PrivacyGroup privacyGroup = null;
|
||||
try {
|
||||
privacyGroup =
|
||||
privacyController.retrievePrivacyGroup(
|
||||
privateTransaction.getPrivacyGroupId().get().toBase64String(),
|
||||
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));
|
||||
} catch (final EnclaveClientException e) {
|
||||
// it is an onchain group
|
||||
}
|
||||
if (privacyGroup == null
|
||||
|| !privacyGroup
|
||||
.getMembers()
|
||||
.contains(enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()))) {
|
||||
privacyMarkerTransaction =
|
||||
privacyController.createPrivacyMarkerTransaction(
|
||||
buildCompoundKey(enclaveKey, addPayloadEnclaveKey),
|
||||
privateTransaction,
|
||||
Address.ONCHAIN_PRIVACY);
|
||||
} else {
|
||||
privacyMarkerTransaction =
|
||||
privacyController.createPrivacyMarkerTransaction(enclaveKey, privateTransaction);
|
||||
}
|
||||
} else {
|
||||
privacyMarkerTransaction =
|
||||
privacyController.createPrivacyMarkerTransaction(enclaveKey, privateTransaction);
|
||||
}
|
||||
|
||||
return transactionPool
|
||||
.addLocalTransaction(privacyMarkerTransaction)
|
||||
@@ -99,4 +134,14 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
|
||||
return new JsonRpcErrorResponse(id, convertEnclaveInvalidReason(e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
private String buildCompoundKey(
|
||||
final String enclaveKey, final Optional<String> addPayloadEnclaveKey) {
|
||||
return addPayloadEnclaveKey.isPresent()
|
||||
? Bytes.concatenate(
|
||||
Bytes.fromBase64String(enclaveKey),
|
||||
Bytes.fromBase64String(addPayloadEnclaveKey.get()))
|
||||
.toBase64String()
|
||||
: enclaveKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -58,12 +59,13 @@ public class PrivFindPrivacyGroup implements JsonRpcMethod {
|
||||
|
||||
LOG.trace("Finding a privacy group with members {}", Arrays.toString(addresses));
|
||||
|
||||
PrivacyGroup[] response;
|
||||
final List<PrivacyGroup> response;
|
||||
try {
|
||||
response =
|
||||
privacyController.findPrivacyGroup(
|
||||
Arrays.asList(addresses),
|
||||
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));
|
||||
Arrays.asList(
|
||||
privacyController.findPrivacyGroup(
|
||||
Arrays.asList(addresses),
|
||||
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser())));
|
||||
} catch (final MultiTenancyValidationException e) {
|
||||
LOG.error("Unauthorized privacy multi-tenancy rpc request. {}", e.getMessage());
|
||||
return new JsonRpcErrorResponse(
|
||||
|
||||
@@ -17,7 +17,9 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.apache.logging.log4j.LogManager.getLogger;
|
||||
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException;
|
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcEnclaveErrorConverter;
|
||||
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;
|
||||
@@ -33,10 +35,17 @@ import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public class PrivGetPrivateTransaction implements JsonRpcMethod {
|
||||
|
||||
@@ -44,14 +53,17 @@ public class PrivGetPrivateTransaction implements JsonRpcMethod {
|
||||
|
||||
private final BlockchainQueries blockchain;
|
||||
private final PrivacyController privacyController;
|
||||
private final PrivateStateStorage privateStateStorage;
|
||||
private final EnclavePublicKeyProvider enclavePublicKeyProvider;
|
||||
|
||||
public PrivGetPrivateTransaction(
|
||||
final BlockchainQueries blockchain,
|
||||
final PrivacyController privacyController,
|
||||
final PrivateStateStorage privateStateStorage,
|
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider) {
|
||||
this.blockchain = blockchain;
|
||||
this.privacyController = privacyController;
|
||||
this.privateStateStorage = privateStateStorage;
|
||||
this.enclavePublicKeyProvider = enclavePublicKeyProvider;
|
||||
}
|
||||
|
||||
@@ -71,32 +83,101 @@ public class PrivGetPrivateTransaction implements JsonRpcMethod {
|
||||
if (resultTransaction == null) {
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), null);
|
||||
}
|
||||
|
||||
final String payloadKey =
|
||||
resultTransaction.getTransaction().getPayload().slice(0, 32).toBase64String();
|
||||
final String enclaveKey = enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser());
|
||||
final Optional<PrivateTransaction> privateTransaction;
|
||||
try {
|
||||
LOG.trace("Fetching transaction information");
|
||||
final ReceiveResponse receiveResponse =
|
||||
privacyController.retrieveTransaction(
|
||||
resultTransaction.getTransaction().getPayload().toBase64String(),
|
||||
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));
|
||||
LOG.trace("Received transaction information");
|
||||
privateTransaction =
|
||||
findPrivateTransactionInEnclave(
|
||||
payloadKey,
|
||||
resultTransaction.getTransaction().getHash(),
|
||||
enclaveKey,
|
||||
resultTransaction.getBlockHash().get());
|
||||
} catch (final EnclaveClientException e) {
|
||||
return new JsonRpcErrorResponse(
|
||||
requestContext.getRequest().getId(),
|
||||
JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason(e.getMessage()));
|
||||
}
|
||||
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromBase64String(new String(receiveResponse.getPayload(), UTF_8)), false);
|
||||
|
||||
final PrivateTransaction privateTransaction = PrivateTransaction.readFrom(input);
|
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) {
|
||||
if (privateTransaction.isPresent()) {
|
||||
if (privateTransaction.get().getPrivacyGroupId().isPresent()) {
|
||||
return new JsonRpcSuccessResponse(
|
||||
requestContext.getRequest().getId(),
|
||||
new PrivateTransactionGroupResult(privateTransaction));
|
||||
new PrivateTransactionGroupResult(privateTransaction.get()));
|
||||
} else {
|
||||
return new JsonRpcSuccessResponse(
|
||||
requestContext.getRequest().getId(),
|
||||
new PrivateTransactionLegacyResult(privateTransaction));
|
||||
new PrivateTransactionLegacyResult(privateTransaction.get()));
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Failed to fetch private transaction", e);
|
||||
} else {
|
||||
return new JsonRpcErrorResponse(
|
||||
requestContext.getRequest().getId(), JsonRpcError.ENCLAVE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<PrivateTransaction> findPrivateTransactionInEnclave(
|
||||
final String payloadKey,
|
||||
final Hash pmtTransactionHash,
|
||||
final String enclaveKey,
|
||||
final Bytes32 blockHash) {
|
||||
PrivateTransaction privateTransaction;
|
||||
try {
|
||||
LOG.trace("Fetching transaction information");
|
||||
final ReceiveResponse receiveResponse =
|
||||
privacyController.retrieveTransaction(payloadKey, enclaveKey);
|
||||
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromBase64String(new String(receiveResponse.getPayload(), UTF_8)), false);
|
||||
input.enterList();
|
||||
if (input.nextIsList()) {
|
||||
privateTransaction = PrivateTransaction.readFrom(input);
|
||||
input.leaveListLenient();
|
||||
} else {
|
||||
input.reset();
|
||||
privateTransaction = PrivateTransaction.readFrom(input);
|
||||
}
|
||||
LOG.trace("Received transaction information");
|
||||
} catch (final EnclaveClientException e) {
|
||||
Optional<PrivateTransaction> privateTransactionOptional = Optional.empty();
|
||||
if (e.getMessage().equals("EnclavePayloadNotFound")) {
|
||||
privateTransactionOptional = fetchPayloadFromAddBlob(blockHash, pmtTransactionHash);
|
||||
}
|
||||
if (privateTransactionOptional.isEmpty()) {
|
||||
throw e;
|
||||
} else {
|
||||
privateTransaction = privateTransactionOptional.get();
|
||||
}
|
||||
}
|
||||
return Optional.ofNullable(privateTransaction);
|
||||
}
|
||||
|
||||
private Optional<PrivateTransaction> fetchPayloadFromAddBlob(
|
||||
final Bytes32 blockHash, final Hash expectedPrivacyMarkerTransactionHash) {
|
||||
LOG.trace("Fetching transaction information from add blob");
|
||||
final Optional<PrivacyGroupHeadBlockMap> privacyGroupHeadBlockMapOptional =
|
||||
privateStateStorage.getPrivacyGroupHeadBlockMap(blockHash);
|
||||
if (privacyGroupHeadBlockMapOptional.isPresent()) {
|
||||
for (final Bytes32 privacyGroupId : privacyGroupHeadBlockMapOptional.get().keySet()) {
|
||||
final Optional<Bytes32> addDataKey = privateStateStorage.getAddDataKey(privacyGroupId);
|
||||
if (addDataKey.isPresent()) {
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList =
|
||||
privacyController.retrieveAddBlob(addDataKey.get().toBase64String());
|
||||
for (final PrivateTransactionWithMetadata privateTransactionWithMetadata :
|
||||
privateTransactionWithMetadataList) {
|
||||
final Hash actualPrivacyMarkerTransactionHash =
|
||||
privateTransactionWithMetadata
|
||||
.getPrivateTransactionMetadata()
|
||||
.getPrivacyMarkerTransactionHash();
|
||||
if (expectedPrivacyMarkerTransactionHash.equals(actualPrivacyMarkerTransactionHash)) {
|
||||
return Optional.of(privateTransactionWithMetadata.getPrivateTransaction());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,12 @@ import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -88,72 +91,146 @@ public class PrivGetTransactionReceipt implements JsonRpcMethod {
|
||||
final Transaction pmtTransaction =
|
||||
blockBody.getTransactions().get(pmtLocation.getTransactionIndex());
|
||||
|
||||
final Hash blockhash = pmtLocation.getBlockHash();
|
||||
final long blockNumber = blockchain.getBlockchain().getBlockHeader(blockhash).get().getNumber();
|
||||
final Hash blockHash = pmtLocation.getBlockHash();
|
||||
final long blockNumber = blockchain.getBlockchain().getBlockHeader(blockHash).get().getNumber();
|
||||
|
||||
final PrivateTransaction privateTransaction;
|
||||
final String privacyGroupId;
|
||||
final String payloadKey = pmtTransaction.getPayload().slice(0, 32).toBase64String();
|
||||
final String enclaveKey = enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser());
|
||||
|
||||
final Optional<PrivateTransaction> privateTransactionOptional;
|
||||
try {
|
||||
final ReceiveResponse receiveResponse =
|
||||
privacyController.retrieveTransaction(
|
||||
pmtTransaction.getPayload().toBase64String(),
|
||||
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));
|
||||
LOG.trace("Received private transaction information");
|
||||
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromBase64String(new String(receiveResponse.getPayload(), UTF_8)), false);
|
||||
|
||||
privateTransaction = PrivateTransaction.readFrom(input);
|
||||
privacyGroupId = receiveResponse.getPrivacyGroupId();
|
||||
privateTransactionOptional =
|
||||
findPrivateTransactionInEnclave(
|
||||
payloadKey, pmtTransactionHash, enclaveKey, pmtLocation.getBlockHash());
|
||||
} catch (final EnclaveClientException e) {
|
||||
return handleEnclaveException(requestContext, e);
|
||||
}
|
||||
|
||||
final String contractAddress =
|
||||
privateTransaction.getTo().isEmpty()
|
||||
? Address.privateContractAddress(
|
||||
privateTransaction.getSender(),
|
||||
privateTransaction.getNonce(),
|
||||
Bytes.fromBase64String(privacyGroupId))
|
||||
.toString()
|
||||
: null;
|
||||
final String privacyGroupId;
|
||||
if (privateTransactionOptional.isPresent()) {
|
||||
final PrivateTransaction privateTransaction = privateTransactionOptional.get();
|
||||
try {
|
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) {
|
||||
privacyGroupId = privateTransaction.getPrivacyGroupId().get().toBase64String();
|
||||
} else {
|
||||
privacyGroupId =
|
||||
privacyController.retrieveTransaction(payloadKey, enclaveKey).getPrivacyGroupId();
|
||||
}
|
||||
} catch (final EnclaveClientException e) {
|
||||
return handleEnclaveException(requestContext, e);
|
||||
}
|
||||
final String contractAddress =
|
||||
privateTransaction.getTo().isEmpty()
|
||||
? Address.privateContractAddress(
|
||||
privateTransaction.getSender(),
|
||||
privateTransaction.getNonce(),
|
||||
Bytes.fromBase64String(privacyGroupId))
|
||||
.toString()
|
||||
: null;
|
||||
|
||||
LOG.trace("Calculated contractAddress: {}", contractAddress);
|
||||
LOG.trace("Calculated contractAddress: {}", contractAddress);
|
||||
|
||||
final Bytes rlpEncoded = RLP.encode(privateTransaction::writeTo);
|
||||
final Bytes32 txHash = org.hyperledger.besu.crypto.Hash.keccak256(rlpEncoded);
|
||||
LOG.trace("Calculated private transaction hash: {}", txHash);
|
||||
final Bytes rlpEncoded = RLP.encode(privateTransaction::writeTo);
|
||||
final Bytes32 txHash = org.hyperledger.besu.crypto.Hash.keccak256(rlpEncoded);
|
||||
LOG.trace("Calculated private transaction hash: {}", txHash);
|
||||
|
||||
final PrivateTransactionReceipt privateTransactioReceipt =
|
||||
privacyParameters
|
||||
.getPrivateStateStorage()
|
||||
.getTransactionReceipt(blockhash, txHash)
|
||||
.orElse(PrivateTransactionReceipt.EMPTY);
|
||||
final PrivateTransactionReceipt privateTransactioReceipt =
|
||||
privacyParameters
|
||||
.getPrivateStateStorage()
|
||||
.getTransactionReceipt(blockHash, txHash)
|
||||
.orElse(PrivateTransactionReceipt.FAILED);
|
||||
|
||||
LOG.trace("Processed private transaction receipt");
|
||||
LOG.trace("Processed private transaction receipt");
|
||||
|
||||
final PrivateTransactionReceiptResult result =
|
||||
new PrivateTransactionReceiptResult(
|
||||
contractAddress,
|
||||
privateTransaction.getSender().toString(),
|
||||
privateTransaction.getTo().map(Address::toString).orElse(null),
|
||||
privateTransactioReceipt.getLogs(),
|
||||
privateTransactioReceipt.getOutput(),
|
||||
blockhash,
|
||||
blockNumber,
|
||||
pmtLocation.getTransactionIndex(),
|
||||
pmtTransaction.getHash(),
|
||||
privateTransaction.getHash(),
|
||||
privateTransaction.getPrivateFrom(),
|
||||
privateTransaction.getPrivateFor().orElse(null),
|
||||
privateTransaction.getPrivacyGroupId().orElse(null),
|
||||
privateTransactioReceipt.getRevertReason().orElse(null),
|
||||
Quantity.create(privateTransactioReceipt.getStatus()));
|
||||
final PrivateTransactionReceiptResult result =
|
||||
new PrivateTransactionReceiptResult(
|
||||
contractAddress,
|
||||
privateTransaction.getSender().toString(),
|
||||
privateTransaction.getTo().map(Address::toString).orElse(null),
|
||||
privateTransactioReceipt.getLogs(),
|
||||
privateTransactioReceipt.getOutput(),
|
||||
blockHash,
|
||||
blockNumber,
|
||||
pmtLocation.getTransactionIndex(),
|
||||
pmtTransaction.getHash(),
|
||||
privateTransaction.getHash(),
|
||||
privateTransaction.getPrivateFrom(),
|
||||
privateTransaction.getPrivateFor().orElse(null),
|
||||
privateTransaction.getPrivacyGroupId().orElse(null),
|
||||
privateTransactioReceipt.getRevertReason().orElse(null),
|
||||
Quantity.create(privateTransactioReceipt.getStatus()));
|
||||
|
||||
LOG.trace("Created Private Transaction Receipt Result from given Transaction Hash");
|
||||
LOG.trace("Created Private Transaction Receipt Result from given Transaction Hash");
|
||||
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result);
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result);
|
||||
} else {
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<PrivateTransaction> findPrivateTransactionInEnclave(
|
||||
final String payloadKey,
|
||||
final Hash pmtTransactionHash,
|
||||
final String enclaveKey,
|
||||
final Bytes32 blockHash) {
|
||||
PrivateTransaction privateTransaction;
|
||||
try {
|
||||
LOG.trace("Fetching transaction information");
|
||||
final ReceiveResponse receiveResponse =
|
||||
privacyController.retrieveTransaction(payloadKey, enclaveKey);
|
||||
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromBase64String(new String(receiveResponse.getPayload(), UTF_8)), false);
|
||||
input.enterList();
|
||||
if (input.nextIsList()) {
|
||||
privateTransaction = PrivateTransaction.readFrom(input);
|
||||
input.leaveListLenient();
|
||||
} else {
|
||||
input.reset();
|
||||
privateTransaction = PrivateTransaction.readFrom(input);
|
||||
}
|
||||
LOG.trace("Received transaction information");
|
||||
} catch (final EnclaveClientException e) {
|
||||
Optional<PrivateTransaction> privateTransactionOptional = Optional.empty();
|
||||
if (e.getMessage().equals("EnclavePayloadNotFound")) {
|
||||
privateTransactionOptional = fetchPayloadFromAddBlob(blockHash, pmtTransactionHash);
|
||||
}
|
||||
if (privateTransactionOptional.isEmpty()) {
|
||||
throw e;
|
||||
} else {
|
||||
privateTransaction = privateTransactionOptional.get();
|
||||
}
|
||||
}
|
||||
return Optional.ofNullable(privateTransaction);
|
||||
}
|
||||
|
||||
private Optional<PrivateTransaction> fetchPayloadFromAddBlob(
|
||||
final Bytes32 blockHash, final Hash expectedPrivacyMarkerTransactionHash) {
|
||||
LOG.trace("Fetching transaction information from add blob");
|
||||
final Optional<PrivacyGroupHeadBlockMap> privacyGroupHeadBlockMapOptional =
|
||||
privacyParameters.getPrivateStateStorage().getPrivacyGroupHeadBlockMap(blockHash);
|
||||
if (privacyGroupHeadBlockMapOptional.isPresent()) {
|
||||
for (final Bytes32 privacyGroupId : privacyGroupHeadBlockMapOptional.get().keySet()) {
|
||||
final Optional<Bytes32> addDataKey =
|
||||
privacyParameters.getPrivateStateStorage().getAddDataKey(privacyGroupId);
|
||||
if (addDataKey.isPresent()) {
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList =
|
||||
privacyController.retrieveAddBlob(addDataKey.get().toBase64String());
|
||||
for (final PrivateTransactionWithMetadata privateTransactionWithMetadata :
|
||||
privateTransactionWithMetadataList) {
|
||||
final Hash actualPrivacyMarkerTransactionHash =
|
||||
privateTransactionWithMetadata
|
||||
.getPrivateTransactionMetadata()
|
||||
.getPrivacyMarkerTransactionHash();
|
||||
if (expectedPrivacyMarkerTransactionHash.equals(actualPrivacyMarkerTransactionHash)) {
|
||||
return Optional.of(privateTransactionWithMetadata.getPrivateTransaction());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private JsonRpcResponse handleEnclaveException(
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.privx;
|
||||
|
||||
import static org.apache.logging.log4j.LogManager.getLogger;
|
||||
|
||||
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.JsonRpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider;
|
||||
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.privacy.PrivacyController;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class PrivxFindOnChainPrivacyGroup implements JsonRpcMethod {
|
||||
|
||||
private static final Logger LOG = getLogger();
|
||||
private final PrivacyController privacyController;
|
||||
private final EnclavePublicKeyProvider enclavePublicKeyProvider;
|
||||
|
||||
public PrivxFindOnChainPrivacyGroup(
|
||||
final PrivacyController privacyController,
|
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider) {
|
||||
this.privacyController = privacyController;
|
||||
this.enclavePublicKeyProvider = enclavePublicKeyProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return RpcMethod.PRIVX_FIND_PRIVACY_GROUP.getMethodName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
|
||||
LOG.trace("Executing {}", RpcMethod.PRIVX_FIND_PRIVACY_GROUP.getMethodName());
|
||||
|
||||
final String[] addresses = requestContext.getRequiredParameter(0, String[].class);
|
||||
|
||||
LOG.trace("Finding a privacy group with members {}", Arrays.toString(addresses));
|
||||
|
||||
final List<PrivacyGroup> response =
|
||||
privacyController.findOnChainPrivacyGroup(
|
||||
Arrays.asList(addresses),
|
||||
enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));
|
||||
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), response);
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,8 @@ public class JsonRpcMethodsFactory {
|
||||
new PermJsonRpcMethods(accountsWhitelistController, nodeWhitelistController),
|
||||
new PrivJsonRpcMethods(
|
||||
blockchainQueries, protocolSchedule, transactionPool, privacyParameters),
|
||||
new PrivxJsonRpcMethods(
|
||||
blockchainQueries, protocolSchedule, transactionPool, privacyParameters),
|
||||
new Web3JsonRpcMethods(clientVersion),
|
||||
// TRACE Methods (Disabled while under development)
|
||||
new TraceJsonRpcMethods(blockchainQueries, protocolSchedule),
|
||||
|
||||
@@ -67,7 +67,10 @@ public class PrivJsonRpcMethods extends PrivacyApiGroupJsonRpcMethods {
|
||||
new PrivGetPrivacyPrecompileAddress(getPrivacyParameters()),
|
||||
new PrivGetTransactionCount(privacyController, enclavePublicKeyProvider),
|
||||
new PrivGetPrivateTransaction(
|
||||
getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
|
||||
getBlockchainQueries(),
|
||||
privacyController,
|
||||
getPrivacyParameters().getPrivateStateStorage(),
|
||||
enclavePublicKeyProvider),
|
||||
new PrivDistributeRawTransaction(privacyController, enclavePublicKeyProvider),
|
||||
new PrivCall(getBlockchainQueries(), privacyController, enclavePublicKeyProvider),
|
||||
new PrivGetCode(getBlockchainQueries(), privacyController, enclavePublicKeyProvider));
|
||||
|
||||
@@ -146,6 +146,7 @@ public abstract class PrivacyApiGroupJsonRpcMethods extends ApiGroupJsonRpcMetho
|
||||
final PrivateMarkerTransactionFactory markerTransactionFactory) {
|
||||
final DefaultPrivacyController defaultPrivacyController =
|
||||
new DefaultPrivacyController(
|
||||
getBlockchainQueries().getBlockchain(),
|
||||
privacyParameters,
|
||||
protocolSchedule.getChainId(),
|
||||
markerTransactionFactory,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.methods;
|
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
|
||||
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.privacy.methods.EnclavePublicKeyProvider;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.privx.PrivxFindOnChainPrivacyGroup;
|
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PrivxJsonRpcMethods extends PrivacyApiGroupJsonRpcMethods {
|
||||
|
||||
public PrivxJsonRpcMethods(
|
||||
final BlockchainQueries blockchainQueries,
|
||||
final ProtocolSchedule<?> protocolSchedule,
|
||||
final TransactionPool transactionPool,
|
||||
final PrivacyParameters privacyParameters) {
|
||||
super(blockchainQueries, protocolSchedule, transactionPool, privacyParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RpcApi getApiGroup() {
|
||||
return RpcApis.PRIV;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, JsonRpcMethod> create(
|
||||
final PrivacyController privacyController,
|
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider) {
|
||||
return mapOf(new PrivxFindOnChainPrivacyGroup(privacyController, enclavePublicKeyProvider));
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.eea;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.PRIVATE_TRANSACTION_FAILED;
|
||||
@@ -26,6 +27,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
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;
|
||||
@@ -199,6 +201,10 @@ public class EeaSendRawTransactionTest {
|
||||
.thenReturn(MOCK_ORION_KEY);
|
||||
when(privacyController.validatePrivateTransaction(any(PrivateTransaction.class), anyString()))
|
||||
.thenReturn(ValidationResult.valid());
|
||||
when(privacyController.retrievePrivacyGroup(any(String.class), any(String.class)))
|
||||
.thenReturn(
|
||||
new PrivacyGroup(
|
||||
"", PrivacyGroup.Type.PANTHEON, "", "", singletonList(ENCLAVE_PUBLIC_KEY)));
|
||||
when(privacyController.createPrivacyMarkerTransaction(
|
||||
any(String.class), any(PrivateTransaction.class)))
|
||||
.thenReturn(PUBLIC_TRANSACTION);
|
||||
@@ -228,6 +234,43 @@ public class EeaSendRawTransactionTest {
|
||||
verify(transactionPool).addLocalTransaction(any(Transaction.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validOnChainTransactionPrivacyGroupIsSentToTransactionPool() {
|
||||
when(privacyController.sendTransaction(any(PrivateTransaction.class), any()))
|
||||
.thenReturn(MOCK_ORION_KEY);
|
||||
when(privacyController.validatePrivateTransaction(
|
||||
any(PrivateTransaction.class), any(String.class)))
|
||||
.thenReturn(ValidationResult.valid());
|
||||
when(privacyController.createPrivacyMarkerTransaction(
|
||||
any(String.class), any(PrivateTransaction.class), any(Address.class)))
|
||||
.thenReturn(PUBLIC_TRANSACTION);
|
||||
when(transactionPool.addLocalTransaction(any(Transaction.class)))
|
||||
.thenReturn(ValidationResult.valid());
|
||||
|
||||
final JsonRpcRequestContext request =
|
||||
new JsonRpcRequestContext(
|
||||
new JsonRpcRequest(
|
||||
"2.0",
|
||||
"eea_sendRawTransaction",
|
||||
new String[] {VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP}));
|
||||
|
||||
final JsonRpcResponse expectedResponse =
|
||||
new JsonRpcSuccessResponse(
|
||||
request.getRequest().getId(),
|
||||
"0x221e930a2c18d91fca4d509eaa3512f3e01fef266f660e32473de67474b36c15");
|
||||
|
||||
final JsonRpcResponse actualResponse = method.response(request);
|
||||
|
||||
assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse);
|
||||
verify(privacyController).sendTransaction(any(PrivateTransaction.class), any());
|
||||
verify(privacyController)
|
||||
.validatePrivateTransaction(any(PrivateTransaction.class), any(String.class));
|
||||
verify(privacyController)
|
||||
.createPrivacyMarkerTransaction(
|
||||
any(String.class), any(PrivateTransaction.class), eq(Address.ONCHAIN_PRIVACY));
|
||||
verify(transactionPool).addLocalTransaction(any(Transaction.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidTransactionWithoutPrivateFromFieldFailsWithDecodeError() {
|
||||
final JsonRpcRequestContext request =
|
||||
|
||||
@@ -73,6 +73,7 @@ public class PrivFindPrivacyGroupTest {
|
||||
privacyGroup.setMembers(Lists.list("member1"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void findsPrivacyGroupWithValidAddresses() {
|
||||
when(privacyController.findPrivacyGroup(ADDRESSES, ENCLAVE_PUBLIC_KEY))
|
||||
@@ -83,9 +84,9 @@ public class PrivFindPrivacyGroupTest {
|
||||
|
||||
final JsonRpcSuccessResponse response =
|
||||
(JsonRpcSuccessResponse) privFindPrivacyGroup.response(request);
|
||||
final PrivacyGroup[] result = (PrivacyGroup[]) response.getResult();
|
||||
final List<PrivacyGroup> result = (List<PrivacyGroup>) response.getResult();
|
||||
assertThat(result).hasSize(1);
|
||||
assertThat(result[0]).isEqualToComparingFieldByField(privacyGroup);
|
||||
assertThat(result.get(0)).isEqualToComparingFieldByField(privacyGroup);
|
||||
verify(privacyController).findPrivacyGroup(ADDRESSES, ENCLAVE_PUBLIC_KEY);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,13 +46,15 @@ import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.Restriction;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.auth.User;
|
||||
import io.vertx.ext.auth.jwt.impl.JWTUser;
|
||||
@@ -78,6 +80,9 @@ public class PrivGetPrivateTransactionTest {
|
||||
Bytes.fromBase64String("5bpr9tz4zhmWmk9RlNng93Ky7lXwFkMc7+ckoFgUMku=").toString();
|
||||
private static final Bytes ENCLAVE_KEY =
|
||||
Bytes.fromBase64String("93Ky7lXwFkMc7+ckoFgUMku5bpr9tz4zhmWmk9RlNng=");
|
||||
private static final List<Bytes> TEST_PRIVATE_FOR =
|
||||
Collections.singletonList(
|
||||
Bytes.fromBase64String("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="));
|
||||
|
||||
private final PrivateTransaction.Builder privateTransactionBuilder =
|
||||
PrivateTransaction.builder()
|
||||
@@ -105,6 +110,7 @@ public class PrivGetPrivateTransactionTest {
|
||||
|
||||
private final Enclave enclave = mock(Enclave.class);
|
||||
private final PrivacyParameters privacyParameters = mock(PrivacyParameters.class);
|
||||
private final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class);
|
||||
private final BlockchainQueries blockchain = mock(BlockchainQueries.class);
|
||||
private final TransactionWithMetadata returnedTransaction = mock(TransactionWithMetadata.class);
|
||||
private final Transaction justTransaction = mock(Transaction.class);
|
||||
@@ -117,6 +123,7 @@ public class PrivGetPrivateTransactionTest {
|
||||
public void before() {
|
||||
when(privacyParameters.getEnclave()).thenReturn(enclave);
|
||||
when(privacyParameters.isEnabled()).thenReturn(true);
|
||||
when(returnedTransaction.getBlockHash()).thenReturn(Optional.of(Hash.ZERO));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -127,16 +134,13 @@ public class PrivGetPrivateTransactionTest {
|
||||
when(justTransaction.getPayload()).thenReturn(ENCLAVE_KEY);
|
||||
|
||||
final PrivateTransaction privateTransaction =
|
||||
privateTransactionBuilder
|
||||
.privateFor(
|
||||
Lists.newArrayList(
|
||||
Bytes.fromBase64String("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=")))
|
||||
.signAndBuild(KEY_PAIR);
|
||||
privateTransactionBuilder.privateFor(TEST_PRIVATE_FOR).signAndBuild(KEY_PAIR);
|
||||
final PrivateTransactionLegacyResult privateTransactionLegacyResult =
|
||||
new PrivateTransactionLegacyResult(privateTransaction);
|
||||
|
||||
final PrivGetPrivateTransaction privGetPrivateTransaction =
|
||||
new PrivGetPrivateTransaction(blockchain, privacyController, enclavePublicKeyProvider);
|
||||
new PrivGetPrivateTransaction(
|
||||
blockchain, privacyController, privateStateStorage, enclavePublicKeyProvider);
|
||||
final Object[] params = new Object[] {TRANSACTION_HASH};
|
||||
final JsonRpcRequestContext request =
|
||||
new JsonRpcRequestContext(
|
||||
@@ -163,17 +167,16 @@ public class PrivGetPrivateTransactionTest {
|
||||
when(blockchain.transactionByHash(any(Hash.class)))
|
||||
.thenReturn(Optional.of(returnedTransaction));
|
||||
when(returnedTransaction.getTransaction()).thenReturn(justTransaction);
|
||||
when(justTransaction.getPayload()).thenReturn(Bytes.fromBase64String(""));
|
||||
when(justTransaction.getPayload()).thenReturn(ENCLAVE_KEY);
|
||||
|
||||
final PrivateTransaction privateTransaction =
|
||||
privateTransactionBuilder
|
||||
.privacyGroupId(Bytes.fromBase64String("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))
|
||||
.signAndBuild(KEY_PAIR);
|
||||
privateTransactionBuilder.privacyGroupId(ENCLAVE_KEY).signAndBuild(KEY_PAIR);
|
||||
final PrivateTransactionGroupResult privateTransactionGroupResult =
|
||||
new PrivateTransactionGroupResult(privateTransaction);
|
||||
|
||||
final PrivGetPrivateTransaction privGetPrivateTransaction =
|
||||
new PrivGetPrivateTransaction(blockchain, privacyController, enclavePublicKeyProvider);
|
||||
new PrivGetPrivateTransaction(
|
||||
blockchain, privacyController, privateStateStorage, enclavePublicKeyProvider);
|
||||
|
||||
final Object[] params = new Object[] {TRANSACTION_HASH};
|
||||
final JsonRpcRequestContext request =
|
||||
@@ -200,15 +203,14 @@ public class PrivGetPrivateTransactionTest {
|
||||
when(blockchain.transactionByHash(any(Hash.class)))
|
||||
.thenReturn(Optional.of(returnedTransaction));
|
||||
when(returnedTransaction.getTransaction()).thenReturn(justTransaction);
|
||||
when(justTransaction.getPayload()).thenReturn(Bytes.fromBase64String(""));
|
||||
when(justTransaction.getPayload()).thenReturn(ENCLAVE_KEY);
|
||||
|
||||
final PrivateTransaction privateTransaction =
|
||||
privateTransactionBuilder
|
||||
.privacyGroupId(Bytes.fromBase64String("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="))
|
||||
.signAndBuild(KEY_PAIR);
|
||||
privateTransactionBuilder.privacyGroupId(ENCLAVE_KEY).signAndBuild(KEY_PAIR);
|
||||
|
||||
final PrivGetPrivateTransaction privGetPrivateTransaction =
|
||||
new PrivGetPrivateTransaction(blockchain, privacyController, enclavePublicKeyProvider);
|
||||
new PrivGetPrivateTransaction(
|
||||
blockchain, privacyController, privateStateStorage, enclavePublicKeyProvider);
|
||||
|
||||
final Object[] params = new Object[] {TRANSACTION_HASH};
|
||||
final JsonRpcRequestContext request =
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -49,14 +50,13 @@ import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.privacy.Restriction;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.vertx.core.json.JsonObject;
|
||||
import io.vertx.ext.auth.User;
|
||||
import io.vertx.ext.auth.jwt.impl.JWTUser;
|
||||
@@ -72,7 +72,8 @@ public class PrivGetTransactionReceiptTest {
|
||||
@Rule public final TemporaryFolder temp = new TemporaryFolder();
|
||||
|
||||
private static final String ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
|
||||
private static final Bytes ENCLAVE_KEY = Bytes.wrap("EnclaveKey".getBytes(UTF_8));
|
||||
private static final Bytes ENCLAVE_KEY =
|
||||
Bytes.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8));
|
||||
private static final Address SENDER =
|
||||
Address.fromHexString("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73");
|
||||
|
||||
@@ -103,10 +104,8 @@ public class PrivGetTransactionReceiptTest {
|
||||
+ "daa4f6b2f003d1b0180029"))
|
||||
.sender(SENDER)
|
||||
.chainId(BigInteger.valueOf(2018))
|
||||
.privateFrom(Bytes.wrap("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=".getBytes(UTF_8)))
|
||||
.privateFor(
|
||||
Lists.newArrayList(
|
||||
Bytes.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=".getBytes(UTF_8))))
|
||||
.privateFrom(Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY))
|
||||
.privateFor(Collections.singletonList(ENCLAVE_KEY))
|
||||
.restriction(Restriction.RESTRICTED)
|
||||
.signAndBuild(KEY_PAIR);
|
||||
|
||||
@@ -115,9 +114,9 @@ public class PrivGetTransactionReceiptTest {
|
||||
.nonce(0)
|
||||
.gasPrice(Wei.of(1000))
|
||||
.gasLimit(3000000)
|
||||
.to(Address.fromHexString("0x627306090abab3a6e1400e9345bc60c78a8bef57"))
|
||||
.to(Address.DEFAULT_PRIVACY)
|
||||
.value(Wei.ZERO)
|
||||
.payload(Bytes.wrap("EnclaveKey".getBytes(UTF_8)))
|
||||
.payload(ENCLAVE_KEY)
|
||||
.sender(SENDER)
|
||||
.chainId(BigInteger.valueOf(2018))
|
||||
.signAndBuild(KEY_PAIR);
|
||||
@@ -125,20 +124,17 @@ public class PrivGetTransactionReceiptTest {
|
||||
private final PrivateTransactionReceiptResult expectedResult =
|
||||
new PrivateTransactionReceiptResult(
|
||||
"0x0bac79b78b9866ef11c989ad21a7fcf15f7a18d7",
|
||||
"0xfe3b557e8fb62b89f4916b721be55ceb828dbd73",
|
||||
SENDER.toHexString(),
|
||||
null,
|
||||
Collections.emptyList(),
|
||||
Bytes.EMPTY,
|
||||
null,
|
||||
0,
|
||||
0,
|
||||
Hash.fromHexString("0x65348ddfe0b282c26862b4610a8c45fd8486a93ae6e2b197836c826b4b671848"),
|
||||
Hash.fromHexString("0x43ef5094212ba4862d6b310a3d337c3478fdf942c5ed3f8e792ad93d6d96994d"),
|
||||
Bytes.fromHexString(
|
||||
"0x41316156744d784c4355486d425648586f5a7a7a42675062572f776a3561784470573958386c393153476f3d"),
|
||||
Collections.singletonList(
|
||||
Bytes.fromHexString(
|
||||
"0x4b6f32625671442b6e4e6c4e594c35454537793349644f6e766966746a69697a706a52742b4854754642733d")),
|
||||
transaction.getHash(),
|
||||
privateTransaction.getHash(),
|
||||
Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY),
|
||||
Collections.singletonList(ENCLAVE_KEY),
|
||||
null,
|
||||
null,
|
||||
Quantity.create(Bytes.of(1).toUnsignedBigInteger()));
|
||||
@@ -155,12 +151,13 @@ public class PrivGetTransactionReceiptTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
when(privacyController.retrieveTransaction(anyString(), any()))
|
||||
.thenReturn(
|
||||
new ReceiveResponse(
|
||||
Base64.getEncoder().encode(RLP.encode(privateTransaction::writeTo).toArray()),
|
||||
"",
|
||||
null));
|
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
|
||||
rlpOutput.startList();
|
||||
privateTransaction.writeTo(rlpOutput);
|
||||
rlpOutput.endList();
|
||||
final byte[] src = rlpOutput.encoded().toArray();
|
||||
when(privacyController.retrieveTransaction(anyString(), anyString()))
|
||||
.thenReturn(new ReceiveResponse(Base64.getEncoder().encode(src), "", null));
|
||||
|
||||
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
|
||||
final TransactionLocation transactionLocation = new TransactionLocation(Hash.EMPTY, 0);
|
||||
@@ -200,7 +197,9 @@ public class PrivGetTransactionReceiptTest {
|
||||
(PrivateTransactionReceiptResult) response.getResult();
|
||||
|
||||
assertThat(result).isEqualToComparingFieldByField(expectedResult);
|
||||
verify(privacyController).retrieveTransaction(ENCLAVE_KEY.toBase64String(), ENCLAVE_PUBLIC_KEY);
|
||||
verify(privacyController, times(2))
|
||||
.retrieveTransaction(
|
||||
transaction.getPayload().slice(0, 32).toBase64String(), ENCLAVE_PUBLIC_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.mainnet.precompiles.privacy;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -37,6 +36,7 @@ import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
|
||||
import org.hyperledger.besu.ethereum.vm.MessageFrame;
|
||||
import org.hyperledger.besu.ethereum.vm.OperationTracer;
|
||||
@@ -45,7 +45,6 @@ import org.hyperledger.orion.testutil.OrionKeyConfiguration;
|
||||
import org.hyperledger.orion.testutil.OrionTestHarness;
|
||||
import org.hyperledger.orion.testutil.OrionTestHarnessFactory;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -63,21 +62,18 @@ public class PrivacyPrecompiledContractIntegrationTest {
|
||||
|
||||
@ClassRule public static final TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private static final byte[] VALID_PRIVATE_TRANSACTION_RLP_BASE64 =
|
||||
Base64.getEncoder()
|
||||
.encode(
|
||||
Bytes.fromHexString(
|
||||
"0xf90113800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87"
|
||||
+ "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
+ "ffff801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d"
|
||||
+ "495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab94"
|
||||
+ "9f53faa07bd2c804ac41316156744d784c4355486d425648586f5a7a7a4267"
|
||||
+ "5062572f776a3561784470573958386c393153476f3df85aac41316156744d"
|
||||
+ "784c4355486d425648586f5a7a7a42675062572f776a356178447057395838"
|
||||
+ "6c393153476f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f"
|
||||
+ "6e766966746a69697a706a52742b4854754642733d8a726573747269637465"
|
||||
+ "64")
|
||||
.toArray());
|
||||
private static final Bytes VALID_PRIVATE_TRANSACTION_RLP =
|
||||
Bytes.fromHexString(
|
||||
"0xf90113800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87"
|
||||
+ "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
+ "ffff801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d"
|
||||
+ "495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab94"
|
||||
+ "9f53faa07bd2c804ac41316156744d784c4355486d425648586f5a7a7a4267"
|
||||
+ "5062572f776a3561784470573958386c393153476f3df85aac41316156744d"
|
||||
+ "784c4355486d425648586f5a7a7a42675062572f776a356178447057395838"
|
||||
+ "6c393153476f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f"
|
||||
+ "6e766966746a69697a706a52742b4854754642733d8a726573747269637465"
|
||||
+ "64");
|
||||
private static final String DEFAULT_OUTPUT = "0x01";
|
||||
|
||||
private static Enclave enclave;
|
||||
@@ -170,7 +166,10 @@ public class PrivacyPrecompiledContractIntegrationTest {
|
||||
public void testSendAndReceive() {
|
||||
final List<String> publicKeys = testHarness.getPublicKeys();
|
||||
|
||||
final String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8);
|
||||
final BytesValueRLPOutput bytesValueRLPOutput = new BytesValueRLPOutput();
|
||||
bytesValueRLPOutput.writeRLP(VALID_PRIVATE_TRANSACTION_RLP);
|
||||
|
||||
final String s = bytesValueRLPOutput.encoded().toBase64String();
|
||||
final SendResponse sr =
|
||||
enclave.send(s, publicKeys.get(0), Lists.newArrayList(publicKeys.get(0)));
|
||||
|
||||
@@ -191,7 +190,7 @@ public class PrivacyPrecompiledContractIntegrationTest {
|
||||
final List<String> publicKeys = testHarness.getPublicKeys();
|
||||
publicKeys.add("noPrivateKey");
|
||||
|
||||
final String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8);
|
||||
final String s = VALID_PRIVATE_TRANSACTION_RLP.toBase64String();
|
||||
|
||||
final Throwable thrown = catchThrowable(() -> enclave.send(s, publicKeys.get(0), publicKeys));
|
||||
|
||||
@@ -203,7 +202,7 @@ public class PrivacyPrecompiledContractIntegrationTest {
|
||||
final List<String> publicKeys = testHarness.getPublicKeys();
|
||||
publicKeys.add("noPrivateKenoPrivateKenoPrivateKenoPrivateK");
|
||||
|
||||
final String s = new String(VALID_PRIVATE_TRANSACTION_RLP_BASE64, UTF_8);
|
||||
final String s = VALID_PRIVATE_TRANSACTION_RLP.toBase64String();
|
||||
|
||||
final Throwable thrown = catchThrowable(() -> enclave.send(s, publicKeys.get(0), publicKeys));
|
||||
|
||||
|
||||
@@ -45,6 +45,9 @@ public class Address extends DelegatingBytes implements org.hyperledger.besu.plu
|
||||
// Last address that can be generated for a pre-compiled contract
|
||||
public static final Integer PRIVACY = Byte.MAX_VALUE - 1;
|
||||
public static final Address DEFAULT_PRIVACY = Address.precompiled(PRIVACY);
|
||||
public static final Address ONCHAIN_PRIVACY = Address.precompiled(PRIVACY - 1);
|
||||
public static final Address PRIVACY_PROXY = Address.precompiled(PRIVACY - 2);
|
||||
public static final Address DEFAULT_PRIVACY_MANAGEMENT = Address.precompiled(PRIVACY - 3);
|
||||
|
||||
public static final Address ZERO = Address.fromHexString("0x0");
|
||||
|
||||
|
||||
@@ -133,7 +133,7 @@ public class MainnetMessageCallProcessor extends AbstractMessageProcessor {
|
||||
final Bytes output = contract.compute(frame.getInputData(), frame);
|
||||
operationTracer.tracePrecompileCall(frame, gasRequirement, output);
|
||||
if (output != null) {
|
||||
if (contract.getName().equals("Privacy")) {
|
||||
if (contract.getName().equals("Privacy") || contract.getName().equals("OnChainPrivacy")) {
|
||||
// do not decrement the gas requirement for a privacy pre-compile contract call -> leads
|
||||
// to discrepancies in receipts root between public and private nodes in a network.
|
||||
frame.incrementRemainingGas(gasRequirement);
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.mainnet.precompiles.ECRECPrecompiledContrac
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.IDPrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.RIPEMD160PrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.SHA256PrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.OnChainPrivacyPrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.PrivacyPrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.vm.GasCalculator;
|
||||
|
||||
@@ -124,6 +125,12 @@ public abstract class MainnetPrecompiledContractRegistries {
|
||||
new PrivacyPrecompiledContract(
|
||||
precompiledContractConfiguration.getGasCalculator(),
|
||||
precompiledContractConfiguration.getPrivacyParameters()));
|
||||
registry.put(
|
||||
Address.ONCHAIN_PRIVACY,
|
||||
accountVersion,
|
||||
new OnChainPrivacyPrecompiledContract(
|
||||
precompiledContractConfiguration.getGasCalculator(),
|
||||
precompiledContractConfiguration.getPrivacyParameters()));
|
||||
return registry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.TransactionFilter;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Comparator;
|
||||
@@ -78,4 +79,13 @@ public class MutableProtocolSchedule<C> implements ProtocolSchedule<C> {
|
||||
public void setTransactionFilter(final TransactionFilter transactionFilter) {
|
||||
protocolSpecs.forEach(spec -> spec.getSpec().setTransactionFilter(transactionFilter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicWorldStateArchiveForPrivacyBlockProcessor(
|
||||
final WorldStateArchive publicWorldStateArchive) {
|
||||
protocolSpecs.forEach(
|
||||
spec ->
|
||||
spec.getSpec()
|
||||
.setPublicWorldStateArchiveForPrivacyBlockProcessor(publicWorldStateArchive));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,23 +14,52 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import org.hyperledger.besu.enclave.Enclave;
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException;
|
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateStateRehydration;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionWithMetadata;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public class PrivacyBlockProcessor implements BlockProcessor {
|
||||
private final BlockProcessor blockProcessor;
|
||||
private final ProtocolSchedule<?> protocolSchedule;
|
||||
private final Enclave enclave;
|
||||
private final PrivateStateStorage privateStateStorage;
|
||||
private final WorldStateArchive privateWorldStateArchive;
|
||||
private WorldStateArchive publicWorldStateArchive;
|
||||
|
||||
public PrivacyBlockProcessor(
|
||||
final BlockProcessor blockProcessor, final PrivateStateStorage privateStateStorage) {
|
||||
public <C> PrivacyBlockProcessor(
|
||||
final BlockProcessor blockProcessor,
|
||||
final ProtocolSchedule<C> protocolSchedule,
|
||||
final Enclave enclave,
|
||||
final PrivateStateStorage privateStateStorage,
|
||||
final WorldStateArchive privateWorldStateArchive) {
|
||||
this.blockProcessor = blockProcessor;
|
||||
this.protocolSchedule = protocolSchedule;
|
||||
this.enclave = enclave;
|
||||
this.privateStateStorage = privateStateStorage;
|
||||
this.privateWorldStateArchive = privateWorldStateArchive;
|
||||
}
|
||||
|
||||
public void setPublicWorldStateArchive(final WorldStateArchive publicWorldStateArchive) {
|
||||
this.publicWorldStateArchive = publicWorldStateArchive;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,6 +69,47 @@ public class PrivacyBlockProcessor implements BlockProcessor {
|
||||
final BlockHeader blockHeader,
|
||||
final List<Transaction> transactions,
|
||||
final List<BlockHeader> ommers) {
|
||||
final PrivacyGroupHeadBlockMap preProcessPrivacyGroupHeadBlockMap =
|
||||
new PrivacyGroupHeadBlockMap(
|
||||
privateStateStorage
|
||||
.getPrivacyGroupHeadBlockMap(blockHeader.getParentHash())
|
||||
.orElse(PrivacyGroupHeadBlockMap.EMPTY));
|
||||
transactions.stream()
|
||||
.filter(
|
||||
t ->
|
||||
t.getTo().isPresent()
|
||||
&& t.getTo().equals(Optional.of(Address.ONCHAIN_PRIVACY))
|
||||
&& t.getPayload().size() == 64)
|
||||
.forEach(
|
||||
t -> {
|
||||
final Bytes32 addKey = Bytes32.wrap(t.getPayload().slice(32, 32));
|
||||
try {
|
||||
final ReceiveResponse receiveResponse = enclave.receive(addKey.toBase64String());
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList =
|
||||
deserializeAddToGroupPayload(
|
||||
Bytes.wrap(Base64.getDecoder().decode(receiveResponse.getPayload())));
|
||||
final Bytes32 privacyGroupId =
|
||||
Bytes32.wrap(
|
||||
privateTransactionWithMetadataList
|
||||
.get(0)
|
||||
.getPrivateTransaction()
|
||||
.getPrivacyGroupId()
|
||||
.get());
|
||||
if (!preProcessPrivacyGroupHeadBlockMap.containsKey(privacyGroupId)) {
|
||||
final PrivateStateRehydration privateStateRehydration =
|
||||
new PrivateStateRehydration(
|
||||
privateStateStorage,
|
||||
blockchain,
|
||||
protocolSchedule,
|
||||
publicWorldStateArchive,
|
||||
privateWorldStateArchive);
|
||||
privateStateRehydration.rehydrate(privateTransactionWithMetadataList);
|
||||
privateStateStorage.updater().putAddDataKey(privacyGroupId, addKey).commit();
|
||||
}
|
||||
} catch (final EnclaveClientException e) {
|
||||
// we were not being added
|
||||
}
|
||||
});
|
||||
final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap =
|
||||
new PrivacyGroupHeadBlockMap(
|
||||
privateStateStorage
|
||||
@@ -51,4 +121,17 @@ public class PrivacyBlockProcessor implements BlockProcessor {
|
||||
.commit();
|
||||
return blockProcessor.processBlock(blockchain, worldState, blockHeader, transactions, ommers);
|
||||
}
|
||||
|
||||
private List<PrivateTransactionWithMetadata> deserializeAddToGroupPayload(
|
||||
final Bytes encodedAddToGroupPayload) {
|
||||
final ArrayList<PrivateTransactionWithMetadata> deserializedResponse = new ArrayList<>();
|
||||
final BytesValueRLPInput bytesValueRLPInput =
|
||||
new BytesValueRLPInput(encodedAddToGroupPayload, false);
|
||||
final int noOfEntries = bytesValueRLPInput.enterList();
|
||||
for (int i = 0; i < noOfEntries; i++) {
|
||||
deserializedResponse.add(PrivateTransactionWithMetadata.readFrom(bytesValueRLPInput));
|
||||
}
|
||||
bytesValueRLPInput.leaveList();
|
||||
return deserializedResponse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.mainnet;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.TransactionFilter;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
@@ -26,4 +27,7 @@ public interface ProtocolSchedule<C> {
|
||||
Optional<BigInteger> getChainId();
|
||||
|
||||
void setTransactionFilter(TransactionFilter transactionFilter);
|
||||
|
||||
void setPublicWorldStateArchiveForPrivacyBlockProcessor(
|
||||
WorldStateArchive publicWorldStateArchive);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.vm.EVM;
|
||||
import org.hyperledger.besu.ethereum.vm.GasCalculator;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
/** A protocol specification. */
|
||||
public class ProtocolSpec<C> {
|
||||
@@ -288,4 +289,10 @@ public class ProtocolSpec<C> {
|
||||
public void setTransactionFilter(final TransactionFilter transactionFilter) {
|
||||
transactionValidator.setTransactionFilter(transactionFilter);
|
||||
}
|
||||
|
||||
public void setPublicWorldStateArchiveForPrivacyBlockProcessor(
|
||||
final WorldStateArchive publicWorldStateArchive) {
|
||||
if (PrivacyBlockProcessor.class.isAssignableFrom(blockProcessor.getClass()))
|
||||
((PrivacyBlockProcessor) blockProcessor).setPublicWorldStateArchive(publicWorldStateArchive);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.core.BlockImporter;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.OnChainPrivacyPrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.PrivacyPrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
|
||||
@@ -312,8 +313,17 @@ public class ProtocolSpecBuilder<T> {
|
||||
(PrivacyPrecompiledContract)
|
||||
precompileContractRegistry.get(address, Account.DEFAULT_VERSION);
|
||||
privacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor);
|
||||
final OnChainPrivacyPrecompiledContract onChainPrivacyPrecompiledContract =
|
||||
(OnChainPrivacyPrecompiledContract)
|
||||
precompileContractRegistry.get(Address.ONCHAIN_PRIVACY, Account.DEFAULT_VERSION);
|
||||
onChainPrivacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor);
|
||||
blockProcessor =
|
||||
new PrivacyBlockProcessor(blockProcessor, privacyParameters.getPrivateStateStorage());
|
||||
new PrivacyBlockProcessor(
|
||||
blockProcessor,
|
||||
protocolSchedule,
|
||||
privacyParameters.getEnclave(),
|
||||
privacyParameters.getPrivateStateStorage(),
|
||||
privacyParameters.getPrivateWorldStateArchive());
|
||||
}
|
||||
|
||||
final BlockValidator<T> blockValidator =
|
||||
|
||||
@@ -0,0 +1,463 @@
|
||||
/*
|
||||
* 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.mainnet.precompiles.privacy;
|
||||
|
||||
import static org.hyperledger.besu.crypto.Hash.keccak256;
|
||||
import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
import org.hyperledger.besu.enclave.Enclave;
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException;
|
||||
import org.hyperledger.besu.enclave.EnclaveIOException;
|
||||
import org.hyperledger.besu.enclave.EnclaveServerException;
|
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.DefaultEvmAccount;
|
||||
import org.hyperledger.besu.ethereum.core.Gas;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.MutableAccount;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.WorldUpdater;
|
||||
import org.hyperledger.besu.ethereum.debug.TraceOptions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.AbstractPrecompiledContract;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.privacy.Restriction;
|
||||
import org.hyperledger.besu.ethereum.privacy.VersionedPrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateBlockMetadata;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
|
||||
import org.hyperledger.besu.ethereum.vm.GasCalculator;
|
||||
import org.hyperledger.besu.ethereum.vm.MessageFrame;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
public class OnChainPrivacyPrecompiledContract extends AbstractPrecompiledContract {
|
||||
|
||||
// Dummy signature for transactions to not fail being processed.
|
||||
private static final SECP256K1.Signature FAKE_SIGNATURE =
|
||||
SECP256K1.Signature.create(SECP256K1.HALF_CURVE_ORDER, SECP256K1.HALF_CURVE_ORDER, (byte) 0);
|
||||
|
||||
private final Enclave enclave;
|
||||
private final WorldStateArchive privateWorldStateArchive;
|
||||
private final PrivateStateStorage privateStateStorage;
|
||||
private final PrivateStateRootResolver privateStateRootResolver;
|
||||
private PrivateTransactionProcessor privateTransactionProcessor;
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
public OnChainPrivacyPrecompiledContract(
|
||||
final GasCalculator gasCalculator, final PrivacyParameters privacyParameters) {
|
||||
this(
|
||||
gasCalculator,
|
||||
privacyParameters.getEnclave(),
|
||||
privacyParameters.getPrivateWorldStateArchive(),
|
||||
privacyParameters.getPrivateStateStorage());
|
||||
}
|
||||
|
||||
OnChainPrivacyPrecompiledContract(
|
||||
final GasCalculator gasCalculator,
|
||||
final Enclave enclave,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final PrivateStateStorage privateStateStorage) {
|
||||
super("OnChainPrivacy", gasCalculator);
|
||||
this.enclave = enclave;
|
||||
this.privateWorldStateArchive = worldStateArchive;
|
||||
this.privateStateStorage = privateStateStorage;
|
||||
this.privateStateRootResolver = new PrivateStateRootResolver(privateStateStorage);
|
||||
}
|
||||
|
||||
public void setPrivateTransactionProcessor(
|
||||
final PrivateTransactionProcessor privateTransactionProcessor) {
|
||||
this.privateTransactionProcessor = privateTransactionProcessor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Gas gasRequirement(final Bytes input) {
|
||||
return Gas.of(0L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
|
||||
final ProcessableBlockHeader currentBlockHeader = messageFrame.getBlockHeader();
|
||||
if (!BlockHeader.class.isAssignableFrom(currentBlockHeader.getClass())) {
|
||||
if (!messageFrame.isPersistingPrivateState()) {
|
||||
// We get in here from block mining.
|
||||
return Bytes.EMPTY;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"The MessageFrame contains an illegal block header type. Cannot persist private block metadata without current block hash.");
|
||||
}
|
||||
}
|
||||
final Hash currentBlockHash = ((BlockHeader) currentBlockHeader).getHash();
|
||||
|
||||
final String key = input.slice(0, 32).toBase64String();
|
||||
|
||||
final ReceiveResponse receiveResponse;
|
||||
try {
|
||||
receiveResponse = enclave.receive(key);
|
||||
} catch (final EnclaveClientException e) {
|
||||
LOG.debug("Can not fetch private transaction payload with key {}", key, e);
|
||||
return Bytes.EMPTY;
|
||||
} catch (final EnclaveServerException e) {
|
||||
LOG.error("Enclave is responding but errored perhaps it has a misconfiguration?", e);
|
||||
throw e;
|
||||
} catch (final EnclaveIOException e) {
|
||||
LOG.error("Can not communicate with enclave is it up?", e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
final BytesValueRLPInput bytesValueRLPInput =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.wrap(Base64.getDecoder().decode(receiveResponse.getPayload())), false);
|
||||
final VersionedPrivateTransaction versionedPrivateTransaction =
|
||||
VersionedPrivateTransaction.readFrom(bytesValueRLPInput);
|
||||
final PrivateTransaction privateTransaction =
|
||||
versionedPrivateTransaction.getPrivateTransaction();
|
||||
final Bytes32 version = versionedPrivateTransaction.getVersion();
|
||||
|
||||
final WorldUpdater publicWorldState = messageFrame.getWorldState();
|
||||
|
||||
final Optional<Bytes> maybeGroupId = privateTransaction.getPrivacyGroupId();
|
||||
if (maybeGroupId.isEmpty()) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
final Bytes32 privacyGroupId = Bytes32.wrap(maybeGroupId.get());
|
||||
|
||||
LOG.debug(
|
||||
"Processing private transaction {} in privacy group {}",
|
||||
privateTransaction.getHash(),
|
||||
privacyGroupId);
|
||||
|
||||
final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap =
|
||||
privateStateStorage.getPrivacyGroupHeadBlockMap(currentBlockHash).orElseThrow();
|
||||
|
||||
final Blockchain blockchain = messageFrame.getBlockchain();
|
||||
|
||||
final Hash lastRootHash =
|
||||
privateStateRootResolver.resolveLastStateRoot(privacyGroupId, currentBlockHash);
|
||||
|
||||
final MutableWorldState disposablePrivateState =
|
||||
privateWorldStateArchive.getMutable(lastRootHash).get();
|
||||
|
||||
final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater();
|
||||
|
||||
maybeInjectDefaultManagementAndProxy(
|
||||
lastRootHash, disposablePrivateState, privateWorldStateUpdater);
|
||||
|
||||
final boolean isAddingParticipant =
|
||||
privateTransaction
|
||||
.getPayload()
|
||||
.toHexString()
|
||||
.startsWith(OnChainGroupManagement.ADD_TO_GROUP_METHOD_SIGNATURE.toHexString());
|
||||
|
||||
final boolean isPrivacyGroupLocked =
|
||||
isContractLocked(
|
||||
messageFrame,
|
||||
currentBlockHeader,
|
||||
publicWorldState,
|
||||
privacyGroupId,
|
||||
blockchain,
|
||||
disposablePrivateState,
|
||||
privateWorldStateUpdater);
|
||||
|
||||
if (isAddingParticipant && !isPrivacyGroupLocked) {
|
||||
LOG.debug(
|
||||
"Privacy Group {} is not locked while trying to add to group with commitment {}",
|
||||
privacyGroupId.toHexString(),
|
||||
messageFrame.getTransactionHash());
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
if (!isAddingParticipant && isPrivacyGroupLocked) {
|
||||
LOG.debug(
|
||||
"Privacy Group {} is locked while trying to execute transaction with commitment {}",
|
||||
privacyGroupId.toHexString(),
|
||||
messageFrame.getTransactionHash());
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
if (!onChainPrivacyGroupVersionMatches(
|
||||
messageFrame,
|
||||
currentBlockHeader,
|
||||
version,
|
||||
publicWorldState,
|
||||
privacyGroupId,
|
||||
blockchain,
|
||||
disposablePrivateState,
|
||||
privateWorldStateUpdater)) return Bytes.EMPTY;
|
||||
|
||||
final PrivateTransactionProcessor.Result result =
|
||||
privateTransactionProcessor.processTransaction(
|
||||
blockchain,
|
||||
publicWorldState,
|
||||
privateWorldStateUpdater,
|
||||
currentBlockHeader,
|
||||
privateTransaction,
|
||||
messageFrame.getMiningBeneficiary(),
|
||||
new DebugOperationTracer(TraceOptions.DEFAULT),
|
||||
messageFrame.getBlockHashLookup(),
|
||||
privacyGroupId);
|
||||
|
||||
if (result.isInvalid() || !result.isSuccessful()) {
|
||||
LOG.error(
|
||||
"Failed to process private transaction {}: {}",
|
||||
privateTransaction.getHash(),
|
||||
result.getValidationResult().getErrorMessage());
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
if (messageFrame.isPersistingPrivateState()) {
|
||||
persistPrivateState(
|
||||
messageFrame.getTransactionHash(),
|
||||
currentBlockHash,
|
||||
privateTransaction,
|
||||
privacyGroupId,
|
||||
privacyGroupHeadBlockMap,
|
||||
disposablePrivateState,
|
||||
privateWorldStateUpdater,
|
||||
result);
|
||||
}
|
||||
|
||||
return result.getOutput();
|
||||
}
|
||||
|
||||
protected boolean isContractLocked(
|
||||
final MessageFrame messageFrame,
|
||||
final ProcessableBlockHeader currentBlockHeader,
|
||||
final WorldUpdater publicWorldState,
|
||||
final Bytes32 privacyGroupId,
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState disposablePrivateState,
|
||||
final WorldUpdater privateWorldStateUpdater) {
|
||||
final PrivateTransactionProcessor.Result result =
|
||||
checkCanExecute(
|
||||
messageFrame,
|
||||
currentBlockHeader,
|
||||
publicWorldState,
|
||||
privacyGroupId,
|
||||
blockchain,
|
||||
disposablePrivateState,
|
||||
privateWorldStateUpdater,
|
||||
OnChainGroupManagement.CAN_EXECUTE_METHOD_SIGNATURE);
|
||||
return result.getOutput().toHexString().endsWith("0");
|
||||
}
|
||||
|
||||
protected PrivateTransactionProcessor.Result checkCanExecute(
|
||||
final MessageFrame messageFrame,
|
||||
final ProcessableBlockHeader currentBlockHeader,
|
||||
final WorldUpdater publicWorldState,
|
||||
final Bytes32 privacyGroupId,
|
||||
final Blockchain currentBlockchain,
|
||||
final MutableWorldState disposablePrivateState,
|
||||
final WorldUpdater privateWorldStateUpdater,
|
||||
final Bytes canExecuteMethodSignature) {
|
||||
// We need the "lock status" of the group for every single transaction but we don't want this
|
||||
// call to affect the state
|
||||
// privateTransactionProcessor.processTransaction(...) commits the state if the process was
|
||||
// successful before it returns
|
||||
final MutableWorldState canExecutePrivateState =
|
||||
privateWorldStateArchive.getMutable(disposablePrivateState.rootHash()).get();
|
||||
final WorldUpdater canExecuteUpdater = canExecutePrivateState.updater();
|
||||
|
||||
return privateTransactionProcessor.processTransaction(
|
||||
currentBlockchain,
|
||||
publicWorldState,
|
||||
canExecuteUpdater,
|
||||
currentBlockHeader,
|
||||
buildSimulationTransaction(
|
||||
privacyGroupId, privateWorldStateUpdater, canExecuteMethodSignature),
|
||||
messageFrame.getMiningBeneficiary(),
|
||||
new DebugOperationTracer(TraceOptions.DEFAULT),
|
||||
messageFrame.getBlockHashLookup(),
|
||||
privacyGroupId);
|
||||
}
|
||||
|
||||
protected void maybeInjectDefaultManagementAndProxy(
|
||||
final Hash lastRootHash,
|
||||
final MutableWorldState disposablePrivateState,
|
||||
final WorldUpdater privateWorldStateUpdater) {
|
||||
if (lastRootHash.equals(EMPTY_ROOT_HASH)) {
|
||||
// inject management
|
||||
final DefaultEvmAccount managementPrecompile =
|
||||
privateWorldStateUpdater.createAccount(Address.DEFAULT_PRIVACY_MANAGEMENT);
|
||||
final MutableAccount mutableManagementPrecompiled = managementPrecompile.getMutable();
|
||||
// this is the code for the simple management contract
|
||||
mutableManagementPrecompiled.setCode(OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_CODE);
|
||||
|
||||
// inject proxy
|
||||
final DefaultEvmAccount proxyPrecompile =
|
||||
privateWorldStateUpdater.createAccount(Address.PRIVACY_PROXY);
|
||||
final MutableAccount mutableProxyPrecompiled = proxyPrecompile.getMutable();
|
||||
// this is the code for the proxy contract
|
||||
mutableProxyPrecompiled.setCode(OnChainGroupManagement.DEFAULT_PROXY_PRECOMPILED_CODE);
|
||||
// manually set the management contract address so the proxy can trust it
|
||||
mutableProxyPrecompiled.setStorageValue(
|
||||
UInt256.ZERO, UInt256.fromBytes(Bytes32.leftPad(Address.DEFAULT_PRIVACY_MANAGEMENT)));
|
||||
|
||||
privateWorldStateUpdater.commit();
|
||||
disposablePrivateState.persist();
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean onChainPrivacyGroupVersionMatches(
|
||||
final MessageFrame messageFrame,
|
||||
final ProcessableBlockHeader currentBlockHeader,
|
||||
final Bytes32 version,
|
||||
final WorldUpdater publicWorldState,
|
||||
final Bytes32 privacyGroupId,
|
||||
final Blockchain currentBlockchain,
|
||||
final MutableWorldState disposablePrivateState,
|
||||
final WorldUpdater privateWorldStateUpdater) {
|
||||
// We need the "version" of the group for every single transaction but we don't want this
|
||||
// call to affect the state
|
||||
// privateTransactionProcessor.processTransaction(...) commits the state if the process was
|
||||
// successful before it returns
|
||||
final PrivateTransactionProcessor.Result getVersionResult =
|
||||
checkCanExecute(
|
||||
messageFrame,
|
||||
currentBlockHeader,
|
||||
publicWorldState,
|
||||
privacyGroupId,
|
||||
currentBlockchain,
|
||||
disposablePrivateState,
|
||||
privateWorldStateUpdater,
|
||||
OnChainGroupManagement.GET_VERSION_METHOD_SIGNATURE);
|
||||
|
||||
if (version.equals(getVersionResult.getOutput())) {
|
||||
return true;
|
||||
}
|
||||
LOG.debug(
|
||||
"Privacy Group {} version mismatch for commitment {}: expecting {} but got {}",
|
||||
privacyGroupId.toBase64String(),
|
||||
messageFrame.getTransactionHash(),
|
||||
getVersionResult.getOutput(),
|
||||
version);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void persistPrivateState(
|
||||
final Hash commitmentHash,
|
||||
final Hash currentBlockHash,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Bytes32 privacyGroupId,
|
||||
final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap,
|
||||
final MutableWorldState disposablePrivateState,
|
||||
final WorldUpdater privateWorldStateUpdater,
|
||||
final PrivateTransactionProcessor.Result result) {
|
||||
|
||||
LOG.trace(
|
||||
"Persisting private state {} for privacyGroup {}",
|
||||
disposablePrivateState.rootHash(),
|
||||
privacyGroupId);
|
||||
privateWorldStateUpdater.commit();
|
||||
disposablePrivateState.persist();
|
||||
|
||||
final PrivateStateStorage.Updater privateStateUpdater = privateStateStorage.updater();
|
||||
|
||||
updatePrivateBlockMetadata(
|
||||
commitmentHash,
|
||||
currentBlockHash,
|
||||
privacyGroupId,
|
||||
disposablePrivateState.rootHash(),
|
||||
privateStateUpdater);
|
||||
|
||||
final Bytes32 txHash = keccak256(RLP.encode(privateTransaction::writeTo));
|
||||
|
||||
final int txStatus =
|
||||
result.getStatus() == PrivateTransactionProcessor.Result.Status.SUCCESSFUL ? 1 : 0;
|
||||
|
||||
final PrivateTransactionReceipt privateTransactionReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
txStatus, result.getLogs(), result.getOutput(), result.getRevertReason());
|
||||
|
||||
privateStateUpdater.putTransactionReceipt(currentBlockHash, txHash, privateTransactionReceipt);
|
||||
|
||||
if (!privacyGroupHeadBlockMap.contains(Bytes32.wrap(privacyGroupId), currentBlockHash)) {
|
||||
privacyGroupHeadBlockMap.put(Bytes32.wrap(privacyGroupId), currentBlockHash);
|
||||
privateStateUpdater.putPrivacyGroupHeadBlockMap(
|
||||
currentBlockHash, new PrivacyGroupHeadBlockMap(privacyGroupHeadBlockMap));
|
||||
}
|
||||
|
||||
if (privateTransaction
|
||||
.getPayload()
|
||||
.toHexString()
|
||||
.startsWith(OnChainGroupManagement.REMOVE_PARTICIPANT_METHOD_SIGNATURE.toHexString())) {
|
||||
privacyGroupHeadBlockMap.remove(Bytes32.wrap(privacyGroupId));
|
||||
privateStateUpdater.putPrivacyGroupHeadBlockMap(
|
||||
currentBlockHash, new PrivacyGroupHeadBlockMap(privacyGroupHeadBlockMap));
|
||||
}
|
||||
privateStateUpdater.commit();
|
||||
}
|
||||
|
||||
private PrivateTransaction buildSimulationTransaction(
|
||||
final Bytes privacyGroupId,
|
||||
final WorldUpdater privateWorldStateUpdater,
|
||||
final Bytes payload) {
|
||||
return PrivateTransaction.builder()
|
||||
.privateFrom(Bytes.EMPTY)
|
||||
.privacyGroupId(privacyGroupId)
|
||||
.restriction(Restriction.RESTRICTED)
|
||||
.nonce(
|
||||
privateWorldStateUpdater.getAccount(Address.ZERO) != null
|
||||
? privateWorldStateUpdater.getAccount(Address.ZERO).getNonce()
|
||||
: 0)
|
||||
.gasPrice(Wei.of(1000))
|
||||
.gasLimit(3000000)
|
||||
.to(Address.PRIVACY_PROXY)
|
||||
.sender(Address.ZERO)
|
||||
.value(Wei.ZERO)
|
||||
.payload(payload)
|
||||
.signature(FAKE_SIGNATURE)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void updatePrivateBlockMetadata(
|
||||
final Hash markerTransactionHash,
|
||||
final Hash currentBlockHash,
|
||||
final Bytes32 privacyGroupId,
|
||||
final Hash rootHash,
|
||||
final PrivateStateStorage.Updater privateStateUpdater) {
|
||||
final PrivateBlockMetadata privateBlockMetadata =
|
||||
privateStateStorage
|
||||
.getPrivateBlockMetadata(currentBlockHash, Bytes32.wrap(privacyGroupId))
|
||||
.orElseGet(PrivateBlockMetadata::empty);
|
||||
privateBlockMetadata.addPrivateTransactionMetadata(
|
||||
new PrivateTransactionMetadata(markerTransactionHash, rootHash));
|
||||
privateStateUpdater.putPrivateBlockMetadata(
|
||||
Bytes32.wrap(currentBlockHash), Bytes32.wrap(privacyGroupId), privateBlockMetadata);
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,8 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
|
||||
final BytesValueRLPInput bytesValueRLPInput =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.wrap(Base64.getDecoder().decode(receiveResponse.getPayload())), false);
|
||||
final PrivateTransaction privateTransaction = PrivateTransaction.readFrom(bytesValueRLPInput);
|
||||
final PrivateTransaction privateTransaction =
|
||||
PrivateTransaction.readFrom(bytesValueRLPInput.readAsRlp());
|
||||
final WorldUpdater publicWorldState = messageFrame.getWorldState();
|
||||
final Bytes32 privacyGroupId =
|
||||
Bytes32.wrap(Bytes.fromBase64String(receiveResponse.getPrivacyGroupId()));
|
||||
@@ -148,6 +149,7 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
|
||||
privateWorldStateArchive.getMutable(lastRootHash).get();
|
||||
|
||||
final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater();
|
||||
|
||||
final PrivateTransactionProcessor.Result result =
|
||||
privateTransactionProcessor.processTransaction(
|
||||
currentBlockchain,
|
||||
@@ -197,8 +199,6 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
|
||||
privateStateUpdater.putTransactionReceipt(
|
||||
currentBlockHash, txHash, privateTransactionReceipt);
|
||||
|
||||
// TODO: this map could be passed through from @PrivacyBlockProcessor and saved once at the
|
||||
// end of block processing
|
||||
if (!privacyGroupHeadBlockMap.contains(Bytes32.wrap(privacyGroupId), currentBlockHash)) {
|
||||
privacyGroupHeadBlockMap.put(Bytes32.wrap(privacyGroupId), currentBlockHash);
|
||||
privateStateUpdater.putPrivacyGroupHeadBlockMap(
|
||||
|
||||
@@ -14,22 +14,39 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.privacy;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.ADD_TO_GROUP_METHOD_SIGNATURE;
|
||||
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.GET_PARTICIPANTS_METHOD_SIGNATURE;
|
||||
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.GET_VERSION_METHOD_SIGNATURE;
|
||||
|
||||
import org.hyperledger.besu.enclave.Enclave;
|
||||
import org.hyperledger.besu.enclave.EnclaveClientException;
|
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup;
|
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup.Type;
|
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse;
|
||||
import org.hyperledger.besu.enclave.types.SendResponse;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -40,11 +57,14 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
public class DefaultPrivacyController implements PrivacyController {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
private final Blockchain blockchain;
|
||||
private final PrivateStateStorage privateStateStorage;
|
||||
private final Enclave enclave;
|
||||
private final PrivateTransactionValidator privateTransactionValidator;
|
||||
private final PrivateMarkerTransactionFactory privateMarkerTransactionFactory;
|
||||
@@ -53,6 +73,7 @@ public class DefaultPrivacyController implements PrivacyController {
|
||||
private final PrivateWorldStateReader privateWorldStateReader;
|
||||
|
||||
public DefaultPrivacyController(
|
||||
final Blockchain blockchain,
|
||||
final PrivacyParameters privacyParameters,
|
||||
final Optional<BigInteger> chainId,
|
||||
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory,
|
||||
@@ -60,6 +81,8 @@ public class DefaultPrivacyController implements PrivacyController {
|
||||
final PrivateNonceProvider privateNonceProvider,
|
||||
final PrivateWorldStateReader privateWorldStateReader) {
|
||||
this(
|
||||
blockchain,
|
||||
privacyParameters.getPrivateStateStorage(),
|
||||
privacyParameters.getEnclave(),
|
||||
new PrivateTransactionValidator(chainId),
|
||||
privateMarkerTransactionFactory,
|
||||
@@ -69,12 +92,16 @@ public class DefaultPrivacyController implements PrivacyController {
|
||||
}
|
||||
|
||||
public DefaultPrivacyController(
|
||||
final Blockchain blockchain,
|
||||
final PrivateStateStorage privateStateStorage,
|
||||
final Enclave enclave,
|
||||
final PrivateTransactionValidator privateTransactionValidator,
|
||||
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory,
|
||||
final PrivateTransactionSimulator privateTransactionSimulator,
|
||||
final PrivateNonceProvider privateNonceProvider,
|
||||
final PrivateWorldStateReader privateWorldStateReader) {
|
||||
this.blockchain = blockchain;
|
||||
this.privateStateStorage = privateStateStorage;
|
||||
this.enclave = enclave;
|
||||
this.privateTransactionValidator = privateTransactionValidator;
|
||||
this.privateMarkerTransactionFactory = privateMarkerTransactionFactory;
|
||||
@@ -128,6 +155,15 @@ public class DefaultPrivacyController implements PrivacyController {
|
||||
return privateMarkerTransactionFactory.create(transactionEnclaveKey, privateTransaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction createPrivacyMarkerTransaction(
|
||||
final String transactionEnclaveKey,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Address privacyPrecompileAddress) {
|
||||
return privateMarkerTransactionFactory.create(
|
||||
transactionEnclaveKey, privateTransaction, privacyPrecompileAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult<TransactionValidator.TransactionInvalidReason> validatePrivateTransaction(
|
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) {
|
||||
@@ -185,6 +221,205 @@ public class DefaultPrivacyController implements PrivacyController {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> buildAndSendAddPayload(
|
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) {
|
||||
if (isGroupAdditionTransaction(privateTransaction)) {
|
||||
final List<PrivateTransactionMetadata> privateTransactionMetadataList =
|
||||
buildTransactionMetadataList(Bytes32.wrap(privateTransaction.getPrivacyGroupId().get()));
|
||||
if (privateTransactionMetadataList.size() > 0) {
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList =
|
||||
retrievePrivateTransactions(
|
||||
Bytes32.wrap(privateTransaction.getPrivacyGroupId().get()),
|
||||
privateTransactionMetadataList,
|
||||
enclavePublicKey);
|
||||
final Bytes bytes = serializeAddToGroupPayload(privateTransactionWithMetadataList);
|
||||
final List<String> privateFor =
|
||||
getParticipantsFromParameter(privateTransaction.getPayload());
|
||||
return Optional.of(
|
||||
enclave.send(bytes.toBase64String(), enclavePublicKey, privateFor).getKey());
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivacyGroup retrievePrivacyGroup(final String privacyGroupId, final String enclaveKey) {
|
||||
return enclave.retrievePrivacyGroup(privacyGroupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrivacyGroup> findOnChainPrivacyGroup(
|
||||
final List<String> addresses, final String enclavePublicKey) {
|
||||
final ArrayList<PrivacyGroup> privacyGroups = new ArrayList<>();
|
||||
final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap =
|
||||
privateStateStorage
|
||||
.getPrivacyGroupHeadBlockMap(blockchain.getChainHeadHash())
|
||||
.orElse(PrivacyGroupHeadBlockMap.EMPTY);
|
||||
privacyGroupHeadBlockMap
|
||||
.keySet()
|
||||
.forEach(
|
||||
c -> {
|
||||
final List<String> participants = getExistingParticipants(c, enclavePublicKey);
|
||||
if (participants.containsAll(addresses)) {
|
||||
privacyGroups.add(
|
||||
new PrivacyGroup(c.toBase64String(), Type.ONCHAIN, "", "", participants));
|
||||
}
|
||||
});
|
||||
return privacyGroups;
|
||||
}
|
||||
|
||||
private List<String> getExistingParticipants(
|
||||
final Bytes privacyGroupId, final String enclavePublicKey) {
|
||||
// get the privateFor list from the management contract
|
||||
final Optional<PrivateTransactionProcessor.Result> privateTransactionSimulatorResultOptional =
|
||||
privateTransactionSimulator.process(
|
||||
privacyGroupId.toBase64String(),
|
||||
buildCallParams(
|
||||
Bytes.fromBase64String(enclavePublicKey), GET_PARTICIPANTS_METHOD_SIGNATURE));
|
||||
|
||||
if (privateTransactionSimulatorResultOptional.isPresent()
|
||||
&& privateTransactionSimulatorResultOptional.get().isSuccessful()) {
|
||||
final RLPInput rlpInput =
|
||||
RLP.input(privateTransactionSimulatorResultOptional.get().getOutput());
|
||||
if (rlpInput.nextSize() > 0) {
|
||||
return decodeList(rlpInput.raw());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
} else {
|
||||
// if the management contract does not exist this will prompt
|
||||
// Orion to resolve the privateFor
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> decodeList(final Bytes rlpEncodedList) {
|
||||
final ArrayList<String> decodedElements = new ArrayList<>();
|
||||
// first 32 bytes is dynamic list offset
|
||||
final UInt256 lengthOfList = UInt256.fromBytes(rlpEncodedList.slice(32, 32)); // length of list
|
||||
for (int i = 0; i < lengthOfList.toLong(); ++i) {
|
||||
decodedElements.add(
|
||||
Bytes.wrap(rlpEncodedList.slice(64 + (32 * i), 32)).toBase64String()); // participant
|
||||
}
|
||||
return decodedElements;
|
||||
}
|
||||
|
||||
private List<String> getParticipantsFromParameter(final Bytes input) {
|
||||
final List<String> participants = new ArrayList<>();
|
||||
final Bytes mungedParticipants = input.slice(4 + 32 + 32 + 32);
|
||||
for (int i = 0; i <= mungedParticipants.size() - 32; i += 32) {
|
||||
participants.add(mungedParticipants.slice(i, 32).toBase64String());
|
||||
}
|
||||
return participants;
|
||||
}
|
||||
|
||||
private CallParameter buildCallParams(final Bytes enclavePublicKey, final Bytes methodCall) {
|
||||
return new CallParameter(
|
||||
Address.ZERO,
|
||||
Address.PRIVACY_PROXY,
|
||||
3000000,
|
||||
Wei.of(1000),
|
||||
Wei.ZERO,
|
||||
Bytes.concatenate(methodCall, enclavePublicKey));
|
||||
}
|
||||
|
||||
private List<PrivateTransactionMetadata> buildTransactionMetadataList(
|
||||
final Bytes32 privacyGroupId) {
|
||||
final List<PrivateTransactionMetadata> pmtHashes = new ArrayList<>();
|
||||
PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap =
|
||||
privateStateStorage
|
||||
.getPrivacyGroupHeadBlockMap(blockchain.getChainHeadHash())
|
||||
.orElse(PrivacyGroupHeadBlockMap.EMPTY);
|
||||
if (privacyGroupHeadBlockMap.get(privacyGroupId) != null) {
|
||||
Hash blockHash = privacyGroupHeadBlockMap.get(privacyGroupId);
|
||||
while (blockHash != null) {
|
||||
pmtHashes.addAll(
|
||||
0,
|
||||
privateStateStorage
|
||||
.getPrivateBlockMetadata(blockHash, privacyGroupId)
|
||||
.get()
|
||||
.getPrivateTransactionMetadataList());
|
||||
blockHash = blockchain.getBlockHeader(blockHash).get().getParentHash();
|
||||
privacyGroupHeadBlockMap =
|
||||
privateStateStorage
|
||||
.getPrivacyGroupHeadBlockMap(blockHash)
|
||||
.orElse(PrivacyGroupHeadBlockMap.EMPTY);
|
||||
if (privacyGroupHeadBlockMap.get(privacyGroupId) != null) {
|
||||
blockHash = privacyGroupHeadBlockMap.get(privacyGroupId);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pmtHashes;
|
||||
}
|
||||
|
||||
private List<PrivateTransactionWithMetadata> retrievePrivateTransactions(
|
||||
final Bytes32 privacyGroupId,
|
||||
final List<PrivateTransactionMetadata> privateTransactionMetadataList,
|
||||
final String enclavePublicKey) {
|
||||
final ArrayList<PrivateTransactionWithMetadata> privateTransactions = new ArrayList<>();
|
||||
privateStateStorage
|
||||
.getAddDataKey(privacyGroupId)
|
||||
.ifPresent(key -> privateTransactions.addAll(retrieveAddBlob(key.toBase64String())));
|
||||
for (int i = privateTransactions.size(); i < privateTransactionMetadataList.size(); i++) {
|
||||
final PrivateTransactionMetadata privateTransactionMetadata =
|
||||
privateTransactionMetadataList.get(i);
|
||||
final Transaction privateMarkerTransaction =
|
||||
blockchain
|
||||
.getTransactionByHash(privateTransactionMetadata.getPrivacyMarkerTransactionHash())
|
||||
.orElseThrow();
|
||||
final ReceiveResponse receiveResponse =
|
||||
retrieveTransaction(
|
||||
privateMarkerTransaction.getPayload().slice(0, 32).toBase64String(),
|
||||
enclavePublicKey);
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromBase64String(new String(receiveResponse.getPayload(), UTF_8)), false);
|
||||
input.enterList();
|
||||
privateTransactions.add(
|
||||
new PrivateTransactionWithMetadata(
|
||||
PrivateTransaction.readFrom(input), privateTransactionMetadata));
|
||||
input.leaveListLenient();
|
||||
}
|
||||
|
||||
return privateTransactions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrivateTransactionWithMetadata> retrieveAddBlob(final String addDataKey) {
|
||||
final ReceiveResponse addReceiveResponse = enclave.receive(addDataKey);
|
||||
return deserializeAddToGroupPayload(
|
||||
Bytes.wrap(Base64.getDecoder().decode(addReceiveResponse.getPayload())));
|
||||
}
|
||||
|
||||
private Bytes serializeAddToGroupPayload(
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList) {
|
||||
|
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
|
||||
rlpOutput.startList();
|
||||
privateTransactionWithMetadataList.forEach(
|
||||
privateTransactionWithMetadata -> privateTransactionWithMetadata.writeTo(rlpOutput));
|
||||
rlpOutput.endList();
|
||||
|
||||
return rlpOutput.encoded();
|
||||
}
|
||||
|
||||
private List<PrivateTransactionWithMetadata> deserializeAddToGroupPayload(
|
||||
final Bytes encodedAddToGroupPayload) {
|
||||
final ArrayList<PrivateTransactionWithMetadata> deserializedResponse = new ArrayList<>();
|
||||
final BytesValueRLPInput bytesValueRLPInput =
|
||||
new BytesValueRLPInput(encodedAddToGroupPayload, false);
|
||||
final int noOfEntries = bytesValueRLPInput.enterList();
|
||||
for (int i = 0; i < noOfEntries; i++) {
|
||||
deserializedResponse.add(PrivateTransactionWithMetadata.readFrom(bytesValueRLPInput));
|
||||
}
|
||||
bytesValueRLPInput.leaveList();
|
||||
return deserializedResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Bytes> getContractCode(
|
||||
final String privacyGroupId,
|
||||
@@ -197,23 +432,75 @@ public class DefaultPrivacyController implements PrivacyController {
|
||||
private SendResponse sendRequest(
|
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) {
|
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
|
||||
|
||||
final List<String> privateFor = resolvePrivateFor(privateTransaction, enclavePublicKey);
|
||||
|
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) {
|
||||
PrivacyGroup privacyGroup;
|
||||
try {
|
||||
privacyGroup =
|
||||
enclave.retrievePrivacyGroup(
|
||||
privateTransaction.getPrivacyGroupId().get().toBase64String());
|
||||
} catch (final EnclaveClientException e) {
|
||||
// onchain privacy group
|
||||
final Optional<PrivateTransactionProcessor.Result> result =
|
||||
privateTransactionSimulator.process(
|
||||
privateTransaction.getPrivacyGroupId().get().toBase64String(),
|
||||
buildCallParams(
|
||||
Bytes.fromBase64String(enclavePublicKey), GET_VERSION_METHOD_SIGNATURE));
|
||||
new VersionedPrivateTransaction(privateTransaction, result).writeTo(rlpOutput);
|
||||
if (privateFor.isEmpty()) {
|
||||
privateFor.add(privateTransaction.getPrivateFrom().toBase64String());
|
||||
}
|
||||
return enclave.send(
|
||||
rlpOutput.encoded().toBase64String(),
|
||||
privateTransaction.getPrivateFrom().toBase64String(),
|
||||
privateFor);
|
||||
}
|
||||
if (privacyGroup != null) {
|
||||
privateTransaction.writeTo(rlpOutput);
|
||||
return enclave.send(
|
||||
rlpOutput.encoded().toBase64String(),
|
||||
privateTransaction.getPrivateFrom().toBase64String(),
|
||||
privateTransaction.getPrivacyGroupId().get().toBase64String());
|
||||
}
|
||||
}
|
||||
if (privateFor.isEmpty()) {
|
||||
privateFor.add(privateTransaction.getPrivateFrom().toBase64String());
|
||||
}
|
||||
|
||||
privateTransaction.writeTo(rlpOutput);
|
||||
final String payload = rlpOutput.encoded().toBase64String();
|
||||
|
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) {
|
||||
return enclave.send(
|
||||
payload, enclavePublicKey, privateTransaction.getPrivacyGroupId().get().toBase64String());
|
||||
} else {
|
||||
final List<String> privateFor =
|
||||
return enclave.send(payload, privateTransaction.getPrivateFrom().toBase64String(), privateFor);
|
||||
}
|
||||
|
||||
private List<String> resolvePrivateFor(
|
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) {
|
||||
final ArrayList<String> privateFor = new ArrayList<>();
|
||||
final boolean isLegacyTransaction = privateTransaction.getPrivateFor().isPresent();
|
||||
if (isLegacyTransaction) {
|
||||
privateFor.addAll(
|
||||
privateTransaction.getPrivateFor().get().stream()
|
||||
.map(Bytes::toBase64String)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (privateFor.isEmpty()) {
|
||||
privateFor.add(privateTransaction.getPrivateFrom().toBase64String());
|
||||
}
|
||||
return enclave.send(
|
||||
payload, privateTransaction.getPrivateFrom().toBase64String(), privateFor);
|
||||
.collect(Collectors.toList()));
|
||||
} else if (isGroupAdditionTransaction(privateTransaction)) {
|
||||
privateFor.addAll(getParticipantsFromParameter(privateTransaction.getPayload()));
|
||||
privateFor.addAll(
|
||||
getExistingParticipants(privateTransaction.getPrivacyGroupId().get(), enclavePublicKey));
|
||||
} else {
|
||||
privateFor.addAll(
|
||||
getExistingParticipants(privateTransaction.getPrivacyGroupId().get(), enclavePublicKey));
|
||||
}
|
||||
return privateFor;
|
||||
}
|
||||
|
||||
private boolean isGroupAdditionTransaction(final PrivateTransaction privateTransaction) {
|
||||
return privateTransaction.getTo().isPresent()
|
||||
&& privateTransaction.getTo().get().equals(Address.PRIVACY_PROXY)
|
||||
&& privateTransaction
|
||||
.getPayload()
|
||||
.toHexString()
|
||||
.startsWith(ADD_TO_GROUP_METHOD_SIGNATURE.toHexString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInv
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -83,7 +84,11 @@ public class MultiTenancyPrivacyController implements PrivacyController {
|
||||
throw new MultiTenancyValidationException(
|
||||
"Privacy group addresses must contain the enclave public key");
|
||||
}
|
||||
return privacyController.findPrivacyGroup(addresses, enclavePublicKey);
|
||||
PrivacyGroup[] resultantGroups =
|
||||
privacyController.findPrivacyGroup(addresses, enclavePublicKey);
|
||||
return Arrays.stream(resultantGroups)
|
||||
.filter(g -> g.getMembers().contains(enclavePublicKey))
|
||||
.toArray(PrivacyGroup[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,6 +98,15 @@ public class MultiTenancyPrivacyController implements PrivacyController {
|
||||
transactionEnclaveKey, privateTransaction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transaction createPrivacyMarkerTransaction(
|
||||
final String transactionEnclaveKey,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Address privacyPrecompileAddress) {
|
||||
return privacyController.createPrivacyMarkerTransaction(
|
||||
transactionEnclaveKey, privateTransaction, privacyPrecompileAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValidationResult<TransactionInvalidReason> validatePrivateTransaction(
|
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) {
|
||||
@@ -127,6 +141,39 @@ public class MultiTenancyPrivacyController implements PrivacyController {
|
||||
privacyGroupId, enclavePublicKey, callParams, blockNumber);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> buildAndSendAddPayload(
|
||||
final PrivateTransaction privateTransaction, final String enclaveKey) {
|
||||
verifyPrivateFromMatchesEnclavePublicKey(
|
||||
privateTransaction.getPrivateFrom().toBase64String(), enclaveKey);
|
||||
verifyPrivacyGroupContainsEnclavePublicKey(
|
||||
privateTransaction.getPrivacyGroupId().get().toBase64String(), enclaveKey);
|
||||
return privacyController.buildAndSendAddPayload(privateTransaction, enclaveKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivacyGroup retrievePrivacyGroup(
|
||||
final String privacyGroupId, final String enclavePublicKey) {
|
||||
final PrivacyGroup privacyGroup =
|
||||
privacyController.retrievePrivacyGroup(privacyGroupId, enclavePublicKey);
|
||||
if (!privacyGroup.getMembers().contains(enclavePublicKey)) {
|
||||
throw new MultiTenancyValidationException(
|
||||
"Privacy group must contain the enclave public key");
|
||||
}
|
||||
return privacyGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrivacyGroup> findOnChainPrivacyGroup(
|
||||
final List<String> asList, final String enclaveKey) {
|
||||
return privacyController.findOnChainPrivacyGroup(asList, enclaveKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PrivateTransactionWithMetadata> retrieveAddBlob(final String addDataKey) {
|
||||
return privacyController.retrieveAddBlob(addDataKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Bytes> getContractCode(
|
||||
final String privacyGroupId,
|
||||
|
||||
@@ -44,6 +44,11 @@ public interface PrivacyController {
|
||||
Transaction createPrivacyMarkerTransaction(
|
||||
String transactionEnclaveKey, PrivateTransaction privateTransaction);
|
||||
|
||||
Transaction createPrivacyMarkerTransaction(
|
||||
String transactionEnclaveKey,
|
||||
PrivateTransaction privateTransaction,
|
||||
Address privacyPrecompileAddress);
|
||||
|
||||
ValidationResult<TransactionInvalidReason> validatePrivateTransaction(
|
||||
PrivateTransaction privateTransaction, String enclavePublicKey);
|
||||
|
||||
@@ -58,9 +63,17 @@ public interface PrivacyController {
|
||||
final CallParameter callParams,
|
||||
final long blockNumber);
|
||||
|
||||
Optional<String> buildAndSendAddPayload(PrivateTransaction privateTransaction, String enclaveKey);
|
||||
|
||||
PrivacyGroup retrievePrivacyGroup(String toBase64String, String enclaveKey);
|
||||
|
||||
List<PrivacyGroup> findOnChainPrivacyGroup(List<String> asList, String enclaveKey);
|
||||
|
||||
Optional<Bytes> getContractCode(
|
||||
final String privacyGroupId,
|
||||
final Address contractAddress,
|
||||
final Hash blockHash,
|
||||
final String enclavePublicKey);
|
||||
|
||||
List<PrivateTransactionWithMetadata> retrieveAddBlob(String addDataKey);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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.privacy;
|
||||
|
||||
import static org.hyperledger.besu.crypto.Hash.keccak256;
|
||||
import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.DefaultEvmAccount;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.MutableAccount;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.WorldUpdater;
|
||||
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
|
||||
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateBlockMetadata;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
|
||||
import org.hyperledger.besu.ethereum.vm.OperationTracer;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
public class PrivateGroupRehydrationBlockProcessor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
static final int MAX_GENERATION = 6;
|
||||
|
||||
private final TransactionProcessor transactionProcessor;
|
||||
private final PrivateTransactionProcessor privateTransactionProcessor;
|
||||
private final MainnetBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
|
||||
final Wei blockReward;
|
||||
private final boolean skipZeroBlockRewards;
|
||||
private final MiningBeneficiaryCalculator miningBeneficiaryCalculator;
|
||||
|
||||
public PrivateGroupRehydrationBlockProcessor(
|
||||
final TransactionProcessor transactionProcessor,
|
||||
final PrivateTransactionProcessor privateTransactionProcessor,
|
||||
final MainnetBlockProcessor.TransactionReceiptFactory transactionReceiptFactory,
|
||||
final Wei blockReward,
|
||||
final MiningBeneficiaryCalculator miningBeneficiaryCalculator,
|
||||
final boolean skipZeroBlockRewards) {
|
||||
this.transactionProcessor = transactionProcessor;
|
||||
this.privateTransactionProcessor = privateTransactionProcessor;
|
||||
this.transactionReceiptFactory = transactionReceiptFactory;
|
||||
this.blockReward = blockReward;
|
||||
this.miningBeneficiaryCalculator = miningBeneficiaryCalculator;
|
||||
this.skipZeroBlockRewards = skipZeroBlockRewards;
|
||||
}
|
||||
|
||||
public AbstractBlockProcessor.Result processBlock(
|
||||
final Blockchain blockchain,
|
||||
final MutableWorldState worldState,
|
||||
final WorldStateArchive privateWorldStateArchive,
|
||||
final PrivateStateStorage privateStateStorage,
|
||||
final Block block,
|
||||
final Map<Hash, PrivateTransaction> forExecution,
|
||||
final List<BlockHeader> ommers) {
|
||||
long gasUsed = 0;
|
||||
final List<TransactionReceipt> receipts = new ArrayList<>();
|
||||
|
||||
final List<Transaction> transactions = block.getBody().getTransactions();
|
||||
final BlockHeader blockHeader = block.getHeader();
|
||||
for (final Transaction transaction : transactions) {
|
||||
|
||||
final long remainingGasBudget = blockHeader.getGasLimit() - gasUsed;
|
||||
if (Long.compareUnsigned(transaction.getGasLimit(), remainingGasBudget) > 0) {
|
||||
LOG.warn(
|
||||
"Transaction processing error: transaction gas limit {} exceeds available block budget remaining {}",
|
||||
transaction.getGasLimit(),
|
||||
remainingGasBudget);
|
||||
return AbstractBlockProcessor.Result.failed();
|
||||
}
|
||||
|
||||
final WorldUpdater worldStateUpdater = worldState.updater();
|
||||
final BlockHashLookup blockHashLookup = new BlockHashLookup(blockHeader, blockchain);
|
||||
final Address miningBeneficiary =
|
||||
miningBeneficiaryCalculator.calculateBeneficiary(blockHeader);
|
||||
|
||||
final PrivateStateRootResolver privateStateRootResolver =
|
||||
new PrivateStateRootResolver(privateStateStorage);
|
||||
if (forExecution.containsKey(transaction.getHash())) {
|
||||
final PrivateTransaction privateTransaction = forExecution.get(transaction.getHash());
|
||||
final Hash lastRootHash =
|
||||
privateStateRootResolver.resolveLastStateRoot(
|
||||
Bytes32.wrap(privateTransaction.getPrivacyGroupId().get()),
|
||||
blockHeader.getParentHash());
|
||||
|
||||
final MutableWorldState disposablePrivateState =
|
||||
privateWorldStateArchive.getMutable(lastRootHash).get();
|
||||
final WorldUpdater privateStateUpdater = disposablePrivateState.updater();
|
||||
maybeInjectDefaultManagementAndProxy(
|
||||
lastRootHash, disposablePrivateState, privateStateUpdater);
|
||||
LOG.debug(
|
||||
"Pre-rehydrate root hash: {} for tx {}",
|
||||
disposablePrivateState.rootHash(),
|
||||
privateTransaction.getHash());
|
||||
|
||||
final PrivateTransactionProcessor.Result privateResult =
|
||||
privateTransactionProcessor.processTransaction(
|
||||
blockchain,
|
||||
worldStateUpdater.updater(),
|
||||
privateStateUpdater,
|
||||
blockHeader,
|
||||
privateTransaction,
|
||||
miningBeneficiary,
|
||||
OperationTracer.NO_TRACING,
|
||||
new BlockHashLookup(blockHeader, blockchain),
|
||||
privateTransaction.getPrivacyGroupId().get());
|
||||
persistPrivateState(
|
||||
transaction.getHash(),
|
||||
blockHeader.getHash(),
|
||||
privateTransaction,
|
||||
Bytes32.wrap(privateTransaction.getPrivacyGroupId().get()),
|
||||
disposablePrivateState,
|
||||
privateStateUpdater,
|
||||
privateStateStorage,
|
||||
privateResult);
|
||||
LOG.debug("Post-rehydrate root hash: {}", disposablePrivateState.rootHash());
|
||||
}
|
||||
|
||||
final TransactionProcessor.Result result =
|
||||
transactionProcessor.processTransaction(
|
||||
blockchain,
|
||||
worldStateUpdater,
|
||||
blockHeader,
|
||||
transaction,
|
||||
miningBeneficiary,
|
||||
blockHashLookup,
|
||||
false,
|
||||
TransactionValidationParams.processingBlock());
|
||||
if (result.isInvalid()) {
|
||||
return AbstractBlockProcessor.Result.failed();
|
||||
}
|
||||
|
||||
worldStateUpdater.commit();
|
||||
gasUsed = transaction.getGasLimit() - result.getGasRemaining() + gasUsed;
|
||||
final TransactionReceipt transactionReceipt =
|
||||
transactionReceiptFactory.create(result, worldState, gasUsed);
|
||||
receipts.add(transactionReceipt);
|
||||
}
|
||||
|
||||
if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) {
|
||||
return AbstractBlockProcessor.Result.failed();
|
||||
}
|
||||
|
||||
return AbstractBlockProcessor.Result.successful(receipts);
|
||||
}
|
||||
|
||||
protected void persistPrivateState(
|
||||
final Hash commitmentHash,
|
||||
final Hash currentBlockHash,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Bytes32 privacyGroupId,
|
||||
final MutableWorldState disposablePrivateState,
|
||||
final WorldUpdater privateWorldStateUpdater,
|
||||
final PrivateStateStorage privateStateStorage,
|
||||
final PrivateTransactionProcessor.Result result) {
|
||||
|
||||
LOG.trace(
|
||||
"Persisting private state {} for privacyGroup {}",
|
||||
disposablePrivateState.rootHash(),
|
||||
privacyGroupId);
|
||||
privateWorldStateUpdater.commit();
|
||||
disposablePrivateState.persist();
|
||||
|
||||
final PrivateStateStorage.Updater privateStateUpdater = privateStateStorage.updater();
|
||||
|
||||
updatePrivateBlockMetadata(
|
||||
commitmentHash,
|
||||
currentBlockHash,
|
||||
privacyGroupId,
|
||||
disposablePrivateState.rootHash(),
|
||||
privateStateUpdater,
|
||||
privateStateStorage);
|
||||
|
||||
final Bytes32 txHash = keccak256(RLP.encode(privateTransaction::writeTo));
|
||||
|
||||
final int txStatus =
|
||||
result.getStatus() == PrivateTransactionProcessor.Result.Status.SUCCESSFUL ? 1 : 0;
|
||||
|
||||
final PrivateTransactionReceipt privateTransactionReceipt =
|
||||
new PrivateTransactionReceipt(
|
||||
txStatus, result.getLogs(), result.getOutput(), result.getRevertReason());
|
||||
|
||||
privateStateUpdater.putTransactionReceipt(currentBlockHash, txHash, privateTransactionReceipt);
|
||||
final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap =
|
||||
privateStateStorage.getPrivacyGroupHeadBlockMap(currentBlockHash).get();
|
||||
if (!privacyGroupHeadBlockMap.contains(Bytes32.wrap(privacyGroupId), currentBlockHash)) {
|
||||
privacyGroupHeadBlockMap.put(Bytes32.wrap(privacyGroupId), currentBlockHash);
|
||||
privateStateUpdater.putPrivacyGroupHeadBlockMap(
|
||||
currentBlockHash, new PrivacyGroupHeadBlockMap(privacyGroupHeadBlockMap));
|
||||
}
|
||||
privateStateUpdater.commit();
|
||||
}
|
||||
|
||||
protected void maybeInjectDefaultManagementAndProxy(
|
||||
final Hash lastRootHash,
|
||||
final MutableWorldState disposablePrivateState,
|
||||
final WorldUpdater privateWorldStateUpdater) {
|
||||
if (lastRootHash.equals(EMPTY_ROOT_HASH)) {
|
||||
// inject management
|
||||
final DefaultEvmAccount managementPrecompile =
|
||||
privateWorldStateUpdater.createAccount(Address.DEFAULT_PRIVACY_MANAGEMENT);
|
||||
final MutableAccount mutableManagementPrecompiled = managementPrecompile.getMutable();
|
||||
// this is the code for the simple management contract
|
||||
mutableManagementPrecompiled.setCode(OnChainGroupManagement.DEFAULT_GROUP_MANAGEMENT_CODE);
|
||||
|
||||
// inject proxy
|
||||
final DefaultEvmAccount proxyPrecompile =
|
||||
privateWorldStateUpdater.createAccount(Address.PRIVACY_PROXY);
|
||||
final MutableAccount mutableProxyPrecompiled = proxyPrecompile.getMutable();
|
||||
// this is the code for the proxy contract
|
||||
mutableProxyPrecompiled.setCode(OnChainGroupManagement.DEFAULT_PROXY_PRECOMPILED_CODE);
|
||||
// manually set the management contract address so the proxy can trust it
|
||||
mutableProxyPrecompiled.setStorageValue(
|
||||
UInt256.ZERO, UInt256.fromBytes(Bytes32.leftPad(Address.DEFAULT_PRIVACY_MANAGEMENT)));
|
||||
|
||||
privateWorldStateUpdater.commit();
|
||||
disposablePrivateState.persist();
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePrivateBlockMetadata(
|
||||
final Hash markerTransactionHash,
|
||||
final Hash currentBlockHash,
|
||||
final Bytes32 privacyGroupId,
|
||||
final Hash rootHash,
|
||||
final PrivateStateStorage.Updater privateStateUpdater,
|
||||
final PrivateStateStorage privateStateStorage) {
|
||||
final PrivateBlockMetadata privateBlockMetadata =
|
||||
privateStateStorage
|
||||
.getPrivateBlockMetadata(currentBlockHash, Bytes32.wrap(privacyGroupId))
|
||||
.orElseGet(PrivateBlockMetadata::empty);
|
||||
privateBlockMetadata.addPrivateTransactionMetadata(
|
||||
new PrivateTransactionMetadata(markerTransactionHash, rootHash));
|
||||
privateStateUpdater.putPrivateBlockMetadata(
|
||||
Bytes32.wrap(currentBlockHash), Bytes32.wrap(privacyGroupId), privateBlockMetadata);
|
||||
}
|
||||
|
||||
private boolean rewardCoinbase(
|
||||
final MutableWorldState worldState,
|
||||
final ProcessableBlockHeader header,
|
||||
final List<BlockHeader> ommers,
|
||||
final boolean skipZeroBlockRewards) {
|
||||
if (skipZeroBlockRewards && blockReward.isZero()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final Wei coinbaseReward = blockReward.add(blockReward.multiply(ommers.size()).divide(32));
|
||||
final WorldUpdater updater = worldState.updater();
|
||||
final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()).getMutable();
|
||||
|
||||
coinbase.incrementBalance(coinbaseReward);
|
||||
for (final BlockHeader ommerHeader : ommers) {
|
||||
if (ommerHeader.getNumber() - header.getNumber() > MAX_GENERATION) {
|
||||
LOG.warn(
|
||||
"Block processing error: ommer block number {} more than {} generations current block number {}",
|
||||
ommerHeader.getNumber(),
|
||||
MAX_GENERATION,
|
||||
header.getNumber());
|
||||
return false;
|
||||
}
|
||||
|
||||
final MutableAccount ommerCoinbase =
|
||||
updater.getOrCreate(ommerHeader.getCoinbase()).getMutable();
|
||||
final long distance = header.getNumber() - ommerHeader.getNumber();
|
||||
final Wei ommerReward = blockReward.subtract(blockReward.multiply(distance).divide(8));
|
||||
ommerCoinbase.incrementBalance(ommerReward);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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.privacy;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.TransactionLocation;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
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 PrivateStateRehydration {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
private final PrivateStateStorage privateStateStorage;
|
||||
private final Blockchain blockchain;
|
||||
private final ProtocolSchedule<?> protocolSchedule;
|
||||
private final WorldStateArchive publicWorldStateArchive;
|
||||
private final WorldStateArchive privateWorldStateArchive;
|
||||
|
||||
public PrivateStateRehydration(
|
||||
final PrivateStateStorage privateStateStorage,
|
||||
final Blockchain blockchain,
|
||||
final ProtocolSchedule<?> protocolSchedule,
|
||||
final WorldStateArchive publicWorldStateArchive,
|
||||
final WorldStateArchive privateWorldStateArchive) {
|
||||
this.privateStateStorage = privateStateStorage;
|
||||
this.blockchain = blockchain;
|
||||
this.protocolSchedule = protocolSchedule;
|
||||
this.publicWorldStateArchive = publicWorldStateArchive;
|
||||
this.privateWorldStateArchive = privateWorldStateArchive;
|
||||
}
|
||||
|
||||
public void rehydrate(
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList) {
|
||||
final long rehydrationStartTimestamp = System.currentTimeMillis();
|
||||
final long chainHeadBlockNumber = blockchain.getChainHeadBlockNumber();
|
||||
final Optional<Bytes> maybeGroupId =
|
||||
privateTransactionWithMetadataList.get(0).getPrivateTransaction().getPrivacyGroupId();
|
||||
if (maybeGroupId.isEmpty()) {
|
||||
LOG.debug("On-chain groups must have a group id.");
|
||||
return;
|
||||
}
|
||||
final Bytes32 privacyGroupId = Bytes32.wrap(maybeGroupId.get());
|
||||
|
||||
LOG.debug("Rehydrating privacy group {}", privacyGroupId.toBase64String());
|
||||
|
||||
// check if there is a privacyGroupHeadBlockMap for the first block ...
|
||||
final boolean needEmptyPrivacyGroupHeadBlockMap =
|
||||
privateStateStorage
|
||||
.getPrivacyGroupHeadBlockMap(
|
||||
getBlockHashForIndex(0, privateTransactionWithMetadataList))
|
||||
.isEmpty();
|
||||
if (needEmptyPrivacyGroupHeadBlockMap) {
|
||||
privateStateStorage
|
||||
.updater()
|
||||
.putPrivacyGroupHeadBlockMap(
|
||||
getBlockHashForIndex(0, privateTransactionWithMetadataList),
|
||||
PrivacyGroupHeadBlockMap.EMPTY)
|
||||
.commit();
|
||||
}
|
||||
|
||||
for (int i = 0; i < privateTransactionWithMetadataList.size(); i++) {
|
||||
// find out which block this transaction is in
|
||||
final Hash blockHash = getBlockHashForIndex(i, privateTransactionWithMetadataList);
|
||||
|
||||
// if there are multiple pmts in the list we can increment our index i. At the end of the
|
||||
// while loop i will be the index of the last PMT (for this group) that is in this block.
|
||||
while (i + 1 < privateTransactionWithMetadataList.size()
|
||||
&& blockHash.equals(getBlockHashForIndex(i + 1, privateTransactionWithMetadataList))) {
|
||||
i++;
|
||||
}
|
||||
|
||||
final Hash lastPmtHash =
|
||||
privateTransactionWithMetadataList
|
||||
.get(i)
|
||||
.getPrivateTransactionMetadata()
|
||||
.getPrivacyMarkerTransactionHash();
|
||||
|
||||
final Optional<TransactionLocation> transactionLocationOfLastPmtInBlock =
|
||||
blockchain.getTransactionLocation(lastPmtHash);
|
||||
if (transactionLocationOfLastPmtInBlock.isEmpty()) {
|
||||
LOG.debug("Rehydartion failed - missing marker transaction for {}", lastPmtHash);
|
||||
return;
|
||||
}
|
||||
|
||||
final Block block = blockchain.getBlockByHash(blockHash).orElseThrow(RuntimeException::new);
|
||||
final BlockHeader blockHeader = block.getHeader();
|
||||
LOG.debug(
|
||||
"Rehydrating block {} ({}/{}), {}",
|
||||
blockHash,
|
||||
blockHeader.getNumber(),
|
||||
chainHeadBlockNumber,
|
||||
block.getBody().getTransactions().stream()
|
||||
.map(Transaction::getHash)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
final ProtocolSpec<?> protocolSpec =
|
||||
protocolSchedule.getByBlockNumber(blockchain.getBlockHeader(blockHash).get().getNumber());
|
||||
final PrivateGroupRehydrationBlockProcessor privateGroupRehydrationBlockProcessor =
|
||||
new PrivateGroupRehydrationBlockProcessor(
|
||||
protocolSpec.getTransactionProcessor(),
|
||||
protocolSpec.getPrivateTransactionProcessor(),
|
||||
protocolSpec.getTransactionReceiptFactory(),
|
||||
protocolSpec.getBlockReward(),
|
||||
protocolSpec.getMiningBeneficiaryCalculator(),
|
||||
protocolSpec.isSkipZeroBlockRewards());
|
||||
|
||||
final MutableWorldState publicWorldState =
|
||||
blockchain
|
||||
.getBlockHeader(blockHeader.getParentHash())
|
||||
.map(BlockHeader::getStateRoot)
|
||||
.flatMap(publicWorldStateArchive::getMutable)
|
||||
.orElseThrow(RuntimeException::new);
|
||||
|
||||
// enclave cache for private block rehydration
|
||||
final LinkedHashMap<Hash, PrivateTransaction> enclaveMap = new LinkedHashMap<>();
|
||||
for (int j = 0; j < privateTransactionWithMetadataList.size(); j++) {
|
||||
final PrivateTransactionWithMetadata transactionWithMetadata =
|
||||
privateTransactionWithMetadataList.get(j);
|
||||
enclaveMap.put(
|
||||
transactionWithMetadata
|
||||
.getPrivateTransactionMetadata()
|
||||
.getPrivacyMarkerTransactionHash(),
|
||||
transactionWithMetadata.getPrivateTransaction());
|
||||
}
|
||||
|
||||
privateGroupRehydrationBlockProcessor.processBlock(
|
||||
blockchain,
|
||||
publicWorldState,
|
||||
privateWorldStateArchive,
|
||||
privateStateStorage,
|
||||
block,
|
||||
enclaveMap,
|
||||
block.getBody().getOmmers());
|
||||
|
||||
// check the resulting private state against the state in the meta data
|
||||
final Optional<Hash> latestStateRoot =
|
||||
privateStateStorage
|
||||
.getPrivateBlockMetadata(blockHash, privacyGroupId)
|
||||
.orElseThrow()
|
||||
.getLatestStateRoot();
|
||||
if (latestStateRoot.isPresent()) {
|
||||
if (!latestStateRoot
|
||||
.get()
|
||||
.equals(
|
||||
privateTransactionWithMetadataList
|
||||
.get(i)
|
||||
.getPrivateTransactionMetadata()
|
||||
.getStateRoot())) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
// fix the privacy group header block map for the blocks between the current block and the
|
||||
// next block containing a pmt for this privacy group
|
||||
if (i + 1 < privateTransactionWithMetadataList.size()) {
|
||||
rehydratePrivacyGroupHeadBlockMap(
|
||||
privacyGroupId,
|
||||
blockHash,
|
||||
blockchain,
|
||||
getBlockNumberForIndex(i, privateTransactionWithMetadataList),
|
||||
getBlockNumberForIndex(i + 1, privateTransactionWithMetadataList));
|
||||
} else {
|
||||
rehydratePrivacyGroupHeadBlockMap(
|
||||
privacyGroupId,
|
||||
blockHash,
|
||||
blockchain,
|
||||
getBlockNumberForIndex(i, privateTransactionWithMetadataList),
|
||||
blockchain.getChainHeadBlockNumber() + 1);
|
||||
}
|
||||
}
|
||||
final long rehydrationDuration = System.currentTimeMillis() - rehydrationStartTimestamp;
|
||||
LOG.debug("Rehydration took {} seconds", rehydrationDuration / 1000.0);
|
||||
}
|
||||
|
||||
protected void rehydratePrivacyGroupHeadBlockMap(
|
||||
final Bytes32 privacyGroupId,
|
||||
final Hash hashOfLastBlockWithPmt,
|
||||
final Blockchain currentBlockchain,
|
||||
final long from,
|
||||
final long to) {
|
||||
for (long j = from + 1; j < to; j++) {
|
||||
final BlockHeader theBlockHeader = currentBlockchain.getBlockHeader(j).orElseThrow();
|
||||
final PrivacyGroupHeadBlockMap thePrivacyGroupHeadBlockMap =
|
||||
privateStateStorage
|
||||
.getPrivacyGroupHeadBlockMap(theBlockHeader.getHash())
|
||||
.orElse(PrivacyGroupHeadBlockMap.EMPTY);
|
||||
final PrivateStateStorage.Updater privateStateUpdater = privateStateStorage.updater();
|
||||
thePrivacyGroupHeadBlockMap.put(privacyGroupId, hashOfLastBlockWithPmt);
|
||||
privateStateUpdater.putPrivacyGroupHeadBlockMap(
|
||||
theBlockHeader.getHash(), new PrivacyGroupHeadBlockMap(thePrivacyGroupHeadBlockMap));
|
||||
privateStateUpdater.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private long getBlockNumberForIndex(
|
||||
final int index,
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList) {
|
||||
return blockchain
|
||||
.getBlockHeader(getBlockHashForIndex(index, privateTransactionWithMetadataList))
|
||||
.orElseThrow()
|
||||
.getNumber();
|
||||
}
|
||||
|
||||
private Hash getBlockHashForIndex(
|
||||
final int index,
|
||||
final List<PrivateTransactionWithMetadata> privateTransactionWithMetadataList) {
|
||||
return blockchain
|
||||
.getTransactionLocation(
|
||||
privateTransactionWithMetadataList
|
||||
.get(index)
|
||||
.getPrivateTransactionMetadata()
|
||||
.getPrivacyMarkerTransactionHash())
|
||||
.orElseThrow()
|
||||
.getBlockHash();
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
public class PrivateTransactionReceipt {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final PrivateTransactionReceipt EMPTY =
|
||||
public static final PrivateTransactionReceipt FAILED =
|
||||
new PrivateTransactionReceipt(
|
||||
0, Collections.EMPTY_LIST, Bytes.EMPTY, Optional.ofNullable(null));
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@ public class PrivateTransactionSimulator {
|
||||
new PrivateStateRootResolver(privacyParameters.getPrivateStateStorage());
|
||||
}
|
||||
|
||||
public Optional<PrivateTransactionProcessor.Result> process(
|
||||
final String privacyGroupId, final CallParameter callParams) {
|
||||
final BlockHeader header = blockchain.getChainHeadHeader();
|
||||
return process(privacyGroupId, callParams, header);
|
||||
}
|
||||
|
||||
public Optional<PrivateTransactionProcessor.Result> process(
|
||||
final String privacyGroupId, final CallParameter callParams, final Hash blockHeaderHash) {
|
||||
final BlockHeader header = blockchain.getBlockHeader(blockHeaderHash).orElse(null);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.privacy;
|
||||
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPException;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class PrivateTransactionWithMetadata {
|
||||
private final PrivateTransaction privateTransaction;
|
||||
private final PrivateTransactionMetadata privateTransactionMetadata;
|
||||
|
||||
public static PrivateTransactionWithMetadata readFrom(final RLPInput input) throws RLPException {
|
||||
input.enterList();
|
||||
final PrivateTransaction privateTransaction = PrivateTransaction.readFrom(input.readAsRlp());
|
||||
final PrivateTransactionMetadata privateTransactionMetadata =
|
||||
PrivateTransactionMetadata.readFrom(input.readAsRlp());
|
||||
input.leaveList();
|
||||
return new PrivateTransactionWithMetadata(privateTransaction, privateTransactionMetadata);
|
||||
}
|
||||
|
||||
public PrivateTransactionWithMetadata(
|
||||
final PrivateTransaction privateTransaction,
|
||||
final PrivateTransactionMetadata privateTransactionMetadata) {
|
||||
this.privateTransaction = privateTransaction;
|
||||
this.privateTransactionMetadata = privateTransactionMetadata;
|
||||
}
|
||||
|
||||
public void writeTo(final RLPOutput out) {
|
||||
out.startList();
|
||||
privateTransaction.writeTo(out);
|
||||
privateTransactionMetadata.writeTo(out);
|
||||
out.endList();
|
||||
}
|
||||
|
||||
public PrivateTransaction getPrivateTransaction() {
|
||||
return privateTransaction;
|
||||
}
|
||||
|
||||
public PrivateTransactionMetadata getPrivateTransactionMetadata() {
|
||||
return privateTransactionMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final PrivateTransactionWithMetadata that = (PrivateTransactionWithMetadata) o;
|
||||
return privateTransaction.equals(that.privateTransaction)
|
||||
&& privateTransactionMetadata.equals(that.privateTransactionMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(privateTransaction, privateTransactionMetadata);
|
||||
}
|
||||
}
|
||||
@@ -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.privacy;
|
||||
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPException;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
|
||||
public class VersionedPrivateTransaction {
|
||||
private final PrivateTransaction privateTransaction;
|
||||
private final Bytes32 version;
|
||||
|
||||
public VersionedPrivateTransaction(
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Optional<PrivateTransactionProcessor.Result> result) {
|
||||
this(
|
||||
privateTransaction,
|
||||
result
|
||||
.map(value -> Bytes32.fromHexStringLenient(value.getOutput().toHexString()))
|
||||
.orElse(Bytes32.ZERO));
|
||||
}
|
||||
|
||||
public VersionedPrivateTransaction(
|
||||
final PrivateTransaction privateTransaction, final Bytes32 version) {
|
||||
this.privateTransaction = privateTransaction;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public static VersionedPrivateTransaction readFrom(final RLPInput input) throws RLPException {
|
||||
input.enterList();
|
||||
final PrivateTransaction privateTransaction = PrivateTransaction.readFrom(input.readAsRlp());
|
||||
final Bytes32 version = input.readBytes32();
|
||||
input.leaveList();
|
||||
return new VersionedPrivateTransaction(privateTransaction, version);
|
||||
}
|
||||
|
||||
public PrivateTransaction getPrivateTransaction() {
|
||||
return privateTransaction;
|
||||
}
|
||||
|
||||
public Bytes32 getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void writeTo(final BytesValueRLPOutput rlpOutput) {
|
||||
rlpOutput.startList();
|
||||
privateTransaction.writeTo(rlpOutput);
|
||||
rlpOutput.writeBytes(version);
|
||||
rlpOutput.endList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.privacy.group;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class OnChainGroupManagement {
|
||||
public static final Bytes DEFAULT_PROXY_PRECOMPILED_CODE =
|
||||
Bytes.fromHexString(
|
||||
"0x608060405234801561001057600080fd5b50600436106100935760003560e01c806361544c911161006657806361544c91146101c757806378b9033714610217578063a69df4b514610239578063f744b08914610243578063f83d08ba1461031d57610093565b80630b0235be146100985780630d8e6e2c1461011b5780633659cfe6146101395780635c60da1b1461017d575b600080fd5b6100c4600480360360208110156100ae57600080fd5b8101908080359060200190929190505050610327565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156101075780820151818401526020810190506100ec565b505050509050019250505060405180910390f35b61012361047d565b6040518082815260200191505060405180910390f35b61017b6004803603602081101561014f57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061052b565b005b610185610591565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101fd600480360360408110156101dd57600080fd5b8101908080359060200190929190803590602001909291905050506105b6565b604051808215151515815260200191505060405180910390f35b61021f61067c565b604051808215151515815260200191505060405180910390f35b61024161072a565b005b6103036004803603604081101561025957600080fd5b81019080803590602001909291908035906020019064010000000081111561028057600080fd5b82018360208201111561029257600080fd5b803590602001918460208302840111640100000000831117156102b457600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192905050506107b3565b604051808215151515815260200191505060405180910390f35b6103256108ba565b005b606060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff16630b0235be846040518263ffffffff1660e01b81526004018082815260200191505060006040518083038186803b1580156103a057600080fd5b505afa1580156103b4573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060208110156103de57600080fd5b81019080805160405193929190846401000000008211156103fe57600080fd5b8382019150602082018581111561041457600080fd5b825186602082028301116401000000008211171561043157600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561046857808201518184015260208101905061044d565b50505050905001604052505050915050919050565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff16630d8e6e2c6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104ea57600080fd5b505afa1580156104fe573d6000803e3d6000fd5b505050506040513d602081101561051457600080fd5b810190808051906020019092919050505091505090565b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561058557600080fd5b61058e81610943565b50565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff166361544c9185856040518363ffffffff1660e01b81526004018083815260200182815260200192505050602060405180830381600087803b15801561063857600080fd5b505af115801561064c573d6000803e3d6000fd5b505050506040513d602081101561066257600080fd5b810190808051906020019092919050505091505092915050565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff166378b903376040518163ffffffff1660e01b815260040160206040518083038186803b1580156106e957600080fd5b505afa1580156106fd573d6000803e3d6000fd5b505050506040513d602081101561071357600080fd5b810190808051906020019092919050505091505090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663a69df4b56040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561079857600080fd5b505af11580156107ac573d6000803e3d6000fd5b5050505050565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663f744b08985856040518363ffffffff1660e01b81526004018083815260200180602001828103825283818151815260200191508051906020019060200280838360005b83811015610850578082015181840152602081019050610835565b505050509050019350505050602060405180830381600087803b15801561087657600080fd5b505af115801561088a573d6000803e3d6000fd5b505050506040513d60208110156108a057600080fd5b810190808051906020019092919050505091505092915050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff1663f83d08ba6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561092857600080fd5b505af115801561093c573d6000803e3d6000fd5b5050505050565b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505056fea265627a7a72315820498cff5aa36efa4596466bc5546201e9c41df019fcc994f209e16b330d34284b64736f6c63430005100032");
|
||||
|
||||
public static final Bytes DEFAULT_GROUP_MANAGEMENT_CODE =
|
||||
Bytes.fromHexString(
|
||||
"0x608060405234801561001057600080fd5b506004361061007d5760003560e01c806378b903371161005b57806378b9033714610173578063a69df4b514610195578063f744b0891461019f578063f83d08ba146102795761007d565b80630b0235be146100825780630d8e6e2c1461010557806361544c9114610123575b600080fd5b6100ae6004803603602081101561009857600080fd5b8101908080359060200190929190505050610283565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156100f15780820151818401526020810190506100d6565b505050509050019250505060405180910390f35b61010d6102ef565b6040518082815260200191505060405180910390f35b6101596004803603604081101561013957600080fd5b8101908080359060200190929190803590602001909291905050506102f9565b604051808215151515815260200191505060405180910390f35b61017b61031e565b604051808215151515815260200191505060405180910390f35b61019d610334565b005b61025f600480360360408110156101b557600080fd5b8101908080359060200190929190803590602001906401000000008111156101dc57600080fd5b8201836020820111156101ee57600080fd5b8035906020019184602083028401116401000000008311171561021057600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050610369565b604051808215151515815260200191505060405180910390f35b6102816103f3565b005b606061028e82610427565b61029757600080fd5b60028054806020026020016040519081016040528092919081815260200182805480156102e357602002820191906000526020600020905b8154815260200190600101908083116102cf575b50505050509050919050565b6000600154905090565b600061030483610427565b61030d57600080fd5b61031682610447565b905092915050565b60008060009054906101000a900460ff16905090565b6000809054906101000a900460ff161561034d57600080fd5b60016000806101000a81548160ff021916908315150217905550565b60008060009054906101000a900460ff161561038457600080fd5b6000600280549050141561039d5761039b8361052a565b505b6103a683610427565b6103af57600080fd5b60006103bb848461059c565b905060016000806101000a81548160ff0219169083151502179055506001600081548092919060010191905055508091505092915050565b6000809054906101000a900460ff1661040b57600080fd5b60008060006101000a81548160ff021916908315150217905550565b600080600360008481526020019081526020016000205414159050919050565b6000806003600084815260200190815260200160002054905060008111801561047557506002805490508111155b1561051f5760028054905081146104e357600060026001600280549050038154811061049d57fe5b9060005260206000200154905080600260018403815481106104bb57fe5b9060005260206000200181905550816003600083815260200190815260200160002081905550505b60016002818180549050039150816104fb919061087e565b50600060036000858152602001908152602001600020819055506001915050610525565b60009150505b919050565b600080600360008481526020019081526020016000205414156105925760028290806001815401808255809150509060018203906000526020600020016000909192909190915055600360008481526020019081526020016000208190555060019050610597565b600090505b919050565b6000806001905060008090505b8351811015610873578381815181106105be57fe5b6020026020010151851415610652577fcc7365305ae5f16c463d1383713d699f43c5548bbda5537ee61373ceb9aaf21360008583815181106105fc57fe5b60200260200101516040518083151515158152602001828152602001806020018281038252602f8152602001806108f1602f9139604001935050505060405180910390a181801561064b575060005b9150610866565b61066e84828151811061066157fe5b6020026020010151610427565b15610715577fcc7365305ae5f16c463d1383713d699f43c5548bbda5537ee61373ceb9aaf21360008583815181106106a257fe5b60200260200101516040518083151515158152602001828152602001806020018281038252601b8152602001807f4163636f756e7420697320616c72656164792061204d656d6265720000000000815250602001935050505060405180910390a181801561070e575060005b9150610865565b600061073385838151811061072657fe5b602002602001015161052a565b9050606081610777576040518060400160405280601b81526020017f4163636f756e7420697320616c72656164792061204d656d6265720000000000815250610791565b6040518060600160405280602181526020016108d0602191395b90507fcc7365305ae5f16c463d1383713d699f43c5548bbda5537ee61373ceb9aaf213828785815181106107c157fe5b602002602001015183604051808415151515815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561081a5780820151818401526020810190506107ff565b50505050905090810190601f1680156108475780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a18380156108605750815b935050505b5b80806001019150506105a9565b508091505092915050565b8154818355818111156108a5578183600052602060002091820191016108a491906108aa565b5b505050565b6108cc91905b808211156108c85760008160009055506001016108b0565b5090565b9056fe4d656d626572206163636f756e74206164646564207375636365737366756c6c79416464696e67206f776e206163636f756e742061732061204d656d626572206973206e6f74207065726d6974746564a265627a7a7231582081adaba054a78ca50b49183102a909f50f15b49eb4947bfa3593139d8833895564736f6c63430005100032");
|
||||
|
||||
public static final Bytes ADD_TO_GROUP_METHOD_SIGNATURE = Bytes.fromHexString("0xf744b089");
|
||||
public static final Bytes CAN_EXECUTE_METHOD_SIGNATURE = Bytes.fromHexString("0x78b90337");
|
||||
public static final Bytes GET_PARTICIPANTS_METHOD_SIGNATURE = Bytes.fromHexString("0x0b0235be");
|
||||
public static final Bytes GET_VERSION_METHOD_SIGNATURE = Bytes.fromHexString("0x0d8e6e2c");
|
||||
public static final Bytes REMOVE_PARTICIPANT_METHOD_SIGNATURE = Bytes.fromHexString("0x61544c91");
|
||||
public static final Bytes LOCK_GROUP_METHOD_SIGNATURE = Bytes.fromHexString("0xf83d08ba");
|
||||
}
|
||||
@@ -40,8 +40,14 @@ public class FixedKeySigningPrivateMarkerTransactionFactory
|
||||
|
||||
@Override
|
||||
public Transaction create(
|
||||
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) {
|
||||
final String transactionEnclaveKey,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Address precompileAddress) {
|
||||
return create(
|
||||
transactionEnclaveKey, privateTransaction, nonceProvider.getNonce(sender), signingKey);
|
||||
transactionEnclaveKey,
|
||||
privateTransaction,
|
||||
nonceProvider.getNonce(sender),
|
||||
signingKey,
|
||||
precompileAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,15 @@ public abstract class PrivateMarkerTransactionFactory {
|
||||
return privacyPrecompileAddress;
|
||||
}
|
||||
|
||||
public Transaction create(
|
||||
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) {
|
||||
return create(transactionEnclaveKey, privateTransaction, privacyPrecompileAddress);
|
||||
}
|
||||
|
||||
public abstract Transaction create(
|
||||
final String transactionEnclaveKey, final PrivateTransaction privateTransaction);
|
||||
final String transactionEnclaveKey,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Address precompileAddress);
|
||||
|
||||
protected Transaction create(
|
||||
final String transactionEnclaveKey,
|
||||
@@ -50,4 +57,20 @@ public abstract class PrivateMarkerTransactionFactory {
|
||||
.payload(Bytes.fromBase64String(transactionEnclaveKey))
|
||||
.signAndBuild(signingKey);
|
||||
}
|
||||
|
||||
protected Transaction create(
|
||||
final String transactionEnclaveKey,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final long nonce,
|
||||
final KeyPair signingKey,
|
||||
final Address precompileAddress) {
|
||||
return Transaction.builder()
|
||||
.nonce(nonce)
|
||||
.gasPrice(privateTransaction.getGasPrice())
|
||||
.gasLimit(privateTransaction.getGasLimit())
|
||||
.to(precompileAddress)
|
||||
.value(privateTransaction.getValue())
|
||||
.payload(Bytes.fromBase64String(transactionEnclaveKey))
|
||||
.signAndBuild(signingKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,10 @@ public class RandomSigningPrivateMarkerTransactionFactory extends PrivateMarkerT
|
||||
|
||||
@Override
|
||||
public Transaction create(
|
||||
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) {
|
||||
final String transactionEnclaveKey,
|
||||
final PrivateTransaction privateTransaction,
|
||||
final Address precompileAddress) {
|
||||
final KeyPair signingKey = KeyPair.generate();
|
||||
return create(transactionEnclaveKey, privateTransaction, 0, signingKey);
|
||||
return create(transactionEnclaveKey, privateTransaction, 0, signingKey, precompileAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ public class PrivateStateKeyValueStorage implements PrivateStateStorage {
|
||||
private static final Bytes PRIVACY_GROUP_HEAD_BLOCK_MAP_SUFFIX =
|
||||
Bytes.of("PGHEADMAP".getBytes(UTF_8));
|
||||
private static final Bytes LEGACY_STATUS_KEY_SUFFIX = Bytes.of("STATUS".getBytes(UTF_8));
|
||||
private static final Bytes ADD_DATA_KEY = Bytes.of("ADDKEY".getBytes(UTF_8));
|
||||
|
||||
private final KeyValueStorage keyValueStorage;
|
||||
|
||||
@@ -68,6 +69,11 @@ public class PrivateStateKeyValueStorage implements PrivateStateStorage {
|
||||
.map(b -> PrivacyGroupHeadBlockMap.readFrom(new BytesValueRLPInput(b, false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Bytes32> getAddDataKey(final Bytes32 privacyGroupId) {
|
||||
return get(privacyGroupId, ADD_DATA_KEY).map(Bytes32::wrap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSchemaVersion() {
|
||||
return get(Bytes.EMPTY, DB_VERSION_KEY).map(Bytes::toInt).orElse(SCHEMA_VERSION_1_0_0);
|
||||
@@ -144,6 +150,13 @@ public class PrivateStateKeyValueStorage implements PrivateStateStorage {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateStateStorage.Updater putAddDataKey(
|
||||
final Bytes32 privacyGroupId, final Bytes32 addDataKey) {
|
||||
set(privacyGroupId, ADD_DATA_KEY, addDataKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
transaction.commit();
|
||||
|
||||
@@ -31,6 +31,8 @@ public interface PrivateStateStorage {
|
||||
|
||||
int getSchemaVersion();
|
||||
|
||||
Optional<Bytes32> getAddDataKey(Bytes32 privacyGroupId);
|
||||
|
||||
boolean isEmpty();
|
||||
|
||||
Updater updater();
|
||||
@@ -47,6 +49,8 @@ public interface PrivateStateStorage {
|
||||
|
||||
Updater putDatabaseVersion(int version);
|
||||
|
||||
Updater putAddDataKey(Bytes32 privacyGroupId, Bytes32 addDataKey);
|
||||
|
||||
void commit();
|
||||
|
||||
void rollback();
|
||||
|
||||
@@ -18,6 +18,8 @@ import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/** Mined private transaction metadata. */
|
||||
public class PrivateTransactionMetadata {
|
||||
private final Hash privacyMarkerTransactionHash;
|
||||
@@ -55,4 +57,18 @@ public class PrivateTransactionMetadata {
|
||||
input.leaveList();
|
||||
return privateTransactionMetadata;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final PrivateTransactionMetadata that = (PrivateTransactionMetadata) o;
|
||||
return privacyMarkerTransactionHash.equals(that.privacyMarkerTransactionHash)
|
||||
&& stateRoot.equals(that.stateRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(privacyMarkerTransactionHash, stateRoot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import org.hyperledger.besu.enclave.Enclave;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
@@ -26,6 +27,7 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -39,12 +41,24 @@ public class PrivacyBlockProcessorTest {
|
||||
private PrivacyBlockProcessor privacyBlockProcessor;
|
||||
private PrivateStateStorage privateStateStorage;
|
||||
private AbstractBlockProcessor blockProcessor;
|
||||
private WorldStateArchive privateWorldStateArchive;
|
||||
private Enclave enclave;
|
||||
private ProtocolSchedule<?> protocolSchedule;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
blockProcessor = mock(AbstractBlockProcessor.class);
|
||||
privateStateStorage = new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage());
|
||||
this.privacyBlockProcessor = new PrivacyBlockProcessor(blockProcessor, privateStateStorage);
|
||||
privateWorldStateArchive = mock(WorldStateArchive.class);
|
||||
enclave = mock(Enclave.class);
|
||||
protocolSchedule = mock(ProtocolSchedule.class);
|
||||
this.privacyBlockProcessor =
|
||||
new PrivacyBlockProcessor(
|
||||
blockProcessor,
|
||||
protocolSchedule,
|
||||
enclave,
|
||||
privateStateStorage,
|
||||
privateWorldStateArchive);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -37,13 +37,14 @@ import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
|
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateTransactionDataFixture;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
|
||||
import org.hyperledger.besu.ethereum.vm.MessageFrame;
|
||||
import org.hyperledger.besu.ethereum.vm.OperationTracer;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -62,25 +63,10 @@ public class PrivacyPrecompiledContractTest {
|
||||
private MessageFrame messageFrame;
|
||||
private Blockchain blockchain;
|
||||
private final String DEFAULT_OUTPUT = "0x01";
|
||||
final String PAYLOAD_TEST_PRIVACY_GROUP_ID = "8lDVI66RZHIrBsolz6Kn88Rd+WsJ4hUjb4hsh29xW/o=";
|
||||
private final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class);
|
||||
final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class);
|
||||
|
||||
private static final byte[] VALID_PRIVATE_TRANSACTION_RLP_BASE64 =
|
||||
Base64.getEncoder()
|
||||
.encode(
|
||||
Bytes.fromHexString(
|
||||
"0xf90113800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87"
|
||||
+ "a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||
+ "ffff801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d"
|
||||
+ "495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab94"
|
||||
+ "9f53faa07bd2c804ac41316156744d784c4355486d425648586f5a7a7a4267"
|
||||
+ "5062572f776a3561784470573958386c393153476f3df85aac41316156744d"
|
||||
+ "784c4355486d425648586f5a7a7a42675062572f776a356178447057395838"
|
||||
+ "6c393153476f3dac4b6f32625671442b6e4e6c4e594c35454537793349644f"
|
||||
+ "6e766966746a69697a706a52742b4854754642733d8a726573747269637465"
|
||||
+ "64")
|
||||
.toArray());
|
||||
|
||||
private PrivateTransactionProcessor mockPrivateTxProcessor() {
|
||||
final PrivateTransactionProcessor mockPrivateTransactionProcessor =
|
||||
mock(PrivateTransactionProcessor.class);
|
||||
@@ -146,10 +132,14 @@ public class PrivacyPrecompiledContractTest {
|
||||
new SpuriousDragonGasCalculator(), enclave, worldStateArchive, privateStateStorage);
|
||||
contract.setPrivateTransactionProcessor(mockPrivateTxProcessor());
|
||||
|
||||
BytesValueRLPOutput bytesValueRLPOutput = new BytesValueRLPOutput();
|
||||
PrivateTransactionDataFixture.privateTransaction(PAYLOAD_TEST_PRIVACY_GROUP_ID)
|
||||
.writeTo(bytesValueRLPOutput);
|
||||
|
||||
final ReceiveResponse response =
|
||||
new ReceiveResponse(
|
||||
VALID_PRIVATE_TRANSACTION_RLP_BASE64,
|
||||
"8lDVI66RZHIrBsolz6Kn88Rd+WsJ4hUjb4hsh29xW/o=",
|
||||
bytesValueRLPOutput.encoded().toBase64String().getBytes(UTF_8),
|
||||
PAYLOAD_TEST_PRIVACY_GROUP_ID,
|
||||
null);
|
||||
when(enclave.receive(any(String.class))).thenReturn(response);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.privacy;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.INCORRECT_PRIVATE_NONCE;
|
||||
@@ -37,20 +38,25 @@ import org.hyperledger.besu.enclave.types.PrivacyGroup;
|
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup.Type;
|
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse;
|
||||
import org.hyperledger.besu.enclave.types.SendResponse;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Log;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
|
||||
import org.hyperledger.besu.ethereum.privacy.markertransaction.FixedKeySigningPrivateMarkerTransactionFactory;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap;
|
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
|
||||
import org.hyperledger.besu.ethereum.transaction.CallParameter;
|
||||
import org.hyperledger.orion.testutil.OrionKeyUtils;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
@@ -78,6 +84,8 @@ public class DefaultPrivacyControllerTest {
|
||||
private static final String ENCLAVE_KEY2 = "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=";
|
||||
private static final String PRIVACY_GROUP_ID = "DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=";
|
||||
private static final ArrayList<Log> LOGS = new ArrayList<>();
|
||||
private static final String MOCK_TRANSACTION_SIMULATOR_RESULT_OUTPUT_BYTES_PREFIX =
|
||||
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002";
|
||||
|
||||
private PrivacyController privacyController;
|
||||
private PrivacyController brokenPrivacyController;
|
||||
@@ -86,6 +94,9 @@ public class DefaultPrivacyControllerTest {
|
||||
private String enclavePublicKey;
|
||||
private PrivateNonceProvider privateNonceProvider;
|
||||
private PrivateWorldStateReader privateWorldStateReader;
|
||||
private PrivateTransactionSimulator privateTransactionSimulator;
|
||||
private Blockchain blockchain;
|
||||
private PrivateStateStorage privateStateStorage;
|
||||
|
||||
private static final Transaction PUBLIC_TRANSACTION =
|
||||
Transaction.builder()
|
||||
@@ -123,18 +134,11 @@ public class DefaultPrivacyControllerTest {
|
||||
return validator;
|
||||
}
|
||||
|
||||
private PrivateTransactionSimulator mockPrivateTransactionSimulator() {
|
||||
final PrivateTransactionSimulator simulator = mock(PrivateTransactionSimulator.class);
|
||||
when(simulator.process(any(), any(), any(long.class)))
|
||||
.thenReturn(
|
||||
Optional.of(
|
||||
PrivateTransactionProcessor.Result.successful(
|
||||
LOGS, 0, Bytes.EMPTY, ValidationResult.valid())));
|
||||
return simulator;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
blockchain = mock(Blockchain.class);
|
||||
privateTransactionSimulator = mock(PrivateTransactionSimulator.class);
|
||||
privateStateStorage = mock(PrivateStateStorage.class);
|
||||
privateNonceProvider = mock(ChainHeadPrivateNonceProvider.class);
|
||||
when(privateNonceProvider.getNonce(any(), any())).thenReturn(1L);
|
||||
|
||||
@@ -143,11 +147,11 @@ public class DefaultPrivacyControllerTest {
|
||||
enclavePublicKey = OrionKeyUtils.loadKey("orion_key_0.pub");
|
||||
privateTransactionValidator = mockPrivateTransactionValidator();
|
||||
enclave = mockEnclave();
|
||||
final PrivateTransactionSimulator privateTransactionSimulator =
|
||||
mockPrivateTransactionSimulator();
|
||||
|
||||
privacyController =
|
||||
new DefaultPrivacyController(
|
||||
blockchain,
|
||||
privateStateStorage,
|
||||
enclave,
|
||||
privateTransactionValidator,
|
||||
new FixedKeySigningPrivateMarkerTransactionFactory(
|
||||
@@ -157,6 +161,8 @@ public class DefaultPrivacyControllerTest {
|
||||
privateWorldStateReader);
|
||||
brokenPrivacyController =
|
||||
new DefaultPrivacyController(
|
||||
blockchain,
|
||||
privateStateStorage,
|
||||
brokenMockEnclave(),
|
||||
privateTransactionValidator,
|
||||
new FixedKeySigningPrivateMarkerTransactionFactory(
|
||||
@@ -192,6 +198,9 @@ public class DefaultPrivacyControllerTest {
|
||||
public void sendValidBesuTransaction() {
|
||||
final PrivateTransaction transaction = buildBesuPrivateTransaction(1);
|
||||
|
||||
when(enclave.retrievePrivacyGroup(any(String.class)))
|
||||
.thenReturn(new PrivacyGroup("", Type.PANTHEON, "", "", emptyList()));
|
||||
|
||||
final String enclaveKey = privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY);
|
||||
|
||||
final ValidationResult<TransactionInvalidReason> validationResult =
|
||||
@@ -209,6 +218,41 @@ public class DefaultPrivacyControllerTest {
|
||||
verify(enclave).send(anyString(), eq(ENCLAVE_PUBLIC_KEY), eq(PRIVACY_GROUP_ID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findOnChainPrivacyGroups() {
|
||||
final List<String> privacyGroupAddresses = newArrayList(ENCLAVE_PUBLIC_KEY, ENCLAVE_KEY2);
|
||||
|
||||
final PrivacyGroup privacyGroup =
|
||||
new PrivacyGroup(PRIVACY_GROUP_ID, Type.ONCHAIN, "", "", privacyGroupAddresses);
|
||||
|
||||
final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap =
|
||||
new PrivacyGroupHeadBlockMap(
|
||||
Map.of(Bytes32.wrap(Bytes.fromBase64String(PRIVACY_GROUP_ID)), Hash.ZERO));
|
||||
when(privateStateStorage.getPrivacyGroupHeadBlockMap(any()))
|
||||
.thenReturn(Optional.of(privacyGroupHeadBlockMap));
|
||||
|
||||
when(privateTransactionSimulator.process(any(), any()))
|
||||
.thenReturn(
|
||||
Optional.of(
|
||||
new PrivateTransactionProcessor.Result(
|
||||
TransactionProcessor.Result.Status.SUCCESSFUL,
|
||||
emptyList(),
|
||||
0,
|
||||
Bytes.fromHexString(
|
||||
MOCK_TRANSACTION_SIMULATOR_RESULT_OUTPUT_BYTES_PREFIX
|
||||
+ Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY).toUnprefixedHexString()
|
||||
+ Bytes.fromBase64String(ENCLAVE_KEY2).toUnprefixedHexString()),
|
||||
ValidationResult.valid(),
|
||||
Optional.empty())));
|
||||
|
||||
final List<PrivacyGroup> privacyGroups =
|
||||
privacyController.findOnChainPrivacyGroup(privacyGroupAddresses, ENCLAVE_PUBLIC_KEY);
|
||||
assertThat(privacyGroups).hasSize(1);
|
||||
assertThat(privacyGroups.get(0)).isEqualToComparingFieldByField(privacyGroup);
|
||||
verify(privateStateStorage).getPrivacyGroupHeadBlockMap(any());
|
||||
verify(privateTransactionSimulator).process(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendTransactionWhenEnclaveFailsThrowsEnclaveError() {
|
||||
assertThatExceptionOfType(EnclaveServerException.class)
|
||||
@@ -373,12 +417,40 @@ public class DefaultPrivacyControllerTest {
|
||||
@Test
|
||||
public void simulatingPrivateTransactionWorks() {
|
||||
final CallParameter callParameter = mock(CallParameter.class);
|
||||
when(privateTransactionSimulator.process(any(), any(), any(long.class)))
|
||||
.thenReturn(
|
||||
Optional.of(
|
||||
PrivateTransactionProcessor.Result.successful(
|
||||
LOGS, 0, Bytes.EMPTY, ValidationResult.valid())));
|
||||
final Optional<PrivateTransactionProcessor.Result> result =
|
||||
privacyController.simulatePrivateTransaction(
|
||||
"Group1", ENCLAVE_PUBLIC_KEY, callParameter, 1);
|
||||
assertThat(result.isPresent()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canCreatePrivacyMarkerTransactionForOnChainPrivacy() {
|
||||
final PrivateTransaction transaction = buildBesuPrivateTransaction(0);
|
||||
|
||||
final String enclaveKey = privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY);
|
||||
|
||||
final Transaction onChainPrivacyMarkerTransaction =
|
||||
privacyController.createPrivacyMarkerTransaction(
|
||||
enclaveKey, transaction, Address.ONCHAIN_PRIVACY);
|
||||
|
||||
assertThat(onChainPrivacyMarkerTransaction.contractAddress())
|
||||
.isEqualTo(PUBLIC_TRANSACTION.contractAddress());
|
||||
assertThat(onChainPrivacyMarkerTransaction.getPayload())
|
||||
.isEqualTo(PUBLIC_TRANSACTION.getPayload());
|
||||
assertThat(onChainPrivacyMarkerTransaction.getNonce()).isEqualTo(PUBLIC_TRANSACTION.getNonce());
|
||||
assertThat(onChainPrivacyMarkerTransaction.getSender())
|
||||
.isEqualTo(PUBLIC_TRANSACTION.getSender());
|
||||
assertThat(onChainPrivacyMarkerTransaction.getValue()).isEqualTo(PUBLIC_TRANSACTION.getValue());
|
||||
assertThat(onChainPrivacyMarkerTransaction.getTo().get()).isEqualTo(Address.ONCHAIN_PRIVACY);
|
||||
verify(enclave)
|
||||
.send(anyString(), eq(ENCLAVE_PUBLIC_KEY), eq(singletonList(ENCLAVE_PUBLIC_KEY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContractCodeCallsPrivateWorldStateReader() {
|
||||
final Hash blockHash = Hash.ZERO;
|
||||
|
||||
@@ -232,7 +232,12 @@ public class MultiTenancyPrivacyControllerTest {
|
||||
public void findsPrivacyGroupWhenEnclavePublicKeyInAddresses() {
|
||||
final List<String> addresses = List.of(ENCLAVE_PUBLIC_KEY1, ENCLAVE_PUBLIC_KEY2);
|
||||
final PrivacyGroup privacyGroup =
|
||||
new PrivacyGroup(PRIVACY_GROUP_ID, Type.PANTHEON, "", "", List.of(ENCLAVE_PUBLIC_KEY2));
|
||||
new PrivacyGroup(
|
||||
PRIVACY_GROUP_ID,
|
||||
Type.PANTHEON,
|
||||
"",
|
||||
"",
|
||||
List.of(ENCLAVE_PUBLIC_KEY1, ENCLAVE_PUBLIC_KEY2));
|
||||
when(privacyController.findPrivacyGroup(addresses, ENCLAVE_PUBLIC_KEY1))
|
||||
.thenReturn(new PrivacyGroup[] {privacyGroup});
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ public class PrivateTransactionDataFixture {
|
||||
|
||||
private static final BlockDataGenerator blockDataGenerator = new BlockDataGenerator();
|
||||
|
||||
static Transaction privacyMarkerTransaction(final String transactionKey) {
|
||||
public static Transaction privacyMarkerTransaction(final String transactionKey) {
|
||||
return Transaction.builder()
|
||||
.nonce(0)
|
||||
.gasPrice(Wei.of(1000))
|
||||
@@ -51,7 +51,7 @@ public class PrivateTransactionDataFixture {
|
||||
.signAndBuild(KEY_PAIR);
|
||||
}
|
||||
|
||||
static PrivateTransaction privateTransaction(final String privacyGroupId) {
|
||||
public static PrivateTransaction privateTransaction(final String privacyGroupId) {
|
||||
return PrivateTransaction.builder()
|
||||
.nonce(0)
|
||||
.gasPrice(Wei.of(1000))
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockImporter;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
@@ -84,4 +85,10 @@ public class NoRewardProtocolScheduleWrapper<C> implements ProtocolSchedule<C> {
|
||||
public void setTransactionFilter(final TransactionFilter transactionFilter) {
|
||||
delegate.setTransactionFilter(transactionFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPublicWorldStateArchiveForPrivacyBlockProcessor(
|
||||
final WorldStateArchive publicWorldStateArchive) {
|
||||
delegate.setPublicWorldStateArchiveForPrivacyBlockProcessor(publicWorldStateArchive);
|
||||
}
|
||||
}
|
||||
|
||||
24
privacy-contracts/build.gradle
Normal file
24
privacy-contracts/build.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
jar { enabled = true }
|
||||
dependencies {
|
||||
implementation 'net.consensys:orion'
|
||||
implementation 'org.apache.tuweni:tuweni-bytes'
|
||||
implementation 'org.apache.tuweni:tuweni-io'
|
||||
implementation 'org.apache.tuweni:tuweni-toml'
|
||||
implementation 'org.web3j:abi'
|
||||
implementation 'org.web3j:besu'
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
/*
|
||||
* 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.privacy.contracts.generated;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import io.reactivex.Flowable;
|
||||
import io.reactivex.functions.Function;
|
||||
import org.web3j.abi.EventEncoder;
|
||||
import org.web3j.abi.TypeReference;
|
||||
import org.web3j.abi.datatypes.Bool;
|
||||
import org.web3j.abi.datatypes.DynamicArray;
|
||||
import org.web3j.abi.datatypes.Event;
|
||||
import org.web3j.abi.datatypes.Type;
|
||||
import org.web3j.abi.datatypes.Utf8String;
|
||||
import org.web3j.abi.datatypes.generated.Bytes32;
|
||||
import org.web3j.abi.datatypes.generated.Int256;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.DefaultBlockParameter;
|
||||
import org.web3j.protocol.core.RemoteCall;
|
||||
import org.web3j.protocol.core.RemoteFunctionCall;
|
||||
import org.web3j.protocol.core.methods.request.EthFilter;
|
||||
import org.web3j.protocol.core.methods.response.BaseEventResponse;
|
||||
import org.web3j.protocol.core.methods.response.Log;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.tx.Contract;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
import org.web3j.tx.gas.ContractGasProvider;
|
||||
|
||||
/**
|
||||
* Auto generated code.
|
||||
*
|
||||
* <p><strong>Do not modify!</strong>
|
||||
*
|
||||
* <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
|
||||
* or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the <a
|
||||
* href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
|
||||
*
|
||||
* <p>Generated with web3j versionimport static org.junit.Assert.assertThat; 4.5.14.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PrivacyGroup extends Contract {
|
||||
public static final String BINARY =
|
||||
"608060405234801561001057600080fd5b50610954806100206000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806378b903371161005b57806378b9033714610173578063a69df4b514610195578063f744b0891461019f578063f83d08ba146102795761007d565b80630b0235be146100825780630d8e6e2c1461010557806361544c9114610123575b600080fd5b6100ae6004803603602081101561009857600080fd5b8101908080359060200190929190505050610283565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156100f15780820151818401526020810190506100d6565b505050509050019250505060405180910390f35b61010d6102ef565b6040518082815260200191505060405180910390f35b6101596004803603604081101561013957600080fd5b8101908080359060200190929190803590602001909291905050506102f9565b604051808215151515815260200191505060405180910390f35b61017b61031e565b604051808215151515815260200191505060405180910390f35b61019d610334565b005b61025f600480360360408110156101b557600080fd5b8101908080359060200190929190803590602001906401000000008111156101dc57600080fd5b8201836020820111156101ee57600080fd5b8035906020019184602083028401116401000000008311171561021057600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050610369565b604051808215151515815260200191505060405180910390f35b6102816103f3565b005b606061028e82610427565b61029757600080fd5b60028054806020026020016040519081016040528092919081815260200182805480156102e357602002820191906000526020600020905b8154815260200190600101908083116102cf575b50505050509050919050565b6000600154905090565b600061030483610427565b61030d57600080fd5b61031682610447565b905092915050565b60008060009054906101000a900460ff16905090565b6000809054906101000a900460ff161561034d57600080fd5b60016000806101000a81548160ff021916908315150217905550565b60008060009054906101000a900460ff161561038457600080fd5b6000600280549050141561039d5761039b8361052a565b505b6103a683610427565b6103af57600080fd5b60006103bb848461059c565b905060016000806101000a81548160ff0219169083151502179055506001600081548092919060010191905055508091505092915050565b6000809054906101000a900460ff1661040b57600080fd5b60008060006101000a81548160ff021916908315150217905550565b600080600360008481526020019081526020016000205414159050919050565b6000806003600084815260200190815260200160002054905060008111801561047557506002805490508111155b1561051f5760028054905081146104e357600060026001600280549050038154811061049d57fe5b9060005260206000200154905080600260018403815481106104bb57fe5b9060005260206000200181905550816003600083815260200190815260200160002081905550505b60016002818180549050039150816104fb919061087e565b50600060036000858152602001908152602001600020819055506001915050610525565b60009150505b919050565b600080600360008481526020019081526020016000205414156105925760028290806001815401808255809150509060018203906000526020600020016000909192909190915055600360008481526020019081526020016000208190555060019050610597565b600090505b919050565b6000806001905060008090505b8351811015610873578381815181106105be57fe5b6020026020010151851415610652577fcc7365305ae5f16c463d1383713d699f43c5548bbda5537ee61373ceb9aaf21360008583815181106105fc57fe5b60200260200101516040518083151515158152602001828152602001806020018281038252602f8152602001806108f1602f9139604001935050505060405180910390a181801561064b575060005b9150610866565b61066e84828151811061066157fe5b6020026020010151610427565b15610715577fcc7365305ae5f16c463d1383713d699f43c5548bbda5537ee61373ceb9aaf21360008583815181106106a257fe5b60200260200101516040518083151515158152602001828152602001806020018281038252601b8152602001807f4163636f756e7420697320616c72656164792061204d656d6265720000000000815250602001935050505060405180910390a181801561070e575060005b9150610865565b600061073385838151811061072657fe5b602002602001015161052a565b9050606081610777576040518060400160405280601b81526020017f4163636f756e7420697320616c72656164792061204d656d6265720000000000815250610791565b6040518060600160405280602181526020016108d0602191395b90507fcc7365305ae5f16c463d1383713d699f43c5548bbda5537ee61373ceb9aaf213828785815181106107c157fe5b602002602001015183604051808415151515815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561081a5780820151818401526020810190506107ff565b50505050905090810190601f1680156108475780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a18380156108605750815b935050505b5b80806001019150506105a9565b508091505092915050565b8154818355818111156108a5578183600052602060002091820191016108a491906108aa565b5b505050565b6108cc91905b808211156108c85760008160009055506001016108b0565b5090565b9056fe4d656d626572206163636f756e74206164646564207375636365737366756c6c79416464696e67206f776e206163636f756e742061732061204d656d626572206973206e6f74207065726d6974746564a265627a7a723158209b8cbc4503135a6654e90319f2db6e4559f25bd3ae7c55c69c7c7c52670c67fa64736f6c634300050c0032";
|
||||
|
||||
public static final String FUNC_ADDPARTICIPANTS = "addParticipants";
|
||||
|
||||
public static final String FUNC_CANEXECUTE = "canExecute";
|
||||
|
||||
public static final String FUNC_GETPARTICIPANTS = "getParticipants";
|
||||
|
||||
public static final String FUNC_GETVERSION = "getVersion";
|
||||
|
||||
public static final String FUNC_LOCK = "lock";
|
||||
|
||||
public static final String FUNC_REMOVEPARTICIPANT = "removeParticipant";
|
||||
|
||||
public static final String FUNC_UNLOCK = "unlock";
|
||||
|
||||
public static final Event PARTICIPANTADDED_EVENT =
|
||||
new Event(
|
||||
"ParticipantAdded",
|
||||
Arrays.<TypeReference<?>>asList(
|
||||
new TypeReference<Bool>() {},
|
||||
new TypeReference<Bytes32>() {},
|
||||
new TypeReference<Utf8String>() {}));;
|
||||
|
||||
@Deprecated
|
||||
protected PrivacyGroup(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
protected PrivacyGroup(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected PrivacyGroup(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
protected PrivacyGroup(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
|
||||
}
|
||||
|
||||
public List<ParticipantAddedEventResponse> getParticipantAddedEvents(
|
||||
TransactionReceipt transactionReceipt) {
|
||||
List<Contract.EventValuesWithLog> valueList =
|
||||
extractEventParametersWithLog(PARTICIPANTADDED_EVENT, transactionReceipt);
|
||||
ArrayList<ParticipantAddedEventResponse> responses =
|
||||
new ArrayList<ParticipantAddedEventResponse>(valueList.size());
|
||||
for (Contract.EventValuesWithLog eventValues : valueList) {
|
||||
ParticipantAddedEventResponse typedResponse = new ParticipantAddedEventResponse();
|
||||
typedResponse.log = eventValues.getLog();
|
||||
typedResponse.adminAdded = (Boolean) eventValues.getNonIndexedValues().get(0).getValue();
|
||||
typedResponse.account = (byte[]) eventValues.getNonIndexedValues().get(1).getValue();
|
||||
typedResponse.message = (String) eventValues.getNonIndexedValues().get(2).getValue();
|
||||
responses.add(typedResponse);
|
||||
}
|
||||
return responses;
|
||||
}
|
||||
|
||||
public Flowable<ParticipantAddedEventResponse> participantAddedEventFlowable(EthFilter filter) {
|
||||
return web3j
|
||||
.ethLogFlowable(filter)
|
||||
.map(
|
||||
new Function<Log, ParticipantAddedEventResponse>() {
|
||||
@Override
|
||||
public ParticipantAddedEventResponse apply(Log log) {
|
||||
Contract.EventValuesWithLog eventValues =
|
||||
extractEventParametersWithLog(PARTICIPANTADDED_EVENT, log);
|
||||
ParticipantAddedEventResponse typedResponse = new ParticipantAddedEventResponse();
|
||||
typedResponse.log = log;
|
||||
typedResponse.adminAdded =
|
||||
(Boolean) eventValues.getNonIndexedValues().get(0).getValue();
|
||||
typedResponse.account =
|
||||
(byte[]) eventValues.getNonIndexedValues().get(1).getValue();
|
||||
typedResponse.message =
|
||||
(String) eventValues.getNonIndexedValues().get(2).getValue();
|
||||
return typedResponse;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Flowable<ParticipantAddedEventResponse> participantAddedEventFlowable(
|
||||
DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) {
|
||||
EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress());
|
||||
filter.addSingleTopic(EventEncoder.encode(PARTICIPANTADDED_EVENT));
|
||||
return participantAddedEventFlowable(filter);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> addParticipants(
|
||||
byte[] _enclaveKey, List<byte[]> _accounts) {
|
||||
final org.web3j.abi.datatypes.Function function =
|
||||
new org.web3j.abi.datatypes.Function(
|
||||
FUNC_ADDPARTICIPANTS,
|
||||
Arrays.<Type>asList(
|
||||
new org.web3j.abi.datatypes.generated.Bytes32(_enclaveKey),
|
||||
new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Bytes32>(
|
||||
org.web3j.abi.datatypes.generated.Bytes32.class,
|
||||
org.web3j.abi.Utils.typeMap(
|
||||
_accounts, org.web3j.abi.datatypes.generated.Bytes32.class))),
|
||||
Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<Boolean> canExecute() {
|
||||
final org.web3j.abi.datatypes.Function function =
|
||||
new org.web3j.abi.datatypes.Function(
|
||||
FUNC_CANEXECUTE,
|
||||
Arrays.<Type>asList(),
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<Bool>() {}));
|
||||
return executeRemoteCallSingleValueReturn(function, Boolean.class);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<List> getParticipants(byte[] _enclaveKey) {
|
||||
final org.web3j.abi.datatypes.Function function =
|
||||
new org.web3j.abi.datatypes.Function(
|
||||
FUNC_GETPARTICIPANTS,
|
||||
Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Bytes32(_enclaveKey)),
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<Bytes32>>() {}));
|
||||
return new RemoteFunctionCall<List>(
|
||||
function,
|
||||
new Callable<List>() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List call() throws Exception {
|
||||
List<Type> result = (List<Type>) executeCallSingleValueReturn(function, List.class);
|
||||
return convertToNative(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<BigInteger> getVersion() {
|
||||
final org.web3j.abi.datatypes.Function function =
|
||||
new org.web3j.abi.datatypes.Function(
|
||||
FUNC_GETVERSION,
|
||||
Arrays.<Type>asList(),
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<Int256>() {}));
|
||||
return executeRemoteCallSingleValueReturn(function, BigInteger.class);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> lock() {
|
||||
final org.web3j.abi.datatypes.Function function =
|
||||
new org.web3j.abi.datatypes.Function(
|
||||
FUNC_LOCK, Arrays.<Type>asList(), Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> removeParticipant(
|
||||
byte[] _enclaveKey, byte[] _account) {
|
||||
final org.web3j.abi.datatypes.Function function =
|
||||
new org.web3j.abi.datatypes.Function(
|
||||
FUNC_REMOVEPARTICIPANT,
|
||||
Arrays.<Type>asList(
|
||||
new org.web3j.abi.datatypes.generated.Bytes32(_enclaveKey),
|
||||
new org.web3j.abi.datatypes.generated.Bytes32(_account)),
|
||||
Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> unlock() {
|
||||
final org.web3j.abi.datatypes.Function function =
|
||||
new org.web3j.abi.datatypes.Function(
|
||||
FUNC_UNLOCK, Arrays.<Type>asList(), Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static PrivacyGroup load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
return new PrivacyGroup(contractAddress, web3j, credentials, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static PrivacyGroup load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
return new PrivacyGroup(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
public static PrivacyGroup load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
return new PrivacyGroup(contractAddress, web3j, credentials, contractGasProvider);
|
||||
}
|
||||
|
||||
public static PrivacyGroup load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
return new PrivacyGroup(contractAddress, web3j, transactionManager, contractGasProvider);
|
||||
}
|
||||
|
||||
public static RemoteCall<PrivacyGroup> deploy(
|
||||
Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
|
||||
return deployRemoteCall(
|
||||
PrivacyGroup.class, web3j, credentials, contractGasProvider, BINARY, "");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static RemoteCall<PrivacyGroup> deploy(
|
||||
Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
|
||||
return deployRemoteCall(PrivacyGroup.class, web3j, credentials, gasPrice, gasLimit, BINARY, "");
|
||||
}
|
||||
|
||||
public static RemoteCall<PrivacyGroup> deploy(
|
||||
Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
|
||||
return deployRemoteCall(
|
||||
PrivacyGroup.class, web3j, transactionManager, contractGasProvider, BINARY, "");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static RemoteCall<PrivacyGroup> deploy(
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
return deployRemoteCall(
|
||||
PrivacyGroup.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, "");
|
||||
}
|
||||
|
||||
public static class ParticipantAddedEventResponse extends BaseEventResponse {
|
||||
public Boolean adminAdded;
|
||||
|
||||
public byte[] account;
|
||||
|
||||
public String message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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.privacy.contracts.generated;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.web3j.abi.TypeReference;
|
||||
import org.web3j.abi.datatypes.Bool;
|
||||
import org.web3j.abi.datatypes.DynamicArray;
|
||||
import org.web3j.abi.datatypes.Function;
|
||||
import org.web3j.abi.datatypes.Type;
|
||||
import org.web3j.abi.datatypes.generated.Bytes32;
|
||||
import org.web3j.abi.datatypes.generated.Int256;
|
||||
import org.web3j.crypto.Credentials;
|
||||
import org.web3j.protocol.Web3j;
|
||||
import org.web3j.protocol.core.RemoteCall;
|
||||
import org.web3j.protocol.core.RemoteFunctionCall;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
import org.web3j.tx.Contract;
|
||||
import org.web3j.tx.TransactionManager;
|
||||
import org.web3j.tx.gas.ContractGasProvider;
|
||||
|
||||
/**
|
||||
* Auto generated code.
|
||||
*
|
||||
* <p><strong>Do not modify!</strong>
|
||||
*
|
||||
* <p>Please use the <a href="https://docs.web3j.io/command_line.html">web3j command line tools</a>,
|
||||
* or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the <a
|
||||
* href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
|
||||
*
|
||||
* <p>Generated with web3j version 4.5.14.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class PrivacyInterface extends Contract {
|
||||
public static final String BINARY = "";
|
||||
|
||||
public static final String FUNC_ADDPARTICIPANTS = "addParticipants";
|
||||
|
||||
public static final String FUNC_CANEXECUTE = "canExecute";
|
||||
|
||||
public static final String FUNC_GETPARTICIPANTS = "getParticipants";
|
||||
|
||||
public static final String FUNC_GETVERSION = "getVersion";
|
||||
|
||||
public static final String FUNC_LOCK = "lock";
|
||||
|
||||
public static final String FUNC_REMOVEPARTICIPANT = "removeParticipant";
|
||||
|
||||
public static final String FUNC_UNLOCK = "unlock";
|
||||
|
||||
@Deprecated
|
||||
protected PrivacyInterface(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
protected PrivacyInterface(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected PrivacyInterface(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
protected PrivacyInterface(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> addParticipants(
|
||||
byte[] enclaveKey, List<byte[]> participants) {
|
||||
final Function function =
|
||||
new Function(
|
||||
FUNC_ADDPARTICIPANTS,
|
||||
Arrays.<Type>asList(
|
||||
new org.web3j.abi.datatypes.generated.Bytes32(enclaveKey),
|
||||
new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Bytes32>(
|
||||
org.web3j.abi.datatypes.generated.Bytes32.class,
|
||||
org.web3j.abi.Utils.typeMap(
|
||||
participants, org.web3j.abi.datatypes.generated.Bytes32.class))),
|
||||
Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<Boolean> canExecute() {
|
||||
final Function function =
|
||||
new Function(
|
||||
FUNC_CANEXECUTE,
|
||||
Arrays.<Type>asList(),
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<Bool>() {}));
|
||||
return executeRemoteCallSingleValueReturn(function, Boolean.class);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<List> getParticipants(byte[] enclaveKey) {
|
||||
final Function function =
|
||||
new Function(
|
||||
FUNC_GETPARTICIPANTS,
|
||||
Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Bytes32(enclaveKey)),
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<Bytes32>>() {}));
|
||||
return new RemoteFunctionCall<List>(
|
||||
function,
|
||||
new Callable<List>() {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List call() throws Exception {
|
||||
List<Type> result = (List<Type>) executeCallSingleValueReturn(function, List.class);
|
||||
return convertToNative(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<BigInteger> getVersion() {
|
||||
final Function function =
|
||||
new Function(
|
||||
FUNC_GETVERSION,
|
||||
Arrays.<Type>asList(),
|
||||
Arrays.<TypeReference<?>>asList(new TypeReference<Int256>() {}));
|
||||
return executeRemoteCallSingleValueReturn(function, BigInteger.class);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> lock() {
|
||||
final Function function =
|
||||
new Function(FUNC_LOCK, Arrays.<Type>asList(), Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> removeParticipant(
|
||||
byte[] enclaveKey, byte[] account) {
|
||||
final Function function =
|
||||
new Function(
|
||||
FUNC_REMOVEPARTICIPANT,
|
||||
Arrays.<Type>asList(
|
||||
new org.web3j.abi.datatypes.generated.Bytes32(enclaveKey),
|
||||
new org.web3j.abi.datatypes.generated.Bytes32(account)),
|
||||
Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
public RemoteFunctionCall<TransactionReceipt> unlock() {
|
||||
final Function function =
|
||||
new Function(FUNC_UNLOCK, Arrays.<Type>asList(), Collections.<TypeReference<?>>emptyList());
|
||||
return executeRemoteCallTransaction(function);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static PrivacyInterface load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
return new PrivacyInterface(contractAddress, web3j, credentials, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static PrivacyInterface load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
return new PrivacyInterface(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
|
||||
}
|
||||
|
||||
public static PrivacyInterface load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
Credentials credentials,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
return new PrivacyInterface(contractAddress, web3j, credentials, contractGasProvider);
|
||||
}
|
||||
|
||||
public static PrivacyInterface load(
|
||||
String contractAddress,
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
ContractGasProvider contractGasProvider) {
|
||||
return new PrivacyInterface(contractAddress, web3j, transactionManager, contractGasProvider);
|
||||
}
|
||||
|
||||
public static RemoteCall<PrivacyInterface> deploy(
|
||||
Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
|
||||
return deployRemoteCall(
|
||||
PrivacyInterface.class, web3j, credentials, contractGasProvider, BINARY, "");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static RemoteCall<PrivacyInterface> deploy(
|
||||
Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
|
||||
return deployRemoteCall(
|
||||
PrivacyInterface.class, web3j, credentials, gasPrice, gasLimit, BINARY, "");
|
||||
}
|
||||
|
||||
public static RemoteCall<PrivacyInterface> deploy(
|
||||
Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
|
||||
return deployRemoteCall(
|
||||
PrivacyInterface.class, web3j, transactionManager, contractGasProvider, BINARY, "");
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static RemoteCall<PrivacyInterface> deploy(
|
||||
Web3j web3j,
|
||||
TransactionManager transactionManager,
|
||||
BigInteger gasPrice,
|
||||
BigInteger gasLimit) {
|
||||
return deployRemoteCall(
|
||||
PrivacyInterface.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, "");
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,106 @@
|
||||
pragma solidity ^0.5.9;
|
||||
import "./OnChainPrivacyGroupManagementInterface.sol";
|
||||
|
||||
contract DefaultOnChainPrivacyGroupManagementContract is OnChainPrivacyGroupManagementInterface {
|
||||
|
||||
bool private _canExecute;
|
||||
int private _version;
|
||||
bytes32[] private distributionList;
|
||||
mapping(bytes32 => uint256) private distributionIndexOf;
|
||||
|
||||
function getVersion() external view returns (int) {
|
||||
return _version;
|
||||
}
|
||||
|
||||
// overrides
|
||||
function canExecute() external view returns (bool) {
|
||||
return _canExecute;
|
||||
}
|
||||
|
||||
function lock() public {
|
||||
require(_canExecute);
|
||||
_canExecute = false;
|
||||
}
|
||||
|
||||
function unlock() public {
|
||||
require(!_canExecute);
|
||||
_canExecute = true;
|
||||
}
|
||||
|
||||
function addParticipants(bytes32 _enclaveKey, bytes32[] memory _accounts) public returns (bool) {
|
||||
require(!_canExecute);
|
||||
if(distributionList.length == 0) {
|
||||
addParticipant(_enclaveKey);
|
||||
}
|
||||
require(isMember(_enclaveKey));
|
||||
bool result = addAll(_enclaveKey, _accounts);
|
||||
_canExecute = true;
|
||||
_version++;
|
||||
return result;
|
||||
}
|
||||
|
||||
function removeParticipant(bytes32 _enclaveKey, bytes32 _account) public returns (bool) {
|
||||
require(isMember(_enclaveKey));
|
||||
return removeInternal(_account);
|
||||
}
|
||||
|
||||
function getParticipants(bytes32 _enclaveKey) public view returns (bytes32[] memory) {
|
||||
require(isMember(_enclaveKey));
|
||||
return distributionList;
|
||||
}
|
||||
|
||||
|
||||
//internal functions
|
||||
function addAll(bytes32 _enclaveKey, bytes32[] memory _accounts) internal returns (bool) {
|
||||
bool allAdded = true;
|
||||
for (uint i = 0; i < _accounts.length; i++) {
|
||||
if (_enclaveKey == _accounts[i]) {
|
||||
emit ParticipantAdded(false, _accounts[i], "Adding own account as a Member is not permitted");
|
||||
allAdded = allAdded && false;
|
||||
} else if (isMember(_accounts[i])) {
|
||||
emit ParticipantAdded(false, _accounts[i], "Account is already a Member");
|
||||
allAdded = allAdded && false;
|
||||
} else {
|
||||
bool result = addParticipant(_accounts[i]);
|
||||
string memory message = result ? "Member account added successfully" : "Account is already a Member";
|
||||
emit ParticipantAdded(result, _accounts[i], message);
|
||||
allAdded = allAdded && result;
|
||||
}
|
||||
}
|
||||
return allAdded;
|
||||
}
|
||||
|
||||
function isMember(bytes32 _account) internal view returns (bool) {
|
||||
return distributionIndexOf[_account] != 0;
|
||||
}
|
||||
|
||||
function addParticipant(bytes32 _participant) internal returns (bool) {
|
||||
if (distributionIndexOf[_participant] == 0) {
|
||||
distributionIndexOf[_participant] = distributionList.push(_participant);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function removeInternal(bytes32 _participant) internal returns (bool) {
|
||||
uint256 index = distributionIndexOf[_participant];
|
||||
if (index > 0 && index <= distributionList.length) {
|
||||
//move last address into index being vacated (unless we are dealing with last index)
|
||||
if (index != distributionList.length) {
|
||||
bytes32 lastAccount = distributionList[distributionList.length - 1];
|
||||
distributionList[index - 1] = lastAccount;
|
||||
distributionIndexOf[lastAccount] = index;
|
||||
}
|
||||
distributionList.length -= 1;
|
||||
distributionIndexOf[_participant] = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
event ParticipantAdded(
|
||||
bool adminAdded,
|
||||
bytes32 account,
|
||||
string message
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
pragma solidity ^0.5.9;
|
||||
|
||||
interface OnChainPrivacyGroupManagementInterface {
|
||||
|
||||
function addParticipants(bytes32 enclaveKey, bytes32[] calldata participants) external returns (bool);
|
||||
|
||||
function removeParticipant(bytes32 enclaveKey, bytes32 account) external returns (bool);
|
||||
|
||||
function getParticipants(bytes32 enclaveKey) external view returns (bytes32[] memory);
|
||||
|
||||
function lock() external;
|
||||
|
||||
function unlock() external;
|
||||
|
||||
function canExecute() external view returns (bool);
|
||||
|
||||
function getVersion() external view returns (int);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
pragma solidity ^0.5.12;
|
||||
|
||||
import "./OnChainPrivacyGroupManagementInterface.sol";
|
||||
|
||||
contract OnChainPrivacyGroupManagementProxy is OnChainPrivacyGroupManagementInterface {
|
||||
|
||||
address public implementation;
|
||||
|
||||
constructor(address _implementation) public {
|
||||
implementation = _implementation;
|
||||
}
|
||||
|
||||
function upgradeTo(address _newImplementation) external {
|
||||
require(implementation != _newImplementation);
|
||||
_setImplementation(_newImplementation);
|
||||
}
|
||||
|
||||
function _setImplementation(address _newImp) internal {
|
||||
implementation = _newImp;
|
||||
}
|
||||
|
||||
function addParticipants(bytes32 enclaveKey, bytes32[] memory participants) public returns (bool) {
|
||||
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
|
||||
return privacyInterface.addParticipants(enclaveKey, participants);
|
||||
}
|
||||
|
||||
function getParticipants(bytes32 enclaveKey) view public returns (bytes32[] memory) {
|
||||
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
|
||||
return privacyInterface.getParticipants(enclaveKey);
|
||||
}
|
||||
|
||||
function removeParticipant(bytes32 enclaveKey, bytes32 account) public returns (bool) {
|
||||
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
|
||||
return privacyInterface.removeParticipant(enclaveKey, account);
|
||||
}
|
||||
|
||||
function lock() public {
|
||||
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
|
||||
return privacyInterface.lock();
|
||||
}
|
||||
|
||||
function unlock() public {
|
||||
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
|
||||
return privacyInterface.unlock();
|
||||
}
|
||||
|
||||
function canExecute() public view returns (bool) {
|
||||
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
|
||||
return privacyInterface.canExecute();
|
||||
}
|
||||
|
||||
function getVersion() public view returns (int) {
|
||||
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
|
||||
return privacyInterface.getVersion();
|
||||
}
|
||||
}
|
||||
25
privacy-contracts/src/main/solidity/generateWrappers.sh
Executable file
25
privacy-contracts/src/main/solidity/generateWrappers.sh
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
targets="
|
||||
OnChainPrivacyGroupManagementInterface
|
||||
DefaultOnChainPrivacyGroupManagementContract
|
||||
OnChainPrivacyGroupManagementProxy
|
||||
"
|
||||
|
||||
for target in ${targets}; do
|
||||
|
||||
solc --overwrite --bin --abi \
|
||||
-o build \
|
||||
${target}.sol
|
||||
|
||||
done
|
||||
|
||||
for target in ${targets}; do
|
||||
|
||||
web3j solidity generate \
|
||||
-b build/${target}.bin \
|
||||
-a build/${target}.abi \
|
||||
-o ../java \
|
||||
-p org.hyperledger.besu.privacy.contracts.generated
|
||||
|
||||
done
|
||||
@@ -47,3 +47,4 @@ include 'services:pipeline'
|
||||
include 'services:tasks'
|
||||
include 'testutil'
|
||||
include 'util'
|
||||
include 'privacy-contracts'
|
||||
|
||||
Reference in New Issue
Block a user