mirror of https://github.com/hyperledger/besu
Add support for private log subscriptions (Pub-Sub API) (#858)
* Created priv_subscribe and priv_unsubscribe methods Signed-off-by: Lucas Saldanha <lucas.saldanha@consensys.net>pull/904/head
parent
f2244ee531
commit
06ca344bae
@ -0,0 +1,43 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionRequestMapper; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController; |
||||
|
||||
abstract class AbstractPrivateSubscriptionMethod extends AbstractSubscriptionMethod { |
||||
|
||||
private final PrivacyController privacyController; |
||||
private final EnclavePublicKeyProvider enclavePublicKeyProvider; |
||||
|
||||
AbstractPrivateSubscriptionMethod( |
||||
final SubscriptionManager subscriptionManager, |
||||
final SubscriptionRequestMapper mapper, |
||||
final PrivacyController privacyController, |
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider) { |
||||
super(subscriptionManager, mapper); |
||||
this.privacyController = privacyController; |
||||
this.enclavePublicKeyProvider = enclavePublicKeyProvider; |
||||
} |
||||
|
||||
void checkIfPrivacyGroupMatchesAuthenticatedEnclaveKey( |
||||
final JsonRpcRequestContext request, final String privacyGroupId) { |
||||
final String enclavePublicKey = enclavePublicKeyProvider.getEnclaveKey(request.getUser()); |
||||
privacyController.verifyPrivacyGroupContainsEnclavePublicKey(privacyGroupId, enclavePublicKey); |
||||
} |
||||
} |
@ -0,0 +1,64 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.InvalidSubscriptionRequestException; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.PrivateSubscribeRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionRequestMapper; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController; |
||||
|
||||
public class PrivSubscribe extends AbstractPrivateSubscriptionMethod { |
||||
|
||||
public PrivSubscribe( |
||||
final SubscriptionManager subscriptionManager, |
||||
final SubscriptionRequestMapper mapper, |
||||
final PrivacyController privacyController, |
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider) { |
||||
super(subscriptionManager, mapper, privacyController, enclavePublicKeyProvider); |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return RpcMethod.PRIV_SUBSCRIBE.getMethodName(); |
||||
} |
||||
|
||||
@Override |
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||
try { |
||||
final PrivateSubscribeRequest subscribeRequest = |
||||
getMapper().mapPrivateSubscribeRequest(requestContext); |
||||
|
||||
checkIfPrivacyGroupMatchesAuthenticatedEnclaveKey( |
||||
requestContext, subscribeRequest.getPrivacyGroupId()); |
||||
|
||||
final Long subscriptionId = subscriptionManager().subscribe(subscribeRequest); |
||||
|
||||
return new JsonRpcSuccessResponse( |
||||
requestContext.getRequest().getId(), Quantity.create(subscriptionId)); |
||||
} catch (final InvalidSubscriptionRequestException isEx) { |
||||
return new JsonRpcErrorResponse( |
||||
requestContext.getRequest().getId(), JsonRpcError.INVALID_REQUEST); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,66 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionNotFoundException; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.InvalidSubscriptionRequestException; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.PrivateUnsubscribeRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionRequestMapper; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController; |
||||
|
||||
public class PrivUnsubscribe extends AbstractPrivateSubscriptionMethod { |
||||
|
||||
public PrivUnsubscribe( |
||||
final SubscriptionManager subscriptionManager, |
||||
final SubscriptionRequestMapper mapper, |
||||
final PrivacyController privacyController, |
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider) { |
||||
super(subscriptionManager, mapper, privacyController, enclavePublicKeyProvider); |
||||
} |
||||
|
||||
@Override |
||||
public String getName() { |
||||
return RpcMethod.PRIV_UNSUBSCRIBE.getMethodName(); |
||||
} |
||||
|
||||
@Override |
||||
public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { |
||||
try { |
||||
final PrivateUnsubscribeRequest unsubscribeRequest = |
||||
getMapper().mapPrivateUnsubscribeRequest(requestContext); |
||||
|
||||
checkIfPrivacyGroupMatchesAuthenticatedEnclaveKey( |
||||
requestContext, unsubscribeRequest.getPrivacyGroupId()); |
||||
|
||||
final boolean unsubscribed = subscriptionManager().unsubscribe(unsubscribeRequest); |
||||
|
||||
return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), unsubscribed); |
||||
} catch (final InvalidSubscriptionRequestException isEx) { |
||||
return new JsonRpcErrorResponse( |
||||
requestContext.getRequest().getId(), JsonRpcError.INVALID_REQUEST); |
||||
} catch (final SubscriptionNotFoundException snfEx) { |
||||
return new JsonRpcErrorResponse( |
||||
requestContext.getRequest().getId(), JsonRpcError.SUBSCRIPTION_NOT_FOUND); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,127 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.LatestNonceProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionRequestMapper; |
||||
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; |
||||
import org.hyperledger.besu.ethereum.core.Address; |
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters; |
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; |
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; |
||||
import org.hyperledger.besu.ethereum.privacy.ChainHeadPrivateNonceProvider; |
||||
import org.hyperledger.besu.ethereum.privacy.DefaultPrivacyController; |
||||
import org.hyperledger.besu.ethereum.privacy.MultiTenancyPrivacyController; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivateNonceProvider; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionSimulator; |
||||
import org.hyperledger.besu.ethereum.privacy.markertransaction.FixedKeySigningPrivateMarkerTransactionFactory; |
||||
import org.hyperledger.besu.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory; |
||||
import org.hyperledger.besu.ethereum.privacy.markertransaction.RandomSigningPrivateMarkerTransactionFactory; |
||||
|
||||
import java.math.BigInteger; |
||||
import java.util.Collection; |
||||
import java.util.Optional; |
||||
import java.util.Set; |
||||
|
||||
public class PrivateWebSocketMethodsFactory { |
||||
|
||||
private final PrivacyParameters privacyParameters; |
||||
private final SubscriptionManager subscriptionManager; |
||||
private final ProtocolSchedule<?> protocolSchedule; |
||||
private final BlockchainQueries blockchainQueries; |
||||
private final TransactionPool transactionPool; |
||||
|
||||
public PrivateWebSocketMethodsFactory( |
||||
final PrivacyParameters privacyParameters, |
||||
final SubscriptionManager subscriptionManager, |
||||
final ProtocolSchedule<?> protocolSchedule, |
||||
final BlockchainQueries blockchainQueries, |
||||
final TransactionPool transactionPool) { |
||||
this.privacyParameters = privacyParameters; |
||||
this.subscriptionManager = subscriptionManager; |
||||
this.protocolSchedule = protocolSchedule; |
||||
this.blockchainQueries = blockchainQueries; |
||||
this.transactionPool = transactionPool; |
||||
} |
||||
|
||||
public Collection<JsonRpcMethod> methods() { |
||||
final SubscriptionRequestMapper subscriptionRequestMapper = new SubscriptionRequestMapper(); |
||||
final EnclavePublicKeyProvider enclavePublicKeyProvider = |
||||
EnclavePublicKeyProvider.build(privacyParameters); |
||||
final PrivacyController privacyController = createPrivacyController(); |
||||
|
||||
return Set.of( |
||||
new PrivSubscribe( |
||||
subscriptionManager, |
||||
subscriptionRequestMapper, |
||||
privacyController, |
||||
enclavePublicKeyProvider), |
||||
new PrivUnsubscribe( |
||||
subscriptionManager, |
||||
subscriptionRequestMapper, |
||||
privacyController, |
||||
enclavePublicKeyProvider)); |
||||
} |
||||
|
||||
private PrivateMarkerTransactionFactory createPrivateMarkerTransactionFactory() { |
||||
|
||||
final Address privateContractAddress = |
||||
Address.privacyPrecompiled(privacyParameters.getPrivacyAddress()); |
||||
|
||||
if (privacyParameters.getSigningKeyPair().isPresent()) { |
||||
return new FixedKeySigningPrivateMarkerTransactionFactory( |
||||
privateContractAddress, |
||||
new LatestNonceProvider(blockchainQueries, transactionPool.getPendingTransactions()), |
||||
privacyParameters.getSigningKeyPair().get()); |
||||
} |
||||
return new RandomSigningPrivateMarkerTransactionFactory(privateContractAddress); |
||||
} |
||||
|
||||
private PrivacyController createPrivacyController() { |
||||
final Optional<BigInteger> chainId = protocolSchedule.getChainId(); |
||||
final DefaultPrivacyController defaultPrivacyController = |
||||
new DefaultPrivacyController( |
||||
blockchainQueries.getBlockchain(), |
||||
privacyParameters, |
||||
chainId, |
||||
createPrivateMarkerTransactionFactory(), |
||||
createPrivateTransactionSimulator(), |
||||
createPrivateNonceProvider(), |
||||
privacyParameters.getPrivateWorldStateReader()); |
||||
return privacyParameters.isMultiTenancyEnabled() |
||||
? new MultiTenancyPrivacyController( |
||||
defaultPrivacyController, chainId, privacyParameters.getEnclave()) |
||||
: defaultPrivacyController; |
||||
} |
||||
|
||||
private PrivateTransactionSimulator createPrivateTransactionSimulator() { |
||||
return new PrivateTransactionSimulator( |
||||
blockchainQueries.getBlockchain(), |
||||
blockchainQueries.getWorldStateArchive(), |
||||
protocolSchedule, |
||||
privacyParameters); |
||||
} |
||||
|
||||
private PrivateNonceProvider createPrivateNonceProvider() { |
||||
return new ChainHeadPrivateNonceProvider( |
||||
blockchainQueries.getBlockchain(), |
||||
privacyParameters.getPrivateStateRootResolver(), |
||||
privacyParameters.getPrivateWorldStateArchive()); |
||||
} |
||||
} |
@ -0,0 +1,35 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.logs; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; |
||||
|
||||
public class PrivateLogsSubscription extends LogsSubscription { |
||||
|
||||
private final String privacyGroupId; |
||||
|
||||
public PrivateLogsSubscription( |
||||
final Long subscriptionId, |
||||
final String connectionId, |
||||
final FilterParameter filterParameter, |
||||
final String privacyGroupId) { |
||||
super(subscriptionId, connectionId, filterParameter); |
||||
this.privacyGroupId = privacyGroupId; |
||||
} |
||||
|
||||
public String getPrivacyGroupId() { |
||||
return privacyGroupId; |
||||
} |
||||
} |
@ -0,0 +1,58 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class PrivateSubscribeRequest extends SubscribeRequest { |
||||
|
||||
private final String privacyGroupId; |
||||
|
||||
public PrivateSubscribeRequest( |
||||
final SubscriptionType subscriptionType, |
||||
final FilterParameter filterParameter, |
||||
final Boolean includeTransaction, |
||||
final String connectionId, |
||||
final String privacyGroupId) { |
||||
super(subscriptionType, filterParameter, includeTransaction, connectionId); |
||||
this.privacyGroupId = privacyGroupId; |
||||
} |
||||
|
||||
public String getPrivacyGroupId() { |
||||
return privacyGroupId; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(final Object o) { |
||||
if (this == o) { |
||||
return true; |
||||
} |
||||
if (o == null || getClass() != o.getClass()) { |
||||
return false; |
||||
} |
||||
if (!super.equals(o)) { |
||||
return false; |
||||
} |
||||
PrivateSubscribeRequest that = (PrivateSubscribeRequest) o; |
||||
return privacyGroupId.equals(that.privacyGroupId); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(super.hashCode(), privacyGroupId); |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
public class PrivateUnsubscribeRequest extends UnsubscribeRequest { |
||||
|
||||
private final String privacyGroupId; |
||||
|
||||
public PrivateUnsubscribeRequest( |
||||
final Long subscriptionId, final String connectionId, final String privacyGroupId) { |
||||
super(subscriptionId, connectionId); |
||||
this.privacyGroupId = privacyGroupId; |
||||
} |
||||
|
||||
public String getPrivacyGroupId() { |
||||
return privacyGroupId; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(final Object o) { |
||||
if (this == o) { |
||||
return true; |
||||
} |
||||
if (o == null || getClass() != o.getClass()) { |
||||
return false; |
||||
} |
||||
if (!super.equals(o)) { |
||||
return false; |
||||
} |
||||
PrivateUnsubscribeRequest that = (PrivateUnsubscribeRequest) o; |
||||
return privacyGroupId.equals(that.privacyGroupId); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(super.hashCode(), privacyGroupId); |
||||
} |
||||
} |
@ -0,0 +1,144 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.eq; |
||||
import static org.mockito.Mockito.doThrow; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.InvalidSubscriptionRequestException; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.PrivateSubscribeRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionRequestMapper; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionType; |
||||
import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController; |
||||
|
||||
import io.vertx.core.json.Json; |
||||
import io.vertx.ext.auth.User; |
||||
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 PrivSubscribeTest { |
||||
|
||||
private final String ENCLAVE_KEY = "enclave_key"; |
||||
private final String PRIVACY_GROUP_ID = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; |
||||
|
||||
@Mock private SubscriptionManager subscriptionManagerMock; |
||||
@Mock private SubscriptionRequestMapper mapperMock; |
||||
@Mock private PrivacyController privacyController; |
||||
@Mock private EnclavePublicKeyProvider enclavePublicKeyProvider; |
||||
|
||||
private PrivSubscribe privSubscribe; |
||||
|
||||
@Before |
||||
public void before() { |
||||
privSubscribe = |
||||
new PrivSubscribe( |
||||
subscriptionManagerMock, mapperMock, privacyController, enclavePublicKeyProvider); |
||||
} |
||||
|
||||
@Test |
||||
public void expectedMethodName() { |
||||
assertThat(privSubscribe.getName()).isEqualTo("priv_subscribe"); |
||||
} |
||||
|
||||
@Test |
||||
public void responseContainsSubscriptionId() { |
||||
final WebSocketRpcRequest webSocketRequest = createWebSocketRpcRequest(); |
||||
final JsonRpcRequestContext jsonRpcrequestContext = new JsonRpcRequestContext(webSocketRequest); |
||||
|
||||
final PrivateSubscribeRequest subscribeRequest = |
||||
new PrivateSubscribeRequest( |
||||
SubscriptionType.LOGS, |
||||
null, |
||||
null, |
||||
webSocketRequest.getConnectionId(), |
||||
PRIVACY_GROUP_ID); |
||||
|
||||
when(mapperMock.mapPrivateSubscribeRequest(eq(jsonRpcrequestContext))) |
||||
.thenReturn(subscribeRequest); |
||||
when(subscriptionManagerMock.subscribe(eq(subscribeRequest))).thenReturn(1L); |
||||
|
||||
final JsonRpcSuccessResponse expectedResponse = |
||||
new JsonRpcSuccessResponse( |
||||
jsonRpcrequestContext.getRequest().getId(), Quantity.create((1L))); |
||||
|
||||
assertThat(privSubscribe.response(jsonRpcrequestContext)).isEqualTo(expectedResponse); |
||||
} |
||||
|
||||
@Test |
||||
public void invalidSubscribeRequestRespondsInvalidRequestResponse() { |
||||
final WebSocketRpcRequest webSocketRequest = createWebSocketRpcRequest(); |
||||
final JsonRpcRequestContext jsonRpcrequestContext = new JsonRpcRequestContext(webSocketRequest); |
||||
|
||||
when(mapperMock.mapPrivateSubscribeRequest(any())) |
||||
.thenThrow(new InvalidSubscriptionRequestException()); |
||||
|
||||
final JsonRpcErrorResponse expectedResponse = |
||||
new JsonRpcErrorResponse( |
||||
jsonRpcrequestContext.getRequest().getId(), JsonRpcError.INVALID_REQUEST); |
||||
|
||||
assertThat(privSubscribe.response(jsonRpcrequestContext)).isEqualTo(expectedResponse); |
||||
} |
||||
|
||||
@Test |
||||
public void multiTenancyCheckFailure() { |
||||
final User user = mock(User.class); |
||||
final WebSocketRpcRequest webSocketRequest = createWebSocketRpcRequest(); |
||||
final JsonRpcRequestContext jsonRpcrequestContext = |
||||
new JsonRpcRequestContext(webSocketRequest, user); |
||||
|
||||
final PrivateSubscribeRequest subscribeRequest = |
||||
new PrivateSubscribeRequest( |
||||
SubscriptionType.LOGS, |
||||
null, |
||||
null, |
||||
webSocketRequest.getConnectionId(), |
||||
PRIVACY_GROUP_ID); |
||||
|
||||
when(mapperMock.mapPrivateSubscribeRequest(any())).thenReturn(subscribeRequest); |
||||
when(enclavePublicKeyProvider.getEnclaveKey(any())).thenReturn(ENCLAVE_KEY); |
||||
doThrow(new MultiTenancyValidationException("msg")) |
||||
.when(privacyController) |
||||
.verifyPrivacyGroupContainsEnclavePublicKey(eq(PRIVACY_GROUP_ID), eq(ENCLAVE_KEY)); |
||||
|
||||
assertThatThrownBy(() -> privSubscribe.response(jsonRpcrequestContext)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessageContaining("msg"); |
||||
} |
||||
|
||||
private WebSocketRpcRequest createWebSocketRpcRequest() { |
||||
return Json.decodeValue( |
||||
"{\"id\": 1, \"method\": \"priv_subscribe\", \"params\": [\"" |
||||
+ PRIVACY_GROUP_ID |
||||
+ "\", \"logs\"], \"connectionId\": \"1\"}", |
||||
WebSocketRpcRequest.class); |
||||
} |
||||
} |
@ -0,0 +1,150 @@ |
||||
/* |
||||
* Copyright ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
* |
||||
* SPDX-License-Identifier: Apache-2.0 |
||||
*/ |
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.any; |
||||
import static org.mockito.ArgumentMatchers.eq; |
||||
import static org.mockito.Mockito.doThrow; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.when; |
||||
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionNotFoundException; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.InvalidSubscriptionRequestException; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.PrivateUnsubscribeRequest; |
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionRequestMapper; |
||||
import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException; |
||||
import org.hyperledger.besu.ethereum.privacy.PrivacyController; |
||||
|
||||
import io.vertx.core.json.Json; |
||||
import io.vertx.ext.auth.User; |
||||
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 PrivUnsubscribeTest { |
||||
|
||||
private final String ENCLAVE_KEY = "enclave_key"; |
||||
private final String PRIVACY_GROUP_ID = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; |
||||
private final String CONNECTION_ID = "test-connection-id"; |
||||
|
||||
@Mock private SubscriptionManager subscriptionManagerMock; |
||||
@Mock private SubscriptionRequestMapper mapperMock; |
||||
@Mock private PrivacyController privacyController; |
||||
@Mock private EnclavePublicKeyProvider enclavePublicKeyProvider; |
||||
|
||||
private PrivUnsubscribe privUnsubscribe; |
||||
|
||||
@Before |
||||
public void before() { |
||||
privUnsubscribe = |
||||
new PrivUnsubscribe( |
||||
subscriptionManagerMock, mapperMock, privacyController, enclavePublicKeyProvider); |
||||
} |
||||
|
||||
@Test |
||||
public void expectedMethodName() { |
||||
assertThat(privUnsubscribe.getName()).isEqualTo("priv_unsubscribe"); |
||||
} |
||||
|
||||
@Test |
||||
public void responseContainsUnsubscribeStatus() { |
||||
final JsonRpcRequestContext request = createPrivUnsubscribeRequest(); |
||||
final PrivateUnsubscribeRequest unsubscribeRequest = |
||||
new PrivateUnsubscribeRequest(1L, CONNECTION_ID, PRIVACY_GROUP_ID); |
||||
when(mapperMock.mapPrivateUnsubscribeRequest(eq(request))).thenReturn(unsubscribeRequest); |
||||
when(subscriptionManagerMock.unsubscribe(eq(unsubscribeRequest))).thenReturn(true); |
||||
|
||||
final JsonRpcSuccessResponse expectedResponse = |
||||
new JsonRpcSuccessResponse(request.getRequest().getId(), true); |
||||
|
||||
assertThat(privUnsubscribe.response(request)).isEqualTo(expectedResponse); |
||||
} |
||||
|
||||
@Test |
||||
public void invalidUnsubscribeRequestReturnsInvalidRequestResponse() { |
||||
final JsonRpcRequestContext request = createPrivUnsubscribeRequest(); |
||||
when(mapperMock.mapPrivateUnsubscribeRequest(any())) |
||||
.thenThrow(new InvalidSubscriptionRequestException()); |
||||
|
||||
final JsonRpcErrorResponse expectedResponse = |
||||
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_REQUEST); |
||||
|
||||
assertThat(privUnsubscribe.response(request)).isEqualTo(expectedResponse); |
||||
} |
||||
|
||||
@Test |
||||
public void whenSubscriptionNotFoundReturnError() { |
||||
final JsonRpcRequestContext request = createPrivUnsubscribeRequest(); |
||||
when(mapperMock.mapPrivateUnsubscribeRequest(any())) |
||||
.thenReturn(mock(PrivateUnsubscribeRequest.class)); |
||||
when(subscriptionManagerMock.unsubscribe(any())) |
||||
.thenThrow(new SubscriptionNotFoundException(1L)); |
||||
|
||||
final JsonRpcErrorResponse expectedResponse = |
||||
new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.SUBSCRIPTION_NOT_FOUND); |
||||
|
||||
assertThat(privUnsubscribe.response(request)).isEqualTo(expectedResponse); |
||||
} |
||||
|
||||
@Test |
||||
public void multiTenancyCheckFailure() { |
||||
final User user = mock(User.class); |
||||
final JsonRpcRequestContext jsonRpcrequestContext = createPrivUnsubscribeRequestWithUser(user); |
||||
|
||||
final PrivateUnsubscribeRequest unsubscribeRequest = |
||||
new PrivateUnsubscribeRequest(0L, CONNECTION_ID, PRIVACY_GROUP_ID); |
||||
|
||||
when(mapperMock.mapPrivateUnsubscribeRequest(any())).thenReturn(unsubscribeRequest); |
||||
when(enclavePublicKeyProvider.getEnclaveKey(any())).thenReturn(ENCLAVE_KEY); |
||||
doThrow(new MultiTenancyValidationException("msg")) |
||||
.when(privacyController) |
||||
.verifyPrivacyGroupContainsEnclavePublicKey(eq(PRIVACY_GROUP_ID), eq(ENCLAVE_KEY)); |
||||
|
||||
assertThatThrownBy(() -> privUnsubscribe.response(jsonRpcrequestContext)) |
||||
.isInstanceOf(MultiTenancyValidationException.class) |
||||
.hasMessageContaining("msg"); |
||||
} |
||||
|
||||
private JsonRpcRequestContext createPrivUnsubscribeRequest() { |
||||
return new JsonRpcRequestContext( |
||||
Json.decodeValue( |
||||
"{\"id\": 1, \"method\": \"priv_unsubscribe\", \"params\": [\"" |
||||
+ PRIVACY_GROUP_ID |
||||
+ "\", \"0x0\"]}", |
||||
JsonRpcRequest.class)); |
||||
} |
||||
|
||||
private JsonRpcRequestContext createPrivUnsubscribeRequestWithUser(final User user) { |
||||
return new JsonRpcRequestContext( |
||||
Json.decodeValue( |
||||
"{\"id\": 1, \"method\": \"priv_unsubscribe\", \"params\": [\"" |
||||
+ PRIVACY_GROUP_ID |
||||
+ "\", \"0x0\"]}", |
||||
JsonRpcRequest.class), |
||||
user); |
||||
} |
||||
} |
@ -1 +1 @@ |
||||
Subproject commit 5841af6da472fb3f19810354cf9a30afd8e72b5f |
||||
Subproject commit 6af0621522dd0274525457741291d391c10002be |
Loading…
Reference in new issue