From 8b5fada135444b5faa27d7196472ba2540a1f449 Mon Sep 17 00:00:00 2001 From: Trent Mohay <37158202+rain-on@users.noreply.github.com> Date: Wed, 20 Feb 2019 16:55:35 +1100 Subject: [PATCH] Ibft notifies EthPeer when remote node has a better block (#849) There is a failure mode of IBFT whereby a validator fails to import a block, and also fails to receive the NewBlock message from its peers. This means said validator is unable to participate in subsequent rounds, and may cause the network to halt. To overcome this issue, if an IBFT validator receives messages from a future height, it will update the "BestEstimatedHeight" of the corresponding EthPeer object, such that the Synchroniser will (eventually) download the requisite blocks - thus allowing the IBFT network to continue to operate. --- .../ibft/support/RoundSpecificPeers.java | 5 + .../support/StubbedSynchronizerUpdater.java | 39 +++++++ .../ibft/support/TestContextBuilder.java | 18 ++- .../consensus/ibft/support/ValidatorPeer.java | 15 +++ .../ibft/tests/FutureHeightTest.java | 9 ++ .../ibft/EthSynchronizerUpdater.java | 42 +++++++ .../consensus/ibft/SynchronizerUpdater.java | 20 ++++ .../ibft/statemachine/IbftController.java | 17 ++- .../ibft/EthSynchronizerUpdaterTest.java | 103 ++++++++++++++++++ .../ibft/statemachine/IbftControllerTest.java | 5 +- .../ethereum/eth/manager/ChainState.java | 12 +- .../ethereum/eth/manager/EthPeers.java | 2 +- .../controller/IbftPantheonController.java | 13 ++- 13 files changed, 282 insertions(+), 18 deletions(-) create mode 100644 consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedSynchronizerUpdater.java create mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdater.java create mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/SynchronizerUpdater.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdaterTest.java diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java index 671160d98d..6cf725cec0 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java @@ -37,6 +37,7 @@ import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; @@ -124,6 +125,10 @@ public void commitForNonProposing(final ConsensusRoundIdentifier roundId, final nonProposingPeers.forEach(peer -> peer.injectCommit(roundId, hash)); } + public void forNonProposing(final Consumer assertion) { + nonProposingPeers.forEach(assertion); + } + public Collection> createSignedPreparePayloadOfNonProposing( final ConsensusRoundIdentifier preparedRound, final Hash hash) { return nonProposingPeers.stream() diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedSynchronizerUpdater.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedSynchronizerUpdater.java new file mode 100644 index 0000000000..a88e0c66f4 --- /dev/null +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedSynchronizerUpdater.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.consensus.ibft.support; + +import static java.util.function.Function.identity; + +import tech.pegasys.pantheon.consensus.ibft.SynchronizerUpdater; +import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class StubbedSynchronizerUpdater implements SynchronizerUpdater { + + private Map validatorNodes = new HashMap<>(); + + @Override + public void updatePeerChainState( + final long knownBlockNumber, final PeerConnection peerConnection) { + validatorNodes.get(peerConnection).updateEstimatedChainHeight(knownBlockNumber); + } + + public void addNetworkPeers(final Collection nodes) { + validatorNodes.putAll( + nodes.stream().collect(Collectors.toMap(ValidatorPeer::getPeerConnection, identity()))); + } +} diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java index 3e46fe78dc..dfae9f6187 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContextBuilder.java @@ -35,6 +35,7 @@ import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; import tech.pegasys.pantheon.consensus.ibft.IbftProtocolSchedule; import tech.pegasys.pantheon.consensus.ibft.RoundTimer; +import tech.pegasys.pantheon.consensus.ibft.SynchronizerUpdater; import tech.pegasys.pantheon.consensus.ibft.UniqueMessageMulticaster; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory; import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; @@ -164,9 +165,17 @@ public TestContext build() { final Gossiper gossiper = useGossip ? new IbftGossip(uniqueMulticaster) : mock(Gossiper.class); + final StubbedSynchronizerUpdater synchronizerUpdater = new StubbedSynchronizerUpdater(); + final ControllerAndState controllerAndState = createControllerAndFinalState( - blockChain, multicaster, nodeKeys, clock, ibftEventQueue, gossiper); + blockChain, + multicaster, + nodeKeys, + clock, + ibftEventQueue, + gossiper, + synchronizerUpdater); // Add each networkNode to the Multicaster (such that each can receive msgs from local node). // NOTE: the remotePeers needs to be ordered based on Address (as this is used to determine @@ -187,6 +196,7 @@ public TestContext build() { LinkedHashMap::new)); multicaster.addNetworkPeers(remotePeers.values()); + synchronizerUpdater.addNetworkPeers(remotePeers.values()); return new TestContext( remotePeers, @@ -227,7 +237,8 @@ private static ControllerAndState createControllerAndFinalState( final KeyPair nodeKeys, final Clock clock, final IbftEventQueue ibftEventQueue, - final Gossiper gossiper) { + final Gossiper gossiper, + final SynchronizerUpdater synchronizerUpdater) { final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); @@ -312,7 +323,8 @@ private static ControllerAndState createControllerAndFinalState( messageValidatorFactory), messageValidatorFactory), gossiper, - DUPLICATE_MESSAGE_LIMIT); + DUPLICATE_MESSAGE_LIMIT, + synchronizerUpdater); final EventMultiplexer eventMultiplexer = new EventMultiplexer(ibftController); //////////////////////////// END IBFT PantheonController //////////////////////////// diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java index 03d1e29c8b..b178f20f72 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/ValidatorPeer.java @@ -12,6 +12,8 @@ */ package tech.pegasys.pantheon.consensus.ibft.support; +import static org.assertj.core.api.Assertions.assertThat; + import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.EventMultiplexer; import tech.pegasys.pantheon.consensus.ibft.ibftevent.IbftEvents; @@ -53,6 +55,7 @@ public class ValidatorPeer { private final List receivedMessages = Lists.newArrayList(); private final EventMultiplexer localEventMultiplexer; + private long estimatedChainHeight = 0; public ValidatorPeer( final NodeParams nodeParams, @@ -74,6 +77,10 @@ public KeyPair getNodeKeys() { return nodeKeys; } + public PeerConnection getPeerConnection() { + return peerConnection; + } + public Proposal injectProposal(final ConsensusRoundIdentifier rId, final Block block) { final Proposal payload = messageFactory.createProposal(rId, block, Optional.empty()); @@ -147,4 +154,12 @@ public MessageFactory getMessageFactory() { public KeyPair getNodeKeyPair() { return nodeKeys; } + + public void updateEstimatedChainHeight(final long estimatedChainHeight) { + this.estimatedChainHeight = estimatedChainHeight; + } + + public void verifyEstimatedChainHeightEquals(final long expectedChainHeight) { + assertThat(estimatedChainHeight).isEqualTo(expectedChainHeight); + } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java index 24982190b4..d02d30f823 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureHeightTest.java @@ -74,8 +74,17 @@ public void messagesForFutureHeightAreBufferedUntilChainHeightCatchesUp() { peers.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); peers.verifyNoMessagesReceived(); + // verify that we have incremented the estimated height of the proposer. + peers + .getProposer() + .verifyEstimatedChainHeightEquals(futureHeightBlock.getHeader().getNumber() - 1); + // Inject prepares and commits from all peers peers.prepareForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); + peers.forNonProposing( + peer -> + peer.verifyEstimatedChainHeightEquals(futureHeightBlock.getHeader().getNumber() - 1)); + peers.commitForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); peers.verifyNoMessagesReceived(); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdater.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdater.java new file mode 100644 index 0000000000..022f22f4cf --- /dev/null +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdater.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.consensus.ibft; + +import static org.apache.logging.log4j.LogManager.getLogger; + +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeers; +import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; + +import org.apache.logging.log4j.Logger; + +public class EthSynchronizerUpdater implements SynchronizerUpdater { + + private static final Logger LOG = getLogger(); + private final EthPeers ethPeers; + + public EthSynchronizerUpdater(final EthPeers ethPeers) { + this.ethPeers = ethPeers; + } + + @Override + public void updatePeerChainState( + final long knownBlockNumber, final PeerConnection peerConnection) { + final EthPeer ethPeer = ethPeers.peer(peerConnection); + if (ethPeer == null) { + LOG.debug("Received message from a peer with no corresponding EthPeer."); + return; + } + ethPeer.chainState().updateHeightEstimate(knownBlockNumber); + } +} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/SynchronizerUpdater.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/SynchronizerUpdater.java new file mode 100644 index 0000000000..aa8ebe39d6 --- /dev/null +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/SynchronizerUpdater.java @@ -0,0 +1,20 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.consensus.ibft; + +import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; + +public interface SynchronizerUpdater { + + void updatePeerChainState(long knownBlockNumber, PeerConnection peerConnection); +} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java index 6433ce38fd..c9a7786227 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftController.java @@ -17,6 +17,7 @@ import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.Gossiper; import tech.pegasys.pantheon.consensus.ibft.MessageTracker; +import tech.pegasys.pantheon.consensus.ibft.SynchronizerUpdater; import tech.pegasys.pantheon.consensus.ibft.ibftevent.BlockTimerExpiry; import tech.pegasys.pantheon.consensus.ibft.ibftevent.IbftReceivedMessageEvent; import tech.pegasys.pantheon.consensus.ibft.ibftevent.NewChainHead; @@ -44,6 +45,7 @@ import org.apache.logging.log4j.Logger; public class IbftController { + private static final Logger LOG = LogManager.getLogger(); private final Blockchain blockchain; private final IbftFinalState ibftFinalState; @@ -52,20 +54,23 @@ public class IbftController { private BlockHeightManager currentHeightManager; private final Gossiper gossiper; private final MessageTracker duplicateMessageTracker; + private final SynchronizerUpdater sychronizerUpdater; public IbftController( final Blockchain blockchain, final IbftFinalState ibftFinalState, final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory, final Gossiper gossiper, - final int duplicateMessageLimit) { + final int duplicateMessageLimit, + final SynchronizerUpdater sychronizerUpdater) { this( blockchain, ibftFinalState, ibftBlockHeightManagerFactory, gossiper, Maps.newHashMap(), - new MessageTracker(duplicateMessageLimit)); + new MessageTracker(duplicateMessageLimit), + sychronizerUpdater); } @VisibleForTesting @@ -75,13 +80,15 @@ public IbftController( final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory, final Gossiper gossiper, final Map> futureMessages, - final MessageTracker duplicateMessageTracker) { + final MessageTracker duplicateMessageTracker, + final SynchronizerUpdater sychronizerUpdater) { this.blockchain = blockchain; this.ibftFinalState = ibftFinalState; this.ibftBlockHeightManagerFactory = ibftBlockHeightManagerFactory; this.futureMessages = futureMessages; this.gossiper = gossiper; this.duplicateMessageTracker = duplicateMessageTracker; + this.sychronizerUpdater = sychronizerUpdater; } public void start() { @@ -213,6 +220,10 @@ private boolean processMessage(final IbftMessage msg, final Message rawMsg) { return isMsgFromKnownValidator(msg) && ibftFinalState.isLocalNodeValidator(); } else if (isMsgForFutureChainHeight(msgRoundIdentifier)) { addMessageToFutureMessageBuffer(msgRoundIdentifier.getSequenceNumber(), rawMsg); + // Notify the synchronizer the transmitting peer must have the parent block to the received + // message's target height. + sychronizerUpdater.updatePeerChainState( + msgRoundIdentifier.getSequenceNumber() - 1L, rawMsg.getConnection()); } else { LOG.debug( "IBFT message discarded as it is from a previous block height messageType={} chainHeight={} eventHeight={}", diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdaterTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdaterTest.java new file mode 100644 index 0000000000..730a17e4b3 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/EthSynchronizerUpdaterTest.java @@ -0,0 +1,103 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.consensus.ibft; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import tech.pegasys.pantheon.ethereum.eth.manager.ChainState; +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeers; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; +import tech.pegasys.pantheon.ethereum.p2p.api.PeerConnection; +import tech.pegasys.pantheon.ethereum.p2p.wire.Capability; +import tech.pegasys.pantheon.ethereum.p2p.wire.PeerInfo; +import tech.pegasys.pantheon.ethereum.p2p.wire.messages.DisconnectMessage.DisconnectReason; + +import java.net.SocketAddress; +import java.util.Set; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class EthSynchronizerUpdaterTest { + + @Mock private EthPeers ethPeers; + @Mock private EthPeer ethPeer; + + @Mock private ChainState chainState; + + @Test + public void ethPeerIsMissingResultInNoUpdate() { + when(ethPeers.peer(any())).thenReturn(null); + + final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers); + + updater.updatePeerChainState(1, createAnonymousPeerConnection()); + + verifyZeroInteractions(ethPeer); + } + + @Test + public void chainStateUpdateIsAttemptedIfEthPeerExists() { + when(ethPeers.peer(any())).thenReturn(ethPeer); + when(ethPeer.chainState()).thenReturn(chainState); + + final EthSynchronizerUpdater updater = new EthSynchronizerUpdater(ethPeers); + + final long suppliedChainHeight = 6L; + updater.updatePeerChainState(suppliedChainHeight, createAnonymousPeerConnection()); + verify(chainState, times(1)).updateHeightEstimate(eq(suppliedChainHeight)); + } + + private PeerConnection createAnonymousPeerConnection() { + return new PeerConnection() { + @Override + public void send(final Capability capability, final MessageData message) + throws PeerNotConnected {} + + @Override + public Set getAgreedCapabilities() { + return null; + } + + @Override + public PeerInfo getPeer() { + return new PeerInfo(0, null, null, 0, null); + } + + @Override + public void terminateConnection(final DisconnectReason reason, final boolean peerInitiated) {} + + @Override + public void disconnect(final DisconnectReason reason) {} + + @Override + public SocketAddress getLocalAddress() { + return null; + } + + @Override + public SocketAddress getRemoteAddress() { + return null; + } + }; + } +} diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java index 470d95da81..18df8689a9 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftControllerTest.java @@ -16,6 +16,7 @@ import static org.assertj.core.util.Lists.newArrayList; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -23,6 +24,7 @@ import static org.mockito.Mockito.when; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; +import tech.pegasys.pantheon.consensus.ibft.EthSynchronizerUpdater; import tech.pegasys.pantheon.consensus.ibft.IbftGossip; import tech.pegasys.pantheon.consensus.ibft.MessageTracker; import tech.pegasys.pantheon.consensus.ibft.ibftevent.BlockTimerExpiry; @@ -103,7 +105,8 @@ public void setup() { blockHeightManagerFactory, ibftGossip, futureMessages, - messageTracker); + messageTracker, + mock(EthSynchronizerUpdater.class)); when(chainHeadBlockHeader.getNumber()).thenReturn(1L); when(chainHeadBlockHeader.getHash()).thenReturn(Hash.ZERO); diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/ChainState.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/ChainState.java index 05168036fd..b25428b525 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/ChainState.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/ChainState.java @@ -88,11 +88,13 @@ public void updateForAnnouncedBlock( } } - private void updateHeightEstimate(final long blockNumber) { - estimatedHeightKnown = true; - if (blockNumber > estimatedHeight) { - estimatedHeight = blockNumber; - estimatedHeightListeners.forEach(e -> e.onEstimatedHeightChanged(estimatedHeight)); + public void updateHeightEstimate(final long blockNumber) { + synchronized (this) { + estimatedHeightKnown = true; + if (blockNumber > estimatedHeight) { + estimatedHeight = blockNumber; + estimatedHeightListeners.forEach(e -> e.onEstimatedHeightChanged(estimatedHeight)); + } } } diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/EthPeers.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/EthPeers.java index 7f44a56e3c..4992004a88 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/EthPeers.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/manager/EthPeers.java @@ -64,7 +64,7 @@ void registerDisconnect(final PeerConnection connection) { } } - EthPeer peer(final PeerConnection peerConnection) { + public EthPeer peer(final PeerConnection peerConnection) { return connections.get(peerConnection); } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java index 1c33574f0c..8dd931a462 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java @@ -22,6 +22,7 @@ import tech.pegasys.pantheon.consensus.common.VoteTallyCache; import tech.pegasys.pantheon.consensus.common.VoteTallyUpdater; import tech.pegasys.pantheon.consensus.ibft.BlockTimer; +import tech.pegasys.pantheon.consensus.ibft.EthSynchronizerUpdater; import tech.pegasys.pantheon.consensus.ibft.EventMultiplexer; import tech.pegasys.pantheon.consensus.ibft.IbftBlockInterface; import tech.pegasys.pantheon.consensus.ibft.IbftContext; @@ -56,6 +57,7 @@ import tech.pegasys.pantheon.ethereum.core.TransactionPool; import tech.pegasys.pantheon.ethereum.core.Util; import tech.pegasys.pantheon.ethereum.eth.EthProtocol; +import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManager; import tech.pegasys.pantheon.ethereum.eth.sync.DefaultSynchronizer; import tech.pegasys.pantheon.ethereum.eth.sync.SyncMode; @@ -168,9 +170,10 @@ public static PantheonController init( metricsSystem); final SubProtocol ethSubProtocol = EthProtocol.get(); + final EthContext ethContext = ethProtocolManager.ethContext(); + final SyncState syncState = - new SyncState( - protocolContext.getBlockchain(), ethProtocolManager.ethContext().getEthPeers()); + new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers()); final Synchronizer synchronizer = new DefaultSynchronizer<>( syncConfig, @@ -183,8 +186,7 @@ public static PantheonController init( metricsSystem); final TransactionPool transactionPool = - TransactionPoolFactory.createTransactionPool( - protocolSchedule, protocolContext, ethProtocolManager.ethContext()); + TransactionPoolFactory.createTransactionPool(protocolSchedule, protocolContext, ethContext); final IbftEventQueue ibftEventQueue = new IbftEventQueue(ibftConfig.getMessageQueueLimit()); @@ -250,7 +252,8 @@ public static PantheonController init( messageValidatorFactory), messageValidatorFactory), gossiper, - ibftConfig.getDuplicateMessageLimit()); + ibftConfig.getDuplicateMessageLimit(), + new EthSynchronizerUpdater(ethContext.getEthPeers())); ibftController.start(); final EventMultiplexer eventMultiplexer = new EventMultiplexer(ibftController);