Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Private Log Filters track enclavePublicKey and are removed when user removed from group #1298

Merged
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1235ac9
added isRemovalTransaction
macfarla Aug 12, 2020
419bda7
Merge branch 'master' of https://github.com/hyperledger/besu into mul…
macfarla Aug 12, 2020
4194532
draft of processing Private Tx Event
macfarla Aug 12, 2020
deebbfa
typo
macfarla Aug 12, 2020
edf2766
added enclavePublicKey to private log filter
macfarla Aug 12, 2020
671385b
draft removal event
macfarla Aug 12, 2020
da085c8
final params
macfarla Aug 12, 2020
553f7bb
Merge branch 'master' of https://github.com/hyperledger/besu into mul…
macfarla Aug 13, 2020
dd8cf37
Merge branch 'master' of https://github.com/hyperledger/besu into mul…
macfarla Aug 17, 2020
4a38db3
moved logic for checking group removal tx
macfarla Aug 18, 2020
cce49cc
Merge branch 'master' of https://github.com/hyperledger/besu into mul…
macfarla Aug 18, 2020
6e7d05e
wire up event subscription
macfarla Aug 18, 2020
1750580
spdx header
macfarla Aug 18, 2020
fc1edfb
added param to javadoc
macfarla Aug 18, 2020
6454b3f
Merge branch 'master' of https://github.com/hyperledger/besu into mul…
macfarla Aug 18, 2020
7cd819b
fixed todo's
macfarla Aug 19, 2020
ef68048
removed unnecessary blank line
macfarla Aug 19, 2020
7ef2528
merge
macfarla Aug 19, 2020
0705291
create event with removed user not tx sender
macfarla Aug 19, 2020
8bb848a
formatting
macfarla Aug 19, 2020
d2f6ce5
get removed participant from tx payload
macfarla Aug 20, 2020
5009793
final param
macfarla Aug 20, 2020
bdcc174
cleanup
macfarla Aug 20, 2020
65edf1e
Merge branch 'master' of https://github.com/hyperledger/besu into mul…
macfarla Aug 20, 2020
5c5baa0
only set up filtermanager to listen for group removals if multi-tenan…
macfarla Aug 20, 2020
b2a2dd6
simplified similar variable names
macfarla Aug 20, 2020
6e41ca4
store removal events and process with addBlockEvent
macfarla Aug 20, 2020
7b03933
simplify getting param
macfarla Aug 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,15 @@
import org.hyperledger.besu.ethereum.api.query.PrivacyQueries;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.PrecompiledContract;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.precompiles.privacy.OnChainPrivacyPrecompiledContract;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration;
import org.hyperledger.besu.ethereum.p2p.config.RlpxConfiguration;
Expand Down Expand Up @@ -436,6 +440,8 @@ public Runner build() {
.build();
vertx.deployVerticle(filterManager);

createPrivateTransactionObserver(filterManager, privacyParameters);

final P2PNetwork peerNetwork = networkRunner.getNetwork();

final MiningParameters miningParameters = besuController.getMiningParameters();
Expand Down Expand Up @@ -789,6 +795,26 @@ private void createLogsSubscriptionService(
}
}

// TODO where do I need to unsubscribe the filterManager
private void createPrivateTransactionObserver(
final FilterManager filterManager, final PrivacyParameters privacyParameters) {

// register filterManager as observer of events fired by the onchain precompile
// filterManager needs to remove filters when the creator is removed from onchain groups
if (privacyParameters.isOnchainPrivacyGroupsEnabled()) {
macfarla marked this conversation as resolved.
Show resolved Hide resolved
// TODO this is prob not the right way to get the precompile
PrecompiledContract precompiledContract =
besuController
.getProtocolSchedule()
.getByBlockNumber(1)
.getPrecompileContractRegistry()
.get(Address.ONCHAIN_PRIVACY, Account.DEFAULT_VERSION);
OnChainPrivacyPrecompiledContract onChainPrivacyPrecompiledContract =
macfarla marked this conversation as resolved.
Show resolved Hide resolved
(OnChainPrivacyPrecompiledContract) precompiledContract;
onChainPrivacyPrecompiledContract.addPrivateTransactionObserver(filterManager);
}
}

private void createSyncingSubscriptionService(
final Synchronizer synchronizer, final SubscriptionManager subscriptionManager) {
new SyncingSubscriptionService(subscriptionManager, synchronizer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.hyperledger.besu.ethereum.core.LogWithMetadata;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionEvent;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionObserver;

import java.util.ArrayList;
import java.util.Collection;
Expand All @@ -37,7 +39,7 @@
import io.vertx.core.AbstractVerticle;

/** Manages JSON-RPC filter events. */
public class FilterManager extends AbstractVerticle {
public class FilterManager extends AbstractVerticle implements PrivateTransactionObserver {

private static final int FILTER_TIMEOUT_CHECK_TIMER = 10000;

Expand Down Expand Up @@ -120,19 +122,22 @@ public String installLogFilter(
* Installs a new private log filter
*
* @param privacyGroupId String privacyGroupId
* @param enclavePublicKey String enclavePublicKey of user creating the filter
* @param fromBlock {@link BlockParameter} Integer block number, or latest/pending/earliest.
* @param toBlock {@link BlockParameter} Integer block number, or latest/pending/earliest.
* @param logsQuery {@link LogsQuery} Addresses and/or topics to filter by
* @return the log filter id
*/
public String installPrivateLogFilter(
final String privacyGroupId,
final String enclavePublicKey,
final BlockParameter fromBlock,
final BlockParameter toBlock,
final LogsQuery logsQuery) {
final String filterId = filterIdGenerator.nextId();
filterRepository.save(
new PrivateLogFilter(filterId, privacyGroupId, fromBlock, toBlock, logsQuery));
new PrivateLogFilter(
filterId, privacyGroupId, enclavePublicKey, fromBlock, toBlock, logsQuery));
return filterId;
}

Expand Down Expand Up @@ -192,6 +197,20 @@ public void recordBlockEvent(final BlockAddedEvent event) {
});
}

@Override
public void onPrivateTransactionProcessed(final PrivateTransactionEvent event) {
macfarla marked this conversation as resolved.
Show resolved Hide resolved
// when user removed from privacy group, remove all filters created by that user in that group
filterRepository.getFiltersOfType(PrivateLogFilter.class).stream()
.filter(
privateLogFilter ->
privateLogFilter.getPrivacyGroupId().equals(event.getPrivacyGroupId())
&& privateLogFilter.getEnclavePublicKey().equals(event.getEnclavePublicKey()))
.forEach(
privateLogFilter -> {
uninstallFilter(privateLogFilter.getId());
});
}

@VisibleForTesting
void recordPendingTransactionEvent(final Transaction transaction) {
final Collection<PendingTransactionFilter> pendingTransactionFilters =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,25 @@
public class PrivateLogFilter extends LogFilter {

private final String privacyGroupId;
private final String enclavePublicKey;

PrivateLogFilter(
final String id,
final String privacyGroupId,
final String enclavePublicKey,
final BlockParameter fromBlock,
final BlockParameter toBlock,
final LogsQuery logsQuery) {
super(id, fromBlock, toBlock, logsQuery);
this.privacyGroupId = privacyGroupId;
this.enclavePublicKey = enclavePublicKey;
}

public String getPrivacyGroupId() {
return privacyGroupId;
}

public String getEnclavePublicKey() {
return enclavePublicKey;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
if (maybePrivacyGroup.isEmpty()) {
return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST);
}
} else { // !onchainPirvacyGroupEnabled
} else { // !onchainPrivacyGroupEnabled
if (maybePrivacyGroupId.isPresent()) {
maybePrivacyGroup =
privacyController.retrieveOffChainPrivacyGroup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
if (maybePrivacyGroup.isEmpty()) {
return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST);
}
} else { // !onchainPirvacyGroupEnabled
} else { // !onchainPrivacyGroupEnabled
if (maybePrivacyGroupId.isPresent()) {
maybePrivacyGroup =
privacyController.retrieveOffChainPrivacyGroup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,27 @@ public String getName() {
public JsonRpcResponse response(final JsonRpcRequestContext request) {
final String privacyGroupId = request.getRequiredParameter(0, String.class);
final FilterParameter filter = request.getRequiredParameter(1, FilterParameter.class);
final String enclavePublicKey = enclavePublicKeyProvider.getEnclaveKey(request.getUser());

checkIfPrivacyGroupMatchesAuthenticatedEnclaveKey(request, privacyGroupId);
checkIfPrivacyGroupMatchesAuthenticatedEnclaveKey(enclavePublicKey, privacyGroupId);
macfarla marked this conversation as resolved.
Show resolved Hide resolved

if (!filter.isValid()) {
return new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS);
}

final String logFilterId =
filterManager.installPrivateLogFilter(
privacyGroupId, filter.getFromBlock(), filter.getToBlock(), filter.getLogsQuery());
privacyGroupId,
enclavePublicKey,
filter.getFromBlock(),
filter.getToBlock(),
filter.getLogsQuery());

return new JsonRpcSuccessResponse(request.getRequest().getId(), logFilterId);
}

private void checkIfPrivacyGroupMatchesAuthenticatedEnclaveKey(
final JsonRpcRequestContext request, final String privacyGroupId) {
final String enclavePublicKey = enclavePublicKeyProvider.getEnclaveKey(request.getUser());
final String enclavePublicKey, final String privacyGroupId) {
privacyController.verifyPrivacyGroupContainsEnclavePublicKey(privacyGroupId, enclavePublicKey);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.hyperledger.besu.ethereum.core.Hash;
import org.hyperledger.besu.ethereum.core.LogWithMetadata;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionEvent;

import java.util.List;
import java.util.Optional;
Expand All @@ -59,6 +60,7 @@
public class FilterManagerLogFilterTest {

private static final String PRIVACY_GROUP_ID = "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=";
private static final String ENCLAVE_PUBLIC_KEY = "iOCzoGo5kwtZU0J41Z9xnGXHN6ZNukIa9MspvHtu3Jk=";

private FilterManager filterManager;

Expand Down Expand Up @@ -93,7 +95,8 @@ public void installUninstallNewLogFilter() {

@Test
public void shouldCheckMatchingLogsWhenRecordedNewBlockEventForPrivateFiltersOnly() {
filterManager.installPrivateLogFilter(PRIVACY_GROUP_ID, latest(), latest(), logsQuery());
filterManager.installPrivateLogFilter(
PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY, latest(), latest(), logsQuery());
filterManager.installLogFilter(latest(), latest(), logsQuery());
final Hash blockAddedHash = recordBlockEvents(1).get(0).getBlock().getHash();

Expand All @@ -104,7 +107,7 @@ public void shouldCheckMatchingLogsWhenRecordedNewBlockEventForPrivateFiltersOnl
@Test
public void shouldUseBlockHashWhenCheckingLogsForChangesForPrivateFiltersOnly() {
filterManager.installPrivateLogFilter(
PRIVACY_GROUP_ID, blockNum(1L), blockNum(10L), logsQuery());
PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY, blockNum(1L), blockNum(10L), logsQuery());
filterManager.installLogFilter(blockNum(1L), blockNum(10L), logsQuery());

final Hash blockAddedHash = recordBlockEvents(1).get(0).getBlock().getHash();
Expand Down Expand Up @@ -222,7 +225,8 @@ public void getLogsShouldResetFilterExpireDate() {
@Test
public void installAndUninstallPrivateFilter() {
final String filterId =
filterManager.installPrivateLogFilter(PRIVACY_GROUP_ID, latest(), latest(), logsQuery());
filterManager.installPrivateLogFilter(
PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY, latest(), latest(), logsQuery());

assertThat(filterRepository.getFilter(filterId, PrivateLogFilter.class)).isPresent();

Expand All @@ -239,7 +243,8 @@ public void privateLogFilterShouldQueryPrivacyQueriesObject() {
.thenReturn(singletonList(logWithMetadata));

final String filterId =
filterManager.installPrivateLogFilter(PRIVACY_GROUP_ID, latest(), latest(), logsQuery);
filterManager.installPrivateLogFilter(
PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY, latest(), latest(), logsQuery);
final Hash blockAddedHash = recordBlockEvents(1).get(0).getBlock().getHash();

verify(privacyQueries).matchingLogs(eq(PRIVACY_GROUP_ID), eq(blockAddedHash), eq(logsQuery));
Expand All @@ -253,7 +258,8 @@ public void getLogsForPrivateFilterShouldQueryPrivacyQueriesObject() {
.thenReturn(Lists.newArrayList(logWithMetadata));

final String privateLogFilterId =
filterManager.installPrivateLogFilter(PRIVACY_GROUP_ID, latest(), latest(), logsQuery());
filterManager.installPrivateLogFilter(
PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY, latest(), latest(), logsQuery());

final List<LogWithMetadata> logs = filterManager.logs(privateLogFilterId);

Expand All @@ -264,6 +270,36 @@ public void getLogsForPrivateFilterShouldQueryPrivacyQueriesObject() {
assertThat(logs.get(0)).isEqualTo(logWithMetadata);
}

@Test
public void removalEvent_uninstallsFilter() {
final LogWithMetadata logWithMetadata = logWithMetadata();
when(privacyQueries.matchingLogs(eq(PRIVACY_GROUP_ID), anyLong(), anyLong(), any()))
.thenReturn(Lists.newArrayList(logWithMetadata));

final String privateLogFilterId =
filterManager.installPrivateLogFilter(
PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY, latest(), latest(), logsQuery());

final List<LogWithMetadata> logs = filterManager.logs(privateLogFilterId);

verify(blockchainQueries, times(2)).headBlockNumber();
verify(blockchainQueries, never()).matchingLogs(anyLong(), anyLong(), any(), any());

verify(privacyQueries).matchingLogs(eq(PRIVACY_GROUP_ID), anyLong(), anyLong(), any());
assertThat(logs.get(0)).isEqualTo(logWithMetadata);

// signal removal of user from group
privateTransactionEvent(PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY);

assertThat(filterManager.logs(privateLogFilterId)).isNull();
assertThat(filterRepository.getFilter(privateLogFilterId, PrivateLogFilter.class)).isEmpty();
}

private void privateTransactionEvent(final String privacyGroupId, final String enclavePublicKey) {
PrivateTransactionEvent event = new PrivateTransactionEvent(privacyGroupId, enclavePublicKey);
filterManager.onPrivateTransactionProcessed(event);
}

private LogWithMetadata logWithMetadata() {
return new LogWithMetadata(
0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

import java.util.Collections;
import java.util.List;
import java.util.Optional;

import io.vertx.ext.auth.User;
import org.apache.tuweni.bytes.Bytes32;
Expand Down Expand Up @@ -115,7 +116,9 @@ public void filterWithInvalidParameters() {
@Test
public void filterWithExpectedQueryIsCreated() {
final List<Address> addresses = List.of(Address.ZERO);
final User user = mock(User.class);
final List<List<LogTopic>> logTopics = List.of(List.of(LogTopic.of(Bytes32.random())));
when(enclavePublicKeyProvider.getEnclaveKey(eq(Optional.of(user)))).thenReturn(ENCLAVE_KEY);

final FilterParameter filter =
new FilterParameter(
Expand All @@ -124,12 +127,14 @@ public void filterWithExpectedQueryIsCreated() {
final LogsQuery expectedQuery =
new LogsQuery.Builder().addresses(addresses).topics(logTopics).build();

final JsonRpcRequestContext request = privNewFilterRequest(PRIVACY_GROUP_ID, filter);
final JsonRpcRequestContext request =
privNewFilterRequestWithUser(PRIVACY_GROUP_ID, filter, user);
method.response(request);

verify(filterManager)
.installPrivateLogFilter(
eq(PRIVACY_GROUP_ID),
eq(ENCLAVE_KEY),
refEq(BlockParameter.EARLIEST),
refEq(BlockParameter.LATEST),
eq((expectedQuery)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
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.PrivateTransactionEvent;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionObserver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.ethereum.privacy.Restriction;
import org.hyperledger.besu.ethereum.privacy.VersionedPrivateTransaction;
Expand All @@ -45,6 +47,7 @@
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.util.Subscribers;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;

import java.util.ArrayList;
Expand All @@ -65,6 +68,9 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
private static final SECP256K1.Signature FAKE_SIGNATURE =
SECP256K1.Signature.create(SECP256K1.HALF_CURVE_ORDER, SECP256K1.HALF_CURVE_ORDER, (byte) 0);

private final Subscribers<PrivateTransactionObserver> privateTransactionEventObservers =
Subscribers.create();

private static final Logger LOG = LogManager.getLogger();

public OnChainPrivacyPrecompiledContract(
Expand All @@ -87,6 +93,14 @@ public OnChainPrivacyPrecompiledContract(
"OnChainPrivacy");
}

public long addPrivateTransactionObserver(final PrivateTransactionObserver observer) {
return privateTransactionEventObservers.subscribe(observer);
}

public boolean removePrivateTransactionObserver(final long observerId) {
return privateTransactionEventObservers.unsubscribe(observerId);
}

@Override
public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

Expand Down Expand Up @@ -176,6 +190,18 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
return Bytes.EMPTY;
}

if (privateTransaction.isGroupRemovalTransaction()) {
// get first participant parameter - there will be only one for removal transaction
final String removedParticipant = getParticipantsFromParameter(privateTransaction.getPayload()).get(0);

final PrivateTransactionEvent removalEvent =
new PrivateTransactionEvent(
privateTransaction.getPrivacyGroupId().get().toBase64String(),
removedParticipant);
privateTransactionEventObservers.forEach(
sub -> sub.onPrivateTransactionProcessed(removalEvent));
}

if (messageFrame.isPersistingPrivateState()) {
persistPrivateState(
pmtHash,
Expand Down
Loading