mirror of https://github.com/hyperledger/besu
Privacy multitenancy validation (#296)
Signed-off-by: Jason Frame <jasonwframe@gmail.com>pull/302/head
parent
ef61b54bba
commit
a7cde4dd8e
@ -0,0 +1,230 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.privacy; |
||||
|
||||
import org.hyperledger.besu.enclave.Enclave; |
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup; |
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup.Type; |
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse; |
||||
import org.hyperledger.besu.enclave.types.SendResponse; |
||||
import org.hyperledger.besu.ethereum.core.Account; |
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator; |
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult; |
||||
import org.hyperledger.besu.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory; |
||||
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; |
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; |
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import com.google.common.base.Preconditions; |
||||
import com.google.common.collect.Lists; |
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
import org.apache.tuweni.bytes.Bytes; |
||||
|
||||
public class DefaultPrivacyController implements PrivacyController { |
||||
|
||||
private static final Logger LOG = LogManager.getLogger(); |
||||
|
||||
private final Enclave enclave; |
||||
private final PrivateStateStorage privateStateStorage; |
||||
private final WorldStateArchive privateWorldStateArchive; |
||||
private final PrivateTransactionValidator privateTransactionValidator; |
||||
private final PrivateMarkerTransactionFactory privateMarkerTransactionFactory; |
||||
|
||||
public DefaultPrivacyController( |
||||
final PrivacyParameters privacyParameters, |
||||
final Optional<BigInteger> chainId, |
||||
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory) { |
||||
this( |
||||
privacyParameters.getEnclave(), |
||||
privacyParameters.getPrivateStateStorage(), |
||||
privacyParameters.getPrivateWorldStateArchive(), |
||||
new PrivateTransactionValidator(chainId), |
||||
privateMarkerTransactionFactory); |
||||
} |
||||
|
||||
public DefaultPrivacyController( |
||||
final Enclave enclave, |
||||
final PrivateStateStorage privateStateStorage, |
||||
final WorldStateArchive privateWorldStateArchive, |
||||
final PrivateTransactionValidator privateTransactionValidator, |
||||
final PrivateMarkerTransactionFactory privateMarkerTransactionFactory) { |
||||
this.enclave = enclave; |
||||
this.privateStateStorage = privateStateStorage; |
||||
this.privateWorldStateArchive = privateWorldStateArchive; |
||||
this.privateTransactionValidator = privateTransactionValidator; |
||||
this.privateMarkerTransactionFactory = privateMarkerTransactionFactory; |
||||
} |
||||
|
||||
@Override |
||||
public SendTransactionResponse sendTransaction( |
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) { |
||||
try { |
||||
LOG.trace("Storing private transaction in enclave"); |
||||
final SendResponse sendResponse = sendRequest(privateTransaction, enclavePublicKey); |
||||
final String enclaveKey = sendResponse.getKey(); |
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) { |
||||
final String privacyGroupId = privateTransaction.getPrivacyGroupId().get().toBase64String(); |
||||
return new SendTransactionResponse(enclaveKey, privacyGroupId); |
||||
} else { |
||||
final String privateFrom = privateTransaction.getPrivateFrom().toBase64String(); |
||||
final String privacyGroupId = getPrivacyGroupId(enclaveKey, privateFrom); |
||||
return new SendTransactionResponse(enclaveKey, privacyGroupId); |
||||
} |
||||
} catch (Exception e) { |
||||
LOG.error("Failed to store private transaction in enclave", e); |
||||
throw e; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public ReceiveResponse retrieveTransaction( |
||||
final String enclaveKey, final String enclavePublicKey) { |
||||
return enclave.receive(enclaveKey, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public PrivacyGroup createPrivacyGroup( |
||||
final List<String> addresses, |
||||
final String name, |
||||
final String description, |
||||
final String enclavePublicKey) { |
||||
return enclave.createPrivacyGroup(addresses, enclavePublicKey, name, description); |
||||
} |
||||
|
||||
@Override |
||||
public String deletePrivacyGroup(final String privacyGroupId, final String enclavePublicKey) { |
||||
return enclave.deletePrivacyGroup(privacyGroupId, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public PrivacyGroup[] findPrivacyGroup( |
||||
final List<String> addresses, final String enclavePublicKey) { |
||||
return enclave.findPrivacyGroup(addresses); |
||||
} |
||||
|
||||
@Override |
||||
public Transaction createPrivacyMarkerTransaction( |
||||
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) { |
||||
return privateMarkerTransactionFactory.create(transactionEnclaveKey, privateTransaction); |
||||
} |
||||
|
||||
@Override |
||||
public ValidationResult<TransactionValidator.TransactionInvalidReason> validatePrivateTransaction( |
||||
final PrivateTransaction privateTransaction, |
||||
final String privacyGroupId, |
||||
final String enclavePublicKey) { |
||||
return privateTransactionValidator.validate( |
||||
privateTransaction, |
||||
determineBesuNonce(privateTransaction.getSender(), privacyGroupId, enclavePublicKey)); |
||||
} |
||||
|
||||
@Override |
||||
public long determineEeaNonce( |
||||
final String privateFrom, |
||||
final String[] privateFor, |
||||
final Address address, |
||||
final String enclavePublicKey) { |
||||
final List<String> groupMembers = Lists.asList(privateFrom, privateFor); |
||||
|
||||
final List<PrivacyGroup> matchingGroups = |
||||
Lists.newArrayList(enclave.findPrivacyGroup(groupMembers)); |
||||
|
||||
final List<PrivacyGroup> legacyGroups = |
||||
matchingGroups.stream() |
||||
.filter(group -> group.getType() == Type.LEGACY) |
||||
.collect(Collectors.toList()); |
||||
|
||||
if (legacyGroups.size() == 0) { |
||||
// the legacy group does not exist yet
|
||||
return 0; |
||||
} |
||||
Preconditions.checkArgument( |
||||
legacyGroups.size() == 1, |
||||
String.format( |
||||
"Found invalid number of privacy groups (%d), expected 1.", legacyGroups.size())); |
||||
|
||||
final String privacyGroupId = legacyGroups.get(0).getPrivacyGroupId(); |
||||
|
||||
return determineBesuNonce(address, privacyGroupId, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public long determineBesuNonce( |
||||
final Address sender, final String privacyGroupId, final String enclavePublicKey) { |
||||
return privateStateStorage |
||||
.getLatestStateRoot(Bytes.fromBase64String(privacyGroupId)) |
||||
.map( |
||||
lastRootHash -> |
||||
privateWorldStateArchive |
||||
.getMutable(lastRootHash) |
||||
.map( |
||||
worldState -> { |
||||
final Account maybePrivateSender = worldState.get(sender); |
||||
|
||||
if (maybePrivateSender != null) { |
||||
return maybePrivateSender.getNonce(); |
||||
} |
||||
// account has not interacted in this private state
|
||||
return Account.DEFAULT_NONCE; |
||||
}) |
||||
// private state does not exist
|
||||
.orElse(Account.DEFAULT_NONCE)) |
||||
.orElse( |
||||
// private state does not exist
|
||||
Account.DEFAULT_NONCE); |
||||
} |
||||
|
||||
private SendResponse sendRequest( |
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) { |
||||
final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); |
||||
privateTransaction.writeTo(rlpOutput); |
||||
final String payload = rlpOutput.encoded().toBase64String(); |
||||
|
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) { |
||||
return enclave.send( |
||||
payload, enclavePublicKey, privateTransaction.getPrivacyGroupId().get().toBase64String()); |
||||
} else { |
||||
final List<String> privateFor = |
||||
privateTransaction.getPrivateFor().get().stream() |
||||
.map(Bytes::toBase64String) |
||||
.collect(Collectors.toList()); |
||||
|
||||
if (privateFor.isEmpty()) { |
||||
privateFor.add(privateTransaction.getPrivateFrom().toBase64String()); |
||||
} |
||||
return enclave.send( |
||||
payload, privateTransaction.getPrivateFrom().toBase64String(), privateFor); |
||||
} |
||||
} |
||||
|
||||
private String getPrivacyGroupId(final String key, final String privateFrom) { |
||||
LOG.debug("Getting privacy group for key {} and privateFrom {}", key, privateFrom); |
||||
try { |
||||
return enclave.receive(key, privateFrom).getPrivacyGroupId(); |
||||
} catch (final RuntimeException e) { |
||||
LOG.error("Failed to retrieve private transaction in enclave", e); |
||||
throw e; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,133 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.privacy; |
||||
|
||||
import org.hyperledger.besu.enclave.Enclave; |
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup; |
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse; |
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.Transaction; |
||||
import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason; |
||||
import org.hyperledger.besu.ethereum.mainnet.ValidationResult; |
||||
|
||||
import java.util.List; |
||||
|
||||
public class MultiTenancyPrivacyController implements PrivacyController { |
||||
|
||||
private final PrivacyController privacyController; |
||||
private final Enclave enclave; |
||||
|
||||
public MultiTenancyPrivacyController( |
||||
final PrivacyController privacyController, final Enclave enclave) { |
||||
this.privacyController = privacyController; |
||||
this.enclave = enclave; |
||||
} |
||||
|
||||
@Override |
||||
public SendTransactionResponse sendTransaction( |
||||
final PrivateTransaction privateTransaction, final String enclavePublicKey) { |
||||
verifyPrivateFromMatchesEnclavePublicKey( |
||||
privateTransaction.getPrivateFrom().toBase64String(), enclavePublicKey); |
||||
if (privateTransaction.getPrivacyGroupId().isPresent()) { |
||||
verifyPrivacyGroupContainsEnclavePublicKey( |
||||
privateTransaction.getPrivacyGroupId().get().toBase64String(), enclavePublicKey); |
||||
} |
||||
return privacyController.sendTransaction(privateTransaction, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public ReceiveResponse retrieveTransaction( |
||||
final String enclaveKey, final String enclavePublicKey) { |
||||
// no validation necessary as the enclave receive only returns data for the enclave public key
|
||||
return privacyController.retrieveTransaction(enclaveKey, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public PrivacyGroup createPrivacyGroup( |
||||
final List<String> addresses, |
||||
final String name, |
||||
final String description, |
||||
final String enclavePublicKey) { |
||||
// no validation necessary as the enclave createPrivacyGroup fails if the addresses don't
|
||||
// include the from (enclavePublicKey)
|
||||
return privacyController.createPrivacyGroup(addresses, name, description, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public String deletePrivacyGroup(final String privacyGroupId, final String enclavePublicKey) { |
||||
verifyPrivacyGroupContainsEnclavePublicKey(privacyGroupId, enclavePublicKey); |
||||
return privacyController.deletePrivacyGroup(privacyGroupId, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public PrivacyGroup[] findPrivacyGroup( |
||||
final List<String> addresses, final String enclavePublicKey) { |
||||
if (!addresses.contains(enclavePublicKey)) { |
||||
throw new MultiTenancyValidationException( |
||||
"Privacy group addresses must contain the enclave public key"); |
||||
} |
||||
return privacyController.findPrivacyGroup(addresses, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public Transaction createPrivacyMarkerTransaction( |
||||
final String transactionEnclaveKey, final PrivateTransaction privateTransaction) { |
||||
return privacyController.createPrivacyMarkerTransaction( |
||||
transactionEnclaveKey, privateTransaction); |
||||
} |
||||
|
||||
@Override |
||||
public ValidationResult<TransactionInvalidReason> validatePrivateTransaction( |
||||
final PrivateTransaction privateTransaction, |
||||
final String privacyGroupId, |
||||
final String enclavePublicKey) { |
||||
return privacyController.validatePrivateTransaction( |
||||
privateTransaction, privacyGroupId, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public long determineEeaNonce( |
||||
final String privateFrom, |
||||
final String[] privateFor, |
||||
final Address address, |
||||
final String enclavePublicKey) { |
||||
verifyPrivateFromMatchesEnclavePublicKey(privateFrom, enclavePublicKey); |
||||
return privacyController.determineEeaNonce(privateFrom, privateFor, address, enclavePublicKey); |
||||
} |
||||
|
||||
@Override |
||||
public long determineBesuNonce( |
||||
final Address sender, final String privacyGroupId, final String enclavePublicKey) { |
||||
verifyPrivacyGroupContainsEnclavePublicKey(privacyGroupId, enclavePublicKey); |
||||
return privacyController.determineBesuNonce(sender, privacyGroupId, enclavePublicKey); |
||||
} |
||||
|
||||
private void verifyPrivateFromMatchesEnclavePublicKey( |
||||
final String privateFrom, final String enclavePublicKey) { |
||||
if (!privateFrom.equals(enclavePublicKey)) { |
||||
throw new MultiTenancyValidationException( |
||||
"Transaction privateFrom must match enclave public key"); |
||||
} |
||||
} |
||||
|
||||
private void verifyPrivacyGroupContainsEnclavePublicKey( |
||||
final String privacyGroupId, final String enclavePublicKey) { |
||||
final PrivacyGroup privacyGroup = enclave.retrievePrivacyGroup(privacyGroupId); |
||||
if (!privacyGroup.getMembers().contains(enclavePublicKey)) { |
||||
throw new MultiTenancyValidationException( |
||||
"Privacy group must contain the enclave public key"); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,22 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.privacy; |
||||
|
||||
public class MultiTenancyValidationException extends RuntimeException { |
||||
|
||||
public MultiTenancyValidationException(final String message) { |
||||
super(message); |
||||
} |
||||
} |
@ -0,0 +1,313 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.privacy; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.Mockito.never; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.enclave.Enclave; |
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup; |
||||
import org.hyperledger.besu.enclave.types.PrivacyGroup.Type; |
||||
import org.hyperledger.besu.enclave.types.ReceiveResponse; |
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.apache.tuweni.bytes.Bytes; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class MultiTenancyPrivacyControllerTest { |
||||
|
||||
private static final String ENCLAVE_PUBLIC_KEY1 = "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="; |
||||
private static final String ENCLAVE_PUBLIC_KEY2 = "OnviftjiizpjRt+HTuFBsKo2bVqD+nNlNYL5EE7y3Id="; |
||||
private static final String PRIVACY_GROUP_ID = "nNlNYL5EE7y3IdM="; |
||||
private static final String ENCLAVE_KEY = "Ko2bVqD"; |
||||
|
||||
@Mock private PrivacyController privacyController; |
||||
@Mock private Enclave enclave; |
||||
|
||||
private MultiTenancyPrivacyController multiTenancyPrivacyController; |
||||
|
||||
@Before |
||||
public void setup() { |
||||
multiTenancyPrivacyController = new MultiTenancyPrivacyController(privacyController, enclave); |
||||
} |
||||
|
||||
@Test |
||||
public void |
||||
sendsEeaTransactionWithMatchingPrivateFromAndEnclavePublicKeyAndProducesSuccessfulResponse() { |
||||
final PrivateTransaction transaction = |
||||
PrivateTransaction.builder() |
||||
.privateFrom(Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY1)) |
||||
.build(); |
||||
|
||||
when(privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(new SendTransactionResponse(ENCLAVE_KEY, PRIVACY_GROUP_ID)); |
||||
|
||||
final SendTransactionResponse response = |
||||
multiTenancyPrivacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(response.getEnclaveKey()).isEqualTo(ENCLAVE_KEY); |
||||
assertThat(response.getPrivacyGroupId()).isEqualTo(PRIVACY_GROUP_ID); |
||||
verify(privacyController).sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void |
||||
sendsBesuTransactionWithEnclavePublicKeyInPrivacyGroupAndProducesSuccessfulResponse() { |
||||
final PrivateTransaction transaction = |
||||
PrivateTransaction.builder() |
||||
.privateFrom(Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY1)) |
||||
.privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) |
||||
.build(); |
||||
|
||||
when(privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(new SendTransactionResponse(ENCLAVE_KEY, PRIVACY_GROUP_ID)); |
||||
final PrivacyGroup privacyGroupWithEnclavePublicKey = |
||||
new PrivacyGroup( |
||||
PRIVACY_GROUP_ID, |
||||
Type.PANTHEON, |
||||
"", |
||||
"", |
||||
List.of(ENCLAVE_PUBLIC_KEY1, ENCLAVE_PUBLIC_KEY2)); |
||||
when(enclave.retrievePrivacyGroup(PRIVACY_GROUP_ID)) |
||||
.thenReturn(privacyGroupWithEnclavePublicKey); |
||||
|
||||
final SendTransactionResponse response = |
||||
multiTenancyPrivacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(response.getEnclaveKey()).isEqualTo(ENCLAVE_KEY); |
||||
assertThat(response.getPrivacyGroupId()).isEqualTo(PRIVACY_GROUP_ID); |
||||
verify(privacyController).sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); |
||||
verify(enclave).retrievePrivacyGroup(PRIVACY_GROUP_ID); |
||||
} |
||||
|
||||
@Test |
||||
public void sendEeaTransactionFailsWithValidationExceptionWhenPrivateFromDoesNotMatch() { |
||||
final PrivateTransaction transaction = |
||||
PrivateTransaction.builder() |
||||
.privateFrom(Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY2)) |
||||
.build(); |
||||
|
||||
assertThatThrownBy( |
||||
() -> multiTenancyPrivacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessage("Transaction privateFrom must match enclave public key"); |
||||
|
||||
verify(privacyController, never()).sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void sendBesuTransactionFailsWithValidationExceptionWhenPrivateFromDoesNotMatch() { |
||||
final PrivateTransaction transaction = |
||||
PrivateTransaction.builder() |
||||
.privateFrom(Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY2)) |
||||
.privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) |
||||
.build(); |
||||
|
||||
assertThatThrownBy( |
||||
() -> multiTenancyPrivacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessage("Transaction privateFrom must match enclave public key"); |
||||
|
||||
verify(privacyController, never()).sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void |
||||
sendBesuTransactionFailsWithValidationExceptionWhenPrivacyGroupDoesNotContainEnclavePublicKey() { |
||||
final PrivateTransaction transaction = |
||||
PrivateTransaction.builder() |
||||
.privateFrom(Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY1)) |
||||
.privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) |
||||
.build(); |
||||
|
||||
final PrivacyGroup privacyGroupWithoutEnclavePublicKey = |
||||
new PrivacyGroup(PRIVACY_GROUP_ID, Type.PANTHEON, "", "", List.of(ENCLAVE_PUBLIC_KEY2)); |
||||
when(enclave.retrievePrivacyGroup(PRIVACY_GROUP_ID)) |
||||
.thenReturn(privacyGroupWithoutEnclavePublicKey); |
||||
|
||||
assertThatThrownBy( |
||||
() -> multiTenancyPrivacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessage("Privacy group must contain the enclave public key"); |
||||
|
||||
verify(privacyController, never()).sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void retrieveTransactionDelegatesToPrivacyController() { |
||||
final ReceiveResponse delegateRetrieveResponse = |
||||
new ReceiveResponse(new byte[] {}, PRIVACY_GROUP_ID, ENCLAVE_KEY); |
||||
when(privacyController.retrieveTransaction(ENCLAVE_KEY, ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(delegateRetrieveResponse); |
||||
|
||||
final ReceiveResponse multiTenancyRetrieveResponse = |
||||
multiTenancyPrivacyController.retrieveTransaction(ENCLAVE_KEY, ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(multiTenancyRetrieveResponse) |
||||
.isEqualToComparingFieldByField(delegateRetrieveResponse); |
||||
verify(privacyController).retrieveTransaction(ENCLAVE_KEY, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void createPrivacyGroupDelegatesToPrivacyController() { |
||||
final List<String> addresses = List.of(ENCLAVE_PUBLIC_KEY1, ENCLAVE_PUBLIC_KEY2); |
||||
final PrivacyGroup delegatePrivacyGroup = |
||||
new PrivacyGroup(PRIVACY_GROUP_ID, Type.PANTHEON, "name", "description", addresses); |
||||
|
||||
when(privacyController.createPrivacyGroup( |
||||
addresses, "name", "description", ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(delegatePrivacyGroup); |
||||
|
||||
final PrivacyGroup privacyGroup = |
||||
multiTenancyPrivacyController.createPrivacyGroup( |
||||
addresses, "name", "description", ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(privacyGroup).isEqualToComparingFieldByField(delegatePrivacyGroup); |
||||
verify(privacyController) |
||||
.createPrivacyGroup(addresses, "name", "description", ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void deletesPrivacyGroupWhenEnclavePublicKeyInPrivacyGroup() { |
||||
final PrivacyGroup privacyGroupWithEnclavePublicKey = |
||||
new PrivacyGroup( |
||||
PRIVACY_GROUP_ID, |
||||
Type.PANTHEON, |
||||
"", |
||||
"", |
||||
List.of(ENCLAVE_PUBLIC_KEY1, ENCLAVE_PUBLIC_KEY2)); |
||||
when(enclave.retrievePrivacyGroup(PRIVACY_GROUP_ID)) |
||||
.thenReturn(privacyGroupWithEnclavePublicKey); |
||||
when(privacyController.deletePrivacyGroup(PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(ENCLAVE_PUBLIC_KEY1); |
||||
|
||||
final String privacyGroupId = |
||||
multiTenancyPrivacyController.deletePrivacyGroup(PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(privacyGroupId).isEqualTo(ENCLAVE_PUBLIC_KEY1); |
||||
verify(privacyController).deletePrivacyGroup(PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void |
||||
deletePrivacyGroupFailsWithValidationExceptionWhenPrivacyGroupDoesNotContainEnclavePublicKey() { |
||||
final PrivacyGroup privacyGroupWithoutEnclavePublicKey = |
||||
new PrivacyGroup(PRIVACY_GROUP_ID, Type.PANTHEON, "", "", List.of(ENCLAVE_PUBLIC_KEY2)); |
||||
when(enclave.retrievePrivacyGroup(PRIVACY_GROUP_ID)) |
||||
.thenReturn(privacyGroupWithoutEnclavePublicKey); |
||||
|
||||
assertThatThrownBy( |
||||
() -> |
||||
multiTenancyPrivacyController.deletePrivacyGroup( |
||||
PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessage("Privacy group must contain the enclave public key"); |
||||
} |
||||
|
||||
@Test |
||||
public void findsPrivacyGroupWhenEnclavePublicKeyInAddresses() { |
||||
final List<String> addresses = List.of(ENCLAVE_PUBLIC_KEY1, ENCLAVE_PUBLIC_KEY2); |
||||
final PrivacyGroup privacyGroup = |
||||
new PrivacyGroup(PRIVACY_GROUP_ID, Type.PANTHEON, "", "", List.of(ENCLAVE_PUBLIC_KEY2)); |
||||
when(privacyController.findPrivacyGroup(addresses, ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(new PrivacyGroup[] {privacyGroup}); |
||||
|
||||
final PrivacyGroup[] privacyGroups = |
||||
multiTenancyPrivacyController.findPrivacyGroup(addresses, ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(privacyGroups).hasSize(1); |
||||
assertThat(privacyGroups[0]).isEqualToComparingFieldByField(privacyGroup); |
||||
verify(privacyController).findPrivacyGroup(addresses, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void findPrivacyGroupFailsWithValidationExceptionWhenEnclavePublicKeyNotInAddresses() { |
||||
final List<String> addresses = List.of(ENCLAVE_PUBLIC_KEY2); |
||||
|
||||
assertThatThrownBy( |
||||
() -> multiTenancyPrivacyController.findPrivacyGroup(addresses, ENCLAVE_PUBLIC_KEY1)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessage("Privacy group addresses must contain the enclave public key"); |
||||
} |
||||
|
||||
@Test |
||||
public void determinesEeaNonceWhenPrivateFromMatchesEnclavePublicKey() { |
||||
final String[] privateFor = {ENCLAVE_PUBLIC_KEY2}; |
||||
when(privacyController.determineEeaNonce( |
||||
ENCLAVE_PUBLIC_KEY1, privateFor, Address.ZERO, ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(10L); |
||||
|
||||
final long nonce = |
||||
multiTenancyPrivacyController.determineEeaNonce( |
||||
ENCLAVE_PUBLIC_KEY1, privateFor, Address.ZERO, ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(nonce).isEqualTo(10); |
||||
verify(privacyController) |
||||
.determineEeaNonce(ENCLAVE_PUBLIC_KEY1, privateFor, Address.ZERO, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void |
||||
determineEeaNonceFailsWithValidationExceptionWhenPrivateFromDoesNotMatchEnclavePublicKey() { |
||||
final String[] privateFor = {ENCLAVE_PUBLIC_KEY2}; |
||||
assertThatThrownBy( |
||||
() -> |
||||
multiTenancyPrivacyController.determineEeaNonce( |
||||
ENCLAVE_PUBLIC_KEY2, privateFor, Address.ZERO, ENCLAVE_PUBLIC_KEY1)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessage("Transaction privateFrom must match enclave public key"); |
||||
} |
||||
|
||||
@Test |
||||
public void determineBesuNonceWhenEnclavePublicKeyInPrivacyGroup() { |
||||
final PrivacyGroup privacyGroupWithEnclavePublicKey = |
||||
new PrivacyGroup( |
||||
PRIVACY_GROUP_ID, |
||||
Type.PANTHEON, |
||||
"", |
||||
"", |
||||
List.of(ENCLAVE_PUBLIC_KEY1, ENCLAVE_PUBLIC_KEY2)); |
||||
when(enclave.retrievePrivacyGroup(PRIVACY_GROUP_ID)) |
||||
.thenReturn(privacyGroupWithEnclavePublicKey); |
||||
when(privacyController.determineBesuNonce(Address.ZERO, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1)) |
||||
.thenReturn(10L); |
||||
|
||||
final long nonce = |
||||
multiTenancyPrivacyController.determineBesuNonce( |
||||
Address.ZERO, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1); |
||||
assertThat(nonce).isEqualTo(10); |
||||
verify(privacyController) |
||||
.determineBesuNonce(Address.ZERO, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1); |
||||
} |
||||
|
||||
@Test |
||||
public void |
||||
determineBesuNonceFailsWithValidationExceptionWhenEnclavePublicKeyNotInPrivacyGroup() { |
||||
final PrivacyGroup privacyGroupWithoutEnclavePublicKey = |
||||
new PrivacyGroup(PRIVACY_GROUP_ID, Type.PANTHEON, "", "", List.of(ENCLAVE_PUBLIC_KEY2)); |
||||
when(enclave.retrievePrivacyGroup(PRIVACY_GROUP_ID)) |
||||
.thenReturn(privacyGroupWithoutEnclavePublicKey); |
||||
|
||||
assertThatThrownBy( |
||||
() -> |
||||
multiTenancyPrivacyController.determineBesuNonce( |
||||
Address.ZERO, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY1)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessage("Privacy group must contain the enclave public key"); |
||||
} |
||||
} |
Loading…
Reference in new issue