On chain group management api changes (#1288)

- On-chain group management changes: additional API to check whether someone is allowed to update the contract, and remove enclave key as a parameter to contract APIs
- Add check for group membership to onchain and offchain precompiled contracts

Signed-off-by: Stefan Pingel <stefan.pingel@consensys.net>
pull/1323/head
Stefan Pingel 4 years ago committed by GitHub
parent 1e7f3f8e86
commit 78c458b321
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      CHANGELOG.md
  2. 15
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/account/Accounts.java
  3. 13
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/transaction/AddToOnChainPrivacyGroupTransaction.java
  4. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/transaction/FindOnChainPrivacyGroupTransaction.java
  5. 8
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/transaction/LockOnChainPrivacyGroupTransaction.java
  6. 20
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/transaction/PrivacyTransactions.java
  7. 13
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/transaction/RemoveFromOnChainPrivacyGroupTransaction.java
  8. 80
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyRequestFactory.java
  9. 2
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/RunHelpTest.java
  10. 6
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/Ibft2PrivacyClusterAcceptanceTest.java
  11. 82
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/OnChainPrivacyAcceptanceTest.java
  12. 77
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/PrivacyGroupAcceptanceTest.java
  13. 117
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/contracts/PrivacyGroupTest.java
  14. 89
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/contracts/PrivacyProxyTest.java
  15. 10
      besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java
  16. 7
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java
  17. 7
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransaction.java
  18. 14
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java
  19. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetPrecompiledContractRegistries.java
  20. 134
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/OnChainPrivacyPrecompiledContract.java
  21. 31
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java
  22. 91
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java
  23. 16
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyController.java
  24. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyController.java
  25. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java
  26. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/group/OnChainGroupManagement.java
  27. 6
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/PrivateTransactionDataFixture.java
  28. 7
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/PrivateTransactionTestFixture.java
  29. 314
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/OnChainPrivacyPrecompiledContractTest.java
  30. 51
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java
  31. 42
      privacy-contracts/src/main/java/org/hyperledger/besu/privacy/contracts/generated/DefaultOnChainPrivacyGroupManagementContract.java
  32. 26
      privacy-contracts/src/main/java/org/hyperledger/besu/privacy/contracts/generated/OnChainPrivacyGroupManagementInterface.java
  33. 128
      privacy-contracts/src/main/java/org/hyperledger/besu/privacy/contracts/generated/OnChainPrivacyGroupManagementProxy.java
  34. 70
      privacy-contracts/src/main/solidity/DefaultOnChainPrivacyGroupManagementContract.sol
  35. 11
      privacy-contracts/src/main/solidity/OnChainPrivacyGroupManagementInterface.sol
  36. 43
      privacy-contracts/src/main/solidity/OnChainPrivacyGroupManagementProxy.sol

@ -17,6 +17,14 @@
- [Permissioning issues on Kubernetes](KNOWN_ISSUES.md#Kubernetes-permissioning-uses-Service-IPs-rather-than-pod-IPs-which-can-fail) - [Permissioning issues on Kubernetes](KNOWN_ISSUES.md#Kubernetes-permissioning-uses-Service-IPs-rather-than-pod-IPs-which-can-fail)
- [Restarts caused by insufficient memory can cause inconsistent private state](KNOWN_ISSUES.md#Restart-caused-by-insufficient-memory-can-cause-inconsistent-private-state) - [Restarts caused by insufficient memory can cause inconsistent private state](KNOWN_ISSUES.md#Restart-caused-by-insufficient-memory-can-cause-inconsistent-private-state)
### Breaking Changes
When upgrading to 1.5.3, ensure you've taken into account the following breaking changes.
#### Onchain Privacy Group Management
This early access feature was changed in a way that makes onchain privacy groups created with previous versions no longer usable.
## 1.5.2 ## 1.5.2
### Additions and Improvements ### Additions and Improvements

@ -21,6 +21,8 @@ public class Accounts {
public static final String GENESIS_ACCOUNT_ONE_PRIVATE_KEY = public static final String GENESIS_ACCOUNT_ONE_PRIVATE_KEY =
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"; "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63";
public static final String GENESIS_ACCOUNT_TWO_PRIVATE_KEY =
"c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3";
private final EthTransactions eth; private final EthTransactions eth;
private final Account richBenefactorOne; private final Account richBenefactorOne;
@ -31,20 +33,17 @@ public class Accounts {
richBenefactorOne = richBenefactorOne =
Account.fromPrivateKey(eth, "Rich Benefactor One", GENESIS_ACCOUNT_ONE_PRIVATE_KEY); Account.fromPrivateKey(eth, "Rich Benefactor One", GENESIS_ACCOUNT_ONE_PRIVATE_KEY);
richBenefactorTwo = richBenefactorTwo =
Account.fromPrivateKey( Account.fromPrivateKey(eth, "Rich Benefactor Two", GENESIS_ACCOUNT_TWO_PRIVATE_KEY);
eth,
"Rich Benefactor Two",
"c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3");
}
public Account getSecondaryBenefactor() {
return richBenefactorTwo;
} }
public Account getPrimaryBenefactor() { public Account getPrimaryBenefactor() {
return richBenefactorOne; return richBenefactorOne;
} }
public Account getSecondaryBenefactor() {
return richBenefactorTwo;
}
public Account createAccount(final String accountName) { public Account createAccount(final String accountName) {
return Account.create(eth, accountName); return Account.create(eth, accountName);
} }

@ -23,18 +23,23 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.web3j.protocol.exceptions.TransactionException; import org.web3j.crypto.Credentials;
import org.web3j.utils.Base64String; import org.web3j.utils.Base64String;
public class AddToOnChainPrivacyGroupTransaction implements Transaction<String> { public class AddToOnChainPrivacyGroupTransaction implements Transaction<String> {
private final Base64String privacyGroupId; private final Base64String privacyGroupId;
private final PrivacyNode adder; private final PrivacyNode adder;
private final List<String> addresses; private final List<String> addresses;
private final Credentials signer;
public AddToOnChainPrivacyGroupTransaction( public AddToOnChainPrivacyGroupTransaction(
final String privacyGroupId, final PrivacyNode adder, final PrivacyNode... nodes) { final String privacyGroupId,
final PrivacyNode adder,
final Credentials signer,
final PrivacyNode... nodes) {
this.privacyGroupId = Base64String.wrap(privacyGroupId); this.privacyGroupId = Base64String.wrap(privacyGroupId);
this.adder = adder; this.adder = adder;
this.signer = signer;
this.addresses = this.addresses =
Arrays.stream(nodes) Arrays.stream(nodes)
.map(n -> n.getOrion().getDefaultPublicKey()) .map(n -> n.getOrion().getDefaultPublicKey())
@ -44,8 +49,8 @@ public class AddToOnChainPrivacyGroupTransaction implements Transaction<String>
@Override @Override
public String execute(final NodeRequests node) { public String execute(final NodeRequests node) {
try { try {
return node.privacy().privxAddToPrivacyGroup(privacyGroupId, adder, addresses); return node.privacy().privxAddToPrivacyGroup(privacyGroupId, adder, signer, addresses);
} catch (final IOException | TransactionException e) { } catch (final IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

@ -36,7 +36,7 @@ public class FindOnChainPrivacyGroupTransaction
public List<PrivacyRequestFactory.OnChainPrivacyGroup> execute(final NodeRequests node) { public List<PrivacyRequestFactory.OnChainPrivacyGroup> execute(final NodeRequests node) {
try { try {
return node.privacy().privxFindOnChainPrivacyGroup(nodes).send().getGroups(); return node.privacy().privxFindOnChainPrivacyGroup(nodes).send().getGroups();
} catch (IOException e) { } catch (final IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

@ -20,22 +20,26 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException; import java.io.IOException;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.exceptions.TransactionException; import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.utils.Base64String; import org.web3j.utils.Base64String;
public class LockOnChainPrivacyGroupTransaction implements Transaction<String> { public class LockOnChainPrivacyGroupTransaction implements Transaction<String> {
private final Base64String privacyGroupId; private final Base64String privacyGroupId;
private final PrivacyNode locker; private final PrivacyNode locker;
private final Credentials signer;
public LockOnChainPrivacyGroupTransaction(final String privacyGroupId, final PrivacyNode locker) { public LockOnChainPrivacyGroupTransaction(
final String privacyGroupId, final PrivacyNode locker, final Credentials signer) {
this.privacyGroupId = Base64String.wrap(privacyGroupId); this.privacyGroupId = Base64String.wrap(privacyGroupId);
this.locker = locker; this.locker = locker;
this.signer = signer;
} }
@Override @Override
public String execute(final NodeRequests node) { public String execute(final NodeRequests node) {
try { try {
return node.privacy().privxLockPrivacyGroup(locker, privacyGroupId); return node.privacy().privxLockPrivacyGroup(locker, privacyGroupId, signer);
} catch (final IOException | TransactionException e) { } catch (final IOException | TransactionException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

@ -29,6 +29,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.filter.Priv
import java.util.List; import java.util.List;
import org.web3j.crypto.Credentials;
import org.web3j.tx.Contract; import org.web3j.tx.Contract;
public class PrivacyTransactions { public class PrivacyTransactions {
@ -49,13 +50,16 @@ public class PrivacyTransactions {
} }
public AddToOnChainPrivacyGroupTransaction addToPrivacyGroup( public AddToOnChainPrivacyGroupTransaction addToPrivacyGroup(
final String privacyGroupId, final PrivacyNode adder, final PrivacyNode... nodes) { final String privacyGroupId,
return new AddToOnChainPrivacyGroupTransaction(privacyGroupId, adder, nodes); final PrivacyNode adder,
final Credentials signer,
final PrivacyNode... nodes) {
return new AddToOnChainPrivacyGroupTransaction(privacyGroupId, adder, signer, nodes);
} }
public LockOnChainPrivacyGroupTransaction privxLockPrivacyGroupAndCheck( public LockOnChainPrivacyGroupTransaction privxLockPrivacyGroupAndCheck(
final String privacyGroupId, final PrivacyNode locker) { final String privacyGroupId, final PrivacyNode locker, final Credentials signer) {
return new LockOnChainPrivacyGroupTransaction(privacyGroupId, locker); return new LockOnChainPrivacyGroupTransaction(privacyGroupId, locker, signer);
} }
public FindPrivacyGroupTransaction findPrivacyGroup(final List<String> nodes) { public FindPrivacyGroupTransaction findPrivacyGroup(final List<String> nodes) {
@ -87,8 +91,12 @@ public class PrivacyTransactions {
} }
public RemoveFromOnChainPrivacyGroupTransaction removeFromPrivacyGroup( public RemoveFromOnChainPrivacyGroupTransaction removeFromPrivacyGroup(
final String privacyGroupId, final PrivacyNode remover, final PrivacyNode nodeToRemove) { final String privacyGroupId,
return new RemoveFromOnChainPrivacyGroupTransaction(privacyGroupId, remover, nodeToRemove); final PrivacyNode remover,
final Credentials signer,
final PrivacyNode nodeToRemove) {
return new RemoveFromOnChainPrivacyGroupTransaction(
privacyGroupId, remover, signer, nodeToRemove);
} }
public EeaSendRawTransactionTransaction sendRawTransaction(final String transaction) { public EeaSendRawTransactionTransaction sendRawTransaction(final String transaction) {

@ -20,26 +20,31 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
import java.io.IOException; import java.io.IOException;
import org.web3j.protocol.exceptions.TransactionException; import org.web3j.crypto.Credentials;
import org.web3j.utils.Base64String; import org.web3j.utils.Base64String;
public class RemoveFromOnChainPrivacyGroupTransaction implements Transaction<String> { public class RemoveFromOnChainPrivacyGroupTransaction implements Transaction<String> {
private final Base64String privacyGroupId; private final Base64String privacyGroupId;
private final PrivacyNode remover; private final PrivacyNode remover;
private final String toRemove; private final String toRemove;
private final Credentials signer;
public RemoveFromOnChainPrivacyGroupTransaction( public RemoveFromOnChainPrivacyGroupTransaction(
final String privacyGroupId, final PrivacyNode remover, final PrivacyNode toRemove) { final String privacyGroupId,
final PrivacyNode remover,
final Credentials signer,
final PrivacyNode toRemove) {
this.privacyGroupId = Base64String.wrap(privacyGroupId); this.privacyGroupId = Base64String.wrap(privacyGroupId);
this.remover = remover; this.remover = remover;
this.signer = signer;
this.toRemove = toRemove.getOrion().getDefaultPublicKey(); this.toRemove = toRemove.getOrion().getDefaultPublicKey();
} }
@Override @Override
public String execute(final NodeRequests node) { public String execute(final NodeRequests node) {
try { try {
return node.privacy().privxRemoveFromPrivacyGroup(privacyGroupId, remover, toRemove); return node.privacy().privxRemoveFromPrivacyGroup(privacyGroupId, remover, signer, toRemove);
} catch (IOException | TransactionException e) { } catch (final IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }

@ -49,6 +49,7 @@ import org.web3j.protocol.core.Response;
import org.web3j.protocol.core.methods.response.EthCall; import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthFilter; import org.web3j.protocol.core.methods.response.EthFilter;
import org.web3j.protocol.core.methods.response.EthLog; import org.web3j.protocol.core.methods.response.EthLog;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.EthUninstallFilter; import org.web3j.protocol.core.methods.response.EthUninstallFilter;
import org.web3j.protocol.eea.crypto.PrivateTransactionEncoder; import org.web3j.protocol.eea.crypto.PrivateTransactionEncoder;
import org.web3j.protocol.eea.crypto.RawPrivateTransaction; import org.web3j.protocol.eea.crypto.RawPrivateTransaction;
@ -127,18 +128,20 @@ public class PrivacyRequestFactory {
} }
public String privxAddToPrivacyGroup( public String privxAddToPrivacyGroup(
final Base64String privacyGroupId, final PrivacyNode adder, final List<String> addresses) final Base64String privacyGroupId,
throws IOException, TransactionException { final PrivacyNode adder,
final Credentials signer,
final List<String> addresses)
throws IOException {
final BigInteger nonce = final BigInteger nonce =
besuClient besuClient
.privGetTransactionCount(adder.getAddress().toHexString(), privacyGroupId) .privGetTransactionCount(signer.getAddress(), privacyGroupId)
.send() .send()
.getTransactionCount(); .getTransactionCount();
final Bytes payload = final Bytes payload =
encodeAddToGroupFunctionCall( encodeAddToGroupFunctionCall(
Bytes.fromBase64String(adder.getEnclaveKey()),
addresses.stream().map(Bytes::fromBase64String).collect(Collectors.toList())); addresses.stream().map(Bytes::fromBase64String).collect(Collectors.toList()));
final RawPrivateTransaction privateTransaction = final RawPrivateTransaction privateTransaction =
@ -154,26 +157,25 @@ public class PrivacyRequestFactory {
return besuClient return besuClient
.eeaSendRawTransaction( .eeaSendRawTransaction(
Numeric.toHexString( Numeric.toHexString(PrivateTransactionEncoder.signMessage(privateTransaction, signer)))
PrivateTransactionEncoder.signMessage(
privateTransaction, Credentials.create(adder.getTransactionSigningKey()))))
.send() .send()
.getTransactionHash(); .getTransactionHash();
} }
public String privxRemoveFromPrivacyGroup( public String privxRemoveFromPrivacyGroup(
final Base64String privacyGroupId, final PrivacyNode remover, final String toRemove) final Base64String privacyGroupId,
throws IOException, TransactionException { final PrivacyNode remover,
final Credentials signer,
final String toRemove)
throws IOException {
final BigInteger nonce = final BigInteger nonce =
besuClient besuClient
.privGetTransactionCount(remover.getAddress().toHexString(), privacyGroupId) .privGetTransactionCount(signer.getAddress(), privacyGroupId)
.send() .send()
.getTransactionCount(); .getTransactionCount();
final Bytes payload = final Bytes payload = encodeRemoveFromGroupFunctionCall(Bytes.fromBase64String(toRemove));
encodeRemoveFromGroupFunctionCall(
Bytes.fromBase64String(remover.getEnclaveKey()), Bytes.fromBase64String(toRemove));
final RawPrivateTransaction privateTransaction = final RawPrivateTransaction privateTransaction =
RawPrivateTransaction.createTransaction( RawPrivateTransaction.createTransaction(
@ -188,23 +190,21 @@ public class PrivacyRequestFactory {
return besuClient return besuClient
.eeaSendRawTransaction( .eeaSendRawTransaction(
Numeric.toHexString( Numeric.toHexString(PrivateTransactionEncoder.signMessage(privateTransaction, signer)))
PrivateTransactionEncoder.signMessage(
privateTransaction, Credentials.create(remover.getTransactionSigningKey()))))
.send() .send()
.getTransactionHash(); .getTransactionHash();
} }
private Bytes encodeRemoveFromGroupFunctionCall(final Bytes remover, final Bytes toRemove) { private Bytes encodeRemoveFromGroupFunctionCall(final Bytes toRemove) {
return Bytes.concatenate( return Bytes.concatenate(OnChainGroupManagement.REMOVE_PARTICIPANT_METHOD_SIGNATURE, toRemove);
OnChainGroupManagement.REMOVE_PARTICIPANT_METHOD_SIGNATURE, remover, toRemove);
} }
public String privxLockPrivacyGroup(final PrivacyNode locker, final Base64String privacyGroupId) public String privxLockPrivacyGroup(
final PrivacyNode locker, final Base64String privacyGroupId, final Credentials signer)
throws IOException, TransactionException { throws IOException, TransactionException {
final BigInteger nonce = final BigInteger nonce =
besuClient besuClient
.privGetTransactionCount(locker.getAddress().toHexString(), privacyGroupId) .privGetTransactionCount(signer.getAddress(), privacyGroupId)
.send() .send()
.getTransactionCount(); .getTransactionCount();
@ -223,8 +223,7 @@ public class PrivacyRequestFactory {
besuClient besuClient
.eeaSendRawTransaction( .eeaSendRawTransaction(
Numeric.toHexString( Numeric.toHexString(
PrivateTransactionEncoder.signMessage( PrivateTransactionEncoder.signMessage(privateTransaction, signer)))
privateTransaction, Credentials.create(locker.getTransactionSigningKey()))))
.send() .send()
.getTransactionHash(); .getTransactionHash();
@ -244,22 +243,13 @@ public class PrivacyRequestFactory {
secureRandom.nextBytes(bytes); secureRandom.nextBytes(bytes);
final Bytes privacyGroupId = Bytes.wrap(bytes); final Bytes privacyGroupId = Bytes.wrap(bytes);
final BigInteger nonce =
besuClient
.privGetTransactionCount(
creator.getAddress().toHexString(),
Base64String.wrap(privacyGroupId.toArrayUnsafe()))
.send()
.getTransactionCount();
final Bytes payload = final Bytes payload =
encodeAddToGroupFunctionCall( encodeAddToGroupFunctionCall(
Bytes.fromBase64String(creator.getEnclaveKey()),
addresses.stream().map(Bytes::fromBase64String).collect(Collectors.toList())); addresses.stream().map(Bytes::fromBase64String).collect(Collectors.toList()));
final RawPrivateTransaction privateTransaction = final RawPrivateTransaction privateTransaction =
RawPrivateTransaction.createTransaction( RawPrivateTransaction.createTransaction(
nonce, BigInteger.ZERO,
BigInteger.valueOf(1000), BigInteger.valueOf(1000),
BigInteger.valueOf(3000000), BigInteger.valueOf(3000000),
Address.ONCHAIN_PRIVACY_PROXY.toHexString(), Address.ONCHAIN_PRIVACY_PROXY.toHexString(),
@ -268,15 +258,12 @@ public class PrivacyRequestFactory {
Base64String.wrap(privacyGroupId.toArrayUnsafe()), Base64String.wrap(privacyGroupId.toArrayUnsafe()),
org.web3j.utils.Restriction.RESTRICTED); org.web3j.utils.Restriction.RESTRICTED);
final String transactionHash = final Request<?, EthSendTransaction> ethSendTransactionRequest =
besuClient besuClient.eeaSendRawTransaction(
.eeaSendRawTransaction( Numeric.toHexString(
Numeric.toHexString( PrivateTransactionEncoder.signMessage(
PrivateTransactionEncoder.signMessage( privateTransaction, Credentials.create(creator.getTransactionSigningKey()))));
privateTransaction, final String transactionHash = ethSendTransactionRequest.send().getTransactionHash();
Credentials.create(creator.getTransactionSigningKey()))))
.send()
.getTransactionHash();
return new PrivxCreatePrivacyGroupResponse(privacyGroupId.toBase64String(), transactionHash); return new PrivxCreatePrivacyGroupResponse(privacyGroupId.toBase64String(), transactionHash);
} }
@ -531,16 +518,13 @@ public class PrivacyRequestFactory {
} }
} }
private Bytes encodeAddToGroupFunctionCall( private Bytes encodeAddToGroupFunctionCall(final List<Bytes> participants) {
final Bytes privateFrom, final List<Bytes> participants) {
return Bytes.concatenate( return Bytes.concatenate(
OnChainGroupManagement.ADD_TO_GROUP_METHOD_SIGNATURE, OnChainGroupManagement.ADD_PARTICIPANTS_METHOD_SIGNATURE, encodeList(participants));
privateFrom,
encodeList(participants));
} }
private Bytes encodeList(final List<Bytes> participants) { private Bytes encodeList(final List<Bytes> participants) {
final Bytes dynamicParameterOffset = encodeLong(64); final Bytes dynamicParameterOffset = encodeLong(32);
final Bytes length = encodeLong(participants.size()); final Bytes length = encodeLong(participants.size());
return Bytes.concatenate( return Bytes.concatenate(
dynamicParameterOffset, dynamicParameterOffset,

@ -26,7 +26,7 @@ public class RunHelpTest extends AcceptanceTestBase {
@Test @Test
public void testShowsHelpAndExits() throws IOException { public void testShowsHelpAndExits() throws IOException {
BesuNode node = besu.runCommand("--help"); final BesuNode node = besu.runCommand("--help");
cluster.runNodeStart(node); cluster.runNodeStart(node);
WaitUtils.waitFor(5000, () -> node.verify(exitedSuccessfully)); WaitUtils.waitFor(5000, () -> node.verify(exitedSuccessfully));
} }

@ -144,16 +144,16 @@ public class Ibft2PrivacyClusterAcceptanceTest extends PrivacyAcceptanceTestBase
bob.getEnclaveKey())); bob.getEnclaveKey()));
// alice gets receipt from charlie's interaction // alice gets receipt from charlie's interaction
final PrivateTransactionReceipt firstExpectedReceipt = final PrivateTransactionReceipt aliceReceipt =
alice.execute(privacyTransactions.getPrivateTransactionReceipt(firstTransactionHash)); alice.execute(privacyTransactions.getPrivateTransactionReceipt(firstTransactionHash));
// verify bob and charlie have access to the same receipt // verify bob and charlie have access to the same receipt
bob.verify( bob.verify(
privateTransactionVerifier.validPrivateTransactionReceipt( privateTransactionVerifier.validPrivateTransactionReceipt(
firstTransactionHash, firstExpectedReceipt)); firstTransactionHash, aliceReceipt));
charlie.verify( charlie.verify(
privateTransactionVerifier.validPrivateTransactionReceipt( privateTransactionVerifier.validPrivateTransactionReceipt(
firstTransactionHash, firstExpectedReceipt)); firstTransactionHash, aliceReceipt));
// alice deploys second contract // alice deploys second contract
final String secondDeployedAddress = "0xebf56429e6500e84442467292183d4d621359838"; final String secondDeployedAddress = "0xebf56429e6500e84442467292183d4d621359838";

@ -39,6 +39,7 @@ import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt; import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthCall; import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.Log; import org.web3j.protocol.core.methods.response.Log;
@ -78,8 +79,8 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
@Test @Test
public void nodeCanCreatePrivacyGroup() { public void nodeCanCreatePrivacyGroup() {
final String privacyGroupId = createOnChainPrivacyGroup(alice, bob); final String privacyGroupId = createOnChainPrivacyGroup(alice);
checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob); checkOnChainPrivacyGroupExists(privacyGroupId, alice);
} }
@Test @Test
@ -100,8 +101,9 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(commitmentHash)); charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(commitmentHash));
lockPrivacyGroup(privacyGroupId, alice); final Credentials aliceCredentials = Credentials.create(alice.getTransactionSigningKey());
addMembersToPrivacyGroup(privacyGroupId, alice, charlie); lockPrivacyGroup(privacyGroupId, alice, aliceCredentials);
addMembersToPrivacyGroup(privacyGroupId, alice, aliceCredentials, charlie);
checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob, charlie); checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob, charlie);
charlie.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(commitmentHash)); charlie.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(commitmentHash));
@ -111,7 +113,9 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
public void removedMemberCannotSendTransactionToGroup() { public void removedMemberCannotSendTransactionToGroup() {
final String privacyGroupId = createOnChainPrivacyGroup(alice, bob); final String privacyGroupId = createOnChainPrivacyGroup(alice, bob);
final String removeHash = removeFromPrivacyGroup(privacyGroupId, alice, bob); final String removeHash =
removeFromPrivacyGroup(
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
bob.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(removeHash)); bob.verify(privateTransactionVerifier.existingPrivateTransactionReceipt(removeHash));
@ -126,8 +130,9 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
checkOnChainPrivacyGroupExists(privacyGroupId, alice); checkOnChainPrivacyGroupExists(privacyGroupId, alice);
lockPrivacyGroup(privacyGroupId, alice); lockPrivacyGroup(privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()));
addMembersToPrivacyGroup(privacyGroupId, alice, bob); addMembersToPrivacyGroup(
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob); checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob);
@ -142,7 +147,8 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
eventEmitter, eventEmitter,
valueSetWhileBobWasMember); valueSetWhileBobWasMember);
removeFromPrivacyGroup(privacyGroupId, alice, bob); removeFromPrivacyGroup(
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
checkOnChainPrivacyGroupExists(privacyGroupId, alice); checkOnChainPrivacyGroupExists(privacyGroupId, alice);
@ -159,8 +165,9 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
eventEmitter, eventEmitter,
valueSetWhileBobWasMember); // bob did not get the last transaction valueSetWhileBobWasMember); // bob did not get the last transaction
lockPrivacyGroup(privacyGroupId, alice); lockPrivacyGroup(privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()));
addMembersToPrivacyGroup(privacyGroupId, alice, bob); addMembersToPrivacyGroup(
privacyGroupId, alice, Credentials.create(alice.getTransactionSigningKey()), bob);
checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob); checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob);
@ -187,11 +194,11 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
final List<PrivacyNode> nodes, final List<PrivacyNode> nodes,
final String privacyGroupId, final String privacyGroupId,
final EventEmitter eventEmitter, final EventEmitter eventEmitter,
final int valueSetWhileBobWasMember) { final int value) {
final PrivateTransactionReceipt receiptWhileBobMember = final PrivateTransactionReceipt receiptWhileBobMember =
setEventEmitterValue(nodes, privacyGroupId, eventEmitter, valueSetWhileBobWasMember); setEventEmitterValue(nodes, privacyGroupId, eventEmitter, value);
checkEmitterValue(nodes, privacyGroupId, eventEmitter, valueSetWhileBobWasMember); checkEmitterValue(nodes, privacyGroupId, eventEmitter, value);
return receiptWhileBobMember; return receiptWhileBobMember;
} }
@ -222,7 +229,7 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
final List<PrivacyNode> nodes, final List<PrivacyNode> nodes,
final String privacyGroupId, final String privacyGroupId,
final EventEmitter eventEmitter, final EventEmitter eventEmitter,
final int valueWhileBobMember) { final int expectedValue) {
for (final PrivacyNode node : nodes) { for (final PrivacyNode node : nodes) {
final EthCall response = final EthCall response =
node.execute( node.execute(
@ -230,14 +237,14 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall())); privacyGroupId, eventEmitter, eventEmitter.value().encodeFunctionCall()));
assertThat(new BigInteger(response.getValue().substring(2), 16)) assertThat(new BigInteger(response.getValue().substring(2), 16))
.isEqualByComparingTo(BigInteger.valueOf(valueWhileBobMember)); .isEqualByComparingTo(BigInteger.valueOf(expectedValue));
} }
} }
@Test @Test
public void bobCanAddCharlieAfterBeingAddedByAlice() { public void bobCanAddCharlieAfterBeingAddedByAlice() {
final String privacyGroupId = createOnChainPrivacyGroup(alice); final String privacyGroupId = createOnChainPrivacyGroup(alice);
checkOnChainPrivacyGroupExists(privacyGroupId, alice);
final EventEmitter eventEmitter = final EventEmitter eventEmitter =
alice.execute( alice.execute(
privateContractTransactions.createSmartContractWithPrivacyGroupId( privateContractTransactions.createSmartContractWithPrivacyGroupId(
@ -252,14 +259,19 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
eventEmitter.getContractAddress(), alice.getAddress().toString()) eventEmitter.getContractAddress(), alice.getAddress().toString())
.verify(eventEmitter); .verify(eventEmitter);
final Credentials aliceCredentials = Credentials.create(alice.getTransactionSigningKey());
final String aliceLockHash = final String aliceLockHash =
alice.execute(privacyTransactions.privxLockPrivacyGroupAndCheck(privacyGroupId, alice)); alice.execute(
privacyTransactions.privxLockPrivacyGroupAndCheck(
privacyGroupId, alice, aliceCredentials));
alice.execute(privacyTransactions.addToPrivacyGroup(privacyGroupId, alice, bob)); alice.execute(
privacyTransactions.addToPrivacyGroup(privacyGroupId, alice, aliceCredentials, bob));
checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob); checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob);
bob.execute(privacyTransactions.privxLockPrivacyGroupAndCheck(privacyGroupId, bob)); bob.execute(
privacyTransactions.privxLockPrivacyGroupAndCheck(privacyGroupId, bob, aliceCredentials));
alice.execute(minerTransactions.minerStop()); alice.execute(minerTransactions.minerStop());
@ -278,7 +290,8 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
alice.getEnclaveKey(), alice.getEnclaveKey(),
privacyGroupId)); privacyGroupId));
final String bobAddHash = addMembersToPrivacyGroup(privacyGroupId, bob, charlie); final String bobAddHash =
addMembersToPrivacyGroup(privacyGroupId, bob, aliceCredentials, charlie);
alice alice
.getBesu() .getBesu()
@ -392,20 +405,20 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
privateTransactionVerifier.validPrivateTransactionReceipt( privateTransactionVerifier.validPrivateTransactionReceipt(
aliceStoreHash, expectedStoreReceipt)); aliceStoreHash, expectedStoreReceipt));
removeFromPrivacyGroup(privacyGroupId, bob, charlie); removeFromPrivacyGroup(privacyGroupId, bob, aliceCredentials, charlie);
checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob); checkOnChainPrivacyGroupExists(privacyGroupId, alice, bob);
} }
@Test @Test
public void addMembersToTwoGroupsInTheSameBlock() throws InterruptedException { public void addMembersToTwoGroupsInTheSameBlock() {
final String privacyGroupId1 = createOnChainPrivacyGroup(alice); final String privacyGroupId1 = createOnChainPrivacyGroup(alice);
final String privacyGroupId2 = createOnChainPrivacyGroup(bob); final String privacyGroupId2 = createOnChainPrivacyGroup(bob);
checkOnChainPrivacyGroupExists(privacyGroupId1, alice); checkOnChainPrivacyGroupExists(privacyGroupId1, alice);
checkOnChainPrivacyGroupExists(privacyGroupId2, bob); checkOnChainPrivacyGroupExists(privacyGroupId2, bob);
lockPrivacyGroup(privacyGroupId1, alice); lockPrivacyGroup(privacyGroupId1, alice, Credentials.create(alice.getTransactionSigningKey()));
lockPrivacyGroup(privacyGroupId2, bob); lockPrivacyGroup(privacyGroupId2, bob, Credentials.create(bob.getTransactionSigningKey()));
final BigInteger pendingTransactionFilterId = final BigInteger pendingTransactionFilterId =
alice.execute(ethTransactions.newPendingTransactionsFilter()); alice.execute(ethTransactions.newPendingTransactionsFilter());
@ -413,8 +426,12 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
alice.execute(minerTransactions.minerStop()); alice.execute(minerTransactions.minerStop());
alice.getBesu().verify(ethConditions.miningStatus(false)); alice.getBesu().verify(ethConditions.miningStatus(false));
final String aliceAddHash = addMembersToPrivacyGroup(privacyGroupId1, alice, charlie); final String aliceAddHash =
final String bobAddHash = addMembersToPrivacyGroup(privacyGroupId2, bob, alice); addMembersToPrivacyGroup(
privacyGroupId1, alice, Credentials.create(alice.getTransactionSigningKey()), charlie);
final String bobAddHash =
addMembersToPrivacyGroup(
privacyGroupId2, bob, Credentials.create(bob.getTransactionSigningKey()), alice);
alice alice
.getBesu() .getBesu()
@ -457,6 +474,7 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
* @return the id of the privacy group * @return the id of the privacy group
*/ */
private String createOnChainPrivacyGroup(final PrivacyNode... members) { private String createOnChainPrivacyGroup(final PrivacyNode... members) {
final PrivacyNode groupCreator = members[0]; final PrivacyNode groupCreator = members[0];
final CreateOnChainPrivacyGroupTransaction createTx = final CreateOnChainPrivacyGroupTransaction createTx =
@ -485,18 +503,21 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
private String addMembersToPrivacyGroup( private String addMembersToPrivacyGroup(
final String privacyGroupId, final String privacyGroupId,
final PrivacyNode nodeAddingMember, final PrivacyNode nodeAddingMember,
final Credentials signer,
final PrivacyNode... newMembers) { final PrivacyNode... newMembers) {
return nodeAddingMember.execute( return nodeAddingMember.execute(
privacyTransactions.addToPrivacyGroup(privacyGroupId, nodeAddingMember, newMembers)); privacyTransactions.addToPrivacyGroup(
privacyGroupId, nodeAddingMember, signer, newMembers));
} }
private String removeFromPrivacyGroup( private String removeFromPrivacyGroup(
final String privacyGroupId, final String privacyGroupId,
final PrivacyNode nodeRemovingMember, final PrivacyNode nodeRemovingMember,
final Credentials signer,
final PrivacyNode memberBeingRemoved) { final PrivacyNode memberBeingRemoved) {
return nodeRemovingMember.execute( return nodeRemovingMember.execute(
privacyTransactions.removeFromPrivacyGroup( privacyTransactions.removeFromPrivacyGroup(
privacyGroupId, nodeRemovingMember, memberBeingRemoved)); privacyGroupId, nodeRemovingMember, signer, memberBeingRemoved));
} }
private String calculateAddParticipantPMTHash( private String calculateAddParticipantPMTHash(
@ -564,9 +585,10 @@ public class OnChainPrivacyAcceptanceTest extends PrivacyAcceptanceTestBase {
* *
* @return the hash of the lock privacy group transaction * @return the hash of the lock privacy group transaction
*/ */
private String lockPrivacyGroup(final String privacyGroupId, final PrivacyNode member) { private String lockPrivacyGroup(
final String privacyGroupId, final PrivacyNode member, final Credentials signer) {
return member.execute( return member.execute(
privacyTransactions.privxLockPrivacyGroupAndCheck(privacyGroupId, member)); privacyTransactions.privxLockPrivacyGroupAndCheck(privacyGroupId, member, signer));
} }
private ExpectValidOnChainPrivacyGroupCreated onChainPrivacyGroupExists( private ExpectValidOnChainPrivacyGroupCreated onChainPrivacyGroupExists(

@ -18,10 +18,14 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase; 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.privacy.PrivacyNode;
import org.hyperledger.besu.tests.web3j.generated.EventEmitter;
import java.math.BigInteger;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.web3j.protocol.besu.response.privacy.PrivacyGroup; import org.web3j.protocol.besu.response.privacy.PrivacyGroup;
import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt;
import org.web3j.utils.Base64String; import org.web3j.utils.Base64String;
public class PrivacyGroupAcceptanceTest extends PrivacyAcceptanceTestBase { public class PrivacyGroupAcceptanceTest extends PrivacyAcceptanceTestBase {
@ -124,4 +128,77 @@ public class PrivacyGroupAcceptanceTest extends PrivacyAcceptanceTestBase {
bob.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected)); bob.verify(privateTransactionVerifier.validPrivacyGroupCreated(expected));
} }
@Test
public void canInteractWithMultiplePrivacyGroups() {
final String privacyGroupIdABC =
alice.execute(privacyTransactions.createPrivacyGroup(null, null, alice, bob, charlie));
final EventEmitter firstEventEmitter =
alice.execute(
privateContractTransactions.createSmartContractWithPrivacyGroupId(
EventEmitter.class,
alice.getTransactionSigningKey(),
POW_CHAIN_ID,
alice.getEnclaveKey(),
privacyGroupIdABC));
// charlie interacts with contract
final String firstTransactionHash =
charlie.execute(
privateContractTransactions.callSmartContractWithPrivacyGroupId(
firstEventEmitter.getContractAddress(),
firstEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
charlie.getTransactionSigningKey(),
POW_CHAIN_ID,
charlie.getEnclaveKey(),
privacyGroupIdABC));
// alice gets receipt from charlie's interaction
final PrivateTransactionReceipt firstExpectedReceipt =
alice.execute(privacyTransactions.getPrivateTransactionReceipt(firstTransactionHash));
// verify bob and charlie have access to the same receipt
bob.verify(
privateTransactionVerifier.validPrivateTransactionReceipt(
firstTransactionHash, firstExpectedReceipt));
charlie.verify(
privateTransactionVerifier.validPrivateTransactionReceipt(
firstTransactionHash, firstExpectedReceipt));
// alice deploys second contract
final String privacyGroupIdAB =
alice.execute(privacyTransactions.createPrivacyGroup(null, null, alice, bob));
final EventEmitter secondEventEmitter =
alice.execute(
privateContractTransactions.createSmartContractWithPrivacyGroupId(
EventEmitter.class,
alice.getTransactionSigningKey(),
POW_CHAIN_ID,
alice.getEnclaveKey(),
privacyGroupIdAB));
// bob interacts with contract
final String secondTransactionHash =
bob.execute(
privateContractTransactions.callSmartContractWithPrivacyGroupId(
secondEventEmitter.getContractAddress(),
secondEventEmitter.store(BigInteger.ONE).encodeFunctionCall(),
bob.getTransactionSigningKey(),
POW_CHAIN_ID,
bob.getEnclaveKey(),
privacyGroupIdAB));
// alice gets receipt from bob's interaction
final PrivateTransactionReceipt secondExpectedReceipt =
alice.execute(privacyTransactions.getPrivateTransactionReceipt(secondTransactionHash));
bob.verify(
privateTransactionVerifier.validPrivateTransactionReceipt(
secondTransactionHash, secondExpectedReceipt));
// charlie cannot see the receipt
charlie.verify(privateTransactionVerifier.noPrivateTransactionReceipt(secondTransactionHash));
}
} }

@ -20,11 +20,14 @@ import org.hyperledger.besu.privacy.contracts.generated.DefaultOnChainPrivacyGro
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.web3j.protocol.core.RemoteFunctionCall;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.exceptions.TransactionException; import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.utils.Base64String; import org.web3j.utils.Base64String;
@ -37,14 +40,13 @@ public class PrivacyGroupTest extends AcceptanceTestBase {
Base64String.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="); Base64String.wrap("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=");
private final Base64String thirdParticipant = private final Base64String thirdParticipant =
Base64String.wrap("Jo2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="); Base64String.wrap("Jo2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=");
private DefaultOnChainPrivacyGroupManagementContract privacyGroup; private DefaultOnChainPrivacyGroupManagementContract defaultPrivacyGroupManagementContract;
private static final String RAW_FIRST_PARTICIPANT = private static final String RAW_FIRST_PARTICIPANT = "0x5aa68ac0";
"0x0b0235be035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486a";
private static final String RAW_ADD_PARTICIPANT = private static final String RAW_ADD_PARTICIPANT =
"0xf744b089035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486a000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000012a8d9b56a0fe9cd94d60be4413bcb721d3a7be27ed8e28b3a6346df874ee141b"; "0xb4926e25000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000012a8d9b56a0fe9cd94d60be4413bcb721d3a7be27ed8e28b3a6346df874ee141b";
private static final String RAW_REMOVE_PARTICIPANT = private static final String RAW_REMOVE_PARTICIPANT =
"0x61544c91035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486a2a8d9b56a0fe9cd94d60be4413bcb721d3a7be27ed8e28b3a6346df874ee141b"; "0xfd0177972a8d9b56a0fe9cd94d60be4413bcb721d3a7be27ed8e28b3a6346df874ee141b";
private static final String RAW_LOCK = "0xf83d08ba"; private static final String RAW_LOCK = "0xf83d08ba";
private static final String RAW_UNLOCK = "0xa69df4b5"; private static final String RAW_UNLOCK = "0xa69df4b5";
private static final String RAW_CAN_EXECUTE = "0x78b90337"; private static final String RAW_CAN_EXECUTE = "0x78b90337";
@ -56,7 +58,7 @@ public class PrivacyGroupTest extends AcceptanceTestBase {
public void setUp() throws Exception { public void setUp() throws Exception {
minerNode = besu.createMinerNode("node"); minerNode = besu.createMinerNode("node");
cluster.start(minerNode); cluster.start(minerNode);
privacyGroup = defaultPrivacyGroupManagementContract =
minerNode.execute( minerNode.execute(
contractTransactions.createSmartContract( contractTransactions.createSmartContract(
DefaultOnChainPrivacyGroupManagementContract.class)); DefaultOnChainPrivacyGroupManagementContract.class));
@ -65,36 +67,43 @@ public class PrivacyGroupTest extends AcceptanceTestBase {
@Test @Test
public void rlp() throws Exception { public void rlp() throws Exception {
final String contractAddress = "0x42699a7612a82f1d9c36148af9c77354759b210b"; final String contractAddress = "0x42699a7612a82f1d9c36148af9c77354759b210b";
assertThat(privacyGroup.isValid()).isEqualTo(true); assertThat(defaultPrivacyGroupManagementContract.isValid()).isEqualTo(true);
contractVerifier.validTransactionReceipt(contractAddress).verify(privacyGroup); contractVerifier
.validTransactionReceipt(contractAddress)
.verify(defaultPrivacyGroupManagementContract);
// 0x0b0235be // 0x0b0235be
assertThat(RAW_FIRST_PARTICIPANT) assertThat(RAW_FIRST_PARTICIPANT)
.isEqualTo(privacyGroup.getParticipants(firstParticipant.raw()).encodeFunctionCall()); .isEqualTo(defaultPrivacyGroupManagementContract.getParticipants().encodeFunctionCall());
// 0xf744b089 // 0xf744b089
assertThat(RAW_ADD_PARTICIPANT) assertThat(RAW_ADD_PARTICIPANT)
.isEqualTo( .isEqualTo(
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants( .addParticipants(Collections.singletonList(secondParticipant.raw()))
firstParticipant.raw(), Collections.singletonList(secondParticipant.raw()))
.encodeFunctionCall()); .encodeFunctionCall());
// 0xf744b089 // 0xf744b089
assertThat(RAW_REMOVE_PARTICIPANT) assertThat(RAW_REMOVE_PARTICIPANT)
.isEqualTo( .isEqualTo(
privacyGroup defaultPrivacyGroupManagementContract
.removeParticipant(firstParticipant.raw(), secondParticipant.raw()) .removeParticipant(secondParticipant.raw())
.encodeFunctionCall()); .encodeFunctionCall());
assertThat(RAW_LOCK).isEqualTo(privacyGroup.lock().encodeFunctionCall()); assertThat(RAW_LOCK)
assertThat(RAW_UNLOCK).isEqualTo(privacyGroup.unlock().encodeFunctionCall()); .isEqualTo(defaultPrivacyGroupManagementContract.lock().encodeFunctionCall());
assertThat(RAW_CAN_EXECUTE).isEqualTo(privacyGroup.canExecute().encodeFunctionCall()); assertThat(RAW_UNLOCK)
assertThat(RAW_GET_VERSION).isEqualTo(privacyGroup.getVersion().encodeFunctionCall()); .isEqualTo(defaultPrivacyGroupManagementContract.unlock().encodeFunctionCall());
assertThat(RAW_CAN_EXECUTE)
.isEqualTo(defaultPrivacyGroupManagementContract.canExecute().encodeFunctionCall());
assertThat(RAW_GET_VERSION)
.isEqualTo(defaultPrivacyGroupManagementContract.getVersion().encodeFunctionCall());
} }
@Test @Test
public void canInitiallyAddParticipants() throws Exception { public void canInitiallyAddParticipants() throws Exception {
privacyGroup final RemoteFunctionCall<TransactionReceipt> transactionReceiptRemoteFunctionCall =
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) defaultPrivacyGroupManagementContract.addParticipants(
.send(); Arrays.asList(firstParticipant.raw(), secondParticipant.raw()));
final List<byte[]> participants = privacyGroup.getParticipants(firstParticipant.raw()).send(); transactionReceiptRemoteFunctionCall.send();
final List<byte[]> participants =
defaultPrivacyGroupManagementContract.getParticipants().send();
assertThat(participants.size()).isEqualTo(2); assertThat(participants.size()).isEqualTo(2);
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1)); assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
@ -102,51 +111,50 @@ public class PrivacyGroupTest extends AcceptanceTestBase {
@Test @Test
public void canRemoveParticipant() throws Exception { public void canRemoveParticipant() throws Exception {
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Arrays.asList(firstParticipant.raw(), secondParticipant.raw()))
.send(); .send();
final List<byte[]> participants = privacyGroup.getParticipants(firstParticipant.raw()).send(); final List<byte[]> participants =
defaultPrivacyGroupManagementContract.getParticipants().send();
assertThat(participants.size()).isEqualTo(2); assertThat(participants.size()).isEqualTo(2);
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1)); assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
privacyGroup.removeParticipant(firstParticipant.raw(), secondParticipant.raw()).send(); defaultPrivacyGroupManagementContract.removeParticipant(secondParticipant.raw()).send();
final List<byte[]> participantsAfterRemove = final List<byte[]> participantsAfterRemove =
privacyGroup.getParticipants(firstParticipant.raw()).send(); defaultPrivacyGroupManagementContract.getParticipants().send();
assertThat(participantsAfterRemove.size()).isEqualTo(1); assertThat(participantsAfterRemove.size()).isEqualTo(1);
assertThat(firstParticipant.raw()).isEqualTo(participantsAfterRemove.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participantsAfterRemove.get(0));
} }
@Test(expected = TransactionException.class) @Test(expected = TransactionException.class)
public void cannotAddToContractWhenNotLocked() throws Exception { public void cannotAddToContractWhenNotLocked() throws Exception {
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw())) .addParticipants(Collections.singletonList(thirdParticipant.raw()))
.send(); .send();
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Collections.singletonList(secondParticipant.raw()))
.send(); .send();
} }
@Test @Test
public void ensureContractIsNotLockedAfterDeploy() throws Exception { public void ensureContractIsLockedAfterDeploy() throws Exception {
privacyGroup.unlock().send(); assertThat(defaultPrivacyGroupManagementContract.canExecute().send()).isFalse();
assertThat(privacyGroup.canExecute().send()).isTrue();
} }
@Test @Test
public void ensurePrivacyGroupVersionIsAlwaysDifferent() throws Exception { public void ensurePrivacyGroupVersionIsAlwaysDifferent() throws Exception {
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Collections.singletonList(secondParticipant.raw()))
.send(); .send();
final byte[] version1 = privacyGroup.getVersion().send(); final byte[] version1 = defaultPrivacyGroupManagementContract.getVersion().send();
privacyGroup.lock().send(); defaultPrivacyGroupManagementContract.lock().send();
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw())) .addParticipants(Collections.singletonList(thirdParticipant.raw()))
.send(); .send();
final byte[] version2 = privacyGroup.getVersion().send(); final byte[] version2 = defaultPrivacyGroupManagementContract.getVersion().send();
privacyGroup.lock().send(); defaultPrivacyGroupManagementContract.removeParticipant(secondParticipant.raw()).send();
privacyGroup.removeParticipant(firstParticipant.raw(), secondParticipant.raw()).send(); final byte[] version3 = defaultPrivacyGroupManagementContract.getVersion().send();
final byte[] version3 = privacyGroup.getVersion().send();
assertThat(version1).isNotEqualTo(version2); assertThat(version1).isNotEqualTo(version2);
assertThat(version1).isNotEqualTo(version3); assertThat(version1).isNotEqualTo(version3);
@ -155,15 +163,16 @@ public class PrivacyGroupTest extends AcceptanceTestBase {
@Test @Test
public void canAddTwiceToContractWhenCallLock() throws Exception { public void canAddTwiceToContractWhenCallLock() throws Exception {
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw())) .addParticipants(Arrays.asList(firstParticipant.raw(), thirdParticipant.raw()))
.send(); .send();
privacyGroup.lock().send(); defaultPrivacyGroupManagementContract.lock().send();
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Collections.singletonList(secondParticipant.raw()))
.send(); .send();
final List<byte[]> participants = privacyGroup.getParticipants(firstParticipant.raw()).send(); final List<byte[]> participants =
defaultPrivacyGroupManagementContract.getParticipants().send();
assertThat(participants.size()).isEqualTo(3); assertThat(participants.size()).isEqualTo(3);
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
assertThat(thirdParticipant.raw()).isEqualTo(participants.get(1)); assertThat(thirdParticipant.raw()).isEqualTo(participants.get(1));
@ -172,10 +181,10 @@ public class PrivacyGroupTest extends AcceptanceTestBase {
@Test(expected = TransactionException.class) @Test(expected = TransactionException.class)
public void cannotLockTwice() throws Exception { public void cannotLockTwice() throws Exception {
privacyGroup defaultPrivacyGroupManagementContract
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw())) .addParticipants(Collections.singletonList(thirdParticipant.raw()))
.send(); .send();
privacyGroup.lock().send(); defaultPrivacyGroupManagementContract.lock().send();
privacyGroup.lock().send(); defaultPrivacyGroupManagementContract.lock().send();
} }
} }

@ -15,18 +15,26 @@
package org.hyperledger.besu.tests.web3j.privacy.contracts; package org.hyperledger.besu.tests.web3j.privacy.contracts;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.hyperledger.besu.privacy.contracts.generated.DefaultOnChainPrivacyGroupManagementContract; import org.hyperledger.besu.privacy.contracts.generated.DefaultOnChainPrivacyGroupManagementContract;
import org.hyperledger.besu.privacy.contracts.generated.OnChainPrivacyGroupManagementProxy; import org.hyperledger.besu.privacy.contracts.generated.OnChainPrivacyGroupManagementProxy;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Accounts;
import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.DefaultGasProvider;
import org.web3j.utils.Base64String; import org.web3j.utils.Base64String;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -40,25 +48,26 @@ public class PrivacyProxyTest extends AcceptanceTestBase {
Base64String.wrap("Jo2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="); Base64String.wrap("Jo2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=");
private OnChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy; private OnChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy;
private static final String RAW_FIRST_PARTICIPANT = private static final String RAW_GET_PARTICIPANTS = "0x5aa68ac0";
"0x0b0235bef772b2ee55f016431cefe724a05814324bb96e9afdb73e338665a693d4653678";
private static final String RAW_ADD_PARTICIPANT = private static final String RAW_ADD_PARTICIPANT =
"0xf744b089f772b2ee55f016431cefe724a05814324bb96e9afdb73e338665a693d465367800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000"; "0xb4926e2500000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000";
private BesuNode minerNode; private BesuNode minerNode;
private DefaultOnChainPrivacyGroupManagementContract defaultOnChainPrivacyGroupManagementContract;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
minerNode = besu.createMinerNode("node"); minerNode = besu.createMinerNode("node");
cluster.start(minerNode); cluster.start(minerNode);
DefaultOnChainPrivacyGroupManagementContract privacyGroup = defaultOnChainPrivacyGroupManagementContract =
minerNode.execute( minerNode.execute(
contractTransactions.createSmartContract( contractTransactions.createSmartContract(
DefaultOnChainPrivacyGroupManagementContract.class)); DefaultOnChainPrivacyGroupManagementContract.class));
onChainPrivacyGroupManagementProxy = onChainPrivacyGroupManagementProxy =
minerNode.execute( minerNode.execute(
contractTransactions.createSmartContract( contractTransactions.createSmartContract(
OnChainPrivacyGroupManagementProxy.class, privacyGroup.getContractAddress())); OnChainPrivacyGroupManagementProxy.class,
defaultOnChainPrivacyGroupManagementContract.getContractAddress()));
} }
@Test @Test
@ -67,47 +76,39 @@ public class PrivacyProxyTest extends AcceptanceTestBase {
contractVerifier contractVerifier
.validTransactionReceipt(onChainPrivacyGroupManagementProxy.getContractAddress()) .validTransactionReceipt(onChainPrivacyGroupManagementProxy.getContractAddress())
.verify(onChainPrivacyGroupManagementProxy); .verify(onChainPrivacyGroupManagementProxy);
// 0x0b0235be assertThat(RAW_GET_PARTICIPANTS)
assertThat(RAW_FIRST_PARTICIPANT) .isEqualTo(onChainPrivacyGroupManagementProxy.getParticipants().encodeFunctionCall());
.isEqualTo(
onChainPrivacyGroupManagementProxy
.getParticipants(firstParticipant.raw())
.encodeFunctionCall());
// 0xf744b089
assertThat(RAW_ADD_PARTICIPANT) assertThat(RAW_ADD_PARTICIPANT)
.isEqualTo( .isEqualTo(
onChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy
.addParticipants(firstParticipant.raw(), Collections.emptyList()) .addParticipants(Collections.emptyList())
.encodeFunctionCall()); .encodeFunctionCall());
} }
@Ignore("return 0x which causes web3j to throw exception instead of return empty list") @Ignore("return 0x which causes web3j to throw exception instead of return empty list")
@Test @Test
public void deploysWithNoParticipant() throws Exception { public void deploysWithNoParticipant() throws Exception {
final List<byte[]> participants = final List<byte[]> participants = onChainPrivacyGroupManagementProxy.getParticipants().send();
onChainPrivacyGroupManagementProxy.getParticipants(firstParticipant.raw()).send();
assertThat(participants.size()).isEqualTo(0); assertThat(participants.size()).isEqualTo(0);
} }
@Test @Test
public void canAddParticipants() throws Exception { public void canAddParticipants() throws Exception {
onChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Arrays.asList(firstParticipant.raw(), secondParticipant.raw()))
.send(); .send();
final List<byte[]> participants = final List<byte[]> participants = onChainPrivacyGroupManagementProxy.getParticipants().send();
onChainPrivacyGroupManagementProxy.getParticipants(firstParticipant.raw()).send();
assertThat(participants.size()).isEqualTo(2); assertThat(participants.size()).isEqualTo(2);
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1)); assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
} }
@Test @Test
public void canUpgrade() throws Exception { public void nonOwnerCannotUpgrade() throws Exception {
onChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Arrays.asList(firstParticipant.raw(), secondParticipant.raw()))
.send(); .send();
final List<byte[]> participants = final List<byte[]> participants = onChainPrivacyGroupManagementProxy.getParticipants().send();
onChainPrivacyGroupManagementProxy.getParticipants(firstParticipant.raw()).send();
assertThat(participants.size()).isEqualTo(2); assertThat(participants.size()).isEqualTo(2);
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1)); assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
@ -117,12 +118,43 @@ public class PrivacyProxyTest extends AcceptanceTestBase {
contractTransactions.createSmartContract( contractTransactions.createSmartContract(
DefaultOnChainPrivacyGroupManagementContract.class)); DefaultOnChainPrivacyGroupManagementContract.class));
onChainPrivacyGroupManagementProxy.upgradeTo(upgradedContract.getContractAddress()).send(); final HttpService httpService =
new HttpService(
"http://" + minerNode.getHostName() + ":" + minerNode.getJsonRpcSocketPort().get());
final Web3j web3j = Web3j.build(httpService);
// load the proxy contract, use it with another signer
final OnChainPrivacyGroupManagementProxy proxyContractAccount2 =
OnChainPrivacyGroupManagementProxy.load(
onChainPrivacyGroupManagementProxy.getContractAddress(),
web3j,
Credentials.create(Accounts.GENESIS_ACCOUNT_TWO_PRIVATE_KEY),
new DefaultGasProvider());
// contract is the proxy contract and uses genesis account 2. It should not be able to upgrade
// the contract, because it is not the owner of "upgradedContract"
assertThatThrownBy(
() -> proxyContractAccount2.upgradeTo(upgradedContract.getContractAddress()).send())
.isInstanceOf(TransactionException.class);
}
@Test
public void ownerCanUpgrade() throws Exception {
onChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Arrays.asList(firstParticipant.raw(), secondParticipant.raw()))
.send(); .send();
final List<byte[]> participants = onChainPrivacyGroupManagementProxy.getParticipants().send();
assertThat(participants.size()).isEqualTo(2);
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
assertThat(secondParticipant.raw()).isEqualTo(participants.get(1));
final DefaultOnChainPrivacyGroupManagementContract upgradedContract =
minerNode.execute(
contractTransactions.createSmartContract(
DefaultOnChainPrivacyGroupManagementContract.class));
onChainPrivacyGroupManagementProxy.upgradeTo(upgradedContract.getContractAddress()).send();
final List<byte[]> participantsAfterUpgrade = final List<byte[]> participantsAfterUpgrade =
onChainPrivacyGroupManagementProxy.getParticipants(firstParticipant.raw()).send(); onChainPrivacyGroupManagementProxy.getParticipants().send();
assertThat(participantsAfterUpgrade.size()).isEqualTo(2); assertThat(participantsAfterUpgrade.size()).isEqualTo(2);
assertThat(firstParticipant.raw()).isEqualTo(participantsAfterUpgrade.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participantsAfterUpgrade.get(0));
assertThat(secondParticipant.raw()).isEqualTo(participantsAfterUpgrade.get(1)); assertThat(secondParticipant.raw()).isEqualTo(participantsAfterUpgrade.get(1));
@ -131,14 +163,13 @@ public class PrivacyProxyTest extends AcceptanceTestBase {
@Test @Test
public void canAddTwiceToContractWhenCallLock() throws Exception { public void canAddTwiceToContractWhenCallLock() throws Exception {
onChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy
.addParticipants(firstParticipant.raw(), Collections.singletonList(thirdParticipant.raw())) .addParticipants(Arrays.asList(firstParticipant.raw(), thirdParticipant.raw()))
.send(); .send();
onChainPrivacyGroupManagementProxy.lock().send(); onChainPrivacyGroupManagementProxy.lock().send();
onChainPrivacyGroupManagementProxy onChainPrivacyGroupManagementProxy
.addParticipants(firstParticipant.raw(), Collections.singletonList(secondParticipant.raw())) .addParticipants(Collections.singletonList(secondParticipant.raw()))
.send(); .send();
final List<byte[]> participants = final List<byte[]> participants = onChainPrivacyGroupManagementProxy.getParticipants().send();
onChainPrivacyGroupManagementProxy.getParticipants(firstParticipant.raw()).send();
assertThat(participants.size()).isEqualTo(3); assertThat(participants.size()).isEqualTo(3);
assertThat(firstParticipant.raw()).isEqualTo(participants.get(0)); assertThat(firstParticipant.raw()).isEqualTo(participants.get(0));
assertThat(thirdParticipant.raw()).isEqualTo(participants.get(1)); assertThat(thirdParticipant.raw()).isEqualTo(participants.get(1));

@ -16,6 +16,9 @@ package org.hyperledger.besu;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH; import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.controller.BesuController;
@ -24,6 +27,7 @@ import org.hyperledger.besu.crypto.NodeKeyUtils;
import org.hyperledger.besu.crypto.SECP256K1; import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.enclave.Enclave; import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveFactory; import org.hyperledger.besu.enclave.EnclaveFactory;
import org.hyperledger.besu.enclave.types.PrivacyGroup;
import org.hyperledger.besu.enclave.types.SendResponse; import org.hyperledger.besu.enclave.types.SendResponse;
import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
@ -42,6 +46,7 @@ import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.privacy.DefaultPrivacyController;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.Restriction; import org.hyperledger.besu.ethereum.privacy.Restriction;
@ -69,6 +74,7 @@ import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
@ -130,6 +136,7 @@ public class PrivacyReorgTest {
private OrionTestHarness enclave; private OrionTestHarness enclave;
private PrivateStateRootResolver privateStateRootResolver; private PrivateStateRootResolver privateStateRootResolver;
private PrivacyParameters privacyParameters; private PrivacyParameters privacyParameters;
private DefaultPrivacyController privacyController;
@Before @Before
public void setUp() throws IOException { public void setUp() throws IOException {
@ -153,6 +160,9 @@ public class PrivacyReorgTest {
.setEnclaveFactory(new EnclaveFactory(Vertx.vertx())) .setEnclaveFactory(new EnclaveFactory(Vertx.vertx()))
.build(); .build();
privacyParameters.setEnclavePublicKey(ENCLAVE_PUBLIC_KEY.toBase64String()); privacyParameters.setEnclavePublicKey(ENCLAVE_PUBLIC_KEY.toBase64String());
privacyController = mock(DefaultPrivacyController.class);
when(privacyController.retrieveOffChainPrivacyGroup(any(), any()))
.thenReturn(Optional.of(new PrivacyGroup()));
privateStateRootResolver = privateStateRootResolver =
new PrivateStateRootResolver(privacyParameters.getPrivateStateStorage()); new PrivateStateRootResolver(privacyParameters.getPrivateStateStorage());

@ -93,10 +93,9 @@ public class EeaSendRawTransaction implements JsonRpcMethod {
return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE); return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE);
} }
maybePrivacyGroup = maybePrivacyGroup =
privacyController.retrieveOnChainPrivacyGroup( privacyController.retrieveOnChainPrivacyGroupWithToBeAddedMembers(
maybePrivacyGroupId.get(), enclavePublicKey); maybePrivacyGroupId.get(), enclavePublicKey, privateTransaction);
if (maybePrivacyGroup.isEmpty() if (maybePrivacyGroup.isEmpty()) {
&& !privacyController.isGroupAdditionTransaction(privateTransaction)) {
return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST); return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST);
} }
} else { // !onchainPirvacyGroupEnabled } else { // !onchainPirvacyGroupEnabled

@ -88,10 +88,9 @@ public class PrivDistributeRawTransaction implements JsonRpcMethod {
return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE); return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE);
} }
maybePrivacyGroup = maybePrivacyGroup =
privacyController.retrieveOnChainPrivacyGroup( privacyController.retrieveOnChainPrivacyGroupWithToBeAddedMembers(
maybePrivacyGroupId.get(), enclavePublicKey); maybePrivacyGroupId.get(), enclavePublicKey, privateTransaction);
if (maybePrivacyGroup.isEmpty() if (maybePrivacyGroup.isEmpty()) {
&& !privacyController.isGroupAdditionTransaction(privateTransaction)) {
return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST); return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST);
} }
} else { // !onchainPirvacyGroupEnabled } else { // !onchainPirvacyGroupEnabled

@ -50,7 +50,7 @@ import org.hyperledger.besu.ethereum.privacy.Restriction;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Collections; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -255,10 +255,12 @@ public class EeaSendRawTransactionTest {
when(privacyController.validatePrivateTransaction( when(privacyController.validatePrivateTransaction(
any(PrivateTransaction.class), any(String.class))) any(PrivateTransaction.class), any(String.class)))
.thenReturn(ValidationResult.valid()); .thenReturn(ValidationResult.valid());
when(privacyController.retrieveOnChainPrivacyGroup(any(Bytes.class), any(String.class))) final Optional<PrivacyGroup> optionalPrivacyGroup =
.thenReturn( Optional.of(
Optional.of( new PrivacyGroup(
new PrivacyGroup("", PrivacyGroup.Type.ONCHAIN, "", "", Collections.emptyList()))); "", PrivacyGroup.Type.ONCHAIN, "", "", Arrays.asList(ENCLAVE_PUBLIC_KEY)));
when(privacyController.retrieveOnChainPrivacyGroupWithToBeAddedMembers(any(), any(), any()))
.thenReturn(optionalPrivacyGroup);
when(privacyController.buildAndSendAddPayload( when(privacyController.buildAndSendAddPayload(
any(PrivateTransaction.class), any(Bytes32.class), any(String.class))) any(PrivateTransaction.class), any(Bytes32.class), any(String.class)))
.thenReturn(Optional.of(ENCLAVE_PUBLIC_KEY)); .thenReturn(Optional.of(ENCLAVE_PUBLIC_KEY));
@ -321,7 +323,7 @@ public class EeaSendRawTransactionTest {
new EeaSendRawTransaction( new EeaSendRawTransaction(
transactionPool, privacyController, enclavePublicKeyProvider, true); transactionPool, privacyController, enclavePublicKeyProvider, true);
when(privacyController.retrieveOnChainPrivacyGroup(any(Bytes.class), any(String.class))) when(privacyController.retrieveOnChainPrivacyGroupWithToBeAddedMembers(any(), any(), any()))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
final JsonRpcRequestContext request = final JsonRpcRequestContext request =

@ -183,7 +183,8 @@ public abstract class MainnetPrecompiledContractRegistries {
accountVersion, accountVersion,
new PrivacyPrecompiledContract( new PrivacyPrecompiledContract(
precompiledContractConfiguration.getGasCalculator(), precompiledContractConfiguration.getGasCalculator(),
precompiledContractConfiguration.getPrivacyParameters())); precompiledContractConfiguration.getPrivacyParameters(),
"Privacy"));
} }
} }
} }

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet.precompiles.privacy;
import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH; import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH;
import org.hyperledger.besu.crypto.SECP256K1; import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveClientException; import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.types.ReceiveResponse; import org.hyperledger.besu.enclave.types.ReceiveResponse;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
@ -31,6 +32,7 @@ import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.core.WorldUpdater; import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.debug.TraceOptions; import org.hyperledger.besu.ethereum.debug.TraceOptions;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.ethereum.privacy.Restriction; import org.hyperledger.besu.ethereum.privacy.Restriction;
@ -38,11 +40,17 @@ import org.hyperledger.besu.ethereum.privacy.VersionedPrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement; import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.ethereum.vm.GasCalculator; import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame; import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.ArrayList;
import java.util.Base64; import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -61,12 +69,21 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
public OnChainPrivacyPrecompiledContract( public OnChainPrivacyPrecompiledContract(
final GasCalculator gasCalculator, final PrivacyParameters privacyParameters) { final GasCalculator gasCalculator, final PrivacyParameters privacyParameters) {
super(gasCalculator, privacyParameters, "OnChainPrivacy");
}
public OnChainPrivacyPrecompiledContract(
final GasCalculator gasCalculator,
final Enclave enclave,
final WorldStateArchive worldStateArchive,
final PrivateStateStorage privateStateStorage,
final PrivateStateRootResolver privateStateRootResolver) {
super( super(
gasCalculator, gasCalculator,
privacyParameters.getEnclave(), enclave,
privacyParameters.getPrivateWorldStateArchive(), worldStateArchive,
privacyParameters.getPrivateStateStorage(), privateStateStorage,
privacyParameters.getPrivateStateRootResolver(), privateStateRootResolver,
"OnChainPrivacy"); "OnChainPrivacy");
} }
@ -97,13 +114,11 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
final PrivateTransaction privateTransaction = final PrivateTransaction privateTransaction =
versionedPrivateTransaction.getPrivateTransaction(); versionedPrivateTransaction.getPrivateTransaction();
if (!privateFromMatchesSenderKey( final Bytes privateFrom = privateTransaction.getPrivateFrom();
privateTransaction.getPrivateFrom(), receiveResponse.getSenderKey())) { if (!privateFromMatchesSenderKey(privateFrom, receiveResponse.getSenderKey())) {
return Bytes.EMPTY; return Bytes.EMPTY;
} }
final Bytes32 version = versionedPrivateTransaction.getVersion();
final Optional<Bytes> maybeGroupId = privateTransaction.getPrivacyGroupId(); final Optional<Bytes> maybeGroupId = privateTransaction.getPrivacyGroupId();
if (maybeGroupId.isEmpty()) { if (maybeGroupId.isEmpty()) {
return Bytes.EMPTY; return Bytes.EMPTY;
@ -124,22 +139,23 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater(); final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater();
final WorldUpdater publicWorldState = messageFrame.getWorldState();
final Blockchain blockchain = messageFrame.getBlockchain();
maybeInjectDefaultManagementAndProxy( maybeInjectDefaultManagementAndProxy(
lastRootHash, disposablePrivateState, privateWorldStateUpdater); lastRootHash, disposablePrivateState, privateWorldStateUpdater);
final Blockchain blockchain = messageFrame.getBlockchain();
final WorldUpdater publicWorldState = messageFrame.getWorldState();
if (!canExecute( if (!canExecute(
messageFrame, messageFrame,
currentBlockHeader, currentBlockHeader,
privateTransaction, privateTransaction,
version, versionedPrivateTransaction.getVersion(),
publicWorldState, publicWorldState,
privacyGroupId, privacyGroupId,
blockchain, blockchain,
disposablePrivateState, disposablePrivateState,
privateWorldStateUpdater)) { privateWorldStateUpdater,
privateFrom)) {
return Bytes.EMPTY; return Bytes.EMPTY;
} }
@ -164,7 +180,6 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
persistPrivateState( persistPrivateState(
pmtHash, pmtHash,
currentBlockHash, currentBlockHash,
privateTransaction,
privacyGroupId, privacyGroupId,
disposablePrivateState, disposablePrivateState,
privateWorldStateUpdater, privateWorldStateUpdater,
@ -183,13 +198,14 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
final Bytes32 privacyGroupId, final Bytes32 privacyGroupId,
final Blockchain blockchain, final Blockchain blockchain,
final MutableWorldState disposablePrivateState, final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater) { final WorldUpdater privateWorldStateUpdater,
final Bytes privateFrom) {
final boolean isAddingParticipant = final boolean isAddingParticipant =
privateTransaction privateTransaction
.getPayload() .getPayload()
.toHexString() .toHexString()
.startsWith(OnChainGroupManagement.ADD_TO_GROUP_METHOD_SIGNATURE.toHexString()); .startsWith(OnChainGroupManagement.ADD_PARTICIPANTS_METHOD_SIGNATURE.toHexString());
final boolean isPrivacyGroupLocked = final boolean isPrivacyGroupLocked =
isContractLocked( isContractLocked(
@ -226,11 +242,97 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
blockchain, blockchain,
disposablePrivateState, disposablePrivateState,
privateWorldStateUpdater)) { privateWorldStateUpdater)) {
LOG.debug(
"Privacy group version mismatch while trying to execute transaction with commitment {}",
messageFrame.getTransactionHash());
return false;
}
if (!isMemberOfPrivacyGroup(
isAddingParticipant,
privateTransaction,
privateFrom,
privacyGroupId,
messageFrame,
currentBlockHeader,
publicWorldState,
blockchain,
disposablePrivateState,
privateWorldStateUpdater)) {
LOG.debug(
"PrivateTransaction with hash {} cannot execute in privacy group {} because privateFrom {} is not a member.",
messageFrame.getTransactionHash(),
privacyGroupId.toBase64String(),
privateFrom.toBase64String());
return false; return false;
} }
return true; return true;
} }
private boolean isMemberOfPrivacyGroup(
final boolean isAddingParticipant,
final PrivateTransaction privateTransaction,
final Bytes privateFrom,
final Bytes32 privacyGroupId,
final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader,
final WorldUpdater publicWorldState,
final Blockchain blockchain,
final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater) {
final PrivateTransactionProcessor.Result result =
simulateTransaction(
messageFrame,
currentBlockHeader,
publicWorldState,
privacyGroupId,
blockchain,
disposablePrivateState,
privateWorldStateUpdater,
OnChainGroupManagement.GET_PARTICIPANTS_METHOD_SIGNATURE);
final List<Bytes> list = getMembersFromResult(result);
List<String> participantsFromParameter = Collections.emptyList();
if (list.isEmpty() && isAddingParticipant) {
// creating a new group, so we are checking whether the privateFrom is one of the members of
// the new group
participantsFromParameter = getParticipantsFromParameter(privateTransaction.getPayload());
}
return list.contains(privateFrom)
|| participantsFromParameter.contains(privateFrom.toBase64String());
}
List<Bytes> getMembersFromResult(final PrivateTransactionProcessor.Result result) {
List<Bytes> list = Collections.emptyList();
if (result != null && result.isSuccessful()) {
final RLPInput rlpInput = RLP.input(result.getOutput());
if (rlpInput.nextSize() > 0) {
list = decodeList(rlpInput.raw());
}
}
return list;
}
// TODO: this method is copied from DefaultPrivacyController. Fix the duplication in a separate GI
private List<String> getParticipantsFromParameter(final Bytes input) {
final List<String> participants = new ArrayList<>();
final Bytes mungedParticipants = input.slice(4 + 32 + 32);
for (int i = 0; i <= mungedParticipants.size() - 32; i += 32) {
participants.add(mungedParticipants.slice(i, 32).toBase64String());
}
return participants;
}
private List<Bytes> decodeList(final Bytes rlpEncodedList) {
final ArrayList<Bytes> 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))); // participant
}
return decodedElements;
}
protected boolean isContractLocked( protected boolean isContractLocked(
final MessageFrame messageFrame, final MessageFrame messageFrame,
final ProcessableBlockHeader currentBlockHeader, final ProcessableBlockHeader currentBlockHeader,

@ -60,14 +60,16 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
private static final Logger LOG = LogManager.getLogger(); private static final Logger LOG = LogManager.getLogger();
public PrivacyPrecompiledContract( public PrivacyPrecompiledContract(
final GasCalculator gasCalculator, final PrivacyParameters privacyParameters) { final GasCalculator gasCalculator,
final PrivacyParameters privacyParameters,
final String name) {
this( this(
gasCalculator, gasCalculator,
privacyParameters.getEnclave(), privacyParameters.getEnclave(),
privacyParameters.getPrivateWorldStateArchive(), privacyParameters.getPrivateWorldStateArchive(),
privacyParameters.getPrivateStateStorage(), privacyParameters.getPrivateStateStorage(),
privacyParameters.getPrivateStateRootResolver(), privacyParameters.getPrivateStateRootResolver(),
"Privacy"); name);
} }
@VisibleForTesting @VisibleForTesting
@ -134,14 +136,33 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
final PrivateTransaction privateTransaction = final PrivateTransaction privateTransaction =
PrivateTransaction.readFrom(bytesValueRLPInput.readAsRlp()); PrivateTransaction.readFrom(bytesValueRLPInput.readAsRlp());
if (!privateFromMatchesSenderKey( final Bytes privateFrom = privateTransaction.getPrivateFrom();
privateTransaction.getPrivateFrom(), receiveResponse.getSenderKey())) { if (!privateFromMatchesSenderKey(privateFrom, receiveResponse.getSenderKey())) {
return Bytes.EMPTY; return Bytes.EMPTY;
} }
final Bytes32 privacyGroupId = final Bytes32 privacyGroupId =
Bytes32.wrap(Bytes.fromBase64String(receiveResponse.getPrivacyGroupId())); Bytes32.wrap(Bytes.fromBase64String(receiveResponse.getPrivacyGroupId()));
try {
if (privateTransaction.getPrivateFor().isEmpty()
&& !enclave
.retrievePrivacyGroup(privacyGroupId.toBase64String())
.getMembers()
.contains(privateFrom.toBase64String())) {
return Bytes.EMPTY;
}
} catch (final EnclaveClientException e) {
// This exception is thrown when the privacy group can not be found
return Bytes.EMPTY;
} catch (final EnclaveServerException e) {
LOG.error("Enclave is responding with an error, 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;
}
LOG.debug("Processing private transaction {} in privacy group {}", pmtHash, privacyGroupId); LOG.debug("Processing private transaction {} in privacy group {}", pmtHash, privacyGroupId);
final Hash currentBlockHash = ((BlockHeader) messageFrame.getBlockHeader()).getHash(); final Hash currentBlockHash = ((BlockHeader) messageFrame.getBlockHeader()).getHash();
@ -175,7 +196,6 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
persistPrivateState( persistPrivateState(
pmtHash, pmtHash,
currentBlockHash, currentBlockHash,
privateTransaction,
privacyGroupId, privacyGroupId,
disposablePrivateState, disposablePrivateState,
privateWorldStateUpdater, privateWorldStateUpdater,
@ -188,7 +208,6 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
void persistPrivateState( void persistPrivateState(
final Hash commitmentHash, final Hash commitmentHash,
final Hash currentBlockHash, final Hash currentBlockHash,
final PrivateTransaction privateTransaction,
final Bytes32 privacyGroupId, final Bytes32 privacyGroupId,
final MutableWorldState disposablePrivateState, final MutableWorldState disposablePrivateState,
final WorldUpdater privateWorldStateUpdater, final WorldUpdater privateWorldStateUpdater,

@ -15,7 +15,7 @@
package org.hyperledger.besu.ethereum.privacy; package org.hyperledger.besu.ethereum.privacy;
import static java.nio.charset.StandardCharsets.UTF_8; 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.ADD_PARTICIPANTS_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_PARTICIPANTS_METHOD_SIGNATURE;
import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.GET_VERSION_METHOD_SIGNATURE; import static org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement.GET_VERSION_METHOD_SIGNATURE;
@ -272,8 +272,7 @@ public class DefaultPrivacyController implements PrivacyController {
.keySet() .keySet()
.forEach( .forEach(
c -> { c -> {
final Optional<PrivacyGroup> maybePrivacyGroup = final Optional<PrivacyGroup> maybePrivacyGroup = retrieveOnChainPrivacyGroup(c);
retrieveOnChainPrivacyGroup(c, enclavePublicKey);
if (maybePrivacyGroup.isPresent() if (maybePrivacyGroup.isPresent()
&& maybePrivacyGroup.get().getMembers().containsAll(addresses)) { && maybePrivacyGroup.get().getMembers().containsAll(addresses)) {
privacyGroups.add(maybePrivacyGroup.get()); privacyGroups.add(maybePrivacyGroup.get());
@ -282,15 +281,11 @@ public class DefaultPrivacyController implements PrivacyController {
return privacyGroups; return privacyGroups;
} }
@Override public Optional<PrivacyGroup> retrieveOnChainPrivacyGroup(final Bytes privacyGroupId) {
public Optional<PrivacyGroup> retrieveOnChainPrivacyGroup(
final Bytes privacyGroupId, final String enclavePublicKey) {
// get the privateFor list from the management contract // get the privateFor list from the management contract
final Optional<PrivateTransactionProcessor.Result> privateTransactionSimulatorResultOptional = final Optional<PrivateTransactionProcessor.Result> privateTransactionSimulatorResultOptional =
privateTransactionSimulator.process( privateTransactionSimulator.process(
privacyGroupId.toBase64String(), privacyGroupId.toBase64String(), buildCallParams(GET_PARTICIPANTS_METHOD_SIGNATURE));
buildCallParams(
Bytes.fromBase64String(enclavePublicKey), GET_PARTICIPANTS_METHOD_SIGNATURE));
if (privateTransactionSimulatorResultOptional.isPresent() if (privateTransactionSimulatorResultOptional.isPresent()
&& privateTransactionSimulatorResultOptional.get().isSuccessful()) { && privateTransactionSimulatorResultOptional.get().isSuccessful()) {
@ -307,8 +302,45 @@ public class DefaultPrivacyController implements PrivacyController {
} else { } else {
return Optional.empty(); return Optional.empty();
} }
} else {
return Optional.empty();
}
}
@Override
public Optional<PrivacyGroup> retrieveOnChainPrivacyGroupWithToBeAddedMembers(
final Bytes privacyGroupId,
final String enclavePublicKey,
final PrivateTransaction privateTransaction) {
// get the privateFor list from the management contract
final Optional<PrivateTransactionProcessor.Result> privateTransactionSimulatorResultOptional =
privateTransactionSimulator.process(
privacyGroupId.toBase64String(), buildCallParams(GET_PARTICIPANTS_METHOD_SIGNATURE));
final List<String> members = new ArrayList<>();
if (privateTransactionSimulatorResultOptional.isPresent()
&& privateTransactionSimulatorResultOptional.get().isSuccessful()) {
final RLPInput rlpInput =
RLP.input(privateTransactionSimulatorResultOptional.get().getOutput());
if (rlpInput.nextSize() > 0) {
members.addAll(decodeList(rlpInput.raw()));
if (!members.contains(enclavePublicKey)) {
return Optional.empty();
}
}
}
if (isGroupAdditionTransaction(privateTransaction)) {
final List<String> participantsFromParameter =
getParticipantsFromParameter(privateTransaction.getPayload());
members.addAll(participantsFromParameter);
}
if (members.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(
new PrivacyGroup(
privacyGroupId.toBase64String(), PrivacyGroup.Type.ONCHAIN, "", "", members));
} }
return Optional.empty();
} }
private List<String> decodeList(final Bytes rlpEncodedList) { private List<String> decodeList(final Bytes rlpEncodedList) {
@ -324,21 +356,16 @@ public class DefaultPrivacyController implements PrivacyController {
private List<String> getParticipantsFromParameter(final Bytes input) { private List<String> getParticipantsFromParameter(final Bytes input) {
final List<String> participants = new ArrayList<>(); final List<String> participants = new ArrayList<>();
final Bytes mungedParticipants = input.slice(4 + 32 + 32 + 32); final Bytes mungedParticipants = input.slice(4 + 32 + 32);
for (int i = 0; i <= mungedParticipants.size() - 32; i += 32) { for (int i = 0; i <= mungedParticipants.size() - 32; i += 32) {
participants.add(mungedParticipants.slice(i, 32).toBase64String()); participants.add(mungedParticipants.slice(i, 32).toBase64String());
} }
return participants; return participants;
} }
private CallParameter buildCallParams(final Bytes enclavePublicKey, final Bytes methodCall) { private CallParameter buildCallParams(final Bytes methodCall) {
return new CallParameter( return new CallParameter(
Address.ZERO, Address.ZERO, Address.ONCHAIN_PRIVACY_PROXY, 3000000, Wei.of(1000), Wei.ZERO, methodCall);
Address.ONCHAIN_PRIVACY_PROXY,
3000000,
Wei.of(1000),
Wei.ZERO,
Bytes.concatenate(methodCall, enclavePublicKey));
} }
private List<PrivateTransactionMetadata> buildTransactionMetadataList( private List<PrivateTransactionMetadata> buildTransactionMetadataList(
@ -411,7 +438,7 @@ public class DefaultPrivacyController implements PrivacyController {
&& privateTransaction && privateTransaction
.getPayload() .getPayload()
.toHexString() .toHexString()
.startsWith(ADD_TO_GROUP_METHOD_SIGNATURE.toHexString()); .startsWith(ADD_PARTICIPANTS_METHOD_SIGNATURE.toHexString());
} }
@Override @Override
@ -448,23 +475,21 @@ public class DefaultPrivacyController implements PrivacyController {
final Optional<PrivacyGroup> maybePrivacyGroup) { final Optional<PrivacyGroup> maybePrivacyGroup) {
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
if (maybePrivacyGroup.isPresent() || isGroupAdditionTransaction(privateTransaction)) { if (maybePrivacyGroup.isPresent()) {
if (isGroupAdditionTransaction(privateTransaction) final PrivacyGroup privacyGroup = maybePrivacyGroup.get();
|| maybePrivacyGroup.get().getType() == PrivacyGroup.Type.ONCHAIN) { if (privacyGroup.getType() == PrivacyGroup.Type.ONCHAIN) {
// onchain privacy group // onchain privacy group
final Optional<PrivateTransactionProcessor.Result> result = final Optional<PrivateTransactionProcessor.Result> result =
privateTransactionSimulator.process( privateTransactionSimulator.process(
privateTransaction.getPrivacyGroupId().get().toBase64String(), privateTransaction.getPrivacyGroupId().get().toBase64String(),
buildCallParams( buildCallParams(GET_VERSION_METHOD_SIGNATURE));
Bytes.fromBase64String(enclavePublicKey), GET_VERSION_METHOD_SIGNATURE));
new VersionedPrivateTransaction(privateTransaction, result).writeTo(rlpOutput); new VersionedPrivateTransaction(privateTransaction, result).writeTo(rlpOutput);
final List<String> onChainPrivateFor = final List<String> onChainPrivateFor = privacyGroup.getMembers();
resolveOnChainPrivateFor(privateTransaction, maybePrivacyGroup);
return enclave.send( return enclave.send(
rlpOutput.encoded().toBase64String(), rlpOutput.encoded().toBase64String(),
privateTransaction.getPrivateFrom().toBase64String(), privateTransaction.getPrivateFrom().toBase64String(),
onChainPrivateFor); onChainPrivateFor);
} else if (maybePrivacyGroup.get().getType() == PrivacyGroup.Type.PANTHEON) { } else if (privacyGroup.getType() == PrivacyGroup.Type.PANTHEON) {
// offchain privacy group // offchain privacy group
privateTransaction.writeTo(rlpOutput); privateTransaction.writeTo(rlpOutput);
return enclave.send( return enclave.send(
@ -499,18 +524,6 @@ public class DefaultPrivacyController implements PrivacyController {
return privateFor; return privateFor;
} }
private List<String> resolveOnChainPrivateFor(
final PrivateTransaction privateTransaction, final Optional<PrivacyGroup> privacyGroup) {
final ArrayList<String> privateFor = new ArrayList<>();
if (isGroupAdditionTransaction(privateTransaction)) {
privateFor.addAll(getParticipantsFromParameter(privateTransaction.getPayload()));
}
if (privacyGroup.isPresent()) {
privateFor.addAll(privacyGroup.get().getMembers());
}
return privateFor;
}
@Override @Override
public void verifyPrivacyGroupContainsEnclavePublicKey( public void verifyPrivacyGroupContainsEnclavePublicKey(
final String privacyGroupId, final String enclavePublicKey) { final String privacyGroupId, final String enclavePublicKey) {

@ -230,15 +230,15 @@ public class MultiTenancyPrivacyController implements PrivacyController {
} }
@Override @Override
public Optional<PrivacyGroup> retrieveOnChainPrivacyGroup( public Optional<PrivacyGroup> retrieveOnChainPrivacyGroupWithToBeAddedMembers(
final Bytes privacyGroupId, final String enclavePublicKey) { final Bytes privacyGroupId,
final String enclavePublicKey,
final PrivateTransaction privateTransaction) {
final Optional<PrivacyGroup> maybePrivacyGroup = final Optional<PrivacyGroup> maybePrivacyGroup =
privacyController.retrieveOnChainPrivacyGroup(privacyGroupId, enclavePublicKey); privacyController.retrieveOnChainPrivacyGroupWithToBeAddedMembers(
if (maybePrivacyGroup.isPresent() privacyGroupId, enclavePublicKey, privateTransaction);
&& !maybePrivacyGroup.get().getMembers().contains(enclavePublicKey)) { // The check that the enclavePublicKey is a member (if the group already exists) is done in the
throw new MultiTenancyValidationException( // DefaultPrivacyController.
"Privacy group must contain the enclave public key");
}
return maybePrivacyGroup; return maybePrivacyGroup;
} }

@ -83,7 +83,8 @@ public interface PrivacyController {
final Hash blockHash, final Hash blockHash,
final String enclavePublicKey); final String enclavePublicKey);
Optional<PrivacyGroup> retrieveOnChainPrivacyGroup(Bytes privacyGroupId, String enclavePublicKey); Optional<PrivacyGroup> retrieveOnChainPrivacyGroupWithToBeAddedMembers(
Bytes privacyGroupId, String enclavePublicKey, final PrivateTransaction privateTransaction);
List<PrivateTransactionWithMetadata> retrieveAddBlob(String addDataKey); List<PrivateTransactionWithMetadata> retrieveAddBlob(String addDataKey);

@ -48,8 +48,7 @@ public class PrivateTransactionSimulator {
private static final SECP256K1.Signature FAKE_SIGNATURE = private static final SECP256K1.Signature FAKE_SIGNATURE =
SECP256K1.Signature.create(SECP256K1.HALF_CURVE_ORDER, SECP256K1.HALF_CURVE_ORDER, (byte) 0); SECP256K1.Signature.create(SECP256K1.HALF_CURVE_ORDER, SECP256K1.HALF_CURVE_ORDER, (byte) 0);
private static final Address DEFAULT_FROM = private static final Address DEFAULT_FROM = Address.ZERO;
Address.fromHexString("0x0000000000000000000000000000000000000000");
private final Blockchain blockchain; private final Blockchain blockchain;
private final WorldStateArchive worldStateArchive; private final WorldStateArchive worldStateArchive;

@ -116,6 +116,12 @@ public class PrivateTransactionDataFixture {
.createTransaction(KEY_PAIR); .createTransaction(KEY_PAIR);
} }
public static VersionedPrivateTransaction versionedPrivateTransactionBesu() {
return new PrivateTransactionTestFixture()
.privacyGroupId(VALID_BASE64_ENCLAVE_KEY)
.createVersionedPrivateTransaction((KEY_PAIR));
}
public static PrivateTransaction privateContractDeploymentTransactionBesu() { public static PrivateTransaction privateContractDeploymentTransactionBesu() {
return new PrivateTransactionTestFixture() return new PrivateTransactionTestFixture()
.payload(VALID_CONTRACT_DEPLOYMENT_PAYLOAD) .payload(VALID_CONTRACT_DEPLOYMENT_PAYLOAD)

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.crypto.SECP256K1.KeyPair; import org.hyperledger.besu.crypto.SECP256K1.KeyPair;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.Restriction; import org.hyperledger.besu.ethereum.privacy.Restriction;
import org.hyperledger.besu.ethereum.privacy.VersionedPrivateTransaction;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List; import java.util.List;
@ -24,6 +25,7 @@ import java.util.Optional;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
public class PrivateTransactionTestFixture { public class PrivateTransactionTestFixture {
@ -84,6 +86,11 @@ public class PrivateTransactionTestFixture {
return builder.signAndBuild(keys); return builder.signAndBuild(keys);
} }
public VersionedPrivateTransaction createVersionedPrivateTransaction(final KeyPair keyPair) {
final PrivateTransaction transaction = createTransaction(keyPair);
return new VersionedPrivateTransaction(transaction, Bytes32.ZERO);
}
public PrivateTransactionTestFixture nonce(final long nonce) { public PrivateTransactionTestFixture nonce(final long nonce) {
this.nonce = nonce; this.nonce = nonce;
return this; return this;

@ -0,0 +1,314 @@
/*
* 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 java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.versionedPrivateTransactionBesu;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.EnclaveConfigurationException;
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.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.Log;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.WorldUpdater;
import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
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.VersionedPrivateTransaction;
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;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
public class OnChainPrivacyPrecompiledContractTest {
@Rule public final TemporaryFolder temp = new TemporaryFolder();
private final Bytes txEnclaveKey = Bytes.random(32);
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);
final PrivateStateRootResolver privateStateRootResolver =
new PrivateStateRootResolver(privateStateStorage);
private PrivateTransactionProcessor mockPrivateTxProcessor(
final PrivateTransactionProcessor.Result result) {
final PrivateTransactionProcessor mockPrivateTransactionProcessor =
mock(PrivateTransactionProcessor.class);
when(mockPrivateTransactionProcessor.processTransaction(
nullable(Blockchain.class),
nullable(WorldUpdater.class),
nullable(WorldUpdater.class),
nullable(ProcessableBlockHeader.class),
nullable((Hash.class)),
nullable(PrivateTransaction.class),
nullable(Address.class),
nullable(OperationTracer.class),
nullable(BlockHashLookup.class),
nullable(Bytes.class)))
.thenReturn(result);
return mockPrivateTransactionProcessor;
}
@Before
public void setUp() {
final MutableWorldState mutableWorldState = mock(MutableWorldState.class);
when(mutableWorldState.updater()).thenReturn(mock(WorldUpdater.class));
when(worldStateArchive.getMutable()).thenReturn(mutableWorldState);
when(worldStateArchive.getMutable(any())).thenReturn(Optional.of(mutableWorldState));
final PrivateStateStorage.Updater storageUpdater = mock(PrivateStateStorage.Updater.class);
when(privateStateStorage.getPrivacyGroupHeadBlockMap(any()))
.thenReturn(Optional.of(PrivacyGroupHeadBlockMap.EMPTY));
when(privateStateStorage.getPrivateBlockMetadata(any(), any())).thenReturn(Optional.empty());
when(storageUpdater.putPrivateBlockMetadata(
nullable(Bytes32.class), nullable(Bytes32.class), any()))
.thenReturn(storageUpdater);
when(storageUpdater.putPrivacyGroupHeadBlockMap(nullable(Bytes32.class), any()))
.thenReturn(storageUpdater);
when(storageUpdater.putTransactionReceipt(
nullable(Bytes32.class), nullable(Bytes32.class), any()))
.thenReturn(storageUpdater);
when(privateStateStorage.updater()).thenReturn(storageUpdater);
messageFrame = mock(MessageFrame.class);
blockchain = mock(Blockchain.class);
final BlockDataGenerator blockGenerator = new BlockDataGenerator();
final Block genesis = blockGenerator.genesisBlock();
final Block block =
blockGenerator.block(
new BlockDataGenerator.BlockOptions().setParentHash(genesis.getHeader().getHash()));
when(blockchain.getGenesisBlock()).thenReturn(genesis);
when(blockchain.getBlockByHash(block.getHash())).thenReturn(Optional.of(block));
when(blockchain.getBlockByHash(genesis.getHash())).thenReturn(Optional.of(genesis));
when(messageFrame.getBlockchain()).thenReturn(blockchain);
when(messageFrame.getBlockHeader()).thenReturn(block.getHeader());
}
@Test
public void testPayloadFoundInEnclave() {
final Enclave enclave = mock(Enclave.class);
final OnChainPrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
final List<Log> logs = new ArrayList<>();
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
PrivateTransactionProcessor.Result.successful(
logs, 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null)));
final VersionedPrivateTransaction versionedPrivateTransaction =
versionedPrivateTransactionBesu();
final byte[] payload = convertVersionedPrivateTransactionToBytes(versionedPrivateTransaction);
final String privateFrom =
versionedPrivateTransaction.getPrivateTransaction().getPrivateFrom().toBase64String();
final ReceiveResponse response =
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, privateFrom);
when(enclave.receive(any())).thenReturn(response);
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doNothing().when(contractSpy).maybeInjectDefaultManagementAndProxy(any(), any(), any());
Mockito.doReturn(true)
.when(contractSpy)
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any(), any());
final Bytes actual = contractSpy.compute(txEnclaveKey, messageFrame);
assertThat(actual).isEqualTo(Bytes.fromHexString(DEFAULT_OUTPUT));
}
@Test
public void testPayloadNotFoundInEnclave() {
final Enclave enclave = mock(Enclave.class);
final PrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
when(enclave.receive(any(String.class))).thenThrow(EnclaveClientException.class);
final Bytes expected = contract.compute(txEnclaveKey, messageFrame);
assertThat(expected).isEqualTo(Bytes.EMPTY);
}
@Test(expected = RuntimeException.class)
public void testEnclaveDown() {
final Enclave enclave = mock(Enclave.class);
final PrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
when(enclave.receive(any(String.class))).thenThrow(new RuntimeException());
contract.compute(txEnclaveKey, messageFrame);
}
@Test
public void testEnclaveBelowRequiredVersion() {
final Enclave enclave = mock(Enclave.class);
final OnChainPrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
final VersionedPrivateTransaction versionedPrivateTransaction =
versionedPrivateTransactionBesu();
final byte[] payload = convertVersionedPrivateTransactionToBytes(versionedPrivateTransaction);
final ReceiveResponse responseWithoutSenderKey =
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, null);
when(enclave.receive(eq(txEnclaveKey.toBase64String()))).thenReturn(responseWithoutSenderKey);
assertThatThrownBy(() -> contract.compute(txEnclaveKey, messageFrame))
.isInstanceOf(EnclaveConfigurationException.class)
.hasMessage("Incompatible Orion version. Orion version must be 1.6.0 or greater.");
}
@Test
public void testPayloadNotMatchingPrivateFrom() {
final Enclave enclave = mock(Enclave.class);
final OnChainPrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
final VersionedPrivateTransaction versionedPrivateTransaction =
versionedPrivateTransactionBesu();
final byte[] payload = convertVersionedPrivateTransactionToBytes(versionedPrivateTransaction);
final String wrongSenderKey = Bytes.random(32).toBase64String();
final ReceiveResponse responseWithWrongSenderKey =
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, wrongSenderKey);
when(enclave.receive(eq(txEnclaveKey.toBase64String()))).thenReturn(responseWithWrongSenderKey);
final Bytes expected = contract.compute(txEnclaveKey, messageFrame);
assertThat(expected).isEqualTo(Bytes.EMPTY);
}
@Test
public void testPrivateFromNotMemberOfGroup() {
final Enclave enclave = mock(Enclave.class);
final OnChainPrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doNothing().when(contractSpy).maybeInjectDefaultManagementAndProxy(any(), any(), any());
Mockito.doReturn(false)
.when(contractSpy)
.isContractLocked(any(), any(), any(), any(), any(), any(), any());
Mockito.doReturn(true)
.when(contractSpy)
.onChainPrivacyGroupVersionMatches(any(), any(), any(), any(), any(), any(), any(), any());
final PrivateTransactionProcessor.Result mockResult =
mock(PrivateTransactionProcessor.Result.class);
Mockito.doReturn(mockResult)
.when(contractSpy)
.simulateTransaction(any(), any(), any(), any(), any(), any(), any(), any());
Mockito.doReturn(Arrays.asList(Bytes.ofUnsignedInt(1L)))
.when(contractSpy)
.getMembersFromResult(any());
final VersionedPrivateTransaction privateTransaction = versionedPrivateTransactionBesu();
final byte[] payload = convertVersionedPrivateTransactionToBytes(privateTransaction);
final String privateFrom =
privateTransaction.getPrivateTransaction().getPrivateFrom().toBase64String();
final ReceiveResponse response =
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, privateFrom);
when(enclave.receive(any(String.class))).thenReturn(response);
final Bytes actual = contractSpy.compute(txEnclaveKey, messageFrame);
assertThat(actual).isEqualTo(Bytes.EMPTY);
}
@Test
public void testInvalidPrivateTransaction() {
final Enclave enclave = mock(Enclave.class);
final OnChainPrivacyPrecompiledContract contract =
new OnChainPrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
privateStateStorage,
privateStateRootResolver);
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
PrivateTransactionProcessor.Result.invalid(
ValidationResult.invalid(
TransactionValidator.TransactionInvalidReason.INCORRECT_NONCE))));
final OnChainPrivacyPrecompiledContract contractSpy = spy(contract);
Mockito.doNothing().when(contractSpy).maybeInjectDefaultManagementAndProxy(any(), any(), any());
Mockito.doReturn(true)
.when(contractSpy)
.canExecute(any(), any(), any(), any(), any(), any(), any(), any(), any(), any());
final VersionedPrivateTransaction privateTransaction = versionedPrivateTransactionBesu();
final byte[] payload = convertVersionedPrivateTransactionToBytes(privateTransaction);
final String privateFrom =
privateTransaction.getPrivateTransaction().getPrivateFrom().toBase64String();
final ReceiveResponse response =
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, privateFrom);
when(enclave.receive(any(String.class))).thenReturn(response);
final Bytes actual = contractSpy.compute(txEnclaveKey, messageFrame);
assertThat(actual).isEqualTo(Bytes.EMPTY);
}
private byte[] convertVersionedPrivateTransactionToBytes(
final VersionedPrivateTransaction privateTransaction) {
final BytesValueRLPOutput bytesValueRLPOutput = new BytesValueRLPOutput();
privateTransaction.writeTo(bytesValueRLPOutput);
return bytesValueRLPOutput.encoded().toBase64String().getBytes(UTF_8);
}
private OnChainPrivacyPrecompiledContract buildPrivacyPrecompiledContract(final Enclave enclave) {
return new OnChainPrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
privateStateStorage,
privateStateRootResolver);
}
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet.precompiles.privacy;
import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.VALID_BASE64_ENCLAVE_KEY;
import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.privateTransactionBesu; import static org.hyperledger.besu.ethereum.core.PrivateTransactionDataFixture.privateTransactionBesu;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -28,6 +29,7 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.enclave.Enclave; import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveClientException; import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.EnclaveConfigurationException; import org.hyperledger.besu.enclave.EnclaveConfigurationException;
import org.hyperledger.besu.enclave.types.PrivacyGroup;
import org.hyperledger.besu.enclave.types.ReceiveResponse; import org.hyperledger.besu.enclave.types.ReceiveResponse;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
@ -53,6 +55,7 @@ import org.hyperledger.besu.ethereum.vm.OperationTracer;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -136,6 +139,14 @@ public class PrivacyPrecompiledContractTest {
@Test @Test
public void testPayloadFoundInEnclave() { public void testPayloadFoundInEnclave() {
final Enclave enclave = mock(Enclave.class); final Enclave enclave = mock(Enclave.class);
when(enclave.retrievePrivacyGroup(PAYLOAD_TEST_PRIVACY_GROUP_ID))
.thenReturn(
new PrivacyGroup(
PAYLOAD_TEST_PRIVACY_GROUP_ID,
PrivacyGroup.Type.PANTHEON,
"",
"",
Arrays.asList(VALID_BASE64_ENCLAVE_KEY.toBase64String())));
final PrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave); final PrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
final List<Log> logs = new ArrayList<>(); final List<Log> logs = new ArrayList<>();
contract.setPrivateTransactionProcessor( contract.setPrivateTransactionProcessor(
@ -144,7 +155,7 @@ public class PrivacyPrecompiledContractTest {
logs, 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null))); logs, 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null)));
final PrivateTransaction privateTransaction = privateTransactionBesu(); final PrivateTransaction privateTransaction = privateTransactionBesu();
byte[] payload = convertPrivateTransactionToBytes(privateTransaction); final byte[] payload = convertPrivateTransactionToBytes(privateTransaction);
final String privateFrom = privateTransaction.getPrivateFrom().toBase64String(); final String privateFrom = privateTransaction.getPrivateFrom().toBase64String();
final ReceiveResponse response = final ReceiveResponse response =
@ -226,9 +237,47 @@ public class PrivacyPrecompiledContractTest {
assertThat(expected).isEqualTo(Bytes.EMPTY); assertThat(expected).isEqualTo(Bytes.EMPTY);
} }
@Test
public void testPrivateFromNotMemberOfGroup() {
final Enclave enclave = mock(Enclave.class);
when(enclave.retrievePrivacyGroup(PAYLOAD_TEST_PRIVACY_GROUP_ID))
.thenReturn(
new PrivacyGroup(
PAYLOAD_TEST_PRIVACY_GROUP_ID,
PrivacyGroup.Type.PANTHEON,
"",
"",
Arrays.asList(VALID_BASE64_ENCLAVE_KEY.toBase64String())));
final PrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
PrivateTransactionProcessor.Result.successful(
new ArrayList<>(), 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null)));
final PrivateTransaction privateTransaction = privateTransactionBesu();
final byte[] payload = convertPrivateTransactionToBytes(privateTransaction);
final String privateFrom = privateTransaction.getPrivateFrom().toBase64String();
final ReceiveResponse response =
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, privateFrom);
when(enclave.receive(any(String.class))).thenReturn(response);
final Bytes actual = contract.compute(txEnclaveKey, messageFrame);
assertThat(actual).isEqualTo(Bytes.fromHexString(DEFAULT_OUTPUT));
}
@Test @Test
public void testInvalidPrivateTransaction() { public void testInvalidPrivateTransaction() {
final Enclave enclave = mock(Enclave.class); final Enclave enclave = mock(Enclave.class);
when(enclave.retrievePrivacyGroup(PAYLOAD_TEST_PRIVACY_GROUP_ID))
.thenReturn(
new PrivacyGroup(
PAYLOAD_TEST_PRIVACY_GROUP_ID,
PrivacyGroup.Type.PANTHEON,
"",
"",
Arrays.asList(VALID_BASE64_ENCLAVE_KEY.toBase64String())));
final PrivacyPrecompiledContract contract = final PrivacyPrecompiledContract contract =
new PrivacyPrecompiledContract( new PrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(), new SpuriousDragonGasCalculator(),

@ -44,7 +44,7 @@ import org.web3j.tx.gas.ContractGasProvider;
* or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the <a * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the <a
* href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update. * href="https://github.com/web3j/web3j/tree/master/codegen">codegen module</a> to update.
* *
* <p>Generated with web3j version 4.5.15. * <p>Generated with web3j version 4.5.16.
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class OnChainPrivacyGroupManagementInterface extends Contract { public class OnChainPrivacyGroupManagementInterface extends Contract {
@ -54,6 +54,8 @@ public class OnChainPrivacyGroupManagementInterface extends Contract {
public static final String FUNC_CANEXECUTE = "canExecute"; public static final String FUNC_CANEXECUTE = "canExecute";
public static final String FUNC_CANUPGRADE = "canUpgrade";
public static final String FUNC_GETPARTICIPANTS = "getParticipants"; public static final String FUNC_GETPARTICIPANTS = "getParticipants";
public static final String FUNC_GETVERSION = "getVersion"; public static final String FUNC_GETVERSION = "getVersion";
@ -100,13 +102,11 @@ public class OnChainPrivacyGroupManagementInterface extends Contract {
super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
} }
public RemoteFunctionCall<TransactionReceipt> addParticipants( public RemoteFunctionCall<TransactionReceipt> addParticipants(List<byte[]> participants) {
byte[] enclaveKey, List<byte[]> participants) {
final Function function = final Function function =
new Function( new Function(
FUNC_ADDPARTICIPANTS, FUNC_ADDPARTICIPANTS,
Arrays.<Type>asList( Arrays.<Type>asList(
new org.web3j.abi.datatypes.generated.Bytes32(enclaveKey),
new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Bytes32>( new org.web3j.abi.datatypes.DynamicArray<org.web3j.abi.datatypes.generated.Bytes32>(
org.web3j.abi.datatypes.generated.Bytes32.class, org.web3j.abi.datatypes.generated.Bytes32.class,
org.web3j.abi.Utils.typeMap( org.web3j.abi.Utils.typeMap(
@ -124,11 +124,18 @@ public class OnChainPrivacyGroupManagementInterface extends Contract {
return executeRemoteCallSingleValueReturn(function, Boolean.class); return executeRemoteCallSingleValueReturn(function, Boolean.class);
} }
public RemoteFunctionCall<List> getParticipants(byte[] enclaveKey) { public RemoteFunctionCall<TransactionReceipt> canUpgrade() {
final Function function =
new Function(
FUNC_CANUPGRADE, Arrays.<Type>asList(), Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function);
}
public RemoteFunctionCall<List> getParticipants() {
final Function function = final Function function =
new Function( new Function(
FUNC_GETPARTICIPANTS, FUNC_GETPARTICIPANTS,
Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Bytes32(enclaveKey)), Arrays.<Type>asList(),
Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<Bytes32>>() {})); Arrays.<TypeReference<?>>asList(new TypeReference<DynamicArray<Bytes32>>() {}));
return new RemoteFunctionCall<List>( return new RemoteFunctionCall<List>(
function, function,
@ -157,14 +164,11 @@ public class OnChainPrivacyGroupManagementInterface extends Contract {
return executeRemoteCallTransaction(function); return executeRemoteCallTransaction(function);
} }
public RemoteFunctionCall<TransactionReceipt> removeParticipant( public RemoteFunctionCall<TransactionReceipt> removeParticipant(byte[] account) {
byte[] enclaveKey, byte[] account) {
final Function function = final Function function =
new Function( new Function(
FUNC_REMOVEPARTICIPANT, FUNC_REMOVEPARTICIPANT,
Arrays.<Type>asList( Arrays.<Type>asList(new org.web3j.abi.datatypes.generated.Bytes32(account)),
new org.web3j.abi.datatypes.generated.Bytes32(enclaveKey),
new org.web3j.abi.datatypes.generated.Bytes32(account)),
Collections.<TypeReference<?>>emptyList()); Collections.<TypeReference<?>>emptyList());
return executeRemoteCallTransaction(function); return executeRemoteCallTransaction(function);
} }

@ -3,6 +3,7 @@ import "./OnChainPrivacyGroupManagementInterface.sol";
contract DefaultOnChainPrivacyGroupManagementContract is OnChainPrivacyGroupManagementInterface { contract DefaultOnChainPrivacyGroupManagementContract is OnChainPrivacyGroupManagementInterface {
address private _owner;
bool private _canExecute; bool private _canExecute;
bytes32 private _version; bytes32 private _version;
bytes32[] private distributionList; bytes32[] private distributionList;
@ -12,89 +13,92 @@ contract DefaultOnChainPrivacyGroupManagementContract is OnChainPrivacyGroupMana
return _version; return _version;
} }
// overrides
function canExecute() external view returns (bool) { function canExecute() external view returns (bool) {
return _canExecute; return _canExecute;
} }
function lock() public { function lock() public {
require(_canExecute); require(_canExecute);
require(tx.origin == _owner, "Origin not the owner.");
_canExecute = false; _canExecute = false;
} }
function unlock() public { function unlock() public {
require(!_canExecute); require(!_canExecute);
require(tx.origin == _owner, "Origin not the owner.");
_canExecute = true; _canExecute = true;
} }
function addParticipants(bytes32 _enclaveKey, bytes32[] memory _accounts) public returns (bool) { function addParticipants(bytes32[] memory _publicEnclaveKeys) public returns (bool) {
require(!_canExecute); require(!_canExecute);
if(distributionList.length == 0) { if (_owner == address(0x0)) {
addParticipant(_enclaveKey); // The account creating this group is set to be the owner
_owner = tx.origin;
} }
require(isMember(_enclaveKey)); require(tx.origin == _owner, "Origin not the owner.");
bool result = addAll(_enclaveKey, _accounts); bool result = addAll(_publicEnclaveKeys);
_canExecute = true; _canExecute = true;
updateVersion(); updateVersion();
return result; return result;
} }
function removeParticipant(bytes32 _enclaveKey, bytes32 _account) public returns (bool) { function removeParticipant(bytes32 _member) public returns (bool) {
require(isMember(_enclaveKey)); require(_canExecute);
bool result = removeInternal(_account); require(tx.origin == _owner, "Origin not the owner.");
bool result = removeInternal(_member);
updateVersion(); updateVersion();
return result; return result;
} }
function getParticipants(bytes32 _enclaveKey) public view returns (bytes32[] memory) { function getParticipants() public view returns (bytes32[] memory) {
require(isMember(_enclaveKey));
return distributionList; return distributionList;
} }
function canUpgrade() external returns (bool) {
return tx.origin == _owner;
}
//internal functions //internal functions
function addAll(bytes32 _enclaveKey, bytes32[] memory _accounts) internal returns (bool) { function addAll(bytes32[] memory _publicEnclaveKeys) internal returns (bool) {
bool allAdded = true; bool allAdded = true;
for (uint i = 0; i < _accounts.length; i++) { for (uint i = 0; i < _publicEnclaveKeys.length; i++) {
if (_enclaveKey == _accounts[i]) { if (isMember(_publicEnclaveKeys[i])) {
emit ParticipantAdded(false, _accounts[i], "Adding own account as a Member is not permitted"); emit ParticipantAdded(false, _publicEnclaveKeys[i], "Account is already a Member");
allAdded = allAdded && false;
} else if (isMember(_accounts[i])) {
emit ParticipantAdded(false, _accounts[i], "Account is already a Member");
allAdded = allAdded && false; allAdded = allAdded && false;
} else { } else {
bool result = addParticipant(_accounts[i]); bool result = addParticipant(_publicEnclaveKeys[i]);
string memory message = result ? "Member account added successfully" : "Account is already a Member"; string memory message = result ? "Member account added successfully" : "Account is already a Member";
emit ParticipantAdded(result, _accounts[i], message); emit ParticipantAdded(result, _publicEnclaveKeys[i], message);
allAdded = allAdded && result; allAdded = allAdded && result;
} }
} }
return allAdded; return allAdded;
} }
function isMember(bytes32 _account) internal view returns (bool) { function isMember(bytes32 _publicEnclaveKey) internal view returns (bool) {
return distributionIndexOf[_account] != 0; return distributionIndexOf[_publicEnclaveKey] != 0;
} }
function addParticipant(bytes32 _participant) internal returns (bool) { function addParticipant(bytes32 _publicEnclaveKey) internal returns (bool) {
if (distributionIndexOf[_participant] == 0) { if (distributionIndexOf[_publicEnclaveKey] == 0) {
distributionIndexOf[_participant] = distributionList.push(_participant); distributionIndexOf[_publicEnclaveKey] = distributionList.push(_publicEnclaveKey);
return true; return true;
} }
return false; return false;
} }
function removeInternal(bytes32 _participant) internal returns (bool) { function removeInternal(bytes32 _member) internal returns (bool) {
uint256 index = distributionIndexOf[_participant]; uint256 index = distributionIndexOf[_member];
if (index > 0 && index <= distributionList.length) { if (index > 0 && index <= distributionList.length) {
//move last address into index being vacated (unless we are dealing with last index) //move last address into index being vacated (unless we are dealing with last index)
if (index != distributionList.length) { if (index != distributionList.length) {
bytes32 lastAccount = distributionList[distributionList.length - 1]; bytes32 lastPublicKey = distributionList[distributionList.length - 1];
distributionList[index - 1] = lastAccount; distributionList[index - 1] = lastPublicKey;
distributionIndexOf[lastAccount] = index; distributionIndexOf[lastPublicKey] = index;
} }
distributionList.length -= 1; distributionList.length -= 1;
distributionIndexOf[_participant] = 0; distributionIndexOf[_member] = 0;
return true; return true;
} }
return false; return false;
@ -105,8 +109,8 @@ contract DefaultOnChainPrivacyGroupManagementContract is OnChainPrivacyGroupMana
} }
event ParticipantAdded( event ParticipantAdded(
bool adminAdded, bool success,
bytes32 account, bytes32 publicEnclaveKey,
string message string message
); );
} }

@ -1,12 +1,11 @@
pragma solidity ^0.5.9; pragma solidity ^0.5.9;
interface OnChainPrivacyGroupManagementInterface { interface OnChainPrivacyGroupManagementInterface {
function addParticipants(bytes32 enclaveKey, bytes32[] calldata participants) external returns (bool); function addParticipants(bytes32[] calldata participants) external returns (bool);
function removeParticipant(bytes32 enclaveKey, bytes32 account) external returns (bool); function removeParticipant(bytes32 account) external returns (bool);
function getParticipants(bytes32 enclaveKey) external view returns (bytes32[] memory); function getParticipants() external view returns (bytes32[] memory);
function lock() external; function lock() external;
@ -15,4 +14,6 @@ interface OnChainPrivacyGroupManagementInterface {
function canExecute() external view returns (bool); function canExecute() external view returns (bool);
function getVersion() external view returns (bytes32); function getVersion() external view returns (bytes32);
}
function canUpgrade() external returns (bool);
}

@ -1,5 +1,6 @@
pragma solidity ^0.5.12; pragma solidity ^0.5.12;
pragma solidity ^0.5.9;
import "./OnChainPrivacyGroupManagementInterface.sol"; import "./OnChainPrivacyGroupManagementInterface.sol";
contract OnChainPrivacyGroupManagementProxy is OnChainPrivacyGroupManagementInterface { contract OnChainPrivacyGroupManagementProxy is OnChainPrivacyGroupManagementInterface {
@ -10,28 +11,27 @@ contract OnChainPrivacyGroupManagementProxy is OnChainPrivacyGroupManagementInte
implementation = _implementation; implementation = _implementation;
} }
function upgradeTo(address _newImplementation) external {
require(implementation != _newImplementation);
_setImplementation(_newImplementation);
}
function _setImplementation(address _newImp) internal { function _setImplementation(address _newImp) internal {
implementation = _newImp; implementation = _newImp;
} }
function addParticipants(bytes32 enclaveKey, bytes32[] memory participants) public returns (bool) { function addParticipants(bytes32[] memory _publicEnclaveKeys) public returns (bool) {
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation); OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
return privacyInterface.addParticipants(enclaveKey, participants); return privacyInterface.addParticipants(_publicEnclaveKeys);
} }
function getParticipants(bytes32 enclaveKey) view public returns (bytes32[] memory) { function getParticipants() view public returns (bytes32[] memory) {
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation); OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
return privacyInterface.getParticipants(enclaveKey); return privacyInterface.getParticipants();
} }
function removeParticipant(bytes32 enclaveKey, bytes32 account) public returns (bool) { function removeParticipant(bytes32 _member) public returns (bool) {
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation); OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
return privacyInterface.removeParticipant(enclaveKey, account); bool result = privacyInterface.removeParticipant(_member);
if (result) {
emit ParticipantRemoved(_member);
}
return result;
} }
function lock() public { function lock() public {
@ -53,4 +53,25 @@ contract OnChainPrivacyGroupManagementProxy is OnChainPrivacyGroupManagementInte
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation); OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
return privacyInterface.getVersion(); return privacyInterface.getVersion();
} }
function canUpgrade() external returns (bool) {
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
return privacyInterface.canUpgrade();
}
function upgradeTo(address _newImplementation) external {
require(this.canExecute(), "The contract is locked.");
require(implementation != _newImplementation, "The contract to upgrade to has to be different from the current management contract.");
require(this.canUpgrade(), "Not allowed to upgrade the management contract.");
bytes32[] memory participants = this.getParticipants();
_setImplementation(_newImplementation);
OnChainPrivacyGroupManagementInterface privacyInterface = OnChainPrivacyGroupManagementInterface(implementation);
privacyInterface.addParticipants(participants);
}
event ParticipantRemoved(
bytes32 publicEnclaveKey
);
} }

Loading…
Cancel
Save