This repository has been archived by the owner on Sep 26, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 130
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[PAN-2342] Update discovery logic to trust bootnodes only when out of…
… sync (#1039) * [PAN-2342] Created SyncStatusNodePermissioningProvider and NodePermissioningController * Fix block height comparison logic * Unit test for SyncStatusNodePermissioningProvider * Add comment about permissioning while not in sync * PR comments * Fix missing final * Fixing unit test * Unsubscribing from Synchronizer SyncStatus updates after reaching sync * Fix race condition * Simplifying synchronization between callbacks
- Loading branch information
1 parent
0499e1a
commit 7fbbcc9
Showing
7 changed files
with
404 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
...n/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Copyright 2019 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. | ||
*/ | ||
package tech.pegasys.pantheon.ethereum.permissioning.node; | ||
|
||
import tech.pegasys.pantheon.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; | ||
import tech.pegasys.pantheon.util.enode.EnodeURL; | ||
|
||
import java.util.Optional; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.Logger; | ||
|
||
public class NodePermissioningController { | ||
|
||
private static final Logger LOG = LogManager.getLogger(); | ||
|
||
private final Optional<SyncStatusNodePermissioningProvider> syncStatusNodePermissioningProvider; | ||
|
||
public NodePermissioningController( | ||
final SyncStatusNodePermissioningProvider syncStatusNodePermissioningProvider) { | ||
this.syncStatusNodePermissioningProvider = Optional.of(syncStatusNodePermissioningProvider); | ||
} | ||
|
||
public NodePermissioningController() { | ||
this.syncStatusNodePermissioningProvider = Optional.empty(); | ||
} | ||
|
||
public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { | ||
LOG.trace("Checking node permission: {} -> {}", sourceEnode, destinationEnode); | ||
|
||
return syncStatusNodePermissioningProvider | ||
.map((provider) -> provider.isPermitted(sourceEnode, destinationEnode)) | ||
.orElse(true); | ||
} | ||
|
||
public void startPeerDiscoveryCallback(final Runnable peerDiscoveryCallback) { | ||
syncStatusNodePermissioningProvider.ifPresent( | ||
(p) -> p.setHasReachedSyncCallback(peerDiscoveryCallback)); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...ain/java/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* Copyright 2019 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. | ||
*/ | ||
package tech.pegasys.pantheon.ethereum.permissioning.node; | ||
|
||
import tech.pegasys.pantheon.util.enode.EnodeURL; | ||
|
||
@FunctionalInterface | ||
public interface NodePermissioningProvider { | ||
|
||
boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode); | ||
} |
102 changes: 102 additions & 0 deletions
102
...ys/pantheon/ethereum/permissioning/node/provider/SyncStatusNodePermissioningProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
/* | ||
* Copyright 2019 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. | ||
*/ | ||
package tech.pegasys.pantheon.ethereum.permissioning.node.provider; | ||
|
||
import static com.google.common.base.Preconditions.checkNotNull; | ||
|
||
import tech.pegasys.pantheon.ethereum.core.SyncStatus; | ||
import tech.pegasys.pantheon.ethereum.core.Synchronizer; | ||
import tech.pegasys.pantheon.ethereum.permissioning.node.NodePermissioningProvider; | ||
import tech.pegasys.pantheon.util.enode.EnodeURL; | ||
|
||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import java.util.Optional; | ||
import java.util.OptionalLong; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
|
||
public class SyncStatusNodePermissioningProvider implements NodePermissioningProvider { | ||
|
||
private final Synchronizer synchronizer; | ||
private final Collection<EnodeURL> bootnodes = new HashSet<>(); | ||
private OptionalLong syncStatusObserverId; | ||
private boolean hasReachedSync = false; | ||
private Optional<Runnable> hasReachedSyncCallback = Optional.empty(); | ||
|
||
public SyncStatusNodePermissioningProvider( | ||
final Synchronizer synchronizer, final Collection<EnodeURL> bootnodes) { | ||
checkNotNull(synchronizer); | ||
this.synchronizer = synchronizer; | ||
long id = this.synchronizer.observeSyncStatus(this::handleSyncStatusUpdate); | ||
this.syncStatusObserverId = OptionalLong.of(id); | ||
this.bootnodes.addAll(bootnodes); | ||
} | ||
|
||
private void handleSyncStatusUpdate(final SyncStatus syncStatus) { | ||
if (syncStatus != null) { | ||
long blocksBehind = syncStatus.getHighestBlock() - syncStatus.getCurrentBlock(); | ||
if (blocksBehind <= 0) { | ||
synchronized (this) { | ||
if (!hasReachedSync) { | ||
runCallback(); | ||
syncStatusObserverId.ifPresent( | ||
id -> { | ||
synchronizer.removeObserver(id); | ||
syncStatusObserverId = OptionalLong.empty(); | ||
}); | ||
hasReachedSync = true; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
public synchronized void setHasReachedSyncCallback(final Runnable runnable) { | ||
if (hasReachedSync) { | ||
runCallback(); | ||
} else { | ||
this.hasReachedSyncCallback = Optional.of(runnable); | ||
} | ||
} | ||
|
||
private synchronized void runCallback() { | ||
hasReachedSyncCallback.ifPresent(Runnable::run); | ||
hasReachedSyncCallback = Optional.empty(); | ||
} | ||
|
||
/** | ||
* Before reaching a sync'd state, the node will only be allowed to talk to its bootnodes | ||
* (outgoing connections). After reaching a sync'd state, it is expected that other providers will | ||
* check the permissions (most likely the smart contract based provider). That's why we always | ||
* return true after reaching a sync'd state. | ||
* | ||
* @param sourceEnode the enode source of the packet or connection | ||
* @param destinationEnode the enode target of the packet or connection | ||
* @return true, if the communication from sourceEnode to destinationEnode is permitted, false | ||
* otherwise | ||
*/ | ||
@Override | ||
public boolean isPermitted(final EnodeURL sourceEnode, final EnodeURL destinationEnode) { | ||
if (hasReachedSync) { | ||
return true; | ||
} else { | ||
return bootnodes.contains(destinationEnode); | ||
} | ||
} | ||
|
||
@VisibleForTesting | ||
boolean hasReachedSync() { | ||
return hasReachedSync; | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
...va/tech/pegasys/pantheon/ethereum/permissioning/node/NodePermissioningControllerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright 2019 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. | ||
*/ | ||
package tech.pegasys.pantheon.ethereum.permissioning.node; | ||
|
||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.eq; | ||
import static org.mockito.Mockito.verify; | ||
|
||
import tech.pegasys.pantheon.ethereum.permissioning.node.provider.SyncStatusNodePermissioningProvider; | ||
import tech.pegasys.pantheon.util.enode.EnodeURL; | ||
|
||
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 NodePermissioningControllerTest { | ||
|
||
private static final EnodeURL enode1 = | ||
new EnodeURL( | ||
"enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.168.0.2:1234"); | ||
private static final EnodeURL enode2 = | ||
new EnodeURL( | ||
"enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.3:5678"); | ||
|
||
@Mock private SyncStatusNodePermissioningProvider syncStatusNodePermissioningProvider; | ||
|
||
private NodePermissioningController controller; | ||
|
||
@Before | ||
public void before() { | ||
this.controller = new NodePermissioningController(syncStatusNodePermissioningProvider); | ||
} | ||
|
||
@Test | ||
public void isPermittedShouldDelegateToSyncStatusProvider() { | ||
controller.isPermitted(enode1, enode2); | ||
|
||
verify(syncStatusNodePermissioningProvider).isPermitted(eq(enode1), eq(enode2)); | ||
} | ||
|
||
@Test | ||
public void peerDiscoveryCallbackShouldBeDelegatedToSyncStatusNodePermissioningProvider() { | ||
controller.startPeerDiscoveryCallback(() -> {}); | ||
|
||
verify(syncStatusNodePermissioningProvider).setHasReachedSyncCallback(any(Runnable.class)); | ||
} | ||
} |
Oops, something went wrong.