From f669b09c756b1a88bda9de28b093176fa03104d0 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Thu, 2 Jul 2020 20:33:11 +0800 Subject: [PATCH 01/20] Fix bug when not validator of an epoch --- ...llProposalsHaveDirectParentsInvariant.java | 2 +- .../com/radixdlt/consensus/EpochManager.java | 10 ++++-- .../radixdlt/consensus/EpochManagerTest.java | 36 +++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/invariants/bft/AllProposalsHaveDirectParentsInvariant.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/invariants/bft/AllProposalsHaveDirectParentsInvariant.java index 5bedf5753..7ae3560cf 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/invariants/bft/AllProposalsHaveDirectParentsInvariant.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/invariants/bft/AllProposalsHaveDirectParentsInvariant.java @@ -45,7 +45,7 @@ public Observable check(RunningNetwork network) { return Observable.merge(correctProposals) .concatMap(v -> { if (!v.hasDirectParent()) { - return Observable.just(new TestInvariantError("Vertex has no direct parent")); + return Observable.just(new TestInvariantError(String.format("Vertex %s has no direct parent", v))); } else { return Observable.empty(); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index 534dcda2a..9e4695d45 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -87,9 +87,15 @@ public EpochManager( public void processEpochChange(EpochChange epochChange) { ValidatorSet validatorSet = epochChange.getValidatorSet(); - ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); + log.info("NEXT_EPOCH: {} {}", epochChange); - log.info("NEXT_EPOCH: {} {}", epochChange, proposerElection); + if (!validatorSet.containsKey(selfKey.getPublicKey())) { + log.info("NEXT_EPOCH: Not a validator"); + this.eventProcessor = EMPTY_PROCESSOR; + return; + } + + ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); VertexMetadata ancestorMetadata = epochChange.getAncestor(); Vertex genesisVertex = Vertex.createGenesis(ancestorMetadata); final long nextEpoch = genesisVertex.getEpoch(); diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index 62ec2d6c0..d3dfd05b4 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -1,7 +1,10 @@ package com.radixdlt.consensus; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -21,6 +24,38 @@ import org.junit.Test; public class EpochManagerTest { + @Test + public void when_next_epoch_does_not_contain_self__then_should_not_emit_any_consensus_events() { + ECKeyPair keyPair = mock(ECKeyPair.class); + ECPublicKey publicKey = mock(ECPublicKey.class); + when(keyPair.getPublicKey()).thenReturn(publicKey); + + BFTEventSender bftEventSender = mock(BFTEventSender.class); + ScheduledTimeoutSender scheduledTimeoutSender = mock(ScheduledTimeoutSender.class); + + EpochManager epochManager = new EpochManager( + mock(Mempool.class), + bftEventSender, + scheduledTimeoutSender, + timeoutSender -> mock(Pacemaker.class), + mock(VertexStoreFactory.class), + proposers -> mock(ProposerElection.class), + mock(Hasher.class), + keyPair, + mock(SystemCounters.class) + ); + EpochChange epochChange = mock(EpochChange.class); + ValidatorSet validatorSet = mock(ValidatorSet.class); + when(validatorSet.containsKey(eq(publicKey))).thenReturn(false); + when(epochChange.getValidatorSet()).thenReturn(validatorSet); + epochManager.processEpochChange(epochChange); + + verify(bftEventSender, never()).sendNewView(any(), any()); + verify(bftEventSender, never()).sendVote(any(), any()); + verify(bftEventSender, never()).broadcastProposal(any()); + verify(scheduledTimeoutSender, never()).scheduleTimeout(any(), anyLong()); + } + @Test public void when_no_epoch_change__then_processing_events_should_not_fail() { ECKeyPair keyPair = mock(ECKeyPair.class); @@ -67,6 +102,7 @@ public void when_next_epoch__then_get_vertices_rpc_should_be_forwarded_to_vertex Validator validator = mock(Validator.class); when(validator.nodeKey()).thenReturn(mock(ECPublicKey.class)); ValidatorSet validatorSet = mock(ValidatorSet.class); + when(validatorSet.containsKey(any())).thenReturn(true); when(validatorSet.getValidators()).thenReturn(ImmutableSet.of(validator)); epochManager.processEpochChange(new EpochChange(VertexMetadata.ofGenesisAncestor(), validatorSet)); From b19bb37db59907dec6456379e1e0c4b9ef118afe Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Thu, 2 Jul 2020 20:50:57 +0800 Subject: [PATCH 02/20] Add changing validator set test --- .../consensus/simulation/SimulatedTest.java | 42 ++++++++++++++- .../ChangingEpochSyncedStateComputer.java | 4 +- .../simulation/network/SimulatedNetwork.java | 42 +++++---------- .../OneValidatorChangePerEpochTest.java | 54 +++++++++++++++++++ ...torTest.java => StaticValidatorsTest.java} | 2 +- .../com/radixdlt/consensus/EpochManager.java | 20 +++---- .../radixdlt/consensus/EpochManagerTest.java | 2 + 7 files changed, 125 insertions(+), 41 deletions(-) create mode 100644 radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java rename radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/{EpochsStaticValidatorTest.java => StaticValidatorsTest.java} (98%) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulatedTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulatedTest.java index 1d3e714dc..1378e90b1 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulatedTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulatedTest.java @@ -22,6 +22,7 @@ import com.radixdlt.consensus.View; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; import com.radixdlt.consensus.simulation.invariants.epochs.EpochViewInvariant; +import com.radixdlt.consensus.simulation.network.ChangingEpochSyncedStateComputer; import com.radixdlt.consensus.simulation.network.DroppingLatencyProvider; import com.radixdlt.consensus.simulation.network.OneProposalPerViewDropper; import com.radixdlt.consensus.simulation.network.RandomLatencyProvider; @@ -32,11 +33,16 @@ import com.radixdlt.consensus.simulation.invariants.bft.NoTimeoutsInvariant; import com.radixdlt.consensus.simulation.invariants.bft.NoneCommittedInvariant; import com.radixdlt.consensus.simulation.invariants.bft.SafetyInvariant; +import com.radixdlt.consensus.simulation.network.SimulatedNetwork.SimulatedStateComputer; +import com.radixdlt.consensus.simulation.network.SingleEpochAlwaysSyncedStateComputer; +import com.radixdlt.consensus.validators.Validator; +import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.middleware2.network.TestEventCoordinatorNetwork; import com.radixdlt.middleware2.network.TestEventCoordinatorNetwork.LatencyProvider; import com.radixdlt.utils.Pair; +import com.radixdlt.utils.UInt256; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; import java.util.Collections; @@ -44,7 +50,10 @@ import java.util.Map; import java.util.Optional; import java.util.Random; +import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -57,6 +66,7 @@ public class SimulatedTest { private final LatencyProvider latencyProvider; private final ImmutableMap checks; private final int pacemakerTimeout; + private final Function validatorSetMapping; private final boolean getVerticesRPCEnabled; private final View epochHighView; @@ -65,6 +75,7 @@ private SimulatedTest( LatencyProvider latencyProvider, int pacemakerTimeout, View epochHighView, + Function validatorSetMapping, boolean getVerticesRPCEnabled, ImmutableMap checks ) { @@ -73,6 +84,7 @@ private SimulatedTest( this.checks = checks; this.pacemakerTimeout = pacemakerTimeout; this.epochHighView = epochHighView; + this.validatorSetMapping = validatorSetMapping; this.getVerticesRPCEnabled = getVerticesRPCEnabled; } @@ -83,6 +95,7 @@ public static class Builder { private int pacemakerTimeout = 12 * TestEventCoordinatorNetwork.DEFAULT_LATENCY; private boolean getVerticesRPCEnabled = true; private View epochHighView = null; + private Function> epochToNodeIndexMapper; private Builder() { } @@ -120,6 +133,11 @@ public Builder epochHighView(View epochHighView) { return this; } + public Builder epochToNodesMapper(Function> epochToNodeIndexMapper) { + this.epochToNodeIndexMapper = epochToNodeIndexMapper; + return this; + } + public Builder setGetVerticesRPCEnabled(boolean getVerticesRPCEnabled) { this.getVerticesRPCEnabled = getVerticesRPCEnabled; return this; @@ -166,11 +184,24 @@ public Builder checkEpochHighView(String invariantName, View epochHighView) { } public SimulatedTest build() { + final List publicKeys = nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toList()); + Function epochToValidatorSetMapping = + epochToNodeIndexMapper == null + ? epoch -> ValidatorSet.from( + publicKeys.stream() + .map(pk -> Validator.from(pk, UInt256.ONE)) + .collect(Collectors.toList())) + : epochToNodeIndexMapper.andThen(indices -> ValidatorSet.from( + indices.stream() + .map(nodes::get) + .map(kp -> Validator.from(kp.getPublicKey(), UInt256.ONE)) + .collect(Collectors.toList()))); return new SimulatedTest( ImmutableList.copyOf(nodes), latencyProvider.copyOf(), pacemakerTimeout, epochHighView, + epochToValidatorSetMapping, getVerticesRPCEnabled, this.checksBuilder.build() ); @@ -223,7 +254,16 @@ public Map> run(long duration, TimeUnit tim TestEventCoordinatorNetwork network = TestEventCoordinatorNetwork.builder() .latencyProvider(this.latencyProvider) .build(); - SimulatedNetwork bftNetwork = new SimulatedNetwork(nodes, network, pacemakerTimeout, epochHighView, getVerticesRPCEnabled); + + final Supplier stateComputerSupplier; + final List publicKeys = nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toList()); + if (epochHighView == null) { + stateComputerSupplier = () -> new SingleEpochAlwaysSyncedStateComputer(publicKeys); + } else { + stateComputerSupplier = () -> new ChangingEpochSyncedStateComputer(epochHighView, validatorSetMapping); + } + + SimulatedNetwork bftNetwork = new SimulatedNetwork(nodes, network, pacemakerTimeout, stateComputerSupplier, getVerticesRPCEnabled); return bftNetwork.start() .timeout(10, TimeUnit.SECONDS) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/ChangingEpochSyncedStateComputer.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/ChangingEpochSyncedStateComputer.java index 5370da146..4474255a1 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/ChangingEpochSyncedStateComputer.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/ChangingEpochSyncedStateComputer.java @@ -37,10 +37,10 @@ * State computer which changes epochs after some number of views */ public class ChangingEpochSyncedStateComputer implements SimulatedStateComputer { - private VertexMetadata lastEpochChange = null; private final Subject epochChanges = BehaviorSubject.create().toSerialized(); - private View epochHighView; + private final View epochHighView; private final Function validatorSetMapping; + private VertexMetadata lastEpochChange = null; public ChangingEpochSyncedStateComputer(View epochHighView, Function validatorSetMapping) { this.epochHighView = Objects.requireNonNull(epochHighView); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java index 302850a06..5d5c9e8d5 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java @@ -32,23 +32,18 @@ import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.VertexStoreEventsRx; import com.radixdlt.consensus.VertexStoreFactory; -import com.radixdlt.consensus.View; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; import com.radixdlt.consensus.liveness.WeightedRotatingLeaders; -import com.radixdlt.consensus.validators.Validator; -import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.mempool.EmptyMempool; import com.radixdlt.mempool.Mempool; import com.radixdlt.middleware2.CommittedAtom; import com.radixdlt.middleware2.network.TestEventCoordinatorNetwork; import com.radixdlt.middleware2.network.TestEventCoordinatorNetwork.SimulatedNetworkReceiver; -import com.radixdlt.utils.UInt256; import io.reactivex.rxjava3.core.Completable; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Single; @@ -58,6 +53,7 @@ import java.util.Objects; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Supplier; import java.util.stream.Collectors; import static com.radixdlt.utils.ThreadFactories.daemonThreads; @@ -73,9 +69,9 @@ public class SimulatedNetwork { private final ImmutableMap runners; private final List nodes; private final boolean getVerticesRPCEnabled; - private final View epochHighView; + private final Supplier stateComputerSupplier; - interface SimulatedStateComputer extends SyncedStateComputer, EpochChangeRx { + public interface SimulatedStateComputer extends SyncedStateComputer, EpochChangeRx { } /** @@ -88,11 +84,11 @@ public SimulatedNetwork( List nodes, TestEventCoordinatorNetwork underlyingNetwork, int pacemakerTimeout, - View epochHighView, + Supplier stateComputerSupplier, boolean getVerticesRPCEnabled ) { this.nodes = nodes; - this.epochHighView = epochHighView; + this.stateComputerSupplier = stateComputerSupplier; this.getVerticesRPCEnabled = getVerticesRPCEnabled; this.underlyingNetwork = Objects.requireNonNull(underlyingNetwork); this.pacemakerTimeout = pacemakerTimeout; @@ -102,24 +98,14 @@ public SimulatedNetwork( } private ConsensusRunner createBFTInstance(ECKeyPair key) { - Mempool mempool = new EmptyMempool(); - Hasher hasher = new DefaultHasher(); - ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(daemonThreads("TimeoutSender")); - ScheduledTimeoutSender timeoutSender = new ScheduledTimeoutSender(scheduledExecutorService); - - List publicKeys = nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toList()); - - final SimulatedStateComputer stateComputer; - if (epochHighView == null) { - stateComputer = new SingleEpochAlwaysSyncedStateComputer(publicKeys); - } else { - stateComputer = new ChangingEpochSyncedStateComputer( - epochHighView, - v -> ValidatorSet.from(publicKeys.stream().map(pk -> Validator.from(pk, UInt256.ONE)).collect(Collectors.toList())) - ); - } + final Mempool mempool = new EmptyMempool(); + final Hasher hasher = new DefaultHasher(); + final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(daemonThreads("TimeoutSender")); + final ScheduledTimeoutSender timeoutSender = new ScheduledTimeoutSender(scheduledExecutorService); + + final SimulatedStateComputer stateComputer = stateComputerSupplier.get(); - VertexStoreFactory vertexStoreFactory = (v, qc) -> { + final VertexStoreFactory vertexStoreFactory = (v, qc) -> { SyncVerticesRPCSender syncVerticesRPCSender = getVerticesRPCEnabled ? underlyingNetwork.getVerticesRequestSender(key.getPublicKey()) : EmptySyncVerticesRPCSender.INSTANCE; @@ -133,7 +119,7 @@ private ConsensusRunner createBFTInstance(ECKeyPair key) { ); }; - EpochManager epochManager = new EpochManager( + final EpochManager epochManager = new EpochManager( mempool, underlyingNetwork.getNetworkSender(key.getPublicKey()), timeoutSender, @@ -145,7 +131,7 @@ private ConsensusRunner createBFTInstance(ECKeyPair key) { counters.get(key) ); - SimulatedNetworkReceiver rx = underlyingNetwork.getNetworkRx(key.getPublicKey()); + final SimulatedNetworkReceiver rx = underlyingNetwork.getNetworkRx(key.getPublicKey()); return new ConsensusRunner( stateComputer, diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java new file mode 100644 index 000000000..18db3ba0e --- /dev/null +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.simulation.tests.epochs; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import com.google.common.collect.Sets; +import com.radixdlt.consensus.View; +import com.radixdlt.consensus.simulation.SimulatedTest; +import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.Test; + +public class OneValidatorChangePerEpochTest { + private final Builder bftTestBuilder = SimulatedTest.builder() + .numNodes(4) + .checkSafety("safety") + .checkLiveness("liveness") + .checkNoTimeouts("noTimeouts") + .checkAllProposalsHaveDirectParents("directParents"); + + @Test + public void given_correct_bft_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { + SimulatedTest bftTest = bftTestBuilder + .epochHighView(View.of(100)) + .epochToNodesMapper(epoch -> + Sets.newHashSet((int) (epoch % 4), (int) (epoch + 1) % 4, (int) (epoch + 2) % 4) + ) + .checkEpochHighView("epochHighView", View.of(100)) + .build(); + Map> results = bftTest.run(1, TimeUnit.MINUTES); + assertThat(results).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); + } + +} diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/EpochsStaticValidatorTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/StaticValidatorsTest.java similarity index 98% rename from radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/EpochsStaticValidatorTest.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/StaticValidatorsTest.java index d64078fa8..923fa6a0b 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/EpochsStaticValidatorTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/StaticValidatorsTest.java @@ -28,7 +28,7 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; -public class EpochsStaticValidatorTest { +public class StaticValidatorsTest { private final Builder bftTestBuilder = SimulatedTest.builder() .numNodes(4) .checkSafety("safety") diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index 9e4695d45..2c4574930 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -89,13 +89,6 @@ public void processEpochChange(EpochChange epochChange) { ValidatorSet validatorSet = epochChange.getValidatorSet(); log.info("NEXT_EPOCH: {} {}", epochChange); - if (!validatorSet.containsKey(selfKey.getPublicKey())) { - log.info("NEXT_EPOCH: Not a validator"); - this.eventProcessor = EMPTY_PROCESSOR; - return; - } - - ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); VertexMetadata ancestorMetadata = epochChange.getAncestor(); Vertex genesisVertex = Vertex.createGenesis(ancestorMetadata); final long nextEpoch = genesisVertex.getEpoch(); @@ -105,8 +98,17 @@ public void processEpochChange(EpochChange epochChange) { throw new IllegalStateException("Epoch change has already occurred: " + epochChange); } - counters.set(CounterType.EPOCHS_EPOCH, nextEpoch); + this.currentEpoch = nextEpoch; + this.counters.set(CounterType.EPOCHS_EPOCH, nextEpoch); + if (!validatorSet.containsKey(selfKey.getPublicKey())) { + log.info("NEXT_EPOCH: Not a validator"); + this.eventProcessor = EMPTY_PROCESSOR; + this.vertexStore = null; + return; + } + + ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); TimeoutSender sender = (view, ms) -> scheduledTimeoutSender.scheduleTimeout(new LocalTimeout(nextEpoch, view), ms); Pacemaker pacemaker = pacemakerFactory.create(sender); SafetyRules safetyRules = new SafetyRules(this.selfKey, SafetyState.initialState(), this.hasher); @@ -115,6 +117,7 @@ public void processEpochChange(EpochChange epochChange) { QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(genesisVertex); this.vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC); + ProposalGenerator proposalGenerator = new MempoolProposalGenerator(this.vertexStore, this.mempool); BFTEventReducer reducer = new BFTEventReducer( @@ -138,7 +141,6 @@ public void processEpochChange(EpochChange epochChange) { counters ); - this.currentEpoch = nextEpoch; this.eventProcessor = new BFTEventPreprocessor( this.selfKey.getPublicKey(), reducer, diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index d3dfd05b4..f7f435e78 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -48,6 +48,8 @@ public void when_next_epoch_does_not_contain_self__then_should_not_emit_any_cons ValidatorSet validatorSet = mock(ValidatorSet.class); when(validatorSet.containsKey(eq(publicKey))).thenReturn(false); when(epochChange.getValidatorSet()).thenReturn(validatorSet); + VertexMetadata vertexMetadata = mock(VertexMetadata.class); + when(epochChange.getAncestor()).thenReturn(vertexMetadata); epochManager.processEpochChange(epochChange); verify(bftEventSender, never()).sendNewView(any(), any()); From 753daeacb4d9e3b7f01627ab8733c0221d72aa22 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Fri, 3 Jul 2020 16:31:26 +0800 Subject: [PATCH 03/20] Add RPC to allow a validator node to retrieve the next epoch --- .../simulation/network/SimulatedNetwork.java | 9 +-- .../OneValidatorChangePerEpochTest.java | 3 +- .../java/com/radixdlt/CerberusModule.java | 16 ++++- .../radixdlt/consensus/ConsensusEvent.java | 8 +++ .../consensus/EmptySyncEpochsRPCSender.java | 34 ++++++++++ .../com/radixdlt/consensus/EpochManager.java | 55 +++++++++++++--- .../consensus/RequiresSyncConsensusEvent.java | 8 --- .../consensus/SyncEpochsRPCSender.java | 44 +++++++++++++ .../consensus/VertexStoreFactory.java | 5 +- .../java/com/radixdlt/consensus/Vote.java | 1 + .../consensus/epoch/GetEpochRequest.java | 41 ++++++++++++ .../consensus/epoch/GetEpochResponse.java | 46 ++++++++++++++ .../radixdlt/consensus/EpochManagerTest.java | 62 +++++++++---------- 13 files changed, 274 insertions(+), 58 deletions(-) create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/SyncEpochsRPCSender.java create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochResponse.java diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java index 5d5c9e8d5..f3342bcc2 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java @@ -22,6 +22,7 @@ import com.radixdlt.consensus.ConsensusRunner.Event; import com.radixdlt.consensus.ConsensusRunner.EventType; import com.radixdlt.consensus.DefaultHasher; +import com.radixdlt.consensus.EmptySyncEpochsRPCSender; import com.radixdlt.consensus.EmptySyncVerticesRPCSender; import com.radixdlt.consensus.EpochManager; import com.radixdlt.consensus.EpochChangeRx; @@ -102,10 +103,7 @@ private ConsensusRunner createBFTInstance(ECKeyPair key) { final Hasher hasher = new DefaultHasher(); final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(daemonThreads("TimeoutSender")); final ScheduledTimeoutSender timeoutSender = new ScheduledTimeoutSender(scheduledExecutorService); - - final SimulatedStateComputer stateComputer = stateComputerSupplier.get(); - - final VertexStoreFactory vertexStoreFactory = (v, qc) -> { + final VertexStoreFactory vertexStoreFactory = (v, qc, stateComputer) -> { SyncVerticesRPCSender syncVerticesRPCSender = getVerticesRPCEnabled ? underlyingNetwork.getVerticesRequestSender(key.getPublicKey()) : EmptySyncVerticesRPCSender.INSTANCE; @@ -119,9 +117,12 @@ private ConsensusRunner createBFTInstance(ECKeyPair key) { ); }; + final SimulatedStateComputer stateComputer = stateComputerSupplier.get(); final EpochManager epochManager = new EpochManager( + stateComputer, mempool, underlyingNetwork.getNetworkSender(key.getPublicKey()), + EmptySyncEpochsRPCSender.INSTANCE, timeoutSender, timeoutSender1 -> new FixedTimeoutPacemaker(this.pacemakerTimeout, timeoutSender1), vertexStoreFactory, diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java index 18db3ba0e..702ec75fc 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java @@ -28,6 +28,7 @@ import java.util.Optional; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AssertionsForClassTypes; +import org.junit.Ignore; import org.junit.Test; public class OneValidatorChangePerEpochTest { @@ -39,6 +40,7 @@ public class OneValidatorChangePerEpochTest { .checkAllProposalsHaveDirectParents("directParents"); @Test + @Ignore public void given_correct_bft_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { SimulatedTest bftTest = bftTestBuilder .epochHighView(View.of(100)) @@ -50,5 +52,4 @@ public void given_correct_bft_with_changing_epochs_per_100_views__then_should_pa Map> results = bftTest.run(1, TimeUnit.MINUTES); assertThat(results).allSatisfy((name, err) -> AssertionsForClassTypes.assertThat(err).isEmpty()); } - } diff --git a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java index c26a765fe..dde02c40e 100644 --- a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java +++ b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java @@ -27,9 +27,11 @@ import com.radixdlt.consensus.CommittedStateSyncRx; import com.radixdlt.consensus.ConsensusRunner; import com.radixdlt.consensus.DefaultHasher; +import com.radixdlt.consensus.EmptySyncEpochsRPCSender; import com.radixdlt.consensus.EpochChangeRx; import com.radixdlt.consensus.EpochManager; import com.radixdlt.consensus.EventCoordinatorNetworkRx; +import com.radixdlt.consensus.SyncEpochsRPCSender; import com.radixdlt.consensus.SyncedStateComputer; import com.radixdlt.consensus.VertexStoreEventsRx; import com.radixdlt.consensus.InternalMessagePasser; @@ -102,11 +104,19 @@ protected void configure() { bind(Hasher.class).to(DefaultHasher.class); } + @Provides + @Singleton + private SyncEpochsRPCSender syncEpochsRPCSender() { + return EmptySyncEpochsRPCSender.INSTANCE; + } + @Provides @Singleton private EpochManager epochManager( + SyncedRadixEngine syncedRadixEngine, Mempool mempool, BFTEventSender sender, + SyncEpochsRPCSender syncEpochsRPCSender, ScheduledTimeoutSender scheduledTimeoutSender, PacemakerFactory pacemakerFactory, VertexStoreFactory vertexStoreFactory, @@ -115,10 +125,11 @@ private EpochManager epochManager( @Named("self") ECKeyPair selfKey, SystemCounters counters ) { - return new EpochManager( + syncedRadixEngine, mempool, sender, + syncEpochsRPCSender, scheduledTimeoutSender, pacemakerFactory, vertexStoreFactory, @@ -212,12 +223,11 @@ private PacemakerFactory pacemakerFactory() { @Provides @Singleton private VertexStoreFactory vertexStoreFactory( - SyncedRadixEngine syncedRadixEngine, SyncVerticesRPCSender syncVerticesRPCSender, VertexStoreEventSender vertexStoreEventSender, SystemCounters counters ) { - return (genesisVertex, genesisQC) -> new VertexStore( + return (genesisVertex, genesisQC, syncedRadixEngine) -> new VertexStore( genesisVertex, genesisQC, syncedRadixEngine, diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java b/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java index 95d0bd6c0..7796f4e3f 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusEvent.java @@ -17,6 +17,8 @@ package com.radixdlt.consensus; +import com.radixdlt.crypto.ECPublicKey; + /** * A message meant for consensus. Currently a marker interface so that all consensus * related messages can be handled within a single rxjava stream. @@ -29,4 +31,10 @@ public interface ConsensusEvent { * @return the epoch number */ long getEpoch(); + + /** + * Get the node author of this consensus message + * @return the node author + */ + ECPublicKey getAuthor(); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java new file mode 100644 index 000000000..44143e896 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java @@ -0,0 +1,34 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus; + +import com.radixdlt.crypto.ECPublicKey; + +public enum EmptySyncEpochsRPCSender implements SyncEpochsRPCSender { + INSTANCE; + + @Override + public void sendGetEpochRequest(ECPublicKey peer, long epoch) { + // No-op + } + + @Override + public void sendGetEpochResponse(ECPublicKey peer, VertexMetadata ancestor) { + // No-op + } +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index 2c4574930..be0446c52 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -20,6 +20,8 @@ import com.google.common.collect.ImmutableSet; import com.google.inject.name.Named; import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.epoch.GetEpochRequest; +import com.radixdlt.consensus.epoch.GetEpochResponse; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker.TimeoutSender; import com.radixdlt.consensus.liveness.MempoolProposalGenerator; import com.radixdlt.consensus.liveness.Pacemaker; @@ -36,6 +38,8 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.Hash; import com.radixdlt.mempool.Mempool; +import com.radixdlt.middleware2.CommittedAtom; +import java.util.Collections; import java.util.Objects; import javax.annotation.concurrent.NotThreadSafe; import org.apache.logging.log4j.LogManager; @@ -50,6 +54,7 @@ public class EpochManager { private static final BFTEventProcessor EMPTY_PROCESSOR = new EmptyBFTEventProcessor(); private final Mempool mempool; + private final SyncEpochsRPCSender epochsRPCSender; private final BFTEventSender sender; private final PacemakerFactory pacemakerFactory; private final VertexStoreFactory vertexStoreFactory; @@ -58,14 +63,17 @@ public class EpochManager { private final SystemCounters counters; private final Hasher hasher; private final ScheduledTimeoutSender scheduledTimeoutSender; + private final SyncedStateComputer syncedStateComputer; - private long currentEpoch; + private VertexMetadata currentAncestor; private VertexStore vertexStore; private BFTEventProcessor eventProcessor = EMPTY_PROCESSOR; public EpochManager( + SyncedStateComputer syncedStateComputer, Mempool mempool, BFTEventSender sender, + SyncEpochsRPCSender epochsRPCSender, ScheduledTimeoutSender scheduledTimeoutSender, PacemakerFactory pacemakerFactory, VertexStoreFactory vertexStoreFactory, @@ -74,8 +82,10 @@ public EpochManager( @Named("self") ECKeyPair selfKey, SystemCounters counters ) { + this.syncedStateComputer = Objects.requireNonNull(syncedStateComputer); this.mempool = Objects.requireNonNull(mempool); this.sender = Objects.requireNonNull(sender); + this.epochsRPCSender = Objects.requireNonNull(epochsRPCSender); this.scheduledTimeoutSender = Objects.requireNonNull(scheduledTimeoutSender); this.pacemakerFactory = Objects.requireNonNull(pacemakerFactory); this.vertexStoreFactory = Objects.requireNonNull(vertexStoreFactory); @@ -85,6 +95,10 @@ public EpochManager( this.hasher = Objects.requireNonNull(hasher); } + private long currentEpoch() { + return (this.currentAncestor == null) ? 0 : this.currentAncestor.getEpoch() + 1; + } + public void processEpochChange(EpochChange epochChange) { ValidatorSet validatorSet = epochChange.getValidatorSet(); log.info("NEXT_EPOCH: {} {}", epochChange); @@ -94,11 +108,11 @@ public void processEpochChange(EpochChange epochChange) { final long nextEpoch = genesisVertex.getEpoch(); // Sanity check - if (nextEpoch <= this.currentEpoch) { + if (nextEpoch <= this.currentEpoch()) { throw new IllegalStateException("Epoch change has already occurred: " + epochChange); } - this.currentEpoch = nextEpoch; + this.currentAncestor = ancestorMetadata; this.counters.set(CounterType.EPOCHS_EPOCH, nextEpoch); if (!validatorSet.containsKey(selfKey.getPublicKey())) { @@ -116,7 +130,7 @@ public void processEpochChange(EpochChange epochChange) { QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(genesisVertex); - this.vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC); + this.vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC, syncedStateComputer); ProposalGenerator proposalGenerator = new MempoolProposalGenerator(this.vertexStore, this.mempool); @@ -168,11 +182,38 @@ public void processGetVerticesResponse(GetVerticesResponse response) { vertexStore.processGetVerticesResponse(response); } + public void processGetEpochRequest(GetEpochRequest request) { + if (this.currentEpoch() == request.getEpoch()) { + epochsRPCSender.sendGetEpochResponse(request.getSender(), this.currentAncestor); + } else { + // TODO: Send better error message back + epochsRPCSender.sendGetEpochResponse(request.getSender(), null); + } + } + + public void processGetEpochResponse(GetEpochResponse response) { + if (response.getEpochAncestor() == null) { + log.warn("Received empty GetEpochResponse {}", response); + return; + } + + final VertexMetadata ancestor = response.getEpochAncestor(); + if (ancestor.getEpoch() + 1 > this.currentEpoch()) { + syncedStateComputer.syncTo(ancestor, Collections.singletonList(response.getSender()), null); + } + } + public void processConsensusEvent(ConsensusEvent consensusEvent) { + if (consensusEvent.getEpoch() > this.currentEpoch()) { + log.warn("Received higher epoch event {} from current epoch: {}", consensusEvent, this.currentEpoch()); + epochsRPCSender.sendGetEpochRequest(consensusEvent.getAuthor(), this.currentEpoch() + 1); + return; + } + // TODO: Add the rest of consensus event verification here including signature verification - if (consensusEvent.getEpoch() != this.currentEpoch) { - log.warn("Received event not in the current epoch ({}): {}", this.currentEpoch, consensusEvent); + if (consensusEvent.getEpoch() < this.currentEpoch()) { + log.warn("Received lower epoch event {} from current epoch: {}", consensusEvent, this.currentEpoch()); return; } @@ -188,7 +229,7 @@ public void processConsensusEvent(ConsensusEvent consensusEvent) { } public void processLocalTimeout(LocalTimeout localTimeout) { - if (localTimeout.getEpoch() != this.currentEpoch) { + if (localTimeout.getEpoch() != this.currentEpoch()) { return; } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/RequiresSyncConsensusEvent.java b/radixdlt/src/main/java/com/radixdlt/consensus/RequiresSyncConsensusEvent.java index fdb496ca9..b902ff310 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/RequiresSyncConsensusEvent.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/RequiresSyncConsensusEvent.java @@ -17,8 +17,6 @@ package com.radixdlt.consensus; -import com.radixdlt.crypto.ECPublicKey; - /** * A consensus event which requires syncing to be effectively * processed @@ -36,10 +34,4 @@ public interface RequiresSyncConsensusEvent extends ConsensusEvent { * @return highest known committed QC of peer */ QuorumCertificate getCommittedQC(); - - /** - * Get the author of the event - * @return the author of the event - */ - ECPublicKey getAuthor(); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/SyncEpochsRPCSender.java b/radixdlt/src/main/java/com/radixdlt/consensus/SyncEpochsRPCSender.java new file mode 100644 index 000000000..27865699e --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/SyncEpochsRPCSender.java @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus; + +import com.radixdlt.crypto.ECPublicKey; + +/** + * A sender of GetEpoch RPC requests/responses + */ +public interface SyncEpochsRPCSender { + + /** + * Send a request to a peer for proof of an epoch + * @param peer the peer to send to + * @param epoch the epoch to retrieve proof for + */ + void sendGetEpochRequest(ECPublicKey peer, long epoch); + + /** + * Send an epoch proof resposne to a peer + * + * TODO: currently just actually sending an ancestor but should contain + * TODO: proof as well + * + * @param peer the peer to send to + * @param ancestor the ancestor of the epoch + */ + void sendGetEpochResponse(ECPublicKey peer, VertexMetadata ancestor); +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java index 55caf0e86..d91754ca2 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java @@ -17,6 +17,8 @@ package com.radixdlt.consensus; +import com.radixdlt.middleware2.CommittedAtom; + /** * A Vertex Store factory */ @@ -26,7 +28,8 @@ public interface VertexStoreFactory { * Creates a new VertexStore given initial vertex and QC * @param genesisVertex the root vertex * @param genesisQC the root QC + * @param syncedStateComputer the underlying state computer * @return a new VertexStore */ - VertexStore create(Vertex genesisVertex, QuorumCertificate genesisQC); + VertexStore create(Vertex genesisVertex, QuorumCertificate genesisQC, SyncedStateComputer syncedStateComputer); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java b/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java index c35d6e6bb..028f1c41d 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/Vote.java @@ -69,6 +69,7 @@ public long getEpoch() { return voteData.getProposed().getEpoch(); } + @Override public ECPublicKey getAuthor() { return author; } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java b/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java new file mode 100644 index 000000000..eecfbfec0 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java @@ -0,0 +1,41 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.epoch; + +import com.radixdlt.crypto.ECPublicKey; + +/** + * An RPC request to retrieve proof of an epoch + */ +public final class GetEpochRequest { + private final long epoch; + private final ECPublicKey sender; + + public GetEpochRequest(ECPublicKey sender, final long epoch) { + this.sender = sender; + this.epoch = epoch; + } + + public long getEpoch() { + return epoch; + } + + public ECPublicKey getSender() { + return sender; + } +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochResponse.java b/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochResponse.java new file mode 100644 index 000000000..1a97bd090 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochResponse.java @@ -0,0 +1,46 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.epoch; + +import com.radixdlt.consensus.VertexMetadata; +import com.radixdlt.crypto.ECPublicKey; + +/** + * An RPC Response to a GetEpoch request + */ +public final class GetEpochResponse { + private final VertexMetadata epochAncestor; + private final ECPublicKey sender; + + public GetEpochResponse(ECPublicKey sender, VertexMetadata epochAncestor) { + this.epochAncestor = epochAncestor; + this.sender = sender; + } + + public ECPublicKey getSender() { + return sender; + } + + public VertexMetadata getEpochAncestor() { + return epochAncestor; + } + + public String toString() { + return String.format("%s{sender=%s ancestor=%s}", this.getClass().getSimpleName(), this.sender, this.epochAncestor); + } +} diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index f7f435e78..f174a5036 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -14,6 +14,7 @@ import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; +import com.radixdlt.consensus.sync.SyncedRadixEngine; import com.radixdlt.consensus.validators.Validator; import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.counters.SystemCounters; @@ -21,29 +22,48 @@ import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; import com.radixdlt.mempool.Mempool; +import org.junit.Before; import org.junit.Test; public class EpochManagerTest { - @Test - public void when_next_epoch_does_not_contain_self__then_should_not_emit_any_consensus_events() { + private ECPublicKey publicKey; + private EpochManager epochManager; + private BFTEventSender bftEventSender; + private SyncEpochsRPCSender syncEpochsRPCSender; + private ScheduledTimeoutSender scheduledTimeoutSender; + private VertexStore vertexStore; + + @Before + public void setup() { ECKeyPair keyPair = mock(ECKeyPair.class); - ECPublicKey publicKey = mock(ECPublicKey.class); + this.publicKey = mock(ECPublicKey.class); when(keyPair.getPublicKey()).thenReturn(publicKey); - BFTEventSender bftEventSender = mock(BFTEventSender.class); - ScheduledTimeoutSender scheduledTimeoutSender = mock(ScheduledTimeoutSender.class); + this.bftEventSender = mock(BFTEventSender.class); + this.syncEpochsRPCSender = mock(SyncEpochsRPCSender.class); + this.scheduledTimeoutSender = mock(ScheduledTimeoutSender.class); + + this.vertexStore = mock(VertexStore.class); + VertexStoreFactory vertexStoreFactory = mock(VertexStoreFactory.class); + when(vertexStoreFactory.create(any(), any(), any())).thenReturn(this.vertexStore); - EpochManager epochManager = new EpochManager( + this.epochManager = new EpochManager( + mock(SyncedRadixEngine.class), mock(Mempool.class), bftEventSender, + syncEpochsRPCSender, scheduledTimeoutSender, timeoutSender -> mock(Pacemaker.class), - mock(VertexStoreFactory.class), + vertexStoreFactory, proposers -> mock(ProposerElection.class), mock(Hasher.class), keyPair, mock(SystemCounters.class) ); + } + + @Test + public void when_next_epoch_does_not_contain_self__then_should_not_emit_any_consensus_events() { EpochChange epochChange = mock(EpochChange.class); ValidatorSet validatorSet = mock(ValidatorSet.class); when(validatorSet.containsKey(eq(publicKey))).thenReturn(false); @@ -55,23 +75,12 @@ public void when_next_epoch_does_not_contain_self__then_should_not_emit_any_cons verify(bftEventSender, never()).sendNewView(any(), any()); verify(bftEventSender, never()).sendVote(any(), any()); verify(bftEventSender, never()).broadcastProposal(any()); + verify(syncEpochsRPCSender, never()).sendGetEpochRequest(any(), anyLong()); verify(scheduledTimeoutSender, never()).scheduleTimeout(any(), anyLong()); } @Test public void when_no_epoch_change__then_processing_events_should_not_fail() { - ECKeyPair keyPair = mock(ECKeyPair.class); - EpochManager epochManager = new EpochManager( - mock(Mempool.class), - mock(BFTEventSender.class), - mock(ScheduledTimeoutSender.class), - timeoutSender -> mock(Pacemaker.class), - mock(VertexStoreFactory.class), - proposers -> mock(ProposerElection.class), - mock(Hasher.class), - keyPair, - mock(SystemCounters.class) - ); epochManager.processLocalTimeout(mock(LocalTimeout.class)); epochManager.processLocalSync(mock(Hash.class)); epochManager.processGetVerticesRequest(mock(GetVerticesRequest.class)); @@ -84,23 +93,8 @@ public void when_no_epoch_change__then_processing_events_should_not_fail() { @Test public void when_next_epoch__then_get_vertices_rpc_should_be_forwarded_to_vertex_store() { - ECKeyPair keyPair = mock(ECKeyPair.class); - when(keyPair.getPublicKey()).thenReturn(mock(ECPublicKey.class)); - VertexStore vertexStore = mock(VertexStore.class); when(vertexStore.getHighestQC()).thenReturn(mock(QuorumCertificate.class)); - EpochManager epochManager = new EpochManager( - mock(Mempool.class), - mock(BFTEventSender.class), - mock(ScheduledTimeoutSender.class), - timeoutSender -> mock(Pacemaker.class), - (v, qc) -> vertexStore, - proposers -> mock(ProposerElection.class), - mock(Hasher.class), - keyPair, - mock(SystemCounters.class) - ); - Validator validator = mock(Validator.class); when(validator.nodeKey()).thenReturn(mock(ECPublicKey.class)); ValidatorSet validatorSet = mock(ValidatorSet.class); From a5ba7da6958487a42d6334d972d88e941432254f Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Fri, 3 Jul 2020 20:14:38 +0800 Subject: [PATCH 04/20] Refactor ControlledNode to use EpochManager --- .../deterministic/BFTDeterministicTest.java | 6 +- .../deterministic/ControlledBFTNode.java | 100 ++++++------------ 2 files changed, 37 insertions(+), 69 deletions(-) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/BFTDeterministicTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/BFTDeterministicTest.java index 5807edd0d..62ad0a6ae 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/BFTDeterministicTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/BFTDeterministicTest.java @@ -62,7 +62,7 @@ public BFTDeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanS .map(ECKeyPair::getPublicKey) .collect(ImmutableList.toImmutableList()); this.network = new ControlledBFTNetwork(pks); - ValidatorSet validatorSet = ValidatorSet.from( + ValidatorSet initialValidatorSet = ValidatorSet.from( pks.stream().map(pk -> Validator.from(pk, UInt256.ONE)).collect(Collectors.toList()) ); @@ -70,8 +70,8 @@ public BFTDeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanS .map(key -> new ControlledBFTNode( key, network.getSender(key.getPublicKey()), - new WeightedRotatingLeaders(validatorSet, Comparator.comparing(v -> v.nodeKey().euid()), 5), - validatorSet, + vset -> new WeightedRotatingLeaders(vset, Comparator.comparing(v -> v.nodeKey().euid()), 5), + initialValidatorSet, enableGetVerticesRPC, syncedSupplier )) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java index 8ad8681c4..2ed34dd11 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java @@ -19,37 +19,27 @@ import static org.mockito.Mockito.mock; -import com.radixdlt.consensus.BFTEventPreprocessor; -import com.radixdlt.consensus.BFTEventProcessor; import com.radixdlt.consensus.CommittedStateSync; +import com.radixdlt.consensus.ConsensusEvent; import com.radixdlt.consensus.DefaultHasher; +import com.radixdlt.consensus.EmptySyncEpochsRPCSender; import com.radixdlt.consensus.EmptySyncVerticesRPCSender; +import com.radixdlt.consensus.EpochChange; +import com.radixdlt.consensus.EpochManager; import com.radixdlt.consensus.GetVerticesResponse; +import com.radixdlt.consensus.LocalTimeout; +import com.radixdlt.consensus.ProposerElectionFactory; import com.radixdlt.consensus.VertexMetadata; import com.radixdlt.consensus.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.Hasher; -import com.radixdlt.consensus.NewView; -import com.radixdlt.consensus.PendingVotes; -import com.radixdlt.consensus.Proposal; -import com.radixdlt.consensus.QuorumCertificate; -import com.radixdlt.consensus.BFTEventReducer; -import com.radixdlt.consensus.SyncQueues; import com.radixdlt.consensus.SyncedStateComputer; import com.radixdlt.consensus.Vertex; import com.radixdlt.consensus.VertexStore; import com.radixdlt.consensus.SyncVerticesRPCSender; -import com.radixdlt.consensus.View; -import com.radixdlt.consensus.Vote; +import com.radixdlt.consensus.VertexStoreFactory; import com.radixdlt.consensus.deterministic.ControlledBFTNetwork.ControlledSender; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker; -import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker.TimeoutSender; -import com.radixdlt.consensus.liveness.MempoolProposalGenerator; -import com.radixdlt.consensus.liveness.Pacemaker; -import com.radixdlt.consensus.liveness.ProposalGenerator; -import com.radixdlt.consensus.liveness.ProposerElection; -import com.radixdlt.consensus.safety.SafetyRules; -import com.radixdlt.consensus.safety.SafetyState; -import com.radixdlt.consensus.validators.Validator; +import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; @@ -61,28 +51,26 @@ import com.radixdlt.middleware2.CommittedAtom; import java.util.List; import java.util.function.BooleanSupplier; -import java.util.stream.Collectors; /** * Controlled BFT Node where its state machine is managed by a synchronous * processNext() call. */ class ControlledBFTNode { - private final BFTEventProcessor ec; + private final EpochManager epochManager; private final SystemCounters systemCounters; - private final VertexStore vertexStore; + private final ValidatorSet initialValidatorSet; ControlledBFTNode( ECKeyPair key, ControlledSender sender, - ProposerElection proposerElection, - ValidatorSet validatorSet, + ProposerElectionFactory proposerElectionFactory, + ValidatorSet initialValidatorSet, boolean enableGetVerticesRPC, BooleanSupplier syncedSupplier ) { this.systemCounters = new SystemCountersImpl(); - Vertex genesisVertex = Vertex.createGenesis(); - QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(genesisVertex); + this.initialValidatorSet = initialValidatorSet; SyncedStateComputer stateComputer = new SyncedStateComputer() { @Override @@ -106,41 +94,24 @@ public void execute(CommittedAtom instruction) { }; SyncVerticesRPCSender syncVerticesRPCSender = enableGetVerticesRPC ? sender : EmptySyncVerticesRPCSender.INSTANCE; - this.vertexStore = new VertexStore(genesisVertex, genesisQC, stateComputer, syncVerticesRPCSender, sender, systemCounters); Mempool mempool = new EmptyMempool(); - ProposalGenerator proposalGenerator = new MempoolProposalGenerator(vertexStore, mempool); - TimeoutSender timeoutSender = mock(TimeoutSender.class); - // Timeout doesn't matter here - Pacemaker pacemaker = new FixedTimeoutPacemaker(1, timeoutSender); Hasher hasher = new DefaultHasher(); - SafetyRules safetyRules = new SafetyRules(key, SafetyState.initialState(), hasher); - PendingVotes pendingVotes = new PendingVotes(hasher); - BFTEventReducer reducer = new BFTEventReducer( - proposalGenerator, + VertexStoreFactory vertexStoreFactory = (vertex, qc, syncedStateComputer) -> + new VertexStore(vertex, qc, syncedStateComputer, syncVerticesRPCSender, sender, systemCounters); + + this.epochManager = new EpochManager( + stateComputer, mempool, sender, - safetyRules, - pacemaker, - vertexStore, - pendingVotes, - proposerElection, + EmptySyncEpochsRPCSender.INSTANCE, + mock(ScheduledTimeoutSender.class), + timeoutSender -> new FixedTimeoutPacemaker(1, timeoutSender), + vertexStoreFactory, + proposerElectionFactory, + hasher, key, - validatorSet, - systemCounters - ); - SyncQueues syncQueues = new SyncQueues( - validatorSet.getValidators().stream().map(Validator::nodeKey).collect(Collectors.toSet()), systemCounters ); - - this.ec = new BFTEventPreprocessor( - key.getPublicKey(), - reducer, - pacemaker, - vertexStore, - proposerElection, - syncQueues - ); } SystemCounters getSystemCounters() { @@ -148,26 +119,23 @@ SystemCounters getSystemCounters() { } void start() { - ec.start(); + EpochChange epochChange = new EpochChange(VertexMetadata.ofGenesisAncestor(), this.initialValidatorSet); + this.epochManager.processEpochChange(epochChange); } void processNext(Object msg) { if (msg instanceof GetVerticesRequest) { - vertexStore.processGetVerticesRequest((GetVerticesRequest) msg); + this.epochManager.processGetVerticesRequest((GetVerticesRequest) msg); } else if (msg instanceof GetVerticesResponse) { - vertexStore.processGetVerticesResponse((GetVerticesResponse) msg); + this.epochManager.processGetVerticesResponse((GetVerticesResponse) msg); } else if (msg instanceof CommittedStateSync) { - vertexStore.processCommittedStateSync((CommittedStateSync) msg); - } else if (msg instanceof View) { - ec.processLocalTimeout((View) msg); - } else if (msg instanceof NewView) { - ec.processNewView((NewView) msg); - } else if (msg instanceof Proposal) { - ec.processProposal((Proposal) msg); - } else if (msg instanceof Vote) { - ec.processVote((Vote) msg); + this.epochManager.processCommittedStateSync((CommittedStateSync) msg); + } else if (msg instanceof LocalTimeout) { + this.epochManager.processLocalTimeout((LocalTimeout) msg); + } else if (msg instanceof ConsensusEvent) { + this.epochManager.processConsensusEvent((ConsensusEvent) msg); } else if (msg instanceof Hash) { - ec.processLocalSync((Hash) msg); + this.epochManager.processLocalSync((Hash) msg); } else { throw new IllegalStateException("Unknown msg: " + msg); } From 3ad0cb14c9429edb5dbc928e69f642e1816f14be Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sat, 4 Jul 2020 00:22:20 +0800 Subject: [PATCH 05/20] Queue future epoch events in EpochManager --- .../deterministic/ControlledBFTNetwork.java | 9 +++- .../deterministic/ControlledBFTNode.java | 11 +++-- .../bft/synchronous/OneSlowNodeTest.java | 9 ++++ .../com/radixdlt/consensus/EpochManager.java | 44 ++++++++++++++----- 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java index 14ed6c444..c6956ef14 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java @@ -19,8 +19,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.radixdlt.EpochChangeSender; import com.radixdlt.consensus.BFTEventSender; import com.radixdlt.consensus.CommittedStateSync; +import com.radixdlt.consensus.EpochChange; import com.radixdlt.consensus.GetVerticesResponse; import com.radixdlt.consensus.NewView; import com.radixdlt.consensus.Proposal; @@ -164,7 +166,7 @@ public ControlledSender getSender(ECPublicKey sender) { return new ControlledSender(sender); } - public final class ControlledSender implements BFTEventSender, VertexStoreEventSender, SyncVerticesRPCSender { + public final class ControlledSender implements BFTEventSender, VertexStoreEventSender, SyncVerticesRPCSender, EpochChangeSender { private final ECPublicKey sender; private ControlledSender(ECPublicKey sender) { @@ -209,6 +211,11 @@ public void committedStateSync(CommittedStateSync committedStateSync) { putMesssage(new ControlledMessage(sender, sender, committedStateSync)); } + @Override + public void epochChange(EpochChange epochChange) { + putMesssage(new ControlledMessage(sender, sender, epochChange)); + } + @Override public void committedVertex(Vertex vertex) { // Ignore committed vertex signal diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java index 2ed34dd11..f0b898c7c 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java @@ -50,6 +50,7 @@ import com.radixdlt.mempool.Mempool; import com.radixdlt.middleware2.CommittedAtom; import java.util.List; +import java.util.Objects; import java.util.function.BooleanSupplier; /** @@ -60,6 +61,7 @@ class ControlledBFTNode { private final EpochManager epochManager; private final SystemCounters systemCounters; private final ValidatorSet initialValidatorSet; + private final ControlledSender controlledSender; ControlledBFTNode( ECKeyPair key, @@ -70,7 +72,8 @@ class ControlledBFTNode { BooleanSupplier syncedSupplier ) { this.systemCounters = new SystemCountersImpl(); - this.initialValidatorSet = initialValidatorSet; + this.controlledSender = Objects.requireNonNull(sender); + this.initialValidatorSet = Objects.requireNonNull(initialValidatorSet); SyncedStateComputer stateComputer = new SyncedStateComputer() { @Override @@ -120,11 +123,13 @@ SystemCounters getSystemCounters() { void start() { EpochChange epochChange = new EpochChange(VertexMetadata.ofGenesisAncestor(), this.initialValidatorSet); - this.epochManager.processEpochChange(epochChange); + controlledSender.epochChange(epochChange); } void processNext(Object msg) { - if (msg instanceof GetVerticesRequest) { + if (msg instanceof EpochChange) { + this.epochManager.processEpochChange((EpochChange) msg); + } else if (msg instanceof GetVerticesRequest) { this.epochManager.processGetVerticesRequest((GetVerticesRequest) msg); } else if (msg instanceof GetVerticesResponse) { this.epochManager.processGetVerticesResponse((GetVerticesResponse) msg); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java index 5c860d0c1..bf0b043e6 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java @@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import com.radixdlt.consensus.EpochChange; import com.radixdlt.consensus.NewView; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; @@ -34,6 +35,10 @@ public void when_three_fast_nodes_and_one_slow_node_two_cycles__then_missing_par test.start(); + test.processNextMsg(1, 1, EpochChange.class); + test.processNextMsg(2, 2, EpochChange.class); + test.processNextMsg(3, 3, EpochChange.class); + for (int curLeader = 1; curLeader <= 2; curLeader++) { test.processNextMsg(curLeader, 1, NewView.class); test.processNextMsg(curLeader, 2, NewView.class); @@ -62,6 +67,10 @@ public void when_three_fast_nodes_and_one_slow_node__then_missing_parent_should_ test.start(); + test.processNextMsg(1, 1, EpochChange.class); + test.processNextMsg(2, 2, EpochChange.class); + test.processNextMsg(3, 3, EpochChange.class); + for (int curLeader = 1; curLeader <= 3; curLeader++) { test.processNextMsg(curLeader, 1, NewView.class); test.processNextMsg(curLeader, 2, NewView.class); diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index 804a2b839..ca5977fda 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -38,7 +38,11 @@ import com.radixdlt.crypto.Hash; import com.radixdlt.mempool.Mempool; import com.radixdlt.middleware2.CommittedAtom; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; import javax.annotation.concurrent.NotThreadSafe; import org.apache.logging.log4j.LogManager; @@ -63,6 +67,7 @@ public class EpochManager { private final Hasher hasher; private final ScheduledTimeoutSender scheduledTimeoutSender; private final SyncedStateComputer syncedStateComputer; + private final Map> queuedEvents; private VertexMetadata currentAncestor; private VertexStore vertexStore; @@ -92,6 +97,7 @@ public EpochManager( this.selfKey = Objects.requireNonNull(selfKey); this.counters = Objects.requireNonNull(counters); this.hasher = Objects.requireNonNull(hasher); + this.queuedEvents = new HashMap<>(); } private long currentEpoch() { @@ -163,6 +169,12 @@ public void processEpochChange(EpochChange epochChange) { syncQueues ); this.eventProcessor.start(); + + // Execute any queued up consensus events + for (ConsensusEvent consensusEvent : queuedEvents.getOrDefault(nextEpoch, Collections.emptyList())) { + this.processConsensusEventInternal(consensusEvent); + } + queuedEvents.remove(nextEpoch); } public void processGetVerticesRequest(GetVerticesRequest request) { @@ -202,29 +214,39 @@ public void processGetEpochResponse(GetEpochResponse response) { } } + private void processConsensusEventInternal(ConsensusEvent consensusEvent) { + if (consensusEvent instanceof NewView) { + eventProcessor.processNewView((NewView) consensusEvent); + } else if (consensusEvent instanceof Proposal) { + eventProcessor.processProposal((Proposal) consensusEvent); + } else if (consensusEvent instanceof Vote) { + eventProcessor.processVote((Vote) consensusEvent); + } else { + throw new IllegalStateException("Unknown consensus event: " + consensusEvent); + } + } + public void processConsensusEvent(ConsensusEvent consensusEvent) { + // TODO: Add the rest of consensus event verification here including signature verification + if (consensusEvent.getEpoch() > this.currentEpoch()) { log.warn("Received higher epoch event {} from current epoch: {}", consensusEvent, this.currentEpoch()); + + // queue higher epoch events for later processing + // TODO: need to clear this by some rule (e.g. timeout or max size) + queuedEvents.computeIfAbsent(consensusEvent.getEpoch(), e -> new ArrayList<>()).add(consensusEvent); + + // Send request for higher epoch proof epochsRPCSender.sendGetEpochRequest(consensusEvent.getAuthor(), this.currentEpoch() + 1); return; } - // TODO: Add the rest of consensus event verification here including signature verification - if (consensusEvent.getEpoch() < this.currentEpoch()) { log.warn("Received lower epoch event {} from current epoch: {}", consensusEvent, this.currentEpoch()); return; } - if (consensusEvent instanceof NewView) { - eventProcessor.processNewView((NewView) consensusEvent); - } else if (consensusEvent instanceof Proposal) { - eventProcessor.processProposal((Proposal) consensusEvent); - } else if (consensusEvent instanceof Vote) { - eventProcessor.processVote((Vote) consensusEvent); - } else { - throw new IllegalStateException("Unknown consensus event: " + consensusEvent); - } + this.processConsensusEventInternal(consensusEvent); } public void processLocalTimeout(LocalTimeout localTimeout) { From a16fdcaee03345d2233038875a42affeefbdf4d9 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 15:55:47 +0800 Subject: [PATCH 06/20] Refactor VertexStore --- .../deterministic/ControlledBFTNetwork.java | 11 +- .../deterministic/ControlledBFTNode.java | 5 +- .../consensus/EmptySyncVerticesRPCSender.java | 6 ++ .../com/radixdlt/consensus/EpochManager.java | 10 ++ .../radixdlt/consensus/SyncVerticesRPCRx.java | 8 ++ .../consensus/SyncVerticesRPCSender.java | 8 ++ .../com/radixdlt/consensus/VertexStore.java | 102 +++++++++++------- .../bft/GetVerticesErrorResponse.java | 57 ++++++++++ .../{ => bft}/GetVerticesResponse.java | 3 +- .../GetVerticesErrorResponseMessage.java | 75 +++++++++++++ .../MessageCentralSyncVerticesRPCNetwork.java | 39 ++++++- .../network/TestEventCoordinatorNetwork.java | 19 +++- .../radixdlt/consensus/EpochManagerTest.java | 1 + .../radixdlt/consensus/VertexStoreTest.java | 1 + ...sageCentralSyncVerticesRPCNetworkTest.java | 2 +- 15 files changed, 304 insertions(+), 43 deletions(-) create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java rename radixdlt/src/main/java/com/radixdlt/consensus/{ => bft}/GetVerticesResponse.java (95%) create mode 100644 radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java index c6956ef14..b6aad89f6 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java @@ -23,7 +23,8 @@ import com.radixdlt.consensus.BFTEventSender; import com.radixdlt.consensus.CommittedStateSync; import com.radixdlt.consensus.EpochChange; -import com.radixdlt.consensus.GetVerticesResponse; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.NewView; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.QuorumCertificate; @@ -185,6 +186,14 @@ public void sendGetVerticesResponse(GetVerticesRequest originalRequest, Immutabl putMesssage(new ControlledMessage(sender, request.requestor, response)); } + @Override + public void sendGetVerticesErrorResponse(GetVerticesRequest originalRequest, QuorumCertificate highestQC, + QuorumCertificate highestCommittedQC) { + ControlledGetVerticesRequest request = (ControlledGetVerticesRequest) originalRequest; + GetVerticesErrorResponse response = new GetVerticesErrorResponse(request.getVertexId(), highestQC, highestCommittedQC, request.opaque); + putMesssage(new ControlledMessage(sender, request.requestor, response)); + } + @Override public void syncedVertex(Vertex vertex) { putMesssage(new ControlledMessage(sender, sender, vertex.getId())); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java index f0b898c7c..85d596579 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java @@ -26,7 +26,8 @@ import com.radixdlt.consensus.EmptySyncVerticesRPCSender; import com.radixdlt.consensus.EpochChange; import com.radixdlt.consensus.EpochManager; -import com.radixdlt.consensus.GetVerticesResponse; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.LocalTimeout; import com.radixdlt.consensus.ProposerElectionFactory; import com.radixdlt.consensus.VertexMetadata; @@ -133,6 +134,8 @@ void processNext(Object msg) { this.epochManager.processGetVerticesRequest((GetVerticesRequest) msg); } else if (msg instanceof GetVerticesResponse) { this.epochManager.processGetVerticesResponse((GetVerticesResponse) msg); + } else if (msg instanceof GetVerticesErrorResponse) { + this.epochManager.processGetVerticesErrorResponse((GetVerticesErrorResponse) msg); } else if (msg instanceof CommittedStateSync) { this.epochManager.processCommittedStateSync((CommittedStateSync) msg); } else if (msg instanceof LocalTimeout) { diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java index 8c1797834..f7274c690 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java @@ -37,4 +37,10 @@ public void sendGetVerticesRequest(Hash id, ECPublicKey node, int count, Object public void sendGetVerticesResponse(GetVerticesRequest originalRequest, ImmutableList vertices) { // empty } + + @Override + public void sendGetVerticesErrorResponse(GetVerticesRequest originalRequest, QuorumCertificate highestQC, + QuorumCertificate highestCommittedQC) { + // empty + } } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index ca5977fda..2299314b9 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -19,6 +19,8 @@ import com.google.common.collect.ImmutableSet; import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.epoch.GetEpochRequest; import com.radixdlt.consensus.epoch.GetEpochResponse; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker.TimeoutSender; @@ -185,6 +187,14 @@ public void processGetVerticesRequest(GetVerticesRequest request) { vertexStore.processGetVerticesRequest(request); } + public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { + if (this.vertexStore == null) { + return; + } + + vertexStore.processGetVerticesErrorResponse(response); + } + public void processGetVerticesResponse(GetVerticesResponse response) { if (this.vertexStore == null) { return; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java index 7cea56dac..e728f860c 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java @@ -18,6 +18,8 @@ package com.radixdlt.consensus; import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import io.reactivex.rxjava3.core.Observable; /** @@ -36,4 +38,10 @@ public interface SyncVerticesRPCRx { * @return a never-ending stream of responses */ Observable responses(); + + /** + * Retrieve a never-ending stream of error responses + * @return a never-ending stream of error responses + */ + Observable errorResponses(); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java index 30d596505..547379492 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java @@ -44,4 +44,12 @@ public interface SyncVerticesRPCSender { * @param vertices the response data of vertices */ void sendGetVerticesResponse(GetVerticesRequest originalRequest, ImmutableList vertices); + + /** + * Send an RPC error response to a given request + * @param originalRequest the original request + * @param highestQC highestQC sync info + * @param highestCommittedQC highestCommittedQC sync info + */ + void sendGetVerticesErrorResponse(GetVerticesRequest originalRequest, QuorumCertificate highestQC, QuorumCertificate highestCommittedQC); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java index 7c4f07187..a58cfd26f 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java @@ -18,6 +18,8 @@ package com.radixdlt.consensus; import com.google.common.collect.ImmutableList; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.crypto.ECPublicKey; @@ -27,23 +29,22 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import javax.annotation.Nullable; +import javax.annotation.concurrent.NotThreadSafe; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; /** * Manages the BFT Vertex chain. - * - * In general this class is NOT thread-safe except for getVertices() and getHighestQC(). */ +@NotThreadSafe public final class VertexStore { private static final Logger log = LogManager.getLogger(); @@ -58,19 +59,18 @@ public interface VertexStoreEventSender { void highQC(QuorumCertificate qc); } - private final VertexStoreEventSender vertexStoreEventSender; private final SyncVerticesRPCSender syncVerticesRPCSender; private final SyncedStateComputer syncedStateComputer; private final SystemCounters counters; - // These should never be empty - private final AtomicReference rootId = new AtomicReference<>(); - private final AtomicReference highestQC = new AtomicReference<>(); - private final AtomicReference highestCommittedQC = new AtomicReference<>(); + // These should never be null + private Hash rootId; + private QuorumCertificate highestQC; + private QuorumCertificate highestCommittedQC; - private final Map vertices = new ConcurrentHashMap<>(); - private final Map syncing = new ConcurrentHashMap<>(); + private final Map vertices = new HashMap<>(); + private final Map syncing = new HashMap<>(); public VertexStore( Vertex rootVertex, @@ -112,6 +112,10 @@ public VertexStore( this.rebuild(rootVertex, rootQC, rootQC, vertices); } + private Vertex getRoot() { + return this.vertices.get(this.rootId); + } + private void rebuild(Vertex rootVertex, QuorumCertificate rootQC, QuorumCertificate rootCommitQC, List vertices) { if (!rootQC.getProposed().getId().equals(rootVertex.getId())) { throw new IllegalStateException(String.format("rootQC=%s does not match rootVertex=%s", rootQC, rootVertex)); @@ -127,10 +131,10 @@ private void rebuild(Vertex rootVertex, QuorumCertificate rootQC, QuorumCertific } this.vertices.clear(); - this.rootId.set(rootVertex.getId()); - this.highestQC.set(rootQC); + this.rootId = rootVertex.getId(); + this.highestQC = rootQC; this.vertexStoreEventSender.highQC(rootQC); - this.highestCommittedQC.set(rootCommitQC); + this.highestCommittedQC = rootCommitQC; this.vertices.put(rootVertex.getId(), rootVertex); for (Vertex vertex : vertices) { @@ -196,7 +200,7 @@ public String toString() { private boolean requiresCommittedStateSync(SyncState syncState) { final VertexMetadata committedMetadata = syncState.committedVertexMetadata; if (!vertices.containsKey(committedMetadata.getId())) { - View rootView = vertices.get(rootId.get()).getView(); + View rootView = this.getRoot().getView(); return rootView.compareTo(committedMetadata.getView()) < 0; } @@ -208,12 +212,17 @@ public void processGetVerticesRequest(GetVerticesRequest request) { log.info("SYNC_VERTICES: Received GetVerticesRequest {}", request); ImmutableList fetched = this.getVertices(request.getVertexId(), request.getCount()); + if (fetched.isEmpty()) { + this.syncVerticesRPCSender.sendGetVerticesErrorResponse(request, this.getHighestQC(), this.getHighestCommittedQC()); + return; + } + log.info("SYNC_VERTICES: Sending Response {}", fetched); this.syncVerticesRPCSender.sendGetVerticesResponse(request, fetched); } private void rebuildAndSyncQC(SyncState syncState) { - log.info("SYNC_STATE: Rebuilding and syncing QC: sync={} curRoot={}", syncState, vertices.get(rootId.get())); + log.info("SYNC_STATE: Rebuilding and syncing QC: sync={} curRoot={}", syncState, this.getRoot()); // TODO: check if there are any vertices which haven't been local sync processed yet if (requiresCommittedStateSync(syncState)) { @@ -275,13 +284,13 @@ private void processVerticesResponseForQCSync(Hash syncTo, SyncState syncState, addQC(syncState.qc); } else { log.info("SYNC_VERTICES: Sending further GetVerticesRequest for qc={} fetched={} root={}", - syncState.qc, syncState.fetched.size(), vertices.get(rootId.get())); + syncState.qc, syncState.fetched.size(), this.getRoot()); syncVerticesRPCSender.sendGetVerticesRequest(nextVertexId, syncState.author, 1, syncTo); } } - public void processGetVerticesResponse(GetVerticesResponse response) { - log.info("SYNC_VERTICES: Received GetVerticesResponse {}", response); + public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { + log.info("SYNC_VERTICES: Received GetVerticesErrorResponse {} ", response); final Hash syncTo = (Hash) response.getOpaque(); SyncState syncState = syncing.get(syncTo); @@ -289,11 +298,27 @@ public void processGetVerticesResponse(GetVerticesResponse response) { return; // sync requirements already satisfied by another sync } - if (response.getVertices().isEmpty()) { - log.info("GET_VERTICES failed: response was empty sync={}", syncState); - // failed - // TODO: retry - return; + switch (syncState.syncStage) { + case GET_COMMITTED_VERTICES: + // TODO: retry + break; + case GET_QC_VERTICES: + // TODO: retry + break; + default: + throw new IllegalStateException("Unknown sync stage: " + syncState.syncStage); + } + } + + public void processGetVerticesResponse(GetVerticesResponse response) { + // TODO: check response + + log.info("SYNC_VERTICES: Received GetVerticesResponse {}", response); + + final Hash syncTo = (Hash) response.getOpaque(); + SyncState syncState = syncing.get(syncTo); + if (syncState == null) { + return; // sync requirements already satisfied by another sync } switch (syncState.syncStage) { @@ -341,6 +366,10 @@ public void processLocalSync(Hash vertexId) { * @return true if already synced, false otherwise */ public boolean syncToQC(QuorumCertificate qc, QuorumCertificate committedQC, @Nullable ECPublicKey author) { + if (qc.getProposed().getView().compareTo(this.getRoot().getView()) < 0) { + return true; + } + if (addQC(qc)) { return true; } @@ -374,20 +403,19 @@ private boolean addQC(QuorumCertificate qc) { return false; } - if (highestQC.get().getView().compareTo(qc.getView()) < 0) { - highestQC.set(qc); + if (highestQC.getView().compareTo(qc.getView()) < 0) { + highestQC = qc; vertexStoreEventSender.highQC(qc); } qc.getCommitted().ifPresent(vertexMetadata -> { - QuorumCertificate highestCommitted = highestCommittedQC.get(); - Optional highest = highestCommitted.getCommitted(); - if (!highest.isPresent() && !highestCommitted.getView().isGenesis()) { - throw new IllegalStateException(String.format("Highest Committed does not have a commit: %s", highestCommitted)); + Optional highest = this.highestCommittedQC.getCommitted(); + if (!highest.isPresent() && !this.highestCommittedQC.getView().isGenesis()) { + throw new IllegalStateException(String.format("Highest Committed does not have a commit: %s", this.highestCommittedQC)); } if (!highest.isPresent() || highest.get().getView().compareTo(vertexMetadata.getView()) < 0) { - this.highestCommittedQC.set(qc); + this.highestCommittedQC = qc; } }); @@ -436,7 +464,7 @@ public VertexMetadata insertVertex(Vertex vertex) throws VertexInsertionExceptio * @return the vertex if sucessful, otherwise an empty optional if vertex was already committed */ public Optional commitVertex(VertexMetadata commitMetadata) { - if (commitMetadata.getView().compareTo(vertices.get(rootId.get()).getView()) < 0) { + if (commitMetadata.getView().compareTo(this.getRoot().getView()) < 0) { return Optional.empty(); } @@ -447,7 +475,7 @@ public Optional commitVertex(VertexMetadata commitMetadata) { } final LinkedList path = new LinkedList<>(); Vertex vertex = tipVertex; - while (vertex != null && !rootId.get().equals(vertex.getId())) { + while (vertex != null && !rootId.equals(vertex.getId())) { path.addFirst(vertex); vertex = vertices.remove(vertex.getParentId()); } @@ -460,7 +488,7 @@ public Optional commitVertex(VertexMetadata commitMetadata) { this.vertexStoreEventSender.committedVertex(committed); } - rootId.set(commitMetadata.getId()); + rootId = commitMetadata.getId(); updateVertexStoreSize(); return Optional.of(tipVertex); @@ -470,7 +498,7 @@ public List getPathFromRoot(Hash vertexId) { final List path = new ArrayList<>(); Vertex vertex = vertices.get(vertexId); - while (vertex != null && !vertex.getId().equals(rootId.get())) { + while (vertex != null && !vertex.getId().equals(rootId)) { path.add(vertex); vertex = vertices.get(vertex.getParentId()); } @@ -483,7 +511,7 @@ public List getPathFromRoot(Hash vertexId) { * @return the highest committed qc */ public QuorumCertificate getHighestCommittedQC() { - return this.highestCommittedQC.get(); + return this.highestCommittedQC; } /** @@ -493,7 +521,7 @@ public QuorumCertificate getHighestCommittedQC() { * @return the highest quorum certificate */ public QuorumCertificate getHighestQC() { - return this.highestQC.get(); + return this.highestQC; } /** diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java b/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java new file mode 100644 index 000000000..b388d3e57 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java @@ -0,0 +1,57 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.bft; + +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.crypto.Hash; +import java.util.Objects; + +public class GetVerticesErrorResponse { + private final Hash vertexId; + private final Object opaque; + private final QuorumCertificate highestQC; + private final QuorumCertificate highestCommittedQC; + + public GetVerticesErrorResponse(Hash vertexId, QuorumCertificate highestQC, QuorumCertificate highestCommittedQC, Object opaque) { + this.vertexId = Objects.requireNonNull(vertexId); + this.highestQC = Objects.requireNonNull(highestQC); + this.highestCommittedQC = Objects.requireNonNull(highestCommittedQC); + this.opaque = opaque; + } + + public Hash getVertexId() { + return vertexId; + } + + public Object getOpaque() { + return opaque; + } + + public QuorumCertificate getHighestQC() { + return highestQC; + } + + public QuorumCertificate getHighestCommittedQC() { + return highestCommittedQC; + } + + @Override + public String toString() { + return String.format("%s{highQC=%s highCommittedQC=%s opaque=%s}", this.getClass().getSimpleName(), highestQC, highestCommittedQC, opaque); + } +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/GetVerticesResponse.java b/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesResponse.java similarity index 95% rename from radixdlt/src/main/java/com/radixdlt/consensus/GetVerticesResponse.java rename to radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesResponse.java index 33c81ac75..13344ab4d 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/GetVerticesResponse.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesResponse.java @@ -15,8 +15,9 @@ * language governing permissions and limitations under the License. */ -package com.radixdlt.consensus; +package com.radixdlt.consensus.bft; +import com.radixdlt.consensus.Vertex; import com.radixdlt.crypto.Hash; import java.util.List; import java.util.Objects; diff --git a/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java b/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java new file mode 100644 index 000000000..15309dedb --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessage.java @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.middleware2.network; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.crypto.Hash; +import com.radixdlt.serialization.DsonOutput; +import com.radixdlt.serialization.DsonOutput.Output; +import com.radixdlt.serialization.SerializerId2; +import java.util.Objects; +import org.radix.network.messaging.Message; + +@SerializerId2("message.consensus.vertices_error_response") +public class GetVerticesErrorResponseMessage extends Message { + @JsonProperty("vertexId") + @DsonOutput(Output.ALL) + private final Hash vertexId; + + @JsonProperty("highest_qc") + @DsonOutput(Output.ALL) + private final QuorumCertificate highestQC; + + @JsonProperty("highest_committed_qc") + @DsonOutput(Output.ALL) + private final QuorumCertificate highestCommittedQC; + + GetVerticesErrorResponseMessage() { + // Serializer only + super(0); + this.vertexId = null; + this.highestQC = null; + this.highestCommittedQC = null; + } + + GetVerticesErrorResponseMessage(int magic, Hash vertexId, QuorumCertificate highestQC, QuorumCertificate highestCommittedQC) { + super(magic); + this.vertexId = Objects.requireNonNull(vertexId); + this.highestQC = Objects.requireNonNull(highestQC); + this.highestCommittedQC = Objects.requireNonNull(highestCommittedQC); + } + + public Hash getVertexId() { + return vertexId; + } + + public QuorumCertificate getHighestQC() { + return highestQC; + } + + public QuorumCertificate getHighestCommittedQC() { + return highestCommittedQC; + } + + @Override + public String toString() { + return String.format("%s{vertexId=%s}", getClass().getSimpleName(), vertexId); + } + +} diff --git a/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java b/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java index 55610c2a0..b5aa74e17 100644 --- a/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java +++ b/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java @@ -21,7 +21,9 @@ import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableList; import com.google.inject.name.Named; -import com.radixdlt.consensus.GetVerticesResponse; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.SyncVerticesRPCRx; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.Vertex; @@ -92,6 +94,19 @@ public void sendGetVerticesResponse(GetVerticesRequest originalRequest, Immutabl this.messageCentral.send(peer, response); } + @Override + public void sendGetVerticesErrorResponse(GetVerticesRequest originalRequest, QuorumCertificate highestQC, QuorumCertificate highestCommittedQC) { + MessageCentralGetVerticesRequest messageCentralGetVerticesRequest = (MessageCentralGetVerticesRequest) originalRequest; + GetVerticesErrorResponseMessage response = new GetVerticesErrorResponseMessage( + this.magic, + messageCentralGetVerticesRequest.getVertexId(), + highestQC, + highestCommittedQC + ); + Peer peer = messageCentralGetVerticesRequest.getRequestor(); + this.messageCentral.send(peer, response); + } + @Override public Observable requests() { return Observable.create(emitter -> { @@ -121,6 +136,28 @@ public Observable responses() { }); } + @Override + public Observable errorResponses() { + return Observable.create(emitter -> { + MessageListener listener = (src, msg) -> { + Object opaque = opaqueCache.getIfPresent(msg.getVertexId()); + if (opaque == null) { + return; // TODO: send error? + } + + GetVerticesErrorResponse response = new GetVerticesErrorResponse( + msg.getVertexId(), + msg.getHighestQC(), + msg.getHighestCommittedQC(), + opaque + ); + emitter.onNext(response); + }; + this.messageCentral.addListener(GetVerticesErrorResponseMessage.class, listener); + emitter.setCancellable(() -> this.messageCentral.removeListener(listener)); + }); + } + /** * An RPC request to retrieve a given vertex */ diff --git a/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java b/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java index 45a57914a..ef2203318 100644 --- a/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java +++ b/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java @@ -21,7 +21,9 @@ import com.radixdlt.consensus.ConsensusEvent; import com.radixdlt.consensus.EventCoordinatorNetworkRx; import com.radixdlt.consensus.BFTEventSender; -import com.radixdlt.consensus.GetVerticesResponse; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.NewView; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.SyncVerticesRPCRx; @@ -245,6 +247,16 @@ public void sendGetVerticesResponse(GetVerticesRequest originalRequest, Immutabl receivedMessages.onNext(MessageInTransit.newMessage(vertexResponse, thisNode, request.requestor)); } + @Override + public void sendGetVerticesErrorResponse(GetVerticesRequest originalRequest, QuorumCertificate highestQC, + QuorumCertificate highestCommittedQC) { + + SimulatedVerticesRequest request = (SimulatedVerticesRequest) originalRequest; + Object opaque = receivers.computeIfAbsent(request.requestor, SimulatedNetworkImpl::new).opaqueMap.get(request.vertexId); + GetVerticesErrorResponse vertexResponse = new GetVerticesErrorResponse(request.vertexId, highestQC, highestCommittedQC, opaque); + receivedMessages.onNext(MessageInTransit.newMessage(vertexResponse, thisNode, request.requestor)); + } + @Override public Observable consensusEvents() { return myMessages.ofType(ConsensusEvent.class); @@ -259,6 +271,11 @@ public Observable requests() { public Observable responses() { return myMessages.ofType(GetVerticesResponse.class); } + + @Override + public Observable errorResponses() { + return myMessages.ofType(GetVerticesErrorResponse.class); + } } public SimulatedNetworkReceiver getNetworkRx(ECPublicKey forNode) { diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index 500305621..5055d63aa 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableSet; import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java index ee2bb87ba..7537227c5 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java @@ -33,6 +33,7 @@ import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.VertexStore.VertexStoreEventSender; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECDSASignatures; import com.radixdlt.crypto.ECPublicKey; diff --git a/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java b/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java index 8a2a0f75c..189c39c8d 100644 --- a/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java +++ b/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java @@ -27,7 +27,7 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.GetVerticesResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.Vertex; import com.radixdlt.consensus.VertexStore.GetVerticesRequest; import com.radixdlt.crypto.ECKeyPair; From 7c5870cd287eced67b0f9e64b4890b5f4eb8e246 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 17:33:09 +0800 Subject: [PATCH 07/20] Implement GetVertices retry --- .../deterministic/ControlledBFTNetwork.java | 9 +- .../deterministic/ControlledBFTNode.java | 2 + .../OneProposalDropperResponsiveTest.java | 2 +- ...oposalDropperRandomSyncResponsiveTest.java | 107 ++++++++++++++++++ .../simulation/network/SimulatedNetwork.java | 2 + .../java/com/radixdlt/CerberusModule.java | 2 + .../com/radixdlt/consensus/BFTFactory.java | 50 ++++++++ .../com/radixdlt/consensus/EpochManager.java | 100 ++++++++-------- .../consensus/InternalMessagePasser.java | 4 +- .../com/radixdlt/consensus/VertexStore.java | 44 +++---- .../com/radixdlt/counters/SystemCounters.java | 3 +- .../radixdlt/consensus/EpochManagerTest.java | 95 +++++++++++++--- .../consensus/InternalMessagePasserTest.java | 4 +- .../radixdlt/consensus/VertexStoreTest.java | 20 ++-- 14 files changed, 344 insertions(+), 100 deletions(-) create mode 100644 radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java index b6aad89f6..93feaf4aa 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java @@ -161,6 +161,11 @@ public Hash getVertexId() { public int getCount() { return count; } + + @Override + public String toString() { + return String.format("%s{count=%s}", this.getClass().getSimpleName(), count); + } } public ControlledSender getSender(ECPublicKey sender) { @@ -195,7 +200,7 @@ public void sendGetVerticesErrorResponse(GetVerticesRequest originalRequest, Quo } @Override - public void syncedVertex(Vertex vertex) { + public void sendSyncedVertex(Vertex vertex) { putMesssage(new ControlledMessage(sender, sender, vertex.getId())); } @@ -226,7 +231,7 @@ public void epochChange(EpochChange epochChange) { } @Override - public void committedVertex(Vertex vertex) { + public void sendCommittedVertex(Vertex vertex) { // Ignore committed vertex signal } diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java index 85d596579..980ee38fe 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.mock; +import com.radixdlt.consensus.BFTEventReducer; import com.radixdlt.consensus.CommittedStateSync; import com.radixdlt.consensus.ConsensusEvent; import com.radixdlt.consensus.DefaultHasher; @@ -113,6 +114,7 @@ public void execute(CommittedAtom instruction) { vertexStoreFactory, proposerElectionFactory, hasher, + BFTEventReducer::new, key, systemCounters ); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java index c3e324ba7..db3667347 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java @@ -34,7 +34,7 @@ private void runOneProposalDropperResponsiveTest(int numNodes, Function proposalToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, random::nextBoolean); + final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, () -> true); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java new file mode 100644 index 000000000..45fabbcf1 --- /dev/null +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java @@ -0,0 +1,107 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.deterministic.tests.syncedstatecomputer; + +import com.radixdlt.consensus.Proposal; +import com.radixdlt.consensus.View; +import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.function.Function; +import org.junit.Test; + +public class OneProposalDropperRandomSyncResponsiveTest { + + private final Random random = new Random(123456); + + private void runOneProposalDropperResponsiveTest(int numNodes, Function nodeToDropFunction) { + final Map proposalToDrop = new HashMap<>(); + final Map proposalCount = new HashMap<>(); + + final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, random::nextBoolean); + test.start(); + for (int step = 0; step < 100000; step++) { + test.processNextMsg(random, (receiverId, msg) -> { + if (msg instanceof Proposal) { + final Proposal proposal = (Proposal) msg; + final View view = proposal.getVertex().getView(); + final Integer nodeToDrop = proposalToDrop.computeIfAbsent(view, nodeToDropFunction); + if (proposalCount.merge(view, 1, Integer::sum).equals(numNodes)) { + proposalToDrop.remove(view); + proposalCount.remove(view); + } + + return !receiverId.equals(nodeToDrop); + } + + return true; + }); + } + } + + @Test + public void when_run_4_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(4, v -> random.nextInt(4)); + } + + @Test + public void when_run_5_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(5, v -> random.nextInt(5)); + } + + @Test + public void when_run_10_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(10, v -> random.nextInt(10)); + } + + @Test + public void when_run_50_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(50, v -> random.nextInt(50)); + } + + @Test + public void when_run_100_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(100, v -> random.nextInt(100)); + } + + @Test + public void when_run_4_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(4, v -> 0); + } + + @Test + public void when_run_5_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(5, v -> 0); + } + + @Test + public void when_run_10_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(10, v -> 0); + } + + @Test + public void when_run_50_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(50, v -> 0); + } + + @Test + public void when_run_100_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runOneProposalDropperResponsiveTest(100, v -> 0); + } +} diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java index f3342bcc2..93b51d8f2 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java @@ -18,6 +18,7 @@ package com.radixdlt.consensus.simulation.network; import com.google.common.collect.ImmutableMap; +import com.radixdlt.consensus.BFTEventReducer; import com.radixdlt.consensus.ConsensusRunner; import com.radixdlt.consensus.ConsensusRunner.Event; import com.radixdlt.consensus.ConsensusRunner.EventType; @@ -128,6 +129,7 @@ private ConsensusRunner createBFTInstance(ECKeyPair key) { vertexStoreFactory, proposers -> new WeightedRotatingLeaders(proposers, Comparator.comparing(v -> v.nodeKey().euid()), 5), hasher, + BFTEventReducer::new, key, counters.get(key) ); diff --git a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java index dde02c40e..229301c63 100644 --- a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java +++ b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java @@ -22,6 +22,7 @@ import com.google.inject.Scopes; import com.google.inject.Singleton; import com.google.inject.name.Named; +import com.radixdlt.consensus.BFTEventReducer; import com.radixdlt.consensus.BFTEventSender; import com.radixdlt.consensus.AddressBookValidatorSetProvider; import com.radixdlt.consensus.CommittedStateSyncRx; @@ -135,6 +136,7 @@ private EpochManager epochManager( vertexStoreFactory, proposerElectionFactory, hasher, + BFTEventReducer::new, selfKey, counters ); diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java b/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java new file mode 100644 index 000000000..5260aadf8 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java @@ -0,0 +1,50 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus; + +import com.radixdlt.consensus.liveness.Pacemaker; +import com.radixdlt.consensus.liveness.ProposalGenerator; +import com.radixdlt.consensus.liveness.ProposerElection; +import com.radixdlt.consensus.safety.SafetyRules; +import com.radixdlt.consensus.validators.ValidatorSet; +import com.radixdlt.counters.SystemCounters; +import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.mempool.Mempool; + +public interface BFTFactory { + + /** + * Create a new BFT processor + * TODO: Cleanup + * + * @return a new bft processor + */ + BFTEventProcessor create( + ProposalGenerator proposalGenerator, + Mempool mempool, + BFTEventSender sender, + SafetyRules safetyRules, + Pacemaker pacemaker, + VertexStore vertexStore, + PendingVotes pendingVotes, + ProposerElection proposerElection, + ECKeyPair selfKey, + ValidatorSet validatorSet, + SystemCounters counters + ); +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index 2299314b9..c2ad3f09e 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -70,10 +70,12 @@ public class EpochManager { private final ScheduledTimeoutSender scheduledTimeoutSender; private final SyncedStateComputer syncedStateComputer; private final Map> queuedEvents; + private final BFTFactory bftFactory; private VertexMetadata currentAncestor; private VertexStore vertexStore; private BFTEventProcessor eventProcessor = EMPTY_PROCESSOR; + private int numQueuedConsensusEvents = 0; public EpochManager( SyncedStateComputer syncedStateComputer, @@ -85,6 +87,7 @@ public EpochManager( VertexStoreFactory vertexStoreFactory, ProposerElectionFactory proposerElectionFactory, Hasher hasher, + BFTFactory bftFactory, ECKeyPair selfKey, SystemCounters counters ) { @@ -96,6 +99,7 @@ public EpochManager( this.pacemakerFactory = Objects.requireNonNull(pacemakerFactory); this.vertexStoreFactory = Objects.requireNonNull(vertexStoreFactory); this.proposerElectionFactory = Objects.requireNonNull(proposerElectionFactory); + this.bftFactory = bftFactory; this.selfKey = Objects.requireNonNull(selfKey); this.counters = Objects.requireNonNull(counters); this.hasher = Objects.requireNonNull(hasher); @@ -120,62 +124,66 @@ public void processEpochChange(EpochChange epochChange) { } this.currentAncestor = ancestorMetadata; - this.counters.set(CounterType.EPOCHS_EPOCH, nextEpoch); + this.counters.set(CounterType.EPOCH_MANAGER_EPOCH, nextEpoch); if (!validatorSet.containsKey(selfKey.getPublicKey())) { log.info("NEXT_EPOCH: Not a validator"); this.eventProcessor = EMPTY_PROCESSOR; this.vertexStore = null; - return; + } else { + ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); + TimeoutSender sender = (view, ms) -> scheduledTimeoutSender.scheduleTimeout(new LocalTimeout(nextEpoch, view), ms); + Pacemaker pacemaker = pacemakerFactory.create(sender); + SafetyRules safetyRules = new SafetyRules(this.selfKey, SafetyState.initialState(), this.hasher); + PendingVotes pendingVotes = new PendingVotes(this.hasher); + + QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(genesisVertex); + + this.vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC, syncedStateComputer); + + ProposalGenerator proposalGenerator = new MempoolProposalGenerator(this.vertexStore, this.mempool); + + BFTEventProcessor reducer = bftFactory.create( + proposalGenerator, + this.mempool, + this.sender, + safetyRules, + pacemaker, + this.vertexStore, + pendingVotes, + proposerElection, + this.selfKey, + validatorSet, + counters + ); + + SyncQueues syncQueues = new SyncQueues( + validatorSet.getValidators().stream() + .map(Validator::nodeKey) + .collect(ImmutableSet.toImmutableSet()), + counters + ); + + this.eventProcessor = new BFTEventPreprocessor( + this.selfKey.getPublicKey(), + reducer, + pacemaker, + this.vertexStore, + proposerElection, + syncQueues + ); } - ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); - TimeoutSender sender = (view, ms) -> scheduledTimeoutSender.scheduleTimeout(new LocalTimeout(nextEpoch, view), ms); - Pacemaker pacemaker = pacemakerFactory.create(sender); - SafetyRules safetyRules = new SafetyRules(this.selfKey, SafetyState.initialState(), this.hasher); - PendingVotes pendingVotes = new PendingVotes(this.hasher); - - QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(genesisVertex); - - this.vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC, syncedStateComputer); - - ProposalGenerator proposalGenerator = new MempoolProposalGenerator(this.vertexStore, this.mempool); - - BFTEventReducer reducer = new BFTEventReducer( - proposalGenerator, - this.mempool, - this.sender, - safetyRules, - pacemaker, - vertexStore, - pendingVotes, - proposerElection, - this.selfKey, - validatorSet, - counters - ); - - SyncQueues syncQueues = new SyncQueues( - validatorSet.getValidators().stream() - .map(Validator::nodeKey) - .collect(ImmutableSet.toImmutableSet()), - counters - ); - - this.eventProcessor = new BFTEventPreprocessor( - this.selfKey.getPublicKey(), - reducer, - pacemaker, - this.vertexStore, - proposerElection, - syncQueues - ); this.eventProcessor.start(); // Execute any queued up consensus events - for (ConsensusEvent consensusEvent : queuedEvents.getOrDefault(nextEpoch, Collections.emptyList())) { + final List queuedEventsForEpoch = queuedEvents.getOrDefault(nextEpoch, Collections.emptyList()); + for (ConsensusEvent consensusEvent : queuedEventsForEpoch) { this.processConsensusEventInternal(consensusEvent); } + + numQueuedConsensusEvents -= queuedEventsForEpoch.size(); + counters.set(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS, numQueuedConsensusEvents); queuedEvents.remove(nextEpoch); } @@ -243,8 +251,10 @@ public void processConsensusEvent(ConsensusEvent consensusEvent) { log.warn("Received higher epoch event {} from current epoch: {}", consensusEvent, this.currentEpoch()); // queue higher epoch events for later processing - // TODO: need to clear this by some rule (e.g. timeout or max size) + // TODO: need to clear this by some rule (e.g. timeout or max size) or else memory leak attack possible queuedEvents.computeIfAbsent(consensusEvent.getEpoch(), e -> new ArrayList<>()).add(consensusEvent); + numQueuedConsensusEvents++; + counters.set(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS, numQueuedConsensusEvents); // Send request for higher epoch proof epochsRPCSender.sendGetEpochRequest(consensusEvent.getAuthor(), this.currentEpoch() + 1); diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java b/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java index fb2f31002..01e24ba51 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java @@ -59,7 +59,7 @@ public Observable highQCs() { } @Override - public void syncedVertex(Vertex vertex) { + public void sendSyncedVertex(Vertex vertex) { localSyncsSubject.onNext(vertex.getId()); } @@ -74,7 +74,7 @@ public void sendCommittedStateSync(long stateVersion, Object opaque) { } @Override - public void committedVertex(Vertex vertex) { + public void sendCommittedVertex(Vertex vertex) { committedVertices.onNext(vertex); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java index a58cfd26f..b6db33aac 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java @@ -54,8 +54,9 @@ public interface GetVerticesRequest { } public interface VertexStoreEventSender { - void syncedVertex(Vertex vertex); - void committedVertex(Vertex vertex); + // TODO: combine Synced and Committed + void sendSyncedVertex(Vertex vertex); + void sendCommittedVertex(Vertex vertex); void highQC(QuorumCertificate qc); } @@ -158,6 +159,7 @@ private enum SyncStage { } private static class SyncState { + private final Hash localSyncId; private final QuorumCertificate qc; private final QuorumCertificate committedQC; private final VertexMetadata committedVertexMetadata; @@ -165,7 +167,9 @@ private static class SyncState { private SyncStage syncStage; private final LinkedList fetched = new LinkedList<>(); - SyncState(QuorumCertificate qc, QuorumCertificate committedQC, ECPublicKey author) { + SyncState(Hash localSyncId, QuorumCertificate qc, QuorumCertificate committedQC, ECPublicKey author) { + this.localSyncId = localSyncId; + if (committedQC.getView().equals(View.genesis())) { this.committedVertexMetadata = committedQC.getProposed(); } else { @@ -290,6 +294,8 @@ private void processVerticesResponseForQCSync(Hash syncTo, SyncState syncState, } public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { + // TODO: check response + log.info("SYNC_VERTICES: Received GetVerticesErrorResponse {} ", response); final Hash syncTo = (Hash) response.getOpaque(); @@ -298,16 +304,8 @@ public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { return; // sync requirements already satisfied by another sync } - switch (syncState.syncStage) { - case GET_COMMITTED_VERTICES: - // TODO: retry - break; - case GET_QC_VERTICES: - // TODO: retry - break; - default: - throw new IllegalStateException("Unknown sync stage: " + syncState.syncStage); - } + // error response indicates that the node has moved on from last sync so try and sync to a new sync + this.startSync(syncTo, response.getHighestQC(), response.getHighestCommittedQC(), syncState.author); } public void processGetVerticesResponse(GetVerticesResponse response) { @@ -334,19 +332,17 @@ public void processGetVerticesResponse(GetVerticesResponse response) { } private void doQCSync(SyncState syncState) { - final Hash vertexId = syncState.getQC().getProposed().getId(); syncState.setSyncStage(SyncStage.GET_QC_VERTICES); log.info("SYNC_VERTICES: QC: Sending initial GetVerticesRequest for sync={}", syncState); - syncVerticesRPCSender.sendGetVerticesRequest(vertexId, syncState.author, 1, vertexId); + syncVerticesRPCSender.sendGetVerticesRequest(syncState.qc.getProposed().getId(), syncState.author, 1, syncState.localSyncId); } private void doCommittedSync(SyncState syncState) { final Hash committedQCId = syncState.getCommittedQC().getProposed().getId(); - final Hash qcId = syncState.qc.getProposed().getId(); syncState.setSyncStage(SyncStage.GET_COMMITTED_VERTICES); log.info("SYNC_VERTICES: Committed: Sending initial GetVerticesRequest for sync={}", syncState); // Retrieve the 3 vertices preceding the committedQC so we can create a valid committed root - syncVerticesRPCSender.sendGetVerticesRequest(committedQCId, syncState.author, 3, qcId); + syncVerticesRPCSender.sendGetVerticesRequest(committedQCId, syncState.author, 3, syncState.localSyncId); } public void processLocalSync(Hash vertexId) { @@ -387,15 +383,19 @@ public boolean syncToQC(QuorumCertificate qc, QuorumCertificate committedQC, @Nu throw new IllegalStateException("Syncing required but author wasn't provided."); } - final SyncState syncState = new SyncState(qc, committedQC, author); + this.startSync(vertexId, qc, committedQC, author); + + return false; + } + + private void startSync(Hash vertexId, QuorumCertificate qc,QuorumCertificate committedQC, ECPublicKey author) { + final SyncState syncState = new SyncState(vertexId, qc, committedQC, author); syncing.put(vertexId, syncState); if (requiresCommittedStateSync(syncState)) { this.doCommittedSync(syncState); } else { this.doQCSync(syncState); } - - return false; } private boolean addQC(QuorumCertificate qc) { @@ -445,7 +445,7 @@ private VertexMetadata insertVertexInternal(Vertex vertex) throws VertexInsertio updateVertexStoreSize(); if (syncing.containsKey(vertexToUse.getId())) { - vertexStoreEventSender.syncedVertex(vertexToUse); + vertexStoreEventSender.sendSyncedVertex(vertexToUse); } return VertexMetadata.ofVertex(vertexToUse, isEndOfEpoch); @@ -485,7 +485,7 @@ public Optional commitVertex(VertexMetadata commitMetadata) { this.counters.increment(CounterType.CONSENSUS_PROCESSED); syncedStateComputer.execute(committedAtom); - this.vertexStoreEventSender.committedVertex(committed); + this.vertexStoreEventSender.sendCommittedVertex(committed); } rootId = commitMetadata.getId(); diff --git a/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java b/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java index 6a4f8c9bb..a21140270 100644 --- a/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java +++ b/radixdlt/src/main/java/com/radixdlt/counters/SystemCounters.java @@ -37,7 +37,8 @@ enum CounterType { CONSENSUS_VIEW("consensus.view"), CONSENSUS_PROCESSED("consensus.processed"), // TODO: Move some CONSENSUS epoch related counters into epoch - EPOCHS_EPOCH("epochs.epoch"), + EPOCH_MANAGER_EPOCH("epoch_manager.epoch"), + EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS("epoch_manager.queued_consensus_events"), LEDGER_STATE_VERSION("ledger.state_version"), MEMPOOL_COUNT("mempool.count"), MEMPOOL_MAXCOUNT("mempool.maxcount"), diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index 5055d63aa..3a33e825a 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -17,6 +17,7 @@ package com.radixdlt.consensus; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; @@ -36,6 +37,8 @@ import com.radixdlt.consensus.validators.Validator; import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.counters.SystemCounters; +import com.radixdlt.counters.SystemCounters.CounterType; +import com.radixdlt.counters.SystemCountersImpl; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; @@ -46,37 +49,41 @@ public class EpochManagerTest { private ECPublicKey publicKey; private EpochManager epochManager; - private BFTEventSender bftEventSender; private SyncEpochsRPCSender syncEpochsRPCSender; - private ScheduledTimeoutSender scheduledTimeoutSender; private VertexStore vertexStore; + private BFTFactory bftFactory; + private Pacemaker pacemaker; + private SystemCounters systemCounters; @Before public void setup() { - ECKeyPair keyPair = mock(ECKeyPair.class); - this.publicKey = mock(ECPublicKey.class); - when(keyPair.getPublicKey()).thenReturn(publicKey); + ECKeyPair keyPair = ECKeyPair.generateNew(); + this.publicKey = keyPair.getPublicKey(); - this.bftEventSender = mock(BFTEventSender.class); this.syncEpochsRPCSender = mock(SyncEpochsRPCSender.class); - this.scheduledTimeoutSender = mock(ScheduledTimeoutSender.class); this.vertexStore = mock(VertexStore.class); VertexStoreFactory vertexStoreFactory = mock(VertexStoreFactory.class); when(vertexStoreFactory.create(any(), any(), any())).thenReturn(this.vertexStore); + this.pacemaker = mock(Pacemaker.class); + + this.bftFactory = mock(BFTFactory.class); + + this.systemCounters = new SystemCountersImpl(); this.epochManager = new EpochManager( mock(SyncedRadixEngine.class), mock(Mempool.class), - bftEventSender, + mock(BFTEventSender.class), syncEpochsRPCSender, - scheduledTimeoutSender, - timeoutSender -> mock(Pacemaker.class), + mock(ScheduledTimeoutSender.class), + timeoutSender -> this.pacemaker, vertexStoreFactory, proposers -> mock(ProposerElection.class), mock(Hasher.class), + bftFactory, keyPair, - mock(SystemCounters.class) + systemCounters ); } @@ -90,11 +97,8 @@ public void when_next_epoch_does_not_contain_self__then_should_not_emit_any_cons when(epochChange.getAncestor()).thenReturn(vertexMetadata); epochManager.processEpochChange(epochChange); - verify(bftEventSender, never()).sendNewView(any(), any()); - verify(bftEventSender, never()).sendVote(any(), any()); - verify(bftEventSender, never()).broadcastProposal(any()); + verify(bftFactory, never()).create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); verify(syncEpochsRPCSender, never()).sendGetEpochRequest(any(), anyLong()); - verify(scheduledTimeoutSender, never()).scheduleTimeout(any(), anyLong()); } @Test @@ -109,10 +113,71 @@ public void when_no_epoch_change__then_processing_events_should_not_fail() { epochManager.processConsensusEvent(mock(Vote.class)); } + @Test + public void when_receive_next_epoch_events_and_then_epoch_change_and_part_of_validator_set__then_should_execute_queued_epoch_events() { + Validator authorValidator = mock(Validator.class); + ECPublicKey author = mock(ECPublicKey.class); + when(authorValidator.nodeKey()).thenReturn(author); + + when(pacemaker.getCurrentView()).thenReturn(View.genesis()); + when(vertexStore.getHighestQC()).thenReturn(mock(QuorumCertificate.class)); + when(vertexStore.syncToQC(any(), any(), any())).thenReturn(true); + + VertexMetadata ancestor = VertexMetadata.ofGenesisAncestor(); + + Proposal proposal = mock(Proposal.class); + Vertex vertex = mock(Vertex.class); + when(vertex.getView()).thenReturn(View.of(1)); + when(proposal.getEpoch()).thenReturn(ancestor.getEpoch() + 1); + when(proposal.getVertex()).thenReturn(vertex); + when(proposal.getAuthor()).thenReturn(author); + epochManager.processConsensusEvent(proposal); + + assertThat(systemCounters.get(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS)).isEqualTo(1); + + BFTEventProcessor eventProcessor = mock(BFTEventProcessor.class); + when(bftFactory.create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(eventProcessor); + + Validator validator = mock(Validator.class); + when(validator.nodeKey()).thenReturn(mock(ECPublicKey.class)); + ValidatorSet validatorSet = mock(ValidatorSet.class); + when(validatorSet.containsKey(any())).thenReturn(true); + when(validatorSet.getValidators()).thenReturn(ImmutableSet.of(validator, authorValidator)); + epochManager.processEpochChange(new EpochChange(ancestor, validatorSet)); + + assertThat(systemCounters.get(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS)).isEqualTo(0); + verify(eventProcessor, times(1)).processProposal(eq(proposal)); + } + + @Test + public void when_receive_next_epoch_events_and_then_epoch_change_and_not_part_of_validator_set__then_queued_events_should_be_cleared() { + Validator authorValidator = mock(Validator.class); + ECPublicKey author = mock(ECPublicKey.class); + when(authorValidator.nodeKey()).thenReturn(author); + + VertexMetadata ancestor = VertexMetadata.ofGenesisAncestor(); + + Proposal proposal = mock(Proposal.class); + when(proposal.getEpoch()).thenReturn(ancestor.getEpoch() + 1); + epochManager.processConsensusEvent(proposal); + assertThat(systemCounters.get(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS)).isEqualTo(1); + + Validator validator = mock(Validator.class); + when(validator.nodeKey()).thenReturn(mock(ECPublicKey.class)); + ValidatorSet validatorSet = mock(ValidatorSet.class); + when(validatorSet.containsKey(any())).thenReturn(false); + epochManager.processEpochChange(new EpochChange(ancestor, validatorSet)); + + assertThat(systemCounters.get(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS)).isEqualTo(0); + } + @Test public void when_next_epoch__then_get_vertices_rpc_should_be_forwarded_to_vertex_store() { when(vertexStore.getHighestQC()).thenReturn(mock(QuorumCertificate.class)); + BFTEventProcessor eventProcessor = mock(BFTEventProcessor.class); + when(bftFactory.create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(eventProcessor); + Validator validator = mock(Validator.class); when(validator.nodeKey()).thenReturn(mock(ECPublicKey.class)); ValidatorSet validatorSet = mock(ValidatorSet.class); diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/InternalMessagePasserTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/InternalMessagePasserTest.java index 4abd5b207..dfb30e61d 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/InternalMessagePasserTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/InternalMessagePasserTest.java @@ -32,7 +32,7 @@ public void when_send_sync_event__then_should_receive_it() { Hash hash = mock(Hash.class); Vertex vertex = mock(Vertex.class); when(vertex.getId()).thenReturn(hash); - internalMessagePasser.syncedVertex(vertex); + internalMessagePasser.sendSyncedVertex(vertex); testObserver.awaitCount(1); testObserver.assertValue(hash); testObserver.assertNotComplete(); @@ -43,7 +43,7 @@ public void when_send_committed_vertex_event__then_should_receive_it() { InternalMessagePasser internalMessagePasser = new InternalMessagePasser(); TestObserver testObserver = internalMessagePasser.committedVertices().test(); Vertex vertex = mock(Vertex.class); - internalMessagePasser.committedVertex(vertex); + internalMessagePasser.sendCommittedVertex(vertex); testObserver.awaitCount(1); testObserver.assertValue(vertex); testObserver.assertNotComplete(); diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java index 7537227c5..f6a420c2c 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java @@ -167,7 +167,7 @@ public void when_vertex_retriever_succeeds__then_vertex_is_inserted() { GetVerticesResponse getVerticesResponse = new GetVerticesResponse(vertex.getId(), Collections.singletonList(vertex), opaque.get()); vertexStore.processGetVerticesResponse(getVerticesResponse); - verify(vertexStoreEventSender, times(1)).syncedVertex(eq(vertex)); + verify(vertexStoreEventSender, times(1)).sendSyncedVertex(eq(vertex)); assertThat(vertexStore.getHighestQC()).isEqualTo(qc); } @@ -223,7 +223,7 @@ public void when_insert_vertex__then_it_should_not_be_committed_or_stored_in_eng Vertex nextVertex = Vertex.createVertex(rootQC, View.of(1), null); vertexStore.insertVertex(nextVertex); - verify(vertexStoreEventSender, never()).committedVertex(any()); + verify(vertexStoreEventSender, never()).sendCommittedVertex(any()); verify(syncedStateComputer, times(0)).execute(any()); // not stored } @@ -238,7 +238,7 @@ public void when_insert_and_commit_vertex__then_it_should_be_committed_and_store assertThat(vertexStore.commitVertex(vertexMetadata)).hasValue(nextVertex); verify(vertexStoreEventSender, times(1)) - .committedVertex(eq(nextVertex)); + .sendCommittedVertex(eq(nextVertex)); verify(syncedStateComputer, times(1)) .execute(argThat(a -> a.getClientAtom().equals(clientAtom))); // next atom stored } @@ -261,7 +261,7 @@ public void when_insert_and_commit_vertex__then_committed_vertex_should_emit_and VertexMetadata vertexMetadata = VertexMetadata.ofVertex(vertex, false); assertThat(vertexStore.commitVertex(vertexMetadata)).hasValue(vertex); - verify(vertexStoreEventSender, times(1)).committedVertex(eq(vertex)); + verify(vertexStoreEventSender, times(1)).sendCommittedVertex(eq(vertex)); assertThat(vertexStore.getSize()).isEqualTo(1); } @@ -279,8 +279,8 @@ public void when_insert_and_commit_vertex_2x__then_committed_vertex_should_emit_ VertexMetadata vertexMetadata2 = VertexMetadata.ofVertex(nextVertex2, false); vertexStore.commitVertex(vertexMetadata2); - verify(vertexStoreEventSender, times(1)).committedVertex(eq(nextVertex1)); - verify(vertexStoreEventSender, times(1)).committedVertex(eq(nextVertex2)); + verify(vertexStoreEventSender, times(1)).sendCommittedVertex(eq(nextVertex1)); + verify(vertexStoreEventSender, times(1)).sendCommittedVertex(eq(nextVertex2)); assertThat(vertexStore.getSize()).isEqualTo(1); assertThat(vertexStore.getSize()).isEqualTo(1); } @@ -298,8 +298,8 @@ public void when_insert_two_and_commit_vertex__then_two_committed_vertices_shoul VertexMetadata vertexMetadata2 = VertexMetadata.ofVertex(nextVertex2, false); vertexStore.commitVertex(vertexMetadata2); - verify(vertexStoreEventSender, times(1)).committedVertex(eq(nextVertex1)); - verify(vertexStoreEventSender, times(1)).committedVertex(eq(nextVertex2)); + verify(vertexStoreEventSender, times(1)).sendCommittedVertex(eq(nextVertex1)); + verify(vertexStoreEventSender, times(1)).sendCommittedVertex(eq(nextVertex2)); assertThat(vertexStore.getSize()).isEqualTo(1); } @@ -311,7 +311,7 @@ public void when_sync_to_qc_which_doesnt_exist_and_vertex_is_inserted_later__the assertThat(vertexStore.syncToQC(qc, vertexStore.getHighestCommittedQC(), mock(ECPublicKey.class))).isFalse(); vertexStore.insertVertex(vertex); - verify(vertexStoreEventSender, times(1)).syncedVertex(eq(vertex)); + verify(vertexStoreEventSender, times(1)).sendSyncedVertex(eq(vertex)); } @Test @@ -450,7 +450,7 @@ public void when_request_for_qc_sync_and_receive_response__then_should_update() assertThat(vertexStore.getHighestQC()).isEqualTo(vertex5.getQC()); - verify(vertexStoreEventSender, times(1)).syncedVertex(eq(vertex4)); + verify(vertexStoreEventSender, times(1)).sendSyncedVertex(eq(vertex4)); } @Test From 75e9a9d986982e86f67ede9864dfb07236216a95 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 17:40:55 +0800 Subject: [PATCH 08/20] Separate out BFT only and BFT+StateComputer FProposalDropper test --- .../FProposalDropperResponsiveTest.java | 2 +- ...oposalDropperRandomSyncResponsiveTest.java | 138 ++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java index abe730747..6431318bf 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java @@ -39,7 +39,7 @@ private void runFProposalDropperResponsiveTest(int numNodes, Function> proposalsToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, random::nextBoolean); + final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, () -> true); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java new file mode 100644 index 000000000..5c9b0a380 --- /dev/null +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java @@ -0,0 +1,138 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.deterministic.tests.syncedstatecomputer; + +import com.google.common.collect.ImmutableSet; +import com.radixdlt.consensus.Proposal; +import com.radixdlt.consensus.View; +import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Test; + +public class FProposalDropperRandomSyncResponsiveTest { + + private final Random random = new Random(123456789); + + private void runFProposalDropperResponsiveTest(int numNodes, Function> nodesToDropFunction) { + final Map> proposalsToDrop = new HashMap<>(); + final Map proposalCount = new HashMap<>(); + + final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, random::nextBoolean); + test.start(); + for (int step = 0; step < 100000; step++) { + test.processNextMsg(random, (receiverId, msg) -> { + if (msg instanceof Proposal) { + final Proposal proposal = (Proposal) msg; + final View view = proposal.getVertex().getView(); + final Set nodesToDrop = proposalsToDrop.computeIfAbsent(view, nodesToDropFunction); + + if (proposalCount.merge(view, 1, Integer::sum).equals(numNodes)) { + proposalsToDrop.remove(view); + proposalCount.remove(view); + } + + return !nodesToDrop.contains(receiverId); + } + + return true; + }); + } + } + + private void runRandomMaliciousNodesTest(int numNodes) { + this.runFProposalDropperResponsiveTest( + numNodes, + v -> { + List nodes = Stream.iterate(0, a -> a + 1).limit(numNodes).collect(Collectors.toList()); + return Stream.iterate(0, a -> a + 1) + .limit((numNodes - 1) / 3) + .map(i -> { + int index = random.nextInt(nodes.size()); + return nodes.remove(index); + }) + .collect(Collectors.toSet()); + } + ); + } + + + @Test + public void when_run_4_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(4); + } + + @Test + public void when_run_5_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(5); + } + + @Test + public void when_run_10_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(10); + } + + @Test + public void when_run_50_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(50); + } + + @Test + public void when_run_100_correct_nodes_with_random_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runRandomMaliciousNodesTest(100); + } + + private void runStaticMaliciousNodesTest(int numNodes) { + Set nodes = Stream.iterate(0, a -> a + 1).limit((numNodes - 1) / 3).collect(ImmutableSet.toImmutableSet()); + this.runFProposalDropperResponsiveTest( + numNodes, + v -> nodes + ); + } + + @Test + public void when_run_4_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(4); + } + + @Test + public void when_run_5_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(5); + } + + @Test + public void when_run_10_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(10); + } + + @Test + public void when_run_50_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(50); + } + + @Test + public void when_run_100_correct_nodes_with_single_node_proposal_dropper_and_timeouts_disabled__then_bft_should_be_responsive() { + this.runStaticMaliciousNodesTest(100); + } +} From 2a77ea82071ba7fa5acec88a2892fc9636ec7171 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 18:36:33 +0800 Subject: [PATCH 09/20] Refactor BFT Factory --- .../deterministic/ControlledBFTNode.java | 33 ++++++++++-- .../simulation/network/SimulatedNetwork.java | 34 ++++++++++-- .../java/com/radixdlt/CerberusModule.java | 52 ++++++++++++++++--- .../com/radixdlt/consensus/BFTFactory.java | 21 ++------ .../com/radixdlt/consensus/EpochManager.java | 41 +++------------ .../com/radixdlt/consensus/VertexStore.java | 2 +- .../radixdlt/consensus/EpochManagerTest.java | 12 ++--- 7 files changed, 119 insertions(+), 76 deletions(-) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java index 980ee38fe..0f462ffa9 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java @@ -20,6 +20,7 @@ import static org.mockito.Mockito.mock; import com.radixdlt.consensus.BFTEventReducer; +import com.radixdlt.consensus.BFTFactory; import com.radixdlt.consensus.CommittedStateSync; import com.radixdlt.consensus.ConsensusEvent; import com.radixdlt.consensus.DefaultHasher; @@ -27,6 +28,7 @@ import com.radixdlt.consensus.EmptySyncVerticesRPCSender; import com.radixdlt.consensus.EpochChange; import com.radixdlt.consensus.EpochManager; +import com.radixdlt.consensus.PendingVotes; import com.radixdlt.consensus.bft.GetVerticesErrorResponse; import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.LocalTimeout; @@ -41,7 +43,11 @@ import com.radixdlt.consensus.VertexStoreFactory; import com.radixdlt.consensus.deterministic.ControlledBFTNetwork.ControlledSender; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker; +import com.radixdlt.consensus.liveness.MempoolProposalGenerator; +import com.radixdlt.consensus.liveness.ProposalGenerator; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; +import com.radixdlt.consensus.safety.SafetyRules; +import com.radixdlt.consensus.safety.SafetyState; import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; @@ -103,19 +109,36 @@ public void execute(CommittedAtom instruction) { Hasher hasher = new DefaultHasher(); VertexStoreFactory vertexStoreFactory = (vertex, qc, syncedStateComputer) -> new VertexStore(vertex, qc, syncedStateComputer, syncVerticesRPCSender, sender, systemCounters); + BFTFactory bftFactory = + (pacemaker, vertexStore, proposerElection, validatorSet) -> { + final ProposalGenerator proposalGenerator = new MempoolProposalGenerator(vertexStore, mempool); + final SafetyRules safetyRules = new SafetyRules(key, SafetyState.initialState(), hasher); + final PendingVotes pendingVotes = new PendingVotes(hasher); + + return new BFTEventReducer( + proposalGenerator, + mempool, + controlledSender, + safetyRules, + pacemaker, + vertexStore, + pendingVotes, + proposerElection, + key, + validatorSet, + systemCounters + ); + }; this.epochManager = new EpochManager( stateComputer, - mempool, - sender, EmptySyncEpochsRPCSender.INSTANCE, mock(ScheduledTimeoutSender.class), timeoutSender -> new FixedTimeoutPacemaker(1, timeoutSender), vertexStoreFactory, proposerElectionFactory, - hasher, - BFTEventReducer::new, - key, + bftFactory, + key.getPublicKey(), systemCounters ); } diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java index 93b51d8f2..64e127f2c 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap; import com.radixdlt.consensus.BFTEventReducer; +import com.radixdlt.consensus.BFTFactory; import com.radixdlt.consensus.ConsensusRunner; import com.radixdlt.consensus.ConsensusRunner.Event; import com.radixdlt.consensus.ConsensusRunner.EventType; @@ -29,14 +30,19 @@ import com.radixdlt.consensus.EpochChangeRx; import com.radixdlt.consensus.Hasher; import com.radixdlt.consensus.InternalMessagePasser; +import com.radixdlt.consensus.PendingVotes; import com.radixdlt.consensus.SyncedStateComputer; import com.radixdlt.consensus.VertexStore; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.VertexStoreEventsRx; import com.radixdlt.consensus.VertexStoreFactory; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker; +import com.radixdlt.consensus.liveness.MempoolProposalGenerator; +import com.radixdlt.consensus.liveness.ProposalGenerator; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; import com.radixdlt.consensus.liveness.WeightedRotatingLeaders; +import com.radixdlt.consensus.safety.SafetyRules; +import com.radixdlt.consensus.safety.SafetyState; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCountersImpl; import com.radixdlt.crypto.ECKeyPair; @@ -119,18 +125,36 @@ private ConsensusRunner createBFTInstance(ECKeyPair key) { }; final SimulatedStateComputer stateComputer = stateComputerSupplier.get(); + BFTFactory bftFactory = + (pacemaker, vertexStore, proposerElection, validatorSet) -> { + final ProposalGenerator proposalGenerator = new MempoolProposalGenerator(vertexStore, mempool); + final SafetyRules safetyRules = new SafetyRules(key, SafetyState.initialState(), hasher); + final PendingVotes pendingVotes = new PendingVotes(hasher); + + return new BFTEventReducer( + proposalGenerator, + mempool, + underlyingNetwork.getNetworkSender(key.getPublicKey()), + safetyRules, + pacemaker, + vertexStore, + pendingVotes, + proposerElection, + key, + validatorSet, + counters.get(key) + ); + }; + final EpochManager epochManager = new EpochManager( stateComputer, - mempool, - underlyingNetwork.getNetworkSender(key.getPublicKey()), EmptySyncEpochsRPCSender.INSTANCE, timeoutSender, timeoutSender1 -> new FixedTimeoutPacemaker(this.pacemakerTimeout, timeoutSender1), vertexStoreFactory, proposers -> new WeightedRotatingLeaders(proposers, Comparator.comparing(v -> v.nodeKey().euid()), 5), - hasher, - BFTEventReducer::new, - key, + bftFactory, + key.getPublicKey(), counters.get(key) ); diff --git a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java index 229301c63..f26d1f156 100644 --- a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java +++ b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java @@ -25,6 +25,7 @@ import com.radixdlt.consensus.BFTEventReducer; import com.radixdlt.consensus.BFTEventSender; import com.radixdlt.consensus.AddressBookValidatorSetProvider; +import com.radixdlt.consensus.BFTFactory; import com.radixdlt.consensus.CommittedStateSyncRx; import com.radixdlt.consensus.ConsensusRunner; import com.radixdlt.consensus.DefaultHasher; @@ -32,6 +33,7 @@ import com.radixdlt.consensus.EpochChangeRx; import com.radixdlt.consensus.EpochManager; import com.radixdlt.consensus.EventCoordinatorNetworkRx; +import com.radixdlt.consensus.PendingVotes; import com.radixdlt.consensus.SyncEpochsRPCSender; import com.radixdlt.consensus.SyncedStateComputer; import com.radixdlt.consensus.VertexStoreEventsRx; @@ -45,10 +47,14 @@ import com.radixdlt.consensus.VertexStoreFactory; import com.radixdlt.consensus.View; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker; +import com.radixdlt.consensus.liveness.MempoolProposalGenerator; import com.radixdlt.consensus.liveness.PacemakerFactory; import com.radixdlt.consensus.liveness.PacemakerRx; +import com.radixdlt.consensus.liveness.ProposalGenerator; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; import com.radixdlt.consensus.liveness.WeightedRotatingLeaders; +import com.radixdlt.consensus.safety.SafetyRules; +import com.radixdlt.consensus.safety.SafetyState; import com.radixdlt.consensus.sync.StateSyncNetwork; import com.radixdlt.consensus.sync.SyncedRadixEngine; import com.radixdlt.consensus.sync.SyncedRadixEngine.CommittedStateSyncSender; @@ -111,33 +117,63 @@ private SyncEpochsRPCSender syncEpochsRPCSender() { return EmptySyncEpochsRPCSender.INSTANCE; } + @Provides + @Singleton + private BFTFactory bftFactory( + BFTEventSender bftEventSender, + Mempool mempool, + @Named("self") ECKeyPair selfKey, + Hasher hasher, + SystemCounters counters + ) { + return ( + pacemaker, + vertexStore, + proposerElection, + validatorSet + ) -> { + final ProposalGenerator proposalGenerator = new MempoolProposalGenerator(vertexStore, mempool); + final SafetyRules safetyRules = new SafetyRules(selfKey, SafetyState.initialState(), hasher); + final PendingVotes pendingVotes = new PendingVotes(hasher); + + return new BFTEventReducer( + proposalGenerator, + mempool, + bftEventSender, + safetyRules, + pacemaker, + vertexStore, + pendingVotes, + proposerElection, + selfKey, + validatorSet, + counters + ); + }; + } + @Provides @Singleton private EpochManager epochManager( SyncedRadixEngine syncedRadixEngine, - Mempool mempool, - BFTEventSender sender, + BFTFactory bftFactory, SyncEpochsRPCSender syncEpochsRPCSender, ScheduledTimeoutSender scheduledTimeoutSender, PacemakerFactory pacemakerFactory, VertexStoreFactory vertexStoreFactory, ProposerElectionFactory proposerElectionFactory, - Hasher hasher, @Named("self") ECKeyPair selfKey, SystemCounters counters ) { return new EpochManager( syncedRadixEngine, - mempool, - sender, syncEpochsRPCSender, scheduledTimeoutSender, pacemakerFactory, vertexStoreFactory, proposerElectionFactory, - hasher, - BFTEventReducer::new, - selfKey, + bftFactory, + selfKey.getPublicKey(), counters ); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java b/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java index 5260aadf8..d2bab1e5c 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java @@ -18,33 +18,22 @@ package com.radixdlt.consensus; import com.radixdlt.consensus.liveness.Pacemaker; -import com.radixdlt.consensus.liveness.ProposalGenerator; import com.radixdlt.consensus.liveness.ProposerElection; -import com.radixdlt.consensus.safety.SafetyRules; import com.radixdlt.consensus.validators.ValidatorSet; -import com.radixdlt.counters.SystemCounters; -import com.radixdlt.crypto.ECKeyPair; -import com.radixdlt.mempool.Mempool; +/** + * Creates a new bft processor + */ public interface BFTFactory { - /** - * Create a new BFT processor - * TODO: Cleanup + * Create a new clean BFT processor * * @return a new bft processor */ BFTEventProcessor create( - ProposalGenerator proposalGenerator, - Mempool mempool, - BFTEventSender sender, - SafetyRules safetyRules, Pacemaker pacemaker, VertexStore vertexStore, - PendingVotes pendingVotes, ProposerElection proposerElection, - ECKeyPair selfKey, - ValidatorSet validatorSet, - SystemCounters counters + ValidatorSet validatorSet ); } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index c2ad3f09e..de6cdf651 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -24,21 +24,16 @@ import com.radixdlt.consensus.epoch.GetEpochRequest; import com.radixdlt.consensus.epoch.GetEpochResponse; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker.TimeoutSender; -import com.radixdlt.consensus.liveness.MempoolProposalGenerator; import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.PacemakerFactory; -import com.radixdlt.consensus.liveness.ProposalGenerator; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; -import com.radixdlt.consensus.safety.SafetyRules; -import com.radixdlt.consensus.safety.SafetyState; import com.radixdlt.consensus.validators.Validator; import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; -import com.radixdlt.crypto.ECKeyPair; +import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; -import com.radixdlt.mempool.Mempool; import com.radixdlt.middleware2.CommittedAtom; import java.util.ArrayList; import java.util.Collections; @@ -58,15 +53,12 @@ public class EpochManager { private static final Logger log = LogManager.getLogger("EM"); private static final BFTEventProcessor EMPTY_PROCESSOR = new EmptyBFTEventProcessor(); - private final Mempool mempool; private final SyncEpochsRPCSender epochsRPCSender; - private final BFTEventSender sender; private final PacemakerFactory pacemakerFactory; private final VertexStoreFactory vertexStoreFactory; private final ProposerElectionFactory proposerElectionFactory; - private final ECKeyPair selfKey; + private final ECPublicKey selfPublicKey; private final SystemCounters counters; - private final Hasher hasher; private final ScheduledTimeoutSender scheduledTimeoutSender; private final SyncedStateComputer syncedStateComputer; private final Map> queuedEvents; @@ -79,30 +71,24 @@ public class EpochManager { public EpochManager( SyncedStateComputer syncedStateComputer, - Mempool mempool, - BFTEventSender sender, SyncEpochsRPCSender epochsRPCSender, ScheduledTimeoutSender scheduledTimeoutSender, PacemakerFactory pacemakerFactory, VertexStoreFactory vertexStoreFactory, ProposerElectionFactory proposerElectionFactory, - Hasher hasher, BFTFactory bftFactory, - ECKeyPair selfKey, + ECPublicKey selfPublicKey, SystemCounters counters ) { this.syncedStateComputer = Objects.requireNonNull(syncedStateComputer); - this.mempool = Objects.requireNonNull(mempool); - this.sender = Objects.requireNonNull(sender); this.epochsRPCSender = Objects.requireNonNull(epochsRPCSender); this.scheduledTimeoutSender = Objects.requireNonNull(scheduledTimeoutSender); this.pacemakerFactory = Objects.requireNonNull(pacemakerFactory); this.vertexStoreFactory = Objects.requireNonNull(vertexStoreFactory); this.proposerElectionFactory = Objects.requireNonNull(proposerElectionFactory); this.bftFactory = bftFactory; - this.selfKey = Objects.requireNonNull(selfKey); + this.selfPublicKey = Objects.requireNonNull(selfPublicKey); this.counters = Objects.requireNonNull(counters); - this.hasher = Objects.requireNonNull(hasher); this.queuedEvents = new HashMap<>(); } @@ -126,7 +112,7 @@ public void processEpochChange(EpochChange epochChange) { this.currentAncestor = ancestorMetadata; this.counters.set(CounterType.EPOCH_MANAGER_EPOCH, nextEpoch); - if (!validatorSet.containsKey(selfKey.getPublicKey())) { + if (!validatorSet.containsKey(selfPublicKey)) { log.info("NEXT_EPOCH: Not a validator"); this.eventProcessor = EMPTY_PROCESSOR; this.vertexStore = null; @@ -134,38 +120,27 @@ public void processEpochChange(EpochChange epochChange) { ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); TimeoutSender sender = (view, ms) -> scheduledTimeoutSender.scheduleTimeout(new LocalTimeout(nextEpoch, view), ms); Pacemaker pacemaker = pacemakerFactory.create(sender); - SafetyRules safetyRules = new SafetyRules(this.selfKey, SafetyState.initialState(), this.hasher); - PendingVotes pendingVotes = new PendingVotes(this.hasher); QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(genesisVertex); this.vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC, syncedStateComputer); - ProposalGenerator proposalGenerator = new MempoolProposalGenerator(this.vertexStore, this.mempool); - BFTEventProcessor reducer = bftFactory.create( - proposalGenerator, - this.mempool, - this.sender, - safetyRules, pacemaker, this.vertexStore, - pendingVotes, proposerElection, - this.selfKey, - validatorSet, - counters + validatorSet ); SyncQueues syncQueues = new SyncQueues( validatorSet.getValidators().stream() .map(Validator::nodeKey) .collect(ImmutableSet.toImmutableSet()), - counters + this.counters ); this.eventProcessor = new BFTEventPreprocessor( - this.selfKey.getPublicKey(), + this.selfPublicKey, reducer, pacemaker, this.vertexStore, diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java index b6db33aac..ca80e38d5 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java @@ -388,7 +388,7 @@ public boolean syncToQC(QuorumCertificate qc, QuorumCertificate committedQC, @Nu return false; } - private void startSync(Hash vertexId, QuorumCertificate qc,QuorumCertificate committedQC, ECPublicKey author) { + private void startSync(Hash vertexId, QuorumCertificate qc, QuorumCertificate committedQC, ECPublicKey author) { final SyncState syncState = new SyncState(vertexId, qc, committedQC, author); syncing.put(vertexId, syncState); if (requiresCommittedStateSync(syncState)) { diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index 3a33e825a..67a9d8712 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -42,7 +42,6 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; -import com.radixdlt.mempool.Mempool; import org.junit.Before; import org.junit.Test; @@ -73,16 +72,13 @@ public void setup() { this.epochManager = new EpochManager( mock(SyncedRadixEngine.class), - mock(Mempool.class), - mock(BFTEventSender.class), syncEpochsRPCSender, mock(ScheduledTimeoutSender.class), timeoutSender -> this.pacemaker, vertexStoreFactory, proposers -> mock(ProposerElection.class), - mock(Hasher.class), bftFactory, - keyPair, + this.publicKey, systemCounters ); } @@ -97,7 +93,7 @@ public void when_next_epoch_does_not_contain_self__then_should_not_emit_any_cons when(epochChange.getAncestor()).thenReturn(vertexMetadata); epochManager.processEpochChange(epochChange); - verify(bftFactory, never()).create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); + verify(bftFactory, never()).create(any(), any(), any(), any()); verify(syncEpochsRPCSender, never()).sendGetEpochRequest(any(), anyLong()); } @@ -136,7 +132,7 @@ public void when_receive_next_epoch_events_and_then_epoch_change_and_part_of_val assertThat(systemCounters.get(CounterType.EPOCH_MANAGER_QUEUED_CONSENSUS_EVENTS)).isEqualTo(1); BFTEventProcessor eventProcessor = mock(BFTEventProcessor.class); - when(bftFactory.create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(eventProcessor); + when(bftFactory.create(any(), any(), any(), any())).thenReturn(eventProcessor); Validator validator = mock(Validator.class); when(validator.nodeKey()).thenReturn(mock(ECPublicKey.class)); @@ -176,7 +172,7 @@ public void when_next_epoch__then_get_vertices_rpc_should_be_forwarded_to_vertex when(vertexStore.getHighestQC()).thenReturn(mock(QuorumCertificate.class)); BFTEventProcessor eventProcessor = mock(BFTEventProcessor.class); - when(bftFactory.create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any())).thenReturn(eventProcessor); + when(bftFactory.create(any(), any(), any(), any())).thenReturn(eventProcessor); Validator validator = mock(Validator.class); when(validator.nodeKey()).thenReturn(mock(ECPublicKey.class)); From 4d9b933287c3118913847f1013b5d4e2d76711f8 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 19:01:44 +0800 Subject: [PATCH 10/20] Refactor VertexStore event interfaces --- .../deterministic/ControlledBFTNetwork.java | 4 +- .../deterministic/ControlledBFTNode.java | 4 +- .../simulation/network/SimulatedNetwork.java | 2 +- .../java/com/radixdlt/CerberusModule.java | 4 +- .../consensus/BFTEventPreprocessor.java | 1 + .../radixdlt/consensus/BFTEventReducer.java | 1 + .../com/radixdlt/consensus/BFTFactory.java | 1 + .../consensus/EmptyBFTEventProcessor.java | 4 +- .../consensus/EmptySyncVerticesRPCSender.java | 2 +- .../EmptyVertexStoreEventProcessor.java | 46 ++++++++++ .../com/radixdlt/consensus/EpochManager.java | 90 ++++++++----------- .../consensus/InternalMessagePasser.java | 2 +- .../consensus/MissingParentException.java | 14 --- .../radixdlt/consensus/SyncVerticesRPCRx.java | 2 +- .../consensus/SyncVerticesRPCSender.java | 2 +- .../consensus/VertexStoreEventProcessor.java | 52 +++++++++++ .../consensus/VertexStoreFactory.java | 1 + .../consensus/bft/MissingParentException.java | 31 +++++++ .../consensus/{ => bft}/VertexStore.java | 41 +++++---- .../liveness/MempoolProposalGenerator.java | 2 +- .../MessageCentralSyncVerticesRPCNetwork.java | 2 +- .../network/TestEventCoordinatorNetwork.java | 2 +- .../consensus/BFTEventPreprocessorTest.java | 1 + .../consensus/BFTEventReducerTest.java | 1 + .../consensus/ConsensusRunnerTest.java | 2 +- .../radixdlt/consensus/EpochManagerTest.java | 3 +- .../consensus/{ => bft}/VertexStoreTest.java | 18 ++-- .../MempoolProposalGeneratorTest.java | 2 +- ...sageCentralSyncVerticesRPCNetworkTest.java | 2 +- .../TestBFTEventProcessorNetworkTest.java | 2 +- 30 files changed, 236 insertions(+), 105 deletions(-) create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java delete mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/MissingParentException.java create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreEventProcessor.java create mode 100644 radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java rename radixdlt/src/main/java/com/radixdlt/consensus/{ => bft}/VertexStore.java (93%) rename radixdlt/src/test/java/com/radixdlt/consensus/{ => bft}/VertexStoreTest.java (96%) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java index 93feaf4aa..8b352076a 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java @@ -30,8 +30,8 @@ import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.Vertex; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; -import com.radixdlt.consensus.VertexStore.VertexStoreEventSender; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.VertexStoreEventSender; import com.radixdlt.consensus.Vote; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java index 0f462ffa9..e89f26388 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java @@ -34,11 +34,11 @@ import com.radixdlt.consensus.LocalTimeout; import com.radixdlt.consensus.ProposerElectionFactory; import com.radixdlt.consensus.VertexMetadata; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.Hasher; import com.radixdlt.consensus.SyncedStateComputer; import com.radixdlt.consensus.Vertex; -import com.radixdlt.consensus.VertexStore; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.VertexStoreFactory; import com.radixdlt.consensus.deterministic.ControlledBFTNetwork.ControlledSender; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java index 64e127f2c..558131d70 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SimulatedNetwork.java @@ -32,7 +32,7 @@ import com.radixdlt.consensus.InternalMessagePasser; import com.radixdlt.consensus.PendingVotes; import com.radixdlt.consensus.SyncedStateComputer; -import com.radixdlt.consensus.VertexStore; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.VertexStoreEventsRx; import com.radixdlt.consensus.VertexStoreFactory; diff --git a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java index f26d1f156..abce95d14 100644 --- a/radixdlt/src/main/java/com/radixdlt/CerberusModule.java +++ b/radixdlt/src/main/java/com/radixdlt/CerberusModule.java @@ -41,8 +41,8 @@ import com.radixdlt.consensus.ProposerElectionFactory; import com.radixdlt.consensus.Hasher; import com.radixdlt.consensus.SyncVerticesRPCRx; -import com.radixdlt.consensus.VertexStore; -import com.radixdlt.consensus.VertexStore.VertexStoreEventSender; +import com.radixdlt.consensus.bft.VertexStore; +import com.radixdlt.consensus.bft.VertexStore.VertexStoreEventSender; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.VertexStoreFactory; import com.radixdlt.consensus.View; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventPreprocessor.java b/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventPreprocessor.java index 601ec11fd..8bfb0cd82 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventPreprocessor.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventPreprocessor.java @@ -18,6 +18,7 @@ package com.radixdlt.consensus; import com.radixdlt.consensus.SyncQueues.SyncQueue; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.liveness.PacemakerState; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.crypto.ECPublicKey; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventReducer.java b/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventReducer.java index 816689945..b82bdae80 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventReducer.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/BFTEventReducer.java @@ -19,6 +19,7 @@ import com.google.inject.Inject; import com.google.inject.name.Named; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.liveness.ProposalGenerator; import com.radixdlt.identifiers.EUID; import com.radixdlt.consensus.liveness.Pacemaker; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java b/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java index d2bab1e5c..c536905fe 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/BFTFactory.java @@ -17,6 +17,7 @@ package com.radixdlt.consensus; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.consensus.validators.ValidatorSet; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EmptyBFTEventProcessor.java b/radixdlt/src/main/java/com/radixdlt/consensus/EmptyBFTEventProcessor.java index 47b161e21..fcf32f667 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EmptyBFTEventProcessor.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EmptyBFTEventProcessor.java @@ -22,7 +22,9 @@ /** * An empty BFT event processor */ -public class EmptyBFTEventProcessor implements BFTEventProcessor { +public enum EmptyBFTEventProcessor implements BFTEventProcessor { + INSTANCE; + @Override public void processVote(Vote vote) { // No-op diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java index f7274c690..6ee87d753 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncVerticesRPCSender.java @@ -18,7 +18,7 @@ package com.radixdlt.consensus; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java b/radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java new file mode 100644 index 000000000..862b6e080 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java @@ -0,0 +1,46 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus; + +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; + +public enum EmptyVertexStoreEventProcessor implements VertexStoreEventProcessor { + INSTANCE; + + @Override + public void processGetVerticesRequest(GetVerticesRequest request) { + // No-op + } + + @Override + public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { + // No-op + } + + @Override + public void processGetVerticesResponse(GetVerticesResponse response) { + // No-op + } + + @Override + public void processCommittedStateSync(CommittedStateSync committedStateSync) { + // No-op + } +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java index de6cdf651..75fe24de8 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EpochManager.java @@ -18,7 +18,8 @@ package com.radixdlt.consensus; import com.google.common.collect.ImmutableSet; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.bft.GetVerticesErrorResponse; import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.epoch.GetEpochRequest; @@ -49,9 +50,8 @@ * Manages Epochs and the BFT instance (which is mostly epoch agnostic) associated with each epoch */ @NotThreadSafe -public class EpochManager { +public final class EpochManager { private static final Logger log = LogManager.getLogger("EM"); - private static final BFTEventProcessor EMPTY_PROCESSOR = new EmptyBFTEventProcessor(); private final SyncEpochsRPCSender epochsRPCSender; private final PacemakerFactory pacemakerFactory; @@ -65,8 +65,8 @@ public class EpochManager { private final BFTFactory bftFactory; private VertexMetadata currentAncestor; - private VertexStore vertexStore; - private BFTEventProcessor eventProcessor = EMPTY_PROCESSOR; + private VertexStoreEventProcessor vertexStoreEventProcessor = EmptyVertexStoreEventProcessor.INSTANCE; + private BFTEventProcessor bftEventProcessor = EmptyBFTEventProcessor.INSTANCE; private int numQueuedConsensusEvents = 0; public EpochManager( @@ -98,7 +98,7 @@ private long currentEpoch() { public void processEpochChange(EpochChange epochChange) { ValidatorSet validatorSet = epochChange.getValidatorSet(); - log.info("NEXT_EPOCH: {} {}", epochChange); + log.info("NEXT_EPOCH: {}", epochChange); VertexMetadata ancestorMetadata = epochChange.getAncestor(); Vertex genesisVertex = Vertex.createGenesis(ancestorMetadata); @@ -112,26 +112,25 @@ public void processEpochChange(EpochChange epochChange) { this.currentAncestor = ancestorMetadata; this.counters.set(CounterType.EPOCH_MANAGER_EPOCH, nextEpoch); + final BFTEventProcessor bftEventProcessor; + final VertexStoreEventProcessor vertexStoreEventProcessor; + if (!validatorSet.containsKey(selfPublicKey)) { log.info("NEXT_EPOCH: Not a validator"); - this.eventProcessor = EMPTY_PROCESSOR; - this.vertexStore = null; + bftEventProcessor = EmptyBFTEventProcessor.INSTANCE; + vertexStoreEventProcessor = EmptyVertexStoreEventProcessor.INSTANCE; } else { ProposerElection proposerElection = proposerElectionFactory.create(validatorSet); TimeoutSender sender = (view, ms) -> scheduledTimeoutSender.scheduleTimeout(new LocalTimeout(nextEpoch, view), ms); Pacemaker pacemaker = pacemakerFactory.create(sender); - QuorumCertificate genesisQC = QuorumCertificate.ofGenesis(genesisVertex); - - this.vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC, syncedStateComputer); - + VertexStore vertexStore = vertexStoreFactory.create(genesisVertex, genesisQC, syncedStateComputer); BFTEventProcessor reducer = bftFactory.create( pacemaker, - this.vertexStore, + vertexStore, proposerElection, validatorSet ); - SyncQueues syncQueues = new SyncQueues( validatorSet.getValidators().stream() .map(Validator::nodeKey) @@ -139,17 +138,22 @@ public void processEpochChange(EpochChange epochChange) { this.counters ); - this.eventProcessor = new BFTEventPreprocessor( + vertexStoreEventProcessor = vertexStore; + bftEventProcessor = new BFTEventPreprocessor( this.selfPublicKey, reducer, pacemaker, - this.vertexStore, + vertexStore, proposerElection, syncQueues ); } - this.eventProcessor.start(); + // Update processors + this.bftEventProcessor = bftEventProcessor; + this.vertexStoreEventProcessor = vertexStoreEventProcessor; + + this.bftEventProcessor.start(); // Execute any queued up consensus events final List queuedEventsForEpoch = queuedEvents.getOrDefault(nextEpoch, Collections.emptyList()); @@ -162,30 +166,6 @@ public void processEpochChange(EpochChange epochChange) { queuedEvents.remove(nextEpoch); } - public void processGetVerticesRequest(GetVerticesRequest request) { - if (this.vertexStore == null) { - return; - } - - vertexStore.processGetVerticesRequest(request); - } - - public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { - if (this.vertexStore == null) { - return; - } - - vertexStore.processGetVerticesErrorResponse(response); - } - - public void processGetVerticesResponse(GetVerticesResponse response) { - if (this.vertexStore == null) { - return; - } - - vertexStore.processGetVerticesResponse(response); - } - public void processGetEpochRequest(GetEpochRequest request) { if (this.currentEpoch() == request.getEpoch()) { epochsRPCSender.sendGetEpochResponse(request.getSender(), this.currentAncestor); @@ -209,11 +189,11 @@ public void processGetEpochResponse(GetEpochResponse response) { private void processConsensusEventInternal(ConsensusEvent consensusEvent) { if (consensusEvent instanceof NewView) { - eventProcessor.processNewView((NewView) consensusEvent); + bftEventProcessor.processNewView((NewView) consensusEvent); } else if (consensusEvent instanceof Proposal) { - eventProcessor.processProposal((Proposal) consensusEvent); + bftEventProcessor.processProposal((Proposal) consensusEvent); } else if (consensusEvent instanceof Vote) { - eventProcessor.processVote((Vote) consensusEvent); + bftEventProcessor.processVote((Vote) consensusEvent); } else { throw new IllegalStateException("Unknown consensus event: " + consensusEvent); } @@ -249,18 +229,26 @@ public void processLocalTimeout(LocalTimeout localTimeout) { return; } - eventProcessor.processLocalTimeout(localTimeout.getView()); + bftEventProcessor.processLocalTimeout(localTimeout.getView()); } public void processLocalSync(Hash synced) { - eventProcessor.processLocalSync(synced); + bftEventProcessor.processLocalSync(synced); } - public void processCommittedStateSync(CommittedStateSync committedStateSync) { - if (vertexStore == null) { - return; - } + public void processGetVerticesRequest(GetVerticesRequest request) { + vertexStoreEventProcessor.processGetVerticesRequest(request); + } + + public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { + vertexStoreEventProcessor.processGetVerticesErrorResponse(response); + } - vertexStore.processCommittedStateSync(committedStateSync); + public void processGetVerticesResponse(GetVerticesResponse response) { + vertexStoreEventProcessor.processGetVerticesResponse(response); + } + + public void processCommittedStateSync(CommittedStateSync committedStateSync) { + vertexStoreEventProcessor.processCommittedStateSync(committedStateSync); } } diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java b/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java index 01e24ba51..8b68ef245 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/InternalMessagePasser.java @@ -18,7 +18,7 @@ package com.radixdlt.consensus; import com.radixdlt.EpochChangeSender; -import com.radixdlt.consensus.VertexStore.VertexStoreEventSender; +import com.radixdlt.consensus.bft.VertexStore.VertexStoreEventSender; import com.radixdlt.consensus.sync.SyncedRadixEngine.CommittedStateSyncSender; import com.radixdlt.crypto.Hash; import io.reactivex.rxjava3.core.Observable; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/MissingParentException.java b/radixdlt/src/main/java/com/radixdlt/consensus/MissingParentException.java deleted file mode 100644 index c0c914a62..000000000 --- a/radixdlt/src/main/java/com/radixdlt/consensus/MissingParentException.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.radixdlt.consensus; - -import com.radixdlt.crypto.Hash; -import java.util.Objects; - -/** - * Exception specifying that a vertex cannot be inserted because - * it's parent is missing from the current store. - */ -class MissingParentException extends RuntimeException { - MissingParentException(Hash parentId) { - super("Parent Vertex missing: " + Objects.requireNonNull(parentId)); - } -} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java index e728f860c..6d50daec3 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCRx.java @@ -17,7 +17,7 @@ package com.radixdlt.consensus; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.bft.GetVerticesErrorResponse; import com.radixdlt.consensus.bft.GetVerticesResponse; import io.reactivex.rxjava3.core.Observable; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java index 547379492..24ca4d952 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/SyncVerticesRPCSender.java @@ -18,7 +18,7 @@ package com.radixdlt.consensus; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreEventProcessor.java b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreEventProcessor.java new file mode 100644 index 000000000..c06b51515 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreEventProcessor.java @@ -0,0 +1,52 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus; + +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; +import com.radixdlt.consensus.bft.GetVerticesResponse; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; + +/** + * Processor of vertex store events + */ +public interface VertexStoreEventProcessor { + + /** + * Process a get vertices request + * @param request the get vertices request + */ + void processGetVerticesRequest(GetVerticesRequest request); + + /** + * Process a get vertices error response + * @param response the get vertices error response + */ + void processGetVerticesErrorResponse(GetVerticesErrorResponse response); + + /** + * Process a get vertices response + * @param response the get vertices response + */ + void processGetVerticesResponse(GetVerticesResponse response); + + /** + * Process a committed state ync + * @param committedStateSync the committed state sync + */ + void processCommittedStateSync(CommittedStateSync committedStateSync); +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java index d91754ca2..3163cb243 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/VertexStoreFactory.java @@ -17,6 +17,7 @@ package com.radixdlt.consensus; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.middleware2.CommittedAtom; /** diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java b/radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java new file mode 100644 index 000000000..ede611ea7 --- /dev/null +++ b/radixdlt/src/main/java/com/radixdlt/consensus/bft/MissingParentException.java @@ -0,0 +1,31 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.bft; + +import com.radixdlt.crypto.Hash; +import java.util.Objects; + +/** + * Exception specifying that a vertex cannot be inserted because + * it's parent is missing from the current store. + */ +class MissingParentException extends RuntimeException { + MissingParentException(Hash parentId) { + super("Parent Vertex missing: " + Objects.requireNonNull(parentId)); + } +} diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java b/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexStore.java similarity index 93% rename from radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java rename to radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexStore.java index ca80e38d5..a3461a598 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/VertexStore.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/bft/VertexStore.java @@ -1,25 +1,32 @@ /* - * (C) Copyright 2020 Radix DLT Ltd + * (C) Copyright 2020 Radix DLT Ltd * - * Radix DLT Ltd licenses this file to you 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 + * Radix DLT Ltd licenses this file to you 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 + * 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. + * 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 com.radixdlt.consensus; +package com.radixdlt.consensus.bft; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.bft.GetVerticesErrorResponse; -import com.radixdlt.consensus.bft.GetVerticesResponse; +import com.radixdlt.consensus.CommittedStateSync; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.SyncVerticesRPCSender; +import com.radixdlt.consensus.SyncedStateComputer; +import com.radixdlt.consensus.Vertex; +import com.radixdlt.consensus.VertexInsertionException; +import com.radixdlt.consensus.VertexMetadata; +import com.radixdlt.consensus.VertexStoreEventProcessor; +import com.radixdlt.consensus.View; import com.radixdlt.counters.SystemCounters; import com.radixdlt.counters.SystemCounters.CounterType; import com.radixdlt.crypto.ECPublicKey; @@ -45,7 +52,7 @@ * Manages the BFT Vertex chain. */ @NotThreadSafe -public final class VertexStore { +public final class VertexStore implements VertexStoreEventProcessor { private static final Logger log = LogManager.getLogger(); public interface GetVerticesRequest { @@ -211,6 +218,7 @@ private boolean requiresCommittedStateSync(SyncState syncState) { return false; } + @Override public void processGetVerticesRequest(GetVerticesRequest request) { // TODO: Handle nodes trying to DDOS this endpoint @@ -244,6 +252,7 @@ private void rebuildAndSyncQC(SyncState syncState) { } } + @Override public void processCommittedStateSync(CommittedStateSync committedStateSync) { log.info("SYNC_STATE: synced {}", committedStateSync); @@ -293,6 +302,7 @@ private void processVerticesResponseForQCSync(Hash syncTo, SyncState syncState, } } + @Override public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { // TODO: check response @@ -308,6 +318,7 @@ public void processGetVerticesErrorResponse(GetVerticesErrorResponse response) { this.startSync(syncTo, response.getHighestQC(), response.getHighestCommittedQC(), syncState.author); } + @Override public void processGetVerticesResponse(GetVerticesResponse response) { // TODO: check response diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/liveness/MempoolProposalGenerator.java b/radixdlt/src/main/java/com/radixdlt/consensus/liveness/MempoolProposalGenerator.java index 77414b6ee..cea245368 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/liveness/MempoolProposalGenerator.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/liveness/MempoolProposalGenerator.java @@ -20,7 +20,7 @@ import com.radixdlt.identifiers.AID; import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.Vertex; -import com.radixdlt.consensus.VertexStore; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.View; import com.radixdlt.mempool.Mempool; import com.radixdlt.middleware2.ClientAtom; diff --git a/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java b/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java index b5aa74e17..7eb52674f 100644 --- a/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java +++ b/radixdlt/src/main/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetwork.java @@ -27,7 +27,7 @@ import com.radixdlt.consensus.SyncVerticesRPCRx; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.Vertex; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; import com.radixdlt.network.addressbook.AddressBook; diff --git a/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java b/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java index ef2203318..3139d5050 100644 --- a/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java +++ b/radixdlt/src/main/java/com/radixdlt/middleware2/network/TestEventCoordinatorNetwork.java @@ -29,7 +29,7 @@ import com.radixdlt.consensus.SyncVerticesRPCRx; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.Vertex; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.Vote; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventPreprocessorTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventPreprocessorTest.java index 099f3327d..0f5805a42 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventPreprocessorTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventPreprocessorTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.crypto.ECKeyPair; diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventReducerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventReducerTest.java index ba6993851..1792ade36 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventReducerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/BFTEventReducerTest.java @@ -18,6 +18,7 @@ package com.radixdlt.consensus; import com.google.common.collect.Lists; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.liveness.ProposalGenerator; import com.radixdlt.identifiers.AID; import com.radixdlt.consensus.liveness.Pacemaker; diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java index ff9785c45..132e8d00f 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java @@ -24,7 +24,7 @@ import static org.mockito.Mockito.when; import com.radixdlt.consensus.ConsensusRunner.Event; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.liveness.PacemakerRx; import com.radixdlt.crypto.Hash; import io.reactivex.rxjava3.core.Observable; diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index 67a9d8712..6775d0b40 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -28,7 +28,8 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableSet; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.ProposerElection; diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/bft/VertexStoreTest.java similarity index 96% rename from radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java rename to radixdlt/src/test/java/com/radixdlt/consensus/bft/VertexStoreTest.java index f6a420c2c..743c765f2 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/VertexStoreTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/bft/VertexStoreTest.java @@ -6,7 +6,7 @@ * compliance with the License. You may obtain a copy of the * License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 @@ -15,7 +15,7 @@ * language governing permissions and limitations under the License. */ -package com.radixdlt.consensus; +package com.radixdlt.consensus.bft; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; @@ -31,9 +31,17 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; -import com.radixdlt.consensus.VertexStore.VertexStoreEventSender; -import com.radixdlt.consensus.bft.GetVerticesResponse; +import com.radixdlt.consensus.CommittedStateSync; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.SyncVerticesRPCSender; +import com.radixdlt.consensus.SyncedStateComputer; +import com.radixdlt.consensus.Vertex; +import com.radixdlt.consensus.VertexInsertionException; +import com.radixdlt.consensus.VertexMetadata; +import com.radixdlt.consensus.View; +import com.radixdlt.consensus.VoteData; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.VertexStoreEventSender; import com.radixdlt.counters.SystemCounters; import com.radixdlt.crypto.ECDSASignatures; import com.radixdlt.crypto.ECPublicKey; diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/liveness/MempoolProposalGeneratorTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/liveness/MempoolProposalGeneratorTest.java index fa6784662..59bc56517 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/liveness/MempoolProposalGeneratorTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/liveness/MempoolProposalGeneratorTest.java @@ -27,7 +27,7 @@ import com.radixdlt.consensus.QuorumCertificate; import com.radixdlt.consensus.Vertex; import com.radixdlt.consensus.VertexMetadata; -import com.radixdlt.consensus.VertexStore; +import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.View; import com.radixdlt.mempool.Mempool; import com.radixdlt.middleware2.ClientAtom; diff --git a/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java b/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java index 189c39c8d..b85536847 100644 --- a/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java +++ b/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java @@ -29,7 +29,7 @@ import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.Vertex; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; diff --git a/radixdlt/src/test/java/com/radixdlt/middleware2/network/TestBFTEventProcessorNetworkTest.java b/radixdlt/src/test/java/com/radixdlt/middleware2/network/TestBFTEventProcessorNetworkTest.java index 3747a7772..98f99a447 100644 --- a/radixdlt/src/test/java/com/radixdlt/middleware2/network/TestBFTEventProcessorNetworkTest.java +++ b/radixdlt/src/test/java/com/radixdlt/middleware2/network/TestBFTEventProcessorNetworkTest.java @@ -21,7 +21,7 @@ import com.radixdlt.consensus.ConsensusEvent; import com.radixdlt.consensus.Proposal; -import com.radixdlt.consensus.VertexStore.GetVerticesRequest; +import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.consensus.NewView; From a668b9e4398bab049bd0bb284e78299bf8a85cc8 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 19:32:22 +0800 Subject: [PATCH 11/20] Refactor testing packages --- ...BFTNetwork.java => ControlledNetwork.java} | 4 ++-- ...rolledBFTNode.java => ControlledNode.java} | 8 +++---- ...nisticTest.java => DeterministicTest.java} | 22 +++++++++---------- .../FProposalDropperResponsiveTest.java | 4 ++-- .../OneProposalDropperResponsiveTest.java | 4 ++-- .../bft/synchronous/OneSlowNodeTest.java | 6 ++--- .../RandomChannelOrderResponsiveTest.java | 6 ++--- ...oposalDropperRandomSyncResponsiveTest.java | 4 ++-- ...oposalDropperRandomSyncResponsiveTest.java | 4 ++-- ...SimulatedTest.java => SimulationTest.java} | 18 +++++++-------- .../ChangingEpochSyncedStateComputer.java | 2 +- .../DroppingLatencyProvider.java | 2 +- .../OneProposalPerViewDropper.java | 2 +- .../RandomLatencyProvider.java | 2 +- .../SingleEpochAlwaysSyncedStateComputer.java | 2 +- .../synchronous/FPlusOneOutOfBoundsTest.java | 10 ++++----- .../bft/synchronous/OneOutOfBoundsTest.java | 8 +++---- .../synchronous/OneProposalDropperTest.java | 10 ++++----- .../bft/synchronous/OneSlowNodeTest.java | 8 +++---- .../bft/synchronous/RandomLatencyTest.java | 10 ++++----- .../bft/synchronous/UniformLatencyTest.java | 4 ++-- .../OneValidatorChangePerEpochTest.java | 8 +++---- .../tests/epochs/StaticValidatorsTest.java | 12 +++++----- 23 files changed, 80 insertions(+), 80 deletions(-) rename radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/{ControlledBFTNetwork.java => ControlledNetwork.java} (98%) rename radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/{ControlledBFTNode.java => ControlledNode.java} (96%) rename radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/{BFTDeterministicTest.java => DeterministicTest.java} (83%) rename radixdlt/src/integration/java/com/radixdlt/consensus/simulation/{SimulatedTest.java => SimulationTest.java} (95%) rename radixdlt/src/integration/java/com/radixdlt/consensus/simulation/{network => configuration}/ChangingEpochSyncedStateComputer.java (98%) rename radixdlt/src/integration/java/com/radixdlt/consensus/simulation/{network => configuration}/DroppingLatencyProvider.java (97%) rename radixdlt/src/integration/java/com/radixdlt/consensus/simulation/{network => configuration}/OneProposalPerViewDropper.java (97%) rename radixdlt/src/integration/java/com/radixdlt/consensus/simulation/{network => configuration}/RandomLatencyProvider.java (97%) rename radixdlt/src/integration/java/com/radixdlt/consensus/simulation/{network => configuration}/SingleEpochAlwaysSyncedStateComputer.java (97%) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledNetwork.java similarity index 98% rename from radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledNetwork.java index 8b352076a..acd1ce69f 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNetwork.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledNetwork.java @@ -46,11 +46,11 @@ * * This class is not thread safe. */ -public final class ControlledBFTNetwork { +public final class ControlledNetwork { private final ImmutableList nodes; private final ImmutableMap> messageQueue; - ControlledBFTNetwork(ImmutableList nodes) { + ControlledNetwork(ImmutableList nodes) { this.nodes = nodes; this.messageQueue = nodes.stream() .flatMap(n0 -> nodes.stream().map(n1 -> new ChannelId(n0, n1))) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledNode.java similarity index 96% rename from radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledNode.java index e89f26388..6bd715cf5 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledBFTNode.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/ControlledNode.java @@ -41,7 +41,7 @@ import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.SyncVerticesRPCSender; import com.radixdlt.consensus.VertexStoreFactory; -import com.radixdlt.consensus.deterministic.ControlledBFTNetwork.ControlledSender; +import com.radixdlt.consensus.deterministic.ControlledNetwork.ControlledSender; import com.radixdlt.consensus.liveness.FixedTimeoutPacemaker; import com.radixdlt.consensus.liveness.MempoolProposalGenerator; import com.radixdlt.consensus.liveness.ProposalGenerator; @@ -62,16 +62,16 @@ import java.util.function.BooleanSupplier; /** - * Controlled BFT Node where its state machine is managed by a synchronous + * Controlled Node where its state machine is managed by a synchronous * processNext() call. */ -class ControlledBFTNode { +class ControlledNode { private final EpochManager epochManager; private final SystemCounters systemCounters; private final ValidatorSet initialValidatorSet; private final ControlledSender controlledSender; - ControlledBFTNode( + ControlledNode( ECKeyPair key, ControlledSender sender, ProposerElectionFactory proposerElectionFactory, diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/BFTDeterministicTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/DeterministicTest.java similarity index 83% rename from radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/BFTDeterministicTest.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/DeterministicTest.java index 62ad0a6ae..633cb3122 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/BFTDeterministicTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/DeterministicTest.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import com.google.common.collect.ImmutableList; -import com.radixdlt.consensus.deterministic.ControlledBFTNetwork.ChannelId; -import com.radixdlt.consensus.deterministic.ControlledBFTNetwork.ControlledMessage; +import com.radixdlt.consensus.deterministic.ControlledNetwork.ChannelId; +import com.radixdlt.consensus.deterministic.ControlledNetwork.ControlledMessage; import com.radixdlt.consensus.liveness.WeightedRotatingLeaders; import com.radixdlt.consensus.validators.Validator; import com.radixdlt.consensus.validators.ValidatorSet; @@ -39,21 +39,21 @@ import java.util.stream.Stream; /** - * A deterministic BFT test where each event that occurs in the BFT network + * A deterministic test where each event that occurs in the network * is emitted and processed synchronously by the caller. */ -public class BFTDeterministicTest { - private final ImmutableList nodes; +public class DeterministicTest { + private final ImmutableList nodes; private final ImmutableList pks; - private final ControlledBFTNetwork network; + private final ControlledNetwork network; - public BFTDeterministicTest(int numNodes, boolean enableGetVerticesRPC) { + public DeterministicTest(int numNodes, boolean enableGetVerticesRPC) { this(numNodes, enableGetVerticesRPC, () -> { throw new UnsupportedOperationException(); }); } - public BFTDeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanSupplier syncedSupplier) { + public DeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanSupplier syncedSupplier) { ImmutableList keys = Stream.generate(ECKeyPair::generateNew) .limit(numNodes) .sorted(Comparator.comparing(k -> k.getPublicKey().euid()).reversed()) @@ -61,13 +61,13 @@ public BFTDeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanS this.pks = keys.stream() .map(ECKeyPair::getPublicKey) .collect(ImmutableList.toImmutableList()); - this.network = new ControlledBFTNetwork(pks); + this.network = new ControlledNetwork(pks); ValidatorSet initialValidatorSet = ValidatorSet.from( pks.stream().map(pk -> Validator.from(pk, UInt256.ONE)).collect(Collectors.toList()) ); this.nodes = keys.stream() - .map(key -> new ControlledBFTNode( + .map(key -> new ControlledNode( key, network.getSender(key.getPublicKey()), vset -> new WeightedRotatingLeaders(vset, Comparator.comparing(v -> v.nodeKey().euid()), 5), @@ -79,7 +79,7 @@ public BFTDeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanS } public void start() { - nodes.forEach(ControlledBFTNode::start); + nodes.forEach(ControlledNode::start); } public void processNextMsg(int toIndex, int fromIndex, Class expectedClass) { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java index 6431318bf..5173eee67 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java @@ -20,7 +20,7 @@ import com.google.common.collect.ImmutableSet; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.View; -import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import com.radixdlt.consensus.deterministic.DeterministicTest; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,7 +39,7 @@ private void runFProposalDropperResponsiveTest(int numNodes, Function> proposalsToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, () -> true); + final DeterministicTest test = new DeterministicTest(numNodes, true, () -> true); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java index db3667347..9922563e7 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java @@ -19,7 +19,7 @@ import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.View; -import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import com.radixdlt.consensus.deterministic.DeterministicTest; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -34,7 +34,7 @@ private void runOneProposalDropperResponsiveTest(int numNodes, Function proposalToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, () -> true); + final DeterministicTest test = new DeterministicTest(numNodes, true, () -> true); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java index bf0b043e6..6728e9dc9 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java @@ -23,7 +23,7 @@ import com.radixdlt.consensus.NewView; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.Vote; -import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import com.radixdlt.consensus.deterministic.DeterministicTest; import com.radixdlt.counters.SystemCounters.CounterType; import org.junit.Test; @@ -31,7 +31,7 @@ public class OneSlowNodeTest { @Test public void when_three_fast_nodes_and_one_slow_node_two_cycles__then_missing_parent_should_not_cause_sync_exception() { - final BFTDeterministicTest test = new BFTDeterministicTest(4, false); + final DeterministicTest test = new DeterministicTest(4, false); test.start(); @@ -63,7 +63,7 @@ public void when_three_fast_nodes_and_one_slow_node_two_cycles__then_missing_par */ @Test public void when_three_fast_nodes_and_one_slow_node__then_missing_parent_should_not_cause_exception() { - final BFTDeterministicTest test = new BFTDeterministicTest(4, false); + final DeterministicTest test = new DeterministicTest(4, false); test.start(); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java index 3bb1209ed..01489ca33 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java @@ -17,7 +17,7 @@ package com.radixdlt.consensus.deterministic.tests.bft.synchronous; -import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import com.radixdlt.consensus.deterministic.DeterministicTest; import java.util.Random; import org.junit.Test; @@ -26,7 +26,7 @@ public class RandomChannelOrderResponsiveTest { @Test public void when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { final Random random = new Random(12345); - final BFTDeterministicTest test = new BFTDeterministicTest(4, false); + final DeterministicTest test = new DeterministicTest(4, false); test.start(); for (int step = 0; step < 100000; step++) { @@ -37,7 +37,7 @@ public void when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disa @Test public void when_run_100_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { final Random random = new Random(12345); - final BFTDeterministicTest test = new BFTDeterministicTest(100, false); + final DeterministicTest test = new DeterministicTest(100, false); test.start(); for (int step = 0; step < 100000; step++) { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java index 5c9b0a380..67c8d7681 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java @@ -20,7 +20,7 @@ import com.google.common.collect.ImmutableSet; import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.View; -import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import com.radixdlt.consensus.deterministic.DeterministicTest; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -39,7 +39,7 @@ private void runFProposalDropperResponsiveTest(int numNodes, Function> proposalsToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, random::nextBoolean); + final DeterministicTest test = new DeterministicTest(numNodes, true, random::nextBoolean); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java index 45fabbcf1..b3da7097d 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java @@ -19,7 +19,7 @@ import com.radixdlt.consensus.Proposal; import com.radixdlt.consensus.View; -import com.radixdlt.consensus.deterministic.BFTDeterministicTest; +import com.radixdlt.consensus.deterministic.DeterministicTest; import java.util.HashMap; import java.util.Map; import java.util.Random; @@ -34,7 +34,7 @@ private void runOneProposalDropperResponsiveTest(int numNodes, Function proposalToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final BFTDeterministicTest test = new BFTDeterministicTest(numNodes, true, random::nextBoolean); + final DeterministicTest test = new DeterministicTest(numNodes, true, random::nextBoolean); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulatedTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulationTest.java similarity index 95% rename from radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulatedTest.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulationTest.java index 1378e90b1..3022b8abc 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulatedTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/SimulationTest.java @@ -22,10 +22,10 @@ import com.radixdlt.consensus.View; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; import com.radixdlt.consensus.simulation.invariants.epochs.EpochViewInvariant; -import com.radixdlt.consensus.simulation.network.ChangingEpochSyncedStateComputer; -import com.radixdlt.consensus.simulation.network.DroppingLatencyProvider; -import com.radixdlt.consensus.simulation.network.OneProposalPerViewDropper; -import com.radixdlt.consensus.simulation.network.RandomLatencyProvider; +import com.radixdlt.consensus.simulation.configuration.ChangingEpochSyncedStateComputer; +import com.radixdlt.consensus.simulation.configuration.DroppingLatencyProvider; +import com.radixdlt.consensus.simulation.configuration.OneProposalPerViewDropper; +import com.radixdlt.consensus.simulation.configuration.RandomLatencyProvider; import com.radixdlt.consensus.simulation.network.SimulatedNetwork; import com.radixdlt.consensus.simulation.network.SimulatedNetwork.RunningNetwork; import com.radixdlt.consensus.simulation.invariants.bft.AllProposalsHaveDirectParentsInvariant; @@ -34,7 +34,7 @@ import com.radixdlt.consensus.simulation.invariants.bft.NoneCommittedInvariant; import com.radixdlt.consensus.simulation.invariants.bft.SafetyInvariant; import com.radixdlt.consensus.simulation.network.SimulatedNetwork.SimulatedStateComputer; -import com.radixdlt.consensus.simulation.network.SingleEpochAlwaysSyncedStateComputer; +import com.radixdlt.consensus.simulation.configuration.SingleEpochAlwaysSyncedStateComputer; import com.radixdlt.consensus.validators.Validator; import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.crypto.ECKeyPair; @@ -61,7 +61,7 @@ /** * High level BFT Simulation Test Runner */ -public class SimulatedTest { +public class SimulationTest { private final ImmutableList nodes; private final LatencyProvider latencyProvider; private final ImmutableMap checks; @@ -70,7 +70,7 @@ public class SimulatedTest { private final boolean getVerticesRPCEnabled; private final View epochHighView; - private SimulatedTest( + private SimulationTest( ImmutableList nodes, LatencyProvider latencyProvider, int pacemakerTimeout, @@ -183,7 +183,7 @@ public Builder checkEpochHighView(String invariantName, View epochHighView) { return this; } - public SimulatedTest build() { + public SimulationTest build() { final List publicKeys = nodes.stream().map(ECKeyPair::getPublicKey).collect(Collectors.toList()); Function epochToValidatorSetMapping = epochToNodeIndexMapper == null @@ -196,7 +196,7 @@ public SimulatedTest build() { .map(nodes::get) .map(kp -> Validator.from(kp.getPublicKey(), UInt256.ONE)) .collect(Collectors.toList()))); - return new SimulatedTest( + return new SimulationTest( ImmutableList.copyOf(nodes), latencyProvider.copyOf(), pacemakerTimeout, diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/ChangingEpochSyncedStateComputer.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/ChangingEpochSyncedStateComputer.java similarity index 98% rename from radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/ChangingEpochSyncedStateComputer.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/ChangingEpochSyncedStateComputer.java index 4474255a1..d07997b35 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/ChangingEpochSyncedStateComputer.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/ChangingEpochSyncedStateComputer.java @@ -15,7 +15,7 @@ * language governing permissions and limitations under the License. */ -package com.radixdlt.consensus.simulation.network; +package com.radixdlt.consensus.simulation.configuration; import com.radixdlt.consensus.EpochChange; import com.radixdlt.consensus.Vertex; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/DroppingLatencyProvider.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/DroppingLatencyProvider.java similarity index 97% rename from radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/DroppingLatencyProvider.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/DroppingLatencyProvider.java index 03b2e3331..cf55f7045 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/DroppingLatencyProvider.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/DroppingLatencyProvider.java @@ -15,7 +15,7 @@ * language governing permissions and limitations under the License. */ -package com.radixdlt.consensus.simulation.network; +package com.radixdlt.consensus.simulation.configuration; import com.google.common.collect.Sets; import com.radixdlt.crypto.ECPublicKey; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/OneProposalPerViewDropper.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/OneProposalPerViewDropper.java similarity index 97% rename from radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/OneProposalPerViewDropper.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/OneProposalPerViewDropper.java index e9786659e..32605938f 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/OneProposalPerViewDropper.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/OneProposalPerViewDropper.java @@ -15,7 +15,7 @@ * language governing permissions and limitations under the License. */ -package com.radixdlt.consensus.simulation.network; +package com.radixdlt.consensus.simulation.configuration; import com.google.common.collect.ImmutableList; import com.radixdlt.consensus.Proposal; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/RandomLatencyProvider.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/RandomLatencyProvider.java similarity index 97% rename from radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/RandomLatencyProvider.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/RandomLatencyProvider.java index 0e285e989..2ef0e3b34 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/RandomLatencyProvider.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/RandomLatencyProvider.java @@ -15,7 +15,7 @@ * language governing permissions and limitations under the License. */ -package com.radixdlt.consensus.simulation.network; +package com.radixdlt.consensus.simulation.configuration; import com.radixdlt.middleware2.network.TestEventCoordinatorNetwork.LatencyProvider; import com.radixdlt.middleware2.network.TestEventCoordinatorNetwork.MessageInTransit; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SingleEpochAlwaysSyncedStateComputer.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/SingleEpochAlwaysSyncedStateComputer.java similarity index 97% rename from radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SingleEpochAlwaysSyncedStateComputer.java rename to radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/SingleEpochAlwaysSyncedStateComputer.java index 5e5175885..a130dad97 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/network/SingleEpochAlwaysSyncedStateComputer.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/configuration/SingleEpochAlwaysSyncedStateComputer.java @@ -15,7 +15,7 @@ * language governing permissions and limitations under the License. */ -package com.radixdlt.consensus.simulation.network; +package com.radixdlt.consensus.simulation.configuration; import com.radixdlt.consensus.EpochChange; import com.radixdlt.consensus.Vertex; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/FPlusOneOutOfBoundsTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/FPlusOneOutOfBoundsTest.java index 501113663..d6fe730ce 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/FPlusOneOutOfBoundsTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/FPlusOneOutOfBoundsTest.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.consensus.simulation.SimulatedTest; -import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.SimulationTest; +import com.radixdlt.consensus.simulation.SimulationTest.Builder; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -31,7 +31,7 @@ public class FPlusOneOutOfBoundsTest { private final int latency = 50; private final int synchronousTimeout = 8 * latency; private final int outOfBoundsLatency = synchronousTimeout; - private final Builder bftTestBuilder = SimulatedTest.builder() + private final Builder bftTestBuilder = SimulationTest.builder() .pacemakerTimeout(2 * synchronousTimeout) .checkSafety("safety") .checkNoneCommitted("noneCommitted"); @@ -41,7 +41,7 @@ public class FPlusOneOutOfBoundsTest { */ @Test public void given_0_out_of_3_nodes_out_of_synchrony_bounds() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .numNodesAndLatencies(3, latency, latency, latency) .build(); @@ -54,7 +54,7 @@ public void given_0_out_of_3_nodes_out_of_synchrony_bounds() { */ @Test public void given_1_out_of_3_nodes_out_of_synchrony_bounds() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .numNodesAndLatencies(3, latency, latency, outOfBoundsLatency) .build(); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneOutOfBoundsTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneOutOfBoundsTest.java index dd6cf5057..4aae8cb9c 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneOutOfBoundsTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneOutOfBoundsTest.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.consensus.simulation.SimulatedTest; -import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.SimulationTest; +import com.radixdlt.consensus.simulation.SimulationTest.Builder; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -33,7 +33,7 @@ public class OneOutOfBoundsTest { private final int synchronousTimeout = 8 * latency; private final int outOfBoundsLatency = synchronousTimeout; // TODO: Add 1 timeout check - private final Builder bftTestBuilder = SimulatedTest.builder() + private final Builder bftTestBuilder = SimulationTest.builder() .pacemakerTimeout(synchronousTimeout) .checkLiveness("liveness", 2 * synchronousTimeout, TimeUnit.MILLISECONDS) .checkSafety("safety"); @@ -43,7 +43,7 @@ public class OneOutOfBoundsTest { */ @Test public void given_1_out_of_4_nodes_out_of_synchrony_bounds() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .numNodesAndLatencies(4, latency, latency, latency, outOfBoundsLatency) .build(); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneProposalDropperTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneProposalDropperTest.java index f2ac99bb1..cf9500094 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneProposalDropperTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneProposalDropperTest.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.consensus.simulation.SimulatedTest; -import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.SimulationTest; +import com.radixdlt.consensus.simulation.SimulationTest.Builder; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -36,7 +36,7 @@ public class OneProposalDropperTest { private final int maxLatency = 200; private final int trips = 20; private final int synchronousTimeout = maxLatency * trips; - private final Builder bftTestBuilder = SimulatedTest.builder() + private final Builder bftTestBuilder = SimulationTest.builder() .numNodes(4) .randomLatency(minLatency, maxLatency) .pacemakerTimeout(synchronousTimeout) @@ -50,7 +50,7 @@ public class OneProposalDropperTest { */ @Test public void given_get_vertices_disabled__then_test_should_fail_against_drop_proposal_adversary() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .setGetVerticesRPCEnabled(false) .build(); @@ -64,7 +64,7 @@ public void given_get_vertices_disabled__then_test_should_fail_against_drop_prop */ @Test public void given_get_vertices_enabled__then_test_should_succeed_against_drop_proposal_adversary() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .setGetVerticesRPCEnabled(true) .build(); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneSlowNodeTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneSlowNodeTest.java index 4f72d2fd7..37311de84 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneSlowNodeTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/OneSlowNodeTest.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.consensus.simulation.SimulatedTest; -import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.SimulationTest; +import com.radixdlt.consensus.simulation.SimulationTest.Builder; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -38,7 +38,7 @@ public class OneSlowNodeTest { private final int maxLatency = 200; private final int trips = 8; private final int synchronousTimeout = maxLatency * trips; - private final Builder bftTestBuilder = SimulatedTest.builder() + private final Builder bftTestBuilder = SimulationTest.builder() .numNodesAndLatencies(4, minLatency, minLatency, minLatency, maxLatency) .pacemakerTimeout(synchronousTimeout) .checkSafety("safety") @@ -51,7 +51,7 @@ public class OneSlowNodeTest { */ @Test public void given_4_nodes_3_fast_and_1_slow_node_and_sync_disabled__then_a_timeout_wont_occur() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .setGetVerticesRPCEnabled(false) .build(); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/RandomLatencyTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/RandomLatencyTest.java index d6647fabe..4789523eb 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/RandomLatencyTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/RandomLatencyTest.java @@ -20,8 +20,8 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.consensus.simulation.SimulatedTest; -import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.SimulationTest; +import com.radixdlt.consensus.simulation.SimulationTest.Builder; import java.util.Map; import java.util.Optional; import org.assertj.core.api.AssertionsForClassTypes; @@ -44,7 +44,7 @@ public class RandomLatencyTest { private final int trips = 6; private final int synchronousTimeout = maxLatency * trips; - private Builder bftTestBuilder = SimulatedTest.builder() + private Builder bftTestBuilder = SimulationTest.builder() .randomLatency(minLatency, maxLatency) .setGetVerticesRPCEnabled(false) .pacemakerTimeout(synchronousTimeout) // Since no syncing needed 6*MTT required @@ -58,7 +58,7 @@ public class RandomLatencyTest { */ @Test public void given_3_correct_nodes_in_random_network_and_no_sync__then_all_synchronous_checks_should_pass() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .numNodes(3) .build(); @@ -71,7 +71,7 @@ public void given_3_correct_nodes_in_random_network_and_no_sync__then_all_synchr */ @Test public void given_4_correct_bfts_in_random_network_and_no_sync__then_all_synchronous_checks_should_pass() { - SimulatedTest test = bftTestBuilder + SimulationTest test = bftTestBuilder .numNodes(4) .build(); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/UniformLatencyTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/UniformLatencyTest.java index 3909806a0..676831021 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/UniformLatencyTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/bft/synchronous/UniformLatencyTest.java @@ -20,7 +20,7 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.consensus.simulation.SimulatedTest; +import com.radixdlt.consensus.simulation.SimulationTest; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -41,7 +41,7 @@ public class UniformLatencyTest { */ @Test public void given_4_correct_bfts__then_should_pass_sanity_tests_over_1_minute() { - SimulatedTest bftTest = SimulatedTest.builder() + SimulationTest bftTest = SimulationTest.builder() .numNodes(4) .checkSafety("safety") .checkLiveness("liveness") diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java index 702ec75fc..e993db5db 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/OneValidatorChangePerEpochTest.java @@ -21,8 +21,8 @@ import com.google.common.collect.Sets; import com.radixdlt.consensus.View; -import com.radixdlt.consensus.simulation.SimulatedTest; -import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.SimulationTest; +import com.radixdlt.consensus.simulation.SimulationTest.Builder; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; import java.util.Map; import java.util.Optional; @@ -32,7 +32,7 @@ import org.junit.Test; public class OneValidatorChangePerEpochTest { - private final Builder bftTestBuilder = SimulatedTest.builder() + private final Builder bftTestBuilder = SimulationTest.builder() .numNodes(4) .checkSafety("safety") .checkLiveness("liveness") @@ -42,7 +42,7 @@ public class OneValidatorChangePerEpochTest { @Test @Ignore public void given_correct_bft_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { - SimulatedTest bftTest = bftTestBuilder + SimulationTest bftTest = bftTestBuilder .epochHighView(View.of(100)) .epochToNodesMapper(epoch -> Sets.newHashSet((int) (epoch % 4), (int) (epoch + 1) % 4, (int) (epoch + 2) % 4) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/StaticValidatorsTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/StaticValidatorsTest.java index 923fa6a0b..288aa6161 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/StaticValidatorsTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/simulation/tests/epochs/StaticValidatorsTest.java @@ -20,16 +20,16 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import com.radixdlt.consensus.View; -import com.radixdlt.consensus.simulation.SimulatedTest.Builder; +import com.radixdlt.consensus.simulation.SimulationTest.Builder; import com.radixdlt.consensus.simulation.TestInvariant.TestInvariantError; -import com.radixdlt.consensus.simulation.SimulatedTest; +import com.radixdlt.consensus.simulation.SimulationTest; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import org.junit.Test; public class StaticValidatorsTest { - private final Builder bftTestBuilder = SimulatedTest.builder() + private final Builder bftTestBuilder = SimulationTest.builder() .numNodes(4) .checkSafety("safety") .checkLiveness("liveness", 1000, TimeUnit.MILLISECONDS) @@ -38,7 +38,7 @@ public class StaticValidatorsTest { @Test public void given_correct_bft_with_changing_epochs_every_view__then_should_pass_bft_and_epoch_invariants() { - SimulatedTest bftTest = bftTestBuilder + SimulationTest bftTest = bftTestBuilder .pacemakerTimeout(1000) .epochHighView(View.of(1)) .checkEpochHighView("epochHighView", View.of(1)) @@ -49,7 +49,7 @@ public void given_correct_bft_with_changing_epochs_every_view__then_should_pass_ @Test public void given_correct_bft_with_changing_epochs_per_100_views__then_should_fail_incorrect_epoch_invariant() { - SimulatedTest bftTest = bftTestBuilder + SimulationTest bftTest = bftTestBuilder .epochHighView(View.of(100)) .checkEpochHighView("epochHighView", View.of(99)) .build(); @@ -59,7 +59,7 @@ public void given_correct_bft_with_changing_epochs_per_100_views__then_should_fa @Test public void given_correct_bft_with_changing_epochs_per_100_views__then_should_pass_bft_and_epoch_invariants() { - SimulatedTest bftTest = bftTestBuilder + SimulationTest bftTest = bftTestBuilder .epochHighView(View.of(100)) .checkEpochHighView("epochHighView", View.of(100)) .build(); From 16bd4f79d2fe50c8ce6fad1087310586c648d330 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 20:05:17 +0800 Subject: [PATCH 12/20] Add more descriptive Deterministic constructor names --- .../deterministic/DeterministicTest.java | 44 +++++++++++++++---- .../FProposalDropperResponsiveTest.java | 2 +- .../OneProposalDropperResponsiveTest.java | 2 +- .../bft/synchronous/OneSlowNodeTest.java | 4 +- .../RandomChannelOrderResponsiveTest.java | 4 +- ...oposalDropperRandomSyncResponsiveTest.java | 2 +- ...oposalDropperRandomSyncResponsiveTest.java | 2 +- 7 files changed, 44 insertions(+), 16 deletions(-) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/DeterministicTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/DeterministicTest.java index 633cb3122..c6fb30b58 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/DeterministicTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/DeterministicTest.java @@ -42,18 +42,12 @@ * A deterministic test where each event that occurs in the network * is emitted and processed synchronously by the caller. */ -public class DeterministicTest { +public final class DeterministicTest { private final ImmutableList nodes; private final ImmutableList pks; private final ControlledNetwork network; - public DeterministicTest(int numNodes, boolean enableGetVerticesRPC) { - this(numNodes, enableGetVerticesRPC, () -> { - throw new UnsupportedOperationException(); - }); - } - - public DeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanSupplier syncedSupplier) { + private DeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanSupplier syncedSupplier) { ImmutableList keys = Stream.generate(ECKeyPair::generateNew) .limit(numNodes) .sorted(Comparator.comparing(k -> k.getPublicKey().euid()).reversed()) @@ -78,6 +72,40 @@ public DeterministicTest(int numNodes, boolean enableGetVerticesRPC, BooleanSupp .collect(ImmutableList.toImmutableList()); } + /** + * Creates a new randomly synced BFT/SyncedStateComputer test + * @param numNodes number of nodes in the network + * @param random the randomizer + * @return a deterministic test + */ + public static DeterministicTest createRandomlySyncedBFTAndSyncedStateComputerTest(int numNodes, Random random) { + return new DeterministicTest(numNodes, true, random::nextBoolean); + } + + /** + * Creates a new "always synced BFT" Deterministic test solely on the bft layer, + * + * @param numNodes number of nodes in the network + * @return a deterministic test + */ + public static DeterministicTest createAlwaysSyncedBFTTest(int numNodes) { + return new DeterministicTest(numNodes, true, () -> true); + } + + /** + * Creates a new "non syncing BFT" Deterministic test solely on the bft layer, + * "non syncing BFT" implying that the configuration of the network should never + * require a vertex sync nor a state computer sync + * + * @param numNodes number of nodes in the network + * @return a deterministic test + */ + public static DeterministicTest createNonSyncingBFTTest(int numNodes) { + return new DeterministicTest(numNodes, false, () -> { + throw new IllegalStateException("This is a nonsyncing bft test and should not have required a state sync"); + }); + } + public void start() { nodes.forEach(ControlledNode::start); } diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java index 5173eee67..ac657bfb1 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java @@ -39,7 +39,7 @@ private void runFProposalDropperResponsiveTest(int numNodes, Function> proposalsToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final DeterministicTest test = new DeterministicTest(numNodes, true, () -> true); + final DeterministicTest test = DeterministicTest.createAlwaysSyncedBFTTest(numNodes); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java index 9922563e7..f14aee049 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java @@ -34,7 +34,7 @@ private void runOneProposalDropperResponsiveTest(int numNodes, Function proposalToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final DeterministicTest test = new DeterministicTest(numNodes, true, () -> true); + final DeterministicTest test = DeterministicTest.createAlwaysSyncedBFTTest(numNodes); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java index 6728e9dc9..17d766235 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneSlowNodeTest.java @@ -31,7 +31,7 @@ public class OneSlowNodeTest { @Test public void when_three_fast_nodes_and_one_slow_node_two_cycles__then_missing_parent_should_not_cause_sync_exception() { - final DeterministicTest test = new DeterministicTest(4, false); + final DeterministicTest test = DeterministicTest.createNonSyncingBFTTest(4); test.start(); @@ -63,7 +63,7 @@ public void when_three_fast_nodes_and_one_slow_node_two_cycles__then_missing_par */ @Test public void when_three_fast_nodes_and_one_slow_node__then_missing_parent_should_not_cause_exception() { - final DeterministicTest test = new DeterministicTest(4, false); + final DeterministicTest test = DeterministicTest.createNonSyncingBFTTest(4); test.start(); diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java index 01489ca33..eddbc8578 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java @@ -26,7 +26,7 @@ public class RandomChannelOrderResponsiveTest { @Test public void when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { final Random random = new Random(12345); - final DeterministicTest test = new DeterministicTest(4, false); + final DeterministicTest test = DeterministicTest.createAlwaysSyncedBFTTest(4); test.start(); for (int step = 0; step < 100000; step++) { @@ -37,7 +37,7 @@ public void when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disa @Test public void when_run_100_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { final Random random = new Random(12345); - final DeterministicTest test = new DeterministicTest(100, false); + final DeterministicTest test = DeterministicTest.createAlwaysSyncedBFTTest(100); test.start(); for (int step = 0; step < 100000; step++) { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java index 67c8d7681..40136b0ec 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java @@ -39,7 +39,7 @@ private void runFProposalDropperResponsiveTest(int numNodes, Function> proposalsToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final DeterministicTest test = new DeterministicTest(numNodes, true, random::nextBoolean); + final DeterministicTest test = DeterministicTest.createRandomlySyncedBFTAndSyncedStateComputerTest(numNodes, random); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java index b3da7097d..c2f25b4c0 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java @@ -34,7 +34,7 @@ private void runOneProposalDropperResponsiveTest(int numNodes, Function proposalToDrop = new HashMap<>(); final Map proposalCount = new HashMap<>(); - final DeterministicTest test = new DeterministicTest(numNodes, true, random::nextBoolean); + final DeterministicTest test = DeterministicTest.createRandomlySyncedBFTAndSyncedStateComputerTest(numNodes, random); test.start(); for (int step = 0; step < 100000; step++) { test.processNextMsg(random, (receiverId, msg) -> { From c6a78995bc428004fe263cd01eecbdf8e61f058c Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Sun, 5 Jul 2020 21:07:48 +0800 Subject: [PATCH 13/20] Add error responses to ConsensusRunner --- .../main/java/com/radixdlt/consensus/ConsensusRunner.java | 7 +++++++ .../java/com/radixdlt/consensus/ConsensusRunnerTest.java | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusRunner.java b/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusRunner.java index 2eb6ee080..8d01e9e6f 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusRunner.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/ConsensusRunner.java @@ -49,6 +49,7 @@ public enum EventType { CONSENSUS_EVENT, GET_VERTICES_REQUEST, GET_VERTICES_RESPONSE, + GET_VERTICES_ERROR_RESPONSE, } public static class Event { @@ -121,6 +122,12 @@ public ConsensusRunner( epochManager.processGetVerticesResponse(e); return new Event(EventType.GET_VERTICES_RESPONSE, e); }), + rpcRx.errorResponses() + .observeOn(singleThreadScheduler) + .map(e -> { + epochManager.processGetVerticesErrorResponse(e); + return new Event(EventType.GET_VERTICES_ERROR_RESPONSE, e); + }), vertexStoreEventsRx.syncedVertices() .observeOn(singleThreadScheduler) .map(e -> { diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java index 132e8d00f..1a9f7792c 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/ConsensusRunnerTest.java @@ -56,6 +56,7 @@ public void when_events_get_emitted__then_event_coordinator_should_be_called() { GetVerticesRequest request = mock(GetVerticesRequest.class); when(syncVerticesRPCRx.requests()).thenReturn(Observable.just(request).concatWith(Observable.never())); when(syncVerticesRPCRx.responses()).thenReturn(Observable.never()); + when(syncVerticesRPCRx.errorResponses()).thenReturn(Observable.never()); VertexStoreEventsRx vertexStoreEventsRx = mock(VertexStoreEventsRx.class); Hash id = mock(Hash.class); @@ -65,7 +66,8 @@ public void when_events_get_emitted__then_event_coordinator_should_be_called() { CommittedStateSync stateSync = mock(CommittedStateSync.class); when(committedStateSyncRx.committedStateSyncs()).thenReturn(Observable.just(stateSync).concatWith(Observable.never())); - ConsensusRunner consensusRunner = new ConsensusRunner(epochChangeRx, + ConsensusRunner consensusRunner = new ConsensusRunner( + epochChangeRx, networkRx, pacemakerRx, vertexStoreEventsRx, From a445aa36a84f7d35f704439c5dad923adeb58ae2 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Mon, 6 Jul 2020 14:32:54 +0800 Subject: [PATCH 14/20] Add unit tests --- .../consensus/epoch/GetEpochRequest.java | 3 +- .../EmptySyncEpochsRPCSenderTest.java | 32 +++++++++++ .../bft/GetVerticesErrorResponseTest.java | 57 +++++++++++++++++++ .../consensus/epoch/GetEpochRequestTest.java | 49 ++++++++++++++++ .../consensus/epoch/GetEpochResponseTest.java | 51 +++++++++++++++++ ...icesErrorResponseMessageSerializeTest.java | 38 +++++++++++++ .../GetVerticesErrorResponseMessageTest.java | 38 +++++++++++++ ...sageCentralSyncVerticesRPCNetworkTest.java | 46 +++++++++++++++ 8 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 radixdlt/src/test/java/com/radixdlt/consensus/EmptySyncEpochsRPCSenderTest.java create mode 100644 radixdlt/src/test/java/com/radixdlt/consensus/bft/GetVerticesErrorResponseTest.java create mode 100644 radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochRequestTest.java create mode 100644 radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochResponseTest.java create mode 100644 radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java create mode 100644 radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java b/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java index eecfbfec0..e52194f7b 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/epoch/GetEpochRequest.java @@ -18,6 +18,7 @@ package com.radixdlt.consensus.epoch; import com.radixdlt.crypto.ECPublicKey; +import java.util.Objects; /** * An RPC request to retrieve proof of an epoch @@ -27,7 +28,7 @@ public final class GetEpochRequest { private final ECPublicKey sender; public GetEpochRequest(ECPublicKey sender, final long epoch) { - this.sender = sender; + this.sender = Objects.requireNonNull(sender); this.epoch = epoch; } diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EmptySyncEpochsRPCSenderTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EmptySyncEpochsRPCSenderTest.java new file mode 100644 index 000000000..f8f50a06a --- /dev/null +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EmptySyncEpochsRPCSenderTest.java @@ -0,0 +1,32 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus; + +import static org.mockito.Mockito.mock; + +import com.radixdlt.crypto.ECPublicKey; +import org.junit.Test; + +public class EmptySyncEpochsRPCSenderTest { + @Test + public void when_send_request_and_response__then_no_exception_occurs() { + EmptySyncEpochsRPCSender.INSTANCE.sendGetEpochRequest(mock(ECPublicKey.class), 12345L); + EmptySyncEpochsRPCSender.INSTANCE.sendGetEpochResponse(mock(ECPublicKey.class), mock(VertexMetadata.class)); + } + +} \ No newline at end of file diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/bft/GetVerticesErrorResponseTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/bft/GetVerticesErrorResponseTest.java new file mode 100644 index 000000000..7ac9ded90 --- /dev/null +++ b/radixdlt/src/test/java/com/radixdlt/consensus/bft/GetVerticesErrorResponseTest.java @@ -0,0 +1,57 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.bft; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.mock; + +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.crypto.Hash; +import org.junit.Before; +import org.junit.Test; + +public class GetVerticesErrorResponseTest { + private Hash vertexId; + private Object opaque; + private QuorumCertificate highestQC; + private QuorumCertificate highestCommittedQC; + private GetVerticesErrorResponse response; + + @Before + public void setUp() { + this.vertexId = mock(Hash.class); + this.opaque = mock(Object.class); + this.highestQC = mock(QuorumCertificate.class); + this.highestCommittedQC = mock(QuorumCertificate.class); + this.response = new GetVerticesErrorResponse(this.vertexId, this.highestQC, this.highestCommittedQC, this.opaque); + } + + @Test + public void testGetters() { + assertThat(this.response.getVertexId()).isEqualTo(this.vertexId); + assertThat(this.response.getOpaque()).isEqualTo(this.opaque); + assertThat(this.response.getHighestQC()).isEqualTo(this.highestQC); + assertThat(this.response.getHighestCommittedQC()).isEqualTo(this.highestCommittedQC); + } + + @Test + public void testToString() { + assertThat(this.response.toString()).isNotNull(); + } + +} \ No newline at end of file diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochRequestTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochRequestTest.java new file mode 100644 index 000000000..f76ef1dd5 --- /dev/null +++ b/radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochRequestTest.java @@ -0,0 +1,49 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.epoch; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.mock; + +import com.radixdlt.crypto.ECPublicKey; +import org.junit.Before; +import org.junit.Test; + +public class GetEpochRequestTest { + private ECPublicKey sender; + private long epoch; + private GetEpochRequest request; + + @Before + public void setUp() { + this.sender = mock(ECPublicKey.class); + this.epoch = 12345; + this.request = new GetEpochRequest(this.sender, this.epoch); + } + + @Test + public void testGetters() { + assertThat(this.request.getEpoch()).isEqualTo(epoch); + assertThat(this.request.getSender()).isEqualTo(this.sender); + } + + @Test + public void testToString() { + assertThat(this.request.toString()).isNotNull(); + } +} \ No newline at end of file diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochResponseTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochResponseTest.java new file mode 100644 index 000000000..e3d3a079c --- /dev/null +++ b/radixdlt/src/test/java/com/radixdlt/consensus/epoch/GetEpochResponseTest.java @@ -0,0 +1,51 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.consensus.epoch; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.Mockito.mock; + +import com.radixdlt.consensus.VertexMetadata; +import com.radixdlt.crypto.ECPublicKey; +import org.junit.Before; +import org.junit.Test; + +public class GetEpochResponseTest { + private ECPublicKey sender; + private VertexMetadata ancestor; + private GetEpochResponse response; + + @Before + public void setUp() { + this.sender = mock(ECPublicKey.class); + this.ancestor = mock(VertexMetadata.class); + this.response = new GetEpochResponse(this.sender, this.ancestor); + } + + @Test + public void testGetters() { + assertThat(this.response.getEpochAncestor()).isEqualTo(this.ancestor); + assertThat(this.response.getSender()).isEqualTo(this.sender); + } + + @Test + public void testToString() { + assertThat(this.response.toString()).isNotNull(); + } + +} \ No newline at end of file diff --git a/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java b/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java new file mode 100644 index 000000000..743d3982a --- /dev/null +++ b/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageSerializeTest.java @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.middleware2.network; + +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.Vertex; +import com.radixdlt.crypto.Hash; +import org.radix.serialization.SerializeMessageObject; + +public class GetVerticesErrorResponseMessageSerializeTest extends SerializeMessageObject { + public GetVerticesErrorResponseMessageSerializeTest() { + super(GetVerticesErrorResponseMessage.class, GetVerticesErrorResponseMessageSerializeTest::get); + } + + private static GetVerticesErrorResponseMessage get() { + return new GetVerticesErrorResponseMessage( + 12345, + Hash.random(), + QuorumCertificate.ofGenesis(Vertex.createGenesis()), + QuorumCertificate.ofGenesis(Vertex.createGenesis()) + ); + } +} diff --git a/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java b/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java new file mode 100644 index 000000000..0c46afd02 --- /dev/null +++ b/radixdlt/src/test/java/com/radixdlt/middleware2/network/GetVerticesErrorResponseMessageTest.java @@ -0,0 +1,38 @@ +/* + * (C) Copyright 2020 Radix DLT Ltd + * + * Radix DLT Ltd licenses this file to you 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 com.radixdlt.middleware2.network; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.Vertex; +import com.radixdlt.crypto.Hash; +import org.junit.Test; + +public class GetVerticesErrorResponseMessageTest { + @Test + public void sensibleToString() { + Hash vertexId = Hash.random(); + QuorumCertificate qc = QuorumCertificate.ofGenesis(Vertex.createGenesis()); + GetVerticesErrorResponseMessage msg1 = new GetVerticesErrorResponseMessage(0, vertexId, qc, qc); + String s1 = msg1.toString(); + assertThat(s1, containsString(GetVerticesErrorResponseMessage.class.getSimpleName())); + assertThat(s1, containsString(vertexId.toString())); + } +} \ No newline at end of file diff --git a/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java b/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java index b85536847..fd0c877f7 100644 --- a/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java +++ b/radixdlt/src/test/java/com/radixdlt/middleware2/network/MessageCentralSyncVerticesRPCNetworkTest.java @@ -27,6 +27,8 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; +import com.radixdlt.consensus.QuorumCertificate; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.Vertex; import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; @@ -108,6 +110,39 @@ public void when_send_request_and_receive_response__then_should_receive_same_opa testObserver.assertValue(resp -> resp.getOpaque().equals(opaque)); } + @Test + public void when_send_request_and_receive_error_response__then_should_receive_same_opaque() { + Hash id = mock(Hash.class); + ECPublicKey node = mock(ECPublicKey.class); + when(node.euid()).thenReturn(EUID.ONE); + Peer peer = mock(Peer.class); + when(addressBook.peer(eq(EUID.ONE))).thenReturn(Optional.of(peer)); + int count = 1; + Object opaque = mock(Object.class); + network.sendGetVerticesRequest(id, node, count, opaque); + verify(messageCentral, times(1)).send(eq(peer), any(GetVerticesRequestMessage.class)); + + AtomicReference> listener = new AtomicReference<>(); + + doAnswer(invocation -> { + listener.set(invocation.getArgument(1)); + return null; + }).when(messageCentral).addListener(eq(GetVerticesErrorResponseMessage.class), any()); + + TestObserver testObserver = network.errorResponses().test(); + + GetVerticesErrorResponseMessage responseMessage = mock(GetVerticesErrorResponseMessage.class); + Vertex vertex = mock(Vertex.class); + when(vertex.getId()).thenReturn(id); + when(responseMessage.getVertexId()).thenReturn(id); + when(responseMessage.getHighestCommittedQC()).thenReturn(mock(QuorumCertificate.class)); + when(responseMessage.getHighestQC()).thenReturn(mock(QuorumCertificate.class)); + listener.get().handleMessage(mock(Peer.class), responseMessage); + + testObserver.awaitCount(1); + testObserver.assertValue(resp -> resp.getOpaque().equals(opaque)); + } + @Test public void when_send_response__then_message_central_will_send_response() { MessageCentralGetVerticesRequest request = mock(MessageCentralGetVerticesRequest.class); @@ -121,6 +156,17 @@ public void when_send_response__then_message_central_will_send_response() { verify(messageCentral, times(1)).send(eq(peer), any(GetVerticesResponseMessage.class)); } + @Test + public void when_send_error_response__then_message_central_will_send_error_response() { + MessageCentralGetVerticesRequest request = mock(MessageCentralGetVerticesRequest.class); + Peer peer = mock(Peer.class); + when(request.getVertexId()).thenReturn(mock(Hash.class)); + when(request.getRequestor()).thenReturn(peer); + QuorumCertificate qc = mock(QuorumCertificate.class); + network.sendGetVerticesErrorResponse(request, qc, qc); + verify(messageCentral, times(1)).send(eq(peer), any(GetVerticesErrorResponseMessage.class)); + } + @Test public void when_subscribed_to_rpc_requests__then_should_receive_requests() { Hash vertexId0 = mock(Hash.class); From 7e5e7aa0646757b2c6fd3497ce986f1d23eb845b Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Mon, 6 Jul 2020 14:46:02 +0800 Subject: [PATCH 15/20] Add EpochManager unit tests --- .../radixdlt/consensus/EpochManagerTest.java | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java index 6775d0b40..4dba3f629 100644 --- a/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java +++ b/radixdlt/src/test/java/com/radixdlt/consensus/EpochManagerTest.java @@ -28,13 +28,15 @@ import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableSet; +import com.radixdlt.consensus.bft.GetVerticesErrorResponse; import com.radixdlt.consensus.bft.VertexStore; import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; import com.radixdlt.consensus.bft.GetVerticesResponse; +import com.radixdlt.consensus.epoch.GetEpochRequest; +import com.radixdlt.consensus.epoch.GetEpochResponse; import com.radixdlt.consensus.liveness.Pacemaker; import com.radixdlt.consensus.liveness.ProposerElection; import com.radixdlt.consensus.liveness.ScheduledTimeoutSender; -import com.radixdlt.consensus.sync.SyncedRadixEngine; import com.radixdlt.consensus.validators.Validator; import com.radixdlt.consensus.validators.ValidatorSet; import com.radixdlt.counters.SystemCounters; @@ -43,6 +45,7 @@ import com.radixdlt.crypto.ECKeyPair; import com.radixdlt.crypto.ECPublicKey; import com.radixdlt.crypto.Hash; +import com.radixdlt.middleware2.CommittedAtom; import org.junit.Before; import org.junit.Test; @@ -54,6 +57,7 @@ public class EpochManagerTest { private BFTFactory bftFactory; private Pacemaker pacemaker; private SystemCounters systemCounters; + private SyncedStateComputer syncedStateComputer; @Before public void setup() { @@ -70,17 +74,18 @@ public void setup() { this.bftFactory = mock(BFTFactory.class); this.systemCounters = new SystemCountersImpl(); + this.syncedStateComputer = mock(SyncedStateComputer.class); this.epochManager = new EpochManager( - mock(SyncedRadixEngine.class), - syncEpochsRPCSender, + this.syncedStateComputer, + this.syncEpochsRPCSender, mock(ScheduledTimeoutSender.class), timeoutSender -> this.pacemaker, vertexStoreFactory, proposers -> mock(ProposerElection.class), - bftFactory, + this.bftFactory, this.publicKey, - systemCounters + this.systemCounters ); } @@ -110,6 +115,24 @@ public void when_no_epoch_change__then_processing_events_should_not_fail() { epochManager.processConsensusEvent(mock(Vote.class)); } + @Test + public void when_receive_next_epoch_then_epoch_request__then_should_return_current_ancestor() { + VertexMetadata ancestor = VertexMetadata.ofGenesisAncestor(); + ValidatorSet validatorSet = mock(ValidatorSet.class); + epochManager.processEpochChange(new EpochChange(ancestor, validatorSet)); + ECPublicKey sender = mock(ECPublicKey.class); + epochManager.processGetEpochRequest(new GetEpochRequest(sender, ancestor.getEpoch() + 1)); + verify(syncEpochsRPCSender, times(1)).sendGetEpochResponse(eq(sender), eq(ancestor)); + } + + @Test + public void when_receive_epoch_response__then_should_sync_state_computer() { + GetEpochResponse response = mock(GetEpochResponse.class); + when(response.getEpochAncestor()).thenReturn(VertexMetadata.ofGenesisAncestor()); + epochManager.processGetEpochResponse(response); + verify(syncedStateComputer, times(1)).syncTo(eq(VertexMetadata.ofGenesisAncestor()), any(), any()); + } + @Test public void when_receive_next_epoch_events_and_then_epoch_change_and_part_of_validator_set__then_should_execute_queued_epoch_events() { Validator authorValidator = mock(Validator.class); @@ -190,6 +213,10 @@ public void when_next_epoch__then_get_vertices_rpc_should_be_forwarded_to_vertex epochManager.processGetVerticesResponse(getVerticesResponse); verify(vertexStore, times(1)).processGetVerticesResponse(eq(getVerticesResponse)); + GetVerticesErrorResponse getVerticesErrorResponse = mock(GetVerticesErrorResponse.class); + epochManager.processGetVerticesErrorResponse(getVerticesErrorResponse); + verify(vertexStore, times(1)).processGetVerticesErrorResponse(eq(getVerticesErrorResponse)); + CommittedStateSync committedStateSync = mock(CommittedStateSync.class); epochManager.processCommittedStateSync(committedStateSync); verify(vertexStore, times(1)).processCommittedStateSync(eq(committedStateSync)); From 362ed79ebd35e8a0c6ea4f9038ce56a1acabee7a Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 7 Jul 2020 08:07:34 +0800 Subject: [PATCH 16/20] Add javadoc --- .../com/radixdlt/consensus/EmptySyncEpochsRPCSender.java | 3 +++ .../radixdlt/consensus/EmptyVertexStoreEventProcessor.java | 3 +++ .../com/radixdlt/consensus/bft/GetVerticesErrorResponse.java | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java index 44143e896..0b8a043dc 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EmptySyncEpochsRPCSender.java @@ -19,6 +19,9 @@ import com.radixdlt.crypto.ECPublicKey; +/** + * A mocked sync epochs rpc sender + */ public enum EmptySyncEpochsRPCSender implements SyncEpochsRPCSender { INSTANCE; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java b/radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java index 862b6e080..db0606db0 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/EmptyVertexStoreEventProcessor.java @@ -21,6 +21,9 @@ import com.radixdlt.consensus.bft.GetVerticesResponse; import com.radixdlt.consensus.bft.VertexStore.GetVerticesRequest; +/** + * An empty/mocked vertex store event processor + */ public enum EmptyVertexStoreEventProcessor implements VertexStoreEventProcessor { INSTANCE; diff --git a/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java b/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java index b388d3e57..bfb33e694 100644 --- a/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java +++ b/radixdlt/src/main/java/com/radixdlt/consensus/bft/GetVerticesErrorResponse.java @@ -21,7 +21,10 @@ import com.radixdlt.crypto.Hash; import java.util.Objects; -public class GetVerticesErrorResponse { +/** + * An error response to the GetVertices call + */ +public final class GetVerticesErrorResponse { private final Hash vertexId; private final Object opaque; private final QuorumCertificate highestQC; From a68ac68908251bb93f2a7686903aabbcf20397b9 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 7 Jul 2020 16:21:22 +0800 Subject: [PATCH 17/20] Add travis_wait --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d89536318..78a379310 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ jobs: - stage: sonar script: ./gradlew jacocoTestReport sonarqube --info - stage: integration - script: ./gradlew integrationTest + script: travis_wait ./gradlew integrationTest cache: directories: From baeadef2e0fc89945bc949badad48507132f54da Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 7 Jul 2020 17:30:50 +0800 Subject: [PATCH 18/20] Reduce number of steps for Travis --- .../bft/synchronous/OneProposalDropperResponsiveTest.java | 3 ++- .../bft/synchronous/RandomChannelOrderResponsiveTest.java | 5 +++-- .../FProposalDropperRandomSyncResponsiveTest.java | 3 ++- .../OneProposalDropperRandomSyncResponsiveTest.java | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java index f14aee049..24b906b54 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/OneProposalDropperResponsiveTest.java @@ -27,6 +27,7 @@ import org.junit.Test; public class OneProposalDropperResponsiveTest { + private static final int NUM_STEPS = 30000; private final Random random = new Random(123456); @@ -36,7 +37,7 @@ private void runOneProposalDropperResponsiveTest(int numNodes, Function { if (msg instanceof Proposal) { final Proposal proposal = (Proposal) msg; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java index eddbc8578..2138c929e 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/RandomChannelOrderResponsiveTest.java @@ -22,6 +22,7 @@ import org.junit.Test; public class RandomChannelOrderResponsiveTest { + private static final int NUM_STEPS = 30000; @Test public void when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disabled__then_bft_should_be_responsive() { @@ -29,7 +30,7 @@ public void when_run_4_correct_nodes_with_channel_order_random_and_timeouts_disa final DeterministicTest test = DeterministicTest.createAlwaysSyncedBFTTest(4); test.start(); - for (int step = 0; step < 100000; step++) { + for (int step = 0; step < NUM_STEPS; step++) { test.processNextMsg(random); } } @@ -40,7 +41,7 @@ public void when_run_100_correct_nodes_with_channel_order_random_and_timeouts_di final DeterministicTest test = DeterministicTest.createAlwaysSyncedBFTTest(100); test.start(); - for (int step = 0; step < 100000; step++) { + for (int step = 0; step < NUM_STEPS; step++) { test.processNextMsg(random); } } diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java index 40136b0ec..0d0876e95 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/FProposalDropperRandomSyncResponsiveTest.java @@ -32,6 +32,7 @@ import org.junit.Test; public class FProposalDropperRandomSyncResponsiveTest { + private static final int NUM_STEPS = 30000; private final Random random = new Random(123456789); @@ -41,7 +42,7 @@ private void runFProposalDropperResponsiveTest(int numNodes, Function { if (msg instanceof Proposal) { final Proposal proposal = (Proposal) msg; diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java index c2f25b4c0..8cafd5b3a 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/syncedstatecomputer/OneProposalDropperRandomSyncResponsiveTest.java @@ -27,6 +27,7 @@ import org.junit.Test; public class OneProposalDropperRandomSyncResponsiveTest { + private static final int NUM_STEPS = 30000; private final Random random = new Random(123456); @@ -36,7 +37,7 @@ private void runOneProposalDropperResponsiveTest(int numNodes, Function { if (msg instanceof Proposal) { final Proposal proposal = (Proposal) msg; From 086481c8422c152b640e5763fdea900234f60c51 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 7 Jul 2020 17:35:54 +0800 Subject: [PATCH 19/20] Reduce number of steps for Travis --- .../tests/bft/synchronous/FProposalDropperResponsiveTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java index ac657bfb1..1e77a5340 100644 --- a/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java +++ b/radixdlt/src/integration/java/com/radixdlt/consensus/deterministic/tests/bft/synchronous/FProposalDropperResponsiveTest.java @@ -32,6 +32,7 @@ import org.junit.Test; public class FProposalDropperResponsiveTest { + private static final int NUM_STEPS = 30000; private final Random random = new Random(123456789); @@ -41,7 +42,7 @@ private void runFProposalDropperResponsiveTest(int numNodes, Function { if (msg instanceof Proposal) { final Proposal proposal = (Proposal) msg; From 1ebff30a4ff52d9d6e4ded3761dab55a4a591882 Mon Sep 17 00:00:00 2001 From: Joshua Primero Date: Tue, 7 Jul 2020 17:54:00 +0800 Subject: [PATCH 20/20] Remove travis_wait --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 78a379310..d89536318 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ jobs: - stage: sonar script: ./gradlew jacocoTestReport sonarqube --info - stage: integration - script: travis_wait ./gradlew integrationTest + script: ./gradlew integrationTest cache: directories: