From f1116a0009198554bd4ae04c860333cb5d33c685 Mon Sep 17 00:00:00 2001 From: tmohay Date: Thu, 24 Jan 2019 21:33:07 +1100 Subject: [PATCH 01/31] Ibft transmitted packets are logged by gossiper Messages which originate with the current node are logged in the gossiper such that if a remote peer sends a packet which originated from the local back to the local node, it should not go back out again. --- .../pantheon/consensus/ibft/IbftGossip.java | 12 ++++++++++-- .../ibft/network/IbftMessageTransmitter.java | 19 +++++++++++-------- .../ibft/statemachine/IbftFinalState.java | 4 +++- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java index bccd9da18b..ea9c72b0f1 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java @@ -35,6 +35,7 @@ /** Class responsible for rebroadcasting IBFT messages to known validators */ public class IbftGossip { + private final ValidatorMulticaster multicaster; // Size of the seenMessages cache, should end up utilising 65bytes * this number + some meta data @@ -100,9 +101,16 @@ public boolean gossipMessage(final Message message) { final List
excludeAddressesList = Lists.newArrayList( message.getConnection().getPeer().getAddress(), signedData.getSender()); - multicaster.send(messageData, excludeAddressesList); - seenMessages.add(signature); + transmit(messageData, signature, excludeAddressesList); return true; } } + + public void transmit( + final MessageData messageData, + final Signature signature, + final List
excludeAddressesList) { + multicaster.send(messageData, excludeAddressesList); + seenMessages.add(signature); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java index 55cc1cec68..9a1fca23c6 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java @@ -12,7 +12,10 @@ */ package tech.pegasys.pantheon.consensus.ibft.network; +import static java.util.Collections.emptyList; + import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; +import tech.pegasys.pantheon.consensus.ibft.IbftGossip; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; @@ -36,12 +39,12 @@ public class IbftMessageTransmitter { private final MessageFactory messageFactory; - private final ValidatorMulticaster multicaster; + private final IbftGossip networkInterface; public IbftMessageTransmitter( - final MessageFactory messageFactory, final ValidatorMulticaster multicaster) { + final MessageFactory messageFactory, final IbftGossip networkInterface) { this.messageFactory = messageFactory; - this.multicaster = multicaster; + this.networkInterface = networkInterface; } public void multicastProposal(final ConsensusRoundIdentifier roundIdentifier, final Block block) { @@ -50,7 +53,7 @@ public void multicastProposal(final ConsensusRoundIdentifier roundIdentifier, fi final ProposalMessageData message = ProposalMessageData.create(signedPayload); - multicaster.send(message); + networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); } public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { @@ -59,7 +62,7 @@ public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, fin final PrepareMessageData message = PrepareMessageData.create(signedPayload); - multicaster.send(message); + networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); } public void multicastCommit( @@ -71,7 +74,7 @@ public void multicastCommit( final CommitMessageData message = CommitMessageData.create(signedPayload); - multicaster.send(message); + networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); } public void multicastRoundChange( @@ -83,7 +86,7 @@ public void multicastRoundChange( final RoundChangeMessageData message = RoundChangeMessageData.create(signedPayload); - multicaster.send(message); + networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); } public void multicastNewRound( @@ -97,6 +100,6 @@ public void multicastNewRound( final NewRoundMessageData message = NewRoundMessageData.create(signedPayload); - multicaster.send(message); + networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java index 433225157a..bd795f375e 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java @@ -17,6 +17,7 @@ import tech.pegasys.pantheon.consensus.common.ValidatorProvider; import tech.pegasys.pantheon.consensus.ibft.BlockTimer; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; +import tech.pegasys.pantheon.consensus.ibft.IbftGossip; import tech.pegasys.pantheon.consensus.ibft.RoundTimer; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory; import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; @@ -64,7 +65,8 @@ public IbftFinalState( this.blockCreatorFactory = blockCreatorFactory; this.messageFactory = messageFactory; this.clock = clock; - this.messageTransmitter = new IbftMessageTransmitter(messageFactory, validatorMulticaster); + this.messageTransmitter = + new IbftMessageTransmitter(messageFactory, new IbftGossip(validatorMulticaster)); } public int getQuorum() { From f52c6160caad6b37a0eb7296b585d884a2826b86 Mon Sep 17 00:00:00 2001 From: tmohay Date: Fri, 25 Jan 2019 09:38:07 +1100 Subject: [PATCH 02/31] touch ups after review --- .../ibft/support/StubbedGossiper.java | 41 +++++++++++++++++++ .../ibft/support/TestContextBuilder.java | 18 ++++---- .../pantheon/consensus/ibft/Gossiper.java | 26 ++++++++++++ .../pantheon/consensus/ibft/IbftGossip.java | 38 ++++++++--------- .../ibft/network/IbftMessageTransmitter.java | 19 ++++----- .../ibft/statemachine/IbftController.java | 17 ++++---- .../ibft/statemachine/IbftFinalState.java | 8 ++-- .../ibft/statemachine/IbftControllerTest.java | 2 +- .../controller/IbftPantheonController.java | 9 +++- 9 files changed, 123 insertions(+), 55 deletions(-) create mode 100644 consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java create mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java new file mode 100644 index 0000000000..2740698f34 --- /dev/null +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java @@ -0,0 +1,41 @@ +/* + * 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 tech.pegasys.pantheon.consensus.ibft.Gossiper; +import tech.pegasys.pantheon.consensus.ibft.network.ValidatorMulticaster; +import tech.pegasys.pantheon.ethereum.core.Address; +import tech.pegasys.pantheon.ethereum.p2p.api.Message; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; + +import java.util.List; + +public class StubbedGossiper implements Gossiper { + + private final ValidatorMulticaster multicaster; + + StubbedGossiper(final ValidatorMulticaster multicaster) { + this.multicaster = multicaster; + } + + @Override + public boolean gossipMessage(final Message message) { + return false; + } + + @Override + public boolean send(final MessageData messageData, final List
excludeAddressesList) { + multicaster.send(messageData, excludeAddressesList); + return true; + } +} 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 03f0e6535b..5b7b2e6232 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 @@ -13,7 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.support; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.mockito.Mockito.mock; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; @@ -25,6 +24,7 @@ import tech.pegasys.pantheon.consensus.common.VoteTallyUpdater; import tech.pegasys.pantheon.consensus.ibft.BlockTimer; import tech.pegasys.pantheon.consensus.ibft.EventMultiplexer; +import tech.pegasys.pantheon.consensus.ibft.Gossiper; import tech.pegasys.pantheon.consensus.ibft.IbftBlockHashing; import tech.pegasys.pantheon.consensus.ibft.IbftBlockInterface; import tech.pegasys.pantheon.consensus.ibft.IbftContext; @@ -156,7 +156,8 @@ public TestContext build() { // Use a stubbed version of the multicaster, to prevent creating PeerConnections etc. final StubValidatorMulticaster multicaster = new StubValidatorMulticaster(); - final IbftGossip gossiper = useGossip ? new IbftGossip(multicaster) : mock(IbftGossip.class); + final Gossiper gossiper = + useGossip ? new IbftGossip(multicaster) : new StubbedGossiper(multicaster); final ControllerAndState controllerAndState = createControllerAndFinalState( @@ -219,11 +220,11 @@ private static Block createGenesisBlock(final Set
validators) { private static ControllerAndState createControllerAndFinalState( final MutableBlockchain blockChain, - final StubValidatorMulticaster stubbedMulticaster, + final StubValidatorMulticaster multicaster, final KeyPair nodeKeys, final Clock clock, final IbftEventQueue ibftEventQueue, - final IbftGossip gossiper) { + final Gossiper gossiper) { final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); @@ -272,7 +273,7 @@ private static ControllerAndState createControllerAndFinalState( nodeKeys, Util.publicKeyToAddress(nodeKeys.getPublicKey()), proposerSelector, - stubbedMulticaster, + multicaster, new RoundTimer( ibftEventQueue, ROUND_TIMER_SEC * 1000, Executors.newScheduledThreadPool(1)), new BlockTimer( @@ -282,7 +283,8 @@ private static ControllerAndState createControllerAndFinalState( Clock.systemUTC()), blockCreatorFactory, new MessageFactory(nodeKeys), - clock); + clock, + gossiper); final MessageValidatorFactory messageValidatorFactory = new MessageValidatorFactory(proposerSelector, protocolSchedule, protocolContext); @@ -298,8 +300,8 @@ private static ControllerAndState createControllerAndFinalState( new IbftRoundFactory( finalState, protocolContext, protocolSchedule, minedBlockObservers), messageValidatorFactory), - new HashMap<>(), - gossiper); + gossiper, + new HashMap<>()); final EventMultiplexer eventMultiplexer = new EventMultiplexer(ibftController); //////////////////////////// END IBFT PantheonController //////////////////////////// diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java new file mode 100644 index 0000000000..272419b47b --- /dev/null +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java @@ -0,0 +1,26 @@ +/* + * 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.core.Address; +import tech.pegasys.pantheon.ethereum.p2p.api.Message; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; + +import java.util.List; + +public interface Gossiper { + + boolean gossipMessage(Message message); + + boolean send(MessageData messageData, List
excludeAddressesList); +} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java index ea9c72b0f1..f67b529fbb 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java @@ -20,8 +20,8 @@ import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; import tech.pegasys.pantheon.consensus.ibft.network.ValidatorMulticaster; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.crypto.SECP256K1.Signature; import tech.pegasys.pantheon.ethereum.core.Address; +import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.p2p.api.Message; import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; @@ -34,7 +34,7 @@ import com.google.common.collect.Lists; /** Class responsible for rebroadcasting IBFT messages to known validators */ -public class IbftGossip { +public class IbftGossip implements Gossiper { private final ValidatorMulticaster multicaster; @@ -42,11 +42,11 @@ public class IbftGossip { private final int maxSeenMessages; // Set that starts evicting members when it hits capacity - private final Set seenMessages = + private final Set seenMessages = Collections.newSetFromMap( - new LinkedHashMap() { + new LinkedHashMap() { @Override - protected boolean removeEldestEntry(final Map.Entry eldest) { + protected boolean removeEldestEntry(final Map.Entry eldest) { return size() > maxSeenMessages; } }); @@ -71,6 +71,7 @@ public IbftGossip(final ValidatorMulticaster multicaster) { * @param message The raw message to be gossiped * @return Whether the message was rebroadcast or has been ignored as a repeat */ + @Override public boolean gossipMessage(final Message message) { final MessageData messageData = message.getData(); final SignedData signedData; @@ -94,23 +95,20 @@ public boolean gossipMessage(final Message message) { throw new IllegalArgumentException( "Received message does not conform to any recognised IBFT message structure."); } - final Signature signature = signedData.getSignature(); - if (seenMessages.contains(signature)) { - return false; - } else { - final List
excludeAddressesList = - Lists.newArrayList( - message.getConnection().getPeer().getAddress(), signedData.getSender()); - transmit(messageData, signature, excludeAddressesList); - return true; - } + final List
excludeAddressesList = + Lists.newArrayList(message.getConnection().getPeer().getAddress(), signedData.getSender()); + + return send(messageData, excludeAddressesList); } - public void transmit( - final MessageData messageData, - final Signature signature, - final List
excludeAddressesList) { + @Override + public boolean send(final MessageData messageData, final List
excludeAddressesList) { + Hash uniqueID = Hash.hash(messageData.getData()); + if (seenMessages.contains(uniqueID)) { + return false; + } multicaster.send(messageData, excludeAddressesList); - seenMessages.add(signature); + seenMessages.add(uniqueID); + return true; } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java index 9a1fca23c6..f3c0434c1b 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java @@ -15,7 +15,7 @@ import static java.util.Collections.emptyList; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.IbftGossip; +import tech.pegasys.pantheon.consensus.ibft.Gossiper; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; @@ -39,12 +39,11 @@ public class IbftMessageTransmitter { private final MessageFactory messageFactory; - private final IbftGossip networkInterface; + private final Gossiper network; - public IbftMessageTransmitter( - final MessageFactory messageFactory, final IbftGossip networkInterface) { + public IbftMessageTransmitter(final MessageFactory messageFactory, final Gossiper network) { this.messageFactory = messageFactory; - this.networkInterface = networkInterface; + this.network = network; } public void multicastProposal(final ConsensusRoundIdentifier roundIdentifier, final Block block) { @@ -53,7 +52,7 @@ public void multicastProposal(final ConsensusRoundIdentifier roundIdentifier, fi final ProposalMessageData message = ProposalMessageData.create(signedPayload); - networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); + network.send(message, emptyList()); } public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { @@ -62,7 +61,7 @@ public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, fin final PrepareMessageData message = PrepareMessageData.create(signedPayload); - networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); + network.send(message, emptyList()); } public void multicastCommit( @@ -74,7 +73,7 @@ public void multicastCommit( final CommitMessageData message = CommitMessageData.create(signedPayload); - networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); + network.send(message, emptyList()); } public void multicastRoundChange( @@ -86,7 +85,7 @@ public void multicastRoundChange( final RoundChangeMessageData message = RoundChangeMessageData.create(signedPayload); - networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); + network.send(message, emptyList()); } public void multicastNewRound( @@ -100,6 +99,6 @@ public void multicastNewRound( final NewRoundMessageData message = NewRoundMessageData.create(signedPayload); - networkInterface.transmit(message, signedPayload.getSignature(), emptyList()); + network.send(message, emptyList()); } } 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 749feb6171..9b8f8de299 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 @@ -15,6 +15,7 @@ import static java.util.Collections.emptyList; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; +import tech.pegasys.pantheon.consensus.ibft.Gossiper; import tech.pegasys.pantheon.consensus.ibft.IbftGossip; import tech.pegasys.pantheon.consensus.ibft.ibftevent.BlockTimerExpiry; import tech.pegasys.pantheon.consensus.ibft.ibftevent.IbftReceivedMessageEvent; @@ -51,18 +52,14 @@ public class IbftController { private final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory; private final Map> futureMessages; private BlockHeightManager currentHeightManager; - private final IbftGossip gossiper; + private final Gossiper gossiper; public IbftController( final Blockchain blockchain, final IbftFinalState ibftFinalState, - final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory) { - this( - blockchain, - ibftFinalState, - ibftBlockHeightManagerFactory, - Maps.newHashMap(), - new IbftGossip(ibftFinalState.getValidatorMulticaster())); + final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory, + final IbftGossip gossiper) { + this(blockchain, ibftFinalState, ibftBlockHeightManagerFactory, gossiper, Maps.newHashMap()); } @VisibleForTesting @@ -70,8 +67,8 @@ public IbftController( final Blockchain blockchain, final IbftFinalState ibftFinalState, final IbftBlockHeightManagerFactory ibftBlockHeightManagerFactory, - final Map> futureMessages, - final IbftGossip gossiper) { + final Gossiper gossiper, + final Map> futureMessages) { this.blockchain = blockchain; this.ibftFinalState = ibftFinalState; this.ibftBlockHeightManagerFactory = ibftBlockHeightManagerFactory; diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java index bd795f375e..b323a84391 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java @@ -17,7 +17,7 @@ import tech.pegasys.pantheon.consensus.common.ValidatorProvider; import tech.pegasys.pantheon.consensus.ibft.BlockTimer; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.IbftGossip; +import tech.pegasys.pantheon.consensus.ibft.Gossiper; import tech.pegasys.pantheon.consensus.ibft.RoundTimer; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory; import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; @@ -54,7 +54,8 @@ public IbftFinalState( final BlockTimer blockTimer, final IbftBlockCreatorFactory blockCreatorFactory, final MessageFactory messageFactory, - final Clock clock) { + final Clock clock, + final Gossiper gossiper) { this.validatorProvider = validatorProvider; this.nodeKeys = nodeKeys; this.localAddress = localAddress; @@ -65,8 +66,7 @@ public IbftFinalState( this.blockCreatorFactory = blockCreatorFactory; this.messageFactory = messageFactory; this.clock = clock; - this.messageTransmitter = - new IbftMessageTransmitter(messageFactory, new IbftGossip(validatorMulticaster)); + this.messageTransmitter = new IbftMessageTransmitter(messageFactory, gossiper); } public int getQuorum() { 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 9beaa38d9b..3fcc991eb4 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 @@ -108,7 +108,7 @@ public void setup() { when(ibftFinalState.getValidators()).thenReturn(ImmutableList.of(validator)); ibftController = new IbftController( - blockChain, ibftFinalState, blockHeightManagerFactory, futureMessages, ibftGossip); + blockChain, ibftFinalState, blockHeightManagerFactory, ibftGossip, futureMessages); when(chainHeadBlockHeader.getNumber()).thenReturn(1L); when(chainHeadBlockHeader.getHash()).thenReturn(Hash.ZERO); 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 90057534c6..364d8b3a25 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java @@ -26,6 +26,7 @@ import tech.pegasys.pantheon.consensus.ibft.IbftBlockInterface; import tech.pegasys.pantheon.consensus.ibft.IbftContext; import tech.pegasys.pantheon.consensus.ibft.IbftEventQueue; +import tech.pegasys.pantheon.consensus.ibft.IbftGossip; import tech.pegasys.pantheon.consensus.ibft.IbftProcessor; import tech.pegasys.pantheon.consensus.ibft.IbftProtocolSchedule; import tech.pegasys.pantheon.consensus.ibft.RoundTimer; @@ -205,6 +206,8 @@ public static PantheonController init( final Subscribers minedBlockObservers = new Subscribers<>(); minedBlockObservers.subscribe(ethProtocolManager); + final IbftGossip gossiper = new IbftGossip(peers); + final IbftFinalState finalState = new IbftFinalState( voteTally, @@ -223,7 +226,8 @@ public static PantheonController init( Clock.systemUTC()), blockCreatorFactory, new MessageFactory(nodeKeys), - Clock.systemUTC()); + Clock.systemUTC(), + gossiper); final MessageValidatorFactory messageValidatorFactory = new MessageValidatorFactory(proposerSelector, protocolSchedule, protocolContext); @@ -236,7 +240,8 @@ public static PantheonController init( finalState, new IbftRoundFactory( finalState, protocolContext, protocolSchedule, minedBlockObservers), - messageValidatorFactory)); + messageValidatorFactory), + gossiper); ibftController.start(); final EventMultiplexer eventMultiplexer = new EventMultiplexer(ibftController); From cbd5a4b6d02cfbf737b1772cf13749c202dc3888 Mon Sep 17 00:00:00 2001 From: Chris Mckay Date: Fri, 25 Jan 2019 08:59:41 +1000 Subject: [PATCH 03/31] [NC-1970] Added handling in admin_addPeer for when p2p is disabled (#655) --- .../internal/methods/AdminAddPeer.java | 3 ++ .../internal/methods/AdminAddPeerTest.java | 28 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java index 75e43de590..8f626ee154 100644 --- a/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java +++ b/ethereum/jsonrpc/src/main/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeer.java @@ -19,6 +19,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.p2p.P2pDisabledException; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import tech.pegasys.pantheon.ethereum.p2p.peers.DefaultPeer; import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; @@ -52,6 +53,8 @@ public JsonRpcResponse response(final JsonRpcRequest req) { return new JsonRpcErrorResponse(req.getId(), JsonRpcError.INVALID_PARAMS); } catch (final IllegalArgumentException e) { return new JsonRpcErrorResponse(req.getId(), JsonRpcError.PARSE_ERROR); + } catch (final P2pDisabledException e) { + return new JsonRpcErrorResponse(req.getId(), JsonRpcError.P2P_DISABLED); } catch (final Exception e) { LOG.error("Error processing request: " + req, e); throw e; diff --git a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java index 0b0c654244..cb471f1adc 100644 --- a/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java +++ b/ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/AdminAddPeerTest.java @@ -22,6 +22,7 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcErrorResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import tech.pegasys.pantheon.ethereum.p2p.P2pDisabledException; import tech.pegasys.pantheon.ethereum.p2p.api.P2PNetwork; import org.junit.Before; @@ -113,4 +114,31 @@ public void requestReturnsFalseIfAddFails() { assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } + + @Test + public void requestReturnsErrorWhenP2pDisabled() { + when(p2pNetwork.addMaintainConnectionPeer(any())) + .thenThrow( + new P2pDisabledException("P2P networking disabled. Unable to connect to add peer.")); + + final JsonRpcRequest request = + new JsonRpcRequest( + "2.0", + "admin_addPeer", + new String[] { + "enode://" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "@127.0.0.1:30303" + }); + + final JsonRpcResponse expectedResponse = + new JsonRpcErrorResponse(request.getId(), JsonRpcError.P2P_DISABLED); + + final JsonRpcResponse actualResponse = method.response(request); + + assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + } } From cbd8f7d03817c88a7b9daebf0fa65db365e606d3 Mon Sep 17 00:00:00 2001 From: CJ Hare Date: Fri, 25 Jan 2019 09:25:52 +1000 Subject: [PATCH 04/31] Refactoring for more readable IBFT IT (#614) * Refactoring for more readable IBFT IT * Renaming Roles to peers * Moving the assert behaviour into the RoundChangePeers * Renmaing prefix of assert to verify, grammar * Reducing usage of getAllPeers() * Dropping the getter for the peer list * Dropping peer from method names, as it's now in the class name * Spotless --- .../ibft/support/MessageReceptionHelpers.java | 86 ------- .../ibft/support/RoundSpecificNodeRoles.java | 49 ---- .../ibft/support/RoundSpecificPeers.java | 219 ++++++++++++++++++ .../consensus/ibft/support/TestContext.java | 4 +- .../consensus/ibft/support/TestHelpers.java | 25 +- .../ibft/tests/FutureHeightTest.java | 105 +++------ .../consensus/ibft/tests/FutureRoundTest.java | 59 ++--- .../consensus/ibft/tests/GossipTest.java | 69 +++--- .../ibft/tests/LocalNodeIsProposerTest.java | 38 ++- .../ibft/tests/LocalNodeNotProposerTest.java | 96 ++++---- .../ibft/tests/ReceivedNewRoundTest.java | 99 +++----- .../consensus/ibft/tests/RoundChangeTest.java | 114 ++++----- .../ibft/tests/SpuriousBehaviourTest.java | 59 ++--- 13 files changed, 484 insertions(+), 538 deletions(-) delete mode 100644 consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java delete mode 100644 consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java create mode 100644 consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java deleted file mode 100644 index 6410bff73b..0000000000 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/MessageReceptionHelpers.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Fail.fail; - -import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; -import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; -import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; -import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; -import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; -import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; -import tech.pegasys.pantheon.consensus.ibft.payload.Payload; -import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -public class MessageReceptionHelpers { - - public static void assertPeersReceivedNoMessages(final Collection nodes) { - nodes.forEach(n -> assertThat(n.getReceivedMessages()).isEmpty()); - } - - @SafeVarargs - public static void assertPeersReceivedExactly( - final Collection allPeers, final SignedData... msgs) { - allPeers.forEach(n -> assertThat(n.getReceivedMessages().size()).isEqualTo(msgs.length)); - - List> msgList = Arrays.asList(msgs); - - for (int i = 0; i < msgList.size(); i++) { - final int index = i; - final SignedData msg = msgList.get(index); - allPeers.forEach( - n -> { - final List rxMsgs = n.getReceivedMessages(); - final MessageData rxMsgData = rxMsgs.get(index); - messageMatchesExpected(rxMsgData, msg); - }); - } - allPeers.forEach(ValidatorPeer::clearReceivedMessages); - } - - public static void messageMatchesExpected( - final MessageData actual, final SignedData signedExpectedPayload) { - final Payload expectedPayload = signedExpectedPayload.getPayload(); - SignedData actualSignedPayload = null; - - switch (expectedPayload.getMessageType()) { - case IbftV2.PROPOSAL: - actualSignedPayload = ProposalMessageData.fromMessageData(actual).decode(); - break; - case IbftV2.PREPARE: - actualSignedPayload = PrepareMessageData.fromMessageData(actual).decode(); - break; - case IbftV2.COMMIT: - actualSignedPayload = CommitMessageData.fromMessageData(actual).decode(); - break; - case IbftV2.NEW_ROUND: - actualSignedPayload = NewRoundMessageData.fromMessageData(actual).decode(); - break; - case IbftV2.ROUND_CHANGE: - actualSignedPayload = RoundChangeMessageData.fromMessageData(actual).decode(); - break; - default: - fail("Illegal IBFTV2 message type."); - break; - } - assertThat(signedExpectedPayload) - .isEqualToComparingFieldByFieldRecursively(actualSignedPayload); - } -} diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java deleted file mode 100644 index 5614b35448..0000000000 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificNodeRoles.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 java.util.Collection; -import java.util.Collections; -import java.util.List; - -public class RoundSpecificNodeRoles { - - private final ValidatorPeer proposer; - private final Collection peers; - private final List nonProposingPeers; - - public RoundSpecificNodeRoles( - final ValidatorPeer proposer, - final Collection peers, - final List nonProposingPeers) { - this.proposer = proposer; - this.peers = peers; - this.nonProposingPeers = nonProposingPeers; - } - - public ValidatorPeer getProposer() { - return proposer; - } - - public Collection getAllPeers() { - return Collections.unmodifiableCollection(peers); - } - - public List getNonProposingPeers() { - return Collections.unmodifiableList(nonProposingPeers); - } - - public ValidatorPeer getNonProposingPeer(final int index) { - return nonProposingPeers.get(index); - } -} 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 new file mode 100644 index 0000000000..14d2884f0d --- /dev/null +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/RoundSpecificPeers.java @@ -0,0 +1,219 @@ +/* + * 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.Optional.empty; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Fail.fail; + +import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; +import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; +import tech.pegasys.pantheon.consensus.ibft.messagedata.IbftV2; +import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; +import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; +import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; +import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; +import tech.pegasys.pantheon.consensus.ibft.payload.Payload; +import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; +import tech.pegasys.pantheon.consensus.ibft.payload.PreparedCertificate; +import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; +import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; +import tech.pegasys.pantheon.crypto.SECP256K1.Signature; +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +public class RoundSpecificPeers { + + private final ValidatorPeer proposer; + private final Collection peers; + private final List nonProposingPeers; + + public RoundSpecificPeers( + final ValidatorPeer proposer, + final Collection peers, + final List nonProposingPeers) { + this.proposer = proposer; + this.peers = peers; + this.nonProposingPeers = nonProposingPeers; + } + + public ValidatorPeer getProposer() { + return proposer; + } + + public ValidatorPeer getFirstNonProposer() { + return nonProposingPeers.get(0); + } + + public void clearReceivedMessages() { + peers.forEach(ValidatorPeer::clearReceivedMessages); + } + + public List sign(final Hash digest) { + return peers.stream().map(peer -> peer.getBlockSignature(digest)).collect(Collectors.toList()); + } + + public ValidatorPeer getNonProposing(final int index) { + return nonProposingPeers.get(index); + } + + public List> roundChangeForNonProposing( + final ConsensusRoundIdentifier targetRound) { + return nonProposingPeers + .stream() + .map(peer -> peer.injectRoundChange(targetRound, empty())) + .collect(Collectors.toList()); + } + + public void commit(final ConsensusRoundIdentifier roundId, final Hash hash) { + peers.forEach(peer -> peer.injectCommit(roundId, hash)); + } + + public List> roundChange(final ConsensusRoundIdentifier roundId) { + final List> changes = Lists.newArrayList(); + + for (final ValidatorPeer peer : peers) { + changes.add(peer.injectRoundChange(roundId, empty())); + } + + return changes; + } + + public List> createSignedRoundChangePayload( + final ConsensusRoundIdentifier roundId) { + return peers + .stream() + .map(p -> p.getMessageFactory().createSignedRoundChangePayload(roundId, empty())) + .collect(Collectors.toList()); + } + + public List> createSignedRoundChangePayload( + final ConsensusRoundIdentifier roundId, final PreparedCertificate preparedCertificate) { + return peers + .stream() + .map( + p -> + p.getMessageFactory() + .createSignedRoundChangePayload(roundId, Optional.of(preparedCertificate))) + .collect(Collectors.toList()); + } + + public void prepareForNonProposing(final ConsensusRoundIdentifier roundId, final Hash hash) { + nonProposingPeers.forEach(peer -> peer.injectPrepare(roundId, hash)); + } + + public void commitForNonProposing(final ConsensusRoundIdentifier roundId, final Hash hash) { + nonProposingPeers.forEach(peer -> peer.injectCommit(roundId, hash)); + } + + Collection> createSignedPreparePayloadOfNonProposing( + final ConsensusRoundIdentifier preparedRound, final Hash hash) { + return nonProposingPeers + .stream() + .map(role -> role.getMessageFactory().createSignedPreparePayload(preparedRound, hash)) + .collect(Collectors.toList()); + } + + public void verifyNoMessagesReceived() { + peers.forEach(n -> assertThat(n.getReceivedMessages()).isEmpty()); + } + + public void verifyNoMessagesReceivedNonProposing() { + nonProposingPeers.forEach(n -> assertThat(n.getReceivedMessages()).isEmpty()); + } + + public void verifyNoMessagesReceivedProposer() { + assertThat(proposer.getReceivedMessages()).isEmpty(); + } + + @SafeVarargs + public final void verifyMessagesReceivedPropser(final SignedData... msgs) { + verifyMessagesReceived(ImmutableList.of(proposer), msgs); + } + + @SafeVarargs + public final void verifyMessagesReceivedNonPropsingExcluding( + final ValidatorPeer exclude, final SignedData... msgs) { + final Collection candidates = Lists.newArrayList(nonProposingPeers); + candidates.remove(exclude); + verifyMessagesReceived(candidates, msgs); + } + + @SafeVarargs + public final void verifyMessagesReceivedNonPropsing(final SignedData... msgs) { + verifyMessagesReceived(nonProposingPeers, msgs); + } + + @SafeVarargs + public final void verifyMessagesReceived(final SignedData... msgs) { + verifyMessagesReceived(peers, msgs); + } + + @SafeVarargs + private final void verifyMessagesReceived( + final Collection candidates, final SignedData... msgs) { + candidates.forEach(n -> assertThat(n.getReceivedMessages().size()).isEqualTo(msgs.length)); + + List> msgList = Arrays.asList(msgs); + + for (int i = 0; i < msgList.size(); i++) { + final int index = i; + final SignedData msg = msgList.get(index); + candidates.forEach( + n -> { + final List rxMsgs = n.getReceivedMessages(); + final MessageData rxMsgData = rxMsgs.get(index); + verifyMessage(rxMsgData, msg); + }); + } + candidates.forEach(ValidatorPeer::clearReceivedMessages); + } + + private void verifyMessage( + final MessageData actual, final SignedData signedExpectedPayload) { + final Payload expectedPayload = signedExpectedPayload.getPayload(); + SignedData actualSignedPayload = null; + + switch (expectedPayload.getMessageType()) { + case IbftV2.PROPOSAL: + actualSignedPayload = ProposalMessageData.fromMessageData(actual).decode(); + break; + case IbftV2.PREPARE: + actualSignedPayload = PrepareMessageData.fromMessageData(actual).decode(); + break; + case IbftV2.COMMIT: + actualSignedPayload = CommitMessageData.fromMessageData(actual).decode(); + break; + case IbftV2.NEW_ROUND: + actualSignedPayload = NewRoundMessageData.fromMessageData(actual).decode(); + break; + case IbftV2.ROUND_CHANGE: + actualSignedPayload = RoundChangeMessageData.fromMessageData(actual).decode(); + break; + default: + fail("Illegal IBFTV2 message type."); + break; + } + assertThat(signedExpectedPayload) + .isEqualToComparingFieldByFieldRecursively(actualSignedPayload); + } +} diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java index 21ef4e5735..745945399c 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestContext.java @@ -80,7 +80,7 @@ public Block createBlockForProposalFromChainHead(final int round, final long tim return createBlockForProposal(blockchain.getChainHeadHeader(), round, timestamp); } - public RoundSpecificNodeRoles getRoundSpecificRoles(final ConsensusRoundIdentifier roundId) { + public RoundSpecificPeers roundSpecificPeers(final ConsensusRoundIdentifier roundId) { // This will return NULL if the LOCAL node is the proposer for the specified round final Address proposerAddress = finalState.getProposerForRound(roundId); final ValidatorPeer proposer = remotePeers.getOrDefault(proposerAddress, null); @@ -88,7 +88,7 @@ public RoundSpecificNodeRoles getRoundSpecificRoles(final ConsensusRoundIdentifi final List nonProposers = new ArrayList<>(remotePeers.values()); nonProposers.remove(proposer); - return new RoundSpecificNodeRoles(proposer, remotePeers.values(), nonProposers); + return new RoundSpecificPeers(proposer, remotePeers.values(), nonProposers); } public NodeParams getLocalNodeParams() { diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java index fa22d0b7e8..27dd846c25 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/TestHelpers.java @@ -12,8 +12,6 @@ */ package tech.pegasys.pantheon.consensus.ibft.support; -import static java.util.Optional.empty; - import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftBlockHashing; import tech.pegasys.pantheon.consensus.ibft.IbftExtraData; @@ -30,9 +28,7 @@ import tech.pegasys.pantheon.crypto.SECP256K1.Signature; import tech.pegasys.pantheon.ethereum.core.Block; -import java.util.Collection; import java.util.List; -import java.util.stream.Collectors; public class TestHelpers { @@ -53,32 +49,19 @@ public static SignedData createSignedCommitPayload( public static PreparedCertificate createValidPreparedCertificate( final TestContext context, final ConsensusRoundIdentifier preparedRound, final Block block) { - final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(preparedRound); + final RoundSpecificPeers peers = context.roundSpecificPeers(preparedRound); return new PreparedCertificate( - roles.getProposer().getMessageFactory().createSignedProposalPayload(preparedRound, block), - roles - .getNonProposingPeers() - .stream() - .map( - role -> - role.getMessageFactory() - .createSignedPreparePayload(preparedRound, block.getHash())) - .collect(Collectors.toList())); + peers.getProposer().getMessageFactory().createSignedProposalPayload(preparedRound, block), + peers.createSignedPreparePayloadOfNonProposing(preparedRound, block.getHash())); } public static SignedData injectEmptyNewRound( final ConsensusRoundIdentifier targetRoundId, final ValidatorPeer proposer, - final Collection peers, + final List> roundChangePayloads, final Block blockToPropose) { - final List> roundChangePayloads = - peers - .stream() - .map(p -> p.getMessageFactory().createSignedRoundChangePayload(targetRoundId, empty())) - .collect(Collectors.toList()); - final SignedData proposal = proposer.getMessageFactory().createSignedProposalPayload(targetRoundId, blockToPropose); 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 5399ce999e..44edec54ac 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 @@ -14,8 +14,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.util.Lists.emptyList; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -25,7 +23,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.ethereum.core.Block; @@ -33,7 +31,6 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; -import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; @@ -55,7 +52,7 @@ public class FutureHeightTest { .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final ConsensusRoundIdentifier futureHeightRoundId = new ConsensusRoundIdentifier(2, 0); @@ -70,29 +67,19 @@ public void setup() { public void messagesForFutureHeightAreBufferedUntilChainHeightCatchesUp() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - currentHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(currentHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(currentHeightBlock, peers.sign(currentHeightBlock.getHash())); final Block futureHeightBlock = context.createBlockForProposal(signedCurrentHeightBlock.getHeader(), 0, 60); - roles.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); + peers.verifyNoMessagesReceived(); // Inject prepares and commits from all peers - roles - .getNonProposingPeers() - .forEach( - p -> { - p.injectPrepare(futureHeightRoundId, futureHeightBlock.getHash()); - p.injectCommit(futureHeightRoundId, futureHeightBlock.getHash()); - }); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.prepareForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); + peers.commitForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); + + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); // Add block to chain, and notify system of its arrival. @@ -110,7 +97,7 @@ public void messagesForFutureHeightAreBufferedUntilChainHeightCatchesUp() { createSignedCommitPayload( futureHeightRoundId, futureHeightBlock, context.getLocalNodeParams().getNodeKeyPair()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepareMessage, expectedCommitMessage); + peers.verifyMessagesReceived(expectedPrepareMessage, expectedCommitMessage); assertThat(context.getCurrentChainHeight()).isEqualTo(2); } @@ -118,21 +105,15 @@ public void messagesForFutureHeightAreBufferedUntilChainHeightCatchesUp() { public void messagesFromPreviousHeightAreDiscarded() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - currentHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(currentHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(currentHeightBlock, peers.sign(currentHeightBlock.getHash())); - roles.getProposer().injectProposal(roundId, currentHeightBlock); - roles.getNonProposingPeer(0).injectPrepare(roundId, currentHeightBlock.getHash()); + peers.getProposer().injectProposal(roundId, currentHeightBlock); + peers.getNonProposing(0).injectPrepare(roundId, currentHeightBlock.getHash()); final SignedData expectedPrepareMessage = localNodeMessageFactory.createSignedPreparePayload(roundId, currentHeightBlock.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepareMessage); + peers.verifyMessagesReceived(expectedPrepareMessage); // Add block to chain, and notify system of its arrival. context.getBlockchain().appendBlock(signedCurrentHeightBlock, emptyList()); @@ -143,14 +124,10 @@ public void messagesFromPreviousHeightAreDiscarded() { // Inject prepares and commits from all peers for the 'previous' round (i.e. the height // from before the block arrived). - roles - .getNonProposingPeers() - .forEach( - p -> { - p.injectPrepare(roundId, currentHeightBlock.getHash()); - p.injectCommit(roundId, currentHeightBlock.getHash()); - }); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.prepareForNonProposing(roundId, currentHeightBlock.getHash()); + peers.commitForNonProposing(roundId, currentHeightBlock.getHash()); + + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @@ -158,10 +135,10 @@ public void messagesFromPreviousHeightAreDiscarded() { public void multipleNewChainHeadEventsDoesNotRestartCurrentHeightManager() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); - roles.getProposer().injectProposal(roundId, currentHeightBlock); - roles.getNonProposingPeer(0).injectPrepare(roundId, currentHeightBlock.getHash()); + peers.getProposer().injectProposal(roundId, currentHeightBlock); + peers.getNonProposing(0).injectPrepare(roundId, currentHeightBlock.getHash()); - roles.getAllPeers().forEach(peer -> peer.clearReceivedMessages()); + peers.clearReceivedMessages(); // inject a NewHeight FOR THE CURRENT HEIGHT context @@ -169,36 +146,24 @@ public void multipleNewChainHeadEventsDoesNotRestartCurrentHeightManager() { .handleNewBlockEvent(new NewChainHead(context.getBlockchain().getChainHeadHeader())); // Should only require 1 more prepare to close it out - roles.getNonProposingPeer(1).injectPrepare(roundId, currentHeightBlock.getHash()); + peers.getNonProposing(1).injectPrepare(roundId, currentHeightBlock.getHash()); final SignedData expectedCommitMessage = createSignedCommitPayload( roundId, currentHeightBlock, context.getLocalNodeParams().getNodeKeyPair()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedCommitMessage); + peers.verifyMessagesReceived(expectedCommitMessage); } @Test public void correctMessagesAreExtractedFromFutureHeightBuffer() { final Block currentHeightBlock = context.createBlockForProposalFromChainHead(0, 30); final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - currentHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(currentHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(currentHeightBlock, peers.sign(currentHeightBlock.getHash())); final Block nextHeightBlock = context.createBlockForProposal(signedCurrentHeightBlock.getHeader(), 0, 60); final Block signedNextHeightBlock = - IbftHelpers.createSealedBlock( - nextHeightBlock, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(nextHeightBlock.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(nextHeightBlock, peers.sign(nextHeightBlock.getHash())); final Block futureHeightBlock = context.createBlockForProposal(signedNextHeightBlock.getHeader(), 0, 90); @@ -207,13 +172,8 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { final ConsensusRoundIdentifier futureHeightRoundId = new ConsensusRoundIdentifier(3, 0); // Inject prepares and commits from all peers into FutureHeight (2 height time) - roles - .getNonProposingPeers() - .forEach( - p -> { - p.injectPrepare(futureHeightRoundId, futureHeightBlock.getHash()); - p.injectCommit(futureHeightRoundId, futureHeightBlock.getHash()); - }); + peers.prepareForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); + peers.commitForNonProposing(futureHeightRoundId, futureHeightBlock.getHash()); // Add the "interim" block to chain, and notify system of its arrival. context.getBlockchain().appendBlock(signedCurrentHeightBlock, emptyList()); @@ -222,8 +182,8 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { .getController() .handleNewBlockEvent(new NewChainHead(signedCurrentHeightBlock.getHeader())); - assertPeersReceivedNoMessages(roles.getAllPeers()); - roles.getProposer().injectProposal(nextHeightRoundId, nextHeightBlock); + peers.verifyNoMessagesReceived(); + peers.getProposer().injectProposal(nextHeightRoundId, nextHeightBlock); final SignedData expectedPrepareMessage = localNodeMessageFactory.createSignedPreparePayload( @@ -231,9 +191,9 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { // Assert ONLY a prepare message was received, not any commits (i.e. futureHeightRoundId // messages have not been used. - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepareMessage); + peers.verifyMessagesReceived(expectedPrepareMessage); - roles.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); + peers.getProposer().injectProposal(futureHeightRoundId, futureHeightBlock); // Change to the FutureRound, and confirm prepare and commit msgs are sent context.getBlockchain().appendBlock(signedNextHeightBlock, emptyList()); @@ -252,7 +212,6 @@ public void correctMessagesAreExtractedFromFutureHeightBuffer() { // Assert ONLY a prepare message was received, not any commits (i.e. futureHeightRoundId // messages have not been used. - assertPeersReceivedExactly( - roles.getAllPeers(), expectedCommitMessage, expectedFuturePrepareMessage); + peers.verifyMessagesReceived(expectedCommitMessage, expectedFuturePrepareMessage); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java index 9a672f69f4..573d64eb23 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/FutureRoundTest.java @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.injectEmptyNewRound; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -23,10 +21,9 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; -import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; import tech.pegasys.pantheon.crypto.SECP256K1; import tech.pegasys.pantheon.ethereum.core.Block; @@ -54,10 +51,10 @@ public class FutureRoundTest { .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(1, 5); - private final RoundSpecificNodeRoles futureRoles = context.getRoundSpecificRoles(futureRoundId); + private final RoundSpecificPeers futurePeers = context.roundSpecificPeers(futureRoundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -72,50 +69,53 @@ public void messagesForFutureRoundAreNotActionedUntilRoundIsActive() { context.createBlockForProposalFromChainHead(futureRoundId.getRoundNumber(), 60); final int quorum = IbftHelpers.calculateRequiredValidatorQuorum(NETWORK_SIZE); final ConsensusRoundIdentifier subsequentRoundId = new ConsensusRoundIdentifier(1, 6); - final RoundSpecificNodeRoles subsequentRoles = context.getRoundSpecificRoles(subsequentRoundId); + final RoundSpecificPeers subsequentRoles = context.roundSpecificPeers(subsequentRoundId); // required remotely received Prepares = quorum-2 // required remote received commits = quorum-1 // Inject 1 too few Commit messages (but sufficient Prepare for (int i = 0; i < quorum - 3; i++) { - futureRoles.getNonProposingPeer(i).injectPrepare(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(i).injectPrepare(futureRoundId, futureBlock.getHash()); } for (int i = 0; i < quorum - 2; i++) { - futureRoles.getNonProposingPeer(i).injectCommit(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(i).injectCommit(futureRoundId, futureBlock.getHash()); } // inject a prepare and a commit from a subsequent round, and ensure no transmissions are // created - subsequentRoles.getNonProposingPeer(1).injectPrepare(subsequentRoundId, futureBlock.getHash()); - subsequentRoles.getNonProposingPeer(1).injectCommit(subsequentRoundId, futureBlock.getHash()); + subsequentRoles.getNonProposing(1).injectPrepare(subsequentRoundId, futureBlock.getHash()); + subsequentRoles.getNonProposing(1).injectCommit(subsequentRoundId, futureBlock.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); // inject a newRound to move to 'futureRoundId', and ensure localnode sends prepare, commit // and updates blockchain injectEmptyNewRound( - futureRoundId, futureRoles.getProposer(), futureRoles.getAllPeers(), futureBlock); + futureRoundId, + futurePeers.getProposer(), + futurePeers.createSignedRoundChangePayload(futureRoundId), + futureBlock); final SignedData expectedPrepare = localNodeMessageFactory.createSignedPreparePayload(futureRoundId, futureBlock.getHash()); - assertPeersReceivedExactly(futureRoles.getAllPeers(), expectedPrepare); + peers.verifyMessagesReceived(expectedPrepare); // following 1 more prepare, a commit msg will be sent - futureRoles.getNonProposingPeer(quorum - 3).injectPrepare(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(quorum - 3).injectPrepare(futureRoundId, futureBlock.getHash()); final SignedData expectedCommit = localNodeMessageFactory.createSignedCommitPayload( futureRoundId, futureBlock.getHash(), SECP256K1.sign(futureBlock.getHash(), context.getLocalNodeParams().getNodeKeyPair())); - assertPeersReceivedExactly(futureRoles.getAllPeers(), expectedCommit); + peers.verifyMessagesReceived(expectedCommit); // requires 1 more commit and the blockchain will progress - futureRoles.getNonProposingPeer(quorum - 2).injectCommit(futureRoundId, futureBlock.getHash()); + futurePeers.getNonProposing(quorum - 2).injectCommit(futureRoundId, futureBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1); } @@ -127,28 +127,29 @@ public void priorRoundsCannotBeCompletedAfterReceptionOfNewRound() { final Block futureBlock = context.createBlockForProposalFromChainHead(futureRoundId.getRoundNumber(), 60); - roles.getProposer().injectProposal(roundId, initialBlock); - for (final ValidatorPeer peer : roles.getNonProposingPeers()) { - peer.injectPrepare(roundId, initialBlock.getHash()); - } - roles.getProposer().injectCommit(roundId, initialBlock.getHash()); + peers.getProposer().injectProposal(roundId, initialBlock); + + peers.prepareForNonProposing(roundId, initialBlock.getHash()); + + peers.getProposer().injectCommit(roundId, initialBlock.getHash()); // At this stage, the local node has 2 commit msgs (proposer and local) so has not committed assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.clearReceivedMessages(); - } + peers.clearReceivedMessages(); injectEmptyNewRound( - futureRoundId, futureRoles.getProposer(), futureRoles.getAllPeers(), futureBlock); + futureRoundId, + futurePeers.getProposer(), + futurePeers.createSignedRoundChangePayload(futureRoundId), + futureBlock); final SignedData expectedFuturePrepare = localNodeMessageFactory.createSignedPreparePayload(futureRoundId, futureBlock.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedFuturePrepare); + peers.verifyMessagesReceived(expectedFuturePrepare); // attempt to complete the previous round - roles.getNonProposingPeers().get(0).injectCommit(roundId, initialBlock.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectCommit(roundId, initialBlock.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java index c69f2f3f12..21ccb4a1e5 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/GossipTest.java @@ -14,8 +14,6 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singleton; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; import tech.pegasys.pantheon.consensus.ibft.IbftHelpers; @@ -29,7 +27,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; @@ -39,16 +37,13 @@ import java.time.Clock; import java.time.Instant; import java.time.ZoneId; -import java.util.ArrayList; -import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; -import com.google.common.collect.ImmutableList; import org.junit.Before; import org.junit.Test; public class GossipTest { + private final long blockTimeStamp = 100; private final Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(blockTimeStamp), ZoneId.systemDefault()); @@ -64,7 +59,7 @@ public class GossipTest { .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private Block block; private ValidatorPeer sender; private MessageFactory msgFactory; @@ -73,7 +68,7 @@ public class GossipTest { public void setup() { context.getController().start(); block = context.createBlockForProposalFromChainHead(roundId.getRoundNumber(), 30); - sender = roles.getProposer(); + sender = peers.getProposer(); msgFactory = sender.getMessageFactory(); } @@ -81,19 +76,20 @@ public void setup() { public void gossipMessagesToPeers() { SignedData localPrepare = context.getLocalNodeMessageFactory().createSignedPreparePayload(roundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); + final SignedData proposal = sender.injectProposal(roundId, block); // sender node will have a prepare message as an effect of the proposal being sent - assertPeersReceivedExactly(roles.getNonProposingPeers(), proposal, localPrepare); - assertPeersReceivedExactly(singleton(sender), localPrepare); + peers.verifyMessagesReceivedNonPropsing(proposal, localPrepare); + peers.verifyMessagesReceivedPropser(localPrepare); final SignedData prepare = sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), prepare); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(prepare); + peers.verifyNoMessagesReceivedProposer(); final SignedData commit = sender.injectCommit(roundId, block.getHash()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), commit); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(commit); + peers.verifyNoMessagesReceivedProposer(); final SignedData roundChange = msgFactory.createSignedRoundChangePayload(roundId, Optional.empty()); @@ -101,24 +97,24 @@ public void gossipMessagesToPeers() { new RoundChangeCertificate(singleton(roundChange)); SignedData newRound = sender.injectNewRound(roundId, roundChangeCert, proposal); - assertPeersReceivedExactly(roles.getNonProposingPeers(), newRound); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(newRound); + peers.verifyNoMessagesReceivedProposer(); sender.injectRoundChange(roundId, Optional.empty()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), roundChange); - assertPeersReceivedNoMessages(singleton(sender)); + peers.verifyMessagesReceivedNonPropsing(roundChange); + peers.verifyNoMessagesReceivedProposer(); } @Test public void onlyGossipOnce() { final SignedData prepare = sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedExactly(roles.getNonProposingPeers(), prepare); + peers.verifyMessagesReceivedNonPropsing(prepare); sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); sender.injectPrepare(roundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); } @Test @@ -129,22 +125,20 @@ public void messageWithUnknownValidatorIsNotGossiped() { unknownMsgFactory.createSignedProposalPayload(roundId, block); sender.injectMessage(ProposalMessageData.create(unknownProposal)); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void messageIsNotGossipedToSenderOrCreator() { - final ValidatorPeer msgCreator = roles.getNonProposingPeer(0); + final ValidatorPeer msgCreator = peers.getFirstNonProposer(); final MessageFactory peerMsgFactory = msgCreator.getMessageFactory(); final SignedData proposalFromPeer = peerMsgFactory.createSignedProposalPayload(roundId, block); sender.injectMessage(ProposalMessageData.create(proposalFromPeer)); - final List validators = new ArrayList<>(roles.getNonProposingPeers()); - validators.remove(msgCreator); - assertPeersReceivedExactly(validators, proposalFromPeer); - assertPeersReceivedNoMessages(ImmutableList.of(roles.getProposer(), msgCreator)); + peers.verifyMessagesReceivedNonPropsingExcluding(msgCreator, proposalFromPeer); + peers.verifyNoMessagesReceivedProposer(); } @Test @@ -153,30 +147,25 @@ public void futureMessageIsNotGossipedImmediately() { msgFactory.createSignedProposalPayload(futureRoundId, block); sender.injectProposal(futureRoundId, block); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void previousHeightMessageIsNotGossiped() { final ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(0, 0); sender.injectProposal(futureRoundId, block); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void futureMessageGetGossipedLater() { final Block signedCurrentHeightBlock = - IbftHelpers.createSealedBlock( - block, - roles - .getAllPeers() - .stream() - .map(peer -> peer.getBlockSignature(block.getHash())) - .collect(Collectors.toList())); + IbftHelpers.createSealedBlock(block, peers.sign(block.getHash())); ConsensusRoundIdentifier futureRoundId = new ConsensusRoundIdentifier(2, 0); SignedData futurePrepare = sender.injectPrepare(futureRoundId, block.getHash()); - assertPeersReceivedNoMessages(roles.getNonProposingPeers()); + peers.verifyNoMessagesReceivedNonProposing(); + ; // add block to chain so we can move to next block height context.getBlockchain().appendBlock(signedCurrentHeightBlock, emptyList()); @@ -184,6 +173,6 @@ public void futureMessageGetGossipedLater() { .getController() .handleNewBlockEvent(new NewChainHead(signedCurrentHeightBlock.getHeader())); - assertPeersReceivedExactly(roles.getNonProposingPeers(), futurePrepare); + peers.verifyMessagesReceivedNonPropsing(futurePrepare); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java index 76c2afa426..f0f71b678c 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeIsProposerTest.java @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -23,7 +21,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.ethereum.core.Block; @@ -41,11 +39,11 @@ */ public class LocalNodeIsProposerTest { - final long blockTimeStamp = 100; + private final long blockTimeStamp = 100; private final Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(blockTimeStamp), ZoneId.systemDefault()); - final int NETWORK_SIZE = 4; + private final int NETWORK_SIZE = 4; // Configuration ensures unit under test will be responsible for sending first proposal private final TestContext context = @@ -55,7 +53,7 @@ public class LocalNodeIsProposerTest { .clock(fixedClock) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -80,34 +78,34 @@ public void setup() { @Test public void basicCase() { - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxProposal); + peers.verifyMessagesReceived(expectedTxProposal); // NOTE: In these test roles.getProposer() will return NULL. - roles.getNonProposingPeer(0).injectPrepare(roundId, expectedProposedBlock.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectPrepare(roundId, expectedProposedBlock.getHash()); + peers.verifyNoMessagesReceived(); - roles.getNonProposingPeer(1).injectPrepare(roundId, expectedProposedBlock.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(1).injectPrepare(roundId, expectedProposedBlock.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); - roles.getNonProposingPeer(1).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(1).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); - roles.getNonProposingPeer(2).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(2).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void importsToChainWithoutReceivingPrepareMessages() { - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxProposal); + peers.verifyMessagesReceived(expectedTxProposal); - roles.getNonProposingPeer(1).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(1).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); - roles.getNonProposingPeer(2).injectCommit(roundId, expectedProposedBlock.getHash()); + peers.getNonProposing(2).injectCommit(roundId, expectedProposedBlock.getHash()); assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(1); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java index 368b291583..6716411c66 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/LocalNodeNotProposerTest.java @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -22,10 +20,9 @@ import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; -import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; import tech.pegasys.pantheon.ethereum.core.Block; import org.junit.Before; @@ -33,7 +30,7 @@ public class LocalNodeNotProposerTest { - final int NETWORK_SIZE = 4; + private final int NETWORK_SIZE = 4; // By setting the indexOfFirstLocallyProposedBlock to 0 (and that the blockchain has only the // genesis block) guarantees the local node is not responsible for proposing the first block). @@ -43,7 +40,7 @@ public class LocalNodeNotProposerTest { .indexOfFirstLocallyProposedBlock(0) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -66,57 +63,57 @@ public void setup() { @Test public void basicCase() { - roles.getProposer().injectProposal(roundId, blockToPropose); + peers.getProposer().injectProposal(roundId, blockToPropose); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); + peers.verifyMessagesReceived(expectedTxPrepare); - roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); // Ensure the local blockchain has NOT incremented yet. assertThat(context.getCurrentChainHeight()).isEqualTo(0); // NO further messages should be transmitted when another Prepare is received. - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); // Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet) - roles.getNonProposingPeer(0).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); // A second commit message means quorum is reached, and blockchain should be updated. - roles.getNonProposingPeer(1).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(1).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); // ensure any further commit messages do not affect the system - roles.getProposer().injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getProposer().injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @Test public void prepareFromProposerIsIgnored() { - roles.getProposer().injectProposal(roundId, blockToPropose); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.verifyMessagesReceived(expectedTxPrepare); // No commit message transmitted after receiving prepare from proposer - roles.getProposer().injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getProposer().injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); // Inject a commit, ensure blockChain is not updated, and no message are sent (not quorum yet) - roles.getNonProposingPeer(0).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(0).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(0); // A second commit message means quorum is reached, and blockchain should be updated. - roles.getNonProposingPeer(1).injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.getNonProposing(1).injectCommit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @@ -124,40 +121,35 @@ public void prepareFromProposerIsIgnored() { public void commitMessagesReceivedBeforePrepareCorrectlyImports() { // All peers send a commit, then all non-proposing peers send a prepare, when then Proposal // arrives last, the chain is updated, and a prepare and commit message are transmitted. - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); - assertThat(context.getCurrentChainHeight()).isEqualTo(0); - } - - for (final ValidatorPeer peer : roles.getNonProposingPeers()) { - peer.injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); - assertThat(context.getCurrentChainHeight()).isEqualTo(0); - } - - roles.getProposer().injectProposal(roundId, blockToPropose); + peers.clearReceivedMessages(); + peers.commit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); + assertThat(context.getCurrentChainHeight()).isEqualTo(0); + + peers.prepareForNonProposing(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); + assertThat(context.getCurrentChainHeight()).isEqualTo(0); + + peers.getProposer().injectProposal(roundId, blockToPropose); // TODO(tmm): Unfortunatley, there are times that the Commit will go out BEFORE the prepare // This is one of them :( Maybe fix the testing to be ignorant of ordering? - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit, expectedTxPrepare); + peers.verifyMessagesReceived(expectedTxCommit, expectedTxPrepare); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @Test public void fullQuorumOfCommitMessagesReceivedThenProposalImportsBlockCommitSentAfterFinalPrepare() { - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectCommit(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); - assertThat(context.getCurrentChainHeight()).isEqualTo(0); - } - - roles.getProposer().injectProposal(roundId, blockToPropose); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxPrepare); + peers.commit(roundId, blockToPropose.getHash()); + peers.verifyNoMessagesReceived(); + assertThat(context.getCurrentChainHeight()).isEqualTo(0); + + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.verifyMessagesReceived(expectedTxPrepare); assertThat(context.getCurrentChainHeight()).isEqualTo(1); - roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit); + peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); + peers.verifyMessagesReceived(expectedTxCommit); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedNewRoundTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedNewRoundTest.java index af5704cacc..254b90a290 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedNewRoundTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/ReceivedNewRoundTest.java @@ -12,9 +12,6 @@ */ package tech.pegasys.pantheon.consensus.ibft.tests; -import static java.util.Optional.empty; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createValidPreparedCertificate; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -26,7 +23,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.TestHelpers; @@ -34,10 +31,7 @@ import tech.pegasys.pantheon.ethereum.core.Block; import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import org.assertj.core.util.Lists; import org.junit.Before; import org.junit.Test; @@ -53,7 +47,7 @@ public class ReceivedNewRoundTest { .indexOfFirstLocallyProposedBlock(0) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -70,13 +64,9 @@ public void newRoundMessageWithEmptyPrepareCertificatesOfferNewBlock() { final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 1); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map(p -> p.getMessageFactory().createSignedRoundChangePayload(targetRound, empty())) - .collect(Collectors.toList()); + peers.createSignedRoundChangePayload(targetRound); - final ValidatorPeer nextProposer = context.getRoundSpecificRoles(nextRoundId).getProposer(); + final ValidatorPeer nextProposer = context.roundSpecificPeers(nextRoundId).getProposer(); nextProposer.injectNewRound( targetRound, @@ -86,7 +76,7 @@ public void newRoundMessageWithEmptyPrepareCertificatesOfferNewBlock() { final SignedData expectedPrepare = localNodeMessageFactory.createSignedPreparePayload(targetRound, blockToPropose.getHash()); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.verifyMessagesReceived(expectedPrepare); } @Test @@ -96,14 +86,10 @@ public void newRoundMessageFromIllegalSenderIsDiscardedAndNoPrepareForNewRoundIs context.createBlockForProposalFromChainHead(nextRoundId.getRoundNumber(), 15); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map(p -> p.getMessageFactory().createSignedRoundChangePayload(nextRoundId, empty())) - .collect(Collectors.toList()); + peers.createSignedRoundChangePayload(nextRoundId); final ValidatorPeer illegalProposer = - context.getRoundSpecificRoles(nextRoundId).getNonProposingPeer(0); + context.roundSpecificPeers(nextRoundId).getNonProposing(0); illegalProposer.injectNewRound( nextRoundId, @@ -112,7 +98,7 @@ public void newRoundMessageFromIllegalSenderIsDiscardedAndNoPrepareForNewRoundIs .getMessageFactory() .createSignedProposalPayload(nextRoundId, blockToPropose)); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -125,28 +111,19 @@ public void newRoundWithPrepareCertificateResultsInNewRoundStartingWithExpectedB createValidPreparedCertificate(context, roundId, initialBlock); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map( - p -> - p.getMessageFactory() - .createSignedRoundChangePayload( - nextRoundId, Optional.of(preparedCertificate))) - .collect(Collectors.toList()); - - final ValidatorPeer nextProposer = context.getRoundSpecificRoles(nextRoundId).getProposer(); + peers.createSignedRoundChangePayload(nextRoundId, preparedCertificate); + + final ValidatorPeer nextProposer = context.roundSpecificPeers(nextRoundId).getProposer(); nextProposer.injectNewRound( nextRoundId, new RoundChangeCertificate(roundChanges), - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(nextRoundId, reproposedBlock)); - assertPeersReceivedExactly( - roles.getAllPeers(), + peers.verifyMessagesReceived( localNodeMessageFactory.createSignedPreparePayload(nextRoundId, reproposedBlock.getHash())); } @@ -155,19 +132,14 @@ public void newRoundMessageForPriorRoundIsNotActioned() { // first move to a future round, then inject a newRound for a prior round, local node // should send no messages. final ConsensusRoundIdentifier futureRound = new ConsensusRoundIdentifier(1, 2); - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectRoundChange(futureRound, empty()); - } + peers.roundChange(futureRound); final ConsensusRoundIdentifier interimRound = new ConsensusRoundIdentifier(1, 1); - final List> roundChangePayloads = Lists.newArrayList(); - for (final ValidatorPeer peer : roles.getAllPeers()) { - roundChangePayloads.add( - peer.getMessageFactory().createSignedRoundChangePayload(interimRound, empty())); - } + final List> roundChangePayloads = + peers.createSignedRoundChangePayload(interimRound); final ValidatorPeer interimRoundProposer = - context.getRoundSpecificRoles(interimRound).getProposer(); + context.roundSpecificPeers(interimRound).getProposer(); final SignedData proposal = interimRoundProposer @@ -178,7 +150,7 @@ public void newRoundMessageForPriorRoundIsNotActioned() { interimRoundProposer.injectNewRound( interimRound, new RoundChangeCertificate(roundChangePayloads), proposal); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -191,51 +163,42 @@ public void receiveRoundStateIsNotLostIfASecondNewRoundMessageIsReceivedForCurre createValidPreparedCertificate(context, roundId, initialBlock); final List> roundChanges = - roles - .getAllPeers() - .stream() - .map( - p -> - p.getMessageFactory() - .createSignedRoundChangePayload( - nextRoundId, Optional.of(preparedCertificate))) - .collect(Collectors.toList()); - - final RoundSpecificNodeRoles nextRoles = context.getRoundSpecificRoles(nextRoundId); + peers.createSignedRoundChangePayload(nextRoundId, preparedCertificate); + + final RoundSpecificPeers nextRoles = context.roundSpecificPeers(nextRoundId); final ValidatorPeer nextProposer = nextRoles.getProposer(); nextProposer.injectNewRound( nextRoundId, new RoundChangeCertificate(roundChanges), - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(nextRoundId, reproposedBlock)); - assertPeersReceivedExactly( - roles.getAllPeers(), + peers.verifyMessagesReceived( localNodeMessageFactory.createSignedPreparePayload(nextRoundId, reproposedBlock.getHash())); // Inject a prepare, then re-inject the newRound - then ensure only a single prepare is enough // to trigger a Commit transmission from the local node - nextRoles.getNonProposingPeer(0).injectPrepare(nextRoundId, reproposedBlock.getHash()); + nextRoles.getNonProposing(0).injectPrepare(nextRoundId, reproposedBlock.getHash()); nextProposer.injectNewRound( nextRoundId, new RoundChangeCertificate(roundChanges), - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(nextRoundId, reproposedBlock)); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); - nextRoles.getNonProposingPeer(1).injectPrepare(nextRoundId, reproposedBlock.getHash()); + nextRoles.getNonProposing(1).injectPrepare(nextRoundId, reproposedBlock.getHash()); final SignedData expectedCommit = TestHelpers.createSignedCommitPayload( nextRoundId, reproposedBlock, context.getLocalNodeParams().getNodeKeyPair()); - assertPeersReceivedExactly(nextRoles.getAllPeers(), expectedCommit); + peers.verifyMessagesReceived(expectedCommit); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java index e74a462f11..eff6570a1e 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/RoundChangeTest.java @@ -14,8 +14,6 @@ import static java.util.Collections.emptyList; import static java.util.Optional.empty; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createValidPreparedCertificate; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -29,7 +27,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangeCertificate; import tech.pegasys.pantheon.consensus.ibft.payload.RoundChangePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; @@ -40,7 +38,6 @@ import java.time.ZoneId; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import com.google.common.collect.Lists; import org.junit.Before; @@ -62,7 +59,7 @@ public class RoundChangeTest { .clock(fixedClock) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private final MessageFactory localNodeMessageFactory = context.getLocalNodeMessageFactory(); @@ -81,7 +78,7 @@ public void onRoundChangeTimerExpiryEventRoundChangeMessageIsSent() { final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -90,11 +87,11 @@ public void roundChangeHasEmptyCertificateIfNoPrepareMessagesReceived() { final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); - roles.getProposer().injectProposal(roundId, blockToPropose); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.clearReceivedMessages(); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -105,12 +102,12 @@ public void roundChangeHasEmptyCertificateIfInsufficientPreparesAreReceived() { final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload(targetRound, empty()); - roles.getProposer().injectProposal(roundId, blockToPropose); - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.clearReceivedMessages(); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -120,15 +117,16 @@ public void roundChangeHasPopulatedCertificateIfQuorumPrepareMessagesAndProposal localNodeMessageFactory.createSignedPreparePayload(roundId, blockToPropose.getHash()); final SignedData proposal = - roles.getProposer().injectProposal(roundId, blockToPropose); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getProposer().injectProposal(roundId, blockToPropose); + peers.clearReceivedMessages(); final SignedData p1 = - roles.getNonProposingPeer(0).injectPrepare(roundId, blockToPropose.getHash()); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getNonProposing(0).injectPrepare(roundId, blockToPropose.getHash()); + peers.clearReceivedMessages(); + final SignedData p2 = - roles.getNonProposingPeer(1).injectPrepare(roundId, blockToPropose.getHash()); - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.getNonProposing(1).injectPrepare(roundId, blockToPropose.getHash()); + peers.clearReceivedMessages(); final SignedData expectedTxRoundChange = localNodeMessageFactory.createSignedRoundChangePayload( @@ -138,7 +136,7 @@ public void roundChangeHasPopulatedCertificateIfQuorumPrepareMessagesAndProposal proposal, Lists.newArrayList(localPrepareMessage, p1, p2)))); context.getController().handleRoundExpiry(new RoundExpiry(roundId)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedTxRoundChange); + peers.verifyMessagesReceived(expectedTxRoundChange); } @Test @@ -149,13 +147,13 @@ public void whenSufficientRoundChangeMessagesAreReceivedForNewRoundLocalNodeCrea context.createBlockForProposalFromChainHead(targetRound.getRoundNumber(), blockTimeStamp); final SignedData rc1 = - roles.getNonProposingPeer(0).injectRoundChange(targetRound, empty()); + peers.getNonProposing(0).injectRoundChange(targetRound, empty()); final SignedData rc2 = - roles.getNonProposingPeer(1).injectRoundChange(targetRound, empty()); + peers.getNonProposing(1).injectRoundChange(targetRound, empty()); final SignedData rc3 = - roles.getNonProposingPeer(2).injectRoundChange(targetRound, empty()); + peers.getNonProposing(2).injectRoundChange(targetRound, empty()); final SignedData rc4 = - roles.getProposer().injectRoundChange(targetRound, empty()); + peers.getProposer().injectRoundChange(targetRound, empty()); final SignedData expectedNewRound = localNodeMessageFactory.createSignedNewRoundPayload( @@ -163,7 +161,7 @@ public void whenSufficientRoundChangeMessagesAreReceivedForNewRoundLocalNodeCrea new RoundChangeCertificate(Lists.newArrayList(rc1, rc2, rc3, rc4)), localNodeMessageFactory.createSignedProposalPayload(targetRound, locallyProposedBlock)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test @@ -185,19 +183,19 @@ public void newRoundMessageContainsBlockOnWhichPeerPrepared() { final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); final SignedData rc1 = - roles.getNonProposingPeer(0).injectRoundChange(targetRound, empty()); + peers.getNonProposing(0).injectRoundChange(targetRound, empty()); // Create a roundChange with a PreparedCertificate from an earlier Round (should not be used final SignedData rc2 = - roles.getNonProposingPeer(1).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); + peers.getNonProposing(1).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); // Create a roundChange with a PreparedCertificate from an earlier Round (should not be used final SignedData rc3 = - roles.getNonProposingPeer(2).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); + peers.getNonProposing(2).injectRoundChange(targetRound, Optional.of(earlierPrepCert)); // Create a roundChange containing a PreparedCertificate final SignedData rc4 = - roles.getProposer().injectRoundChange(targetRound, Optional.of(bestPrepCert)); + peers.getProposer().injectRoundChange(targetRound, Optional.of(bestPrepCert)); // Expected to use the block with "ARBITRARY_BLOCKTIME" (i.e. latter block) but with the target // round number. @@ -212,7 +210,7 @@ public void newRoundMessageContainsBlockOnWhichPeerPrepared() { localNodeMessageFactory.createSignedProposalPayload( targetRound, expectedBlockToPropose)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test @@ -220,15 +218,10 @@ public void cannotRoundChangeToAnEarlierRound() { // Controller always starts at 1:0. This test moves to 1:7, then attempts to move back to 1:3. final ConsensusRoundIdentifier futureRound = new ConsensusRoundIdentifier(1, 9); - final List> roundChangeMessages = Lists.newArrayList(); - for (final ValidatorPeer peer : roles.getAllPeers()) { - roundChangeMessages.add(peer.injectRoundChange(futureRound, empty())); - } + final List> roundChangeMessages = peers.roundChange(futureRound); final ConsensusRoundIdentifier priorRound = new ConsensusRoundIdentifier(1, 4); - for (final ValidatorPeer peer : roles.getAllPeers()) { - peer.injectRoundChange(priorRound, empty()); - } + peers.roundChange(priorRound); final Block locallyProposedBlock = context.createBlockForProposalFromChainHead(futureRound.getRoundNumber(), blockTimeStamp); @@ -239,7 +232,7 @@ public void cannotRoundChangeToAnEarlierRound() { new RoundChangeCertificate(roundChangeMessages), localNodeMessageFactory.createSignedProposalPayload(futureRound, locallyProposedBlock)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test @@ -247,13 +240,13 @@ public void multipleRoundChangeMessagesFromSamePeerDoesNotTriggerRoundChange() { // Note: Round-3 is the next round for which the local node is Proposer final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); - final ValidatorPeer transmitter = roles.getNonProposingPeer(0); + final ValidatorPeer transmitter = peers.getNonProposing(0); for (int i = 0; i < IbftHelpers.calculateRequiredValidatorQuorum(NETWORK_SIZE); i++) { transmitter.injectRoundChange(targetRound, empty()); } - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -271,17 +264,12 @@ public void subsequentRoundChangeMessagesFromPeerDoNotOverwritePriorMessage() { List> roundChangeMessages = Lists.newArrayList(); // Create a roundChange containing a PreparedCertificate roundChangeMessages.add( - roles.getProposer().injectRoundChange(targetRound, Optional.of(prepCert))); + peers.getProposer().injectRoundChange(targetRound, Optional.of(prepCert))); // Attempt to override the previously received RoundChange (but now without a payload). - roles.getProposer().injectRoundChange(targetRound, empty()); + peers.getProposer().injectRoundChange(targetRound, empty()); - roundChangeMessages.addAll( - roles - .getNonProposingPeers() - .stream() - .map(peer -> peer.injectRoundChange(targetRound, empty())) - .collect(Collectors.toList())); + roundChangeMessages.addAll(peers.roundChangeForNonProposing(targetRound)); final Block expectedBlockToPropose = context.createBlockForProposalFromChainHead( @@ -294,32 +282,30 @@ public void subsequentRoundChangeMessagesFromPeerDoNotOverwritePriorMessage() { localNodeMessageFactory.createSignedProposalPayload( targetRound, expectedBlockToPropose)); - assertPeersReceivedExactly(roles.getAllPeers(), expectedNewRound); + peers.verifyMessagesReceived(expectedNewRound); } @Test public void messagesFromPreviousRoundAreDiscardedOnTransitionToFutureRound() { - roles.getProposer().injectProposal(roundId, blockToPropose); + peers.getProposer().injectProposal(roundId, blockToPropose); // timeout into next round context.getController().handleRoundExpiry(new RoundExpiry(roundId)); // Clear prior Prepare msg and RoundChange message - roles.getAllPeers().forEach(ValidatorPeer::clearReceivedMessages); + peers.clearReceivedMessages(); // inject enough prepares from prior round to trigger a commit - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectPrepare(roundId, blockToPropose.getHash())); + peers.prepareForNonProposing(roundId, blockToPropose.getHash()); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void roundChangeExpiryForNonCurrentRoundIsDiscarded() { // Manually timeout a future round, and ensure no messages are sent context.getController().handleRoundExpiry(new RoundExpiry(new ConsensusRoundIdentifier(1, 1))); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test @@ -327,26 +313,26 @@ public void illegallyConstructedRoundChangeMessageIsDiscarded() { final ConsensusRoundIdentifier targetRound = new ConsensusRoundIdentifier(1, 4); final SignedData rc1 = - roles.getNonProposingPeer(0).injectRoundChange(targetRound, empty()); + peers.getNonProposing(0).injectRoundChange(targetRound, empty()); final SignedData rc2 = - roles.getNonProposingPeer(1).injectRoundChange(targetRound, empty()); + peers.getNonProposing(1).injectRoundChange(targetRound, empty()); final SignedData rc3 = - roles.getNonProposingPeer(2).injectRoundChange(targetRound, empty()); + peers.getNonProposing(2).injectRoundChange(targetRound, empty()); // create illegal RoundChangeMessage final PreparedCertificate illegalPreparedCertificate = new PreparedCertificate( - roles - .getNonProposingPeer(0) + peers + .getNonProposing(0) .getMessageFactory() .createSignedProposalPayload(roundId, blockToPropose), emptyList()); - roles - .getNonProposingPeer(2) + peers + .getNonProposing(2) .injectRoundChange(targetRound, Optional.of(illegalPreparedCertificate)); // Ensure no NewRound message is sent. - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } } diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java index 401fff9239..f8a5022f6f 100644 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java +++ b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/tests/SpuriousBehaviourTest.java @@ -13,8 +13,6 @@ package tech.pegasys.pantheon.consensus.ibft.tests; import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedExactly; -import static tech.pegasys.pantheon.consensus.ibft.support.MessageReceptionHelpers.assertPeersReceivedNoMessages; import static tech.pegasys.pantheon.consensus.ibft.support.TestHelpers.createSignedCommitPayload; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; @@ -24,7 +22,7 @@ import tech.pegasys.pantheon.consensus.ibft.payload.PreparePayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; import tech.pegasys.pantheon.consensus.ibft.support.NodeParams; -import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificNodeRoles; +import tech.pegasys.pantheon.consensus.ibft.support.RoundSpecificPeers; import tech.pegasys.pantheon.consensus.ibft.support.TestContext; import tech.pegasys.pantheon.consensus.ibft.support.TestContextBuilder; import tech.pegasys.pantheon.consensus.ibft.support.ValidatorPeer; @@ -47,7 +45,7 @@ public class SpuriousBehaviourTest { - final long blockTimeStamp = 100; + private final long blockTimeStamp = 100; private final Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(blockTimeStamp), ZoneId.systemDefault()); @@ -62,7 +60,7 @@ public class SpuriousBehaviourTest { .clock(fixedClock) .build(); private final ConsensusRoundIdentifier roundId = new ConsensusRoundIdentifier(1, 0); - private final RoundSpecificNodeRoles roles = context.getRoundSpecificRoles(roundId); + private final RoundSpecificPeers peers = context.roundSpecificPeers(roundId); private Block proposedBlock = context.createBlockForProposalFromChainHead(0, 30); private SignedData expectedPrepare; @@ -84,19 +82,19 @@ public void setup() { @Test public void badlyFormedRlpDoesNotPreventOngoingIbftOperation() { final MessageData illegalCommitMsg = new RawMessage(IbftV2.PREPARE, BytesValue.EMPTY); - roles.getNonProposingPeer(0).injectMessage(illegalCommitMsg); + peers.getNonProposing(0).injectMessage(illegalCommitMsg); - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); } @Test public void messageWithIllegalMessageCodeAreDiscardedAndDoNotPreventOngoingIbftOperation() { final MessageData illegalCommitMsg = new RawMessage(IbftV2.MESSAGE_SPACE, BytesValue.EMPTY); - roles.getNonProposingPeer(0).injectMessage(illegalCommitMsg); + peers.getNonProposing(0).injectMessage(illegalCommitMsg); - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); } @Test @@ -113,47 +111,40 @@ public void nonValidatorsCannotTriggerReponses() { nonvalidator.injectProposal(new ConsensusRoundIdentifier(1, 0), proposedBlock); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.verifyNoMessagesReceived(); } @Test public void preparesWithMisMatchedDigestAreNotRepondedTo() { - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); - roles.getNonProposingPeers().forEach(peer -> peer.injectPrepare(roundId, Hash.ZERO)); + peers.prepareForNonProposing(roundId, Hash.ZERO); + peers.verifyNoMessagesReceived(); - assertPeersReceivedNoMessages(roles.getAllPeers()); + peers.prepareForNonProposing(roundId, proposedBlock.getHash()); + peers.verifyMessagesReceived(expectedCommit); - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectPrepare(roundId, proposedBlock.getHash())); - - assertPeersReceivedExactly(roles.getAllPeers(), expectedCommit); - - roles.getNonProposingPeers().forEach(peer -> peer.injectCommit(roundId, Hash.ZERO)); + peers.prepareForNonProposing(roundId, Hash.ZERO); assertThat(context.getCurrentChainHeight()).isEqualTo(0); - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectCommit(roundId, proposedBlock.getHash())); + peers.commitForNonProposing(roundId, proposedBlock.getHash()); assertThat(context.getCurrentChainHeight()).isEqualTo(1); } @Test public void oneCommitSealIsIllegalPreventsImport() { - roles.getProposer().injectProposal(roundId, proposedBlock); - assertPeersReceivedExactly(roles.getAllPeers(), expectedPrepare); - roles - .getNonProposingPeers() - .forEach(peer -> peer.injectPrepare(roundId, proposedBlock.getHash())); + peers.getProposer().injectProposal(roundId, proposedBlock); + peers.verifyMessagesReceived(expectedPrepare); + + peers.prepareForNonProposing(roundId, proposedBlock.getHash()); // for a network of 5, 4 seals are required (local + 3 remote) - roles.getNonProposingPeer(0).injectCommit(roundId, proposedBlock.getHash()); - roles.getNonProposingPeer(1).injectCommit(roundId, proposedBlock.getHash()); + peers.getNonProposing(0).injectCommit(roundId, proposedBlock.getHash()); + peers.getNonProposing(1).injectCommit(roundId, proposedBlock.getHash()); // nonProposer-2 will generate an invalid seal - final ValidatorPeer badSealPeer = roles.getNonProposingPeer(2); + final ValidatorPeer badSealPeer = peers.getNonProposing(2); final Signature illegalSeal = SECP256K1.sign(Hash.ZERO, badSealPeer.getNodeKeys()); badSealPeer.injectCommit(roundId, proposedBlock.getHash(), illegalSeal); From a0bccdee0c27b5ba5296332b4f15746dcb72e7e6 Mon Sep 17 00:00:00 2001 From: Chris Mckay Date: Fri, 25 Jan 2019 10:09:24 +1000 Subject: [PATCH 05/31] [NC-1970] admin_addPeer acceptance test (#651) --- .../acceptance/dsl/AcceptanceTestBase.java | 3 ++ .../acceptance/dsl/condition/Condition.java | 1 + .../tests/acceptance/dsl/jsonrpc/Admin.java | 44 +++++++++++++++ .../acceptance/dsl/node/PantheonNode.java | 10 ++-- .../factory/PantheonFactoryConfiguration.java | 6 +-- .../PantheonFactoryConfigurationBuilder.java | 10 ++-- .../dsl/node/factory/PantheonNodeFactory.java | 15 ++++++ .../dsl/transaction/PantheonWeb3j.java | 10 ++++ .../dsl/transaction/Transaction.java | 1 + .../admin/AdminAddPeerAcceptanceTest.java | 53 +++++++++++++++++++ 10 files changed, 140 insertions(+), 13 deletions(-) create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/jsonrpc/Admin.java create mode 100644 acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/admin/AdminAddPeerAcceptanceTest.java diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java index d42e9eb0d1..14f5b58a6d 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/AcceptanceTestBase.java @@ -15,6 +15,7 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.account.Accounts; import tech.pegasys.pantheon.tests.acceptance.dsl.blockchain.Blockchain; import tech.pegasys.pantheon.tests.acceptance.dsl.contract.ContractVerifier; +import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Admin; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Clique; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Eth; import tech.pegasys.pantheon.tests.acceptance.dsl.jsonrpc.Ibft; @@ -45,6 +46,7 @@ public class AcceptanceTestBase { protected final Eth eth; protected final Net net; protected final Perm perm; + protected final Admin admin; protected final PantheonNodeFactory pantheon; protected final ContractVerifier contractVerifier; protected final WaitConditions wait; @@ -61,6 +63,7 @@ protected AcceptanceTestBase() { cluster = new Cluster(net); transactions = new Transactions(accounts); perm = new Perm(transactions); + admin = new Admin(); web3 = new Web3(new Web3Transactions()); pantheon = new PantheonNodeFactory(); contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor()); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/Condition.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/Condition.java index df5b4a49c6..31baa2a967 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/Condition.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/condition/Condition.java @@ -14,6 +14,7 @@ import tech.pegasys.pantheon.tests.acceptance.dsl.node.Node; +@FunctionalInterface public interface Condition { void verify(Node node); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/jsonrpc/Admin.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/jsonrpc/Admin.java new file mode 100644 index 0000000000..b047a5ee9e --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/jsonrpc/Admin.java @@ -0,0 +1,44 @@ +/* + * 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.tests.acceptance.dsl.jsonrpc; + +import static org.assertj.core.api.Assertions.assertThat; + +import tech.pegasys.pantheon.tests.acceptance.dsl.condition.Condition; +import tech.pegasys.pantheon.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +import org.web3j.protocol.core.Response; + +public class Admin { + private Transaction addPeerTransaction(final String enode) { + return (n) -> { + try { + final Response resp = n.adminAddPeer(enode).send(); + assertThat(resp).isNotNull(); + assertThat(resp.hasError()).isFalse(); + return resp.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + }; + } + + public Condition addPeer(final String enode) { + return (n) -> { + final Boolean result = n.execute(addPeerTransaction(enode)); + assertThat(result).isTrue(); + }; + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java index 7091374bd4..fc0608c405 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/PantheonNode.java @@ -92,7 +92,7 @@ public PantheonNode( final GenesisConfigProvider genesisConfigProvider, final int p2pPort, final Boolean p2pEnabled, - final boolean discovery) + final boolean discoveryEnabled) throws IOException { this.homeDirectory = Files.createTempDirectory("acctest"); this.keyPair = KeyPairUtil.loadKeyPair(homeDirectory); @@ -106,7 +106,7 @@ public PantheonNode( this.genesisConfigProvider = genesisConfigProvider; this.devMode = devMode; this.p2pEnabled = p2pEnabled; - this.discoveryEnabled = discovery; + this.discoveryEnabled = discoveryEnabled; LOG.info("Created PantheonNode {}", this.toString()); } @@ -188,7 +188,7 @@ public void useWebSocketsForJsonRpc() { final WebSocketService webSocketService = new WebSocketService(url, true); try { webSocketService.connect(); - } catch (ConnectException e) { + } catch (final ConnectException e) { throw new RuntimeException("Error connection to WebSocket endpoint", e); } @@ -200,7 +200,7 @@ public void useWebSocketsForJsonRpc() { } private void checkIfWebSocketEndpointIsAvailable(final String url) { - WebSocketClient webSocketClient = new WebSocketClient(URI.create(url)); + final WebSocketClient webSocketClient = new WebSocketClient(URI.create(url)); // Web3j implementation always invoke the listener (even when one hasn't been set). We are using // this stub implementation to avoid a NullPointerException. webSocketClient.setListener( @@ -226,7 +226,7 @@ public void onClose() { webSocketClient.connect(); try { Awaitility.await().atMost(5, TimeUnit.SECONDS).until(webSocketClient::isOpen); - } catch (ConditionTimeoutException e) { + } catch (final ConditionTimeoutException e) { throw new WebsocketNotConnectedException(); } finally { webSocketClient.close(); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java index 876508f4a7..5daccc423b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfiguration.java @@ -28,9 +28,9 @@ class PantheonFactoryConfiguration { private final MetricsConfiguration metricsConfiguration; private final PermissioningConfiguration permissioningConfiguration; private final boolean devMode; - private final Boolean discoveryEnabled; private final GenesisConfigProvider genesisConfigProvider; private final Boolean p2pEnabled; + private final boolean discoveryEnabled; PantheonFactoryConfiguration( final String name, @@ -42,7 +42,7 @@ class PantheonFactoryConfiguration { final boolean devMode, final GenesisConfigProvider genesisConfigProvider, final Boolean p2pEnabled, - final Boolean discoveryEnabled) { + final boolean discoveryEnabled) { this.name = name; this.miningParameters = miningParameters; this.jsonRpcConfiguration = jsonRpcConfiguration; @@ -83,7 +83,7 @@ public boolean isDevMode() { return devMode; } - public Boolean isDiscoveryEnabled() { + public boolean isDiscoveryEnabled() { return discoveryEnabled; } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java index d9dcd3d79d..8fae0fb16b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonFactoryConfigurationBuilder.java @@ -35,9 +35,9 @@ public class PantheonFactoryConfigurationBuilder { private PermissioningConfiguration permissioningConfiguration = PermissioningConfiguration.createDefault(); private boolean devMode = true; - private Boolean discoveryEnabled = true; private GenesisConfigProvider genesisConfigProvider = ignore -> Optional.empty(); private Boolean p2pEnabled = true; + private boolean discoveryEnabled = true; public PantheonFactoryConfigurationBuilder setName(final String name) { this.name = name; @@ -109,13 +109,13 @@ public PantheonFactoryConfigurationBuilder setGenesisConfigProvider( return this; } - public PantheonFactoryConfigurationBuilder setDiscoveryEnabled(final Boolean discoveryEnabled) { - this.discoveryEnabled = discoveryEnabled; + public PantheonFactoryConfigurationBuilder setP2pEnabled(final Boolean p2pEnabled) { + this.p2pEnabled = p2pEnabled; return this; } - public PantheonFactoryConfigurationBuilder setP2pEnabled(final Boolean p2pEnabled) { - this.p2pEnabled = p2pEnabled; + public PantheonFactoryConfigurationBuilder setDiscoveryEnabled(final boolean discoveryEnabled) { + this.discoveryEnabled = discoveryEnabled; return this; } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java index 18054fab0a..2d38cb0218 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/factory/PantheonNodeFactory.java @@ -99,6 +99,17 @@ public PantheonNode createArchiveNode(final String name) throws IOException { .build()); } + public PantheonNode createArchiveNodeWithDiscoveryDisabledAndAdmin(final String name) + throws IOException { + return create( + new PantheonFactoryConfigurationBuilder() + .setName(name) + .setJsonRpcConfiguration(jsonRpcConfigWithAdmin()) + .webSocketEnabled() + .setDiscoveryEnabled(false) + .build()); + } + public PantheonNode createArchiveNodeWithP2pDisabled(final String name) throws IOException { return create( new PantheonFactoryConfigurationBuilder() @@ -324,6 +335,10 @@ private JsonRpcConfiguration jsonRpcConfigWithPermissioning() { return createJsonRpcConfigWithRpcApiEnabled(RpcApis.PERM); } + private JsonRpcConfiguration jsonRpcConfigWithAdmin() { + return createJsonRpcConfigWithRpcApiEnabled(RpcApis.ADMIN); + } + private JsonRpcConfiguration createJsonRpcConfigWithRpcApiEnabled(final RpcApi rpcApi) { final JsonRpcConfiguration jsonRpcConfig = createJsonRpcEnabledConfig(); final List rpcApis = new ArrayList<>(jsonRpcConfig.getRpcApis()); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/PantheonWeb3j.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/PantheonWeb3j.java index d488a8d5c1..3bbbce5c20 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/PantheonWeb3j.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/PantheonWeb3j.java @@ -136,4 +136,14 @@ public static class AddNodeResponse extends Response {} public static class RemoveNodeResponse extends Response {} public static class GetNodesWhitelistResponse extends Response> {} + + public static class AdminAddPeerResponse extends Response {} + + public Request adminAddPeer(final String enodeAddress) { + return new Request<>( + "admin_addPeer", + Collections.singletonList(enodeAddress), + web3jService, + AdminAddPeerResponse.class); + } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/Transaction.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/Transaction.java index beea7962ff..474f378c33 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/Transaction.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/transaction/Transaction.java @@ -12,6 +12,7 @@ */ package tech.pegasys.pantheon.tests.acceptance.dsl.transaction; +@FunctionalInterface public interface Transaction { T execute(final PantheonWeb3j node); diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/admin/AdminAddPeerAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/admin/AdminAddPeerAcceptanceTest.java new file mode 100644 index 0000000000..a023c07548 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/jsonrpc/admin/AdminAddPeerAcceptanceTest.java @@ -0,0 +1,53 @@ +/* + * 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.tests.acceptance.jsonrpc.admin; + +import tech.pegasys.pantheon.tests.acceptance.dsl.AcceptanceTestBase; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.PantheonNode; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.Cluster; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.ClusterConfiguration; +import tech.pegasys.pantheon.tests.acceptance.dsl.node.cluster.ClusterConfigurationBuilder; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class AdminAddPeerAcceptanceTest extends AcceptanceTestBase { + private PantheonNode nodeA; + private PantheonNode nodeB; + private Cluster p2pDisabledCluster; + + @Before + public void setUp() throws Exception { + final ClusterConfiguration clusterConfiguration = + new ClusterConfigurationBuilder().setAwaitPeerDiscovery(false).build(); + p2pDisabledCluster = new Cluster(clusterConfiguration, net); + nodeA = pantheon.createArchiveNodeWithDiscoveryDisabledAndAdmin("nodeA"); + nodeB = pantheon.createArchiveNodeWithDiscoveryDisabledAndAdmin("nodeB"); + p2pDisabledCluster.start(nodeA, nodeB); + } + + @After + public void tearDown() { + p2pDisabledCluster.stop(); + } + + @Test + public void adminAddPeerForcesConnection() { + final String nodeBEnode = nodeB.enodeUrl(); + nodeA.verify(net.awaitPeerCount(0)); + nodeA.verify(admin.addPeer(nodeBEnode)); + nodeA.verify(net.awaitPeerCount(1)); + nodeB.verify(net.awaitPeerCount(1)); + } +} From f39db5171d7cd667a08cda2e84ad69a1b75fe268 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 24 Jan 2019 18:15:13 -0700 Subject: [PATCH 06/31] add description to automatic benchmarks (#646) --- Jenkinsfile.benchmark | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile.benchmark b/Jenkinsfile.benchmark index 0c03445d16..d1545f0ecf 100644 --- a/Jenkinsfile.benchmark +++ b/Jenkinsfile.benchmark @@ -33,7 +33,8 @@ pipeline { "NETWORK=${params.NETWORK}", "DATASET=${params.DATASET}", "IMPORT_FILE=${params.IMPORT_FILE}", - "PANTHEON_SRC_DIR=${WORKSPACE}" + "PANTHEON_SRC_DIR=${WORKSPACE}", + "DESCRIPTION=automatic_${params.IMPORT_FILE}" ]) { dir('pantheon-benchmarks') { script { From eb71f3ea87c682ae67aaf9e142ec7e08bb02bb8a Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 25 Jan 2019 16:21:29 +1000 Subject: [PATCH 07/31] Additional logging details for IBFT (#650) --- .../consensus/ibft/IbftExtraData.java | 12 +++++ .../consensus/ibft/IbftProcessor.java | 1 + .../blockcreation/IbftMiningCoordinator.java | 2 +- .../IbftCoinbaseValidationRule.java | 5 +- .../IbftCommitSealsValidationRule.java | 5 +- .../methods/IbftDiscardValidatorVote.java | 7 ++- .../methods/IbftGetValidatorsByBlockHash.java | 5 ++ .../IbftGetValidatorsByBlockNumber.java | 5 ++ .../methods/IbftProposeValidatorVote.java | 10 ++++ .../ibft/network/ValidatorPeers.java | 7 ++- .../statemachine/IbftBlockHeightManager.java | 21 +++++---- .../ibft/statemachine/IbftController.java | 47 ++++++++++++++----- .../ibft/statemachine/IbftRound.java | 41 +++++++++++----- .../ibft/statemachine/RoundState.java | 16 +++++-- 14 files changed, 144 insertions(+), 40 deletions(-) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftExtraData.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftExtraData.java index e739fd6537..7ca182c560 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftExtraData.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftExtraData.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.StringJoiner; /** * Represents the data structure stored in the extraData field of the BlockHeader used when @@ -155,4 +156,15 @@ public Optional getVote() { public int getRound() { return round; } + + @Override + public String toString() { + return new StringJoiner(", ", IbftExtraData.class.getSimpleName() + "[", "]") + .add("vanityData=" + vanityData) + .add("seals=" + seals) + .add("vote=" + vote) + .add("round=" + round) + .add("validators=" + validators) + .toString(); + } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftProcessor.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftProcessor.java index 6dc9c37576..1c6a1a5796 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftProcessor.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftProcessor.java @@ -67,6 +67,7 @@ public void run() { nextIbftEvent().ifPresent(event -> eventMultiplexer.handleIbftEvent(event)); } // Clean up the executor service the round timer has been utilising + LOG.info("Shutting down IBFT event processor"); roundTimerExecutor.shutdownNow(); } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/blockcreation/IbftMiningCoordinator.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/blockcreation/IbftMiningCoordinator.java index 2f75979ec0..3327469489 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/blockcreation/IbftMiningCoordinator.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/blockcreation/IbftMiningCoordinator.java @@ -77,8 +77,8 @@ public void setExtraData(final BytesValue extraData) { @Override public void onBlockAdded(final BlockAddedEvent event, final Blockchain blockchain) { - LOG.info("New canonical head detected. {} ", event.isNewCanonicalHead()); if (event.isNewCanonicalHead()) { + LOG.info("New canonical head detected"); eventQueue.add(new NewChainHead(event.getBlock().getHeader())); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCoinbaseValidationRule.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCoinbaseValidationRule.java index 9dd8642355..733398f7f8 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCoinbaseValidationRule.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCoinbaseValidationRule.java @@ -44,7 +44,10 @@ public boolean validate( final Collection
storedValidators = validatorProvider.getValidators(); if (!storedValidators.contains(proposer)) { - LOGGER.trace("Block proposer is not a member of the validators."); + LOGGER.trace( + "Block proposer is not a member of the validators. proposer={}, validators={}", + proposer, + storedValidators); return false; } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCommitSealsValidationRule.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCommitSealsValidationRule.java index 81388085ea..3e5672f4dd 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCommitSealsValidationRule.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/headervalidationrules/IbftCommitSealsValidationRule.java @@ -67,7 +67,10 @@ private boolean validateCommitters( } if (!storedValidators.containsAll(committers)) { - LOGGER.trace("Not all committers are in the locally maintained validator list."); + LOGGER.trace( + "Not all committers are in the locally maintained validator list. validators={} committers={}", + storedValidators, + committers); return false; } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftDiscardValidatorVote.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftDiscardValidatorVote.java index 14951d470a..b11ac0a0d4 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftDiscardValidatorVote.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftDiscardValidatorVote.java @@ -20,7 +20,11 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class IbftDiscardValidatorVote implements JsonRpcMethod { + private static final Logger LOG = LogManager.getLogger(); private final VoteProposer voteProposer; private final JsonRpcParameter parameters; @@ -37,9 +41,8 @@ public String getName() { @Override public JsonRpcResponse response(final JsonRpcRequest req) { - final Address validatorAddress = parameters.required(req.getParams(), 0, Address.class); - + LOG.trace("Received RPC rpcName={} address={}", getName(), validatorAddress); voteProposer.discard(validatorAddress); return new JsonRpcSuccessResponse(req.getId(), true); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockHash.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockHash.java index 19f5edebb9..dccc43b211 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockHash.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockHash.java @@ -25,7 +25,11 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class IbftGetValidatorsByBlockHash implements JsonRpcMethod { + private static final Logger LOG = LogManager.getLogger(); private final Blockchain blockchain; private final BlockInterface blockInterface; @@ -52,6 +56,7 @@ public JsonRpcResponse response(final JsonRpcRequest request) { private Object blockResult(final JsonRpcRequest request) { final Hash hash = parameters.required(request.getParams(), 0, Hash.class); + LOG.trace("Received RPC rpcName={} blockHash={}", getName(), hash); final Optional blockHeader = blockchain.getBlockHeader(hash); return blockHeader .map( diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java index 8faf0332aa..f07ce98a21 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftGetValidatorsByBlockNumber.java @@ -24,8 +24,12 @@ import java.util.Optional; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class IbftGetValidatorsByBlockNumber extends AbstractBlockParameterMethod implements JsonRpcMethod { + private static final Logger LOG = LogManager.getLogger(); private final BlockInterface blockInterface; @@ -46,6 +50,7 @@ protected BlockParameter blockParameter(final JsonRpcRequest request) { protected Object resultByBlockNumber(final JsonRpcRequest request, final long blockNumber) { final Optional blockHeader = blockchainQueries().getBlockHeaderByNumber(blockNumber); + LOG.trace("Received RPC rpcName={} block={}", getName(), blockNumber); return blockHeader .map( header -> diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftProposeValidatorVote.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftProposeValidatorVote.java index 9217e2e21e..0862b40aac 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftProposeValidatorVote.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/jsonrpc/methods/IbftProposeValidatorVote.java @@ -13,6 +13,7 @@ package tech.pegasys.pantheon.consensus.ibft.jsonrpc.methods; import tech.pegasys.pantheon.consensus.common.VoteProposer; +import tech.pegasys.pantheon.consensus.common.VoteType; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.JsonRpcRequest; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.methods.JsonRpcMethod; @@ -20,7 +21,11 @@ import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcResponse; import tech.pegasys.pantheon.ethereum.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + public class IbftProposeValidatorVote implements JsonRpcMethod { + private static final Logger LOG = LogManager.getLogger(); private final VoteProposer voteProposer; private final JsonRpcParameter parameters; @@ -40,6 +45,11 @@ public JsonRpcResponse response(final JsonRpcRequest req) { final Address validatorAddress = parameters.required(req.getParams(), 0, Address.class); final Boolean add = parameters.required(req.getParams(), 1, Boolean.class); + LOG.trace( + "Received RPC rpcName={} voteType={} address={}", + getName(), + add ? VoteType.ADD : VoteType.DROP, + validatorAddress); if (add) { voteProposer.auth(validatorAddress); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/ValidatorPeers.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/ValidatorPeers.java index ffdcea6d49..d15929fe10 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/ValidatorPeers.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/ValidatorPeers.java @@ -75,6 +75,8 @@ public void send(final MessageData message, final Collection
blackList) private void sendMessageToSpecificAddresses( final Collection
recipients, final MessageData message) { + LOG.trace( + "Sending message to peers messageCode={} recipients={}", message.getCode(), recipients); recipients .stream() .map(peerConnections::get) @@ -84,7 +86,10 @@ private void sendMessageToSpecificAddresses( try { connection.sendForProtocol(PROTOCOL_NAME, message); } catch (final PeerNotConnected peerNotConnected) { - LOG.trace("Lost connection to a validator."); + LOG.trace( + "Lost connection to a validator. remoteAddress={} peerInfo={}", + connection.getRemoteAddress(), + connection.getPeer()); } }); } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java index 42a39e3cc1..10716e5bf1 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftBlockHeightManager.java @@ -124,11 +124,16 @@ public void handleBlockTimerExpiry(final ConsensusRoundIdentifier roundIdentifie @Override public void roundExpired(final RoundExpiry expire) { if (!expire.getView().equals(currentRound.getRoundIdentifier())) { - LOG.info("Ignoring Round timer expired which does not match current round."); + LOG.info( + "Ignoring Round timer expired which does not match current round. round={}, timerRound={}", + currentRound.getRoundIdentifier(), + expire.getView()); return; } - LOG.info("Round has expired, creating PreparedCertificate and notifying peers."); + LOG.info( + "Round has expired, creating PreparedCertificate and notifying peers. round={}", + currentRound.getRoundIdentifier()); final Optional preparedCertificate = currentRound.createPrepareCertificate(); @@ -150,21 +155,21 @@ public void roundExpired(final RoundExpiry expire) { @Override public void handleProposalPayload(final SignedData signedPayload) { - LOG.info("Received a Proposal Payload."); + LOG.debug("Received a Proposal Payload."); actionOrBufferMessage( signedPayload, currentRound::handleProposalMessage, RoundState::setProposedBlock); } @Override public void handlePreparePayload(final SignedData signedPayload) { - LOG.info("Received a prepare Payload."); + LOG.debug("Received a Prepare Payload."); actionOrBufferMessage( signedPayload, currentRound::handlePrepareMessage, RoundState::addPrepareMessage); } @Override public void handleCommitPayload(final SignedData payload) { - LOG.info("Received a commit Payload."); + LOG.debug("Received a Commit Payload."); actionOrBufferMessage(payload, currentRound::handleCommitMessage, RoundState::addCommitMessage); } @@ -192,7 +197,7 @@ public void handleRoundChangePayload(final SignedData signed final MessageAge messageAge = determineAgeOfPayload(signedPayload.getPayload()); if (messageAge == PRIOR_ROUND) { - LOG.info("Received RoundChange Payload for a prior round."); + LOG.debug("Received RoundChange Payload for a prior round. targetRound={}", targetRound); return; } @@ -230,10 +235,10 @@ public void handleNewRoundPayload(final SignedData signedPayloa final MessageAge messageAge = determineAgeOfPayload(payload); if (messageAge == PRIOR_ROUND) { - LOG.info("Received NewRound Payload for a prior round."); + LOG.info("Received NewRound Payload for a prior round={}", payload.getRoundIdentifier()); return; } - LOG.info("Received NewRound Payload for {}", payload.getRoundIdentifier().toString()); + LOG.info("Received NewRound Payload for {}", payload.getRoundIdentifier()); if (newRoundMessageValidator.validateNewRoundMessage(signedPayload)) { if (messageAge == FUTURE_ROUND) { 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 9b8f8de299..003d58d087 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 @@ -124,7 +124,9 @@ private void handleMessage(final Message message) { default: throw new IllegalArgumentException( - "Received message does not conform to any recognised IBFT message structure."); + String.format( + "Received message with messageCode=%d does not conform to any recognised IBFT message structure", + message.getData().getCode())); } } @@ -132,6 +134,10 @@ private

void consumeMessage( final Message message, final SignedData

signedPayload, final Consumer> handleMessage) { + LOG.debug( + "Received IBFT message messageType={} payload={}", + signedPayload.getPayload().getMessageType(), + signedPayload); if (processMessage(signedPayload, message)) { gossiper.gossipMessage(message); handleMessage.accept(signedPayload); @@ -142,15 +148,24 @@ public void handleNewBlockEvent(final NewChainHead newChainHead) { final BlockHeader newBlockHeader = newChainHead.getNewChainHeadHeader(); final BlockHeader currentMiningParent = currentHeightManager.getParentBlockHeader(); if (newBlockHeader.getNumber() < currentMiningParent.getNumber()) { - LOG.info("Discarding NewChainHead event, was for previous block height."); + LOG.debug( + "Discarding NewChainHead event, was for previous block height. chainHeight={} eventHeight={}", + currentMiningParent.getNumber(), + newBlockHeader.getNumber()); return; } if (newBlockHeader.getNumber() == currentMiningParent.getNumber()) { if (newBlockHeader.getHash().equals(currentMiningParent.getHash())) { - LOG.info("Discarding duplicate NewChainHead event."); + LOG.debug( + "Discarding duplicate NewChainHead event. chainHeight={} newBlockHash={} parentBlockHash", + newBlockHeader.getNumber(), + newBlockHeader.getHash(), + currentMiningParent.getHash()); } else { - LOG.error("Subsequent NewChainHead event at same block height indicates chain fork."); + LOG.error( + "Subsequent NewChainHead event at same block height indicates chain fork. chainHeight={}", + currentMiningParent.getNumber()); } return; } @@ -158,10 +173,14 @@ public void handleNewBlockEvent(final NewChainHead newChainHead) { } public void handleBlockTimerExpiry(final BlockTimerExpiry blockTimerExpiry) { - if (isMsgForCurrentHeight(blockTimerExpiry.getRoundIndentifier())) { - currentHeightManager.handleBlockTimerExpiry(blockTimerExpiry.getRoundIndentifier()); + final ConsensusRoundIdentifier roundIndentifier = blockTimerExpiry.getRoundIndentifier(); + if (isMsgForCurrentHeight(roundIndentifier)) { + currentHeightManager.handleBlockTimerExpiry(roundIndentifier); } else { - LOG.info("Block timer event discarded as it is not for current block height"); + LOG.debug( + "Block timer event discarded as it is not for current block height chainHeight={} eventHeight={}", + currentHeightManager.getChainHeight(), + roundIndentifier.getSequenceNumber()); } } @@ -169,7 +188,10 @@ public void handleRoundExpiry(final RoundExpiry roundExpiry) { if (isMsgForCurrentHeight(roundExpiry.getView())) { currentHeightManager.roundExpired(roundExpiry); } else { - LOG.info("Round expiry event discarded as it is not for current block height"); + LOG.debug( + "Round expiry event discarded as it is not for current block height chainHeight={} eventHeight={}", + currentHeightManager.getChainHeight(), + roundExpiry.getView().getSequenceNumber()); } } @@ -177,8 +199,7 @@ private void startNewHeightManager(final BlockHeader parentHeader) { currentHeightManager = ibftBlockHeightManagerFactory.create(parentHeader); currentHeightManager.start(); final long newChainHeight = currentHeightManager.getChainHeight(); - final List orDefault = futureMessages.getOrDefault(newChainHeight, emptyList()); - orDefault.forEach(this::handleMessage); + futureMessages.getOrDefault(newChainHeight, emptyList()).forEach(this::handleMessage); futureMessages.remove(newChainHeight); } @@ -189,7 +210,11 @@ private boolean processMessage(final SignedData msg, final Me } else if (isMsgForFutureChainHeight(msgRoundIdentifier)) { addMessageToFutureMessageBuffer(msgRoundIdentifier.getSequenceNumber(), rawMsg); } else { - LOG.info("IBFT message discarded as it is from a previous block height"); + LOG.debug( + "IBFT message discarded as it is from a previous block height messageType={} chainHeight={} eventHeight={}", + msg.getPayload().getMessageType(), + currentHeightManager.getChainHeight(), + msgRoundIdentifier.getSequenceNumber()); } return false; } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java index b9d7cc4d85..e8da16c704 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftRound.java @@ -84,8 +84,13 @@ public ConsensusRoundIdentifier getRoundIdentifier() { } public void createAndSendProposalMessage(final long headerTimeStampSeconds) { - LOG.info("Creating proposed block."); final Block block = blockCreator.createBlock(headerTimeStampSeconds); + final IbftExtraData extraData = IbftExtraData.decode(block.getHeader().getExtraData()); + LOG.info( + "Creating proposed block. round={} extraData={} blockHeader={}", + roundState.getRoundIdentifier(), + extraData, + block.getHeader()); transmitter.multicastProposal(roundState.getRoundIdentifier(), block); updateStateWithProposedBlock( @@ -99,11 +104,13 @@ public void startRoundWith( SignedData proposal; if (!latestCertificate.isPresent()) { - LOG.info("Multicasting NewRound with new block."); + LOG.trace("Multicasting NewRound with new block. round={}", roundState.getRoundIdentifier()); final Block block = blockCreator.createBlock(headerTimestamp); proposal = messageFactory.createSignedProposalPayload(getRoundIdentifier(), block); } else { - LOG.info("Multicasting NewRound from PreparedCertificate."); + LOG.trace( + "Multicasting NewRound from PreparedCertificate. round={}", + roundState.getRoundIdentifier()); proposal = createProposalFromPreparedCertificate(latestCertificate.get()); } transmitter.multicastNewRound(getRoundIdentifier(), roundChangeCertificate, proposal); @@ -132,6 +139,10 @@ private SignedData createProposalFromPreparedCertificate( blockHeader, extraDataToPublish)); final BlockHeader newHeader = headerBuilder.buildBlockHeader(); final Block newBlock = new Block(newHeader, block.getBody()); + LOG.debug( + "Created proposal from prepared certificate blockHeader={} extraData={}", + block.getHeader(), + extraDataToPublish); return messageFactory.createSignedProposalPayload(getRoundIdentifier(), newBlock); } @@ -139,7 +150,7 @@ public void handleProposalMessage(final SignedData msg) { LOG.info("Handling a Proposal message."); if (getRoundIdentifier().getRoundNumber() != 0) { - LOG.info("Illegally received a Proposal message when not in Round 0."); + LOG.error("Illegally received a Proposal message when not in Round 0."); return; } actionReceivedProposal(msg); @@ -149,7 +160,7 @@ public void handleProposalFromNewRound(final SignedData msg) { LOG.info("Handling a New Round Proposal."); if (getRoundIdentifier().getRoundNumber() == 0) { - LOG.info("Illegally received a NewRound message when in Round 0."); + LOG.error("Illegally received a NewRound message when in Round 0."); return; } actionReceivedProposal(msg.getPayload().getProposalPayload()); @@ -169,12 +180,12 @@ private void actionReceivedProposal(final SignedData msg) { } public void handlePrepareMessage(final SignedData msg) { - LOG.info("Received a prepare message."); + LOG.debug("Received a prepare message. round={}", roundState.getRoundIdentifier()); peerIsPrepared(msg); } public void handleCommitMessage(final SignedData msg) { - LOG.info("Received a commit message."); + LOG.debug("Received a commit message. round={}", roundState.getRoundIdentifier()); peerIsCommitted(msg); } @@ -189,7 +200,7 @@ private boolean updateStateWithProposedBlock(final SignedData m if (blockAccepted) { // There are times handling a proposed block is enough to enter prepared. if (wasPrepared != roundState.isPrepared()) { - LOG.info("Sending commit message."); + LOG.debug("Sending commit message. round={}", roundState.getRoundIdentifier()); final Block block = roundState.getProposedBlock().get(); transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), createCommitSeal(block)); } @@ -212,7 +223,7 @@ private void peerIsPrepared(final SignedData msg) { final boolean wasPrepared = roundState.isPrepared(); roundState.addPrepareMessage(msg); if (wasPrepared != roundState.isPrepared()) { - LOG.info("Sending commit message."); + LOG.debug("Sending commit message. round={}", roundState.getRoundIdentifier()); final Block block = roundState.getProposedBlock().get(); transmitter.multicastCommit(getRoundIdentifier(), block.getHash(), createCommitSeal(block)); } @@ -231,11 +242,17 @@ private void importBlockToChain() { IbftHelpers.createSealedBlock( roundState.getProposedBlock().get(), roundState.getCommitSeals()); - LOG.info("Importing block to chain."); - boolean result = + final long blockNumber = blockToImport.getHeader().getNumber(); + final IbftExtraData extraData = IbftExtraData.decode(blockToImport.getHeader().getExtraData()); + LOG.info("Importing block to chain. block={} extraData={}", blockNumber, extraData); + final boolean result = blockImporter.importBlock(protocolContext, blockToImport, HeaderValidationMode.FULL); if (!result) { - LOG.info("Failed to import block to chain."); + LOG.error( + "Failed to import block to chain. block={} extraData={} blockHeader={}", + blockNumber, + extraData, + blockToImport.getHeader()); } else { notifyNewBlockListeners(blockToImport); } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundState.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundState.java index b883909407..e63a62a01e 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundState.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/RoundState.java @@ -30,9 +30,12 @@ import java.util.stream.Collectors; import com.google.common.collect.Sets; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; // Data items used to define how a round will operate public class RoundState { + private static final Logger LOG = LogManager.getLogger(); private final ConsensusRoundIdentifier roundIdentifier; private final MessageValidator validator; @@ -79,6 +82,7 @@ public boolean setProposedBlock(final SignedData msg) { public void addPrepareMessage(final SignedData msg) { if (!proposalMessage.isPresent() || validator.validatePrepareMessage(msg)) { preparePayloads.add(msg); + LOG.debug("Round state added prepare message prepare={}", msg); } updateState(); } @@ -86,6 +90,7 @@ public void addPrepareMessage(final SignedData msg) { public void addCommitMessage(final SignedData msg) { if (!proposalMessage.isPresent() || validator.validateCommmitMessage(msg)) { commitPayloads.add(msg); + LOG.debug("Round state added commit message commit={}", msg); } updateState(); @@ -94,10 +99,15 @@ public void addCommitMessage(final SignedData msg) { private void updateState() { // NOTE: The quorum for Prepare messages is 1 less than the quorum size as the proposer // does not supply a prepare message - prepared = - (preparePayloads.size() >= prepareMessageCountForQuorum(quorum)) - && proposalMessage.isPresent(); + final long prepareQuorum = prepareMessageCountForQuorum(quorum); + prepared = (preparePayloads.size() >= prepareQuorum) && proposalMessage.isPresent(); committed = (commitPayloads.size() >= quorum) && proposalMessage.isPresent(); + LOG.debug( + "Round state updated prepared={} committed={} prepareQuorum={} quorum={}", + prepared, + committed, + prepareQuorum, + quorum); } public Optional getProposedBlock() { From ebbde8c6a9398be1198a2cdb5d8ce2c9442ad3a8 Mon Sep 17 00:00:00 2001 From: Nicolas MASSART Date: Fri, 25 Jan 2019 12:50:08 +0100 Subject: [PATCH 08/31] Nc 2026 network option (#645) fixes NC-2026 adds a `--network` option instead of separated options Networks are now defined using a `--network` option that takes the case insensitive name of an enum values as input : MAINNET, RINKEBY, ROPSTEN, GOERLI, DEV These presets are a set of network-id, genesis file and boonodes list. If the `--genesis-file` is provided with a valid JSON genesis file, Pantheon uses it instead of the default network. An empty bootnodes list is then the default value and network id is the chain id found in the genesis file. `--network-id` and `--bootnodes` options can override these defaults. You can of course also override the `--network-id` and `--bootnodes` for a predefined known network (using `--network`). User have no reason to set `--network` and `--genesis-file` options at the same time that would lead to misunderstandings so we raise an error to prevent both options to be defined at the same time on the command line. Also fixes NC-2189 renaming --private-genesis-file to --genesis-file Updates a lot of doc according to the options changes --- .../dsl/node/ThreadPantheonNodeRunner.java | 7 +- .../Accounts-for-Testing.md | 12 +- docs/Configuring-Pantheon/Logging.md | 27 ++- .../NetworkID-And-ChainID.md | 18 +- docs/Configuring-Pantheon/Networking.md | 4 +- .../Passing-JVM-Options.md | 2 +- .../Testing-Developing-Nodes.md | 24 ++- .../Using-Configuration-File.md | 20 +- docs/Consensus-Protocols/Clique.md | 33 ++-- docs/Consensus-Protocols/IBFT.md | 6 +- docs/Getting-Started/Run-Docker-Image.md | 37 ++-- docs/Getting-Started/Starting-Pantheon.md | 51 +++-- docs/Reference/JSON-RPC-API-Methods.md | 4 +- docs/Reference/Pantheon-CLI-Syntax.md | 179 ++++++----------- docs/Reference/Using-JSON-RPC-API.md | 4 +- docs/Tutorials/Create-Private-Network.md | 34 ++-- docs/Using-Pantheon/Debugging.md | 4 +- .../pantheon/cli/DefaultCommandValues.java | 2 +- .../pantheon/cli/EthNetworkConfig.java | 39 ++-- .../pegasys/pantheon/cli/NetworkName.java | 4 +- .../pegasys/pantheon/cli/PantheonCommand.java | 162 +++++++-------- .../pantheon/cli/StandaloneCommand.java | 9 +- .../pantheon/cli/PantheonCommandTest.java | 186 ++++++++++++++---- .../src/test/resources/complete_config.toml | 3 +- .../src/test/resources/everything_config.toml | 9 +- 25 files changed, 482 insertions(+), 398 deletions(-) diff --git a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java index 15079ebe66..91466c8a8e 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java +++ b/acceptance-tests/src/test/java/tech/pegasys/pantheon/tests/acceptance/dsl/node/ThreadPantheonNodeRunner.java @@ -12,7 +12,7 @@ */ package tech.pegasys.pantheon.tests.acceptance.dsl.node; -import static tech.pegasys.pantheon.cli.EthNetworkConfig.mainnet; +import static tech.pegasys.pantheon.cli.NetworkName.MAINNET; import tech.pegasys.pantheon.Runner; import tech.pegasys.pantheon.RunnerBuilder; @@ -55,7 +55,10 @@ public void startNode(final PantheonNode node) { final PantheonControllerBuilder builder = new PantheonControllerBuilder(); final EthNetworkConfig ethNetworkConfig = node.ethNetworkConfig() - .orElse(new EthNetworkConfig.Builder(mainnet()).setNetworkId(NETWORK_ID).build()); + .orElse( + new EthNetworkConfig.Builder(EthNetworkConfig.getNetworkConfig(MAINNET)) + .setNetworkId(NETWORK_ID) + .build()); final PantheonController pantheonController; try { pantheonController = diff --git a/docs/Configuring-Pantheon/Accounts-for-Testing.md b/docs/Configuring-Pantheon/Accounts-for-Testing.md index 3ed5609ff2..a6beeaf5ab 100644 --- a/docs/Configuring-Pantheon/Accounts-for-Testing.md +++ b/docs/Configuring-Pantheon/Accounts-for-Testing.md @@ -3,11 +3,13 @@ description: Ethereum accounts used for testing only on private network # Accounts for Testing -You can use existing accounts for testing by including them in the genesis file for a private network. Alternatively, Pantheon provides predefined accounts in development mode. +You can use existing accounts for testing by including them in the genesis file for a private network. +Alternatively, Pantheon provides predefined accounts in development mode. ## Development Mode - When you start Pantheon with the [`--dev-mode`](/Reference/Pantheon-CLI-Syntax/#dev-mode) command line option, the `dev.json` genesis file is used by default. + When you start Pantheon with the [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) + command line option, the `dev.json` genesis file is used by default. The `dev.json` genesis file defines the accounts below that can be used for testing. @@ -15,6 +17,8 @@ You can use existing accounts for testing by including them in the genesis file ## Genesis File -To use existing test accounts, specify the accounts and balances in a genesis file for your test network. For an example of defining accounts in the genesis file, refer to [`dev.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/dev.json). +To use existing test accounts, specify the accounts and balances in a genesis file for your test network. +For an example of defining accounts in the genesis file, refer to [`dev.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/dev.json). -Use the [`--genesis`](/Reference/Pantheon-CLI-Syntax/#genesis) command line option to start Pantheon with the genesis file defining the existing accounts. +Use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) command line option to +start Pantheon with the genesis file defining the existing accounts. diff --git a/docs/Configuring-Pantheon/Logging.md b/docs/Configuring-Pantheon/Logging.md index 57b29f4870..6c58a6e108 100644 --- a/docs/Configuring-Pantheon/Logging.md +++ b/docs/Configuring-Pantheon/Logging.md @@ -15,11 +15,16 @@ Pantheon uses Log4J2 for logging. There are two methods to configure logging beh ## Basic Log Level Setting -Use the [`--logging`](../Reference/Pantheon-CLI-Syntax.md#logging) command line option to specify the logging verbosity. The [`--logging`](../Reference/Pantheon-CLI-Syntax.md#logging) option changes the volume of events displayed in the log. +Use the [`--logging`](../Reference/Pantheon-CLI-Syntax.md#logging) command line option to specify +the logging verbosity. The [`--logging`](../Reference/Pantheon-CLI-Syntax.md#logging) option changes +the volume of events displayed in the log. ## Advanced Custom Logging -You can provide your own logging configuration using the standard Log4J2 configuration mechanisms. For example, the following Log4J2 configuration is the same as the [default configuration](https://github.com/PegaSysEng/pantheon/blob/master/pantheon/src/main/resources/log4j2.xml) except logging of stack traces for exceptions is excluded. +You can provide your own logging configuration using the standard Log4J2 configuration mechanisms. +For example, the following Log4J2 configuration is the same as the +[default configuration](https://github.com/PegaSysEng/pantheon/blob/master/pantheon/src/main/resources/log4j2.xml) +except logging of stack traces for exceptions is excluded. ```xml tab="log4j2.xml" @@ -41,12 +46,18 @@ You can provide your own logging configuration using the standard Log4J2 configu ``` -To use your custom configuration, set the environment variable `LOG4J_CONFIGURATION_FILE` to the location of your configuration file. +To use your custom configuration, set the environment variable `LOG4J_CONFIGURATION_FILE` to the +location of your configuration file. -If you have more specific requirements, you can create your own [log4j2 configuration](https://logging.apache.org/log4j/2.x/manual/configuration.html). +If you have more specific requirements, you can create your own +[log4j2 configuration](https://logging.apache.org/log4j/2.x/manual/configuration.html). -For Bash-based executions, you can set the variable for only the scope of the program execution by setting it before starting Pantheon. For example, to set the debug logging and start Pantheon connected to the Rinkeby testnet: +For Bash-based executions, you can set the variable for only the scope of the program execution by +setting it before starting Pantheon. -```bash -$ LOG4J_CONFIGURATION_FILE=./debug.xml bin/pantheon --rinkeby -``` \ No newline at end of file +!!!example + To set the debug logging and start Pantheon connected to the Rinkeby testnet: + + ```bash + $ LOG4J_CONFIGURATION_FILE=./debug.xml bin/pantheon --network=rinkeby + ``` \ No newline at end of file diff --git a/docs/Configuring-Pantheon/NetworkID-And-ChainID.md b/docs/Configuring-Pantheon/NetworkID-And-ChainID.md index bc6a186243..1e9351211c 100644 --- a/docs/Configuring-Pantheon/NetworkID-And-ChainID.md +++ b/docs/Configuring-Pantheon/NetworkID-And-ChainID.md @@ -3,11 +3,21 @@ description: Pantheon network ID and chain ID implementation # Network ID and Chain ID -Ethereum networks have a **network ID** and a **chain ID**. The network ID is specified using the [`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option and the chain ID is specified in the genesis file. +Ethereum networks have a **network ID** and a **chain ID**. The network ID can be specified using the +[`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option and the chain ID is specified +in the genesis file. -For most networks including mainnet and the public testnets, the network ID and the chain ID are the same. +For most networks including MainNet and the public testnets, the network ID and the chain ID are the +same and Pantheon network id default values are defined according to the genesis chain id value. -The network ID is automatically set by Pantheon when connecting to the Ethereum mainnet ==1==, Rinkeby ==4==, and Ropsten ==3==. +The network ID is automatically set by Pantheon to the chain ID when connecting to the Ethereum networks: -When using the [`--dev-mode`](../Reference/Pantheon-CLI-Syntax.md#dev-mode) or [`--genesis`](../Reference/Pantheon-CLI-Syntax.md#genesis) options, specify the network ID using the [`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option. +- **MainNet:** chain-id ==1==, network-id ==1== +- **Rinkeby:** chain-id ==4==, network-id ==4== +- **Ropsten:** chain-id ==3==, network-id ==3== +- **Dev:** chain-id ==2018==, network-id ==2018== + +When using the [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) or +[`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) options, you can override the +network ID using the [`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option. diff --git a/docs/Configuring-Pantheon/Networking.md b/docs/Configuring-Pantheon/Networking.md index 884034f854..ffc8a6deac 100644 --- a/docs/Configuring-Pantheon/Networking.md +++ b/docs/Configuring-Pantheon/Networking.md @@ -20,7 +20,9 @@ If connections are not getting through the firewalls, ensure the peer discovery ## Peer Discovery Port -The [`--p2p-listen`](../Reference/Pantheon-CLI-Syntax.md#p2p-listen) option specifies the host and port on which P2P peer discovery listens. The default is ==127.0.0.1:30303==. +The [`--p2p-host`](../Reference/Pantheon-CLI-Syntax.md#p2p-host) and [`--p2p-port`](../Reference/Pantheon-CLI-Syntax.md#p2p-port) +options specifies the host and port on which P2P peer discovery listens. The default is ==127.0.0.1== +for host and ==30303== for port. ## Limiting Peers diff --git a/docs/Configuring-Pantheon/Passing-JVM-Options.md b/docs/Configuring-Pantheon/Passing-JVM-Options.md index fdcdfa2a5a..482c718267 100644 --- a/docs/Configuring-Pantheon/Passing-JVM-Options.md +++ b/docs/Configuring-Pantheon/Passing-JVM-Options.md @@ -12,5 +12,5 @@ For Bash-based executions, you can set the variable for only the scope of the pr !!! example ```bash $ PANTHEON_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 \ - $ bin/pantheon --rinkeby + $ bin/pantheon --network=rinkeby ``` \ No newline at end of file diff --git a/docs/Configuring-Pantheon/Testing-Developing-Nodes.md b/docs/Configuring-Pantheon/Testing-Developing-Nodes.md index 08672e4200..0b4a0e2866 100644 --- a/docs/Configuring-Pantheon/Testing-Developing-Nodes.md +++ b/docs/Configuring-Pantheon/Testing-Developing-Nodes.md @@ -21,41 +21,45 @@ To start a bootnode for a private network: !!! example ```bash - pantheon --genesis=privateNetworkGenesis.json --datadir=nodeDataDir export-pub-key bootnode + pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath export-pub-key bootnode ``` - Where `privateNetworkGenesis.json` and `nodeDataDir` are changed to the relevant values for + Where `privateNetworkGenesis.json` and `nodeDataPath` are changed to the relevant values for your private network. The node public key is exported to the `bootnode` file. 2. Start the bootnode, specifying: - * No arguments for the [`--bootnodes` option](../Reference/Pantheon-CLI-Syntax.md#bootnodes) because this is the bootnode. - * Network ID for your private network. * Genesis file and data directory as in the previous step. !!! example ``` - pantheon --bootnodes --genesis=privateNetworkGenesis.json --datadir=nodeDataDir --network-id 123 + pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath ``` -To specify this bootnode for another node, the enode URL for the `--bootnodes` option is `enode://@` where: +To specify this bootnode for another node, the enode URL for the [`--bootnodes`](../Reference/Pantheon-CLI-Syntax.md#bootnodes) +option is `enode://@` where: * `` is the node public key written to the specified file (`bootnode` in the above example) excluding the initial 0x. -* `` is the host and port the bootnode is listening on for P2P peer discovery. Specified by the `--p2p-listen` option for the bootnode (default is `127.0.0.1:30303`). +* `` is the host and port the bootnode is listening on for P2P peer discovery. +Specified by the [`--p2p-host` option](../Reference/Pantheon-CLI-Syntax.md#p2p-host) and +[`--p2p-port` option](../Reference/Pantheon-CLI-Syntax.md#p2p-port) option for the bootnode +(default host is `127.0.0.1` and port is `30303`). !!! example - If the `--p2p-listen` option is not specified and the node public key exported is `0xc35c3ec90a8a51fd5703594c6303382f3ae6b2ecb9589bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f` + If the [`--p2p-host`](../Reference/Pantheon-CLI-Syntax.md#p2p-host) or [`--p2p-port`](../Reference/Pantheon-CLI-Syntax.md#p2p-port) options are not specified and the node public key exported is `0xc35c3ec90a8a51fd5703594c6303382f3ae6b2ecb9589bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f` The enode URL is: `enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb9589bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303` !!! info - The default host and port for P2P peer discovery is `127.0.0.1:30303`. Use the `--p2p-listen` option to specify a host and port. + The default host and port for P2P peer discovery is `127.0.0.1:30303`. + Use the [`--p2p-host`](../Reference/Pantheon-CLI-Syntax.md#p2p-host) and + [`--p2p-port`](../Reference/Pantheon-CLI-Syntax.md#p2p-port) option to specify a host and port. To start a node specifying the bootnode for P2P discovery: !!! example ```bash - pantheon --genesis=privateNetworkGenesis.json --datadir=nodeDataDir --p2p-listen=127.0.0.1:30301 --network-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303 + pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath --p2p-host=127.0.0.1 --p2p-port=30301 --network-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303 ``` \ No newline at end of file diff --git a/docs/Configuring-Pantheon/Using-Configuration-File.md b/docs/Configuring-Pantheon/Using-Configuration-File.md index 676c3b1070..0f02cb5816 100644 --- a/docs/Configuring-Pantheon/Using-Configuration-File.md +++ b/docs/Configuring-Pantheon/Using-Configuration-File.md @@ -3,7 +3,7 @@ To specify command line options in a file, use a TOML configuration file. The configuration file can be saved and reused across node startups. To specify the configuration file, -use the [`--config` option](../Reference/Pantheon-CLI-Syntax.md#config). +use the [`--config-file` option](../Reference/Pantheon-CLI-Syntax.md#config). To override an option specified in the configuration file, specify the same option on the command line. When an option is specified in both places, Pantheon is started with the command line value. @@ -25,17 +25,23 @@ Specific differences between the command line and the TOML file format are: !!!example "Example TOML configuration file" ```toml # Valid TOML config file - datadir="~/pantheondata" # Path + data-path="~/pantheondata" # Path # Network bootnodes=["enode://001@123:4567", "enode://002@123:4567", "enode://003@123:4567"] - p2p-listen="1.2.3.4:1234" # IP:port + + p2p-host="1.2.3.4" + p2p-port=1234 max-peers=42 - rpc-listen="5.6.7.8:5678" # IP:port - ws-listen="9.10.11.12:9101" # IP:port + + rpc-http-host="5.6.7.8" + rpc-http-port=5678 + + rpc-ws-host="9.10.11.12" + rpc-ws-port=9101 # Chain - genesis="~/genesis.json" # Path to the custom genesis file + genesis-file="~/genesis.json" # Path to the custom genesis file # Mining miner-enabled=true @@ -44,5 +50,5 @@ Specific differences between the command line and the TOML file format are: !!!example "Starting Pantheon with a Configuration File" ```bash - pantheon --config=/home/me/me_node/config.toml + pantheon --config-file=/home/me/me_node/config.toml ``` diff --git a/docs/Consensus-Protocols/Clique.md b/docs/Consensus-Protocols/Clique.md index b2999a2d02..e6141673db 100644 --- a/docs/Consensus-Protocols/Clique.md +++ b/docs/Consensus-Protocols/Clique.md @@ -5,13 +5,17 @@ source: rinkeby.json # Clique -Pantheon implements the Clique Proof-of-Authority (PoA) consensus protocol. Clique is used by the Rinkeby testnet and can be used for private networks. +Pantheon implements the Clique Proof-of-Authority (PoA) consensus protocol. Clique is used by the +Rinkeby testnet and can be used for private networks. -In Clique networks, transactions and blocks are validated by approved accounts, known as signers. Signers take turns to create the next block. Existing signers propose and vote to add or remove signers. +In Clique networks, transactions and blocks are validated by approved accounts, known as signers. +Signers take turns to create the next block. Existing signers propose and vote to add or remove signers. ## Genesis File -To use Clique in a private network, Pantheon requires a Clique genesis file. When connecting to Rinkeby, Pantheon uses the [`rinkeby.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/rinkeby.json) genesis file in the `/pantheon/config/src/main/resources` directory. +To use Clique in a private network, Pantheon requires a Clique genesis file. When connecting to Rinkeby, +Pantheon uses the [`rinkeby.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/rinkeby.json) +genesis file in the `/pantheon/config/src/main/resources` directory. A PoA genesis file defines properties specific to Clique: @@ -37,14 +41,17 @@ The properties specific to Clique are: * `epoch` - Number of blocks after which to reset all votes. * `extraData` - Initial signers are specified after the 32 bytes reserved for vanity data. -To connect to the Rinkeby testnet, start Pantheon with the [`--rinkeby`](../Reference/Pantheon-CLI-Syntax.md#rinkeby) command line option. To start a node on a Clique private network, use the [`--genesis`](../Reference/Pantheon-CLI-Syntax.md#genesis`) option to specify the custom genesis file. +To connect to the Rinkeby testnet, start Pantheon with the [`--network=rinkeby`](../Reference/Pantheon-CLI-Syntax.md#network) +command line option. To start a node on a Clique private network, use the +[`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) option to specify the custom genesis file. ## Adding and Removing Signers To propose adding or removing signers using the JSON-RPC methods, enable the HTTP interface -using [`--rpc-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-enabled) or WebSockets interface using -[`--ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#ws-enabled). If also using the [`--rpc-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-api) - or [`--ws-api`](../Reference/Pantheon-CLI-Syntax.md#ws-api) options, include `CLIQUE`. +using [`--rpc-http-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled) or WebSockets interface using +[`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled). If also using the +[`--rpc-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-api) +or [`--ws-api`](../Reference/Pantheon-CLI-Syntax.md#ws-api) options, include `CLIQUE`. The JSON-RPC methods to add or remove signers are: @@ -60,9 +67,11 @@ To propose adding a signer, call `clique_propose` specifying the address of the When the next block is created by the signer, a vote is added to the block for the proposed signer. -When more than half of the existing signers propose adding the signer and their votes have been distributed in blocks, the signer is added and can begin signing blocks. +When more than half of the existing signers propose adding the signer and their votes have been +distributed in blocks, the signer is added and can begin signing blocks. -Use `clique_getSigners` to return a list of the signers and to confirm that your proposed signer has been added. +Use `clique_getSigners` to return a list of the signers and to confirm that your proposed signer has +been added. !!! example "JSON-RPC clique_getSigners Request Example" ```bash curl -X POST --data '{"jsonrpc":"2.0","method":"clique_getSigners","params":["latest"], "id":1}' @@ -74,11 +83,13 @@ To discard your proposal after confirming the signer was added, call `clique_dis curl -X POST --data '{"jsonrpc":"2.0","method":"clique_discard","params":["0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73"], "id":1}' ``` -The process for removing a signer is the same as adding a signer except you specify `false` as the second parameter of `clique_propose`. +The process for removing a signer is the same as adding a signer except you specify `false` as the +second parameter of `clique_propose`. ### Epoch Transition -At each epoch transition, all pending votes collected from received blocks are discarded. Existing proposals remain in effect and signers re-add their vote the next time they create a block. +At each epoch transition, all pending votes collected from received blocks are discarded. +Existing proposals remain in effect and signers re-add their vote the next time they create a block. Define the number of blocks between epoch transitions in the genesis file. diff --git a/docs/Consensus-Protocols/IBFT.md b/docs/Consensus-Protocols/IBFT.md index 6e8341de82..245a88f598 100644 --- a/docs/Consensus-Protocols/IBFT.md +++ b/docs/Consensus-Protocols/IBFT.md @@ -71,13 +71,13 @@ Properties that have specific values in IBFT 2.0 genesis files are: * `difficulty` - `0x1` * `mixHash` - `0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365` for Istanbul block identification. -To start a node on an IBFT 2.0 private network, use the [`--genesis`](../Reference/Pantheon-CLI-Syntax.md#genesis`) option to specify the custom genesis file. +To start a node on an IBFT 2.0 private network, use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file`) option to specify the custom genesis file. ## Adding and Removing Validators To propose adding or removing validators using the JSON-RPC methods, enable the HTTP interface -using [`--rpc-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-enabled) or WebSockets interface using -[`--ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#ws-enabled). If also using the [`--rpc-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-api) +using [`--rpc-http-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled) or WebSockets interface using +[`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled). If also using the [`--rpc-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-api) or [`--ws-api`](../Reference/Pantheon-CLI-Syntax.md#ws-api) options, include `IBFT`. The JSON-RPC methods to add or remove validators are: diff --git a/docs/Getting-Started/Run-Docker-Image.md b/docs/Getting-Started/Run-Docker-Image.md index c97af780d0..e4acdf8087 100644 --- a/docs/Getting-Started/Run-Docker-Image.md +++ b/docs/Getting-Started/Run-Docker-Image.md @@ -47,29 +47,32 @@ docker run pegasyseng/pantheon:latest !!!attention You cannot use the following Pantheon command line options when running Pantheon from the Docker image: - * [`--datadir`](../Reference/Pantheon-CLI-Syntax.md#datadir), see [Persisting Data](#persisting-data) - * [`--config`](../Reference/Pantheon-CLI-Syntax.md#config), see [Custom Configuration File](#custom-configuration-file) - * [`--genesis`](../Reference/Pantheon-CLI-Syntax.md#genesis), see [Custom Genesis File](#custom-genesis-file). - * [`--rpc-listen`](../Reference/Pantheon-CLI-Syntax.md#rpc-listen), [`--p2plisten`](../Reference/Pantheon-CLI-Syntax.md#p2plisten), [`--ws-listen`](../Reference/Pantheon-CLI-Syntax.md#ws-listen), see [Exposing Ports](#exposing-ports) + * [`--data-path`](../Reference/Pantheon-CLI-Syntax.md#data-path), see [Persisting Data](#persisting-data) + * [`--config-file`](../Reference/Pantheon-CLI-Syntax.md#config), see [Custom Configuration File](#custom-configuration-file) + * [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file), see [Custom Genesis File](#custom-genesis-file). + * [`--rpc-http-host`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-host) and [`--rpc-http-port`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-port), + [`--p2p-host`](../Reference/Pantheon-CLI-Syntax.md#p2p-host) and [`--p2p-port`](../Reference/Pantheon-CLI-Syntax.md#p2p-port), + [`--rpc-ws-host`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-host) and [`--rpc-ws-port`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-port), + see [Exposing Ports](#exposing-ports) All other [Pantheon command line options](/Reference/Pantheon-CLI-Syntax) work in the same way as when Pantheon is installed locally. ### Data Directory -Specify a Docker volume for the data directory. This is the equivalent of specifying the [`--datadir`](../Reference/Pantheon-CLI-Syntax.md#datadir) option. +Specify a Docker volume for the data directory. This is the equivalent of specifying the [`--data-path`](../Reference/Pantheon-CLI-Syntax.md#data-path) option. To run Pantheon specifying a volume for the data directory: ```bash -docker run --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest +docker run --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest ``` -Where `` is the volume to which the data is saved. +Where `` is the volume to which the data is saved. ### Custom Configuration File -Specify a custom configuration file to provide a file containing key/value pairs for command line options. This is the equivalent of specifying the [`--config`](../Reference/Pantheon-CLI-Syntax.md#config) option. +Specify a custom configuration file to provide a file containing key/value pairs for command line options. This is the equivalent of specifying the [`--config-file`](../Reference/Pantheon-CLI-Syntax.md#config-file) option. To run Pantheon specifying a custom configuration file: ```bash @@ -85,7 +88,7 @@ Where `myconf.toml` is your custom configuration file and `path` is the absolute ### Custom Genesis File -Specify a custom genesis file to configure the blockchain. This is equivalent to specifying the `--genesis` option. +Specify a custom genesis file to configure the blockchain. This is equivalent to specifying the `--genesis-file` option. To run Pantheon specifying a custom genesis file: ```bash @@ -102,18 +105,18 @@ Where `mygenesis.json` is your custom configuration file and `path` is the absol ### Exposing Ports Expose ports for P2P peer discovery, JSON-RPC service, and WebSockets. This is required to use the -defaults ports or specify different ports (the equivalent of specifying the [`--rpc-listen`](../Reference/Pantheon-CLI-Syntax.md#rpc-listen), -[`--p2p-listen`](../Reference/Pantheon-CLI-Syntax.md#p2p-listen), [`--ws-listen`](../Reference/Pantheon-CLI-Syntax.md#ws-listen) options). +defaults ports or specify different ports (the equivalent of specifying the [`--rpc-http-port`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-port), +[`--p2p-port`](../Reference/Pantheon-CLI-Syntax.md#p2p-port), [`--rpc-ws-port`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-port) options). To run Pantheon exposing local ports for access: ```bash -$ docker run -p :8545 -p :8546 -p :30303 pegasyseng/pantheon:latest --rpc-enabled --ws-enabled +$ docker run -p :8545 -p :8546 -p :30303 pegasyseng/pantheon:latest --rpc-http-enabled --rpc-ws-enabled ``` !!!example To enable RPC calls to http://127.0.0.1:8545 and P2P discovery on http://127.0.0.1:13001: ```bash - docker run -p 8545:8545 -p 13001:30303 pegasyseng/pantheon:latest --rpc-enabled + docker run -p 8545:8545 -p 13001:30303 pegasyseng/pantheon:latest --rpc-http-enabled ``` ## Starting Pantheon @@ -128,7 +131,7 @@ docker run -p 30303:30303 --mount type=bind,source=/,target=/ To run a node on mainnet with the HTTP JSON-RPC service enabled: ```bash -docker run -p 8545:8545 -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --rpc-enabled +docker run -p 8545:8545 -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --rpc-http-enabled ``` ## Run a Node on Ropsten Testnet @@ -137,21 +140,21 @@ Save a local copy of the [Ropsten genesis file](https://github.com/PegaSysEng/pa To run a node on Ropsten: ```bash -docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon --mount type=bind,source=//ropsten.json,target=/etc/pantheon/genesis.json pegasyseng/pantheon:latest --network-id=3 --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303 +docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon --network=ropsten ``` ## Run a Node on Rinkeby Testnet To run a node on Rinkeby: ```bash -docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --rinkeby +docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --network=rinkeby ``` ## Run a Node for Testing To run a node that mines blocks at a rate suitable for testing purposes with WebSockets enabled: ```bash -docker run -p 8546:8546 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --dev-mode --bootnodes= --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-cors-origins "all" --ws-enabled +docker run -p 8546:8546 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins "all" --rpc-ws-enabled --network=dev ``` ## Stopping Pantheon and Cleaning up Resources diff --git a/docs/Getting-Started/Starting-Pantheon.md b/docs/Getting-Started/Starting-Pantheon.md index d2c2c14707..4a89b8d1c8 100644 --- a/docs/Getting-Started/Starting-Pantheon.md +++ b/docs/Getting-Started/Starting-Pantheon.md @@ -13,7 +13,7 @@ Nodes can connect to the Ethereum mainnet, public testnets such as Ropsten, or p ## Local Block Data When connecting to a network other than the network previously connected to, you must either delete the local block data -or use the [`--datadir`](../Reference/Pantheon-CLI-Syntax.md#datadir) option to specify a different data directory. +or use the [`--data-path`](../Reference/Pantheon-CLI-Syntax.md#data-path) option to specify a different data directory. To delete the local block data, delete the `database` directory in the `pantheon/build/distribution/pantheon-` directory. @@ -22,16 +22,18 @@ To delete the local block data, delete the `database` directory in the `pantheon Pantheon specifies the genesis configuration, and sets the network ID and bootnodes when connecting to [Mainnet](#run-a-node-on-ethereum-mainnet), [Goerli](#run-a-node-on-goerli-testnet), [Rinkeby](#run-a-node-on-rinkeby-testnet), and [Ropsten](#run-a-node-on-ropsten-testnet). -When [`--dev-mode`](../Reference/Pantheon-CLI-Syntax.md#dev-mode) is specified, Pantheon uses the development mode genesis configuration. +When [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) is specified, Pantheon uses the +development mode genesis configuration associated to a low difficulty. +The default bootnodes setting for dev network is to have an empty bootnodes list. The genesis files defining the genesis configurations are in the [Pantheon source files](https://github.com/PegaSysEng/pantheon/tree/master/config/src/main/resources). To define a genesis configuration, create a genesis file (for example, `genesis.json`) and specify the file -using the [`--genesis`](../Reference/Pantheon-CLI-Syntax.md#genesis) option. +using the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) option. ## Confirm Node is Running -If you have started Pantheon with the [`--rpc-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-enabled) option, use [cURL](https://curl.haxx.se/) to +If you have started Pantheon with the [`--rpc-http-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled) option, use [cURL](https://curl.haxx.se/) to call [JSON-RPC API methods](../Reference/JSON-RPC-API-Methods.md) to confirm the node is running. !!!example @@ -67,64 +69,55 @@ call [JSON-RPC API methods](../Reference/JSON-RPC-API-Methods.md) to confirm the To run a node that mines blocks at a rate suitable for testing purposes: ```bash -pantheon --dev-mode --network-id="2018" --bootnodes --miner-enabled --miner-coinbase=0xfe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-cors-origins="all" --host-whitelist="all" --ws-enabled --rpc-enabled --datadir=/tmp/tmpDatdir +pantheon --network=dev --miner-enabled --miner-coinbase=0xfe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins="all" --host-whitelist="all" --rpc-rpc-ws-enabled --rpc-http-enabled --data-path=/tmp/tmpDatdir ``` -Alternatively, use the following [configuration file](../Configuring-Pantheon/Using-Configuration-File.md) and `--bootnodes` on the command line to start a node with the same options as above: +Alternatively, use the following [configuration file](../Configuring-Pantheon/Using-Configuration-File.md) +on the command line to start a node with the same options as above: ```toml -dev-mode=true -network-id="2018" +network="dev" miner-enabled=true miner-coinbase="0xfe3b557e8fb62b89f4916b721be55ceb828dbd73" rpc-cors-origins=["all"] host-whitelist=["all"] -ws-enabled=true -rpc-enabled=true -datadir="/tmp/tmpDatadir" +rpc-ws-enabled=true +rpc-http-enabled=true +data-path="/tmp/tmpdata-path" ``` ## Run a Node on Ropsten Testnet -!!!note - From v0.8.2, use the [`--ropsten` option](../Reference/Pantheon-CLI-Syntax.md#options) - instead of the following options. For v0.8.1, use the following options. - To run a node on Ropsten: ```bash -pantheon --network-id=3 --genesis=/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303 +pantheon --network=ropsten ``` To run a node on Ropsten with the HTTP JSON-RPC service enabled and allow Remix to access the node: ```bash -pantheon --rpc-enabled --rpc-cors-origins "http://remix.ethereum.org" --network-id=3 --genesis=/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303 +pantheon --network=ropsten --rpc-http-enabled --rpc-http-cors-origins "http://remix.ethereum.org" ``` - -Where `` is the path to the `/pantheon` directory. - + ## Run a Node on Rinkeby Testnet To run a node on Rinkeby specifying a data directory: ```bash -pantheon --rinkeby --datadir=/rinkebyDataDir +pantheon --network=rinkeby --data-path=/ ``` -Where `` and `` are the path and directory where the Rinkeby chain data is to be saved. +Where `` and `` are the path and directory where the Rinkeby chain data is to be saved. ## Run a Node on Goerli Testnet To run a node on [Goerli](https://github.com/goerli/testnet) specifying a data directory: ```bash -pantheon --goerli --datadir=/ +pantheon --network=goerli --data-path=/ ``` -Where `` and `` are the path and directory where the Goerli chain data is to be saved. +Where `` and `` are the path and directory where the Goerli chain data is to be saved. -!!!note - This option is only available from v0.8.3. - ## Run a Node on Ethereum Mainnet To run a node on the Ethereum mainnet: @@ -133,8 +126,8 @@ To run a node on the Ethereum mainnet: pantheon ``` -To run a node on mainnet with the HTTP JSON-RPC service enabled: +To run a node on mainnet with the HTTP JSON-RPC service enabled and available for localhost only: ```bash -pantheon --rpc-enabled +pantheon --rpc-http-enabled ``` diff --git a/docs/Reference/JSON-RPC-API-Methods.md b/docs/Reference/JSON-RPC-API-Methods.md index 70e978ff1d..295e8c5569 100644 --- a/docs/Reference/JSON-RPC-API-Methods.md +++ b/docs/Reference/JSON-RPC-API-Methods.md @@ -320,7 +320,7 @@ You can get the Ethereum account address from a client such as MetaMask or Ether !!!example ```bash - $ bin/pantheon --miner-coinbase="0xfe3b557e8fb62b89f4916b721be55ceb828dbd73" --rpc-enabled + $ bin/pantheon --miner-coinbase="0xfe3b557e8fb62b89f4916b721be55ceb828dbd73" --rpc-http-enabled ``` **Parameters** @@ -2062,7 +2062,7 @@ Lists the validators defined in the specified block. ### ibft_proposeValidatorVote -Proposes [adding or removing a validator]((../Consensus-Protocols/IBFT.md#adding-and-removing-validators)) with the specified address. +Proposes [adding or removing a validator](../Consensus-Protocols/IBFT.md#adding-and-removing-validators)) with the specified address. **Parameters** diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index 26d7c37e75..2a7dc8e6d8 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -4,9 +4,10 @@ description: Pantheon commande line interface reference # Pantheon Command Line !!! important "Breaking Changes in v0.9" - In v0.9, changes will be made to the command line options to improve usability. These will be breaking changes; that is, - in many cases the v0.8 command line options will no longer work. This reference and the rest of the documentation will be - updated to reflect these changes. Any further information required about the changes will be included in the v0.9 release notes. + In v0.9 the command line changed to improve usability. These are breaking changes; that is, + in many cases the v0.8 command line options will no longer work. This reference and the rest of + the documentation will be updated to reflect these changes. Any further information required + about the changes are included in the v0.9 release notes. This reference describes the syntax of the Pantheon Command Line Interface (CLI) options and subcommands. @@ -79,12 +80,16 @@ bootnodes=["enode://c35c3...d615f@1.2.3.4:30303","enode://f42c13...fc456@1.2.3.5 List of comma-separated enode URLs for P2P discovery bootstrap. -When connecting to mainnet or public testnets, the default is a predefined list of enode URLs. -Specify bootnodes when connecting to a [private network](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes). +When connecting to MainNet or public testnets, the default is a predefined list of enode URLs. + +On custom networks defined by [`--genesis-file`](#genesis-file) option, +an empty list of bootnodes is defined by default unless you define custom bootnodes as described in +[private network documentation](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes). !!! note - Specifying a node is a [bootnode](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes) - must be done on the command line not in a [configuration file](../Configuring-Pantheon/Using-Configuration-File.md). + Specifying that a node is a [bootnode](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes) + must be done on the command line using [`--bootnodes`](#bootnodes) option without value, + not in a [configuration file](../Configuring-Pantheon/Using-Configuration-File.md). ### config-file @@ -93,7 +98,7 @@ Specify bootnodes when connecting to a [private network](../Configuring-Pantheon ``` ```bash tab="Example Command Line" ---config=/home/me/me_node/config.toml +--config-file=/home/me/me_node/config.toml ``` The path to the [TOML configuration file](../Configuring-Pantheon/Using-Configuration-File.md). @@ -121,33 +126,13 @@ The path to the Pantheon data directory. The default is the `/build/distribution !!!note This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#persisting-data). +### genesis-file -### dev-mode - -!!!important - This option is deprecated in favor of the new `--network` option. - It will be completely removed in the 0.9 release. - -```bash tab="Syntax" ---dev-mode -``` - -```bash tab="Example Configuration File" -dev-mode=true -``` - -Set this option to `true` to run in development mode. -For example, specify this option to perform CPU mining more easily in a private test network. -In development mode, a custom genesis configuration specifies the chain ID. -When using this option, also set the [`--network-id`](#network-id) option to the network you use for development. -Default is `false`. - - -!!!note - The [`--dev-mode`](#dev-mode) option overrides the [`--genesis`](#genesis) option. If both are specified, the development mode configuration is used. - +Genesis file is used to create a custom network. -### genesis-file +!!!tip + To use a public Ethereum network such as Rinkeby, use the [`--network`](#network) option. + The network option defines the genesis file for public networks. ```bash tab="Syntax" --genesis-file= @@ -161,36 +146,13 @@ Default is `false`. genesis-file="/home/me/me_node/customGenesisFile.json" ``` -The path to the genesis file. The default is the embedded genesis file for the Ethereum mainnet. -When using this option, it is recommended to also set the [`--network-id`](#network-id) option. - -!!!note - This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#custom-genesis-file). - -!!!note - The [`--genesis`](#genesis) option is overridden by the [`--dev-mode`](#dev-mode) option. - If both are specified, the specified genesis file is ignored and the development mode configuration used. - - -### goerli +The path to the genesis file. !!!important - This option is deprecated in favor of the new `--network` option. - It will be completely removed in the 0.9 release. - -```bash tab="Syntax" ---goerli -``` - -```bash tab="Example Configuration File" -goerli=true -``` - -Uses the Goerli test network. Default is false. + The [`--genesis-file`](#genesis-file) and [`--network`](#network) option can't be used at the same time. !!!note - This option is only available from v0.8.3. - + This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#custom-genesis-file). ### host-whitelist @@ -367,11 +329,47 @@ min-gas-price="1337" The minimum price that a transaction offers for it to be included in a mined block. The default is 1000. -### network-id +### network + +```bash tab="Syntax" +--network= +``` + +```bash tab="Example Command Line" +--network=rinkeby +``` + +```bash tab="Example Configuration File" +network="rinkeby" +``` + +Predefined network configuration. +The default is `mainnet`. +Possible values are : + +`mainnet` +: Main Ethereum network + +`ropsten` +: PoW test network similar to current main Ethereum network. + +`rinkeby` +: PoA test network using Clique. + +`goerli` +: PoA test network using Clique. + +`dev` +: PoW development network with a very low difficulty to enable local CPU mining. + +!!!note + Values are case insensitive, so either `mainnet` or `MAINNET` works. + !!!important - This option is deprecated in favor of the new `--network` option. - It will be completely removed in the 0.9 release. + The [`--network`](#network) and [`--genesis-file`](#genesis-file) option can't be used at the same time. + +### network-id ```bash tab="Syntax" --network-id= @@ -386,7 +384,9 @@ network-id="8675309" ``` P2P network identifier. -The default is set to mainnet with value `1`. + +This option can be used to override your current network ID. +The default value is the current network chain ID which is defined in the genesis file. ### no-discovery @@ -451,21 +451,6 @@ Not intended for use with mainnet or public testnets. !!!note Permissioning is under development and will be available in v1.0. -### ottoman - -!!!important - This option is deprecated in favor of the new `--network` option. - It will be completely removed in the 0.9 release. - -```bash tab="Syntax" ---ottoman -``` - -Enables accepting of blocks in an IBFT 1.0 network. The default is `false`. - -!!!note - A Pantheon node cannot be a validator in an IBFT 1.0 network. Pantheon implements [IBFT 2.0](../Consensus-Protocols/IBFT.md). - ### p2p-host ```bash tab="Syntax" @@ -508,44 +493,6 @@ The default is 30303. !!!note This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#exposing-ports). -### rinkeby - -!!!important - This option is deprecated in favor of the new `--network` option. - It will be completely removed in the 0.9 release. - -```bash tab="Syntax" ---rinkeby -``` - -```bash tab="Example Configuration File" -rinkeby=true -``` - -Uses the Rinkeby test network. -Default is `false`. - - -### ropsten - -!!!important - This option is deprecated in favor of the new `--network` option. - It will be completely removed in the 0.9 release. - -```bash tab="Syntax" ---ropsten -``` - -```bash tab="Example Configuration File" -ropsten=true -``` - -Uses the Ropsten test network. -Default is `false`. - -!!!note - This option is only available only from v0.8.2. For v0.8.1, refer to [Starting Pantheon](../Getting-Started/Starting-Pantheon.md#run-a-node-on-ropsten-testnet). - ### rpc-http-enabled ```bash tab="Syntax" diff --git a/docs/Reference/Using-JSON-RPC-API.md b/docs/Reference/Using-JSON-RPC-API.md index 6080f0ca9d..b3748cf722 100644 --- a/docs/Reference/Using-JSON-RPC-API.md +++ b/docs/Reference/Using-JSON-RPC-API.md @@ -33,8 +33,8 @@ In this reference, the placeholder `` and `` means an endpoint (IP address and port) of the JSON-RPC service of a Pantheon node respectively for http endpoint and for WebSocket endpoint. -To activate JSON-RPC using http or WebSocket, see [`--rpc-listen`](../Reference/Pantheon-CLI-Syntax.md#rpc-listen) -and [`--ws-listen`](../Reference/Pantheon-CLI-Syntax.md#ws-listen) options in the CLI documentation. +To activate JSON-RPC using http or WebSocket, see [`--rpc-http-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled) +and [`--rpc-ws-listen`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-listen) options in the CLI documentation. The transport attributes are: diff --git a/docs/Tutorials/Create-Private-Network.md b/docs/Tutorials/Create-Private-Network.md index b600a8991b..40b00afc30 100644 --- a/docs/Tutorials/Create-Private-Network.md +++ b/docs/Tutorials/Create-Private-Network.md @@ -35,11 +35,11 @@ Create directories for your private network, each of the three nodes, and a data ```bash Private-Network/ ├── Node-1 -│   ├── Node-1-Datadir +│   ├── Node-1-data-path ├── Node-2 -│   ├── Node-2-Datadir +│   ├── Node-2-data-path └── Node-3 - ├── Node-3-Datadir + ├── Node-3-data-path ``` ### 2. Create Genesis File @@ -89,17 +89,17 @@ In the `Node-1` directory, use the [`export-pub-key` subcommand](../Reference/Pa the [node public key](../Configuring-Pantheon/Node-Keys.md) to the specified file (`publicKeyNode1` in this example): ```bash tab="MacOS" -pantheon --datadir=Node-1-Datadir --genesis=../privateNetworkGenesis.json export-pub-key Node-1-Datadir/publicKeyNode1 +pantheon --data-path=Node-1-data-path --genesis-file=../privateNetworkGenesis.json export-pub-key Node-1-data-path/publicKeyNode1 ``` ```bash tab="Windows" -pantheon --datadir=Node-1-Datadir --genesis=..\privateNetworkGenesis.json export-pub-key Node-1-Datadir\publicKeyNode1 +pantheon --data-path=Node-1-data-path --genesis-file=..\privateNetworkGenesis.json export-pub-key Node-1-data-path\publicKeyNode1 ``` Your node 1 directory now contains: ```bash ├── Node-1 -    ├── Node-1-Datadir +    ├── Node-1-data-path ├── database       ├── key       ├── publicKeyNode1 @@ -114,14 +114,14 @@ Start Node-1 specifying: * No arguments for the [`--bootnodes` option](../Reference/Pantheon-CLI-Syntax.md#bootnodes) because this is your bootnode. * Mining is enabled and the account to which mining rewards are paid using the [`--miner-enabled`](../Reference/Pantheon-CLI-Syntax.md#miner-enabled) and [`--miner-coinbase` options](../Reference/Pantheon-CLI-Syntax.md#miner-coinbase). -* JSON-RPC API is enabled using the [`--rpc-enabled` option](../Reference/Pantheon-CLI-Syntax.md#rpc-enabled). +* JSON-RPC API is enabled using the [`--rpc-http-enabled` option](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled). ```bash tab="MacOS" -pantheon --datadir=Node-1-Datadir --genesis=../privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-enabled +pantheon --data-path=Node-1-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled ``` ```bash tab="Windows" -pantheon --datadir=Node-1-Datadir --genesis=..\privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-enabled +pantheon --data-path=Node-1-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled ``` !!! info @@ -144,31 +144,31 @@ The enode URL is `enode://@` where: Start another terminal, change to the `Node-2` directory and start Node-2 specifying: -* Different port to Node-1 for P2P peer discovery using the [`--p2p-listen` option](../Reference/Pantheon-CLI-Syntax.md#p2p-listen). +* Different port to Node-1 for P2P peer discovery using the [`--p2p-port` option](../Reference/Pantheon-CLI-Syntax.md#p2p-port). * Enode URL for Node-1 using the [`--bootnodes` option](../Reference/Pantheon-CLI-Syntax.md#bootnodes). -* Data directory for Node-2 using the [`--datadir` option](../Reference/Pantheon-CLI-Syntax.md#datadir). +* Data directory for Node-2 using the [`--data-path` option](../Reference/Pantheon-CLI-Syntax.md#data-path). * Genesis file and network ID as for Node-1. ```bash tab="MacOS" -pantheon --datadir=Node-2-Datadir --genesis=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-listen=127.0.0.1:30304 +pantheon --data-path=Node-2-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30304 ``` ```bash tab="Windows" -pantheon --datadir=Node-2-Datadir --genesis=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-listen=127.0.0.1:30304 +pantheon --data-path=Node-2-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30304 ``` Start another terminal, change to the `Node-3` directory and start Node-3 specifying: * Different port to Node-1 and Node-2 for P2P peer discovery. - * Data directory for Node-3 using the [`--datadir` option](../Reference/Pantheon-CLI-Syntax.md#datadir). + * Data directory for Node-3 using the [`--data-path` option](../Reference/Pantheon-CLI-Syntax.md#data-path). * Bootnode, genesis file, and network ID as for Node-2. ```bash tab="MacOS" -pantheon --datadir=Node-3-Datadir --genesis=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-listen=127.0.0.1:30305 +pantheon --data-path=Node-3-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port30305 ``` ```bash tab="Windows" -pantheon --datadir=Node-3-Datadir --genesis=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-listen=127.0.0.1:30305 +pantheon --data-path=Node-3-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30305 ``` ### 6. Confirm Private Network is Working @@ -199,7 +199,7 @@ Send transactions using `eth_sendRawTransaction` to [send ether or, deploy or in Use the [JSON-RPC API](../Reference/Using-JSON-RPC-API.md). -Start a node with the `--ws-enabled` option and use the [RPC Pub/Sub API](../Using-Pantheon/RPC-PubSub.md). +Start a node with the `--rpc-ws-enabled` option and use the [RPC Pub/Sub API](../Using-Pantheon/RPC-PubSub.md). ## Stop Nodes diff --git a/docs/Using-Pantheon/Debugging.md b/docs/Using-Pantheon/Debugging.md index ce4bd86f16..b1bfada1b1 100644 --- a/docs/Using-Pantheon/Debugging.md +++ b/docs/Using-Pantheon/Debugging.md @@ -56,8 +56,8 @@ block of the `prometheus.yml` file: a single node for testing with metrics enabled: ```bash tab="Example" - pantheon --dev-mode --network-id="2018" --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 - --rpc-cors-origins="all" --rpc-enabled --metrics-enabled + pantheon --network=dev --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 + --rpc-http-cors-origins="all" --rpc-http-enabled --metrics-enabled ``` 4. In another terminal, run Prometheus specifying the `prometheus.yml` file: diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java index 1917063cca..7884426e27 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/DefaultCommandValues.java @@ -35,7 +35,7 @@ interface DefaultCommandValues { String DEFAULT_DATA_DIR_PATH = "./build/data"; String MANDATORY_INTEGER_FORMAT_HELP = ""; String MANDATORY_MODE_FORMAT_HELP = ""; - String MANDATORY_NETWORK_FORMAT_HELP = ""; + String MANDATORY_NETWORK_FORMAT_HELP = ""; String MANDATORY_NODE_ID_FORMAT_HELP = ""; Wei DEFAULT_MIN_TRANSACTION_GAS_PRICE = Wei.of(1000); BytesValue DEFAULT_EXTRA_DATA = BytesValue.EMPTY; diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java index d16d2cd973..b546477123 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/EthNetworkConfig.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; import java.util.Collection; import java.util.Objects; @@ -33,10 +34,12 @@ public class EthNetworkConfig { private static final int RINKEBY_NETWORK_ID = 4; private static final int ROPSTEN_NETWORK_ID = 3; private static final int GOERLI_NETWORK_ID = 6284; + private static final int DEV_NETWORK_ID = 2018; private static final String MAINNET_GENESIS = "mainnet.json"; private static final String RINKEBY_GENESIS = "rinkeby.json"; private static final String ROPSTEN_GENESIS = "ropsten.json"; private static final String GOERLI_GENESIS = "goerli.json"; + private static final String DEV_GENESIS = "dev.json"; private final String genesisConfig; private final int networkId; private final Collection bootNodes; @@ -93,24 +96,24 @@ public String toString() { + '}'; } - public static EthNetworkConfig mainnet() { - return new EthNetworkConfig( - jsonConfig(MAINNET_GENESIS), MAINNET_NETWORK_ID, MAINNET_BOOTSTRAP_NODES); - } - - public static EthNetworkConfig rinkeby() { - return new EthNetworkConfig( - jsonConfig(RINKEBY_GENESIS), RINKEBY_NETWORK_ID, RINKEBY_BOOTSTRAP_NODES); - } - - public static EthNetworkConfig ropsten() { - return new EthNetworkConfig( - jsonConfig(ROPSTEN_GENESIS), ROPSTEN_NETWORK_ID, ROPSTEN_BOOTSTRAP_NODES); - } - - public static EthNetworkConfig goerli() { - return new EthNetworkConfig( - jsonConfig(GOERLI_GENESIS), GOERLI_NETWORK_ID, GOERLI_BOOTSTRAP_NODES); + public static EthNetworkConfig getNetworkConfig(final NetworkName networkName) { + switch (networkName) { + case ROPSTEN: + return new EthNetworkConfig( + jsonConfig(ROPSTEN_GENESIS), ROPSTEN_NETWORK_ID, ROPSTEN_BOOTSTRAP_NODES); + case RINKEBY: + return new EthNetworkConfig( + jsonConfig(RINKEBY_GENESIS), RINKEBY_NETWORK_ID, RINKEBY_BOOTSTRAP_NODES); + case GOERLI: + return new EthNetworkConfig( + jsonConfig(GOERLI_GENESIS), GOERLI_NETWORK_ID, GOERLI_BOOTSTRAP_NODES); + case DEV: + return new EthNetworkConfig(jsonConfig(DEV_GENESIS), DEV_NETWORK_ID, new ArrayList<>()); + case MAINNET: + default: + return new EthNetworkConfig( + jsonConfig(MAINNET_GENESIS), MAINNET_NETWORK_ID, MAINNET_BOOTSTRAP_NODES); + } } private static String jsonConfig(final String resourceName) { diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/NetworkName.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/NetworkName.java index 12ec370726..45e4158550 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/NetworkName.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/NetworkName.java @@ -14,8 +14,8 @@ public enum NetworkName { MAINNET, - OTTOMAN, RINKEBY, ROPSTEN, - GOERLI + GOERLI, + DEV } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 6bbc051439..99fc395486 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -29,6 +29,7 @@ import tech.pegasys.pantheon.cli.custom.CorsAllowedOriginsProperty; import tech.pegasys.pantheon.cli.custom.EnodeToURIPropertyConverter; import tech.pegasys.pantheon.cli.custom.JsonRPCWhitelistHostsProperty; +import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.consensus.clique.jsonrpc.CliqueRpcApis; import tech.pegasys.pantheon.consensus.ibft.jsonrpc.IbftRpcApis; import tech.pegasys.pantheon.controller.KeyPairUtil; @@ -61,7 +62,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Optional; @@ -73,6 +73,7 @@ import com.google.common.net.HostAndPort; import com.google.common.net.HostSpecifier; import io.vertx.core.Vertx; +import io.vertx.core.json.DecodeException; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; import picocli.CommandLine; @@ -185,7 +186,7 @@ public static class RpcApisConversionException extends Exception { arity = "0..*", converter = EnodeToURIPropertyConverter.class ) - private final Collection bootstrapNodes = null; + private final Collection bootNodes = null; @Option( names = {"--max-peers"}, @@ -231,47 +232,9 @@ public static class RpcApisConversionException extends Exception { paramLabel = MANDATORY_NETWORK_FORMAT_HELP, description = "Synchronize against the indicated network, possible values are ${COMPLETION-CANDIDATES}." - + " (default: ${DEFAULT-VALUE})" + + " (default: MAINNET)" ) - private final NetworkName network = MAINNET; - - /** @deprecated Deprecated in favour of --network option */ - @Deprecated - // Boolean option to indicate if the client have to sync against the ottoman test network - // (see https://github.com/ethereum/EIPs/issues/650). - @Option( - names = {"--ottoman"}, - description = - "Synchronize against the Ottoman test network, only useful if using an iBFT genesis file" - + " - see https://github.com/ethereum/EIPs/issues/650 (default: ${DEFAULT-VALUE})" - ) - private final Boolean syncWithOttoman = false; - - /** @deprecated Deprecated in favour of --network option */ - @Deprecated - @Option( - names = {"--rinkeby"}, - description = - "Use the Rinkeby test network" - + " - see https://github.com/ethereum/EIPs/issues/225 (default: ${DEFAULT-VALUE})" - ) - private final Boolean rinkeby = false; - - /** @deprecated Deprecated in favour of --network option */ - @Deprecated - @Option( - names = {"--ropsten"}, - description = "Use the Ropsten test network (default: ${DEFAULT-VALUE})" - ) - private final Boolean ropsten = false; - - /** @deprecated Deprecated in favour of --network option */ - @Deprecated - @Option( - names = {"--goerli"}, - description = "Use the Goerli test network (default: ${DEFAULT-VALUE})" - ) - private final Boolean goerli = false; + private final NetworkName network = null; @Option( names = {"--p2p-host"}, @@ -290,12 +253,11 @@ public static class RpcApisConversionException extends Exception { ) private final Integer p2pPort = DEFAULT_PORT; - /** @deprecated Deprecated in favour of --network option */ - @Deprecated @Option( names = {"--network-id"}, paramLabel = MANDATORY_INTEGER_FORMAT_HELP, - description = "P2P network identifier (default: ${DEFAULT-VALUE})", + description = + "P2P network identifier. (default: the selected network chain ID or custom genesis chain ID)", arity = "1" ) private final Integer networkId = null; @@ -454,16 +416,6 @@ private Long configureRefreshDelay(final Long refreshDelay) { ) private final JsonRPCWhitelistHostsProperty hostsWhitelist = new JsonRPCWhitelistHostsProperty(); - /** @deprecated Deprecated in favour of --network option */ - @Deprecated - @Option( - names = {"--dev-mode"}, - description = - "set during development to have a custom genesis with specific chain id " - + "and reduced difficulty to enable CPU mining (default: ${DEFAULT-VALUE})." - ) - private final Boolean isDevMode = false; - @Option( names = {"--logging", "-l"}, paramLabel = "", @@ -566,6 +518,8 @@ public void parse( final CommandLine commandLine = new CommandLine(this); + commandLine.setCaseInsensitiveEnumValuesAllowed(true); + standaloneCommands = new StandaloneCommand(); if (isFullInstantiation()) { @@ -601,7 +555,7 @@ public void run() { Configurator.setAllLevels("", logLevel); } - if (!p2pEnabled && (bootstrapNodes != null && !bootstrapNodes.isEmpty())) { + if (!p2pEnabled && (bootNodes != null && !bootNodes.isEmpty())) { throw new ParameterException( new CommandLine(this), "Unable to specify bootnodes if p2p is disabled."); } @@ -613,13 +567,8 @@ public void run() { "Unable to mine without a valid coinbase. Either disable mining (remove --miner-enabled)" + "or specify the beneficiary of mining (via --miner-coinbase

)"); } - if (trueCount(ropsten, rinkeby, goerli) > 1) { - throw new ParameterException( - new CommandLine(this), - "Unable to connect to multiple networks simultaneously. Specify one of --ropsten, --rinkeby or --goerli"); - } - final EthNetworkConfig ethNetworkConfig = ethNetworkConfig(); + final EthNetworkConfig ethNetworkConfig = updateNetworkConfig(getNetwork()); final PermissioningConfiguration permissioningConfiguration = permissioningConfiguration(); ensureAllBootnodesAreInWhitelist(ethNetworkConfig, permissioningConfiguration); @@ -636,6 +585,11 @@ public void run() { permissioningConfiguration); } + private NetworkName getNetwork() { + //noinspection ConstantConditions network is not always null but injected by PicoCLI if used + return network == null ? MAINNET : network; + } + private void ensureAllBootnodesAreInWhitelist( final EthNetworkConfig ethNetworkConfig, final PermissioningConfiguration permissioningConfiguration) { @@ -659,20 +613,16 @@ private void ensureAllBootnodesAreInWhitelist( } } - private static int trueCount(final Boolean... b) { - return (int) Arrays.stream(b).filter(bool -> bool).count(); - } - PantheonController buildController() { try { return controllerBuilder .synchronizerConfiguration(buildSyncConfig()) .homePath(dataDir()) - .ethNetworkConfig(ethNetworkConfig()) - .syncWithOttoman(syncWithOttoman) + .ethNetworkConfig(updateNetworkConfig(getNetwork())) + .syncWithOttoman(false) // ottoman feature is still there but it's now removed from CLI .miningParameters( new MiningParameters(coinbase, minTransactionGasPrice, extraData, isMiningEnabled)) - .devMode(isDevMode) + .devMode(NetworkName.DEV.equals(getNetwork())) .nodePrivateKeyFile(getNodePrivateKeyFile()) .metricsSystem(metricsSystem) .privacyParameters(orionConfiguration()) @@ -811,31 +761,69 @@ private InetAddress autoDiscoverDefaultIP() { return autoDiscoveredDefaultIP; } - private EthNetworkConfig ethNetworkConfig() { - final EthNetworkConfig predefinedNetworkConfig; - if (rinkeby) { - predefinedNetworkConfig = EthNetworkConfig.rinkeby(); - } else if (ropsten) { - predefinedNetworkConfig = EthNetworkConfig.ropsten(); - } else if (goerli) { - predefinedNetworkConfig = EthNetworkConfig.goerli(); - } else { - predefinedNetworkConfig = EthNetworkConfig.mainnet(); - } - return updateNetworkConfig(predefinedNetworkConfig); - } + private EthNetworkConfig updateNetworkConfig(final NetworkName network) { + final EthNetworkConfig.Builder builder = + new EthNetworkConfig.Builder(EthNetworkConfig.getNetworkConfig(network)); + + // custom genesis file use comes with specific default values for the genesis file itself + // but also for the network id and the bootnodes list. + File genesisFile = genesisFile(); + if (genesisFile != null) { + + //noinspection ConstantConditions network is not always null but injected by PicoCLI if used + if (this.network != null) { + // We check if network option was really provided by user and not only looking at the + // default value. + // if user provided it and provided the genesis file option at the same time, it raises a + // conflict error + throw new ParameterException( + new CommandLine(this), + "--network option and --genesis-file option can't be used at the same time." + + "Please refer to CLI reference for more details about this constraint."); + } - private EthNetworkConfig updateNetworkConfig(final EthNetworkConfig ethNetworkConfig) { - final EthNetworkConfig.Builder builder = new EthNetworkConfig.Builder(ethNetworkConfig); - if (genesisFile() != null) { builder.setGenesisConfig(genesisConfig()); + + if (networkId == null) { + // if no network id option is defined on the CLI we have to set a default value from the + // genesis file. + // We do the genesis parsing only in this case as we already have network id constants + // for known networks to speed up the process. + // Also we have to parse the genesis as we don't already have a parsed version at this + // stage. + // If no chain id is found in the genesis as it's an optional, we use mainnet network id. + try { + GenesisConfigFile genesisConfigFile = GenesisConfigFile.fromConfig(genesisConfig()); + builder.setNetworkId( + genesisConfigFile + .getConfigOptions() + .getChainId() + .orElse(EthNetworkConfig.getNetworkConfig(MAINNET).getNetworkId())); + } catch (DecodeException e) { + throw new ParameterException( + new CommandLine(this), + String.format("Unable to parse genesis file %s.", genesisFile), + e); + } + } + + if (bootNodes == null) { + // We default to an empty bootnodes list if the option is not provided on CLI because + // mainnet bootnodes won't work as the default value for a custom genesis, + // so it's better to have an empty list as default value that forces to create a custom one + // than a useless one that may make user think that it can work when it can't. + builder.setBootNodes(new ArrayList<>()); + } } + if (networkId != null) { builder.setNetworkId(networkId); } - if (bootstrapNodes != null) { - builder.setBootNodes(bootstrapNodes); + + if (bootNodes != null) { + builder.setBootNodes(bootNodes); } + return builder.build(); } diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/StandaloneCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/StandaloneCommand.java index a7b99bf508..cd7dc14070 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/StandaloneCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/StandaloneCommand.java @@ -37,13 +37,14 @@ class StandaloneCommand implements DefaultCommandValues { // Genesis file path with null default option if the option // is not defined on command line as this default is handled by Runner - // to use mainnet json file from resources - // NOTE: we have no control over default value here. + // to use mainnet json file from resources as indicated in the + // default network option + // Then we have no control over genesis default value here. @CommandLine.Option( - names = {"--private-genesis-file"}, + names = {"--genesis-file"}, paramLabel = MANDATORY_FILE_FORMAT_HELP, description = - "The path to genesis file. Setting this will also override --chain option to be CUSTOM" + "The path to genesis file. Setting this option makes --network option ignored and requires --network-id to be set." ) final File genesisFile = null; } diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index c60dd33eb6..6314c305e9 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -21,9 +21,15 @@ import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; +import static tech.pegasys.pantheon.cli.NetworkName.DEV; +import static tech.pegasys.pantheon.cli.NetworkName.GOERLI; +import static tech.pegasys.pantheon.cli.NetworkName.MAINNET; +import static tech.pegasys.pantheon.cli.NetworkName.RINKEBY; +import static tech.pegasys.pantheon.cli.NetworkName.ROPSTEN; import static tech.pegasys.pantheon.ethereum.p2p.config.DiscoveryConfiguration.MAINNET_BOOTSTRAP_NODES; import tech.pegasys.pantheon.PantheonInfo; +import tech.pegasys.pantheon.config.GenesisConfigFile; import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.core.MiningParameters; import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; @@ -52,6 +58,7 @@ import java.util.stream.Stream; import com.google.common.io.Resources; +import io.vertx.core.json.JsonObject; import net.consensys.cava.toml.Toml; import net.consensys.cava.toml.TomlParseResult; import org.apache.commons.text.StringEscapeUtils; @@ -62,6 +69,7 @@ import picocli.CommandLine; public class PantheonCommandTest extends CommandTestAbstract { + private final String ORION_URI = "http://1.2.3.4:5555"; private final String VALID_NODE_ID = "6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"; @@ -69,7 +77,12 @@ public class PantheonCommandTest extends CommandTestAbstract { private static final JsonRpcConfiguration defaultJsonRpcConfiguration; private static final WebSocketConfiguration defaultWebSocketConfiguration; private static final MetricsConfiguration defaultMetricsConfiguration; - private static final String GENESIS_CONFIG_TESTDATA = "genesis_config"; + private static final int GENESIS_CONFIG_TEST_CHAINID = 3141592; + private static final JsonObject GENESIS_VALID_JSON = + (new JsonObject()) + .put("config", (new JsonObject()).put("chainId", GENESIS_CONFIG_TEST_CHAINID)); + private static final JsonObject GENESIS_INVALID_DATA = + (new JsonObject()).put("config", new JsonObject()); private final String[] validENodeStrings = { "enode://" + VALID_NODE_ID + "@192.168.0.1:4567", @@ -237,7 +250,7 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException assumeTrue(isFullInstantiation()); final URL configFile = Resources.getResource("complete_config.toml"); - final Path genesisFile = createFakeGenesisFile(); + final Path genesisFile = createFakeGenesisFile(GENESIS_VALID_JSON); final String updatedConfig = Resources.toString(configFile, UTF_8) .replace("~/genesis.json", escapeTomlString(genesisFile.toString())); @@ -282,8 +295,9 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException assertThat(uriListArgumentCaptor.getValue()).isEqualTo(nodes); final EthNetworkConfig networkConfig = - new EthNetworkConfig.Builder(EthNetworkConfig.mainnet()) - .setGenesisConfig(GENESIS_CONFIG_TESTDATA) + new EthNetworkConfig.Builder(EthNetworkConfig.getNetworkConfig(MAINNET)) + .setNetworkId(42) + .setGenesisConfig(encodeJsonGenesis(GENESIS_VALID_JSON)) .setBootNodes(nodes) .build(); verify(mockControllerBuilder).homePath(eq(Paths.get("~/pantheondata"))); @@ -293,8 +307,6 @@ public void overrideDefaultValuesIfKeyIsPresentInConfigFile() throws IOException // TODO: Re-enable as per NC-1057/NC-1681 // verify(mockSyncConfBuilder).syncMode(ArgumentMatchers.eq(SyncMode.FAST)); - assertThat(commandErrorOutput.toString()).isEmpty(); - assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); } @@ -453,21 +465,100 @@ public void dataDirDefaultedUnderDocker() { public void genesisPathOptionMustBeUsed() throws Exception { assumeTrue(isFullInstantiation()); - final Path genesisFile = createFakeGenesisFile(); + final Path genesisFile = createFakeGenesisFile(GENESIS_VALID_JSON); + final ArgumentCaptor networkArg = + ArgumentCaptor.forClass(EthNetworkConfig.class); + + parseCommand("--genesis-file", genesisFile.toString()); + + verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); + verify(mockControllerBuilder).build(); + + assertThat(networkArg.getValue().getGenesisConfig()) + .isEqualTo(encodeJsonGenesis(GENESIS_VALID_JSON)); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); + } + + @Test + public void genesisAndNetworkMustNotBeUsedTogether() throws Exception { + assumeTrue(isFullInstantiation()); + + final Path genesisFile = createFakeGenesisFile(GENESIS_VALID_JSON); + final ArgumentCaptor networkArg = + ArgumentCaptor.forClass(EthNetworkConfig.class); + + parseCommand("--genesis-file", genesisFile.toString(), "--network", "rinkeby"); + + verifyZeroInteractions(mockRunnerBuilder); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()) + .startsWith("--network option and --genesis-file option can't be used at the same time."); + } + + @Test + public void defaultNetworkIdAndBootnodesForCustomNetworkOptions() throws Exception { + assumeTrue(isFullInstantiation()); + + final Path genesisFile = createFakeGenesisFile(GENESIS_VALID_JSON); + + parseCommand("--genesis-file", genesisFile.toString()); + final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); - parseCommand("--private-genesis-file", genesisFile.toString()); + verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); + verify(mockControllerBuilder).build(); + + assertThat(networkArg.getValue().getGenesisConfig()) + .isEqualTo(encodeJsonGenesis(GENESIS_VALID_JSON)); + assertThat(networkArg.getValue().getBootNodes()).isEmpty(); + assertThat(networkArg.getValue().getNetworkId()).isEqualTo(GENESIS_CONFIG_TEST_CHAINID); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); + } + + @Test + public void defaultNetworkIdForInvalidGenesisMustBeMainnetNetworkId() throws Exception { + assumeTrue(isFullInstantiation()); + + final Path genesisFile = createFakeGenesisFile(GENESIS_INVALID_DATA); + + parseCommand("--genesis-file", genesisFile.toString()); + + final ArgumentCaptor networkArg = + ArgumentCaptor.forClass(EthNetworkConfig.class); verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); verify(mockControllerBuilder).build(); - assertThat(networkArg.getValue().getGenesisConfig()).isEqualTo("genesis_config"); + assertThat(networkArg.getValue().getGenesisConfig()) + .isEqualTo(encodeJsonGenesis(GENESIS_INVALID_DATA)); + + // assertThat(networkArg.getValue().getNetworkId()) + // .isEqualTo(EthNetworkConfig.getNetworkConfig(MAINNET).getNetworkId()); assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); } + @Test + public void predefinedNetworkIdsMustBeEqualToChainIds() { + // check the network id against the one in mainnet genesis config + // it implies that EthNetworkConfig.mainnet().getNetworkId() returns a value equals to the chain + // id + // in this network genesis file. + + GenesisConfigFile genesisConfigFile = + GenesisConfigFile.fromConfig(EthNetworkConfig.getNetworkConfig(MAINNET).getGenesisConfig()); + assertThat(genesisConfigFile.getConfigOptions().getChainId().isPresent()).isTrue(); + assertThat(genesisConfigFile.getConfigOptions().getChainId().getAsInt()) + .isEqualTo(EthNetworkConfig.getNetworkConfig(MAINNET).getNetworkId()); + } + @Test public void genesisPathDisabledUnderDocker() { System.setProperty("pantheon.docker", "true"); @@ -736,7 +827,7 @@ public void nodesWhitelistOptionWhichDoesNotIncludeBootnodesMustDisplayError() { @Test public void ropstenWithNodesWhitelistOptionWhichDoesIncludeRopstenBootnodesMustNotDisplayError() { - parseCommand("--ropsten", "--nodes-whitelist", String.join(",", ropstenBootnodes)); + parseCommand("--network", "ropsten", "--nodes-whitelist", String.join(",", ropstenBootnodes)); verify(mockRunnerBuilder) .permissioningConfiguration(permissioningConfigurationArgumentCaptor.capture()); @@ -759,7 +850,7 @@ public void ropstenWithNodesWhitelistOptionWhichDoesIncludeRopstenBootnodesMustN @Test public void ropstenWithNodesWhitelistOptionWhichDoesNotIncludeRopstenBootnodesMustDisplayError() { - parseCommand("--ropsten", "--nodes-whitelist", String.join(",", validENodeStrings)); + parseCommand("--network", "ropsten", "--nodes-whitelist", String.join(",", validENodeStrings)); verifyZeroInteractions(mockRunnerBuilder); @@ -1419,18 +1510,24 @@ public void miningParametersAreCaptured() throws Exception { @Test public void devModeOptionMustBeUsed() throws Exception { - parseCommand("--dev-mode"); + parseCommand("--network", "dev"); + + final ArgumentCaptor networkArg = + ArgumentCaptor.forClass(EthNetworkConfig.class); verify(mockControllerBuilder).devMode(eq(true)); + verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); verify(mockControllerBuilder).build(); + assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(DEV)); + assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); } @Test public void rinkebyValuesAreUsed() throws Exception { - parseCommand("--rinkeby"); + parseCommand("--network", "rinkeby"); final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); @@ -1438,14 +1535,15 @@ public void rinkebyValuesAreUsed() throws Exception { verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); verify(mockControllerBuilder).build(); + assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(RINKEBY)); + assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); - assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.rinkeby()); } @Test public void ropstenValuesAreUsed() throws Exception { - parseCommand("--ropsten"); + parseCommand("--network", "ropsten"); final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); @@ -1453,14 +1551,15 @@ public void ropstenValuesAreUsed() throws Exception { verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); verify(mockControllerBuilder).build(); + assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(ROPSTEN)); + assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); - assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.ropsten()); } @Test public void goerliValuesAreUsed() throws Exception { - parseCommand("--goerli"); + parseCommand("--network", "goerli"); final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); @@ -1468,19 +1567,10 @@ public void goerliValuesAreUsed() throws Exception { verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); verify(mockControllerBuilder).build(); - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).isEmpty(); - assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.goerli()); - } - - @Test - public void noSeveralNetworkOptions() { - parseCommand("--goerli", "--rinkeby"); - - verifyZeroInteractions(mockRunnerBuilder); + assertThat(networkArg.getValue()).isEqualTo(EthNetworkConfig.getNetworkConfig(GOERLI)); assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).contains("Unable to connect to multiple networks"); + assertThat(commandErrorOutput.toString()).isEmpty(); } @Test @@ -1493,16 +1583,24 @@ public void goerliValuesCanBeOverridden() throws Exception { networkValuesCanBeOverridden("goerli"); } + @Test + public void ropstenValuesCanBeOverridden() throws Exception { + networkValuesCanBeOverridden("ropsten"); + } + + @Test + public void devValuesCanBeOverridden() throws Exception { + networkValuesCanBeOverridden("dev"); + } + private void networkValuesCanBeOverridden(final String network) throws Exception { - final Path genesisFile = createFakeGenesisFile(); parseCommand( - "--" + network, + "--network", + network, "--network-id", - "1", + "1234567", "--bootnodes", - String.join(",", validENodeStrings), - "--private-genesis-file", - genesisFile.toString()); + String.join(",", validENodeStrings)); final ArgumentCaptor networkArg = ArgumentCaptor.forClass(EthNetworkConfig.class); @@ -1510,12 +1608,12 @@ private void networkValuesCanBeOverridden(final String network) throws Exception verify(mockControllerBuilder).ethNetworkConfig(networkArg.capture()); verify(mockControllerBuilder).build(); - assertThat(commandOutput.toString()).isEmpty(); - assertThat(commandErrorOutput.toString()).isEmpty(); - assertThat(networkArg.getValue().getGenesisConfig()).isEqualTo("genesis_config"); assertThat(networkArg.getValue().getBootNodes()) .isEqualTo(Stream.of(validENodeStrings).map(URI::create).collect(Collectors.toList())); - assertThat(networkArg.getValue().getNetworkId()).isEqualTo(1); + assertThat(networkArg.getValue().getNetworkId()).isEqualTo(1234567); + + assertThat(commandOutput.toString()).isEmpty(); + assertThat(commandErrorOutput.toString()).isEmpty(); } @Test @@ -1528,7 +1626,7 @@ public void fullCLIOptionsNotShownWhenInDockerContainer() { assertThat(commandOutput.toString()).doesNotContain("--config-file"); assertThat(commandOutput.toString()).doesNotContain("--data-path"); - assertThat(commandOutput.toString()).doesNotContain("--private-genesis-file"); + assertThat(commandOutput.toString()).doesNotContain("--genesis-file"); assertThat(commandErrorOutput.toString()).isEmpty(); } @@ -1540,7 +1638,7 @@ public void fullCLIOptionsShownWhenNotInDockerContainer() { assertThat(commandOutput.toString()).contains("--config-file"); assertThat(commandOutput.toString()).contains("--data-path"); - assertThat(commandOutput.toString()).contains("--private-genesis-file"); + assertThat(commandOutput.toString()).contains("--genesis-file"); assertThat(commandErrorOutput.toString()).isEmpty(); } @@ -1583,12 +1681,16 @@ public void mustVerifyPrivacyIsDisabled() throws IOException { assertThat(orionArg.getValue().isEnabled()).isEqualTo(false); } - private Path createFakeGenesisFile() throws IOException { + private Path createFakeGenesisFile(final JsonObject jsonGenesis) throws IOException { final Path genesisFile = Files.createTempFile("genesisFile", ""); - Files.write(genesisFile, "genesis_config".getBytes(UTF_8)); + Files.write(genesisFile, encodeJsonGenesis(jsonGenesis).getBytes(UTF_8)); return genesisFile; } + private String encodeJsonGenesis(final JsonObject jsonGenesis) { + return jsonGenesis.encodePrettily(); + } + private boolean isFullInstantiation() { return !Boolean.getBoolean("pantheon.docker"); } diff --git a/pantheon/src/test/resources/complete_config.toml b/pantheon/src/test/resources/complete_config.toml index 394f8217c5..70f54141ab 100644 --- a/pantheon/src/test/resources/complete_config.toml +++ b/pantheon/src/test/resources/complete_config.toml @@ -22,7 +22,8 @@ metrics-host="8.6.7.5" metrics-port=309 # chain -private-genesis-file="~/genesis.json" # Path +genesis-file="~/genesis.json" # Path +network-id=42 sync-mode="fast"# should be FAST or FULL (or fast or full) ottoman=false # true means using ottoman testnet if genesys file uses iBFT diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index 3cbcb2ec03..ef80aa7110 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -31,14 +31,9 @@ host-whitelist=["all"] # chain network="MAINNET" -private-genesis-file="~/genesis.json" +genesis-file="~/genesis.json" sync-mode="fast" -ottoman=false -ropsten=false -goerli=false network-id=303 -rinkeby=false -dev-mode=false # JSON-RPC rpc-http-enabled=false @@ -66,7 +61,7 @@ metrics-prometheus-job="pantheon-everything" # Mining miner-enabled=false miner-coinbase="0x0000000000000000000000000000000000000002" -miner-extra-data="Protocol Engineering Group And SYStems" +miner-extra-data="0x444F4E27542050414E4943202120484F444C2C20484F444C2C20484F444C2021" min-gas-price="1" # Permissioning From 016e002261e5e57fab7163dc53db2ba114f65db8 Mon Sep 17 00:00:00 2001 From: Puneetha Karamsetty Date: Fri, 25 Jan 2019 12:30:05 +0000 Subject: [PATCH 09/31] Enable CLI config for privacy precompiled contract address (#653) --- .../tech/pegasys/pantheon/ethereum/core/Address.java | 7 ++++++- .../pantheon/ethereum/core/PrivacyParameters.java | 11 ++++++++++- .../mainnet/MainnetPrecompiledContractRegistries.java | 5 ++++- .../tech/pegasys/pantheon/cli/PantheonCommand.java | 8 ++++++++ pantheon/src/test/resources/everything_config.toml | 3 ++- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Address.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Address.java index 7d167048c2..2c99725730 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Address.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/Address.java @@ -39,7 +39,8 @@ public class Address extends DelegatingBytesValue { public static final Address ALTBN128_PAIRING = Address.precompiled(8); // Last address that can be generated for a pre-compiled contract - public static final Address DEFAULT_PRIVACY = Address.precompiled(Byte.MAX_VALUE - 1); + public static final Integer PRIVACY = Byte.MAX_VALUE - 1; + public static final Address DEFAULT_PRIVACY = Address.precompiled(PRIVACY); protected Address(final BytesValue bytes) { super(bytes); @@ -103,6 +104,10 @@ private static Address precompiled(final int value) { return new Address(BytesValue.wrap(address)); } + public static Address privacyPrecompiled(final int value) { + return precompiled(value); + } + /** * Address of the created contract. * diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java index 14a01b3f96..b46b1fa18b 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/core/PrivacyParameters.java @@ -16,10 +16,10 @@ import java.net.URI; public class PrivacyParameters { - private static final String ORION_URL = "http://localhost:8888"; public static final URI DEFAULT_ORION_URL = URI.create(ORION_URL); + private Integer privacyAddress; private boolean enabled; private String url; private File publicKey; @@ -36,6 +36,7 @@ public static PrivacyParameters noPrivacy() { final PrivacyParameters config = new PrivacyParameters(); config.setEnabled(false); config.setUrl(ORION_URL); + config.setPrivacyAddress(Address.PRIVACY); return config; } @@ -59,4 +60,12 @@ public boolean isEnabled() { public void setEnabled(final boolean enabled) { this.enabled = enabled; } + + public Integer getPrivacyAddress() { + return privacyAddress; + } + + public void setPrivacyAddress(final Integer privacyAddress) { + this.privacyAddress = privacyAddress; + } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java index f2e84b218d..5907adb28e 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetPrecompiledContractRegistries.java @@ -64,8 +64,11 @@ public static PrecompileContractRegistry byzantium( public static PrecompileContractRegistry appendPrivacy( final PrecompileContractRegistry registry, final PrecompiledContractConfiguration precompiledContractConfiguration) { + Address address = + Address.privacyPrecompiled( + precompiledContractConfiguration.getPrivacyParameters().getPrivacyAddress()); registry.put( - Address.DEFAULT_PRIVACY, + address, new PrivacyPrecompiledContract( precompiledContractConfiguration.getGasCalculator(), precompiledContractConfiguration.getPrivacyParameters())); diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 99fc395486..6ce57e537b 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -498,6 +498,13 @@ private Long configureRefreshDelay(final Long refreshDelay) { ) private final Boolean privacyEnabled = false; + @Option( + names = {"--privacy-precompiled-address"}, + description = + "The address to which the privacy pre-compiled contract will be mapped to (default: ${DEFAULT-VALUE})" + ) + private final Integer privacyPrecompiledAddress = Address.PRIVACY; + public PantheonCommand( final BlockImporter blockImporter, final RunnerBuilder runnerBuilder, @@ -686,6 +693,7 @@ private PrivacyParameters orionConfiguration() { privacyParameters.setEnabled(privacyEnabled); privacyParameters.setUrl(privacyUrl.toString()); privacyParameters.setPublicKey(privacyPublicKeyFile); + privacyParameters.setPrivacyAddress(privacyPrecompiledAddress); return privacyParameters; } diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index ef80aa7110..a4fa3171d8 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -71,4 +71,5 @@ nodes-whitelist=["all"] # Privacy privacy-url="http://127.0.0.1:8888" privacy-public-key-file="./pubKey.pub" -privacy-enabled=false \ No newline at end of file +privacy-enabled=false +privacy-precompiled-address=9 \ No newline at end of file From 949755d5d5e57dbcff52dff1c36f1da7987b6b5f Mon Sep 17 00:00:00 2001 From: Nicolas MASSART Date: Sat, 26 Jan 2019 00:10:00 +0100 Subject: [PATCH 10/31] NC-2120 --discovery-enabled option refactoring (#661) fixes NC-2120 --discovery-enabled option refactoring now able to have --discovery-enabled option with true as default and --discovery-enabled=true or --discovery-enabled=false on CLI and discovery-enabled=true or discovery-enabled=false in YAML config file. updates doc --- docs/Configuring-Pantheon/Networking.md | 4 +++- docs/Reference/Pantheon-CLI-Syntax.md | 10 +++++----- .../tech/pegasys/pantheon/cli/PantheonCommand.java | 13 ++++++------- .../pegasys/pantheon/cli/PantheonCommandTest.java | 12 ++++++------ pantheon/src/test/resources/complete_config.toml | 2 +- pantheon/src/test/resources/everything_config.toml | 2 +- 6 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/Configuring-Pantheon/Networking.md b/docs/Configuring-Pantheon/Networking.md index ffc8a6deac..11678d993c 100644 --- a/docs/Configuring-Pantheon/Networking.md +++ b/docs/Configuring-Pantheon/Networking.md @@ -36,7 +36,9 @@ Trailing peers cannot be used to get new blocks and are more likely to be reques ## No Discovery -The [`--no-discovery`](../Reference/Pantheon-CLI-Syntax.md#no-discovery) command line option disables P2P peer discovery. Only use this option if you are running a test node or a test network with fixed nodes. +The [`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled) command line option +enables P2P peer discovery. +Only set this option to `false` if you are running a test node or a test network with fixed nodes. ## Monitoring Peer Connections diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index 2a7dc8e6d8..8bc07b5069 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -388,18 +388,18 @@ P2P network identifier. This option can be used to override your current network ID. The default value is the current network chain ID which is defined in the genesis file. -### no-discovery +### discovery-enabled ```bash tab="Syntax" ---no-discovery +--discovery-enabled=false ``` ```bash tab="Example Configuration File" -no-discovery=true +discovery-enabled=false ``` -Disables P2P peer discovery. -The default is `false`. +Enables or disables P2P peer discovery. +The default is `true`. ### node-private-key-file diff --git a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java index 6ce57e537b..a9c3ba3f68 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/cli/PantheonCommand.java @@ -167,11 +167,11 @@ public static class RpcApisConversionException extends Exception { // Also many other software use the same negative option scheme for false defaults // meaning that it's probably the right way to handle disabling options. @Option( - names = {"--no-discovery"}, - description = "Disable p2p peer discovery (default: ${DEFAULT-VALUE})", + names = {"--discovery-enabled"}, + description = "Enable p2p peer discovery (default: ${DEFAULT-VALUE})", arity = "1" ) - private final Boolean noPeerDiscovery = false; + private final Boolean peerDiscoveryEnabled = true; // A list of bootstrap nodes can be passed // and a hardcoded list will be used otherwise by the Runner. @@ -582,7 +582,7 @@ public void run() { synchronize( buildController(), p2pEnabled, - noPeerDiscovery, + peerDiscoveryEnabled, ethNetworkConfig.getBootNodes(), maxPeers, HostAndPort.fromParts(p2pHost.toString(), p2pPort), @@ -707,7 +707,7 @@ private SynchronizerConfiguration buildSyncConfig() { private void synchronize( final PantheonController controller, final boolean p2pEnabled, - final boolean noPeerDiscovery, + final boolean peerDiscoveryEnabled, final Collection bootstrapNodes, final int maxPeers, final HostAndPort discoveryHostAndPort, @@ -723,8 +723,7 @@ private void synchronize( .vertx(Vertx.vertx()) .pantheonController(controller) .p2pEnabled(p2pEnabled) - // BEWARE: Peer discovery boolean must be inverted as it's negated in the options ! - .discovery(!noPeerDiscovery) + .discovery(peerDiscoveryEnabled) .bootstrapPeers(bootstrapNodes) .discoveryHost(discoveryHostAndPort.getHost()) .discoveryPort(discoveryHostAndPort.getPort()) diff --git a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java index 6314c305e9..edd4791611 100644 --- a/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java +++ b/pantheon/src/test/java/tech/pegasys/pantheon/cli/PantheonCommandTest.java @@ -603,9 +603,9 @@ public void p2pEnabledOptionFalseValueCannotAlsoHaveBootnodesSpecified() { @Test public void discoveryOptionValueTrueMustBeUsed() { - parseCommand("--no-discovery", "true"); - // Discovery stored in runner is the negative of the option passed to CLI - verify(mockRunnerBuilder.discovery(eq(false))).build(); + parseCommand("--discovery-enabled", "true"); + + verify(mockRunnerBuilder.discovery(eq(true))).build(); assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); @@ -613,9 +613,9 @@ public void discoveryOptionValueTrueMustBeUsed() { @Test public void discoveryOptionValueFalseMustBeUsed() { - parseCommand("--no-discovery", "false"); - // Discovery stored in runner is the negative of the option passed to CLI - verify(mockRunnerBuilder.discovery(eq(true))).build(); + parseCommand("--discovery-enabled", "false"); + + verify(mockRunnerBuilder.discovery(eq(false))).build(); assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); diff --git a/pantheon/src/test/resources/complete_config.toml b/pantheon/src/test/resources/complete_config.toml index 70f54141ab..c270fb5e39 100644 --- a/pantheon/src/test/resources/complete_config.toml +++ b/pantheon/src/test/resources/complete_config.toml @@ -5,7 +5,7 @@ data-path="~/pantheondata" # Path #invalid-option=true # network -no-discovery=true # true=no discovery of peers or false=discover peers please +discovery-enabled=false bootnodes=[ "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index a4fa3171d8..262b07ae97 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -15,7 +15,7 @@ node-private-key-file="./path/to/privateKey" # P2P network p2p-enabled=true -no-discovery=true +discovery-enabled=false bootnodes=[ "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", From 66ce6ee2f8aeaa04e478b72bc925d5c1c0882199 Mon Sep 17 00:00:00 2001 From: mbaxter Date: Fri, 25 Jan 2019 20:48:54 -0500 Subject: [PATCH 11/31] [NC-1344] Create a simple WorldStateDownloader (#657) --- .../KeyValueStorageWorldStateStorage.java | 54 +++- .../worldstate/DefaultMutableWorldState.java | 70 ++--- .../worldstate/StateTrieAccountValue.java | 95 ++++++ .../worldstate/WorldStateArchive.java | 6 +- .../worldstate/WorldStateStorage.java | 20 +- .../ethereum/core/BlockDataGenerator.java | 29 +- .../KeyValueStorageWorldStateStorageTest.java | 157 ++++++++++ .../DefaultMutableWorldStateTest.java | 22 +- ethereum/eth/build.gradle | 2 + .../AccountTrieNodeDataRequest.java | 61 ++++ .../sync/worldstate/CodeNodeDataRequest.java | 39 +++ .../eth/sync/worldstate/NodeDataRequest.java | 69 +++++ .../StorageTrieNodeDataRequest.java | 46 +++ .../sync/worldstate/TrieNodeDataRequest.java | 71 +++++ .../sync/worldstate/WorldStateDownloader.java | 201 +++++++++++++ .../manager/DeterministicEthScheduler.java | 16 +- .../manager/EthProtocolManagerTestUtil.java | 10 +- .../ethtaskutils/BlockchainSetupUtil.java | 2 +- .../worldstate/WorldStateDownloaderTest.java | 283 ++++++++++++++++++ .../pantheon/ethereum/trie/BranchNode.java | 14 +- .../pantheon/ethereum/trie/ExtensionNode.java | 16 +- .../pantheon/ethereum/trie/LeafNode.java | 13 +- .../ethereum/trie/MerklePatriciaTrie.java | 4 +- ...xception.java => MerkleTrieException.java} | 6 +- .../pegasys/pantheon/ethereum/trie/Node.java | 16 +- .../pantheon/ethereum/trie/NullNode.java | 17 +- .../trie/StoredMerklePatriciaTrie.java | 6 +- .../pantheon/ethereum/trie/StoredNode.java | 28 +- .../ethereum/trie/StoredNodeFactory.java | 27 +- .../ethereum/trie/TrieNodeDecoder.java | 36 +++ services/queue/build.gradle | 38 +++ .../pantheon/services/queue/BigQueue.java | 33 ++ .../services/queue/InMemoryBigQueue.java | 40 +++ services/queue/src/main/resources/log4j2.xml | 16 + settings.gradle | 1 + 35 files changed, 1436 insertions(+), 128 deletions(-) create mode 100644 ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/StateTrieAccountValue.java create mode 100644 ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorageTest.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/AccountTrieNodeDataRequest.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/CodeNodeDataRequest.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/NodeDataRequest.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/StorageTrieNodeDataRequest.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/TrieNodeDataRequest.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java create mode 100644 ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java rename ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/{MerkleStorageException.java => MerkleTrieException.java} (80%) create mode 100644 ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/TrieNodeDecoder.java create mode 100644 services/queue/build.gradle create mode 100644 services/queue/src/main/java/tech/pegasys/pantheon/services/queue/BigQueue.java create mode 100644 services/queue/src/main/java/tech/pegasys/pantheon/services/queue/InMemoryBigQueue.java create mode 100644 services/queue/src/main/resources/log4j2.xml diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorage.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorage.java index b300ece3b0..51f7c2eb6a 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorage.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorage.java @@ -13,6 +13,7 @@ package tech.pegasys.pantheon.ethereum.storage.keyvalue; import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie; import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage; import tech.pegasys.pantheon.services.kvstore.KeyValueStorage; import tech.pegasys.pantheon.util.bytes.Bytes32; @@ -29,23 +30,41 @@ public KeyValueStorageWorldStateStorage(final KeyValueStorage keyValueStorage) { } @Override - public Optional getCode(final Hash codeHash) { - return keyValueStorage.get(codeHash); + public Optional getCode(final Bytes32 codeHash) { + if (codeHash.equals(Hash.EMPTY)) { + return Optional.of(BytesValue.EMPTY); + } else { + return keyValueStorage.get(codeHash); + } } @Override public Optional getAccountStateTrieNode(final Bytes32 nodeHash) { - return keyValueStorage.get(nodeHash); + return getTrieNode(nodeHash); } @Override public Optional getAccountStorageTrieNode(final Bytes32 nodeHash) { - return keyValueStorage.get(nodeHash); + return getTrieNode(nodeHash); + } + + private Optional getTrieNode(final Bytes32 nodeHash) { + if (nodeHash.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { + return Optional.of(MerklePatriciaTrie.EMPTY_TRIE_NODE); + } else { + return keyValueStorage.get(nodeHash); + } } @Override - public Optional getNodeData(final Hash hash) { - return keyValueStorage.get(hash); + public Optional getNodeData(final Bytes32 hash) { + if (hash.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { + return Optional.of(MerklePatriciaTrie.EMPTY_TRIE_NODE); + } else if (hash.equals(Hash.EMPTY)) { + return Optional.of(BytesValue.EMPTY); + } else { + return keyValueStorage.get(hash); + } } @Override @@ -62,18 +81,33 @@ public Updater(final KeyValueStorage.Transaction transaction) { } @Override - public void putCode(final BytesValue code) { - transaction.put(Hash.hash(code), code); + public Updater putCode(final Bytes32 codeHash, final BytesValue code) { + if (code.size() == 0) { + // Don't save empty values + return this; + } + transaction.put(codeHash, code); + return this; } @Override - public void putAccountStateTrieNode(final Bytes32 nodeHash, final BytesValue node) { + public Updater putAccountStateTrieNode(final Bytes32 nodeHash, final BytesValue node) { + if (nodeHash.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { + // Don't save empty nodes + return this; + } transaction.put(nodeHash, node); + return this; } @Override - public void putAccountStorageTrieNode(final Bytes32 nodeHash, final BytesValue node) { + public Updater putAccountStorageTrieNode(final Bytes32 nodeHash, final BytesValue node) { + if (nodeHash.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { + // Don't save empty nodes + return this; + } transaction.put(nodeHash, node); + return this; } @Override diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldState.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldState.java index b3c87fbd42..f83d0f4e3d 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldState.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldState.java @@ -49,7 +49,7 @@ public class DefaultMutableWorldState implements MutableWorldState { private final WorldStateStorage worldStateStorage; public DefaultMutableWorldState(final WorldStateStorage storage) { - this(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, storage); + this(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, storage); } public DefaultMutableWorldState( @@ -103,31 +103,15 @@ public Account get(final Address address) { private AccountState deserializeAccount( final Address address, final Hash addressHash, final BytesValue encoded) throws RLPException { final RLPInput in = RLP.input(encoded); - in.enterList(); - - final long nonce = in.readLongScalar(); - final Wei balance = in.readUInt256Scalar(Wei::wrap); - final Hash storageRoot = Hash.wrap(in.readBytes32()); - final Hash codeHash = Hash.wrap(in.readBytes32()); - - in.leaveList(); - - return new AccountState(address, addressHash, nonce, balance, storageRoot, codeHash); + StateTrieAccountValue accountValue = StateTrieAccountValue.readFrom(in); + return new AccountState(address, addressHash, accountValue); } private static BytesValue serializeAccount( - final long nonce, final Wei balance, final Hash codeHash, final Hash storageRoot) { - return RLP.encode( - out -> { - out.startList(); - - out.writeLongScalar(nonce); - out.writeUInt256Scalar(balance); - out.writeBytesValue(storageRoot); - out.writeBytesValue(codeHash); - - out.endList(); - }); + final long nonce, final Wei balance, final Hash storageRoot, final Hash codeHash) { + StateTrieAccountValue accountValue = + new StateTrieAccountValue(nonce, balance, storageRoot, codeHash); + return RLP.encode(accountValue::writeTo); } @Override @@ -187,28 +171,17 @@ protected class AccountState implements Account { private final Address address; private final Hash addressHash; - private final long nonce; - private final Wei balance; - private final Hash storageRoot; - private final Hash codeHash; + final StateTrieAccountValue accountValue; // Lazily initialized since we don't always access storage. private volatile MerklePatriciaTrie storageTrie; private AccountState( - final Address address, - final Hash addressHash, - final long nonce, - final Wei balance, - final Hash storageRoot, - final Hash codeHash) { + final Address address, final Hash addressHash, final StateTrieAccountValue accountValue) { this.address = address; this.addressHash = addressHash; - this.nonce = nonce; - this.balance = balance; - this.storageRoot = storageRoot; - this.codeHash = codeHash; + this.accountValue = accountValue; } private MerklePatriciaTrie storageTrie() { @@ -217,7 +190,7 @@ private MerklePatriciaTrie storageTrie() { storageTrie = updatedTrie; } if (storageTrie == null) { - storageTrie = newAccountStorageTrie(storageRoot); + storageTrie = newAccountStorageTrie(getStorageRoot()); } return storageTrie; } @@ -234,12 +207,16 @@ public Hash getAddressHash() { @Override public long getNonce() { - return nonce; + return accountValue.getNonce(); } @Override public Wei getBalance() { - return balance; + return accountValue.getBalance(); + } + + Hash getStorageRoot() { + return accountValue.getStorageRoot(); } @Override @@ -249,6 +226,7 @@ public BytesValue getCode() { return updatedCode; } // No code is common, save the KV-store lookup. + Hash codeHash = getCodeHash(); if (codeHash.equals(Hash.EMPTY)) { return BytesValue.EMPTY; } @@ -262,7 +240,7 @@ public boolean hasCode() { @Override public Hash getCodeHash() { - return codeHash; + return accountValue.getCodeHash(); } @Override @@ -303,8 +281,8 @@ public String toString() { builder.append("address=").append(getAddress()).append(", "); builder.append("nonce=").append(getNonce()).append(", "); builder.append("balance=").append(getBalance()).append(", "); - builder.append("storageRoot=").append(storageRoot).append(", "); - builder.append("codeHash=").append(codeHash); + builder.append("storageRoot=").append(getStorageRoot()).append(", "); + builder.append("codeHash=").append(getCodeHash()); return builder.append("}").toString(); } } @@ -353,14 +331,14 @@ public void commit() { final AccountState origin = updated.getWrappedAccount(); // Save the code in key-value storage ... - Hash codeHash = origin == null ? Hash.EMPTY : origin.codeHash; + Hash codeHash = origin == null ? Hash.EMPTY : origin.getCodeHash(); if (updated.codeWasUpdated()) { codeHash = Hash.hash(updated.getCode()); wrapped.updatedAccountCode.put(updated.getAddress(), updated.getCode()); } // ...and storage in the account trie first. final boolean freshState = origin == null || updated.getStorageWasCleared(); - Hash storageRoot = freshState ? Hash.EMPTY_TRIE_HASH : origin.storageRoot; + Hash storageRoot = freshState ? Hash.EMPTY_TRIE_HASH : origin.getStorageRoot(); if (freshState) { wrapped.updatedStorageTries.remove(updated.getAddress()); } @@ -386,7 +364,7 @@ public void commit() { // Lastly, save the new account. final BytesValue account = - serializeAccount(updated.getNonce(), updated.getBalance(), codeHash, storageRoot); + serializeAccount(updated.getNonce(), updated.getBalance(), storageRoot, codeHash); wrapped.accountStateTrie.put(updated.getAddressHash(), account); } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/StateTrieAccountValue.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/StateTrieAccountValue.java new file mode 100644 index 0000000000..57e5c11c4b --- /dev/null +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/StateTrieAccountValue.java @@ -0,0 +1,95 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.worldstate; + +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.core.Wei; +import tech.pegasys.pantheon.ethereum.rlp.RLPInput; +import tech.pegasys.pantheon.ethereum.rlp.RLPOutput; + +/** Represents the raw values associated with an account in the world state trie. */ +public class StateTrieAccountValue { + + private final long nonce; + private final Wei balance; + private final Hash storageRoot; + private final Hash codeHash; + + public StateTrieAccountValue( + final long nonce, final Wei balance, final Hash storageRoot, final Hash codeHash) { + this.nonce = nonce; + this.balance = balance; + this.storageRoot = storageRoot; + this.codeHash = codeHash; + } + + /** + * The account nonce, that is the number of transactions sent from that account. + * + * @return the account nonce. + */ + public long getNonce() { + return nonce; + } + + /** + * The available balance of that account. + * + * @return the balance, in Wei, of the account. + */ + public Wei getBalance() { + return balance; + } + + /** + * The hash of the root of the storage trie associated with this account. + * + * @return the hash of the root node of the storage trie. + */ + public Hash getStorageRoot() { + return storageRoot; + } + + /** + * The hash of the EVM bytecode associated with this account. + * + * @return the hash of the account code (which may be {@link Hash#EMPTY}. + */ + public Hash getCodeHash() { + return codeHash; + } + + public void writeTo(final RLPOutput out) { + out.startList(); + + out.writeLongScalar(nonce); + out.writeUInt256Scalar(balance); + out.writeBytesValue(storageRoot); + out.writeBytesValue(codeHash); + + out.endList(); + } + + public static StateTrieAccountValue readFrom(final RLPInput in) { + in.enterList(); + + final long nonce = in.readLongScalar(); + final Wei balance = in.readUInt256Scalar(Wei::wrap); + final Hash storageRoot = Hash.wrap(in.readBytes32()); + final Hash codeHash = Hash.wrap(in.readBytes32()); + + in.leaveList(); + + return new StateTrieAccountValue(nonce, balance, storageRoot, codeHash); + } +} diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateArchive.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateArchive.java index 215fc61385..f29742f2db 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateArchive.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateArchive.java @@ -22,7 +22,7 @@ public class WorldStateArchive { private final WorldStateStorage storage; - private static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH); + private static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); public WorldStateArchive(final WorldStateStorage storage) { this.storage = storage; @@ -47,4 +47,8 @@ public MutableWorldState getMutable() { public Optional getNodeData(final Hash hash) { return storage.getNodeData(hash); } + + public WorldStateStorage getStorage() { + return storage; + } } diff --git a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateStorage.java b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateStorage.java index ac2a30af90..2b147cf2ce 100644 --- a/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateStorage.java +++ b/ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateStorage.java @@ -20,23 +20,33 @@ public interface WorldStateStorage { - Optional getCode(Hash codeHash); + Optional getCode(Bytes32 codeHash); Optional getAccountStateTrieNode(Bytes32 nodeHash); Optional getAccountStorageTrieNode(Bytes32 nodeHash); - Optional getNodeData(Hash hash); + Optional getNodeData(Bytes32 hash); + + default boolean contains(final Bytes32 hash) { + return getNodeData(hash).isPresent(); + } Updater updater(); interface Updater { - void putCode(BytesValue code); + Updater putCode(Bytes32 nodeHash, BytesValue code); + + default Updater putCode(final BytesValue code) { + // Skip the hash calculation for empty code + Hash codeHash = code.size() == 0 ? Hash.EMPTY : Hash.hash(code); + return putCode(codeHash, code); + } - void putAccountStateTrieNode(Bytes32 nodeHash, BytesValue node); + Updater putAccountStateTrieNode(Bytes32 nodeHash, BytesValue node); - void putAccountStorageTrieNode(Bytes32 nodeHash, BytesValue node); + Updater putAccountStorageTrieNode(Bytes32 nodeHash, BytesValue node); void commit(); diff --git a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java index a20175cd8f..efdee7741d 100644 --- a/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java @@ -58,7 +58,7 @@ private List blockSequence( final List seq = new ArrayList<>(count); final MutableWorldState worldState = - worldStateArchive.getMutable(Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH)); + worldStateArchive.getMutable(Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)); long nextBlockNumber = nextBlock; Hash parentHash = parent; @@ -95,6 +95,33 @@ private List blockSequence( return seq; } + public List createRandomAccounts(final MutableWorldState worldState, final int count) { + WorldUpdater updater = worldState.updater(); + List accounts = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + MutableAccount account = updater.getOrCreate(address()); + // Make some accounts contract accounts + if (random.nextFloat() < .5) { + // Subset of random accounts are contract accounts + account.setCode(bytesValue(5, 50)); + if (random.nextFloat() < .75) { + // Add some storage for most contract accounts + int storageValues = random.nextInt(20) + 10; + for (int j = 0; j < storageValues; j++) { + account.setStorageValue(uint256(), uint256()); + } + } + } + account.setNonce(random.nextInt(10)); + account.setBalance(Wei.of(positiveLong())); + + accounts.add(account); + } + updater.commit(); + worldState.persist(); + return accounts; + } + public List blockSequence(final int count) { final WorldStateArchive worldState = createInMemoryWorldStateArchive(); return blockSequence(count, worldState, Collections.emptyList(), Collections.emptyList()); diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorageTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorageTest.java new file mode 100644 index 0000000000..ca95b5ce44 --- /dev/null +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorageTest.java @@ -0,0 +1,157 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.storage.keyvalue; + +import static org.assertj.core.api.Assertions.assertThat; + +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie; +import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import org.junit.Test; + +public class KeyValueStorageWorldStateStorageTest { + + @Test + public void getCode_returnsEmpty() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + assertThat(storage.getCode(Hash.EMPTY)).contains(BytesValue.EMPTY); + } + + @Test + public void getAccountStateTrieNode_returnsEmptyNode() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + assertThat(storage.getAccountStateTrieNode(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) + .contains(MerklePatriciaTrie.EMPTY_TRIE_NODE); + } + + @Test + public void getAccountStorageTrieNode_returnsEmptyNode() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + assertThat(storage.getAccountStorageTrieNode(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) + .contains(MerklePatriciaTrie.EMPTY_TRIE_NODE); + } + + @Test + public void getNodeData_returnsEmptyValue() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + assertThat(storage.getNodeData(Hash.EMPTY)).contains(BytesValue.EMPTY); + } + + @Test + public void getNodeData_returnsEmptyNode() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + assertThat(storage.getNodeData(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) + .contains(MerklePatriciaTrie.EMPTY_TRIE_NODE); + } + + @Test + public void getCode_saveAndGetSpecialValues() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage + .updater() + .putCode(MerklePatriciaTrie.EMPTY_TRIE_NODE) + .putCode(BytesValue.EMPTY) + .commit(); + + assertThat(storage.getCode(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) + .contains(MerklePatriciaTrie.EMPTY_TRIE_NODE); + assertThat(storage.getCode(Hash.EMPTY)).contains(BytesValue.EMPTY); + } + + @Test + public void getCode_saveAndGetRegularValue() { + BytesValue bytes = BytesValue.fromHexString("0x123456"); + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage.updater().putCode(bytes).commit(); + + assertThat(storage.getCode(Hash.hash(bytes))).contains(bytes); + } + + @Test + public void getAccountStateTrieNode_saveAndGetSpecialValues() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage + .updater() + .putAccountStateTrieNode( + Hash.hash(MerklePatriciaTrie.EMPTY_TRIE_NODE), MerklePatriciaTrie.EMPTY_TRIE_NODE) + .putAccountStateTrieNode(Hash.hash(BytesValue.EMPTY), BytesValue.EMPTY) + .commit(); + + assertThat(storage.getAccountStateTrieNode(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) + .contains(MerklePatriciaTrie.EMPTY_TRIE_NODE); + assertThat(storage.getAccountStateTrieNode(Hash.EMPTY)).contains(BytesValue.EMPTY); + } + + @Test + public void getAccountStateTrieNode_saveAndGetRegularValue() { + BytesValue bytes = BytesValue.fromHexString("0x123456"); + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage.updater().putAccountStateTrieNode(Hash.hash(bytes), bytes).commit(); + + assertThat(storage.getAccountStateTrieNode(Hash.hash(bytes))).contains(bytes); + } + + @Test + public void getAccountStorageTrieNode_saveAndGetSpecialValues() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage + .updater() + .putAccountStorageTrieNode( + Hash.hash(MerklePatriciaTrie.EMPTY_TRIE_NODE), MerklePatriciaTrie.EMPTY_TRIE_NODE) + .putAccountStorageTrieNode(Hash.hash(BytesValue.EMPTY), BytesValue.EMPTY) + .commit(); + + assertThat(storage.getAccountStorageTrieNode(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) + .contains(MerklePatriciaTrie.EMPTY_TRIE_NODE); + assertThat(storage.getAccountStorageTrieNode(Hash.EMPTY)).contains(BytesValue.EMPTY); + } + + @Test + public void getAccountStorageTrieNode_saveAndGetRegularValue() { + BytesValue bytes = BytesValue.fromHexString("0x123456"); + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage.updater().putAccountStorageTrieNode(Hash.hash(bytes), bytes).commit(); + + assertThat(storage.getAccountStateTrieNode(Hash.hash(bytes))).contains(bytes); + } + + @Test + public void getNodeData_saveAndGetSpecialValues() { + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage + .updater() + .putAccountStorageTrieNode( + Hash.hash(MerklePatriciaTrie.EMPTY_TRIE_NODE), MerklePatriciaTrie.EMPTY_TRIE_NODE) + .putAccountStorageTrieNode(Hash.hash(BytesValue.EMPTY), BytesValue.EMPTY) + .commit(); + + assertThat(storage.getNodeData(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) + .contains(MerklePatriciaTrie.EMPTY_TRIE_NODE); + assertThat(storage.getNodeData(Hash.EMPTY)).contains(BytesValue.EMPTY); + } + + @Test + public void getNodeData_saveAndGetRegularValue() { + BytesValue bytes = BytesValue.fromHexString("0x123456"); + KeyValueStorageWorldStateStorage storage = emptyStorage(); + storage.updater().putAccountStorageTrieNode(Hash.hash(bytes), bytes).commit(); + + assertThat(storage.getNodeData(Hash.hash(bytes))).contains(bytes); + } + + private KeyValueStorageWorldStateStorage emptyStorage() { + return new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); + } +} diff --git a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldStateTest.java b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldStateTest.java index 4fcfa153b3..7f4beb028d 100644 --- a/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldStateTest.java +++ b/ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldStateTest.java @@ -58,10 +58,10 @@ private static MutableWorldState createEmpty() { @Test public void rootHash_Empty() { final MutableWorldState worldState = createEmpty(); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); worldState.persist(); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); } @Test @@ -88,10 +88,10 @@ public void removeAccount_AccountDoesNotExist() { final WorldUpdater updater = worldState.updater(); updater.deleteAccount(ADDRESS); updater.commit(); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); worldState.persist(); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); } @Test @@ -101,10 +101,10 @@ public void removeAccount_UpdatedAccount() { updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.deleteAccount(ADDRESS); updater.commit(); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); worldState.persist(); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); } @Test @@ -115,7 +115,7 @@ public void removeAccount_AccountExists() { updater.createAccount(ADDRESS).setBalance(Wei.of(100000)); updater.commit(); assertNotNull(worldState.get(ADDRESS)); - assertNotEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertNotEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); // Delete account updater = worldState.updater(); @@ -125,7 +125,7 @@ public void removeAccount_AccountExists() { updater.commit(); assertNull(updater.get(ADDRESS)); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); } @Test @@ -137,7 +137,7 @@ public void removeAccount_AccountExistsAndIsPersisted() { updater.commit(); worldState.persist(); assertNotNull(worldState.get(ADDRESS)); - assertNotEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertNotEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); // Delete account updater = worldState.updater(); @@ -151,7 +151,7 @@ public void removeAccount_AccountExistsAndIsPersisted() { worldState.persist(); assertNull(updater.get(ADDRESS)); - assertEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); } @Test @@ -377,7 +377,7 @@ public void clearStorage_AfterPersisting() { updater.commit(); worldState.persist(); assertNotNull(worldState.get(ADDRESS)); - assertNotEquals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, worldState.rootHash()); + assertNotEquals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, worldState.rootHash()); // Clear storage account = updater.getMutable(ADDRESS); diff --git a/ethereum/eth/build.gradle b/ethereum/eth/build.gradle index 931817b3b7..68e5f7cffc 100644 --- a/ethereum/eth/build.gradle +++ b/ethereum/eth/build.gradle @@ -29,9 +29,11 @@ dependencies { implementation project(':ethereum:core') implementation project(':ethereum:p2p') implementation project(':ethereum:rlp') + implementation project(':ethereum:trie') implementation project(':ethereum:permissioning') implementation project(':metrics') implementation project(':services:kvstore') + implementation project(':services:queue') implementation 'io.vertx:vertx-core' implementation 'com.google.guava:guava' diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/AccountTrieNodeDataRequest.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/AccountTrieNodeDataRequest.java new file mode 100644 index 0000000000..fcdb9df8e1 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/AccountTrieNodeDataRequest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.worldstate; + +import static com.google.common.base.Preconditions.checkNotNull; + +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.rlp.RLP; +import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie; +import tech.pegasys.pantheon.ethereum.worldstate.StateTrieAccountValue; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage.Updater; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.util.ArrayList; +import java.util.List; + +class AccountTrieNodeDataRequest extends TrieNodeDataRequest { + + AccountTrieNodeDataRequest(final Hash hash) { + super(Kind.ACCOUNT_TRIE_NODE, hash); + } + + @Override + public void persist(final Updater updater) { + checkNotNull(getData(), "Must set data before node can be persisted."); + updater.putAccountStateTrieNode(getHash(), getData()); + } + + @Override + protected NodeDataRequest createChildNodeDataRequest(final Hash childHash) { + return NodeDataRequest.createAccountDataRequest(childHash); + } + + @Override + protected List getRequestsFromTrieNodeValue(final BytesValue value) { + List nodeData = new ArrayList<>(2); + StateTrieAccountValue accountValue = StateTrieAccountValue.readFrom(RLP.input(value)); + // Add code, if appropriate + if (!accountValue.getCodeHash().equals(Hash.EMPTY)) { + nodeData.add(NodeDataRequest.createCodeRequest(accountValue.getCodeHash())); + } + // Add storage, if appropriate + if (!accountValue.getStorageRoot().equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { + // If storage is non-empty queue download + NodeDataRequest storageNode = + NodeDataRequest.createStorageDataRequest(accountValue.getStorageRoot()); + nodeData.add(storageNode); + } + return nodeData; + } +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/CodeNodeDataRequest.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/CodeNodeDataRequest.java new file mode 100644 index 0000000000..0466252281 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/CodeNodeDataRequest.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.ethereum.eth.sync.worldstate; + +import static com.google.common.base.Preconditions.checkNotNull; + +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage.Updater; + +import java.util.stream.Stream; + +class CodeNodeDataRequest extends NodeDataRequest { + + CodeNodeDataRequest(final Hash hash) { + super(Kind.CODE, hash); + } + + @Override + public void persist(final Updater updater) { + checkNotNull(getData(), "Must set data before node can be persisted."); + updater.putCode(getHash(), getData()); + } + + @Override + public Stream getChildRequests() { + // Code nodes have nothing further to download + return Stream.empty(); + } +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/NodeDataRequest.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/NodeDataRequest.java new file mode 100644 index 0000000000..67b7a63ae5 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/NodeDataRequest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.worldstate; + +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.util.stream.Stream; + +abstract class NodeDataRequest { + public enum Kind { + ACCOUNT_TRIE_NODE, + STORAGE_TRIE_NODE, + CODE + } + + private final Kind kind; + private final Hash hash; + private BytesValue data; + + protected NodeDataRequest(final Kind kind, final Hash hash) { + this.kind = kind; + this.hash = hash; + } + + public static AccountTrieNodeDataRequest createAccountDataRequest(final Hash hash) { + return new AccountTrieNodeDataRequest(hash); + } + + public static StorageTrieNodeDataRequest createStorageDataRequest(final Hash hash) { + return new StorageTrieNodeDataRequest(hash); + } + + public static CodeNodeDataRequest createCodeRequest(final Hash hash) { + return new CodeNodeDataRequest(hash); + } + + public Kind getKind() { + return kind; + } + + public Hash getHash() { + return hash; + } + + public BytesValue getData() { + return data; + } + + public NodeDataRequest setData(final BytesValue data) { + this.data = data; + return this; + } + + public abstract void persist(final WorldStateStorage.Updater updater); + + public abstract Stream getChildRequests(); +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/StorageTrieNodeDataRequest.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/StorageTrieNodeDataRequest.java new file mode 100644 index 0000000000..d61292a066 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/StorageTrieNodeDataRequest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.worldstate; + +import static com.google.common.base.Preconditions.checkNotNull; + +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage.Updater; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.util.Collections; +import java.util.List; + +class StorageTrieNodeDataRequest extends TrieNodeDataRequest { + + StorageTrieNodeDataRequest(final Hash hash) { + super(Kind.STORAGE_TRIE_NODE, hash); + } + + @Override + public void persist(final Updater updater) { + checkNotNull(getData(), "Must set data before node can be persisted."); + updater.putAccountStorageTrieNode(getHash(), getData()); + } + + @Override + protected NodeDataRequest createChildNodeDataRequest(final Hash childHash) { + return NodeDataRequest.createStorageDataRequest(childHash); + } + + @Override + protected List getRequestsFromTrieNodeValue(final BytesValue value) { + // Nothing to do for terminal storage node + return Collections.emptyList(); + } +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/TrieNodeDataRequest.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/TrieNodeDataRequest.java new file mode 100644 index 0000000000..abf9bbd512 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/TrieNodeDataRequest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.worldstate; + +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.trie.Node; +import tech.pegasys.pantheon.ethereum.trie.TrieNodeDecoder; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.util.List; +import java.util.stream.Stream; + +abstract class TrieNodeDataRequest extends NodeDataRequest { + + private static final TrieNodeDecoder nodeDecoder = TrieNodeDecoder.create(); + + TrieNodeDataRequest(final Kind kind, final Hash hash) { + super(kind, hash); + } + + @Override + public Stream getChildRequests() { + if (getData() == null) { + // If this node hasn't been downloaded yet, we can't return any child data + return Stream.empty(); + } + + final Node node = nodeDecoder.decode(getData()); + return getRequestsFromLoadedTrieNode(node); + } + + private Stream getRequestsFromLoadedTrieNode(final Node trieNode) { + // Process this node's children + final Stream childRequests = + trieNode + .getChildren() + .map(List::stream) + .map(s -> s.flatMap(this::getRequestsFromChildTrieNode)) + .orElse(Stream.of()); + + // Process value at this node, if present + return trieNode + .getValue() + .map(v -> Stream.concat(childRequests, (getRequestsFromTrieNodeValue(v).stream()))) + .orElse(childRequests); + } + + private Stream getRequestsFromChildTrieNode(final Node trieNode) { + if (trieNode.isReferencedByHash()) { + // If child nodes are reference by hash, we need to download them + NodeDataRequest req = createChildNodeDataRequest(Hash.wrap(trieNode.getHash())); + return Stream.of(req); + } + // Otherwise if the child's value has been inlined we can go ahead and process it + return getRequestsFromLoadedTrieNode(trieNode); + } + + protected abstract NodeDataRequest createChildNodeDataRequest(final Hash childHash); + + protected abstract List getRequestsFromTrieNodeValue(final BytesValue value); +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java new file mode 100644 index 0000000000..06afeed700 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java @@ -0,0 +1,201 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.worldstate; + +import tech.pegasys.pantheon.ethereum.core.BlockHeader; +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.eth.manager.AbstractPeerTask.PeerTaskResult; +import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; +import tech.pegasys.pantheon.ethereum.eth.sync.tasks.GetNodeDataFromPeerTask; +import tech.pegasys.pantheon.ethereum.eth.sync.tasks.WaitForPeerTask; +import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage; +import tech.pegasys.pantheon.metrics.LabelledMetric; +import tech.pegasys.pantheon.metrics.OperationTimer; +import tech.pegasys.pantheon.services.queue.BigQueue; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class WorldStateDownloader { + + private enum Status { + IDLE, + RUNNING, + DONE + } + + private final EthContext ethContext; + // The target header for which we want to retrieve world state + private final BlockHeader header; + private final BigQueue pendingRequests; + private final WorldStateStorage.Updater worldStateStorageUpdater; + private final int hashCountPerRequest; + private final int maxOutstandingRequests; + private final AtomicInteger outstandingRequests = new AtomicInteger(0); + private final LabelledMetric ethTasksTimer; + private final WorldStateStorage worldStateStorage; + private final AtomicBoolean sendingRequests = new AtomicBoolean(false); + private volatile CompletableFuture future; + private volatile Status status = Status.IDLE; + + public WorldStateDownloader( + final EthContext ethContext, + final WorldStateStorage worldStateStorage, + final BlockHeader header, + final BigQueue pendingRequests, + final int hashCountPerRequest, + final int maxOutstandingRequests, + final LabelledMetric ethTasksTimer) { + this.ethContext = ethContext; + this.worldStateStorage = worldStateStorage; + this.header = header; + this.pendingRequests = pendingRequests; + this.hashCountPerRequest = hashCountPerRequest; + this.maxOutstandingRequests = maxOutstandingRequests; + this.ethTasksTimer = ethTasksTimer; + this.worldStateStorageUpdater = worldStateStorage.updater(); + + Hash stateRoot = header.getStateRoot(); + if (stateRoot.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { + // If we're requesting data for an empty world state, we're already done + markDone(); + } else { + pendingRequests.enqueue(NodeDataRequest.createAccountDataRequest(header.getStateRoot())); + } + } + + public CompletableFuture run() { + synchronized (this) { + if (status == Status.DONE || status == Status.RUNNING) { + return future; + } + status = Status.RUNNING; + future = new CompletableFuture<>(); + } + + requestNodeData(); + return future; + } + + private void requestNodeData() { + if (sendingRequests.compareAndSet(false, true)) { + while (shouldRequestNodeData()) { + Optional maybePeer = ethContext.getEthPeers().idlePeer(header.getNumber()); + + if (!maybePeer.isPresent()) { + // If no peer is available, wait and try again + waitForNewPeer().whenComplete((r, t) -> requestNodeData()); + break; + } else { + EthPeer peer = maybePeer.get(); + + // Collect data to be requested + List toRequest = new ArrayList<>(); + for (int i = 0; i < hashCountPerRequest; i++) { + NodeDataRequest pendingRequest = pendingRequests.dequeue(); + if (pendingRequest == null) { + break; + } + toRequest.add(pendingRequest); + } + + // Request and process node data + outstandingRequests.incrementAndGet(); + sendAndProcessRequests(peer, toRequest) + .whenComplete( + (res, error) -> { + if (outstandingRequests.decrementAndGet() == 0 && pendingRequests.isEmpty()) { + // We're done + worldStateStorageUpdater.commit(); + markDone(); + } else { + // Send out additional requests + requestNodeData(); + } + }); + } + } + sendingRequests.set(false); + } + } + + private synchronized void markDone() { + if (future == null) { + future = CompletableFuture.completedFuture(null); + } else { + future.complete(null); + } + status = Status.DONE; + } + + private boolean shouldRequestNodeData() { + return !future.isDone() + && outstandingRequests.get() < maxOutstandingRequests + && !pendingRequests.isEmpty(); + } + + private CompletableFuture waitForNewPeer() { + return ethContext + .getScheduler() + .timeout(WaitForPeerTask.create(ethContext, ethTasksTimer), Duration.ofSeconds(5)); + } + + private CompletableFuture sendAndProcessRequests( + final EthPeer peer, final List requests) { + List hashes = + requests.stream().map(NodeDataRequest::getHash).distinct().collect(Collectors.toList()); + return GetNodeDataFromPeerTask.forHashes(ethContext, hashes, ethTasksTimer) + .assignPeer(peer) + .run() + .thenApply(PeerTaskResult::getResult) + .thenApply(this::mapNodeDataByHash) + .whenComplete( + (data, err) -> { + boolean requestFailed = err != null; + for (NodeDataRequest request : requests) { + BytesValue matchingData = requestFailed ? null : data.get(request.getHash()); + if (matchingData == null) { + pendingRequests.enqueue(request); + } else { + // Persist request data + request.setData(matchingData); + request.persist(worldStateStorageUpdater); + + // Queue child requests + request + .getChildRequests() + .filter(n -> !worldStateStorage.contains(n.getHash())) + .forEach(pendingRequests::enqueue); + } + } + }); + } + + private Map mapNodeDataByHash(final List data) { + // Map data by hash + Map dataByHash = new HashMap<>(); + data.stream().forEach(d -> dataByHash.put(Hash.hash(d), d)); + return dataByHash; + } +} diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/DeterministicEthScheduler.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/DeterministicEthScheduler.java index dea698c3d8..49c7629c2e 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/DeterministicEthScheduler.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/DeterministicEthScheduler.java @@ -16,6 +16,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; /** Schedules tasks that run immediately and synchronously for testing. */ public class DeterministicEthScheduler extends EthScheduler { @@ -23,7 +24,7 @@ public class DeterministicEthScheduler extends EthScheduler { private final TimeoutPolicy timeoutPolicy; DeterministicEthScheduler() { - this(() -> false); + this(TimeoutPolicy.NEVER); } DeterministicEthScheduler(final TimeoutPolicy timeoutPolicy) { @@ -55,6 +56,19 @@ public void failAfterTimeout(final CompletableFuture promise, final Durat @FunctionalInterface public interface TimeoutPolicy { + TimeoutPolicy NEVER = () -> false; + boolean shouldTimeout(); + + static TimeoutPolicy timeoutXTimes(final int times) { + final AtomicInteger timeouts = new AtomicInteger(times); + return () -> { + if (timeouts.get() <= 0) { + return false; + } + timeouts.decrementAndGet(); + return true; + }; + } } } diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/EthProtocolManagerTestUtil.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/EthProtocolManagerTestUtil.java index b6fb16e705..ff523b04f8 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/EthProtocolManagerTestUtil.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/EthProtocolManagerTestUtil.java @@ -47,7 +47,11 @@ public static EthProtocolManager create( public static EthProtocolManager create( final Blockchain blockchain, final WorldStateArchive worldStateArchive) { - return create(blockchain, worldStateArchive, () -> false); + return create(blockchain, worldStateArchive, TimeoutPolicy.NEVER); + } + + public static EthProtocolManager create() { + return create(TimeoutPolicy.NEVER); } public static EthProtocolManager create(final TimeoutPolicy timeoutPolicy) { @@ -59,10 +63,6 @@ public static EthProtocolManager create(final TimeoutPolicy timeoutPolicy) { return create(blockchain, worldStateArchive, timeoutPolicy); } - public static EthProtocolManager create() { - return create(() -> false); - } - public static void broadcastMessage( final EthProtocolManager ethProtocolManager, final RespondingEthPeer peer, diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/ethtaskutils/BlockchainSetupUtil.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/ethtaskutils/BlockchainSetupUtil.java index 61abd6d906..f6db36633a 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/ethtaskutils/BlockchainSetupUtil.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/manager/ethtaskutils/BlockchainSetupUtil.java @@ -54,7 +54,7 @@ public class BlockchainSetupUtil { private final List blocks; private long maxBlockNumber; - public BlockchainSetupUtil( + private BlockchainSetupUtil( final GenesisState genesisState, final MutableBlockchain blockchain, final ProtocolContext protocolContext, diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java new file mode 100644 index 0000000000..a66e559598 --- /dev/null +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java @@ -0,0 +1,283 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.worldstate; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import tech.pegasys.pantheon.ethereum.chain.Blockchain; +import tech.pegasys.pantheon.ethereum.core.Account; +import tech.pegasys.pantheon.ethereum.core.BlockDataGenerator; +import tech.pegasys.pantheon.ethereum.core.BlockDataGenerator.BlockOptions; +import tech.pegasys.pantheon.ethereum.core.BlockHeader; +import tech.pegasys.pantheon.ethereum.core.Hash; +import tech.pegasys.pantheon.ethereum.core.MutableWorldState; +import tech.pegasys.pantheon.ethereum.core.WorldState; +import tech.pegasys.pantheon.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy; +import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManager; +import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManagerTestUtil; +import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer; +import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer.Responder; +import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage; +import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive; +import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage; +import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; +import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage; +import tech.pegasys.pantheon.services.queue.BigQueue; +import tech.pegasys.pantheon.services.queue.InMemoryBigQueue; +import tech.pegasys.pantheon.util.bytes.Bytes32; +import tech.pegasys.pantheon.util.uint.UInt256; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Test; + +public class WorldStateDownloaderTest { + + private static final Hash EMPTY_TRIE_ROOT = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); + + @Test + public void downloadWorldStateFromPeers_onePeerOneWithManyRequestsOneAtATime() { + downloadAvailableWorldStateFromPeers(1, 50, 1, 1); + } + + @Test + public void downloadWorldStateFromPeers_onePeerOneWithManyRequests() { + downloadAvailableWorldStateFromPeers(1, 50, 1, 10); + } + + @Test + public void downloadWorldStateFromPeers_onePeerWithSingleRequest() { + downloadAvailableWorldStateFromPeers(1, 1, 100, 10); + } + + @Test + public void downloadWorldStateFromPeers_largeStateFromMultiplePeers() { + downloadAvailableWorldStateFromPeers(5, 100, 10, 10); + } + + @Test + public void downloadWorldStateFromPeers_smallStateFromMultiplePeers() { + downloadAvailableWorldStateFromPeers(5, 5, 1, 10); + } + + @Test + public void downloadWorldStateFromPeers_singleRequestWithMultiplePeers() { + downloadAvailableWorldStateFromPeers(5, 1, 50, 50); + } + + @Test + public void downloadEmptyWorldState() { + BlockDataGenerator dataGen = new BlockDataGenerator(1); + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); + BlockHeader header = + dataGen + .block(BlockOptions.create().setStateRoot(EMPTY_TRIE_ROOT).setBlockNumber(10)) + .getHeader(); + + // Create some peers + List peers = + Stream.generate( + () -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber())) + .limit(5) + .collect(Collectors.toList()); + + BigQueue queue = new InMemoryBigQueue<>(); + WorldStateStorage localStorage = + new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); + WorldStateDownloader downloader = + new WorldStateDownloader( + ethProtocolManager.ethContext(), + localStorage, + header, + queue, + 10, + 10, + NoOpMetricsSystem.NO_OP_LABELLED_TIMER); + + CompletableFuture future = downloader.run(); + assertThat(future).isDone(); + + // Peers should not have been queried + for (RespondingEthPeer peer : peers) { + assertThat(peer.hasOutstandingRequests()).isFalse(); + } + } + + @Test + public void canRecoverFromTimeouts() { + BlockDataGenerator dataGen = new BlockDataGenerator(1); + TimeoutPolicy timeoutPolicy = TimeoutPolicy.timeoutXTimes(2); + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(timeoutPolicy); + + // Setup "remote" state + final WorldStateStorage remoteStorage = + new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); + final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage); + final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable(); + + // Generate accounts and save corresponding state root + final List accounts = dataGen.createRandomAccounts(remoteWorldState, 20); + final Hash stateRoot = remoteWorldState.rootHash(); + assertThat(stateRoot).isNotEqualTo(EMPTY_TRIE_ROOT); // Sanity check + BlockHeader header = + dataGen.block(BlockOptions.create().setStateRoot(stateRoot).setBlockNumber(10)).getHeader(); + + // Create some peers + List peers = + Stream.generate( + () -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber())) + .limit(5) + .collect(Collectors.toList()); + + BigQueue queue = new InMemoryBigQueue<>(); + WorldStateStorage localStorage = + new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); + WorldStateDownloader downloader = + new WorldStateDownloader( + ethProtocolManager.ethContext(), + localStorage, + header, + queue, + 10, + 10, + NoOpMetricsSystem.NO_OP_LABELLED_TIMER); + + CompletableFuture result = downloader.run(); + + // Respond to node data requests + Responder responder = + RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive); + while (!result.isDone()) { + for (RespondingEthPeer peer : peers) { + peer.respond(responder); + } + } + + // Check that all expected account data was downloaded + WorldStateArchive localWorldStateArchive = new WorldStateArchive(localStorage); + final WorldState localWorldState = localWorldStateArchive.get(stateRoot); + assertThat(result).isDone(); + assertAccountsMatch(localWorldState, accounts); + } + + private void downloadAvailableWorldStateFromPeers( + final int peerCount, + final int accountCount, + final int hashesPerRequest, + final int maxOutstandingRequests) { + final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); + final int trailingPeerCount = 5; + BlockDataGenerator dataGen = new BlockDataGenerator(1); + + // Setup "remote" state + final WorldStateStorage remoteStorage = + new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); + final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage); + final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable(); + + // Generate accounts and save corresponding state root + final List accounts = dataGen.createRandomAccounts(remoteWorldState, accountCount); + final Hash stateRoot = remoteWorldState.rootHash(); + assertThat(stateRoot).isNotEqualTo(EMPTY_TRIE_ROOT); // Sanity check + BlockHeader header = + dataGen.block(BlockOptions.create().setStateRoot(stateRoot).setBlockNumber(10)).getHeader(); + + // Generate more data that should not be downloaded + final List otherAccounts = dataGen.createRandomAccounts(remoteWorldState, 5); + Hash otherStateRoot = remoteWorldState.rootHash(); + BlockHeader otherHeader = + dataGen + .block(BlockOptions.create().setStateRoot(otherStateRoot).setBlockNumber(11)) + .getHeader(); + assertThat(otherStateRoot).isNotEqualTo(stateRoot); // Sanity check + + BigQueue queue = new InMemoryBigQueue<>(); + WorldStateStorage localStorage = + new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()); + WorldStateArchive localWorldStateArchive = new WorldStateArchive(localStorage); + WorldStateDownloader downloader = + new WorldStateDownloader( + ethProtocolManager.ethContext(), + localStorage, + header, + queue, + hashesPerRequest, + maxOutstandingRequests, + NoOpMetricsSystem.NO_OP_LABELLED_TIMER); + + // Create some peers that can respond + List usefulPeers = + Stream.generate( + () -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, header.getNumber())) + .limit(peerCount) + .collect(Collectors.toList()); + // And some irrelevant peers + List trailingPeers = + Stream.generate( + () -> + EthProtocolManagerTestUtil.createPeer( + ethProtocolManager, header.getNumber() - 1L)) + .limit(trailingPeerCount) + .collect(Collectors.toList()); + + // Start downloader + CompletableFuture result = downloader.run(); + + // Respond to node data requests + Responder responder = + RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive); + while (!result.isDone()) { + for (RespondingEthPeer peer : usefulPeers) { + peer.respond(responder); + } + } + + // Check that trailing peers were not queried for data + for (RespondingEthPeer trailingPeer : trailingPeers) { + assertThat(trailingPeer.hasOutstandingRequests()).isFalse(); + } + + // Check that all expected account data was downloaded + final WorldState localWorldState = localWorldStateArchive.get(stateRoot); + assertThat(result).isDone(); + assertAccountsMatch(localWorldState, accounts); + + // We shouldn't have any extra data locally + assertThat(localStorage.contains(otherHeader.getStateRoot())).isFalse(); + for (Account otherAccount : otherAccounts) { + assertThat(localWorldState.get(otherAccount.getAddress())).isNull(); + } + } + + private void assertAccountsMatch( + final WorldState worldState, final List expectedAccounts) { + for (Account expectedAccount : expectedAccounts) { + Account actualAccount = worldState.get(expectedAccount.getAddress()); + assertThat(actualAccount).isNotNull(); + // Check each field + assertThat(actualAccount.getNonce()).isEqualTo(expectedAccount.getNonce()); + assertThat(actualAccount.getCode()).isEqualTo(expectedAccount.getCode()); + assertThat(actualAccount.getBalance()).isEqualTo(expectedAccount.getBalance()); + + Map actualStorage = actualAccount.storageEntriesFrom(Bytes32.ZERO, 500); + Map expectedStorage = expectedAccount.storageEntriesFrom(Bytes32.ZERO, 500); + assertThat(actualStorage).isEqualTo(expectedStorage); + } + } +} diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/BranchNode.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/BranchNode.java index f3b02ebf1b..2899d4a14d 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/BranchNode.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/BranchNode.java @@ -23,6 +23,8 @@ import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Function; @@ -73,6 +75,11 @@ public Optional getValue() { return value; } + @Override + public Optional>> getChildren() { + return Optional.of(Collections.unmodifiableList(children)); + } + public Node child(final byte index) { return children.get(index); } @@ -103,11 +110,10 @@ public BytesValue getRlp() { @Override public BytesValue getRlpRef() { - final BytesValue rlp = getRlp(); - if (rlp.size() < 32) { - return rlp; - } else { + if (isReferencedByHash()) { return RLP.encodeOne(getHash()); + } else { + return getRlp(); } } diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/ExtensionNode.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/ExtensionNode.java index 7a8be5d591..41c0909581 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/ExtensionNode.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/ExtensionNode.java @@ -22,6 +22,8 @@ import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.List; import java.util.Optional; class ExtensionNode implements Node { @@ -58,7 +60,12 @@ public BytesValue getPath() { @Override public Optional getValue() { - throw new UnsupportedOperationException(); + return Optional.empty(); + } + + @Override + public Optional>> getChildren() { + return Optional.of(Collections.singletonList(child)); } public Node getChild() { @@ -85,11 +92,10 @@ public BytesValue getRlp() { @Override public BytesValue getRlpRef() { - final BytesValue rlp = getRlp(); - if (rlp.size() < 32) { - return rlp; - } else { + if (isReferencedByHash()) { return RLP.encodeOne(getHash()); + } else { + return getRlp(); } } diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/LeafNode.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/LeafNode.java index 07ed18fc5d..45e72e6923 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/LeafNode.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/LeafNode.java @@ -21,6 +21,7 @@ import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; +import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -64,6 +65,11 @@ public Optional getValue() { return Optional.of(value); } + @Override + public Optional>> getChildren() { + return Optional.empty(); + } + @Override public BytesValue getRlp() { if (rlp != null) { @@ -85,11 +91,10 @@ public BytesValue getRlp() { @Override public BytesValue getRlpRef() { - final BytesValue rlp = getRlp(); - if (rlp.size() < 32) { - return rlp; - } else { + if (isReferencedByHash()) { return RLP.encodeOne(getHash()); + } else { + return getRlp(); } } diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerklePatriciaTrie.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerklePatriciaTrie.java index 0cb31da680..1b869ada5e 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerklePatriciaTrie.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerklePatriciaTrie.java @@ -16,6 +16,7 @@ import tech.pegasys.pantheon.ethereum.rlp.RLP; import tech.pegasys.pantheon.util.bytes.Bytes32; +import tech.pegasys.pantheon.util.bytes.BytesValue; import java.util.Map; import java.util.Optional; @@ -23,7 +24,8 @@ /** An Merkle Patricial Trie. */ public interface MerklePatriciaTrie { - Bytes32 EMPTY_TRIE_ROOT_HASH = keccak256(RLP.NULL); + BytesValue EMPTY_TRIE_NODE = RLP.NULL; + Bytes32 EMPTY_TRIE_NODE_HASH = keccak256(EMPTY_TRIE_NODE); /** * Returns an {@code Optional} of value mapped to the hash if it exists; otherwise empty. diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerkleStorageException.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerkleTrieException.java similarity index 80% rename from ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerkleStorageException.java rename to ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerkleTrieException.java index 3ed75052f4..b00a1f60b2 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerkleStorageException.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/MerkleTrieException.java @@ -16,13 +16,13 @@ * This exception is thrown when there is an issue retrieving or decoding values from {@link * MerkleStorage}. */ -public class MerkleStorageException extends RuntimeException { +public class MerkleTrieException extends RuntimeException { - public MerkleStorageException(final String message) { + public MerkleTrieException(final String message) { super(message); } - public MerkleStorageException(final String message, final Exception cause) { + public MerkleTrieException(final String message, final Exception cause) { super(message, cause); } } diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/Node.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/Node.java index 4d8b62e1ce..8f35692d20 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/Node.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/Node.java @@ -15,9 +15,10 @@ import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.util.List; import java.util.Optional; -interface Node { +public interface Node { Node accept(PathNodeVisitor visitor, BytesValue path); @@ -27,10 +28,23 @@ interface Node { Optional getValue(); + Optional>> getChildren(); + BytesValue getRlp(); BytesValue getRlpRef(); + /** + * Whether a reference to this node should be represented as a hash of the rlp, or the node rlp + * itself should be inlined (the rlp stored directly in the parent node). If true, the node is + * referenced by hash. If false, the node is referenced by its rlp-encoded value. + * + * @return true if this node should be referenced by hash + */ + default boolean isReferencedByHash() { + return getRlp().size() >= 32; + } + Bytes32 getHash(); Node replacePath(BytesValue path); diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/NullNode.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/NullNode.java index 322e6240e6..e6996facab 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/NullNode.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/NullNode.java @@ -12,17 +12,13 @@ */ package tech.pegasys.pantheon.ethereum.trie; -import static tech.pegasys.pantheon.crypto.Hash.keccak256; - -import tech.pegasys.pantheon.ethereum.rlp.RLP; import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.util.List; import java.util.Optional; class NullNode implements Node { - private static final Bytes32 HASH = keccak256(RLP.NULL); - @SuppressWarnings("rawtypes") private static final NullNode instance = new NullNode(); @@ -53,19 +49,24 @@ public Optional getValue() { return Optional.empty(); } + @Override + public Optional>> getChildren() { + return Optional.empty(); + } + @Override public BytesValue getRlp() { - return RLP.NULL; + return MerklePatriciaTrie.EMPTY_TRIE_NODE; } @Override public BytesValue getRlpRef() { - return RLP.NULL; + return MerklePatriciaTrie.EMPTY_TRIE_NODE; } @Override public Bytes32 getHash() { - return HASH; + return MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH; } @Override diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredMerklePatriciaTrie.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredMerklePatriciaTrie.java index b7f820c692..63f6f3648c 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredMerklePatriciaTrie.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredMerklePatriciaTrie.java @@ -45,7 +45,7 @@ public StoredMerklePatriciaTrie( final NodeLoader nodeLoader, final Function valueSerializer, final Function valueDeserializer) { - this(nodeLoader, MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH, valueSerializer, valueDeserializer); + this(nodeLoader, MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH, valueSerializer, valueDeserializer); } /** @@ -64,7 +64,7 @@ public StoredMerklePatriciaTrie( final Function valueDeserializer) { this.nodeFactory = new StoredNodeFactory<>(nodeLoader, valueSerializer, valueDeserializer); this.root = - rootHash.equals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH) + rootHash.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH) ? NullNode.instance() : new StoredNode<>(nodeFactory, rootHash); } @@ -99,7 +99,7 @@ public void commit(final NodeUpdater nodeUpdater) { // Reset root so dirty nodes can be garbage collected final Bytes32 rootHash = root.getHash(); this.root = - rootHash.equals(MerklePatriciaTrie.EMPTY_TRIE_ROOT_HASH) + rootHash.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH) ? NullNode.instance() : new StoredNode<>(nodeFactory, rootHash); } diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNode.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNode.java index bd80594f90..a9b0759829 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNode.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNode.java @@ -16,6 +16,7 @@ import tech.pegasys.pantheon.util.bytes.Bytes32; import tech.pegasys.pantheon.util.bytes.BytesValue; +import java.util.List; import java.util.Optional; class StoredNode implements Node { @@ -63,10 +64,14 @@ public Optional getValue() { return load().getValue(); } + @Override + public Optional>> getChildren() { + return load().getChildren(); + } + @Override public BytesValue getRlp() { - // Getting the rlp representation is only needed when persisting a concrete node - throw new UnsupportedOperationException(); + return load().getRlp(); } @Override @@ -75,6 +80,12 @@ public BytesValue getRlpRef() { return RLP.encodeOne(hash); } + @Override + public boolean isReferencedByHash() { + // Stored nodes represent only nodes that are referenced by hash + return true; + } + @Override public Bytes32 getHash() { return hash; @@ -87,7 +98,11 @@ public Node replacePath(final BytesValue path) { private Node load() { if (loaded == null) { - loaded = nodeFactory.retrieve(hash); + loaded = + nodeFactory + .retrieve(hash) + .orElseThrow( + () -> new MerkleTrieException("Unable to load trie node value for hash " + hash)); } return loaded; @@ -95,7 +110,10 @@ private Node load() { @Override public String print() { - final String value = load().print(); - return value; + if (loaded == null) { + return "StoredNode:" + "\n\tRef: " + getRlpRef(); + } else { + return load().print(); + } } } diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNodeFactory.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNodeFactory.java index 5268022fe2..41dca7855e 100644 --- a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNodeFactory.java +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/StoredNodeFactory.java @@ -87,7 +87,7 @@ private Node handleNewNode(final Node node) { return node; } - public Node retrieve(final Bytes32 hash) throws MerkleStorageException { + public Optional> retrieve(final Bytes32 hash) throws MerkleTrieException { return nodeLoader .getNode(hash) .map( @@ -97,16 +97,19 @@ public Node retrieve(final Bytes32 hash) throws MerkleStorageException { assert (hash.equals(node.getHash())) : "Node hash " + node.getHash() + " not equal to expected " + hash; return node; - }) - .orElseThrow(() -> new MerkleStorageException("Missing value for hash " + hash)); + }); + } + + public Node decode(final BytesValue rlp) { + return decode(rlp, () -> String.format("Failed to decode value %s", rlp.toString())); } private Node decode(final BytesValue rlp, final Supplier errMessage) - throws MerkleStorageException { + throws MerkleTrieException { try { return decode(RLP.input(rlp), errMessage); } catch (final RLPException ex) { - throw new MerkleStorageException(errMessage.get(), ex); + throw new MerkleTrieException(errMessage.get(), ex); } } @@ -123,8 +126,7 @@ private Node decode(final RLPInput nodeRLPs, final Supplier errMessag try { path = CompactEncoding.decode(encodedPath); } catch (final IllegalArgumentException ex) { - throw new MerkleStorageException( - errMessage.get() + ": invalid path " + encodedPath, ex); + throw new MerkleTrieException(errMessage.get() + ": invalid path " + encodedPath, ex); } final int size = path.size(); @@ -138,7 +140,7 @@ private Node decode(final RLPInput nodeRLPs, final Supplier errMessag return decodeBranch(nodeRLPs, errMessage); default: - throw new MerkleStorageException( + throw new MerkleTrieException( errMessage.get() + format(": invalid list size %s", nodesCount)); } } finally { @@ -189,7 +191,7 @@ private BranchNode decodeBranch(final RLPInput nodeRLPs, final Supplier decodeLeaf( final BytesValue path, final RLPInput valueRlp, final Supplier errMessage) { if (valueRlp.nextIsNull()) { - throw new MerkleStorageException(errMessage.get() + ": leaf has null value"); + throw new MerkleTrieException(errMessage.get() + ": leaf has null value"); } final V value = decodeValue(valueRlp, errMessage); return new LeafNode<>(path, value, this, valueSerializer); @@ -198,7 +200,7 @@ private LeafNode decodeLeaf( @SuppressWarnings("unchecked") private NullNode decodeNull(final RLPInput nodeRLPs, final Supplier errMessage) { if (!nodeRLPs.nextIsNull()) { - throw new MerkleStorageException(errMessage.get() + ": list size 1 but not null"); + throw new MerkleTrieException(errMessage.get() + ": list size 1 but not null"); } nodeRLPs.skipNext(); return NULL_NODE; @@ -209,7 +211,7 @@ private V decodeValue(final RLPInput valueRlp, final Supplier errMessage try { bytes = valueRlp.readBytesValue(); } catch (final RLPException ex) { - throw new MerkleStorageException( + throw new MerkleTrieException( errMessage.get() + ": failed decoding value rlp " + valueRlp, ex); } return deserializeValue(errMessage, bytes); @@ -220,8 +222,7 @@ private V deserializeValue(final Supplier errMessage, final BytesValue b try { value = valueDeserializer.apply(bytes); } catch (final IllegalArgumentException ex) { - throw new MerkleStorageException( - errMessage.get() + ": failed deserializing value " + bytes, ex); + throw new MerkleTrieException(errMessage.get() + ": failed deserializing value " + bytes, ex); } return value; } diff --git a/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/TrieNodeDecoder.java b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/TrieNodeDecoder.java new file mode 100644 index 0000000000..18ca328e50 --- /dev/null +++ b/ethereum/trie/src/main/java/tech/pegasys/pantheon/ethereum/trie/TrieNodeDecoder.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.trie; + +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.util.Optional; +import java.util.function.Function; + +public class TrieNodeDecoder { + + private final StoredNodeFactory nodeFactory; + + private TrieNodeDecoder() { + nodeFactory = + new StoredNodeFactory<>((h) -> Optional.empty(), Function.identity(), Function.identity()); + } + + public static TrieNodeDecoder create() { + return new TrieNodeDecoder(); + } + + public Node decode(final BytesValue rlp) { + return nodeFactory.decode(rlp); + } +} diff --git a/services/queue/build.gradle b/services/queue/build.gradle new file mode 100644 index 0000000000..616f648e2f --- /dev/null +++ b/services/queue/build.gradle @@ -0,0 +1,38 @@ +/* + * Copyright 2018 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. + */ + +apply plugin: 'java-library' + +jar { + baseName 'pantheon-queue' + manifest { + attributes( + 'Specification-Title': baseName, + 'Specification-Version': project.version, + 'Implementation-Title': baseName, + 'Implementation-Version': calculateVersion() + ) + } +} + +dependencies { + api project(':util') + implementation project(':metrics') + + implementation 'org.apache.logging.log4j:log4j-api' + implementation 'com.google.guava:guava' + + runtime 'org.apache.logging.log4j:log4j-core' + + testImplementation 'junit:junit' +} diff --git a/services/queue/src/main/java/tech/pegasys/pantheon/services/queue/BigQueue.java b/services/queue/src/main/java/tech/pegasys/pantheon/services/queue/BigQueue.java new file mode 100644 index 0000000000..793669a8fb --- /dev/null +++ b/services/queue/src/main/java/tech/pegasys/pantheon/services/queue/BigQueue.java @@ -0,0 +1,33 @@ +/* + * Copyright 2018 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.services.queue; + +import java.io.Closeable; + +/** + * Represents a very large thread-safe queue that may exceed memory limits. + * + * @param the type of data held in the queue + */ +public interface BigQueue extends Closeable { + + void enqueue(T value); + + T dequeue(); + + long size(); + + default boolean isEmpty() { + return size() == 0; + } +} diff --git a/services/queue/src/main/java/tech/pegasys/pantheon/services/queue/InMemoryBigQueue.java b/services/queue/src/main/java/tech/pegasys/pantheon/services/queue/InMemoryBigQueue.java new file mode 100644 index 0000000000..b40b4ea186 --- /dev/null +++ b/services/queue/src/main/java/tech/pegasys/pantheon/services/queue/InMemoryBigQueue.java @@ -0,0 +1,40 @@ +/* + * 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.services.queue; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class InMemoryBigQueue implements BigQueue { + private final Queue internalQueue = new ConcurrentLinkedQueue<>(); + + @Override + public void enqueue(final T value) { + internalQueue.add(value); + } + + @Override + public T dequeue() { + return internalQueue.poll(); + } + + @Override + public long size() { + return internalQueue.size(); + } + + @Override + public void close() { + internalQueue.clear(); + } +} diff --git a/services/queue/src/main/resources/log4j2.xml b/services/queue/src/main/resources/log4j2.xml new file mode 100644 index 0000000000..1d05234b62 --- /dev/null +++ b/services/queue/src/main/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + INFO + + + + + + + + + + + + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index bbfac97692..42fc2c1ec5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,6 +33,7 @@ include 'ethereum:trie' include 'metrics' include 'pantheon' include 'services:kvstore' +include 'services:queue' include 'testutil' include 'util' include 'errorprone-checks' From 424c91b73315850e60ef092df3c68f5b2de6f546 Mon Sep 17 00:00:00 2001 From: MadelineMurray <43356962+MadelineMurray@users.noreply.github.com> Date: Sat, 26 Jan 2019 18:11:19 +1300 Subject: [PATCH 12/31] Added p2p enabled (#654) --- docs/Reference/Pantheon-CLI-Syntax.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index 8bc07b5069..b0a5ac2f53 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -451,6 +451,23 @@ Not intended for use with mainnet or public testnets. !!!note Permissioning is under development and will be available in v1.0. +### p2p-enabled + +```bash tab="Syntax" +--p2p-enabled= +``` + +```bash tab="Command line" +--p2p-enabled=false +``` + +```bash tab="Example Configuration File" +p2p-enabled=false +``` + +Enables or disables all p2p communication. +The default is true. + ### p2p-host ```bash tab="Syntax" From a0f808933b108c164f29df0693ffad38446d511e Mon Sep 17 00:00:00 2001 From: mbaxter Date: Mon, 28 Jan 2019 13:17:11 -0500 Subject: [PATCH 13/31] Update WorldStateDownloader run() interface to accept header (#677) --- .../sync/worldstate/WorldStateDownloader.java | 30 ++++++++----------- .../worldstate/WorldStateDownloaderTest.java | 15 ++++------ 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java index 06afeed700..a043b6bbe2 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloader.java @@ -46,8 +46,6 @@ private enum Status { } private final EthContext ethContext; - // The target header for which we want to retrieve world state - private final BlockHeader header; private final BigQueue pendingRequests; private final WorldStateStorage.Updater worldStateStorageUpdater; private final int hashCountPerRequest; @@ -62,30 +60,20 @@ private enum Status { public WorldStateDownloader( final EthContext ethContext, final WorldStateStorage worldStateStorage, - final BlockHeader header, final BigQueue pendingRequests, final int hashCountPerRequest, final int maxOutstandingRequests, final LabelledMetric ethTasksTimer) { this.ethContext = ethContext; this.worldStateStorage = worldStateStorage; - this.header = header; this.pendingRequests = pendingRequests; this.hashCountPerRequest = hashCountPerRequest; this.maxOutstandingRequests = maxOutstandingRequests; this.ethTasksTimer = ethTasksTimer; this.worldStateStorageUpdater = worldStateStorage.updater(); - - Hash stateRoot = header.getStateRoot(); - if (stateRoot.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { - // If we're requesting data for an empty world state, we're already done - markDone(); - } else { - pendingRequests.enqueue(NodeDataRequest.createAccountDataRequest(header.getStateRoot())); - } } - public CompletableFuture run() { + public CompletableFuture run(final BlockHeader header) { synchronized (this) { if (status == Status.DONE || status == Status.RUNNING) { return future; @@ -94,18 +82,26 @@ public CompletableFuture run() { future = new CompletableFuture<>(); } - requestNodeData(); + Hash stateRoot = header.getStateRoot(); + if (stateRoot.equals(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH)) { + // If we're requesting data for an empty world state, we're already done + markDone(); + } else { + pendingRequests.enqueue(NodeDataRequest.createAccountDataRequest(stateRoot)); + requestNodeData(header); + } + return future; } - private void requestNodeData() { + private void requestNodeData(final BlockHeader header) { if (sendingRequests.compareAndSet(false, true)) { while (shouldRequestNodeData()) { Optional maybePeer = ethContext.getEthPeers().idlePeer(header.getNumber()); if (!maybePeer.isPresent()) { // If no peer is available, wait and try again - waitForNewPeer().whenComplete((r, t) -> requestNodeData()); + waitForNewPeer().whenComplete((r, t) -> requestNodeData(header)); break; } else { EthPeer peer = maybePeer.get(); @@ -131,7 +127,7 @@ private void requestNodeData() { markDone(); } else { // Send out additional requests - requestNodeData(); + requestNodeData(header); } }); } diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java index a66e559598..1478178f74 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java @@ -85,7 +85,7 @@ public void downloadWorldStateFromPeers_singleRequestWithMultiplePeers() { public void downloadEmptyWorldState() { BlockDataGenerator dataGen = new BlockDataGenerator(1); final EthProtocolManager ethProtocolManager = EthProtocolManagerTestUtil.create(); - BlockHeader header = + final BlockHeader header = dataGen .block(BlockOptions.create().setStateRoot(EMPTY_TRIE_ROOT).setBlockNumber(10)) .getHeader(); @@ -104,13 +104,12 @@ public void downloadEmptyWorldState() { new WorldStateDownloader( ethProtocolManager.ethContext(), localStorage, - header, queue, 10, 10, NoOpMetricsSystem.NO_OP_LABELLED_TIMER); - CompletableFuture future = downloader.run(); + CompletableFuture future = downloader.run(header); assertThat(future).isDone(); // Peers should not have been queried @@ -135,7 +134,7 @@ public void canRecoverFromTimeouts() { final List accounts = dataGen.createRandomAccounts(remoteWorldState, 20); final Hash stateRoot = remoteWorldState.rootHash(); assertThat(stateRoot).isNotEqualTo(EMPTY_TRIE_ROOT); // Sanity check - BlockHeader header = + final BlockHeader header = dataGen.block(BlockOptions.create().setStateRoot(stateRoot).setBlockNumber(10)).getHeader(); // Create some peers @@ -152,13 +151,12 @@ public void canRecoverFromTimeouts() { new WorldStateDownloader( ethProtocolManager.ethContext(), localStorage, - header, queue, 10, 10, NoOpMetricsSystem.NO_OP_LABELLED_TIMER); - CompletableFuture result = downloader.run(); + CompletableFuture result = downloader.run(header); // Respond to node data requests Responder responder = @@ -195,7 +193,7 @@ private void downloadAvailableWorldStateFromPeers( final List accounts = dataGen.createRandomAccounts(remoteWorldState, accountCount); final Hash stateRoot = remoteWorldState.rootHash(); assertThat(stateRoot).isNotEqualTo(EMPTY_TRIE_ROOT); // Sanity check - BlockHeader header = + final BlockHeader header = dataGen.block(BlockOptions.create().setStateRoot(stateRoot).setBlockNumber(10)).getHeader(); // Generate more data that should not be downloaded @@ -215,7 +213,6 @@ private void downloadAvailableWorldStateFromPeers( new WorldStateDownloader( ethProtocolManager.ethContext(), localStorage, - header, queue, hashesPerRequest, maxOutstandingRequests, @@ -237,7 +234,7 @@ private void downloadAvailableWorldStateFromPeers( .collect(Collectors.toList()); // Start downloader - CompletableFuture result = downloader.run(); + CompletableFuture result = downloader.run(header); // Respond to node data requests Responder responder = From a68e22ef1311977538ee427dff0eb05f3fe89e19 Mon Sep 17 00:00:00 2001 From: MadelineMurray <43356962+MadelineMurray@users.noreply.github.com> Date: Tue, 29 Jan 2019 05:33:01 +1000 Subject: [PATCH 14/31] Added reference to changelog for CLI changes mapping (#673) --- docs/Getting-Started/Starting-Pantheon.md | 6 ++++++ docs/Reference/Pantheon-CLI-Syntax.md | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/Getting-Started/Starting-Pantheon.md b/docs/Getting-Started/Starting-Pantheon.md index 4a89b8d1c8..a61e88385b 100644 --- a/docs/Getting-Started/Starting-Pantheon.md +++ b/docs/Getting-Started/Starting-Pantheon.md @@ -3,6 +3,12 @@ description: Starting Pantheon # Starting Pantheon +!!! important "Breaking Changes in v0.9" + In v0.9, the command line changed to improve usability. These are breaking changes; that is, + in many cases the v0.8 command line options no longer work. + The examples below and the rest of the documentation has been updated to reflect these changes. The [release notes](https://github.com/PegaSysEng/pantheon/blob/master/CHANGELOG.md) + include a mapping of the previous command line options to the new options. + Pantheon nodes can be used for varying purposes as described in the [Overview](../index.md). Nodes can connect to the Ethereum mainnet, public testnets such as Ropsten, or private networks. diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index b0a5ac2f53..9adf6a1b1f 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -4,10 +4,10 @@ description: Pantheon commande line interface reference # Pantheon Command Line !!! important "Breaking Changes in v0.9" - In v0.9 the command line changed to improve usability. These are breaking changes; that is, - in many cases the v0.8 command line options will no longer work. This reference and the rest of - the documentation will be updated to reflect these changes. Any further information required - about the changes are included in the v0.9 release notes. + In v0.9, the command line changed to improve usability. These are breaking changes; that is, + in many cases the v0.8 command line options no longer work. + This reference and the rest of the documentation has been updated to reflect these changes. The [release notes](https://github.com/PegaSysEng/pantheon/blob/master/CHANGELOG.md) + include a mapping of the previous command line options to the new options. This reference describes the syntax of the Pantheon Command Line Interface (CLI) options and subcommands. From ce7e0343ccc887cf50ed505e0f008b8195ace7e9 Mon Sep 17 00:00:00 2001 From: MadelineMurray <43356962+MadelineMurray@users.noreply.github.com> Date: Tue, 29 Jan 2019 06:26:12 +1000 Subject: [PATCH 15/31] Removed available from vX.X notes (#672) --- docs/Reference/JSON-RPC-API-Methods.md | 3 -- docs/Reference/Pantheon-CLI-Syntax.md | 39 +++++++++----------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/docs/Reference/JSON-RPC-API-Methods.md b/docs/Reference/JSON-RPC-API-Methods.md index 295e8c5569..461f691e86 100644 --- a/docs/Reference/JSON-RPC-API-Methods.md +++ b/docs/Reference/JSON-RPC-API-Methods.md @@ -283,9 +283,6 @@ None Returns the [chain ID](../Configuring-Pantheon/NetworkID-And-ChainID.md). -!!!note - This method is only available from v0.8.2. - **Parameters** None diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index 9adf6a1b1f..70cf4098a0 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -52,9 +52,6 @@ banned-nodeids=["0xc35c3...d615f","0xf42c13...fc456"] ``` List of node IDs with which this node will not peer. The node ID is the public key of the node. You can specify the banned node IDs with or without the `0x` prefix. - -!!!info - This option is only available from v0.8.2. !!!tip The singular `--banned-node-id` and plural `--banned-node-ids` are available and are just two @@ -126,6 +123,19 @@ The path to the Pantheon data directory. The default is the `/build/distribution !!!note This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#persisting-data). +### discovery-enabled + +```bash tab="Syntax" +--discovery-enabled=false +``` + +```bash tab="Example Configuration File" +discovery-enabled=false +``` + +Enables or disables P2P peer discovery. +The default is `true`. + ### genesis-file Genesis file is used to create a custom network. @@ -173,9 +183,6 @@ Comma-separated list of hostnames to allow access to the HTTP JSON-RPC API. Defa !!!tip To allow all hostnames, use `*` or `all`. We don't recommend allowing all hostnames for production code. -!!!note - This option is only available from v0.8.3. Earlier versions allow access by all hostnames. - ### max-peers ```bash tab="Syntax" @@ -388,19 +395,6 @@ P2P network identifier. This option can be used to override your current network ID. The default value is the current network chain ID which is defined in the genesis file. -### discovery-enabled - -```bash tab="Syntax" ---discovery-enabled=false -``` - -```bash tab="Example Configuration File" -discovery-enabled=false -``` - -Enables or disables P2P peer discovery. -The default is `true`. - ### node-private-key-file ```bash tab="Syntax" @@ -423,9 +417,6 @@ otherwise, the existing key file specifies the node private key. !!!attention The private key is not encrypted. - -!!!note - This option is only available from v0.8.2. ### nodes-whitelist @@ -444,10 +435,6 @@ nodes-whitelist=["enode://c35c3...d615f@3.14.15.92:30303","enode://f42c13...fc45 Comma-separated enode URLs for permissioned networks. Not intended for use with mainnet or public testnets. - -!!!note - This option is only available from v0.8.3. - !!!note Permissioning is under development and will be available in v1.0. From 894085413b1a5fae07a43ebdf7f16c6a6fdc51d6 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 29 Jan 2019 07:33:38 +1000 Subject: [PATCH 16/31] [NC-2137] Add world state and chain download into fast sync workflow (#658) --- .../eth/sync/DefaultSynchronizer.java | 7 +- .../eth/sync/fastsync/FastSyncActions.java | 5 + .../eth/sync/fastsync/FastSyncDownloader.java | 35 +++- .../sync/fastsync/WorldStateDownloader.java | 21 +++ .../sync/fastsync/FastSyncDownloaderTest.java | 152 +++++++++++++++++- 5 files changed, 203 insertions(+), 17 deletions(-) create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/WorldStateDownloader.java diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java index e79f7c71d5..8bfaaeda21 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java @@ -12,6 +12,8 @@ */ package tech.pegasys.pantheon.ethereum.eth.sync; +import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.FAST_SYNC_UNAVAILABLE; + import tech.pegasys.pantheon.ethereum.ProtocolContext; import tech.pegasys.pantheon.ethereum.core.SyncStatus; import tech.pegasys.pantheon.ethereum.core.Synchronizer; @@ -76,7 +78,10 @@ public DefaultSynchronizer( Optional.of( new FastSyncDownloader<>( new FastSyncActions<>( - syncConfig, protocolSchedule, protocolContext, ethContext, ethTasksTimer))); + syncConfig, protocolSchedule, protocolContext, ethContext, ethTasksTimer), + pivotBlockHeader -> { + throw new FastSyncException(FAST_SYNC_UNAVAILABLE); + })); } else { this.fastSyncDownloader = Optional.empty(); } diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncActions.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncActions.java index 5a895c4548..1926d25ad4 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncActions.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncActions.java @@ -13,6 +13,7 @@ package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.CHAIN_TOO_SHORT; +import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.FAST_SYNC_UNAVAILABLE; import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.NO_PEERS_AVAILABLE; import tech.pegasys.pantheon.ethereum.ProtocolContext; @@ -143,4 +144,8 @@ public CompletableFuture downloadPivotBlockHeader( currentState.getPivotBlockNumber().getAsLong()) .downloadPivotBlockHeader(); } + + public CompletableFuture downloadChain(final FastSyncState currentState) { + throw new FastSyncException(FAST_SYNC_UNAVAILABLE); + } } diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloader.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloader.java index 7d59f7d84f..1465183271 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloader.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloader.java @@ -12,8 +12,6 @@ */ package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; -import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.FAST_SYNC_UNAVAILABLE; - import java.util.concurrent.CompletableFuture; import org.apache.logging.log4j.LogManager; @@ -22,9 +20,12 @@ public class FastSyncDownloader { private static final Logger LOG = LogManager.getLogger(); private final FastSyncActions fastSyncActions; + private final WorldStateDownloader worldStateDownloader; - public FastSyncDownloader(final FastSyncActions fastSyncActions) { + public FastSyncDownloader( + final FastSyncActions fastSyncActions, final WorldStateDownloader worldStateDownloader) { this.fastSyncActions = fastSyncActions; + this.worldStateDownloader = worldStateDownloader; } public CompletableFuture start() { @@ -33,10 +34,28 @@ public CompletableFuture start() { .waitForSuitablePeers() .thenApply(state -> fastSyncActions.selectPivotBlock()) .thenCompose(fastSyncActions::downloadPivotBlockHeader) - .thenCompose( - state -> { - LOG.info("Reached end of current fast sync implementation with state {}", state); - throw new FastSyncException(FAST_SYNC_UNAVAILABLE); - }); + .thenCompose(this::downloadChainAndWorldState); + } + + private CompletableFuture downloadChainAndWorldState( + final FastSyncState currentState) { + final CompletableFuture worldStateFuture = + worldStateDownloader.downloadWorldState(currentState.getPivotBlockHeader().get()); + final CompletableFuture chainFuture = fastSyncActions.downloadChain(currentState); + + // If either download fails, cancel the other one. + chainFuture.exceptionally( + error -> { + worldStateFuture.cancel(true); + return null; + }); + worldStateFuture.exceptionally( + error -> { + chainFuture.cancel(true); + return null; + }); + + return CompletableFuture.allOf(worldStateFuture, chainFuture) + .thenApply(complete -> currentState); } } diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/WorldStateDownloader.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/WorldStateDownloader.java new file mode 100644 index 0000000000..17d6f98014 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/WorldStateDownloader.java @@ -0,0 +1,21 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; + +import tech.pegasys.pantheon.ethereum.core.BlockHeader; + +import java.util.concurrent.CompletableFuture; + +public interface WorldStateDownloader { + CompletableFuture downloadWorldState(BlockHeader pivotBlockHeader); +} diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java index b879aa48f9..a256f7323f 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java @@ -19,9 +19,10 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.CHAIN_TOO_SHORT; -import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.FAST_SYNC_UNAVAILABLE; +import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.NO_PEERS_AVAILABLE; import static tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncError.UNEXPECTED_ERROR; +import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture; import java.util.Optional; @@ -32,30 +33,39 @@ public class FastSyncDownloaderTest { + private static final CompletableFuture COMPLETE = completedFuture(null); + @SuppressWarnings("unchecked") private final FastSyncActions fastSyncActions = mock(FastSyncActions.class); - private final FastSyncDownloader downloader = new FastSyncDownloader<>(fastSyncActions); + private final WorldStateDownloader worldStateDownloader = mock(WorldStateDownloader.class); + + private final FastSyncDownloader downloader = + new FastSyncDownloader<>(fastSyncActions, worldStateDownloader); @Test public void shouldCompleteFastSyncSuccessfully() { final FastSyncState selectPivotBlockState = new FastSyncState(OptionalLong.of(50)); + final BlockHeader pivotBlockHeader = new BlockHeaderTestFixture().number(50).buildHeader(); final FastSyncState downloadPivotBlockHeaderState = - new FastSyncState( - OptionalLong.of(50), - Optional.of(new BlockHeaderTestFixture().number(50).buildHeader())); - when(fastSyncActions.waitForSuitablePeers()).thenReturn(completedFuture(null)); + new FastSyncState(OptionalLong.of(50), Optional.of(pivotBlockHeader)); + when(fastSyncActions.waitForSuitablePeers()).thenReturn(COMPLETE); when(fastSyncActions.selectPivotBlock()).thenReturn(selectPivotBlockState); when(fastSyncActions.downloadPivotBlockHeader(selectPivotBlockState)) .thenReturn(completedFuture(downloadPivotBlockHeaderState)); + when(fastSyncActions.downloadChain(downloadPivotBlockHeaderState)).thenReturn(COMPLETE); + when(worldStateDownloader.downloadWorldState(pivotBlockHeader)).thenReturn(COMPLETE); final CompletableFuture result = downloader.start(); verify(fastSyncActions).waitForSuitablePeers(); verify(fastSyncActions).selectPivotBlock(); verify(fastSyncActions).downloadPivotBlockHeader(selectPivotBlockState); + verify(fastSyncActions).downloadChain(downloadPivotBlockHeaderState); + verify(worldStateDownloader).downloadWorldState(pivotBlockHeader); verifyNoMoreInteractions(fastSyncActions); - assertCompletedExceptionally(result, FAST_SYNC_UNAVAILABLE); + verifyNoMoreInteractions(worldStateDownloader); + assertThat(result).isCompletedWithValue(downloadPivotBlockHeaderState); } @Test @@ -73,7 +83,7 @@ public void shouldAbortIfWaitForSuitablePeersFails() { @Test public void shouldAbortIfSelectPivotBlockFails() { - when(fastSyncActions.waitForSuitablePeers()).thenReturn(completedFuture(null)); + when(fastSyncActions.waitForSuitablePeers()).thenReturn(COMPLETE); when(fastSyncActions.selectPivotBlock()).thenThrow(new FastSyncException(CHAIN_TOO_SHORT)); final CompletableFuture result = downloader.start(); @@ -85,6 +95,132 @@ public void shouldAbortIfSelectPivotBlockFails() { verifyNoMoreInteractions(fastSyncActions); } + @Test + public void shouldAbortIfWorldStateDownloadFails() { + final CompletableFuture worldStateFuture = new CompletableFuture<>(); + final CompletableFuture chainFuture = new CompletableFuture<>(); + final FastSyncState selectPivotBlockState = new FastSyncState(OptionalLong.of(50)); + final BlockHeader pivotBlockHeader = new BlockHeaderTestFixture().number(50).buildHeader(); + final FastSyncState downloadPivotBlockHeaderState = + new FastSyncState(OptionalLong.of(50), Optional.of(pivotBlockHeader)); + when(fastSyncActions.waitForSuitablePeers()).thenReturn(COMPLETE); + when(fastSyncActions.selectPivotBlock()).thenReturn(selectPivotBlockState); + when(fastSyncActions.downloadPivotBlockHeader(selectPivotBlockState)) + .thenReturn(completedFuture(downloadPivotBlockHeaderState)); + when(fastSyncActions.downloadChain(downloadPivotBlockHeaderState)).thenReturn(chainFuture); + when(worldStateDownloader.downloadWorldState(pivotBlockHeader)).thenReturn(worldStateFuture); + + final CompletableFuture result = downloader.start(); + + verify(fastSyncActions).waitForSuitablePeers(); + verify(fastSyncActions).selectPivotBlock(); + verify(fastSyncActions).downloadPivotBlockHeader(selectPivotBlockState); + verify(fastSyncActions).downloadChain(downloadPivotBlockHeaderState); + verify(worldStateDownloader).downloadWorldState(pivotBlockHeader); + verifyNoMoreInteractions(fastSyncActions); + verifyNoMoreInteractions(worldStateDownloader); + + assertThat(result).isNotDone(); + + worldStateFuture.completeExceptionally(new FastSyncException(NO_PEERS_AVAILABLE)); + assertCompletedExceptionally(result, NO_PEERS_AVAILABLE); + assertThat(chainFuture).isCancelled(); + } + + @Test + public void shouldAbortIfChainDownloadFails() { + final CompletableFuture chainFuture = new CompletableFuture<>(); + final CompletableFuture worldStateFuture = new CompletableFuture<>(); + final FastSyncState selectPivotBlockState = new FastSyncState(OptionalLong.of(50)); + final BlockHeader pivotBlockHeader = new BlockHeaderTestFixture().number(50).buildHeader(); + final FastSyncState downloadPivotBlockHeaderState = + new FastSyncState(OptionalLong.of(50), Optional.of(pivotBlockHeader)); + when(fastSyncActions.waitForSuitablePeers()).thenReturn(COMPLETE); + when(fastSyncActions.selectPivotBlock()).thenReturn(selectPivotBlockState); + when(fastSyncActions.downloadPivotBlockHeader(selectPivotBlockState)) + .thenReturn(completedFuture(downloadPivotBlockHeaderState)); + when(fastSyncActions.downloadChain(downloadPivotBlockHeaderState)).thenReturn(chainFuture); + when(worldStateDownloader.downloadWorldState(pivotBlockHeader)).thenReturn(worldStateFuture); + + final CompletableFuture result = downloader.start(); + + verify(fastSyncActions).waitForSuitablePeers(); + verify(fastSyncActions).selectPivotBlock(); + verify(fastSyncActions).downloadPivotBlockHeader(selectPivotBlockState); + verify(fastSyncActions).downloadChain(downloadPivotBlockHeaderState); + verify(worldStateDownloader).downloadWorldState(pivotBlockHeader); + verifyNoMoreInteractions(fastSyncActions); + verifyNoMoreInteractions(worldStateDownloader); + + assertThat(result).isNotDone(); + + chainFuture.completeExceptionally(new FastSyncException(NO_PEERS_AVAILABLE)); + assertCompletedExceptionally(result, NO_PEERS_AVAILABLE); + assertThat(worldStateFuture).isCancelled(); + } + + @Test + public void shouldNotConsiderFastSyncCompleteIfOnlyWorldStateDownloadIsComplete() { + final CompletableFuture chainFuture = new CompletableFuture<>(); + final CompletableFuture worldStateFuture = new CompletableFuture<>(); + final FastSyncState selectPivotBlockState = new FastSyncState(OptionalLong.of(50)); + final BlockHeader pivotBlockHeader = new BlockHeaderTestFixture().number(50).buildHeader(); + final FastSyncState downloadPivotBlockHeaderState = + new FastSyncState(OptionalLong.of(50), Optional.of(pivotBlockHeader)); + when(fastSyncActions.waitForSuitablePeers()).thenReturn(COMPLETE); + when(fastSyncActions.selectPivotBlock()).thenReturn(selectPivotBlockState); + when(fastSyncActions.downloadPivotBlockHeader(selectPivotBlockState)) + .thenReturn(completedFuture(downloadPivotBlockHeaderState)); + when(fastSyncActions.downloadChain(downloadPivotBlockHeaderState)).thenReturn(chainFuture); + when(worldStateDownloader.downloadWorldState(pivotBlockHeader)).thenReturn(worldStateFuture); + + final CompletableFuture result = downloader.start(); + + verify(fastSyncActions).waitForSuitablePeers(); + verify(fastSyncActions).selectPivotBlock(); + verify(fastSyncActions).downloadPivotBlockHeader(selectPivotBlockState); + verify(fastSyncActions).downloadChain(downloadPivotBlockHeaderState); + verify(worldStateDownloader).downloadWorldState(pivotBlockHeader); + verifyNoMoreInteractions(fastSyncActions); + verifyNoMoreInteractions(worldStateDownloader); + + assertThat(result).isNotDone(); + + worldStateFuture.complete(null); + assertThat(result).isNotDone(); + } + + @Test + public void shouldNotConsiderFastSyncCompleteIfOnlyChainDownloadIsComplete() { + final CompletableFuture chainFuture = new CompletableFuture<>(); + final CompletableFuture worldStateFuture = new CompletableFuture<>(); + final FastSyncState selectPivotBlockState = new FastSyncState(OptionalLong.of(50)); + final BlockHeader pivotBlockHeader = new BlockHeaderTestFixture().number(50).buildHeader(); + final FastSyncState downloadPivotBlockHeaderState = + new FastSyncState(OptionalLong.of(50), Optional.of(pivotBlockHeader)); + when(fastSyncActions.waitForSuitablePeers()).thenReturn(COMPLETE); + when(fastSyncActions.selectPivotBlock()).thenReturn(selectPivotBlockState); + when(fastSyncActions.downloadPivotBlockHeader(selectPivotBlockState)) + .thenReturn(completedFuture(downloadPivotBlockHeaderState)); + when(fastSyncActions.downloadChain(downloadPivotBlockHeaderState)).thenReturn(chainFuture); + when(worldStateDownloader.downloadWorldState(pivotBlockHeader)).thenReturn(worldStateFuture); + + final CompletableFuture result = downloader.start(); + + verify(fastSyncActions).waitForSuitablePeers(); + verify(fastSyncActions).selectPivotBlock(); + verify(fastSyncActions).downloadPivotBlockHeader(selectPivotBlockState); + verify(fastSyncActions).downloadChain(downloadPivotBlockHeaderState); + verify(worldStateDownloader).downloadWorldState(pivotBlockHeader); + verifyNoMoreInteractions(fastSyncActions); + verifyNoMoreInteractions(worldStateDownloader); + + assertThat(result).isNotDone(); + + chainFuture.complete(null); + assertThat(result).isNotDone(); + } + private CompletableFuture completedExceptionally(final Throwable error) { final CompletableFuture result = new CompletableFuture<>(); result.completeExceptionally(error); From 05a449f5f0bb350a8d2e2fb9a1c600a214c15f72 Mon Sep 17 00:00:00 2001 From: Chris Mckay Date: Tue, 29 Jan 2019 09:15:52 +1000 Subject: [PATCH 17/31] Updated Quickstart docs link (#681) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 91396ca125..e760811f50 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Instructions for how to get started with developing on the Pantheon codebase. Pl [User Documentation]: https://docs.pantheon.pegasys.tech/en/latest/ [Installation]: https://docs.pantheon.pegasys.tech/en/latest/Installation/Overview/ [Getting Started]: https://docs.pantheon.pegasys.tech/en/latest/Getting-Started/Getting-Started/ -[Private Network Quickstart]: https://docs.pantheon.pegasys.tech/en/latest/Getting-Started/Private-Network-Quickstart/ +[Private Network Quickstart]: https://docs.pantheon.pegasys.tech/en/latest/Tutorials/Private-Network-Quickstart/ [Command Line Options]: https://docs.pantheon.pegasys.tech/en/latest/Reference/Pantheon-CLI-Syntax/ [JSON-RPC API]: https://docs.pantheon.pegasys.tech/en/latest/Reference/Using-JSON-RPC-API/ [Configuring Pantheon]: https://docs.pantheon.pegasys.tech/en/latest/Configuring-Pantheon/NetworkID-And-ChainID/ From 1242930db7ac93d825fb6a63006f2ba8306416f6 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 29 Jan 2019 09:42:02 +1000 Subject: [PATCH 18/31] [NC-2138] Extract out generic parts of Downloader (#659) Separate the management of sync target and actual import from the rest of the Downloader logic in preparation for introducing a fast sync chain downloader. --- ...ncDownloader.java => ChainDownloader.java} | 182 ++++-------------- .../eth/sync/DefaultSynchronizer.java | 1 + .../ethereum/eth/sync/SyncTargetManager.java | 123 ++++++++++++ .../eth/sync/fullsync/FullSyncDownloader.java | 105 ++++++++++ .../sync/fullsync/FullSyncTargetManager.java | 107 ++++++++++ .../ethereum/eth/sync/state/SyncTarget.java | 2 +- .../tasks/GetHeadersFromPeerByHashTask.java | 19 ++ .../FullSyncDownloaderTest.java | 19 +- 8 files changed, 404 insertions(+), 154 deletions(-) rename ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/{FullSyncDownloader.java => ChainDownloader.java} (63%) create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/SyncTargetManager.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncDownloader.java create mode 100644 ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncTargetManager.java rename ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/{ => fullsync}/FullSyncDownloaderTest.java (97%) diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/FullSyncDownloader.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/ChainDownloader.java similarity index 63% rename from ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/FullSyncDownloader.java rename to ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/ChainDownloader.java index ac728d3b7e..4770d56d10 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/FullSyncDownloader.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/ChainDownloader.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 ConsenSys AG. + * 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 @@ -17,18 +17,12 @@ import tech.pegasys.pantheon.ethereum.core.Block; import tech.pegasys.pantheon.ethereum.core.BlockHeader; import tech.pegasys.pantheon.ethereum.eth.manager.AbstractPeerTask.PeerTaskResult; -import tech.pegasys.pantheon.ethereum.eth.manager.ChainState; import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; -import tech.pegasys.pantheon.ethereum.eth.manager.EthPeers; import tech.pegasys.pantheon.ethereum.eth.manager.EthTask; import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState; import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncTarget; -import tech.pegasys.pantheon.ethereum.eth.sync.tasks.DetermineCommonAncestorTask; import tech.pegasys.pantheon.ethereum.eth.sync.tasks.GetHeadersFromPeerByHashTask; -import tech.pegasys.pantheon.ethereum.eth.sync.tasks.ImportBlocksTask; -import tech.pegasys.pantheon.ethereum.eth.sync.tasks.PipelinedImportChainSegmentTask; -import tech.pegasys.pantheon.ethereum.eth.sync.tasks.WaitForPeerTask; import tech.pegasys.pantheon.ethereum.eth.sync.tasks.WaitForPeersTask; import tech.pegasys.pantheon.ethereum.eth.sync.tasks.exceptions.InvalidBlockException; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; @@ -36,7 +30,6 @@ import tech.pegasys.pantheon.metrics.LabelledMetric; import tech.pegasys.pantheon.metrics.OperationTimer; import tech.pegasys.pantheon.util.ExceptionUtils; -import tech.pegasys.pantheon.util.uint.UInt256; import java.time.Duration; import java.util.ArrayList; @@ -50,43 +43,47 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; -import com.google.common.collect.Lists; +import com.google.common.annotations.VisibleForTesting; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class FullSyncDownloader { +public class ChainDownloader { private static final Logger LOG = LogManager.getLogger(); private final SynchronizerConfiguration config; - private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; private final EthContext ethContext; private final SyncState syncState; + private final SyncTargetManager syncTargetManager; + private final BlockImportTaskFactory blockImportTaskFactory; + private final ProtocolSchedule protocolSchedule; private final LabelledMetric ethTasksTimer; private final Deque checkpointHeaders = new ConcurrentLinkedDeque<>(); private int checkpointTimeouts = 0; private int chainSegmentTimeouts = 0; - private volatile boolean syncTargetDisconnected = false; private final AtomicBoolean started = new AtomicBoolean(false); - private long syncTargetDisconnectListenerId; - protected CompletableFuture currentTask; + private CompletableFuture currentTask; - FullSyncDownloader( + public ChainDownloader( final SynchronizerConfiguration config, final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthContext ethContext, final SyncState syncState, - final LabelledMetric ethTasksTimer) { + final LabelledMetric ethTasksTimer, + final SyncTargetManager syncTargetManager, + final BlockImportTaskFactory blockImportTaskFactory) { + this.protocolSchedule = protocolSchedule; this.ethTasksTimer = ethTasksTimer; this.config = config; - this.protocolSchedule = protocolSchedule; this.protocolContext = protocolContext; this.ethContext = ethContext; this.syncState = syncState; + this.syncTargetManager = syncTargetManager; + this.blockImportTaskFactory = blockImportTaskFactory; } public void start() { @@ -98,11 +95,16 @@ public void start() { } } + @VisibleForTesting + public CompletableFuture getCurrentTask() { + return currentTask; + } + private CompletableFuture executeDownload() { // Find target, pull checkpoint headers, import, repeat currentTask = waitForPeers() - .thenCompose(r -> findSyncTarget()) + .thenCompose(r -> syncTargetManager.findSyncTarget()) .thenCompose(this::pullCheckpointHeaders) .thenCompose(r -> importBlocks()) .thenCompose(r -> checkSyncTarget()) @@ -132,75 +134,15 @@ private CompletableFuture waitForPeers() { return WaitForPeersTask.create(ethContext, 1, ethTasksTimer).run(); } - private CompletableFuture waitForNewPeer() { - return ethContext - .getScheduler() - .timeout(WaitForPeerTask.create(ethContext, ethTasksTimer), Duration.ofSeconds(5)); - } - - private CompletableFuture findSyncTarget() { - final Optional maybeSyncTarget = syncState.syncTarget(); - if (maybeSyncTarget.isPresent()) { - // Nothing to do - return CompletableFuture.completedFuture(maybeSyncTarget.get()); - } - - final Optional maybeBestPeer = ethContext.getEthPeers().bestPeer(); - if (!maybeBestPeer.isPresent()) { - LOG.info("No sync target, wait for peers."); - return waitForPeerAndThenSetSyncTarget(); - } else { - final EthPeer bestPeer = maybeBestPeer.get(); - final long peerHeight = bestPeer.chainState().getEstimatedHeight(); - final UInt256 peerTd = bestPeer.chainState().getBestBlock().getTotalDifficulty(); - if (peerTd.compareTo(syncState.chainHeadTotalDifficulty()) <= 0 - && peerHeight <= syncState.chainHeadNumber()) { - // We're caught up to our best peer, try again when a new peer connects - LOG.debug("Caught up to best peer: " + bestPeer.chainState().getEstimatedHeight()); - return waitForPeerAndThenSetSyncTarget(); - } - return DetermineCommonAncestorTask.create( - protocolSchedule, - protocolContext, - ethContext, - bestPeer, - config.downloaderHeaderRequestSize(), - ethTasksTimer) - .run() - .handle((r, t) -> r) - .thenCompose( - (target) -> { - if (target == null) { - return waitForPeerAndThenSetSyncTarget(); - } - final SyncTarget syncTarget = syncState.setSyncTarget(bestPeer, target); - LOG.info( - "Found common ancestor with peer {} at block {}", bestPeer, target.getNumber()); - syncTargetDisconnectListenerId = - bestPeer.subscribeDisconnect(this::onSyncTargetPeerDisconnect); - return CompletableFuture.completedFuture(syncTarget); - }); - } - } - - private CompletableFuture waitForPeerAndThenSetSyncTarget() { - return waitForNewPeer().handle((r, t) -> r).thenCompose((r) -> findSyncTarget()); - } - - private void onSyncTargetPeerDisconnect(final EthPeer ethPeer) { - LOG.info("Sync target disconnected: {}", ethPeer); - syncTargetDisconnected = true; - } - private CompletableFuture checkSyncTarget() { final Optional maybeSyncTarget = syncState.syncTarget(); if (!maybeSyncTarget.isPresent()) { - // Nothing to do + // No sync target, so nothing to check. return CompletableFuture.completedFuture(null); } final SyncTarget syncTarget = maybeSyncTarget.get(); - if (shouldSwitchSyncTarget(syncTarget)) { + if (syncTargetManager.shouldSwitchSyncTarget(syncTarget)) { LOG.info("Better sync target found, clear current sync target: {}.", syncTarget); clearSyncTarget(syncTarget); return CompletableFuture.completedFuture(null); @@ -218,41 +160,10 @@ private CompletableFuture checkSyncTarget() { return CompletableFuture.completedFuture(null); } - private boolean shouldSwitchSyncTarget(final SyncTarget currentTarget) { - final EthPeer currentPeer = currentTarget.peer(); - final ChainState currentPeerChainState = currentPeer.chainState(); - final Optional maybeBestPeer = ethContext.getEthPeers().bestPeer(); - - return maybeBestPeer - .map( - bestPeer -> { - if (EthPeers.BEST_CHAIN.compare(bestPeer, currentPeer) <= 0) { - // Our current target is better or equal to the best peer - return false; - } - // Require some threshold to be exceeded before switching targets to keep some - // stability - // when multiple peers are in range of each other - final ChainState bestPeerChainState = bestPeer.chainState(); - final long heightDifference = - bestPeerChainState.getEstimatedHeight() - - currentPeerChainState.getEstimatedHeight(); - if (heightDifference == 0 && bestPeerChainState.getEstimatedHeight() == 0) { - // Only check td if we don't have a height metric - final UInt256 tdDifference = - bestPeerChainState - .getBestBlock() - .getTotalDifficulty() - .minus(currentPeerChainState.getBestBlock().getTotalDifficulty()); - return tdDifference.compareTo(config.downloaderChangeTargetThresholdByTd()) > 0; - } - return heightDifference > config.downloaderChangeTargetThresholdByHeight(); - }) - .orElse(false); - } - private boolean finishedSyncingToCurrentTarget() { - return syncTargetDisconnected || checkpointsHaveTimedOut() || chainSegmentsHaveTimedOut(); + return syncTargetManager.isSyncTargetDisconnected() + || checkpointsHaveTimedOut() + || chainSegmentsHaveTimedOut(); } private boolean checkpointsHaveTimedOut() { @@ -274,13 +185,12 @@ private void clearSyncTarget(final SyncTarget syncTarget) { chainSegmentTimeouts = 0; checkpointTimeouts = 0; checkpointHeaders.clear(); - syncTarget.peer().unsubscribeDisconnect(syncTargetDisconnectListenerId); - syncTargetDisconnected = false; + syncTargetManager.clearSyncTarget(syncTarget); syncState.clearSyncTarget(); } private boolean shouldDownloadMoreCheckpoints() { - return !syncTargetDisconnected + return !syncTargetManager.isSyncTargetDisconnected() && checkpointHeaders.size() < config.downloaderHeaderRequestSize() && checkpointTimeouts < config.downloaderCheckpointTimeoutsPermitted(); } @@ -290,8 +200,10 @@ private CompletableFuture pullCheckpointHeaders(final SyncTarget syncTarget) return CompletableFuture.completedFuture(null); } + final BlockHeader lastHeader = + checkpointHeaders.size() > 0 ? checkpointHeaders.getLast() : syncTarget.commonAncestor(); // Try to pull more checkpoint headers - return checkpointHeadersTask(syncTarget) + return checkpointHeadersTask(lastHeader, syncTarget) .run() .handle( (r, t) -> { @@ -321,9 +233,7 @@ private CompletableFuture pullCheckpointHeaders(final SyncTarget syncTarget) } private EthTask>> checkpointHeadersTask( - final SyncTarget syncTarget) { - final BlockHeader lastHeader = - checkpointHeaders.size() > 0 ? checkpointHeaders.getLast() : syncTarget.commonAncestor(); + final BlockHeader lastHeader, final SyncTarget syncTarget) { LOG.debug("Requesting checkpoint headers from {}", lastHeader.getNumber()); return GetHeadersFromPeerByHashTask.startingAtHash( protocolSchedule, @@ -342,29 +252,8 @@ private CompletableFuture> importBlocks() { return CompletableFuture.completedFuture(Collections.emptyList()); } - final CompletableFuture> importedBlocks; - if (checkpointHeaders.size() < 2) { - // Download blocks without constraining the end block - final ImportBlocksTask importTask = - ImportBlocksTask.fromHeader( - protocolSchedule, - protocolContext, - ethContext, - checkpointHeaders.getFirst(), - config.downloaderChainSegmentSize(), - ethTasksTimer); - importedBlocks = importTask.run().thenApply(PeerTaskResult::getResult); - } else { - final PipelinedImportChainSegmentTask importTask = - PipelinedImportChainSegmentTask.forCheckpoints( - protocolSchedule, - protocolContext, - ethContext, - config.downloaderParallelism(), - ethTasksTimer, - Lists.newArrayList(checkpointHeaders)); - importedBlocks = importTask.run(); - } + final CompletableFuture> importedBlocks = + blockImportTaskFactory.importBlocksForCheckpoints(checkpointHeaders); return importedBlocks.whenComplete( (r, t) -> { @@ -418,4 +307,9 @@ private boolean clearImportedCheckpointHeaders() { syncState.setCommonAncestor(lastImportedCheckpointHeader); return imported.size() > 1; } + + public interface BlockImportTaskFactory { + CompletableFuture> importBlocksForCheckpoints( + final Deque checkpointHeaders); + } } diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java index 8bfaaeda21..0bcfcafbb9 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/DefaultSynchronizer.java @@ -22,6 +22,7 @@ import tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncDownloader; import tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncException; import tech.pegasys.pantheon.ethereum.eth.sync.fastsync.FastSyncState; +import tech.pegasys.pantheon.ethereum.eth.sync.fullsync.FullSyncDownloader; import tech.pegasys.pantheon.ethereum.eth.sync.state.PendingBlocks; import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/SyncTargetManager.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/SyncTargetManager.java new file mode 100644 index 0000000000..87fe4bb4d6 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/SyncTargetManager.java @@ -0,0 +1,123 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync; + +import tech.pegasys.pantheon.ethereum.ProtocolContext; +import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; +import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState; +import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncTarget; +import tech.pegasys.pantheon.ethereum.eth.sync.tasks.DetermineCommonAncestorTask; +import tech.pegasys.pantheon.ethereum.eth.sync.tasks.WaitForPeerTask; +import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; +import tech.pegasys.pantheon.metrics.LabelledMetric; +import tech.pegasys.pantheon.metrics.OperationTimer; + +import java.time.Duration; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public abstract class SyncTargetManager { + + private static final Logger LOG = LogManager.getLogger(); + private volatile long syncTargetDisconnectListenerId; + private volatile boolean syncTargetDisconnected = false; + private final SynchronizerConfiguration config; + private final ProtocolSchedule protocolSchedule; + private final ProtocolContext protocolContext; + private final EthContext ethContext; + private final SyncState syncState; + private final LabelledMetric ethTasksTimer; + + public SyncTargetManager( + final SynchronizerConfiguration config, + final ProtocolSchedule protocolSchedule, + final ProtocolContext protocolContext, + final EthContext ethContext, + final SyncState syncState, + final LabelledMetric ethTasksTimer) { + this.config = config; + this.protocolSchedule = protocolSchedule; + this.protocolContext = protocolContext; + this.ethContext = ethContext; + this.syncState = syncState; + this.ethTasksTimer = ethTasksTimer; + } + + public CompletableFuture findSyncTarget() { + final Optional maybeSyncTarget = syncState.syncTarget(); + if (maybeSyncTarget.isPresent()) { + // Nothing to do + return CompletableFuture.completedFuture(maybeSyncTarget.get()); + } + + final Optional maybeBestPeer = selectBestAvailableSyncTarget(); + if (maybeBestPeer.isPresent()) { + final EthPeer bestPeer = maybeBestPeer.get(); + return DetermineCommonAncestorTask.create( + protocolSchedule, + protocolContext, + ethContext, + bestPeer, + config.downloaderHeaderRequestSize(), + ethTasksTimer) + .run() + .handle((r, t) -> r) + .thenCompose( + (target) -> { + if (target == null) { + return waitForPeerAndThenSetSyncTarget(); + } + final SyncTarget syncTarget = syncState.setSyncTarget(bestPeer, target); + LOG.info( + "Found common ancestor with peer {} at block {}", bestPeer, target.getNumber()); + syncTargetDisconnectListenerId = + bestPeer.subscribeDisconnect(this::onSyncTargetPeerDisconnect); + return CompletableFuture.completedFuture(syncTarget); + }); + } else { + return waitForPeerAndThenSetSyncTarget(); + } + } + + protected abstract Optional selectBestAvailableSyncTarget(); + + private CompletableFuture waitForPeerAndThenSetSyncTarget() { + return waitForNewPeer().handle((r, t) -> r).thenCompose((r) -> findSyncTarget()); + } + + private CompletableFuture waitForNewPeer() { + return ethContext + .getScheduler() + .timeout(WaitForPeerTask.create(ethContext, ethTasksTimer), Duration.ofSeconds(5)); + } + + private void onSyncTargetPeerDisconnect(final EthPeer ethPeer) { + LOG.info("Sync target disconnected: {}", ethPeer); + syncTargetDisconnected = true; + } + + public boolean isSyncTargetDisconnected() { + return syncTargetDisconnected; + } + + public void clearSyncTarget(final SyncTarget syncTarget) { + syncTarget.peer().unsubscribeDisconnect(syncTargetDisconnectListenerId); + syncTargetDisconnected = false; + } + + public abstract boolean shouldSwitchSyncTarget(final SyncTarget currentTarget); +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncDownloader.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncDownloader.java new file mode 100644 index 0000000000..6643dada98 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncDownloader.java @@ -0,0 +1,105 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.fullsync; + +import tech.pegasys.pantheon.ethereum.ProtocolContext; +import tech.pegasys.pantheon.ethereum.core.Block; +import tech.pegasys.pantheon.ethereum.core.BlockHeader; +import tech.pegasys.pantheon.ethereum.eth.manager.AbstractPeerTask.PeerTaskResult; +import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; +import tech.pegasys.pantheon.ethereum.eth.sync.ChainDownloader; +import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; +import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState; +import tech.pegasys.pantheon.ethereum.eth.sync.tasks.ImportBlocksTask; +import tech.pegasys.pantheon.ethereum.eth.sync.tasks.PipelinedImportChainSegmentTask; +import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; +import tech.pegasys.pantheon.metrics.LabelledMetric; +import tech.pegasys.pantheon.metrics.OperationTimer; + +import java.util.Deque; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; + +public class FullSyncDownloader { + private final ChainDownloader chainDownloader; + private final SynchronizerConfiguration config; + private final ProtocolSchedule protocolSchedule; + private final ProtocolContext protocolContext; + private final EthContext ethContext; + private final LabelledMetric ethTasksTimer; + + public FullSyncDownloader( + final SynchronizerConfiguration config, + final ProtocolSchedule protocolSchedule, + final ProtocolContext protocolContext, + final EthContext ethContext, + final SyncState syncState, + final LabelledMetric ethTasksTimer) { + this.config = config; + this.protocolSchedule = protocolSchedule; + this.protocolContext = protocolContext; + this.ethContext = ethContext; + this.ethTasksTimer = ethTasksTimer; + chainDownloader = + new ChainDownloader<>( + config, + protocolSchedule, + protocolContext, + ethContext, + syncState, + ethTasksTimer, + new FullSyncTargetManager<>( + config, protocolSchedule, protocolContext, ethContext, syncState, ethTasksTimer), + this::importBlocksForCheckpoints); + } + + public void start() { + chainDownloader.start(); + } + + @VisibleForTesting + CompletableFuture getCurrentTask() { + return chainDownloader.getCurrentTask(); + } + + private CompletableFuture> importBlocksForCheckpoints( + final Deque checkpointHeaders) { + final CompletableFuture> importedBlocks; + if (checkpointHeaders.size() < 2) { + // Download blocks without constraining the end block + final ImportBlocksTask importTask = + ImportBlocksTask.fromHeader( + protocolSchedule, + protocolContext, + ethContext, + checkpointHeaders.getFirst(), + config.downloaderChainSegmentSize(), + ethTasksTimer); + importedBlocks = importTask.run().thenApply(PeerTaskResult::getResult); + } else { + final PipelinedImportChainSegmentTask importTask = + PipelinedImportChainSegmentTask.forCheckpoints( + protocolSchedule, + protocolContext, + ethContext, + config.downloaderParallelism(), + ethTasksTimer, + Lists.newArrayList(checkpointHeaders)); + importedBlocks = importTask.run(); + } + return importedBlocks; + } +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncTargetManager.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncTargetManager.java new file mode 100644 index 0000000000..2daa8d5172 --- /dev/null +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncTargetManager.java @@ -0,0 +1,107 @@ +/* + * Copyright 2019 ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package tech.pegasys.pantheon.ethereum.eth.sync.fullsync; + +import tech.pegasys.pantheon.ethereum.ProtocolContext; +import tech.pegasys.pantheon.ethereum.eth.manager.ChainState; +import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeer; +import tech.pegasys.pantheon.ethereum.eth.manager.EthPeers; +import tech.pegasys.pantheon.ethereum.eth.sync.SyncTargetManager; +import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; +import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState; +import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncTarget; +import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; +import tech.pegasys.pantheon.metrics.LabelledMetric; +import tech.pegasys.pantheon.metrics.OperationTimer; +import tech.pegasys.pantheon.util.uint.UInt256; + +import java.util.Optional; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +class FullSyncTargetManager extends SyncTargetManager { + + private static final Logger LOG = LogManager.getLogger(); + private final SynchronizerConfiguration config; + private final EthContext ethContext; + private final SyncState syncState; + + FullSyncTargetManager( + final SynchronizerConfiguration config, + final ProtocolSchedule protocolSchedule, + final ProtocolContext protocolContext, + final EthContext ethContext, + final SyncState syncState, + final LabelledMetric ethTasksTimer) { + super(config, protocolSchedule, protocolContext, ethContext, syncState, ethTasksTimer); + this.config = config; + this.ethContext = ethContext; + this.syncState = syncState; + } + + @Override + protected Optional selectBestAvailableSyncTarget() { + final Optional maybeBestPeer = ethContext.getEthPeers().bestPeer(); + if (!maybeBestPeer.isPresent()) { + LOG.info("No sync target, wait for peers."); + return Optional.empty(); + } else { + final EthPeer bestPeer = maybeBestPeer.get(); + final long peerHeight = bestPeer.chainState().getEstimatedHeight(); + final UInt256 peerTd = bestPeer.chainState().getBestBlock().getTotalDifficulty(); + if (peerTd.compareTo(syncState.chainHeadTotalDifficulty()) <= 0 + && peerHeight <= syncState.chainHeadNumber()) { + // We're caught up to our best peer, try again when a new peer connects + LOG.debug("Caught up to best peer: " + bestPeer.chainState().getEstimatedHeight()); + return Optional.empty(); + } + return maybeBestPeer; + } + } + + @Override + public boolean shouldSwitchSyncTarget(final SyncTarget currentTarget) { + final EthPeer currentPeer = currentTarget.peer(); + final ChainState currentPeerChainState = currentPeer.chainState(); + final Optional maybeBestPeer = ethContext.getEthPeers().bestPeer(); + + return maybeBestPeer + .map( + bestPeer -> { + if (EthPeers.BEST_CHAIN.compare(bestPeer, currentPeer) <= 0) { + // Our current target is better or equal to the best peer + return false; + } + // Require some threshold to be exceeded before switching targets to keep some + // stability + // when multiple peers are in range of each other + final ChainState bestPeerChainState = bestPeer.chainState(); + final long heightDifference = + bestPeerChainState.getEstimatedHeight() + - currentPeerChainState.getEstimatedHeight(); + if (heightDifference == 0 && bestPeerChainState.getEstimatedHeight() == 0) { + // Only check td if we don't have a height metric + final UInt256 tdDifference = + bestPeerChainState + .getBestBlock() + .getTotalDifficulty() + .minus(currentPeerChainState.getBestBlock().getTotalDifficulty()); + return tdDifference.compareTo(config.downloaderChangeTargetThresholdByTd()) > 0; + } + return heightDifference > config.downloaderChangeTargetThresholdByHeight(); + }) + .orElse(false); + } +} diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/state/SyncTarget.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/state/SyncTarget.java index 3850907b85..6ee1ffb4ad 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/state/SyncTarget.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/state/SyncTarget.java @@ -24,7 +24,7 @@ public class SyncTarget { private final EthPeer peer; private BlockHeader commonAncestor; - SyncTarget(final EthPeer peer, final BlockHeader commonAncestor) { + public SyncTarget(final EthPeer peer, final BlockHeader commonAncestor) { this.peer = peer; this.commonAncestor = commonAncestor; } diff --git a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/GetHeadersFromPeerByHashTask.java b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/GetHeadersFromPeerByHashTask.java index f45306fc50..a6cdffd6fb 100644 --- a/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/GetHeadersFromPeerByHashTask.java +++ b/ethereum/eth/src/main/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/GetHeadersFromPeerByHashTask.java @@ -111,6 +111,25 @@ public static AbstractGetHeadersFromPeerTask endingAtHash( ethTasksTimer); } + public static AbstractGetHeadersFromPeerTask endingAtHash( + final ProtocolSchedule protocolSchedule, + final EthContext ethContext, + final Hash lastHash, + final long lastBlockNumber, + final int segmentLength, + final int skip, + final LabelledMetric ethTasksTimer) { + return new GetHeadersFromPeerByHashTask( + protocolSchedule, + ethContext, + lastHash, + lastBlockNumber, + segmentLength, + skip, + true, + ethTasksTimer); + } + public static AbstractGetHeadersFromPeerTask forSingleHash( final ProtocolSchedule protocolSchedule, final EthContext ethContext, diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/FullSyncDownloaderTest.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java similarity index 97% rename from ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/FullSyncDownloaderTest.java rename to ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java index c596626a92..1087605178 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/FullSyncDownloaderTest.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java @@ -10,7 +10,7 @@ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ -package tech.pegasys.pantheon.ethereum.eth.sync; +package tech.pegasys.pantheon.ethereum.eth.sync.fullsync; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -36,6 +36,7 @@ import tech.pegasys.pantheon.ethereum.eth.manager.ethtaskutils.BlockchainSetupUtil; import tech.pegasys.pantheon.ethereum.eth.messages.EthPV62; import tech.pegasys.pantheon.ethereum.eth.messages.GetBlockHeadersMessage; +import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; import tech.pegasys.pantheon.ethereum.eth.sync.state.SyncState; import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; @@ -333,8 +334,8 @@ public void switchesSyncTarget_betterHeight() { peerB.getEthPeer().chainState().update(gen.hash(), 100); // Process through first task cycle - final CompletableFuture firstTask = downloader.currentTask; - while (downloader.currentTask == firstTask) { + final CompletableFuture firstTask = downloader.getCurrentTask(); + while (downloader.getCurrentTask() == firstTask) { RespondingEthPeer.respondOnce(responder, peerA, peerB); } @@ -373,8 +374,8 @@ public void doesNotSwitchSyncTarget_betterHeightUnderThreshold() { otherPeer.getEthPeer().chainState().update(gen.hash(), 100); // Process through first task cycle - final CompletableFuture firstTask = downloader.currentTask; - while (downloader.currentTask == firstTask) { + final CompletableFuture firstTask = downloader.getCurrentTask(); + while (downloader.getCurrentTask() == firstTask) { RespondingEthPeer.respondOnce(responder, bestPeer, otherPeer); } @@ -416,8 +417,8 @@ public void switchesSyncTarget_betterTd() { .update(gen.header(), syncState.chainHeadTotalDifficulty().plus(300)); // Process through first task cycle - final CompletableFuture firstTask = downloader.currentTask; - while (downloader.currentTask == firstTask) { + final CompletableFuture firstTask = downloader.getCurrentTask(); + while (downloader.getCurrentTask() == firstTask) { RespondingEthPeer.respondOnce(responder, peerA, peerB); } @@ -467,8 +468,8 @@ public void doesNotSwitchSyncTarget_betterTdUnderThreshold() { .update(gen.header(1000), syncState.chainHeadTotalDifficulty().plus(300)); // Process through first task cycle - final CompletableFuture firstTask = downloader.currentTask; - while (downloader.currentTask == firstTask) { + final CompletableFuture firstTask = downloader.getCurrentTask(); + while (downloader.getCurrentTask() == firstTask) { RespondingEthPeer.respondOnce(responder, bestPeer, otherPeer); } From 9ba5cd0ec05b92492bcc88e90b9ac1df993d3e53 Mon Sep 17 00:00:00 2001 From: MadelineMurray <43356962+MadelineMurray@users.noreply.github.com> Date: Tue, 29 Jan 2019 10:03:08 +1000 Subject: [PATCH 19/31] IBFT available in v1.0 note added (#667) --- docs/Configuring-Pantheon/IBFT.md | 0 docs/Consensus-Protocols/Comparing-PoA.md | 3 +++ docs/Consensus-Protocols/IBFT.md | 3 +++ docs/Consensus-Protocols/Overview-Consensus.md | 3 +++ docs/Reference/JSON-RPC-API-Methods.md | 3 +++ docs/Reference/Using-JSON-RPC-API.md | 4 ++-- 6 files changed, 14 insertions(+), 2 deletions(-) delete mode 100644 docs/Configuring-Pantheon/IBFT.md diff --git a/docs/Configuring-Pantheon/IBFT.md b/docs/Configuring-Pantheon/IBFT.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs/Consensus-Protocols/Comparing-PoA.md b/docs/Consensus-Protocols/Comparing-PoA.md index 227b97e3bd..a826d9c043 100644 --- a/docs/Consensus-Protocols/Comparing-PoA.md +++ b/docs/Consensus-Protocols/Comparing-PoA.md @@ -6,6 +6,9 @@ Pantheon implements the Clique and IBFT 2.0 Proof of Authority consensus protoco consensus protocols are used when participants are known to each other and there is a level of trust between them. For example, in a permissioned consortium network. +!!! note + IBFT 2.0 is under development and will be available in v1.0. + Proof of Authority consensus protocols allow faster block times and have a much greater throughput of transactions than the Ethash Proof of Work consensus protocol used on the Ethereum MainNet. diff --git a/docs/Consensus-Protocols/IBFT.md b/docs/Consensus-Protocols/IBFT.md index 245a88f598..cbd6f28cc2 100644 --- a/docs/Consensus-Protocols/IBFT.md +++ b/docs/Consensus-Protocols/IBFT.md @@ -6,6 +6,9 @@ description: Pantheon IBFT 2.0 Proof-of-Authority (PoA) consensus protocol imple # IBFT 2.0 +!!! note + IBFT 2.0 is under development and will be available in v1.0. + Pantheon implements the IBFT 2.0 Proof-of-Authority (PoA) consensus protocol. IBFT 2.0 can be used for private networks. In IBFT 2.0 networks, transactions and blocks are validated by approved accounts, known as validators. diff --git a/docs/Consensus-Protocols/Overview-Consensus.md b/docs/Consensus-Protocols/Overview-Consensus.md index 0ca1ac7fd7..d17554ccc3 100644 --- a/docs/Consensus-Protocols/Overview-Consensus.md +++ b/docs/Consensus-Protocols/Overview-Consensus.md @@ -11,6 +11,9 @@ Pantheon implements a number of consensus protocols: * [IBFT 2.0](IBFT.md) (Proof of Authority) +!!! note + IBFT 2.0 is under development and will be available in v1.0. + The genesis file specifies the consensus protocol for a chain `config`: ```json tab="Ethash" diff --git a/docs/Reference/JSON-RPC-API-Methods.md b/docs/Reference/JSON-RPC-API-Methods.md index 461f691e86..ada4303f65 100644 --- a/docs/Reference/JSON-RPC-API-Methods.md +++ b/docs/Reference/JSON-RPC-API-Methods.md @@ -1928,6 +1928,9 @@ None ## IBFT 2.0 Methods +!!! note + IBFT 2.0 is under development and will be available in v1.0. + ### ibft_discardValidatorVote Discards a proposal to [add or remove a validator](../Consensus-Protocols/IBFT.md#adding-and-removing-validators) with the specified address. diff --git a/docs/Reference/Using-JSON-RPC-API.md b/docs/Reference/Using-JSON-RPC-API.md index b3748cf722..971ad01cea 100644 --- a/docs/Reference/Using-JSON-RPC-API.md +++ b/docs/Reference/Using-JSON-RPC-API.md @@ -76,8 +76,8 @@ The `ETH`, `NET`, `WEB3`, `CLIQUE`, and `IBFT` APIs are enabled by default. Use the [`--rpc-api` option](Pantheon-CLI-Syntax.md#rpc-api) to enable the `ADMIN`, `DEBUG`, and `MINER` APIs. -!!!note - :construction: IBFT is not currently supported. Support for IBFT is in active development. +!!! note + IBFT 2.0 and Permissioning are under development and will be available in v1.0. ### Block Parameter From 238b4e4a05e8ffe404b057dbbeab16c9d2cca899 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 12:29:03 +1100 Subject: [PATCH 20/31] wip --- CHANGELOG.md | 6 +- Jenkinsfile.benchmark | 2 +- ROADMAP.md | 4 +- .../truffle-pet-shop-tutorial/README.md | 14 +- .../ibft/support/StubbedGossiper.java | 41 ----- .../ibft/support/TestContextBuilder.java | 7 +- .../pantheon/consensus/ibft/Gossiper.java | 8 +- .../pantheon/consensus/ibft/IbftGossip.java | 38 +---- .../ibft/TransmittedMessageTracker.java | 68 ++++++++ .../ibft/network/IbftMessageTransmitter.java | 20 +-- .../ibft/statemachine/IbftFinalState.java | 6 +- .../consensus/ibft/IbftGossipTest.java | 114 +------------ .../ibft/TransmittedMessageTrackerTest.java | 93 +++++++++++ .../Accounts-for-Testing.md | 8 +- docs/Configuring-Pantheon/Logging.md | 2 +- .../NetworkID-And-ChainID.md | 24 +-- docs/Configuring-Pantheon/Networking.md | 4 +- .../Passing-JVM-Options.md | 2 +- .../Testing-Developing-Nodes.md | 6 +- docs/Consensus-Protocols/Clique.md | 6 +- docs/Consensus-Protocols/Comparing-PoA.md | 6 +- docs/Consensus-Protocols/IBFT.md | 2 +- docs/DocumentationForRelease0.8.3.html | 156 +++++++++--------- docs/Getting-Started/Getting-Started.md | 2 +- docs/Getting-Started/Run-Docker-Image.md | 6 +- docs/Getting-Started/Starting-Pantheon.md | 22 +-- .../azure/Azure-Private-Network-Quickstart.md | 16 +- docs/Installation/Overview.md | 2 +- docs/Reference/JSON-RPC-API-Methods.md | 16 +- docs/Reference/Pantheon-CLI-Syntax.md | 46 +++--- docs/Tutorials/Create-Private-Network.md | 42 ++--- docs/Tutorials/Private-Network-Quickstart.md | 58 +++---- docs/Using-Pantheon/Debugging.md | 2 +- docs/Using-Pantheon/Transactions.md | 8 +- docs/global/test_accounts.md | 2 +- docs/index.md | 8 +- .../controller/IbftPantheonController.java | 12 +- .../src/test/resources/complete_config.toml | 4 +- .../src/test/resources/everything_config.toml | 6 +- 39 files changed, 426 insertions(+), 463 deletions(-) delete mode 100644 consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java create mode 100644 consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTracker.java create mode 100644 consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ca0bcc900..466e53e231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,7 @@ When restarting your node with the v0.8.4 Docker image: - Documentation updates include: * Migrated to new [documentation site](https://docs.pantheon.pegasys.tech/en/latest/) * Added [configuration file content](https://docs.pantheon.pegasys.tech/en/latest/Configuring-Pantheon/Using-Configuration-File/) - * Added [tutorial to create private network](https://docs.pantheon.pegasys.tech/en/latest/Tutorials/Create-Private-Network/) + * Added [tutorial to create private multicaster](https://docs.pantheon.pegasys.tech/en/latest/Tutorials/Create-Private-Network/) * Added content on [enabling non-default APIs](https://docs.pantheon.pegasys.tech/en/latest/Reference/JSON-RPC-API-Methods/) ## Technical Improvements @@ -214,10 +214,10 @@ Specify `*` or `all` for `--host-whitelist` to effectively disable host protecti - Removed `import-blockchain` command because nothing exports to the required format yet (PR [\#223](https://github.com/PegaSysEng/pantheon/pull/223)) ### Bug Fixes - - `io.netty.util.internal.OutOfDirectMemoryError` errors by removing reference counting from network messages. + - `io.netty.util.internal.OutOfDirectMemoryError` errors by removing reference counting from multicaster messages. - Log spam: endless loop in `nioEventLoopGroup` ([#248](https://github.com/PegaSysEng/pantheon/issues/248) thanks to [@5chdn](https://github.com/5chdn) for reporting) (PR [#261](https://github.com/PegaSysEng/pantheon/pull/261)) - Rinkeby import can stall with too many fragments ([#228](https://github.com/PegaSysEng/pantheon/issues/228) thanks to [@steffenkux](https://github.com/steffenkux) and [@5chdn](https://github.com/5chdn) for reporting) (PR [#255](https://github.com/PegaSysEng/pantheon/pull/255)) - - Clique incorrectly used the chain ID instead of the network ID in ETH status messages (PR [#209](https://github.com/PegaSysEng/pantheon/pull/209)) + - Clique incorrectly used the chain ID instead of the multicaster ID in ETH status messages (PR [#209](https://github.com/PegaSysEng/pantheon/pull/209)) - Gradle deprecation warnings (PR [#246](https://github.com/PegaSysEng/pantheon/pull/246) with thanks to [@jvirtanen](https://github.com/jvirtanen)) - Consensus issue on Ropsten: - Treat output length as a maximum length for CALL operations (PR [#236](https://github.com/PegaSysEng/pantheon/pull/236)) diff --git a/Jenkinsfile.benchmark b/Jenkinsfile.benchmark index d1545f0ecf..6a93a304c4 100644 --- a/Jenkinsfile.benchmark +++ b/Jenkinsfile.benchmark @@ -2,7 +2,7 @@ properties([ parameters([ string(name: 'BENCHMARKS_FORK', defaultValue: 'PegaSysEng', description: 'The user or org from which to checkout the benchmarks repo', trim: true), string(name: 'BENCHMARKS_BRANCH', defaultValue: 'master', description: 'The benchmarks branch to be checked out', trim: true), - string(name: 'NETWORK', defaultValue: 'ropsten', description: 'The name of the network being tested', trim: true), + string(name: 'NETWORK', defaultValue: 'ropsten', description: 'The name of the multicaster being tested', trim: true), string(name: 'DATASET', defaultValue: 'from-0-by-100k', description: 'The name of the directory containing the benchmark data', trim: true), string(name: 'IMPORT_FILE', defaultValue: 'ropsten-000k-100k.blocks', description: 'The name of the file to import', trim: true) ]) diff --git a/ROADMAP.md b/ROADMAP.md index 9a069e63c1..e69107a0ef 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -10,7 +10,7 @@ Our key three areas for now are: * iBFT 2.0 ### Permissioning -We are implementing the key elements of an Enterprise Ethereum Permissioned network. The initial version of this will be based around a JSON RPC API to manage the network. This will form the foundation for a smart contract based solution which will be developed in the `next` release (1.1) +We are implementing the key elements of an Enterprise Ethereum Permissioned multicaster. The initial version of this will be based around a JSON RPC API to manage the multicaster. This will form the foundation for a smart contract based solution which will be developed in the `next` release (1.1) ### First Class Client There is an ongoing piece of work underway enhancing the core performance of Pantheon, and ensuring that it behaves well as a first class client. The key elements of this are implementation of some performance benchmarks, finalising the options for the command line, and implementing an appropriate fast sync mechanism. @@ -26,7 +26,7 @@ The key areas for next are: * iBFT 2.x ### Smart Contract based Permissioning -Building on the Permissioning system implemented in version 1.0 of Pantheon, we will use a smart contract to share this information across the network, giving a consistent set of permissions, and ensuring that all nodes in the network work consistently. +Building on the Permissioning system implemented in version 1.0 of Pantheon, we will use a smart contract to share this information across the multicaster, giving a consistent set of permissions, and ensuring that all nodes in the multicaster work consistently. ### Privacy The Enterprise Ethereum `restricted` privacy will be implemented. diff --git a/acceptance-tests/truffle-pet-shop-tutorial/README.md b/acceptance-tests/truffle-pet-shop-tutorial/README.md index f17aba4d93..12d15916c6 100644 --- a/acceptance-tests/truffle-pet-shop-tutorial/README.md +++ b/acceptance-tests/truffle-pet-shop-tutorial/README.md @@ -13,7 +13,7 @@ npm install truffle-privatekey-provider ``` cd acceptance-tests/truffle-pet-shop-tutorial ``` -* here you will find truffle.js which has network configurations for +* here you will find truffle.js which has multicaster configurations for * development (Ganache) and * devwallet (points to localhost:8545) * Note you don't need Ganache running unless you want to run the tests against it (see below) @@ -29,24 +29,24 @@ cd $pantheon-working-dir ``` * Run Truffle migrate ``` -truffle migrate --network devwallet +truffle migrate --multicaster devwallet ``` * Output should look something like: ``` -Using network 'devwallet'. +Using multicaster 'devwallet'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x2c16dd43c0adfe0c697279e388f531581c2b722e7f0e968e3e65e4345bdeb502 Migrations: 0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6 -Saving successful migration to network... +Saving successful migration to multicaster... ... 0x1135ea1dd6947f262d65dde8712d17b4b0ec0a36cc917772ce8acd7fe01ca8e2 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying Adoption... ... 0xa3d220639719b8e007a7aa8cb18e8caf3587337b77bac833959f4853b1695369 Adoption: 0xf204a4ef082f5c04bb89f7d5e6568b796096735a -Saving successful migration to network... +Saving successful migration to multicaster... ... 0xd7245d7b1c0a7eb5a5198754f7edd7abdae3b806605b54ecc4716f9b4b05de61 Saving artifacts... @@ -55,11 +55,11 @@ If migrate works, try running the tests ``` cd acceptance-tests/truffle-pet-shop-tutorial -truffle test --network devwallet +truffle test --multicaster devwallet ``` * Output should look something like: ``` -Using network 'devwallet'. +Using multicaster 'devwallet'. Compiling ./contracts/Adoption.sol... Compiling ./test/TestAdoption.sol... diff --git a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java b/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java deleted file mode 100644 index 2740698f34..0000000000 --- a/consensus/ibft/src/integration-test/java/tech/pegasys/pantheon/consensus/ibft/support/StubbedGossiper.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 tech.pegasys.pantheon.consensus.ibft.Gossiper; -import tech.pegasys.pantheon.consensus.ibft.network.ValidatorMulticaster; -import tech.pegasys.pantheon.ethereum.core.Address; -import tech.pegasys.pantheon.ethereum.p2p.api.Message; -import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; - -import java.util.List; - -public class StubbedGossiper implements Gossiper { - - private final ValidatorMulticaster multicaster; - - StubbedGossiper(final ValidatorMulticaster multicaster) { - this.multicaster = multicaster; - } - - @Override - public boolean gossipMessage(final Message message) { - return false; - } - - @Override - public boolean send(final MessageData messageData, final List
excludeAddressesList) { - multicaster.send(messageData, excludeAddressesList); - return true; - } -} 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 5b7b2e6232..7e0775c710 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 @@ -13,6 +13,7 @@ package tech.pegasys.pantheon.consensus.ibft.support; import static java.nio.charset.StandardCharsets.UTF_8; +import static org.mockito.Mockito.mock; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; @@ -156,8 +157,7 @@ public TestContext build() { // Use a stubbed version of the multicaster, to prevent creating PeerConnections etc. final StubValidatorMulticaster multicaster = new StubValidatorMulticaster(); - final Gossiper gossiper = - useGossip ? new IbftGossip(multicaster) : new StubbedGossiper(multicaster); + final Gossiper gossiper = useGossip ? new IbftGossip(multicaster) : mock(Gossiper.class); final ControllerAndState controllerAndState = createControllerAndFinalState( @@ -283,8 +283,7 @@ private static ControllerAndState createControllerAndFinalState( Clock.systemUTC()), blockCreatorFactory, new MessageFactory(nodeKeys), - clock, - gossiper); + clock); final MessageValidatorFactory messageValidatorFactory = new MessageValidatorFactory(proposerSelector, protocolSchedule, protocolContext); diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java index 272419b47b..fe7f0121a8 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java @@ -12,15 +12,9 @@ */ package tech.pegasys.pantheon.consensus.ibft; -import tech.pegasys.pantheon.ethereum.core.Address; import tech.pegasys.pantheon.ethereum.p2p.api.Message; -import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; - -import java.util.List; public interface Gossiper { - boolean gossipMessage(Message message); - - boolean send(MessageData messageData, List
excludeAddressesList); + void gossipMessage(Message message); } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java index f67b529fbb..86e39b3198 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java @@ -21,15 +21,10 @@ import tech.pegasys.pantheon.consensus.ibft.network.ValidatorMulticaster; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; import tech.pegasys.pantheon.ethereum.core.Address; -import tech.pegasys.pantheon.ethereum.core.Hash; import tech.pegasys.pantheon.ethereum.p2p.api.Message; import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; -import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Set; import com.google.common.collect.Lists; @@ -38,31 +33,13 @@ public class IbftGossip implements Gossiper { private final ValidatorMulticaster multicaster; - // Size of the seenMessages cache, should end up utilising 65bytes * this number + some meta data - private final int maxSeenMessages; - - // Set that starts evicting members when it hits capacity - private final Set seenMessages = - Collections.newSetFromMap( - new LinkedHashMap() { - @Override - protected boolean removeEldestEntry(final Map.Entry eldest) { - return size() > maxSeenMessages; - } - }); - - IbftGossip(final ValidatorMulticaster multicaster, final int maxSeenMessages) { - this.maxSeenMessages = maxSeenMessages; - this.multicaster = multicaster; - } - /** * Constructor that attaches gossip logic to a set of multicaster * * @param multicaster Network connections to the remote validators */ public IbftGossip(final ValidatorMulticaster multicaster) { - this(multicaster, 10_000); + this.multicaster = multicaster; } /** @@ -72,7 +49,7 @@ public IbftGossip(final ValidatorMulticaster multicaster) { * @return Whether the message was rebroadcast or has been ignored as a repeat */ @Override - public boolean gossipMessage(final Message message) { + public void gossipMessage(final Message message) { final MessageData messageData = message.getData(); final SignedData signedData; switch (messageData.getCode()) { @@ -98,17 +75,6 @@ public boolean gossipMessage(final Message message) { final List
excludeAddressesList = Lists.newArrayList(message.getConnection().getPeer().getAddress(), signedData.getSender()); - return send(messageData, excludeAddressesList); - } - - @Override - public boolean send(final MessageData messageData, final List
excludeAddressesList) { - Hash uniqueID = Hash.hash(messageData.getData()); - if (seenMessages.contains(uniqueID)) { - return false; - } multicaster.send(messageData, excludeAddressesList); - seenMessages.add(uniqueID); - return true; } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTracker.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTracker.java new file mode 100644 index 0000000000..a5e9e85892 --- /dev/null +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTracker.java @@ -0,0 +1,68 @@ +/* + * 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.consensus.ibft.network.ValidatorMulticaster; +import tech.pegasys.pantheon.ethereum.core.Address; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class TransmittedMessageTracker implements ValidatorMulticaster { + + private final int maxSeenMessages; + private final ValidatorMulticaster multicaster; + + TransmittedMessageTracker(final ValidatorMulticaster multicaster, final int maxSeenMessages) { + this.maxSeenMessages = maxSeenMessages; + this.multicaster = multicaster; + } + + /** + * Constructor that attaches gossip logic to a set of multicaster + * + * @param multicaster Network connections to the remote validators + */ + public TransmittedMessageTracker(final ValidatorMulticaster multicaster) { + this(multicaster, 10_000); + } + + // Set that starts evicting members when it hits capacity + private final Set seenMessages = + Collections.newSetFromMap( + new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(final Map.Entry eldest) { + return size() > maxSeenMessages; + } + }); + + @Override + public void send(final MessageData message) { + send(message, Collections.emptyList()); + } + + @Override + public void send(final MessageData message, final Collection
blackList) { + final Object uniqueID = message.hashCode(); + if (seenMessages.contains(uniqueID)) { + return; + } + multicaster.send(message, blackList); + seenMessages.add(uniqueID); + } +} diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java index f3c0434c1b..55cc1cec68 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/network/IbftMessageTransmitter.java @@ -12,10 +12,7 @@ */ package tech.pegasys.pantheon.consensus.ibft.network; -import static java.util.Collections.emptyList; - import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.Gossiper; import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; @@ -39,11 +36,12 @@ public class IbftMessageTransmitter { private final MessageFactory messageFactory; - private final Gossiper network; + private final ValidatorMulticaster multicaster; - public IbftMessageTransmitter(final MessageFactory messageFactory, final Gossiper network) { + public IbftMessageTransmitter( + final MessageFactory messageFactory, final ValidatorMulticaster multicaster) { this.messageFactory = messageFactory; - this.network = network; + this.multicaster = multicaster; } public void multicastProposal(final ConsensusRoundIdentifier roundIdentifier, final Block block) { @@ -52,7 +50,7 @@ public void multicastProposal(final ConsensusRoundIdentifier roundIdentifier, fi final ProposalMessageData message = ProposalMessageData.create(signedPayload); - network.send(message, emptyList()); + multicaster.send(message); } public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, final Hash digest) { @@ -61,7 +59,7 @@ public void multicastPrepare(final ConsensusRoundIdentifier roundIdentifier, fin final PrepareMessageData message = PrepareMessageData.create(signedPayload); - network.send(message, emptyList()); + multicaster.send(message); } public void multicastCommit( @@ -73,7 +71,7 @@ public void multicastCommit( final CommitMessageData message = CommitMessageData.create(signedPayload); - network.send(message, emptyList()); + multicaster.send(message); } public void multicastRoundChange( @@ -85,7 +83,7 @@ public void multicastRoundChange( final RoundChangeMessageData message = RoundChangeMessageData.create(signedPayload); - network.send(message, emptyList()); + multicaster.send(message); } public void multicastNewRound( @@ -99,6 +97,6 @@ public void multicastNewRound( final NewRoundMessageData message = NewRoundMessageData.create(signedPayload); - network.send(message, emptyList()); + multicaster.send(message); } } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java index b323a84391..433225157a 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/statemachine/IbftFinalState.java @@ -17,7 +17,6 @@ import tech.pegasys.pantheon.consensus.common.ValidatorProvider; import tech.pegasys.pantheon.consensus.ibft.BlockTimer; import tech.pegasys.pantheon.consensus.ibft.ConsensusRoundIdentifier; -import tech.pegasys.pantheon.consensus.ibft.Gossiper; import tech.pegasys.pantheon.consensus.ibft.RoundTimer; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory; import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; @@ -54,8 +53,7 @@ public IbftFinalState( final BlockTimer blockTimer, final IbftBlockCreatorFactory blockCreatorFactory, final MessageFactory messageFactory, - final Clock clock, - final Gossiper gossiper) { + final Clock clock) { this.validatorProvider = validatorProvider; this.nodeKeys = nodeKeys; this.localAddress = localAddress; @@ -66,7 +64,7 @@ public IbftFinalState( this.blockCreatorFactory = blockCreatorFactory; this.messageFactory = messageFactory; this.clock = clock; - this.messageTransmitter = new IbftMessageTransmitter(messageFactory, gossiper); + this.messageTransmitter = new IbftMessageTransmitter(messageFactory, validatorMulticaster); } public int getQuorum() { diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java index e189b9e76c..fc04960e6d 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java @@ -13,19 +13,14 @@ package tech.pegasys.pantheon.consensus.ibft; import static com.google.common.collect.Lists.newArrayList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import tech.pegasys.pantheon.consensus.ibft.messagedata.CommitMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.NewRoundMessageData; -import tech.pegasys.pantheon.consensus.ibft.messagedata.PrepareMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.ProposalMessageData; import tech.pegasys.pantheon.consensus.ibft.messagedata.RoundChangeMessageData; import tech.pegasys.pantheon.consensus.ibft.network.MockPeerFactory; import tech.pegasys.pantheon.consensus.ibft.network.ValidatorMulticaster; import tech.pegasys.pantheon.consensus.ibft.payload.Payload; -import tech.pegasys.pantheon.consensus.ibft.payload.ProposalPayload; import tech.pegasys.pantheon.consensus.ibft.payload.SignedData; import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; import tech.pegasys.pantheon.ethereum.core.Address; @@ -52,7 +47,7 @@ public class IbftGossipTest { @Before public void setup() { - ibftGossip = new IbftGossip(validatorMulticaster, 10); + ibftGossip = new IbftGossip(validatorMulticaster); peerConnection = MockPeerFactory.create(senderAddress); } @@ -64,131 +59,26 @@ private

void assertRebroadcastToAllExceptSignerAndSender( final MessageData messageData = createMessageData.apply(payload); final Message message = new DefaultMessage(peerConnection, messageData); - final boolean gossipResult = ibftGossip.gossipMessage(message); - assertThat(gossipResult).isTrue(); + ibftGossip.gossipMessage(message); verify(validatorMulticaster) .send(messageData, newArrayList(senderAddress, payload.getSender())); } - private

void assertRebroadcastOnlyOnce( - final Function> createPayload, - final Function, MessageData> createMessageData) { - final KeyPair keypair = KeyPair.generate(); - final SignedData

payload = createPayload.apply(keypair); - final MessageData messageData = createMessageData.apply(payload); - final Message message = new DefaultMessage(peerConnection, messageData); - - final boolean gossip1Result = ibftGossip.gossipMessage(message); - final boolean gossip2Result = ibftGossip.gossipMessage(message); - assertThat(gossip1Result).isTrue(); - assertThat(gossip2Result).isFalse(); - verify(validatorMulticaster, times(1)) - .send(messageData, newArrayList(senderAddress, payload.getSender())); - } - @Test public void assertRebroadcastsProposalToAllExceptSignerAndSender() { assertRebroadcastToAllExceptSignerAndSender( TestHelpers::createSignedProposalPayload, ProposalMessageData::create); } - @Test - public void assertRebroadcastsProposalOnlyOnce() { - assertRebroadcastOnlyOnce( - TestHelpers::createSignedProposalPayload, ProposalMessageData::create); - } - - @Test - public void assertRebroadcastsPrepareToAllExceptSignerAndSender() { - assertRebroadcastToAllExceptSignerAndSender( - TestHelpers::createSignedPreparePayload, PrepareMessageData::create); - } - - @Test - public void assertRebroadcastsPrepareOnlyOnce() { - assertRebroadcastOnlyOnce(TestHelpers::createSignedPreparePayload, PrepareMessageData::create); - } - - @Test - public void assertRebroadcastsCommitToAllExceptSignerAndSender() { - assertRebroadcastToAllExceptSignerAndSender( - TestHelpers::createSignedCommitPayload, CommitMessageData::create); - } - - @Test - public void assertRebroadcastsCommitOnlyOnce() { - assertRebroadcastOnlyOnce(TestHelpers::createSignedCommitPayload, CommitMessageData::create); - } - @Test public void assertRebroadcastsRoundChangeToAllExceptSignerAndSender() { assertRebroadcastToAllExceptSignerAndSender( TestHelpers::createSignedRoundChangePayload, RoundChangeMessageData::create); } - @Test - public void assertRebroadcastsRoundChangeOnlyOnce() { - assertRebroadcastOnlyOnce( - TestHelpers::createSignedRoundChangePayload, RoundChangeMessageData::create); - } - @Test public void assertRebroadcastsNewRoundToAllExceptSignerAndSender() { assertRebroadcastToAllExceptSignerAndSender( TestHelpers::createSignedNewRoundPayload, NewRoundMessageData::create); } - - @Test - public void assertRebroadcastsNewRoundOnlyOnce() { - assertRebroadcastOnlyOnce( - TestHelpers::createSignedNewRoundPayload, NewRoundMessageData::create); - } - - @Test - public void evictMessageRecordAtCapacity() { - final KeyPair keypair = KeyPair.generate(); - final SignedData payload = - TestHelpers.createSignedProposalPayloadWithRound(keypair, 0); - final MessageData messageData = ProposalMessageData.create(payload); - final Message message = new DefaultMessage(peerConnection, messageData); - final boolean gossip1Result = ibftGossip.gossipMessage(message); - final boolean gossip2Result = ibftGossip.gossipMessage(message); - assertThat(gossip1Result).isTrue(); - assertThat(gossip2Result).isFalse(); - verify(validatorMulticaster, times(1)) - .send(messageData, newArrayList(senderAddress, payload.getSender())); - - for (int i = 1; i <= 9; i++) { - final SignedData nextPayload = - TestHelpers.createSignedProposalPayloadWithRound(keypair, i); - final MessageData nextMessageData = ProposalMessageData.create(nextPayload); - final Message nextMessage = new DefaultMessage(peerConnection, nextMessageData); - final boolean nextGossipResult = ibftGossip.gossipMessage(nextMessage); - assertThat(nextGossipResult).isTrue(); - } - - final boolean gossip3Result = ibftGossip.gossipMessage(message); - assertThat(gossip3Result).isFalse(); - verify(validatorMulticaster, times(1)) - .send(messageData, newArrayList(senderAddress, payload.getSender())); - - { - final SignedData nextPayload = - TestHelpers.createSignedProposalPayloadWithRound(keypair, 10); - final MessageData nextMessageData = ProposalMessageData.create(nextPayload); - final Message nextMessage = new DefaultMessage(peerConnection, nextMessageData); - final boolean nextGossipResult = ibftGossip.gossipMessage(nextMessage); - assertThat(nextGossipResult).isTrue(); - } - - final boolean gossip4Result = ibftGossip.gossipMessage(message); - assertThat(gossip4Result).isTrue(); - verify(validatorMulticaster, times(2)) - .send(messageData, newArrayList(senderAddress, payload.getSender())); - - final boolean gossip5Result = ibftGossip.gossipMessage(message); - assertThat(gossip5Result).isFalse(); - verify(validatorMulticaster, times(2)) - .send(messageData, newArrayList(senderAddress, payload.getSender())); - } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java new file mode 100644 index 0000000000..ff7af6add8 --- /dev/null +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java @@ -0,0 +1,93 @@ +/* + * 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 java.util.Collections.emptyList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import tech.pegasys.pantheon.consensus.ibft.network.ValidatorMulticaster; +import tech.pegasys.pantheon.ethereum.core.Address; +import tech.pegasys.pantheon.ethereum.core.AddressHelpers; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; +import tech.pegasys.pantheon.ethereum.p2p.wire.RawMessage; +import tech.pegasys.pantheon.util.bytes.BytesValue; + +import java.util.List; + +import com.google.common.collect.Lists; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class TransmittedMessageTrackerTest { + + private ValidatorMulticaster multicaster = mock(ValidatorMulticaster.class); + private final TransmittedMessageTracker messageTracker = + new TransmittedMessageTracker(multicaster, 5); + private final RawMessage messageSent = new RawMessage(5, BytesValue.wrap(new byte[5])); + + @Test + public void previouslySentMessageIsNotSentAgain() { + + messageTracker.send(messageSent); + verify(multicaster, times(1)).send(messageSent, emptyList()); + reset(multicaster); + + messageTracker.send(messageSent); + messageTracker.send(messageSent, emptyList()); + verifyZeroInteractions(multicaster); + } + + @Test + public void messagesSentWithABlackListAreNotRetransmitted() { + messageTracker.send(messageSent, emptyList()); + verify(multicaster, times(1)).send(messageSent, emptyList()); + reset(multicaster); + + messageTracker.send(messageSent, emptyList()); + messageTracker.send(messageSent); + verifyZeroInteractions(multicaster); + } + + @Test + public void oldMessagesAreEvictedWhenFullAndCanThenBeRetransmitted() { + final List messagesSent = Lists.newArrayList(); + + for (int i = 0; i < 6; i++) { + final RawMessage msg = new RawMessage(i, BytesValue.wrap(new byte[i])); + messagesSent.add(msg); + messageTracker.send(msg); + verify(multicaster, times(1)).send(msg, emptyList()); + } + reset(multicaster); + + messageTracker.send(messagesSent.get(5)); + verifyZeroInteractions(multicaster); + + messageTracker.send(messagesSent.get(0)); + verify(multicaster, times(1)).send(messagesSent.get(0), emptyList()); + } + + @Test + public void passedInBlackListIsPassedToUnderlyingValidator() { + List

blackList = + Lists.newArrayList(AddressHelpers.ofValue(0), AddressHelpers.ofValue(1)); + messageTracker.send(messageSent, blackList); + verify(multicaster, times(1)).send(messageSent, blackList); + } +} diff --git a/docs/Configuring-Pantheon/Accounts-for-Testing.md b/docs/Configuring-Pantheon/Accounts-for-Testing.md index a6beeaf5ab..11877dd7f5 100644 --- a/docs/Configuring-Pantheon/Accounts-for-Testing.md +++ b/docs/Configuring-Pantheon/Accounts-for-Testing.md @@ -1,14 +1,14 @@ -description: Ethereum accounts used for testing only on private network +description: Ethereum accounts used for testing only on private multicaster # Accounts for Testing -You can use existing accounts for testing by including them in the genesis file for a private network. +You can use existing accounts for testing by including them in the genesis file for a private multicaster. Alternatively, Pantheon provides predefined accounts in development mode. ## Development Mode - When you start Pantheon with the [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) + When you start Pantheon with the [`--multicaster=dev`](../Reference/Pantheon-CLI-Syntax.md#multicaster) command line option, the `dev.json` genesis file is used by default. The `dev.json` genesis file defines the accounts below that can be used for testing. @@ -17,7 +17,7 @@ Alternatively, Pantheon provides predefined accounts in development mode. ## Genesis File -To use existing test accounts, specify the accounts and balances in a genesis file for your test network. +To use existing test accounts, specify the accounts and balances in a genesis file for your test multicaster. For an example of defining accounts in the genesis file, refer to [`dev.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/dev.json). Use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) command line option to diff --git a/docs/Configuring-Pantheon/Logging.md b/docs/Configuring-Pantheon/Logging.md index 6c58a6e108..ddb248bb1d 100644 --- a/docs/Configuring-Pantheon/Logging.md +++ b/docs/Configuring-Pantheon/Logging.md @@ -59,5 +59,5 @@ setting it before starting Pantheon. To set the debug logging and start Pantheon connected to the Rinkeby testnet: ```bash - $ LOG4J_CONFIGURATION_FILE=./debug.xml bin/pantheon --network=rinkeby + $ LOG4J_CONFIGURATION_FILE=./debug.xml bin/pantheon --multicaster=rinkeby ``` \ No newline at end of file diff --git a/docs/Configuring-Pantheon/NetworkID-And-ChainID.md b/docs/Configuring-Pantheon/NetworkID-And-ChainID.md index 1e9351211c..4e14746d5c 100644 --- a/docs/Configuring-Pantheon/NetworkID-And-ChainID.md +++ b/docs/Configuring-Pantheon/NetworkID-And-ChainID.md @@ -1,23 +1,23 @@ -description: Pantheon network ID and chain ID implementation +description: Pantheon multicaster ID and chain ID implementation # Network ID and Chain ID -Ethereum networks have a **network ID** and a **chain ID**. The network ID can be specified using the -[`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option and the chain ID is specified +Ethereum networks have a **multicaster ID** and a **chain ID**. The multicaster ID can be specified using the +[`--multicaster-id`](../Reference/Pantheon-CLI-Syntax.md#multicaster-id) option and the chain ID is specified in the genesis file. -For most networks including MainNet and the public testnets, the network ID and the chain ID are the -same and Pantheon network id default values are defined according to the genesis chain id value. +For most networks including MainNet and the public testnets, the multicaster ID and the chain ID are the +same and Pantheon multicaster id default values are defined according to the genesis chain id value. -The network ID is automatically set by Pantheon to the chain ID when connecting to the Ethereum networks: +The multicaster ID is automatically set by Pantheon to the chain ID when connecting to the Ethereum networks: -- **MainNet:** chain-id ==1==, network-id ==1== -- **Rinkeby:** chain-id ==4==, network-id ==4== -- **Ropsten:** chain-id ==3==, network-id ==3== -- **Dev:** chain-id ==2018==, network-id ==2018== +- **MainNet:** chain-id ==1==, multicaster-id ==1== +- **Rinkeby:** chain-id ==4==, multicaster-id ==4== +- **Ropsten:** chain-id ==3==, multicaster-id ==3== +- **Dev:** chain-id ==2018==, multicaster-id ==2018== -When using the [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) or +When using the [`--multicaster=dev`](../Reference/Pantheon-CLI-Syntax.md#multicaster) or [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) options, you can override the -network ID using the [`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option. +multicaster ID using the [`--multicaster-id`](../Reference/Pantheon-CLI-Syntax.md#multicaster-id) option. diff --git a/docs/Configuring-Pantheon/Networking.md b/docs/Configuring-Pantheon/Networking.md index 11678d993c..bb280c9ca7 100644 --- a/docs/Configuring-Pantheon/Networking.md +++ b/docs/Configuring-Pantheon/Networking.md @@ -3,7 +3,7 @@ description: Pantheon networking is about P2P discovery and communication betwee # Networking -Pantheon uses the network to find and connect to peers. +Pantheon uses the multicaster to find and connect to peers. ## Firewalls and Incoming Connections @@ -38,7 +38,7 @@ Trailing peers cannot be used to get new blocks and are more likely to be reques The [`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled) command line option enables P2P peer discovery. -Only set this option to `false` if you are running a test node or a test network with fixed nodes. +Only set this option to `false` if you are running a test node or a test multicaster with fixed nodes. ## Monitoring Peer Connections diff --git a/docs/Configuring-Pantheon/Passing-JVM-Options.md b/docs/Configuring-Pantheon/Passing-JVM-Options.md index 482c718267..2f7f0b9fe9 100644 --- a/docs/Configuring-Pantheon/Passing-JVM-Options.md +++ b/docs/Configuring-Pantheon/Passing-JVM-Options.md @@ -12,5 +12,5 @@ For Bash-based executions, you can set the variable for only the scope of the pr !!! example ```bash $ PANTHEON_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 \ - $ bin/pantheon --network=rinkeby + $ bin/pantheon --multicaster=rinkeby ``` \ No newline at end of file diff --git a/docs/Configuring-Pantheon/Testing-Developing-Nodes.md b/docs/Configuring-Pantheon/Testing-Developing-Nodes.md index 0b4a0e2866..2b3639fdcc 100644 --- a/docs/Configuring-Pantheon/Testing-Developing-Nodes.md +++ b/docs/Configuring-Pantheon/Testing-Developing-Nodes.md @@ -15,7 +15,7 @@ For mainnet, Rinkeby, Ropsten, and Görli, Pantheon predefines a list of enode U ### Private Networks -To start a bootnode for a private network: +To start a bootnode for a private multicaster: 1. Export the public key to a file: @@ -24,7 +24,7 @@ To start a bootnode for a private network: pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath export-pub-key bootnode ``` Where `privateNetworkGenesis.json` and `nodeDataPath` are changed to the relevant values for - your private network. + your private multicaster. The node public key is exported to the `bootnode` file. @@ -61,5 +61,5 @@ To start a node specifying the bootnode for P2P discovery: !!! example ```bash - pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath --p2p-host=127.0.0.1 --p2p-port=30301 --network-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303 + pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath --p2p-host=127.0.0.1 --p2p-port=30301 --multicaster-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303 ``` \ No newline at end of file diff --git a/docs/Consensus-Protocols/Clique.md b/docs/Consensus-Protocols/Clique.md index e6141673db..72e5670fdd 100644 --- a/docs/Consensus-Protocols/Clique.md +++ b/docs/Consensus-Protocols/Clique.md @@ -13,7 +13,7 @@ Signers take turns to create the next block. Existing signers propose and vote t ## Genesis File -To use Clique in a private network, Pantheon requires a Clique genesis file. When connecting to Rinkeby, +To use Clique in a private multicaster, Pantheon requires a Clique genesis file. When connecting to Rinkeby, Pantheon uses the [`rinkeby.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/rinkeby.json) genesis file in the `/pantheon/config/src/main/resources` directory. @@ -41,8 +41,8 @@ The properties specific to Clique are: * `epoch` - Number of blocks after which to reset all votes. * `extraData` - Initial signers are specified after the 32 bytes reserved for vanity data. -To connect to the Rinkeby testnet, start Pantheon with the [`--network=rinkeby`](../Reference/Pantheon-CLI-Syntax.md#network) -command line option. To start a node on a Clique private network, use the +To connect to the Rinkeby testnet, start Pantheon with the [`--multicaster=rinkeby`](../Reference/Pantheon-CLI-Syntax.md#multicaster) +command line option. To start a node on a Clique private multicaster, use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) option to specify the custom genesis file. ## Adding and Removing Signers diff --git a/docs/Consensus-Protocols/Comparing-PoA.md b/docs/Consensus-Protocols/Comparing-PoA.md index a826d9c043..cb4b42e8ff 100644 --- a/docs/Consensus-Protocols/Comparing-PoA.md +++ b/docs/Consensus-Protocols/Comparing-PoA.md @@ -4,7 +4,7 @@ Pantheon implements the Clique and IBFT 2.0 Proof of Authority consensus protocols. Proof of Authority consensus protocols are used when participants are known to each other and there is a level of trust between them. -For example, in a permissioned consortium network. +For example, in a permissioned consortium multicaster. !!! note IBFT 2.0 is under development and will be available in v1.0. @@ -12,7 +12,7 @@ For example, in a permissioned consortium network. Proof of Authority consensus protocols allow faster block times and have a much greater throughput of transactions than the Ethash Proof of Work consensus protocol used on the Ethereum MainNet. -In Clique and IBFT 2.0, a group of nodes in the network act as signers (Clique) or validators (IBFT 2.0). These nodes propose, validate, +In Clique and IBFT 2.0, a group of nodes in the multicaster act as signers (Clique) or validators (IBFT 2.0). These nodes propose, validate, and add blocks to the blockchain. Nodes are added to or removed from the signer/validator pool by the existing group of nodes voting. !!! note @@ -36,7 +36,7 @@ Clique does not have immediate finality. Implementations using Clique must be aw ### Liveness Clique is more fault tolerant than IBFT 2.0. Clique tolerates up to half to the validators failing. IBFT 2.0 networks -tolerate up to (n-1)/3 faulty nodes. For example, in an IBFT 2.0 network of: +tolerate up to (n-1)/3 faulty nodes. For example, in an IBFT 2.0 multicaster of: * 3, no bad node are tolerated * 4-6, 1 bad node is tolerated diff --git a/docs/Consensus-Protocols/IBFT.md b/docs/Consensus-Protocols/IBFT.md index cbd6f28cc2..305afcb35f 100644 --- a/docs/Consensus-Protocols/IBFT.md +++ b/docs/Consensus-Protocols/IBFT.md @@ -74,7 +74,7 @@ Properties that have specific values in IBFT 2.0 genesis files are: * `difficulty` - `0x1` * `mixHash` - `0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365` for Istanbul block identification. -To start a node on an IBFT 2.0 private network, use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file`) option to specify the custom genesis file. +To start a node on an IBFT 2.0 private multicaster, use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file`) option to specify the custom genesis file. ## Adding and Removing Validators diff --git a/docs/DocumentationForRelease0.8.3.html b/docs/DocumentationForRelease0.8.3.html index a758bb2778..58a0488e1a 100644 --- a/docs/DocumentationForRelease0.8.3.html +++ b/docs/DocumentationForRelease0.8.3.html @@ -728,11 +728,11 @@

Contents

  • What doesn't Pantheon support?
  • What is Pantheon?

    -

    Pantheon is an open-source Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public network, private networks, and test networks such as Rinkeby and Ropsten. Pantheon implements Proof of Work (Ethash) and Proof of Authority (Clique) consensus mechanisms.

    -

    You can use Pantheon to develop enterprise applications requiring secure, high-performance transaction processing in a private network.

    +

    Pantheon is an open-source Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public multicaster, private networks, and test networks such as Rinkeby and Ropsten. Pantheon implements Proof of Work (Ethash) and Proof of Authority (Clique) consensus mechanisms.

    +

    You can use Pantheon to develop enterprise applications requiring secure, high-performance transaction processing in a private multicaster.

    Our roadmap includes Pantheon with privacy features, alternative consensus mechanisms, and other enterprise features.

    What can you do with Pantheon?

    -

    Pantheon includes a command line interface and JSON-RPC API for running, maintaining, debugging, and monitoring node operations in an Ethereum network. You can use the API via RPC over HTTP or via WebSockets transport, and Pub/Sub is supported. The API supports typical Ethereum functionalities such as:

    +

    Pantheon includes a command line interface and JSON-RPC API for running, maintaining, debugging, and monitoring node operations in an Ethereum multicaster. You can use the API via RPC over HTTP or via WebSockets transport, and Pub/Sub is supported. The API supports typical Ethereum functionalities such as:

    • Ether token mining
    • Smart contract development
    • @@ -752,7 +752,7 @@

      Pantheon Installation

      Disk Space and RAM Requirements

      Your computer should have at least 4 GB RAM.

      -

      Disk space needed varies depending on the network on which you run nodes. A small test network might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive.

      +

      Disk space needed varies depending on the multicaster on which you run nodes. A small test multicaster might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive.

      Install Binary Distribution

      Installation from Binary Distribution

      Contents

      @@ -870,24 +870,24 @@

      Getting Started

    • Using the Private Network Quickstart

    To run a single node to connect the Ethereum mainnet or a public testnet, running from the Pantheon docker image or installing the packaged binaries is the fastest way to get started.

    -

    To run a private network on which you can make JSON-RPC requests and send transactions, or explore Pantheon and Ethereum networks, the Private Network Quickstart runs a private network of Pantheon nodes in a Docker container.

    +

    To run a private multicaster on which you can make JSON-RPC requests and send transactions, or explore Pantheon and Ethereum networks, the Private Network Quickstart runs a private multicaster of Pantheon nodes in a Docker container.

    Starting Pantheon

    Starting Pantheon

    Pantheon nodes can be used for varying purposes as described in the Overview. Nodes can connect to the Ethereum mainnet, public testnets such as Ropsten, or private networks.

    Prerequisites

    Pantheon Installed

    Local Block Data

    -

    When connecting to a network other than the network previously connected to, you must either delete the local block data or use the --datadir option to specify a different data directory.

    +

    When connecting to a multicaster other than the multicaster previously connected to, you must either delete the local block data or use the --datadir option to specify a different data directory.

    To delete the local block data, delete the database directory in the pantheon/build/distribution/pantheon-<version> directory.

    Genesis Configuration

    -

    Pantheon specifies the genesis configuration, and sets the network ID and bootnodes when connecting to mainnet, Rinkeby, and Ropsten.

    +

    Pantheon specifies the genesis configuration, and sets the multicaster ID and bootnodes when connecting to mainnet, Rinkeby, and Ropsten.

    When --dev-mode is specified, Pantheon uses the development mode genesis configuration.

    The genesis files defining the genesis configurations are in the Pantheon source files.

    To define a genesis configuration, create a genesis file (for example, genesis.json) and specify the file using the --genesis option.

    Confirm Node is Running

    If you have started Pantheon with the --rpc-enabled option, use cURL to call JSON-RPC API methods to confirm the node is running. For example:

      -
    • eth_chainId returns the chain ID of the network.

      +
    • eth_chainId returns the chain ID of the multicaster.

        curl -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' 127.0.0.1:8545
    • eth_syncing returns the starting, current, and highest block.

       curl -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' 127.0.0.1:8545

      For example, after connecting to mainnet eth_syncing will return something similar to:

      @@ -910,9 +910,9 @@

      Run a Node on Ropsten Testnet

      Note From v0.8.2, use the --ropsten option instead of the following options. For v0.8.1, use the following options.

      To run a node on Ropsten:

      -

      $ bin/pantheon --network-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      +

      $ bin/pantheon --multicaster-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      To run a node on Ropsten with the HTTP JSON-RPC service enabled and allow Remix to access the node:

      -

      $ bin/pantheon --rpc-enabled --rpc-cors-origins "http://remix.ethereum.org" --network-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      +

      $ bin/pantheon --rpc-enabled --rpc-cors-origins "http://remix.ethereum.org" --multicaster-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      Where <path> is the path to the /pantheon directory.

      Run a Node on Rinkeby Testnet

      To run a node on Rinkeby specifying a data directory:

      @@ -927,7 +927,7 @@

      Run a Node on Goerli Testnet

      Run a Node for Testing

      To run a node that mines blocks at a rate suitable for testing purposes:

      -

      $ bin/pantheon --dev-mode --network-id="2018" --bootnodes= --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-cors-origins "all" --ws-enabled --rpc-enabled

      +

      $ bin/pantheon --dev-mode --multicaster-id="2018" --bootnodes= --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-cors-origins "all" --ws-enabled --rpc-enabled

      Running Pantheon from Docker Image

      Run Pantheon from Docker Image

      A Docker image is provided to run a Pantheon node in a Docker container.

      @@ -971,7 +971,7 @@

      Run a Node on Ethereum Mainnet

      docker run -p 8545:8545 -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon>,target=/var/lib/pantheon pegasyseng/pantheon:latest --rpc-enabled

      Run a Node on Ropsten Testnet

      Save a local copy of the Ropsten genesis file.

      To run a node on Ropsten:

      -
      docker run -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon/ropsten>,target=/var/lib/pantheon --mount type=bind,source=/<path>/ropsten.json,target=/etc/pantheon/genesis.json pegasyseng/pantheon:latest --network-id=3 --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      Run a Node on Rinkeby Testnet

      +
      docker run -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon/ropsten>,target=/var/lib/pantheon --mount type=bind,source=/<path>/ropsten.json,target=/etc/pantheon/genesis.json pegasyseng/pantheon:latest --multicaster-id=3 --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      Run a Node on Rinkeby Testnet

      To run a node on Rinkeby:

      docker run -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon/rinkeby>,target=/var/lib/pantheon pegasyseng/pantheon:latest --rinkeby

      Run a Node for Testing

      To run a node that mines blocks at a rate suitable for testing purposes with WebSockets enabled:

      @@ -981,8 +981,8 @@

      Run a Node on Ethereum Mainnet

      docker stop <container-name>

      To delete a container:

      docker rm <container-name>

      To delete a container volume (optional):

      docker volume rm <volume-name>

      Private Network Quickstart

      -

      Private Network Quickstart Tutorial

      -

      This tutorial describes how to use Pantheon to run a private network of Pantheon nodes in a Docker container.

      +

      Private Network Quickstart Tutorial

      +

      This tutorial describes how to use Pantheon to run a private multicaster of Pantheon nodes in a Docker container.

      Note To run the Private Network Quickstart, you must install Pantheon by cloning and building.

      If you have installed Pantheon from the packaged binaries or are running the Docker image, you can proceed with Starting Pantheon.

      @@ -1001,9 +1001,9 @@

      Prerequisites

    Clone Pantheon Source Code

    As indicated in the installation section, clone the repository.

    -

    Build Docker Images and Start Services and Network

    +

    Build Docker Images and Start Services and Network

    This tutorial uses Docker Compose to simplify assembling images and -running in a private network. To run the containers, go to the pantheon directory and run the following commands:

    +running in a private multicaster. To run the containers, go to the pantheon directory and run the following commands:

    On Linux/Mac, run the following shell command provided with the code:

    @@ -1014,7 +1014,7 @@

    Build Docker Images quickstart\docker-compose up -d --scale node=4 // List the endpoints -quickstart\docker-compose port explorer 80

    This script builds Pantheon, builds the images and runs the containers. It will also scale the regular node container to four containers to simulate a network with enough peers to synchronize.

    +quickstart\docker-compose port explorer 80

    This script builds Pantheon, builds the images and runs the containers. It will also scale the regular node container to four containers to simulate a multicaster with enough peers to synchronize.

    When the process ends, it lists the running services:

            Name                       Command               State                              Ports                           
     -----------------------------------------------------------------------------------------------------------------------------
    @@ -1044,8 +1044,8 @@ 

    Build Docker Images
    quickstart\docker-compose port explorer 80

    Block Explorer

    This tutorial uses the Alethio light block explorer.

    Run the Block Explorer

    -

    Access the explorer by copying and pasting the Web block explorer address displayed when starting the private network to your browser.

    -

    The block explorer displays a summary of the private network:

    +

    Access the explorer by copying and pasting the Web block explorer address displayed when starting the private multicaster to your browser.

    +

    The block explorer displays a summary of the private multicaster:

    Notice the explorer indicates 6 peers: the 4 regular nodes, the mining node and the bootnode.

    Click on the block number to the right of Best Block to display the block details.

    @@ -1059,7 +1059,7 @@

    Run JSON-RPC Requests

    On Windows: We suggest using Postman or a similar client to make RPC requests from Windows. Using curl via Command Prompt or Windows PowerShell might not work.

    -

    This tutorial uses the placeholder http://localhost:http-rpc-port. When you run this tutorial, replace http-rpc-port with the JSON-RPC HTTP service endpoint provided when you list the endpoints. (For example, http://localhost:32770/jsonrpc.) The dynamic docker port mapping changes each time you run the network.

    +

    This tutorial uses the placeholder http://localhost:http-rpc-port. When you run this tutorial, replace http-rpc-port with the JSON-RPC HTTP service endpoint provided when you list the endpoints. (For example, http://localhost:32770/jsonrpc.) The dynamic docker port mapping changes each time you run the multicaster.

    Requesting the Node Version

    Run the following command from the host shell :

    curl -X POST --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' http://localhost:http-rpc-port

    The result should be as follows:

    @@ -1101,7 +1101,7 @@

    Checking the Miner Account

    Additional Requests

    Now that you are familiar with basic RPC requests you can run JSON-RPC commands to send transactions. In order to send transactions, you will first need to create an account or use one of the 3 accounts -created during the genesis of this test network.

    +created during the genesis of this test multicaster.

    Account 1

    This is the mining node coinbase account:

      @@ -1125,12 +1125,12 @@

      Account 3

      Note Pantheon does not provide an accounts management system, so if you want to create your own account, you will have to use a third party tool like Metamask.

      Creating a Transaction Using MetaMask

      -

      After you sign in to MetaMask, connect to the private network RPC endpoint by:

      +

      After you sign in to MetaMask, connect to the private multicaster RPC endpoint by:

        -
      1. In the MetaMask network list, select Custom RPC.
      2. -
      3. In the New RPC URL field, enter the JSON-RPC HTTP service endpoint displayed when you started the private network.
      4. +
      5. In the MetaMask multicaster list, select Custom RPC.
      6. +
      7. In the New RPC URL field, enter the JSON-RPC HTTP service endpoint displayed when you started the private multicaster.
      -

      Save the configuration and return to the MetaMask main screen. Your current network is now set to the private network RPC node.

      +

      Save the configuration and return to the MetaMask main screen. Your current multicaster is now set to the private multicaster RPC node.

      Import one of the existing accounts above into metamask using the corresponding private key.

      NOTE that here we don't really care about securing the keys as it's just a tutorial, but be sure @@ -1139,14 +1139,14 @@

      Creating a Transaction Using Meta

      Once this is done, try to create another account from scratch to send some ether to.

      Of course remember that here we are dealing with valueless ether as we are not -on the main network but on a local private network.

      +on the main multicaster but on a local private multicaster.

      In MetaMask, select the new account and copy the account address by clicking the ... button and selecting Copy Address to clipboard.

      In the block explorer, search for the new account by clicking on the magnifying glass and pasting the account address into the search box. The account is displayed with a zero balance.

      Send some ether from the first account (containing some ether) to the new one (that have a zero balance).

      Click refresh on the browser page displaying the new account. The updated balance is displayed and reflects the transaction completed using MetaMask.

      Truffle Pet Shop Tutorial

      -

      With a couple of modifications, we can use the private network in this tutorial as the blockchain for the PetShop tutorial on Truffle website.

      +

      With a couple of modifications, we can use the private multicaster in this tutorial as the blockchain for the PetShop tutorial on Truffle website.

      Prerequisites

      • Node.js v6+ LTS and npm (comes with Node)
      • @@ -1177,7 +1177,7 @@

        Modify the Pet Shop Example

        development: { host: "127.0.0.1", port: 7545, - network_id: "*" // Match any network id + network_id: "*" // Match any multicaster id }, quickstartWallet: { provider: privateKeyProvider, @@ -1187,30 +1187,30 @@

        Modify the Pet Shop Example

        };

    Replace <YOUR HTTP RPC NODE ENDPOINT> with your HTTP RPC node endpoint (for example, http://localhost:32770/jsonrpc).

    The private key is the miner address which means it will have funds.

    Once this is done, you can continue with the regular tutorial steps on the Truffle website until Step 3 in the Migration section.

    -

    Use Pantheon Private Network Instead of Genache

    -

    We are going to use our private network instead of Genache, so skip steps 3, 4, and 5 in the Migration section.

    -

    In step 4, specify our private network:

    -
    truffle migrate --network quickstartWallet

    Output similar to the following is displayed (your addresses will differ):

    -
    Using network 'quickstartWallet'.
    +

    Use Pantheon Private Network Instead of Genache

    +

    We are going to use our private multicaster instead of Genache, so skip steps 3, 4, and 5 in the Migration section.

    +

    In step 4, specify our private multicaster:

    +
    truffle migrate --multicaster quickstartWallet

    Output similar to the following is displayed (your addresses will differ):

    +
    Using multicaster 'quickstartWallet'.
     
     Running migration: 1_initial_migration.js
       Deploying Migrations...
       ... 0xfc1dbc1eaa14fa283c2c4415364579da0d195b3f2f2fefd7e0edb600a6235bdb
       Migrations: 0x9a3dbca554e9f6b9257aaa24010da8377c57c17e
    -Saving successful migration to network...
    +Saving successful migration to multicaster...
       ... 0x77cc6e9966b886fb74268f118b3ff44cf973d32b616ed4f050b3eabf0a31a30e
     Saving artifacts...
     Running migration: 2_deploy_contracts.js
       Deploying Adoption...
       ... 0x5035fe3ea7dab1d81482acc1259450b8bf8fefecfbe1749212aca86dc765660a
       Adoption: 0x2e1f232a9439c3d459fceca0beef13acc8259dd8
    -Saving successful migration to network...
    +Saving successful migration to multicaster...
       ... 0xa7b5a36e0ebc9c25445ce29ff1339a19082d0dda516e5b72c06ee6b99a901ec0
     Saving artifacts...

    Search for the deployed contracts and transactions in the block explorer using the addresses displayed in your output.

    Continue with the regular tutorial steps in the Testing the smart contract section.

    -

    To run the tests in the Running the tests section, specify our private network:

    -
    truffle test --network quickstartWallet

    Output similar to the following is displayed:

    -
    Using network 'quickstartWallet'.
    +

    To run the tests in the Running the tests section, specify our private multicaster:

    +
    truffle test --multicaster quickstartWallet

    Output similar to the following is displayed:

    +
    Using multicaster 'quickstartWallet'.
     
     Compiling ./contracts/Adoption.sol...
     Compiling ./test/TestAdoption.sol...
    @@ -1225,11 +1225,11 @@ 

    Use Pantheon Private Ne 3 passing (37s)

    Continue with the regular tutorial steps in the Creating a user interface to interact with the smart contract section.

    -

    We have already connected our private network to MetaMask so you can skip the Installing and configuring MetaMask section.

    +

    We have already connected our private multicaster to MetaMask so you can skip the Installing and configuring MetaMask section.

    Continue with the regular tutorial steps from the Installing and configuring lite-server section to the end of the tutorial.

    When you adopt pets in the browser and approve the transaction in MetaMask, you will be able to see the transactions in the block explorer.

    -

    Shut Down the Network and Remove the Containers

    -

    To shut down the network and delete all containers:

    +

    Shut Down the Network and Remove the Containers

    +

    To shut down the multicaster and delete all containers:

    On Linux/Mac, run the following shell command:

    @@ -1239,8 +1239,8 @@

    Shut Down the Network a
    quickstart\docker-compose down

    Note On Windows, the quickstart creates a volume called quickstart_public-keys. Remove this volume using docker volume rm quickstart_public-keys.

    -

    Stop and restart the Private Network without Removing the Containers

    -

    To shut down the network without deleting the containers:

    +

    Stop and restart the Private Network without Removing the Containers

    +

    To shut down the multicaster without deleting the containers:

    On Linux/Mac, run the following shell command:

    @@ -1248,7 +1248,7 @@

    St

    On Windows, run the following docker command:

    docker-compose stop

    (This command will also stop other running containers unrelated to quickstart.)

    -

    To restart the private network:

    +

    To restart the private multicaster:

    On Linux/Mac, run the following shell command:

    quickstart/resumePantheonPrivateNetwork.sh
    @@ -1256,11 +1256,11 @@

    St

    On Windows, run the following docker command:

    docker-compose start

    Network ID and Chain ID

    -

    Network ID and Chain ID

    -

    Ethereum networks have a network ID and a chain ID. The network ID is specified using the --network-id option and the chain ID is specified in the genesis file.

    -

    For most networks including mainnet and the public testnets, the network ID and the chain ID are the same.

    -

    The network ID is automatically set by Pantheon when connecting to the Ethereum mainnet (1), Rinkeby (4), and Ropsten(3).

    -

    When using the --dev-mode or --genesis options, specify the network ID using the --network-id option.

    +

    Network ID and Chain ID

    +

    Ethereum networks have a multicaster ID and a chain ID. The multicaster ID is specified using the --multicaster-id option and the chain ID is specified in the genesis file.

    +

    For most networks including mainnet and the public testnets, the multicaster ID and the chain ID are the same.

    +

    The multicaster ID is automatically set by Pantheon when connecting to the Ethereum mainnet (1), Rinkeby (4), and Ropsten(3).

    +

    When using the --dev-mode or --genesis options, specify the multicaster ID using the --multicaster-id option.

    Node Keys

    Node Keys

    Each node has a node key pair consisting of a node private key and node public key.

    @@ -1280,7 +1280,7 @@

    Specifying a Custom Node Priv

    bin/pantheon --node-private-key "/Users/username/privatekeyfile"

    Networking

    Networking

    -

    Pantheon uses the network to find and connect to peers.

    +

    Pantheon uses the multicaster to find and connect to peers.

    Firewalls and Incoming Connections

    The default logging configuration does not list node connection and disconnection messages.

    To enable listing of node connection and disconnection messages, specify the command line option --logging=DEBUG. For more verbosity, specify --logging=TRACE.

    @@ -1294,17 +1294,17 @@

    Limiting Peers

    Use the --max-trailing-peers option to reduce the maximum P2P peer connections for peers that are trailing behind the local chain head. The default is unlimited but the number of trailing peers cannot exceed the value specified by --max-peers.

    Trailing peers cannot be used to get new blocks and are more likely to be requesting blocks from you. Limiting trailing peers may reduce the time taken to catch up to the chain head when synchronizing.

    No Discovery

    -

    The --no-discovery command line option disables P2P peer discovery. Only use this option if you are running a test node or a test network with fixed nodes.

    +

    The --no-discovery command line option disables P2P peer discovery. Only use this option if you are running a test node or a test multicaster with fixed nodes.

    Monitoring Peer Connections

    Use the debug_metrics JSON-RPC API method to obtain information about peer connections.

    Accounts for Testing

    Accounts for Testing

    -

    You can use existing accounts for testing by including them in the genesis file for a private network. Alternatively, Pantheon provides predefined accounts in development mode.

    +

    You can use existing accounts for testing by including them in the genesis file for a private multicaster. Alternatively, Pantheon provides predefined accounts in development mode.

    Development Mode

    When you start Pantheon with the --dev-mode command line option, the dev.json genesis file is used by default.

    The dev.json genesis file defines the accounts below that can be used for testing.

    -

    Warning Do not use the following accounts on mainnet or any public network except for testing. The private keys are displayed here so the accounts are not secure.

    +

    Warning Do not use the following accounts on mainnet or any public multicaster except for testing. The private keys are displayed here so the accounts are not secure.

    Account 1 (Miner Coinbase Account)
      @@ -1326,7 +1326,7 @@
      Account 3

      Genesis File

    -

    To use existing test accounts, specify the accounts and balances in a genesis file for your test network. For an example of defining accounts in the genesis file, refer to dev.json.

    +

    To use existing test accounts, specify the accounts and balances in a genesis file for your test multicaster. For an example of defining accounts in the genesis file, refer to dev.json.

    Use the --genesis command line option to start Pantheon with the genesis file defining the existing accounts.

    Logging

    Logging

    @@ -1367,7 +1367,7 @@

    Bootnodes

    Mainnet and Public Testnets

    For mainnet and Rinkeby, Pantheon predefines a list of enonde URLs. For Ropsten, bootnodes are specified using the --bootnodes option.

    Private Networks

    -

    To start a bootnode for a private network:

    +

    To start a bootnode for a private multicaster:

    1. Export the public key to a file:

      pantheon export-pub-key bootnode

      The node public key is exported to the bootnode file.

      @@ -1375,8 +1375,8 @@

      Private Networks

    2. Start the bootnode, specifying:

      • An empty string for the --bootnodes option because this is the bootnode.
      • -
      • The network ID for your private network.

        -
        pantheon --bootnodes="" --network-id 123 
      • +
      • The multicaster ID for your private multicaster.

        +
        pantheon --bootnodes="" --multicaster-id 123 
    @@ -1392,12 +1392,12 @@

    Private Networks

    Note The default host and port for P2P peer discovery is 127.0.0.1:30303. The --p2p-listen option can be used to specify a host and port.

    To start a node specifying the bootnode for P2P discovery:

    -
    pantheon --datadir=/tmp/pantheon/30301 --p2p-listen=127.0.0.1:30301 --network-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303

    Proof of Authority

    +
    pantheon --datadir=/tmp/pantheon/30301 --p2p-listen=127.0.0.1:30301 --multicaster-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303

    Proof of Authority

    Proof of Authority

    Pantheon implements the Clique Proof-of-Authority (PoA) consensus protocol. Clique is used by the Rinkeby testnet and can be used for private networks.

    In PoA networks, transactions and blocks are validated by approved accounts, known as signers. Signers take turns to create the next block. Existing signers propose and vote to add or remove signers.

    Genesis File

    -

    To use Clique in a private network, Pantheon requires a PoA genesis file. When connecting to Rinkeby, Pantheon uses the rinkeby.json genesis file in the /pantheon/ethereum/core/src/main/resources directory.

    +

    To use Clique in a private multicaster, Pantheon requires a PoA genesis file. When connecting to Rinkeby, Pantheon uses the rinkeby.json genesis file in the /pantheon/ethereum/core/src/main/resources directory.

    A PoA genesis file defines properties specific to Clique:

    {
       "config": {
    @@ -1416,7 +1416,7 @@ 

    Genesis File

  • epoch - Number of blocks after which to reset all votes.
  • extraData - Initial signers are specified after the 32 bytes reserved for vanity data.
  • -

    To connect to the Rinkeby testnet, start Pantheon with the --rinkeby option. To start a node on a PoA private network, use the --network-id and --genesis options.

    +

    To connect to the Rinkeby testnet, start Pantheon with the --rinkeby option. To start a node on a PoA private multicaster, use the --multicaster-id and --genesis options.

    Adding and Removing Signers

    To propose adding or removing signers using the JSON-RPC methods, you must enable the RPC interface using the --rpc-enabled option. If also using the --rpc-api option, include CLIQUE.

    The JSON-RPC methods to add or remove signers are:

    @@ -1460,7 +1460,7 @@

    Transactions

    Note: Node.js must be installed to run JS scripts.

    -

    The example JS scripts can be used to create raw transactions to send in the private network created by the Private Network Quickstart. The JSON-RPC endpoint in the examples must be updated to the endpoint for the private network displayed after running the quickstart/runPantheonPrivateNetwork.sh script.

    +

    The example JS scripts can be used to create raw transactions to send in the private multicaster created by the Private Network Quickstart. The JSON-RPC endpoint in the examples must be updated to the endpoint for the private multicaster displayed after running the quickstart/runPantheonPrivateNetwork.sh script.

    To create and display the transaction string, run the JS script. For example:

    node create_signed_raw_transaction.js

    To send a signed transaction, run:

    @@ -1559,7 +1559,7 @@

    Sending Ether

    Invokes contract function locally -Broadcasts to network +Broadcasts to multicaster Does not change state of blockchain @@ -1791,7 +1791,7 @@

    Options

    List of comma-separated enode URLs for P2P discovery bootstrap.
    -
    When connecting to mainnet and Rinkeby, the default is a predefined list of enode URLs. Specify bootnodes when connecting to Ropsten or a private network.
    +
    When connecting to mainnet and Rinkeby, the default is a predefined list of enode URLs. Specify bootnodes when connecting to Ropsten or a private multicaster.

    --config=<PATH>
    The path to the TOML configuration file. The default is none. The TOML file is composed of key/value pairs. Each key is the same as the corresponding CLI option name without the leading dashes (--). The config option is not used in the config file. Values must be treated according to TOML specifications for string, numbers, arrays and Booleans. @@ -1824,12 +1824,12 @@

    Options


    --dev-mode
    - Set this option to true to run in development mode. For example, specify this option to perform CPU mining more easily in a private test network. In development mode, a custom genesis configuration specifies the chain ID. When using this option, also set the --network-id option to the network you use for development. Default is false. + Set this option to true to run in development mode. For example, specify this option to perform CPU mining more easily in a private test multicaster. In development mode, a custom genesis configuration specifies the chain ID. When using this option, also set the --multicaster-id option to the multicaster you use for development. Default is false.
    > Note: The --dev-mode option overrides the --genesis option. If both are specified, the development mode configuration is used.

    --genesis=<PATH>
    -
    The path to the genesis file. The default is the embedded genesis file for the Ethereum mainnet. When using this option, it is recommended to also set the --network-id option.
    +
    The path to the genesis file. The default is the embedded genesis file for the Ethereum mainnet. When using this option, it is recommended to also set the --multicaster-id option.
    > Note: This option is not used when running Pantheon from the Docker image.
    @@ -1838,7 +1838,7 @@

    Options


    --goerli
    -
    Uses the Goerli test network. Default is false.
    +
    Uses the Goerli test multicaster. Default is false.
    > Note: This option is only available from v0.8.3.

    @@ -1874,8 +1874,8 @@

    Options

    The minimum price that a transaction offers for it to be included in a mined block The default is 1000.

    -
    --network-id=<INTEGER>
    -
    P2P network identifier. The default is set to mainnet with value 1.
    +
    --multicaster-id=<INTEGER>
    +
    P2P multicaster identifier. The default is set to mainnet with value 1.

    --no-discovery
    @@ -1901,7 +1901,7 @@

    Options


    --ottoman
    -
    Synchronize against the Ottoman test network. This is only useful if you are using an IBFT genesis file. The default is false.
    +
    Synchronize against the Ottoman test multicaster. This is only useful if you are using an IBFT genesis file. The default is false.
    > Note: IBFT is not currently supported. Support for IBFT is in active development.
    @@ -1911,12 +1911,12 @@

    Options

    > Note This option is not used when running Pantheon from the Docker image.
    --rinkeby
    -
    Uses the Rinkeby test network. Default is false.
    +
    Uses the Rinkeby test multicaster. Default is false.


    --ropsten
    -
    Uses the Ropsten test network. Default is false.
    +
    Uses the Ropsten test multicaster. Default is false.
    > Note This option is only available only from v0.8.2. For v0.8.1, refer here.
    @@ -2144,7 +2144,7 @@
    Returns
  • version - P2P protocol version
  • name - Client name
  • caps - P2P message capabilities
  • -
  • network - Addresses of local node and remote node
  • +
  • multicaster - Addresses of local node and remote node
  • port - Port on remote node on which P2P peer discovery is listening
  • id - Node public key. Excluding the 0x prefix, the node public key is the ID in the enode URL enode://<id ex 0x>@<host:port>.
  • @@ -2165,7 +2165,7 @@
    Request
    "par/3", "pip/1" ], - "network": { + "multicaster": { "localAddress": "192.168.1.229:50115", "remoteAddress": "168.61.153.255:40303" }, @@ -2227,11 +2227,11 @@
    Request
    "result" : "3" }

    net_listening

    -

    Indicates whether the client is actively listening for network connections.

    +

    Indicates whether the client is actively listening for multicaster connections.

    Parameters

    None

    Returns
    -

    result (BOOLEAN) - true if the client is actively listening for network connections; otherwise false.

    +

    result (BOOLEAN) - true if the client is actively listening for multicaster connections; otherwise false.

    Request
    curl -X POST --data '{"jsonrpc":"2.0","method":"net_listening","params":[],"id":53}' <JSON-RPC-endpoint:port>
    Result
    {
    @@ -2259,13 +2259,13 @@ 
    Parameters
    Returns

    result : Object|Boolean - Object with synchronization status data or false, when not synchronizing:

      -
    • startingBlock : quantity - Index of the highest block on the blockchain when the network synchronization starts.

      +
    • startingBlock : quantity - Index of the highest block on the blockchain when the multicaster synchronization starts.

      If you start with an empty blockchain, the starting block is the beginning of the blockchain (startingBlock = 0).

      If you import a block file using pantheon import <block-file>, the synchronization starts at the head of the blockchain, and the starting block is the next block synchronized. For example, if you imported 1000 blocks, the import would include blocks 0 to 999, so in that case startingBlock = 1000.

    • currentBlock : quantity - Index of the latest block (also known as the best block) for the current node. This is the same index that eth_blockNumber returns.

    • -
    • highestBlock: quantity - Index of the highest known block in the peer network (that is, the highest block so far discovered among peer nodes). This is the same value as currentBlock if the current node has no peers.

      +
    • highestBlock: quantity - Index of the highest known block in the peer multicaster (that is, the highest block so far discovered among peer nodes). This is the same value as currentBlock if the current node has no peers.

    Request
    @@ -2309,7 +2309,7 @@
    Request
    "result" : "0xdd37f65db31c107f773e82a4f85c693058fef7a9" }

    eth_mining

    -

    Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the network regardless of command settings or methods called.

    +

    Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the multicaster regardless of command settings or methods called.

    Parameters

    None

    Returns
    diff --git a/docs/Getting-Started/Getting-Started.md b/docs/Getting-Started/Getting-Started.md index a403e1751b..6110728359 100644 --- a/docs/Getting-Started/Getting-Started.md +++ b/docs/Getting-Started/Getting-Started.md @@ -11,4 +11,4 @@ You can get started with Pantheon by: To run a single node to connect the Ethereum mainnet or a public testnet, running from the Pantheon [Docker image](Run-Docker-Image.md) or [installing the packaged binaries](../Installation/Install-Binaries.md) is the fastest way to get started. -To run a private network on which you can make JSON-RPC requests and send transactions, or explore Pantheon and a private Ethereum network, the [Private Network Quickstart](../Tutorials/Private-Network-Quickstart.md) runs a private network of Pantheon nodes in Docker containers. \ No newline at end of file +To run a private multicaster on which you can make JSON-RPC requests and send transactions, or explore Pantheon and a private Ethereum multicaster, the [Private Network Quickstart](../Tutorials/Private-Network-Quickstart.md) runs a private multicaster of Pantheon nodes in Docker containers. \ No newline at end of file diff --git a/docs/Getting-Started/Run-Docker-Image.md b/docs/Getting-Started/Run-Docker-Image.md index e4acdf8087..feb68b63f6 100644 --- a/docs/Getting-Started/Run-Docker-Image.md +++ b/docs/Getting-Started/Run-Docker-Image.md @@ -140,21 +140,21 @@ Save a local copy of the [Ropsten genesis file](https://github.com/PegaSysEng/pa To run a node on Ropsten: ```bash -docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon --network=ropsten +docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon --multicaster=ropsten ``` ## Run a Node on Rinkeby Testnet To run a node on Rinkeby: ```bash -docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --network=rinkeby +docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --multicaster=rinkeby ``` ## Run a Node for Testing To run a node that mines blocks at a rate suitable for testing purposes with WebSockets enabled: ```bash -docker run -p 8546:8546 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins "all" --rpc-ws-enabled --network=dev +docker run -p 8546:8546 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins "all" --rpc-ws-enabled --multicaster=dev ``` ## Stopping Pantheon and Cleaning up Resources diff --git a/docs/Getting-Started/Starting-Pantheon.md b/docs/Getting-Started/Starting-Pantheon.md index a61e88385b..4cbcc41ee7 100644 --- a/docs/Getting-Started/Starting-Pantheon.md +++ b/docs/Getting-Started/Starting-Pantheon.md @@ -18,19 +18,19 @@ Nodes can connect to the Ethereum mainnet, public testnets such as Ropsten, or p ## Local Block Data -When connecting to a network other than the network previously connected to, you must either delete the local block data +When connecting to a multicaster other than the multicaster previously connected to, you must either delete the local block data or use the [`--data-path`](../Reference/Pantheon-CLI-Syntax.md#data-path) option to specify a different data directory. To delete the local block data, delete the `database` directory in the `pantheon/build/distribution/pantheon-` directory. ## Genesis Configuration -Pantheon specifies the genesis configuration, and sets the network ID and bootnodes when connecting +Pantheon specifies the genesis configuration, and sets the multicaster ID and bootnodes when connecting to [Mainnet](#run-a-node-on-ethereum-mainnet), [Goerli](#run-a-node-on-goerli-testnet), [Rinkeby](#run-a-node-on-rinkeby-testnet), and [Ropsten](#run-a-node-on-ropsten-testnet). -When [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) is specified, Pantheon uses the +When [`--multicaster=dev`](../Reference/Pantheon-CLI-Syntax.md#multicaster) is specified, Pantheon uses the development mode genesis configuration associated to a low difficulty. -The default bootnodes setting for dev network is to have an empty bootnodes list. +The default bootnodes setting for dev multicaster is to have an empty bootnodes list. The genesis files defining the genesis configurations are in the [Pantheon source files](https://github.com/PegaSysEng/pantheon/tree/master/config/src/main/resources). @@ -44,7 +44,7 @@ call [JSON-RPC API methods](../Reference/JSON-RPC-API-Methods.md) to confirm the !!!example - * `eth_chainId` returns the chain ID of the network. + * `eth_chainId` returns the chain ID of the multicaster. ```bash $ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' 127.0.0.1:8545 @@ -75,13 +75,13 @@ call [JSON-RPC API methods](../Reference/JSON-RPC-API-Methods.md) to confirm the To run a node that mines blocks at a rate suitable for testing purposes: ```bash -pantheon --network=dev --miner-enabled --miner-coinbase=0xfe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins="all" --host-whitelist="all" --rpc-rpc-ws-enabled --rpc-http-enabled --data-path=/tmp/tmpDatdir +pantheon --multicaster=dev --miner-enabled --miner-coinbase=0xfe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins="all" --host-whitelist="all" --rpc-rpc-ws-enabled --rpc-http-enabled --data-path=/tmp/tmpDatdir ``` Alternatively, use the following [configuration file](../Configuring-Pantheon/Using-Configuration-File.md) on the command line to start a node with the same options as above: ```toml -network="dev" +multicaster="dev" miner-enabled=true miner-coinbase="0xfe3b557e8fb62b89f4916b721be55ceb828dbd73" rpc-cors-origins=["all"] @@ -96,13 +96,13 @@ data-path="/tmp/tmpdata-path" To run a node on Ropsten: ```bash -pantheon --network=ropsten +pantheon --multicaster=ropsten ``` To run a node on Ropsten with the HTTP JSON-RPC service enabled and allow Remix to access the node: ```bash -pantheon --network=ropsten --rpc-http-enabled --rpc-http-cors-origins "http://remix.ethereum.org" +pantheon --multicaster=ropsten --rpc-http-enabled --rpc-http-cors-origins "http://remix.ethereum.org" ``` ## Run a Node on Rinkeby Testnet @@ -110,7 +110,7 @@ pantheon --network=ropsten --rpc-http-enabled --rpc-http-cors-origins "http://r To run a node on Rinkeby specifying a data directory: ```bash -pantheon --network=rinkeby --data-path=/ +pantheon --multicaster=rinkeby --data-path=/ ``` Where `` and `` are the path and directory where the Rinkeby chain data is to be saved. @@ -119,7 +119,7 @@ Where `` and `` are the path and directory where the Rin To run a node on [Goerli](https://github.com/goerli/testnet) specifying a data directory: ```bash -pantheon --network=goerli --data-path=/ +pantheon --multicaster=goerli --data-path=/ ``` Where `` and `` are the path and directory where the Goerli chain data is to be saved. diff --git a/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md b/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md index 2bee5b52fa..80a77a99f2 100644 --- a/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md +++ b/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md @@ -1,10 +1,10 @@ -description: Pantheon private network quickstart on Azure tutorial +description: Pantheon private multicaster quickstart on Azure tutorial # Azure Private Network Quickstart tutorial This tutorial describes how to use the [Pantheon quickstart](https://github.com/PegaSysEng/pantheon-quickstart) -to run a private network of Pantheon nodes in a Docker container in a Linux Virtual +to run a private multicaster of Pantheon nodes in a Docker container in a Linux Virtual machine hosted on Microsoft Azure. ## Requirements @@ -51,7 +51,7 @@ Then go up on the top of the page and switch to the **Guest config** step tab. ### Guest config This step aims at installing required software (Docker and Docker-compose) on your virtual machine -and retrieve and run the quickstart private network. +and retrieve and run the quickstart private multicaster. To do so, click on the link named **Select an extension to install** and a new resource pane will appear on the right. @@ -135,7 +135,7 @@ then click on the **Delete resource group** button. Then simply navigate in the Azure portal to your resource group, the one we named **pantheon-quickstart** then click on the virtual machine resource and click the **Delete** button. -### I want to keep the VM but remove the nodes network. +### I want to keep the VM but remove the nodes multicaster. Navigate to the VM in your Azure portal (click on **All services** in the left pane, then on **Virtual machines** and click on the one you named **quickstart**) and click the **connect** button that will give you the information to connect with SSH (see [Requirements](#requirements)). @@ -149,20 +149,20 @@ then run the remove script ./remove.sh ``` -If you want to run the network again, then you can use the following script: +If you want to run the multicaster again, then you can use the following script: ```bash ./run.sh -p 80 ``` Where 80 is the port number to use for Block Explorer and RPC connections. -### I just want to stop the nodes network and be able to resume it. -Connect to the VM using SSH like for "[I want to keep the VM but remove the nodes network.](#i-want-to-keep-the-vm-but-remove-the-nodes-network)" +### I just want to stop the nodes multicaster and be able to resume it. +Connect to the VM using SSH like for "[I want to keep the VM but remove the nodes multicaster.](#i-want-to-keep-the-vm-but-remove-the-nodes-multicaster)" but instead of running the remove script, run the stop script. ```bash ./stop.sh ``` -you will be then able to resume the network with +you will be then able to resume the multicaster with ```bash ./resume.sh ``` \ No newline at end of file diff --git a/docs/Installation/Overview.md b/docs/Installation/Overview.md index 66294050a8..850a1d6fa9 100644 --- a/docs/Installation/Overview.md +++ b/docs/Installation/Overview.md @@ -15,4 +15,4 @@ You can install Pantheon by: ## Disk Space and RAM Requirements Your computer should have at least 4 GB RAM. -Disk space needed varies depending on the network on which you run nodes. A small test network might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive. +Disk space needed varies depending on the multicaster on which you run nodes. A small test multicaster might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive. diff --git a/docs/Reference/JSON-RPC-API-Methods.md b/docs/Reference/JSON-RPC-API-Methods.md index ada4303f65..ac222b6fb5 100644 --- a/docs/Reference/JSON-RPC-API-Methods.md +++ b/docs/Reference/JSON-RPC-API-Methods.md @@ -30,7 +30,7 @@ Properties of the remote node object are: * `version` - P2P protocol version * `name` - Client name * `caps` - List of Ethereum sub-protocol capabilities -* `network` - Addresses of local node and remote node +* `multicaster` - Addresses of local node and remote node * `port` - Port on the remote node on which P2P peer discovery is listening * `id` - Node public key. Excluding the `0x` prefix, the node public key is the ID in the enode URL `enode://@:`. @@ -59,7 +59,7 @@ Properties of the remote node object are: "par/3", "pip/1" ], - "network": { + "multicaster": { "localAddress": "192.168.1.229:50115", "remoteAddress": "168.61.153.255:40303" }, @@ -178,7 +178,7 @@ None ### net_listening -Indicates whether the client is actively listening for network connections. +Indicates whether the client is actively listening for multicaster connections. **Parameters** @@ -186,7 +186,7 @@ None **Returns** -`result` (*BOOLEAN*) - `true` if the client is actively listening for network connections; otherwise `false`. +`result` (*BOOLEAN*) - `true` if the client is actively listening for multicaster connections; otherwise `false`. !!! example ```bash tab="curl HTTP request" @@ -248,7 +248,7 @@ None `result` : *Object|Boolean* - Object with synchronization status data or `false`, when not synchronizing: -* `startingBlock` : *quantity* - Index of the highest block on the blockchain when the network synchronization starts. +* `startingBlock` : *quantity* - Index of the highest block on the blockchain when the multicaster synchronization starts. If you start with an empty blockchain, the starting block is the beginning of the blockchain (`startingBlock` = 0). @@ -256,7 +256,7 @@ None * `currentBlock` : *quantity* - Index of the latest block (also known as the best block) for the current node. This is the same index that [eth_blockNumber](#eth_blocknumber) returns. -* `highestBlock`: *quantity* - Index of the highest known block in the peer network (that is, the highest block so far discovered among peer nodes). This is the same value as `currentBlock` if the current node has no peers. +* `highestBlock`: *quantity* - Index of the highest known block in the peer multicaster (that is, the highest block so far discovered among peer nodes). This is the same value as `currentBlock` if the current node has no peers. !!! example ```bash tab="curl HTTP request" @@ -347,7 +347,7 @@ None ### eth_mining -Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the network regardless of command settings or methods called. +Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the multicaster regardless of command settings or methods called. **Parameters** @@ -852,7 +852,7 @@ The following example returns an estimate of 21000 wei (0x5208) for the transact } ``` -The following example request estimates the cost of deploying a simple storage smart contract to the network. The data field +The following example request estimates the cost of deploying a simple storage smart contract to the multicaster. The data field contains the hash of the compiled contract to be deployed. (You can obtain the compiled contract hash from your IDE; for example, **Remix > Compile tab > details > WEB3DEPLOY**.) The result is 113355 wei. diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index 70cf4098a0..2058dcc75d 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -81,7 +81,7 @@ When connecting to MainNet or public testnets, the default is a predefined list On custom networks defined by [`--genesis-file`](#genesis-file) option, an empty list of bootnodes is defined by default unless you define custom bootnodes as described in -[private network documentation](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes). +[private multicaster documentation](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes). !!! note Specifying that a node is a [bootnode](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes) @@ -138,11 +138,11 @@ The default is `true`. ### genesis-file -Genesis file is used to create a custom network. +Genesis file is used to create a custom multicaster. !!!tip - To use a public Ethereum network such as Rinkeby, use the [`--network`](#network) option. - The network option defines the genesis file for public networks. + To use a public Ethereum multicaster such as Rinkeby, use the [`--multicaster`](#multicaster) option. + The multicaster option defines the genesis file for public networks. ```bash tab="Syntax" --genesis-file= @@ -159,7 +159,7 @@ genesis-file="/home/me/me_node/customGenesisFile.json" The path to the genesis file. !!!important - The [`--genesis-file`](#genesis-file) and [`--network`](#network) option can't be used at the same time. + The [`--genesis-file`](#genesis-file) and [`--multicaster`](#multicaster) option can't be used at the same time. !!!note This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#custom-genesis-file). @@ -336,64 +336,64 @@ min-gas-price="1337" The minimum price that a transaction offers for it to be included in a mined block. The default is 1000. -### network +### multicaster ```bash tab="Syntax" ---network= +--multicaster= ``` ```bash tab="Example Command Line" ---network=rinkeby +--multicaster=rinkeby ``` ```bash tab="Example Configuration File" -network="rinkeby" +multicaster="rinkeby" ``` -Predefined network configuration. +Predefined multicaster configuration. The default is `mainnet`. Possible values are : `mainnet` -: Main Ethereum network +: Main Ethereum multicaster `ropsten` -: PoW test network similar to current main Ethereum network. +: PoW test multicaster similar to current main Ethereum multicaster. `rinkeby` -: PoA test network using Clique. +: PoA test multicaster using Clique. `goerli` -: PoA test network using Clique. +: PoA test multicaster using Clique. `dev` -: PoW development network with a very low difficulty to enable local CPU mining. +: PoW development multicaster with a very low difficulty to enable local CPU mining. !!!note Values are case insensitive, so either `mainnet` or `MAINNET` works. !!!important - The [`--network`](#network) and [`--genesis-file`](#genesis-file) option can't be used at the same time. + The [`--multicaster`](#multicaster) and [`--genesis-file`](#genesis-file) option can't be used at the same time. -### network-id +### multicaster-id ```bash tab="Syntax" ---network-id= +--multicaster-id= ``` ```bash tab="Example Command Line" ---network-id=8675309 +--multicaster-id=8675309 ``` ```bash tab="Example Configuration File" -network-id="8675309" +multicaster-id="8675309" ``` -P2P network identifier. +P2P multicaster identifier. -This option can be used to override your current network ID. -The default value is the current network chain ID which is defined in the genesis file. +This option can be used to override your current multicaster ID. +The default value is the current multicaster chain ID which is defined in the genesis file. ### node-private-key-file diff --git a/docs/Tutorials/Create-Private-Network.md b/docs/Tutorials/Create-Private-Network.md index 40b00afc30..89bd2bfa26 100644 --- a/docs/Tutorials/Create-Private-Network.md +++ b/docs/Tutorials/Create-Private-Network.md @@ -1,13 +1,13 @@ # Creating a Private Network -A private network provides a configurable network for testing. By configuring a low difficulty and enabling +A private multicaster provides a configurable multicaster for testing. By configuring a low difficulty and enabling mining, blocks are created quickly. -You can test multi-block and multi-user scenarios on a private network before moving to one of the public testnets. +You can test multi-block and multi-user scenarios on a private multicaster before moving to one of the public testnets. !!!important - An Ethereum private network created as described here is isolated but not protected or secure. - We recommend running the private network behind a properly configured firewall. + An Ethereum private multicaster created as described here is isolated but not protected or secure. + We recommend running the private multicaster behind a properly configured firewall. ## Prerequisites @@ -17,20 +17,20 @@ You can test multi-block and multi-user scenarios on a private network before mo ## Steps -To create a private network: +To create a private multicaster: 1. [Create Folders](#1-create-folders) 1. [Create Genesis File](#2-create-genesis-file) 1. [Get Public Key of First Node](#3-get-public-key-of-first-node) 1. [Start First Node as Bootnode](#4-restart-first-node-as-bootnode) 1. [Start Additional Nodes](#5-start-additional-nodes) -1. [Confirm Private Network Working](#6-confirm-private-network-working) +1. [Confirm Private Network Working](#6-confirm-private-multicaster-working) ### 1. Create Folders Each node requires a data directory for the blockchain data. When the node is started, the node key is saved in this directory. -Create directories for your private network, each of the three nodes, and a data directory for each node: +Create directories for your private multicaster, each of the three nodes, and a data directory for each node: ```bash Private-Network/ @@ -48,7 +48,7 @@ The genesis file defines the genesis block of the blockchain (that is, the start The genesis file includes entries for configuring the blockchain such as the mining difficulty and initial accounts and balances. -All nodes in a network must use the same genesis file. +All nodes in a multicaster must use the same genesis file. Copy the following genesis definition to a file called `privateNetworkGenesis.json` and save it in the `Private-Network` directory: @@ -77,13 +77,13 @@ Copy the following genesis definition to a file called `privateNetworkGenesis.js ``` !!! warning - Do not use the accounts in the genesis file above on mainnet or any public network except for testing. + Do not use the accounts in the genesis file above on mainnet or any public multicaster except for testing. The private keys are displayed so the accounts are not secure. ### 3. Get Public Key of First Node -To enable nodes to discover each other, a network requires one or more nodes to be bootnodes. -For this private network, we will use Node-1 as the bootnode. This requires obtaining the public key for the enode URL. +To enable nodes to discover each other, a multicaster requires one or more nodes to be bootnodes. +For this private multicaster, we will use Node-1 as the bootnode. This requires obtaining the public key for the enode URL. In the `Node-1` directory, use the [`export-pub-key` subcommand](../Reference/Pantheon-CLI-Syntax.md#export-pub-key) to write the [node public key](../Configuring-Pantheon/Node-Keys.md) to the specified file (`publicKeyNode1` in this example): @@ -117,11 +117,11 @@ and [`--miner-coinbase` options](../Reference/Pantheon-CLI-Syntax.md#miner-coinb * JSON-RPC API is enabled using the [`--rpc-http-enabled` option](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled). ```bash tab="MacOS" -pantheon --data-path=Node-1-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled +pantheon --data-path=Node-1-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes --multicaster-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled ``` ```bash tab="Windows" -pantheon --data-path=Node-1-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled +pantheon --data-path=Node-1-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes --multicaster-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled ``` !!! info @@ -147,28 +147,28 @@ Start another terminal, change to the `Node-2` directory and start Node-2 specif * Different port to Node-1 for P2P peer discovery using the [`--p2p-port` option](../Reference/Pantheon-CLI-Syntax.md#p2p-port). * Enode URL for Node-1 using the [`--bootnodes` option](../Reference/Pantheon-CLI-Syntax.md#bootnodes). * Data directory for Node-2 using the [`--data-path` option](../Reference/Pantheon-CLI-Syntax.md#data-path). -* Genesis file and network ID as for Node-1. +* Genesis file and multicaster ID as for Node-1. ```bash tab="MacOS" -pantheon --data-path=Node-2-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30304 +pantheon --data-path=Node-2-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port=30304 ``` ```bash tab="Windows" -pantheon --data-path=Node-2-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30304 +pantheon --data-path=Node-2-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port=30304 ``` Start another terminal, change to the `Node-3` directory and start Node-3 specifying: * Different port to Node-1 and Node-2 for P2P peer discovery. * Data directory for Node-3 using the [`--data-path` option](../Reference/Pantheon-CLI-Syntax.md#data-path). - * Bootnode, genesis file, and network ID as for Node-2. + * Bootnode, genesis file, and multicaster ID as for Node-2. ```bash tab="MacOS" -pantheon --data-path=Node-3-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port30305 +pantheon --data-path=Node-3-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port30305 ``` ```bash tab="Windows" -pantheon --data-path=Node-3-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30305 +pantheon --data-path=Node-3-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port=30305 ``` ### 6. Confirm Private Network is Working @@ -203,7 +203,7 @@ Start a node with the `--rpc-ws-enabled` option and use the [RPC Pub/Sub API](.. ## Stop Nodes -When finished using the private network, stop all nodes using ++ctrl+c++ in each terminal window. +When finished using the private multicaster, stop all nodes using ++ctrl+c++ in each terminal window. !!!tip - To restart the private network in the future, start from [4. Restart First Node as Bootnode](#4-restart-first-node-as-bootnode). \ No newline at end of file + To restart the private multicaster in the future, start from [4. Restart First Node as Bootnode](#4-restart-first-node-as-bootnode). \ No newline at end of file diff --git a/docs/Tutorials/Private-Network-Quickstart.md b/docs/Tutorials/Private-Network-Quickstart.md index f37e2c7bca..f211ac8dbd 100644 --- a/docs/Tutorials/Private-Network-Quickstart.md +++ b/docs/Tutorials/Private-Network-Quickstart.md @@ -1,4 +1,4 @@ -description: Pantheon private network quickstart tutorial +description: Pantheon private multicaster quickstart tutorial # Private Network Quickstart tutorial @@ -7,7 +7,7 @@ description: Pantheon private network quickstart tutorial In v0.9, the Private Network Quickstart moved to the `pantheon-quickstart` repository. The previous version was removed from the `pantheon` repository. -The Private Network Quickstart uses the Pantheon Docker image to run a private network of Pantheon nodes managed by Docker Compose. +The Private Network Quickstart uses the Pantheon Docker image to run a private multicaster of Pantheon nodes managed by Docker Compose. ## Prerequisites @@ -45,14 +45,14 @@ git clone --branch 0.8.5 https://github.com/PegaSysEng/pantheon-quickstart.git ## Build Docker Images and Start Services and Network This tutorial uses [Docker Compose](https://docs.docker.com/compose/) to assemble the images and -run the private network. To build the docker images and run the containers, go to the `pantheon-quickstart` directory and run: +run the private multicaster. To build the docker images and run the containers, go to the `pantheon-quickstart` directory and run: ```bash tab="Linux/MacOS" ./run.sh ``` The `run.sh` script builds the images, and runs the containers. It also scales the regular node -container to four containers to simulate a network with enough peers to synchronize. +container to four containers to simulate a multicaster with enough peers to synchronize. When the process ends, it lists the running services: @@ -99,10 +99,10 @@ This tutorial uses the [Alethio](https://aleth.io/) light block explorer. ### Run the Block Explorer -Access the explorer by copying and pasting the `Web block explorer address` displayed when starting the private network +Access the explorer by copying and pasting the `Web block explorer address` displayed when starting the private multicaster to your browser. -The block explorer displays a summary of the private network: +The block explorer displays a summary of the private multicaster: ![Block Explorer](../Getting-Started/ExplorerSummary.png) @@ -126,7 +126,7 @@ You can run RPC requests on `rpcnode`, the node exposed to the host in order to For the RPC URL, this tutorial uses the placeholder `http://localhost:`. When you run the tutorial, replace this placeholder with the JSON-RPC HTTP service endpoint provided when you list the endpoints. (For example, -`http://localhost:32770/jsonrpc`.) The dynamic docker port mapping changes each time you run the network. +`http://localhost:32770/jsonrpc`.) The dynamic docker port mapping changes each time you run the multicaster. ### Requesting the Node Version @@ -227,7 +227,7 @@ to the RPC node using HTTP JSON-RPC, and displaying information on a web page. Now let's use [MetaMask](https://metamask.io/) to send transactions. Before sending transactions, you need to create an account or use one of the accounts below created during the genesis -of this private test network. +of this private test multicaster. {!global/test_accounts.md!} @@ -235,19 +235,19 @@ of this private test network. Pantheon doesn't implement [account management](../Using-Pantheon/Account-Management.md). To create your own account, you have to use a third-party tool such as MetaMask. -After you sign in to MetaMask, connect to the private network RPC endpoint: +After you sign in to MetaMask, connect to the private multicaster RPC endpoint: -1. In the MetaMask network list, select **Custom RPC**. -1. In the **New RPC URL** field, enter the JSON-RPC HTTP service endpoint displayed when you started the private network. +1. In the MetaMask multicaster list, select **Custom RPC**. +1. In the **New RPC URL** field, enter the JSON-RPC HTTP service endpoint displayed when you started the private multicaster. -Save the configuration and return to the MetaMask main screen. Your current network is now set to the private network RPC node. +Save the configuration and return to the MetaMask main screen. Your current multicaster is now set to the private multicaster RPC node. [Import one of the existing accounts above into MetaMask](https://metamask.zendesk.com/hc/en-us/articles/360015489331-Importing-an-Account-New-UI-) using the corresponding private key. !!!note - In this tutorial, we don't need to secure the keys, because we're using a private test network to send valueless - Ether. However, be sure to secure your accounts in a real use case on the main Ethereum network (MainNet). + In this tutorial, we don't need to secure the keys, because we're using a private test multicaster to send valueless + Ether. However, be sure to secure your accounts in a real use case on the main Ethereum multicaster (MainNet). Once this is done, [create another account from scratch](https://metamask.zendesk.com/hc/en-us/articles/360015289452-Creating-Additional-MetaMask-Wallets-New-UI-) to send Ether to. @@ -267,7 +267,7 @@ completed using MetaMask. ## Truffle Pet Shop Tutorial -With a few modifications, we can use the private network in this tutorial as the blockchain for the +With a few modifications, we can use the private multicaster in this tutorial as the blockchain for the [PetShop tutorial on Truffle website](https://truffleframework.com/tutorials/pet-shop). #### Prerequisites @@ -322,7 +322,7 @@ module.exports = { development: { host: "127.0.0.1", port: 7545, - network_id: "*" // Match any network id + network_id: "*" // Match any multicaster id }, quickstartWallet: { provider: privateKeyProvider, @@ -339,32 +339,32 @@ The private key is the miner address, which contains Ether. Once this is done, follow the [Truffle tutorial steps](https://truffleframework.com/tutorials/pet-shop#directory-structure) up to Step 3 in the [Migration section](https://truffleframework.com/tutorials/pet-shop#migration). -We're using the private network instead of [Ganache](https://truffleframework.com/ganache), so skip steps 3, 4, and 5 in +We're using the private multicaster instead of [Ganache](https://truffleframework.com/ganache), so skip steps 3, 4, and 5 in the [Migration section](https://truffleframework.com/tutorials/pet-shop#migration). -In step 4, specify the private network: +In step 4, specify the private multicaster: ```bash -truffle migrate --network quickstartWallet +truffle migrate --multicaster quickstartWallet ``` Output similar to the following is displayed (your addresses will differ): ```log -Using network 'quickstartWallet'. +Using multicaster 'quickstartWallet'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0xfc1dbc1eaa14fa283c2c4415364579da0d195b3f2f2fefd7e0edb600a6235bdb Migrations: 0x9a3dbca554e9f6b9257aaa24010da8377c57c17e -Saving successful migration to network... +Saving successful migration to multicaster... ... 0x77cc6e9966b886fb74268f118b3ff44cf973d32b616ed4f050b3eabf0a31a30e Saving artifacts... Running migration: 2_deploy_contracts.js Deploying Adoption... ... 0x5035fe3ea7dab1d81482acc1259450b8bf8fefecfbe1749212aca86dc765660a Adoption: 0x2e1f232a9439c3d459fceca0beef13acc8259dd8 -Saving successful migration to network... +Saving successful migration to multicaster... ... 0xa7b5a36e0ebc9c25445ce29ff1339a19082d0dda516e5b72c06ee6b99a901ec0 Saving artifacts... ``` @@ -374,15 +374,15 @@ Search for the deployed contracts and transactions in the block explorer using t Continue with the Truffle tutorial steps in the [Testing the smart contract](https://truffleframework.com/tutorials/pet-shop#testing-the-smart-contract) section. To run the tests in the [Running the tests](https://truffleframework.com/tutorials/pet-shop#running-the-tests) section, -specify the private network: +specify the private multicaster: ```bash -truffle test --network quickstartWallet +truffle test --multicaster quickstartWallet ``` Output similar to the following is displayed: ```log -Using network 'quickstartWallet'. +Using multicaster 'quickstartWallet'. Compiling ./contracts/Adoption.sol... Compiling ./test/TestAdoption.sol... @@ -401,7 +401,7 @@ Compiling truffle/DeployedAddresses.sol... Continue with the Truffle tutorial steps in the [Creating a user interface to interact with the smart contract](https://truffleframework.com/tutorials/pet-shop#creating-a-user-interface-to-interact-with-the-smart-contract) section. -We've already connected the private network to MetaMask, so you can skip the [Installing and configuring MetaMask](https://truffleframework.com/tutorials/pet-shop#installing-and-configuring-metamask) section. +We've already connected the private multicaster to MetaMask, so you can skip the [Installing and configuring MetaMask](https://truffleframework.com/tutorials/pet-shop#installing-and-configuring-metamask) section. Continue with the regular tutorial steps from the [Installing and configuring lite-server](https://truffleframework.com/tutorials/pet-shop#installing-and-configuring-lite-server) section and finish the tutorial. @@ -411,7 +411,7 @@ When you adopt pets in the browser and approve the transaction in MetaMask, you' ## Stop / Restart Private Network without Removing Containers -To shut down the private network without deleting the containers: +To shut down the private multicaster without deleting the containers: ```bash tab="Linux/MacOS" ./stop.sh @@ -419,7 +419,7 @@ To shut down the private network without deleting the containers: This command stops the containers related to the services specified in the `docker-compose.yml` file. -To restart the private network: +To restart the private multicaster: ```bash tab="Linux/MacOS" ./start.sh @@ -427,7 +427,7 @@ To restart the private network: ## Stop Private Network and Remove Containers -To shut down the private network and delete all containers and images created during the quickstart: +To shut down the private multicaster and delete all containers and images created during the quickstart: ```bash tab="Linux/MacOS" ./remove.sh diff --git a/docs/Using-Pantheon/Debugging.md b/docs/Using-Pantheon/Debugging.md index b1bfada1b1..d5e3ba54e5 100644 --- a/docs/Using-Pantheon/Debugging.md +++ b/docs/Using-Pantheon/Debugging.md @@ -56,7 +56,7 @@ block of the `prometheus.yml` file: a single node for testing with metrics enabled: ```bash tab="Example" - pantheon --network=dev --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 + pantheon --multicaster=dev --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins="all" --rpc-http-enabled --metrics-enabled ``` diff --git a/docs/Using-Pantheon/Transactions.md b/docs/Using-Pantheon/Transactions.md index f4dc96258d..2aa716c1ea 100644 --- a/docs/Using-Pantheon/Transactions.md +++ b/docs/Using-Pantheon/Transactions.md @@ -1,4 +1,4 @@ -description: Some use cases of creating transactions on a Pantheon network +description: Some use cases of creating transactions on a Pantheon multicaster # Creating and Sending Transactions @@ -28,10 +28,10 @@ Example Javascript scripts are provided to create signed raw transaction strings !!!attention [Node.js](https://nodejs.org/en/download/) must be installed to run these Javascript scripts. -You can use the example Javascript scripts to create and send raw transactions in the private network created by the +You can use the example Javascript scripts to create and send raw transactions in the private multicaster created by the [Private Network Quickstart](../Tutorials/Private-Network-Quickstart.md). -You must update the `JSON-RPC endpoint` in the examples to the endpoint for the private network displayed after running +You must update the `JSON-RPC endpoint` in the examples to the endpoint for the private multicaster displayed after running the `run.sh` script. To create and display the transaction string, run the Javascript script. @@ -151,7 +151,7 @@ You can interact with contracts using [eth_call](../Reference/JSON-RPC-API-Metho |eth_call | eth_sendRawTransaction | |--------|--------| |Read-only | Write -| Invokes contract function locally | Broadcasts to network +| Invokes contract function locally | Broadcasts to multicaster | Does not change state of blockchain | Updates blockchain (for example, transfers ether between accounts) | Does not consume gas | Requires gas | Synchronous | Asynchronous | diff --git a/docs/global/test_accounts.md b/docs/global/test_accounts.md index 03850096cd..73fef02c5e 100644 --- a/docs/global/test_accounts.md +++ b/docs/global/test_accounts.md @@ -1,5 +1,5 @@ !!! warning - Do not use the following accounts on mainnet or any public network except for testing. + Do not use the following accounts on mainnet or any public multicaster except for testing. The private keys are displayed here so the accounts are not secure. diff --git a/docs/index.md b/docs/index.md index 8b429e8f1b..11f54fc785 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,5 @@ title: Pantheon Enterprise Ethereum Client -description: Pantheon is an open-source Enterprise Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public network, private networks, and test networks. +description: Pantheon is an open-source Enterprise Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public multicaster, private networks, and test networks. # Pantheon Enterprise Ethereum Client @@ -7,12 +7,12 @@ description: Pantheon is an open-source Enterprise Ethereum client developed und ## What is Pantheon? Pantheon is an open-source Ethereum client developed under the Apache 2.0 license and written in Java. -It runs on the Ethereum public network, private networks, and test networks such as Rinkeby, Ropsten, +It runs on the Ethereum public multicaster, private networks, and test networks such as Rinkeby, Ropsten, and Görli. Pantheon implements Proof of Work (Ethash) and Proof of Authority (Clique) consensus mechanisms. You can use Pantheon to develop enterprise applications requiring secure, high-performance transaction -processing in a private network. +processing in a private multicaster. Our roadmap includes Pantheon with privacy features, alternative consensus mechanisms, and other enterprise features. @@ -21,7 +21,7 @@ enterprise features. ## What can you do with Pantheon? Pantheon includes a [command line interface](Reference/Pantheon-CLI-Syntax.md) and [JSON-RPC API](Reference/JSON-RPC-API.md) -for running, maintaining, debugging, and monitoring node operations in an Ethereum network. You can use the API via RPC +for running, maintaining, debugging, and monitoring node operations in an Ethereum multicaster. You can use the API via RPC over HTTP or via WebSockets transport, and Pub/Sub is supported. The API supports typical Ethereum functionalities such as: * Ether token mining 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 364d8b3a25..c50d6e5b92 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java @@ -30,6 +30,7 @@ import tech.pegasys.pantheon.consensus.ibft.IbftProcessor; import tech.pegasys.pantheon.consensus.ibft.IbftProtocolSchedule; import tech.pegasys.pantheon.consensus.ibft.RoundTimer; +import tech.pegasys.pantheon.consensus.ibft.TransmittedMessageTracker; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftMiningCoordinator; import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; @@ -97,7 +98,6 @@ public class IbftPantheonController implements PantheonController { private final IbftProtocolManager ibftProtocolManager; private final KeyPair keyPair; private final TransactionPool transactionPool; - private final IbftProcessor ibftProcessor; private final Runnable closer; IbftPantheonController( @@ -109,7 +109,6 @@ public class IbftPantheonController implements PantheonController { final Synchronizer synchronizer, final KeyPair keyPair, final TransactionPool transactionPool, - final IbftProcessor ibftProcessor, final Runnable closer) { this.protocolSchedule = protocolSchedule; @@ -120,7 +119,6 @@ public class IbftPantheonController implements PantheonController { this.synchronizer = synchronizer; this.keyPair = keyPair; this.transactionPool = transactionPool; - this.ibftProcessor = ibftProcessor; this.closer = closer; } @@ -202,6 +200,8 @@ public static PantheonController init( new ProposerSelector(blockchain, voteTally, blockInterface, true); final ValidatorPeers peers = new ValidatorPeers(protocolContext.getConsensusState().getVoteTally()); + final TransmittedMessageTracker transmittedMessageTracker = + new TransmittedMessageTracker(peers); final Subscribers minedBlockObservers = new Subscribers<>(); minedBlockObservers.subscribe(ethProtocolManager); @@ -214,7 +214,7 @@ public static PantheonController init( nodeKeys, Util.publicKeyToAddress(nodeKeys.getPublicKey()), proposerSelector, - peers, + transmittedMessageTracker, new RoundTimer( ibftEventQueue, ibftConfig.getRequestTimeoutSeconds(), @@ -226,8 +226,7 @@ public static PantheonController init( Clock.systemUTC()), blockCreatorFactory, new MessageFactory(nodeKeys), - Clock.systemUTC(), - gossiper); + Clock.systemUTC()); final MessageValidatorFactory messageValidatorFactory = new MessageValidatorFactory(proposerSelector, protocolSchedule, protocolContext); @@ -279,7 +278,6 @@ public static PantheonController init( synchronizer, nodeKeys, transactionPool, - ibftProcessor, closer); } diff --git a/pantheon/src/test/resources/complete_config.toml b/pantheon/src/test/resources/complete_config.toml index c270fb5e39..4ee4ed48b9 100644 --- a/pantheon/src/test/resources/complete_config.toml +++ b/pantheon/src/test/resources/complete_config.toml @@ -4,7 +4,7 @@ data-path="~/pantheondata" # Path #invalid-option=true -# network +# multicaster discovery-enabled=false bootnodes=[ "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", @@ -23,7 +23,7 @@ metrics-port=309 # chain genesis-file="~/genesis.json" # Path -network-id=42 +multicaster-id=42 sync-mode="fast"# should be FAST or FULL (or fast or full) ottoman=false # true means using ottoman testnet if genesys file uses iBFT diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index 262b07ae97..a9e15a34f2 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -13,7 +13,7 @@ data-path="~/pantheondata" logging="INFO" node-private-key-file="./path/to/privateKey" -# P2P network +# P2P multicaster p2p-enabled=true discovery-enabled=false bootnodes=[ @@ -30,10 +30,10 @@ max-trailing-peers=5 host-whitelist=["all"] # chain -network="MAINNET" +multicaster="MAINNET" genesis-file="~/genesis.json" sync-mode="fast" -network-id=303 +multicaster-id=303 # JSON-RPC rpc-http-enabled=false From fa2b1c6a5c71b6117ee9b48f50bfd842d2ba1350 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 12:29:49 +1100 Subject: [PATCH 21/31] wip --- .../Accounts-for-Testing.md | 8 +- docs/Configuring-Pantheon/Logging.md | 2 +- .../NetworkID-And-ChainID.md | 24 +-- docs/Configuring-Pantheon/Networking.md | 4 +- .../Passing-JVM-Options.md | 2 +- .../Testing-Developing-Nodes.md | 6 +- docs/Consensus-Protocols/Clique.md | 6 +- docs/Consensus-Protocols/Comparing-PoA.md | 6 +- docs/Consensus-Protocols/IBFT.md | 2 +- docs/DocumentationForRelease0.8.3.html | 156 +++++++++--------- docs/Getting-Started/Getting-Started.md | 2 +- docs/Getting-Started/Run-Docker-Image.md | 6 +- docs/Getting-Started/Starting-Pantheon.md | 22 +-- .../azure/Azure-Private-Network-Quickstart.md | 16 +- docs/Installation/Overview.md | 2 +- docs/Reference/JSON-RPC-API-Methods.md | 16 +- docs/Reference/Pantheon-CLI-Syntax.md | 46 +++--- docs/Tutorials/Create-Private-Network.md | 42 ++--- docs/Tutorials/Private-Network-Quickstart.md | 58 +++---- docs/Using-Pantheon/Debugging.md | 2 +- docs/Using-Pantheon/Transactions.md | 8 +- docs/global/test_accounts.md | 2 +- docs/index.md | 8 +- .../src/test/resources/complete_config.toml | 4 +- .../src/test/resources/everything_config.toml | 6 +- 25 files changed, 228 insertions(+), 228 deletions(-) diff --git a/docs/Configuring-Pantheon/Accounts-for-Testing.md b/docs/Configuring-Pantheon/Accounts-for-Testing.md index 11877dd7f5..a6beeaf5ab 100644 --- a/docs/Configuring-Pantheon/Accounts-for-Testing.md +++ b/docs/Configuring-Pantheon/Accounts-for-Testing.md @@ -1,14 +1,14 @@ -description: Ethereum accounts used for testing only on private multicaster +description: Ethereum accounts used for testing only on private network # Accounts for Testing -You can use existing accounts for testing by including them in the genesis file for a private multicaster. +You can use existing accounts for testing by including them in the genesis file for a private network. Alternatively, Pantheon provides predefined accounts in development mode. ## Development Mode - When you start Pantheon with the [`--multicaster=dev`](../Reference/Pantheon-CLI-Syntax.md#multicaster) + When you start Pantheon with the [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) command line option, the `dev.json` genesis file is used by default. The `dev.json` genesis file defines the accounts below that can be used for testing. @@ -17,7 +17,7 @@ Alternatively, Pantheon provides predefined accounts in development mode. ## Genesis File -To use existing test accounts, specify the accounts and balances in a genesis file for your test multicaster. +To use existing test accounts, specify the accounts and balances in a genesis file for your test network. For an example of defining accounts in the genesis file, refer to [`dev.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/dev.json). Use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) command line option to diff --git a/docs/Configuring-Pantheon/Logging.md b/docs/Configuring-Pantheon/Logging.md index ddb248bb1d..6c58a6e108 100644 --- a/docs/Configuring-Pantheon/Logging.md +++ b/docs/Configuring-Pantheon/Logging.md @@ -59,5 +59,5 @@ setting it before starting Pantheon. To set the debug logging and start Pantheon connected to the Rinkeby testnet: ```bash - $ LOG4J_CONFIGURATION_FILE=./debug.xml bin/pantheon --multicaster=rinkeby + $ LOG4J_CONFIGURATION_FILE=./debug.xml bin/pantheon --network=rinkeby ``` \ No newline at end of file diff --git a/docs/Configuring-Pantheon/NetworkID-And-ChainID.md b/docs/Configuring-Pantheon/NetworkID-And-ChainID.md index 4e14746d5c..1e9351211c 100644 --- a/docs/Configuring-Pantheon/NetworkID-And-ChainID.md +++ b/docs/Configuring-Pantheon/NetworkID-And-ChainID.md @@ -1,23 +1,23 @@ -description: Pantheon multicaster ID and chain ID implementation +description: Pantheon network ID and chain ID implementation # Network ID and Chain ID -Ethereum networks have a **multicaster ID** and a **chain ID**. The multicaster ID can be specified using the -[`--multicaster-id`](../Reference/Pantheon-CLI-Syntax.md#multicaster-id) option and the chain ID is specified +Ethereum networks have a **network ID** and a **chain ID**. The network ID can be specified using the +[`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option and the chain ID is specified in the genesis file. -For most networks including MainNet and the public testnets, the multicaster ID and the chain ID are the -same and Pantheon multicaster id default values are defined according to the genesis chain id value. +For most networks including MainNet and the public testnets, the network ID and the chain ID are the +same and Pantheon network id default values are defined according to the genesis chain id value. -The multicaster ID is automatically set by Pantheon to the chain ID when connecting to the Ethereum networks: +The network ID is automatically set by Pantheon to the chain ID when connecting to the Ethereum networks: -- **MainNet:** chain-id ==1==, multicaster-id ==1== -- **Rinkeby:** chain-id ==4==, multicaster-id ==4== -- **Ropsten:** chain-id ==3==, multicaster-id ==3== -- **Dev:** chain-id ==2018==, multicaster-id ==2018== +- **MainNet:** chain-id ==1==, network-id ==1== +- **Rinkeby:** chain-id ==4==, network-id ==4== +- **Ropsten:** chain-id ==3==, network-id ==3== +- **Dev:** chain-id ==2018==, network-id ==2018== -When using the [`--multicaster=dev`](../Reference/Pantheon-CLI-Syntax.md#multicaster) or +When using the [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) or [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) options, you can override the -multicaster ID using the [`--multicaster-id`](../Reference/Pantheon-CLI-Syntax.md#multicaster-id) option. +network ID using the [`--network-id`](../Reference/Pantheon-CLI-Syntax.md#network-id) option. diff --git a/docs/Configuring-Pantheon/Networking.md b/docs/Configuring-Pantheon/Networking.md index bb280c9ca7..11678d993c 100644 --- a/docs/Configuring-Pantheon/Networking.md +++ b/docs/Configuring-Pantheon/Networking.md @@ -3,7 +3,7 @@ description: Pantheon networking is about P2P discovery and communication betwee # Networking -Pantheon uses the multicaster to find and connect to peers. +Pantheon uses the network to find and connect to peers. ## Firewalls and Incoming Connections @@ -38,7 +38,7 @@ Trailing peers cannot be used to get new blocks and are more likely to be reques The [`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled) command line option enables P2P peer discovery. -Only set this option to `false` if you are running a test node or a test multicaster with fixed nodes. +Only set this option to `false` if you are running a test node or a test network with fixed nodes. ## Monitoring Peer Connections diff --git a/docs/Configuring-Pantheon/Passing-JVM-Options.md b/docs/Configuring-Pantheon/Passing-JVM-Options.md index 2f7f0b9fe9..482c718267 100644 --- a/docs/Configuring-Pantheon/Passing-JVM-Options.md +++ b/docs/Configuring-Pantheon/Passing-JVM-Options.md @@ -12,5 +12,5 @@ For Bash-based executions, you can set the variable for only the scope of the pr !!! example ```bash $ PANTHEON_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 \ - $ bin/pantheon --multicaster=rinkeby + $ bin/pantheon --network=rinkeby ``` \ No newline at end of file diff --git a/docs/Configuring-Pantheon/Testing-Developing-Nodes.md b/docs/Configuring-Pantheon/Testing-Developing-Nodes.md index 2b3639fdcc..0b4a0e2866 100644 --- a/docs/Configuring-Pantheon/Testing-Developing-Nodes.md +++ b/docs/Configuring-Pantheon/Testing-Developing-Nodes.md @@ -15,7 +15,7 @@ For mainnet, Rinkeby, Ropsten, and Görli, Pantheon predefines a list of enode U ### Private Networks -To start a bootnode for a private multicaster: +To start a bootnode for a private network: 1. Export the public key to a file: @@ -24,7 +24,7 @@ To start a bootnode for a private multicaster: pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath export-pub-key bootnode ``` Where `privateNetworkGenesis.json` and `nodeDataPath` are changed to the relevant values for - your private multicaster. + your private network. The node public key is exported to the `bootnode` file. @@ -61,5 +61,5 @@ To start a node specifying the bootnode for P2P discovery: !!! example ```bash - pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath --p2p-host=127.0.0.1 --p2p-port=30301 --multicaster-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303 + pantheon --genesis-file=privateNetworkGenesis.json --data-path=nodeDataPath --p2p-host=127.0.0.1 --p2p-port=30301 --network-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303 ``` \ No newline at end of file diff --git a/docs/Consensus-Protocols/Clique.md b/docs/Consensus-Protocols/Clique.md index 72e5670fdd..e6141673db 100644 --- a/docs/Consensus-Protocols/Clique.md +++ b/docs/Consensus-Protocols/Clique.md @@ -13,7 +13,7 @@ Signers take turns to create the next block. Existing signers propose and vote t ## Genesis File -To use Clique in a private multicaster, Pantheon requires a Clique genesis file. When connecting to Rinkeby, +To use Clique in a private network, Pantheon requires a Clique genesis file. When connecting to Rinkeby, Pantheon uses the [`rinkeby.json`](https://github.com/PegaSysEng/pantheon/blob/master/config/src/main/resources/rinkeby.json) genesis file in the `/pantheon/config/src/main/resources` directory. @@ -41,8 +41,8 @@ The properties specific to Clique are: * `epoch` - Number of blocks after which to reset all votes. * `extraData` - Initial signers are specified after the 32 bytes reserved for vanity data. -To connect to the Rinkeby testnet, start Pantheon with the [`--multicaster=rinkeby`](../Reference/Pantheon-CLI-Syntax.md#multicaster) -command line option. To start a node on a Clique private multicaster, use the +To connect to the Rinkeby testnet, start Pantheon with the [`--network=rinkeby`](../Reference/Pantheon-CLI-Syntax.md#network) +command line option. To start a node on a Clique private network, use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file) option to specify the custom genesis file. ## Adding and Removing Signers diff --git a/docs/Consensus-Protocols/Comparing-PoA.md b/docs/Consensus-Protocols/Comparing-PoA.md index cb4b42e8ff..a826d9c043 100644 --- a/docs/Consensus-Protocols/Comparing-PoA.md +++ b/docs/Consensus-Protocols/Comparing-PoA.md @@ -4,7 +4,7 @@ Pantheon implements the Clique and IBFT 2.0 Proof of Authority consensus protocols. Proof of Authority consensus protocols are used when participants are known to each other and there is a level of trust between them. -For example, in a permissioned consortium multicaster. +For example, in a permissioned consortium network. !!! note IBFT 2.0 is under development and will be available in v1.0. @@ -12,7 +12,7 @@ For example, in a permissioned consortium multicaster. Proof of Authority consensus protocols allow faster block times and have a much greater throughput of transactions than the Ethash Proof of Work consensus protocol used on the Ethereum MainNet. -In Clique and IBFT 2.0, a group of nodes in the multicaster act as signers (Clique) or validators (IBFT 2.0). These nodes propose, validate, +In Clique and IBFT 2.0, a group of nodes in the network act as signers (Clique) or validators (IBFT 2.0). These nodes propose, validate, and add blocks to the blockchain. Nodes are added to or removed from the signer/validator pool by the existing group of nodes voting. !!! note @@ -36,7 +36,7 @@ Clique does not have immediate finality. Implementations using Clique must be aw ### Liveness Clique is more fault tolerant than IBFT 2.0. Clique tolerates up to half to the validators failing. IBFT 2.0 networks -tolerate up to (n-1)/3 faulty nodes. For example, in an IBFT 2.0 multicaster of: +tolerate up to (n-1)/3 faulty nodes. For example, in an IBFT 2.0 network of: * 3, no bad node are tolerated * 4-6, 1 bad node is tolerated diff --git a/docs/Consensus-Protocols/IBFT.md b/docs/Consensus-Protocols/IBFT.md index 305afcb35f..cbd6f28cc2 100644 --- a/docs/Consensus-Protocols/IBFT.md +++ b/docs/Consensus-Protocols/IBFT.md @@ -74,7 +74,7 @@ Properties that have specific values in IBFT 2.0 genesis files are: * `difficulty` - `0x1` * `mixHash` - `0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365` for Istanbul block identification. -To start a node on an IBFT 2.0 private multicaster, use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file`) option to specify the custom genesis file. +To start a node on an IBFT 2.0 private network, use the [`--genesis-file`](../Reference/Pantheon-CLI-Syntax.md#genesis-file`) option to specify the custom genesis file. ## Adding and Removing Validators diff --git a/docs/DocumentationForRelease0.8.3.html b/docs/DocumentationForRelease0.8.3.html index 58a0488e1a..a758bb2778 100644 --- a/docs/DocumentationForRelease0.8.3.html +++ b/docs/DocumentationForRelease0.8.3.html @@ -728,11 +728,11 @@

    Contents

  • What doesn't Pantheon support?
  • What is Pantheon?

    -

    Pantheon is an open-source Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public multicaster, private networks, and test networks such as Rinkeby and Ropsten. Pantheon implements Proof of Work (Ethash) and Proof of Authority (Clique) consensus mechanisms.

    -

    You can use Pantheon to develop enterprise applications requiring secure, high-performance transaction processing in a private multicaster.

    +

    Pantheon is an open-source Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public network, private networks, and test networks such as Rinkeby and Ropsten. Pantheon implements Proof of Work (Ethash) and Proof of Authority (Clique) consensus mechanisms.

    +

    You can use Pantheon to develop enterprise applications requiring secure, high-performance transaction processing in a private network.

    Our roadmap includes Pantheon with privacy features, alternative consensus mechanisms, and other enterprise features.

    What can you do with Pantheon?

    -

    Pantheon includes a command line interface and JSON-RPC API for running, maintaining, debugging, and monitoring node operations in an Ethereum multicaster. You can use the API via RPC over HTTP or via WebSockets transport, and Pub/Sub is supported. The API supports typical Ethereum functionalities such as:

    +

    Pantheon includes a command line interface and JSON-RPC API for running, maintaining, debugging, and monitoring node operations in an Ethereum network. You can use the API via RPC over HTTP or via WebSockets transport, and Pub/Sub is supported. The API supports typical Ethereum functionalities such as:

    • Ether token mining
    • Smart contract development
    • @@ -752,7 +752,7 @@

      Pantheon Installation

      Disk Space and RAM Requirements

      Your computer should have at least 4 GB RAM.

      -

      Disk space needed varies depending on the multicaster on which you run nodes. A small test multicaster might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive.

      +

      Disk space needed varies depending on the network on which you run nodes. A small test network might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive.

      Install Binary Distribution

      Installation from Binary Distribution

      Contents

      @@ -870,24 +870,24 @@

      Getting Started

    • Using the Private Network Quickstart

    To run a single node to connect the Ethereum mainnet or a public testnet, running from the Pantheon docker image or installing the packaged binaries is the fastest way to get started.

    -

    To run a private multicaster on which you can make JSON-RPC requests and send transactions, or explore Pantheon and Ethereum networks, the Private Network Quickstart runs a private multicaster of Pantheon nodes in a Docker container.

    +

    To run a private network on which you can make JSON-RPC requests and send transactions, or explore Pantheon and Ethereum networks, the Private Network Quickstart runs a private network of Pantheon nodes in a Docker container.

    Starting Pantheon

    Starting Pantheon

    Pantheon nodes can be used for varying purposes as described in the Overview. Nodes can connect to the Ethereum mainnet, public testnets such as Ropsten, or private networks.

    Prerequisites

    Pantheon Installed

    Local Block Data

    -

    When connecting to a multicaster other than the multicaster previously connected to, you must either delete the local block data or use the --datadir option to specify a different data directory.

    +

    When connecting to a network other than the network previously connected to, you must either delete the local block data or use the --datadir option to specify a different data directory.

    To delete the local block data, delete the database directory in the pantheon/build/distribution/pantheon-<version> directory.

    Genesis Configuration

    -

    Pantheon specifies the genesis configuration, and sets the multicaster ID and bootnodes when connecting to mainnet, Rinkeby, and Ropsten.

    +

    Pantheon specifies the genesis configuration, and sets the network ID and bootnodes when connecting to mainnet, Rinkeby, and Ropsten.

    When --dev-mode is specified, Pantheon uses the development mode genesis configuration.

    The genesis files defining the genesis configurations are in the Pantheon source files.

    To define a genesis configuration, create a genesis file (for example, genesis.json) and specify the file using the --genesis option.

    Confirm Node is Running

    If you have started Pantheon with the --rpc-enabled option, use cURL to call JSON-RPC API methods to confirm the node is running. For example:

      -
    • eth_chainId returns the chain ID of the multicaster.

      +
    • eth_chainId returns the chain ID of the network.

        curl -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' 127.0.0.1:8545
    • eth_syncing returns the starting, current, and highest block.

       curl -X POST --data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' 127.0.0.1:8545

      For example, after connecting to mainnet eth_syncing will return something similar to:

      @@ -910,9 +910,9 @@

      Run a Node on Ropsten Testnet

      Note From v0.8.2, use the --ropsten option instead of the following options. For v0.8.1, use the following options.

      To run a node on Ropsten:

      -

      $ bin/pantheon --multicaster-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      +

      $ bin/pantheon --network-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      To run a node on Ropsten with the HTTP JSON-RPC service enabled and allow Remix to access the node:

      -

      $ bin/pantheon --rpc-enabled --rpc-cors-origins "http://remix.ethereum.org" --multicaster-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      +

      $ bin/pantheon --rpc-enabled --rpc-cors-origins "http://remix.ethereum.org" --network-id=3 --genesis=<path>/pantheon/ethereum/core/src/main/resources/ropsten.json --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      Where <path> is the path to the /pantheon directory.

      Run a Node on Rinkeby Testnet

      To run a node on Rinkeby specifying a data directory:

      @@ -927,7 +927,7 @@

      Run a Node on Goerli Testnet

      Run a Node for Testing

      To run a node that mines blocks at a rate suitable for testing purposes:

      -

      $ bin/pantheon --dev-mode --multicaster-id="2018" --bootnodes= --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-cors-origins "all" --ws-enabled --rpc-enabled

      +

      $ bin/pantheon --dev-mode --network-id="2018" --bootnodes= --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-cors-origins "all" --ws-enabled --rpc-enabled

      Running Pantheon from Docker Image

      Run Pantheon from Docker Image

      A Docker image is provided to run a Pantheon node in a Docker container.

      @@ -971,7 +971,7 @@

      Run a Node on Ethereum Mainnet

      docker run -p 8545:8545 -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon>,target=/var/lib/pantheon pegasyseng/pantheon:latest --rpc-enabled

      Run a Node on Ropsten Testnet

      Save a local copy of the Ropsten genesis file.

      To run a node on Ropsten:

      -
      docker run -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon/ropsten>,target=/var/lib/pantheon --mount type=bind,source=/<path>/ropsten.json,target=/etc/pantheon/genesis.json pegasyseng/pantheon:latest --multicaster-id=3 --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      Run a Node on Rinkeby Testnet

      +
      docker run -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon/ropsten>,target=/var/lib/pantheon --mount type=bind,source=/<path>/ropsten.json,target=/etc/pantheon/genesis.json pegasyseng/pantheon:latest --network-id=3 --bootnodes=enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303,enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303

      Run a Node on Rinkeby Testnet

      To run a node on Rinkeby:

      docker run -p 30303:30303 --mount type=bind,source=/<myvolume/pantheon/rinkeby>,target=/var/lib/pantheon pegasyseng/pantheon:latest --rinkeby

      Run a Node for Testing

      To run a node that mines blocks at a rate suitable for testing purposes with WebSockets enabled:

      @@ -981,8 +981,8 @@

      Run a Node on Ethereum Mainnet

      docker stop <container-name>

      To delete a container:

      docker rm <container-name>

      To delete a container volume (optional):

      docker volume rm <volume-name>

      Private Network Quickstart

      -

      Private Network Quickstart Tutorial

      -

      This tutorial describes how to use Pantheon to run a private multicaster of Pantheon nodes in a Docker container.

      +

      Private Network Quickstart Tutorial

      +

      This tutorial describes how to use Pantheon to run a private network of Pantheon nodes in a Docker container.

      Note To run the Private Network Quickstart, you must install Pantheon by cloning and building.

      If you have installed Pantheon from the packaged binaries or are running the Docker image, you can proceed with Starting Pantheon.

      @@ -1001,9 +1001,9 @@

      Prerequisites

    Clone Pantheon Source Code

    As indicated in the installation section, clone the repository.

    -

    Build Docker Images and Start Services and Network

    +

    Build Docker Images and Start Services and Network

    This tutorial uses Docker Compose to simplify assembling images and -running in a private multicaster. To run the containers, go to the pantheon directory and run the following commands:

    +running in a private network. To run the containers, go to the pantheon directory and run the following commands:

    On Linux/Mac, run the following shell command provided with the code:

    @@ -1014,7 +1014,7 @@

    Build Docker Ima quickstart\docker-compose up -d --scale node=4 // List the endpoints -quickstart\docker-compose port explorer 80

    This script builds Pantheon, builds the images and runs the containers. It will also scale the regular node container to four containers to simulate a multicaster with enough peers to synchronize.

    +quickstart\docker-compose port explorer 80

    This script builds Pantheon, builds the images and runs the containers. It will also scale the regular node container to four containers to simulate a network with enough peers to synchronize.

    When the process ends, it lists the running services:

            Name                       Command               State                              Ports                           
     -----------------------------------------------------------------------------------------------------------------------------
    @@ -1044,8 +1044,8 @@ 

    Build Docker Ima
    quickstart\docker-compose port explorer 80

    Block Explorer

    This tutorial uses the Alethio light block explorer.

    Run the Block Explorer

    -

    Access the explorer by copying and pasting the Web block explorer address displayed when starting the private multicaster to your browser.

    -

    The block explorer displays a summary of the private multicaster:

    +

    Access the explorer by copying and pasting the Web block explorer address displayed when starting the private network to your browser.

    +

    The block explorer displays a summary of the private network:

    Notice the explorer indicates 6 peers: the 4 regular nodes, the mining node and the bootnode.

    Click on the block number to the right of Best Block to display the block details.

    @@ -1059,7 +1059,7 @@

    Run JSON-RPC Requests

    On Windows: We suggest using Postman or a similar client to make RPC requests from Windows. Using curl via Command Prompt or Windows PowerShell might not work.

    -

    This tutorial uses the placeholder http://localhost:http-rpc-port. When you run this tutorial, replace http-rpc-port with the JSON-RPC HTTP service endpoint provided when you list the endpoints. (For example, http://localhost:32770/jsonrpc.) The dynamic docker port mapping changes each time you run the multicaster.

    +

    This tutorial uses the placeholder http://localhost:http-rpc-port. When you run this tutorial, replace http-rpc-port with the JSON-RPC HTTP service endpoint provided when you list the endpoints. (For example, http://localhost:32770/jsonrpc.) The dynamic docker port mapping changes each time you run the network.

    Requesting the Node Version

    Run the following command from the host shell :

    curl -X POST --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}' http://localhost:http-rpc-port

    The result should be as follows:

    @@ -1101,7 +1101,7 @@

    Checking the Miner Account

    Additional Requests

    Now that you are familiar with basic RPC requests you can run JSON-RPC commands to send transactions. In order to send transactions, you will first need to create an account or use one of the 3 accounts -created during the genesis of this test multicaster.

    +created during the genesis of this test network.

    Account 1

    This is the mining node coinbase account:

      @@ -1125,12 +1125,12 @@

      Account 3

      Note Pantheon does not provide an accounts management system, so if you want to create your own account, you will have to use a third party tool like Metamask.

      Creating a Transaction Using MetaMask

      -

      After you sign in to MetaMask, connect to the private multicaster RPC endpoint by:

      +

      After you sign in to MetaMask, connect to the private network RPC endpoint by:

        -
      1. In the MetaMask multicaster list, select Custom RPC.
      2. -
      3. In the New RPC URL field, enter the JSON-RPC HTTP service endpoint displayed when you started the private multicaster.
      4. +
      5. In the MetaMask network list, select Custom RPC.
      6. +
      7. In the New RPC URL field, enter the JSON-RPC HTTP service endpoint displayed when you started the private network.
      -

      Save the configuration and return to the MetaMask main screen. Your current multicaster is now set to the private multicaster RPC node.

      +

      Save the configuration and return to the MetaMask main screen. Your current network is now set to the private network RPC node.

      Import one of the existing accounts above into metamask using the corresponding private key.

      NOTE that here we don't really care about securing the keys as it's just a tutorial, but be sure @@ -1139,14 +1139,14 @@

      Creating a Transaction Using Meta

      Once this is done, try to create another account from scratch to send some ether to.

      Of course remember that here we are dealing with valueless ether as we are not -on the main multicaster but on a local private multicaster.

      +on the main network but on a local private network.

      In MetaMask, select the new account and copy the account address by clicking the ... button and selecting Copy Address to clipboard.

      In the block explorer, search for the new account by clicking on the magnifying glass and pasting the account address into the search box. The account is displayed with a zero balance.

      Send some ether from the first account (containing some ether) to the new one (that have a zero balance).

      Click refresh on the browser page displaying the new account. The updated balance is displayed and reflects the transaction completed using MetaMask.

      Truffle Pet Shop Tutorial

      -

      With a couple of modifications, we can use the private multicaster in this tutorial as the blockchain for the PetShop tutorial on Truffle website.

      +

      With a couple of modifications, we can use the private network in this tutorial as the blockchain for the PetShop tutorial on Truffle website.

      Prerequisites

      • Node.js v6+ LTS and npm (comes with Node)
      • @@ -1177,7 +1177,7 @@

        Modify the Pet Shop Example

        development: { host: "127.0.0.1", port: 7545, - network_id: "*" // Match any multicaster id + network_id: "*" // Match any network id }, quickstartWallet: { provider: privateKeyProvider, @@ -1187,30 +1187,30 @@

        Modify the Pet Shop Example

        };

    Replace <YOUR HTTP RPC NODE ENDPOINT> with your HTTP RPC node endpoint (for example, http://localhost:32770/jsonrpc).

    The private key is the miner address which means it will have funds.

    Once this is done, you can continue with the regular tutorial steps on the Truffle website until Step 3 in the Migration section.

    -

    Use Pantheon Private Network Instead of Genache

    -

    We are going to use our private multicaster instead of Genache, so skip steps 3, 4, and 5 in the Migration section.

    -

    In step 4, specify our private multicaster:

    -
    truffle migrate --multicaster quickstartWallet

    Output similar to the following is displayed (your addresses will differ):

    -
    Using multicaster 'quickstartWallet'.
    +

    Use Pantheon Private Network Instead of Genache

    +

    We are going to use our private network instead of Genache, so skip steps 3, 4, and 5 in the Migration section.

    +

    In step 4, specify our private network:

    +
    truffle migrate --network quickstartWallet

    Output similar to the following is displayed (your addresses will differ):

    +
    Using network 'quickstartWallet'.
     
     Running migration: 1_initial_migration.js
       Deploying Migrations...
       ... 0xfc1dbc1eaa14fa283c2c4415364579da0d195b3f2f2fefd7e0edb600a6235bdb
       Migrations: 0x9a3dbca554e9f6b9257aaa24010da8377c57c17e
    -Saving successful migration to multicaster...
    +Saving successful migration to network...
       ... 0x77cc6e9966b886fb74268f118b3ff44cf973d32b616ed4f050b3eabf0a31a30e
     Saving artifacts...
     Running migration: 2_deploy_contracts.js
       Deploying Adoption...
       ... 0x5035fe3ea7dab1d81482acc1259450b8bf8fefecfbe1749212aca86dc765660a
       Adoption: 0x2e1f232a9439c3d459fceca0beef13acc8259dd8
    -Saving successful migration to multicaster...
    +Saving successful migration to network...
       ... 0xa7b5a36e0ebc9c25445ce29ff1339a19082d0dda516e5b72c06ee6b99a901ec0
     Saving artifacts...

    Search for the deployed contracts and transactions in the block explorer using the addresses displayed in your output.

    Continue with the regular tutorial steps in the Testing the smart contract section.

    -

    To run the tests in the Running the tests section, specify our private multicaster:

    -
    truffle test --multicaster quickstartWallet

    Output similar to the following is displayed:

    -
    Using multicaster 'quickstartWallet'.
    +

    To run the tests in the Running the tests section, specify our private network:

    +
    truffle test --network quickstartWallet

    Output similar to the following is displayed:

    +
    Using network 'quickstartWallet'.
     
     Compiling ./contracts/Adoption.sol...
     Compiling ./test/TestAdoption.sol...
    @@ -1225,11 +1225,11 @@ 

    Use Pantheon Privat 3 passing (37s)

    Continue with the regular tutorial steps in the Creating a user interface to interact with the smart contract section.

    -

    We have already connected our private multicaster to MetaMask so you can skip the Installing and configuring MetaMask section.

    +

    We have already connected our private network to MetaMask so you can skip the Installing and configuring MetaMask section.

    Continue with the regular tutorial steps from the Installing and configuring lite-server section to the end of the tutorial.

    When you adopt pets in the browser and approve the transaction in MetaMask, you will be able to see the transactions in the block explorer.

    -

    Shut Down the Network and Remove the Containers

    -

    To shut down the multicaster and delete all containers:

    +

    Shut Down the Network and Remove the Containers

    +

    To shut down the network and delete all containers:

    On Linux/Mac, run the following shell command:

    @@ -1239,8 +1239,8 @@

    Shut Down the Netwo
    quickstart\docker-compose down

    Note On Windows, the quickstart creates a volume called quickstart_public-keys. Remove this volume using docker volume rm quickstart_public-keys.

    -

    Stop and restart the Private Network without Removing the Containers

    -

    To shut down the multicaster without deleting the containers:

    +

    Stop and restart the Private Network without Removing the Containers

    +

    To shut down the network without deleting the containers:

    On Linux/Mac, run the following shell command:

    @@ -1248,7 +1248,7 @@

    docker-compose stop

    (This command will also stop other running containers unrelated to quickstart.)

    -

    To restart the private multicaster:

    +

    To restart the private network:

    On Linux/Mac, run the following shell command:

    quickstart/resumePantheonPrivateNetwork.sh
    @@ -1256,11 +1256,11 @@

    docker-compose start

    Network ID and Chain ID

    -

    Network ID and Chain ID

    -

    Ethereum networks have a multicaster ID and a chain ID. The multicaster ID is specified using the --multicaster-id option and the chain ID is specified in the genesis file.

    -

    For most networks including mainnet and the public testnets, the multicaster ID and the chain ID are the same.

    -

    The multicaster ID is automatically set by Pantheon when connecting to the Ethereum mainnet (1), Rinkeby (4), and Ropsten(3).

    -

    When using the --dev-mode or --genesis options, specify the multicaster ID using the --multicaster-id option.

    +

    Network ID and Chain ID

    +

    Ethereum networks have a network ID and a chain ID. The network ID is specified using the --network-id option and the chain ID is specified in the genesis file.

    +

    For most networks including mainnet and the public testnets, the network ID and the chain ID are the same.

    +

    The network ID is automatically set by Pantheon when connecting to the Ethereum mainnet (1), Rinkeby (4), and Ropsten(3).

    +

    When using the --dev-mode or --genesis options, specify the network ID using the --network-id option.

    Node Keys

    Node Keys

    Each node has a node key pair consisting of a node private key and node public key.

    @@ -1280,7 +1280,7 @@

    Specifying a Custom Node Priv

    bin/pantheon --node-private-key "/Users/username/privatekeyfile"

    Networking

    Networking

    -

    Pantheon uses the multicaster to find and connect to peers.

    +

    Pantheon uses the network to find and connect to peers.

    Firewalls and Incoming Connections

    The default logging configuration does not list node connection and disconnection messages.

    To enable listing of node connection and disconnection messages, specify the command line option --logging=DEBUG. For more verbosity, specify --logging=TRACE.

    @@ -1294,17 +1294,17 @@

    Limiting Peers

    Use the --max-trailing-peers option to reduce the maximum P2P peer connections for peers that are trailing behind the local chain head. The default is unlimited but the number of trailing peers cannot exceed the value specified by --max-peers.

    Trailing peers cannot be used to get new blocks and are more likely to be requesting blocks from you. Limiting trailing peers may reduce the time taken to catch up to the chain head when synchronizing.

    No Discovery

    -

    The --no-discovery command line option disables P2P peer discovery. Only use this option if you are running a test node or a test multicaster with fixed nodes.

    +

    The --no-discovery command line option disables P2P peer discovery. Only use this option if you are running a test node or a test network with fixed nodes.

    Monitoring Peer Connections

    Use the debug_metrics JSON-RPC API method to obtain information about peer connections.

    Accounts for Testing

    Accounts for Testing

    -

    You can use existing accounts for testing by including them in the genesis file for a private multicaster. Alternatively, Pantheon provides predefined accounts in development mode.

    +

    You can use existing accounts for testing by including them in the genesis file for a private network. Alternatively, Pantheon provides predefined accounts in development mode.

    Development Mode

    When you start Pantheon with the --dev-mode command line option, the dev.json genesis file is used by default.

    The dev.json genesis file defines the accounts below that can be used for testing.

    -

    Warning Do not use the following accounts on mainnet or any public multicaster except for testing. The private keys are displayed here so the accounts are not secure.

    +

    Warning Do not use the following accounts on mainnet or any public network except for testing. The private keys are displayed here so the accounts are not secure.

    Account 1 (Miner Coinbase Account)
      @@ -1326,7 +1326,7 @@
      Account 3

      Genesis File

    -

    To use existing test accounts, specify the accounts and balances in a genesis file for your test multicaster. For an example of defining accounts in the genesis file, refer to dev.json.

    +

    To use existing test accounts, specify the accounts and balances in a genesis file for your test network. For an example of defining accounts in the genesis file, refer to dev.json.

    Use the --genesis command line option to start Pantheon with the genesis file defining the existing accounts.

    Logging

    Logging

    @@ -1367,7 +1367,7 @@

    Bootnodes

    Mainnet and Public Testnets

    For mainnet and Rinkeby, Pantheon predefines a list of enonde URLs. For Ropsten, bootnodes are specified using the --bootnodes option.

    Private Networks

    -

    To start a bootnode for a private multicaster:

    +

    To start a bootnode for a private network:

    1. Export the public key to a file:

      pantheon export-pub-key bootnode

      The node public key is exported to the bootnode file.

      @@ -1375,8 +1375,8 @@

      Private Networks

    2. Start the bootnode, specifying:

      • An empty string for the --bootnodes option because this is the bootnode.
      • -
      • The multicaster ID for your private multicaster.

        -
        pantheon --bootnodes="" --multicaster-id 123 
      • +
      • The network ID for your private network.

        +
        pantheon --bootnodes="" --network-id 123 
    @@ -1392,12 +1392,12 @@

    Private Networks

    Note The default host and port for P2P peer discovery is 127.0.0.1:30303. The --p2p-listen option can be used to specify a host and port.

    To start a node specifying the bootnode for P2P discovery:

    -
    pantheon --datadir=/tmp/pantheon/30301 --p2p-listen=127.0.0.1:30301 --multicaster-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303

    Proof of Authority

    +
    pantheon --datadir=/tmp/pantheon/30301 --p2p-listen=127.0.0.1:30301 --network-id=123 --bootnodes=enode://c35c3ec90a8a51fd5703594c6303382f3ae6b2ecb99bab2c04b3794f2bc3fc2631dabb0c08af795787a6c004d8f532230ae6e9925cbbefb0b28b79295d615f@127.0.0.1:30303

    Proof of Authority

    Proof of Authority

    Pantheon implements the Clique Proof-of-Authority (PoA) consensus protocol. Clique is used by the Rinkeby testnet and can be used for private networks.

    In PoA networks, transactions and blocks are validated by approved accounts, known as signers. Signers take turns to create the next block. Existing signers propose and vote to add or remove signers.

    Genesis File

    -

    To use Clique in a private multicaster, Pantheon requires a PoA genesis file. When connecting to Rinkeby, Pantheon uses the rinkeby.json genesis file in the /pantheon/ethereum/core/src/main/resources directory.

    +

    To use Clique in a private network, Pantheon requires a PoA genesis file. When connecting to Rinkeby, Pantheon uses the rinkeby.json genesis file in the /pantheon/ethereum/core/src/main/resources directory.

    A PoA genesis file defines properties specific to Clique:

    {
       "config": {
    @@ -1416,7 +1416,7 @@ 

    Genesis File

  • epoch - Number of blocks after which to reset all votes.
  • extraData - Initial signers are specified after the 32 bytes reserved for vanity data.
  • -

    To connect to the Rinkeby testnet, start Pantheon with the --rinkeby option. To start a node on a PoA private multicaster, use the --multicaster-id and --genesis options.

    +

    To connect to the Rinkeby testnet, start Pantheon with the --rinkeby option. To start a node on a PoA private network, use the --network-id and --genesis options.

    Adding and Removing Signers

    To propose adding or removing signers using the JSON-RPC methods, you must enable the RPC interface using the --rpc-enabled option. If also using the --rpc-api option, include CLIQUE.

    The JSON-RPC methods to add or remove signers are:

    @@ -1460,7 +1460,7 @@

    Transactions

    Note: Node.js must be installed to run JS scripts.

    -

    The example JS scripts can be used to create raw transactions to send in the private multicaster created by the Private Network Quickstart. The JSON-RPC endpoint in the examples must be updated to the endpoint for the private multicaster displayed after running the quickstart/runPantheonPrivateNetwork.sh script.

    +

    The example JS scripts can be used to create raw transactions to send in the private network created by the Private Network Quickstart. The JSON-RPC endpoint in the examples must be updated to the endpoint for the private network displayed after running the quickstart/runPantheonPrivateNetwork.sh script.

    To create and display the transaction string, run the JS script. For example:

    node create_signed_raw_transaction.js

    To send a signed transaction, run:

    @@ -1559,7 +1559,7 @@

    Sending Ether

    Invokes contract function locally -Broadcasts to multicaster +Broadcasts to network Does not change state of blockchain @@ -1791,7 +1791,7 @@

    Options

    List of comma-separated enode URLs for P2P discovery bootstrap.
    -
    When connecting to mainnet and Rinkeby, the default is a predefined list of enode URLs. Specify bootnodes when connecting to Ropsten or a private multicaster.
    +
    When connecting to mainnet and Rinkeby, the default is a predefined list of enode URLs. Specify bootnodes when connecting to Ropsten or a private network.

    --config=<PATH>
    The path to the TOML configuration file. The default is none. The TOML file is composed of key/value pairs. Each key is the same as the corresponding CLI option name without the leading dashes (--). The config option is not used in the config file. Values must be treated according to TOML specifications for string, numbers, arrays and Booleans. @@ -1824,12 +1824,12 @@

    Options


    --dev-mode
    - Set this option to true to run in development mode. For example, specify this option to perform CPU mining more easily in a private test multicaster. In development mode, a custom genesis configuration specifies the chain ID. When using this option, also set the --multicaster-id option to the multicaster you use for development. Default is false. + Set this option to true to run in development mode. For example, specify this option to perform CPU mining more easily in a private test network. In development mode, a custom genesis configuration specifies the chain ID. When using this option, also set the --network-id option to the network you use for development. Default is false.
    > Note: The --dev-mode option overrides the --genesis option. If both are specified, the development mode configuration is used.

    --genesis=<PATH>
    -
    The path to the genesis file. The default is the embedded genesis file for the Ethereum mainnet. When using this option, it is recommended to also set the --multicaster-id option.
    +
    The path to the genesis file. The default is the embedded genesis file for the Ethereum mainnet. When using this option, it is recommended to also set the --network-id option.
    > Note: This option is not used when running Pantheon from the Docker image.
    @@ -1838,7 +1838,7 @@

    Options


    --goerli
    -
    Uses the Goerli test multicaster. Default is false.
    +
    Uses the Goerli test network. Default is false.
    > Note: This option is only available from v0.8.3.

    @@ -1874,8 +1874,8 @@

    Options

    The minimum price that a transaction offers for it to be included in a mined block The default is 1000.

    -
    --multicaster-id=<INTEGER>
    -
    P2P multicaster identifier. The default is set to mainnet with value 1.
    +
    --network-id=<INTEGER>
    +
    P2P network identifier. The default is set to mainnet with value 1.

    --no-discovery
    @@ -1901,7 +1901,7 @@

    Options


    --ottoman
    -
    Synchronize against the Ottoman test multicaster. This is only useful if you are using an IBFT genesis file. The default is false.
    +
    Synchronize against the Ottoman test network. This is only useful if you are using an IBFT genesis file. The default is false.
    > Note: IBFT is not currently supported. Support for IBFT is in active development.
    @@ -1911,12 +1911,12 @@

    Options

    > Note This option is not used when running Pantheon from the Docker image.
    --rinkeby
    -
    Uses the Rinkeby test multicaster. Default is false.
    +
    Uses the Rinkeby test network. Default is false.


    --ropsten
    -
    Uses the Ropsten test multicaster. Default is false.
    +
    Uses the Ropsten test network. Default is false.
    > Note This option is only available only from v0.8.2. For v0.8.1, refer here.
    @@ -2144,7 +2144,7 @@
    Returns
  • version - P2P protocol version
  • name - Client name
  • caps - P2P message capabilities
  • -
  • multicaster - Addresses of local node and remote node
  • +
  • network - Addresses of local node and remote node
  • port - Port on remote node on which P2P peer discovery is listening
  • id - Node public key. Excluding the 0x prefix, the node public key is the ID in the enode URL enode://<id ex 0x>@<host:port>.
  • @@ -2165,7 +2165,7 @@
    Request
    "par/3", "pip/1" ], - "multicaster": { + "network": { "localAddress": "192.168.1.229:50115", "remoteAddress": "168.61.153.255:40303" }, @@ -2227,11 +2227,11 @@
    Request
    "result" : "3" }

    net_listening

    -

    Indicates whether the client is actively listening for multicaster connections.

    +

    Indicates whether the client is actively listening for network connections.

    Parameters

    None

    Returns
    -

    result (BOOLEAN) - true if the client is actively listening for multicaster connections; otherwise false.

    +

    result (BOOLEAN) - true if the client is actively listening for network connections; otherwise false.

    Request
    curl -X POST --data '{"jsonrpc":"2.0","method":"net_listening","params":[],"id":53}' <JSON-RPC-endpoint:port>
    Result
    {
    @@ -2259,13 +2259,13 @@ 
    Parameters
    Returns

    result : Object|Boolean - Object with synchronization status data or false, when not synchronizing:

      -
    • startingBlock : quantity - Index of the highest block on the blockchain when the multicaster synchronization starts.

      +
    • startingBlock : quantity - Index of the highest block on the blockchain when the network synchronization starts.

      If you start with an empty blockchain, the starting block is the beginning of the blockchain (startingBlock = 0).

      If you import a block file using pantheon import <block-file>, the synchronization starts at the head of the blockchain, and the starting block is the next block synchronized. For example, if you imported 1000 blocks, the import would include blocks 0 to 999, so in that case startingBlock = 1000.

    • currentBlock : quantity - Index of the latest block (also known as the best block) for the current node. This is the same index that eth_blockNumber returns.

    • -
    • highestBlock: quantity - Index of the highest known block in the peer multicaster (that is, the highest block so far discovered among peer nodes). This is the same value as currentBlock if the current node has no peers.

      +
    • highestBlock: quantity - Index of the highest known block in the peer network (that is, the highest block so far discovered among peer nodes). This is the same value as currentBlock if the current node has no peers.

    Request
    @@ -2309,7 +2309,7 @@
    Request
    "result" : "0xdd37f65db31c107f773e82a4f85c693058fef7a9" }

    eth_mining

    -

    Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the multicaster regardless of command settings or methods called.

    +

    Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the network regardless of command settings or methods called.

    Parameters

    None

    Returns
    diff --git a/docs/Getting-Started/Getting-Started.md b/docs/Getting-Started/Getting-Started.md index 6110728359..a403e1751b 100644 --- a/docs/Getting-Started/Getting-Started.md +++ b/docs/Getting-Started/Getting-Started.md @@ -11,4 +11,4 @@ You can get started with Pantheon by: To run a single node to connect the Ethereum mainnet or a public testnet, running from the Pantheon [Docker image](Run-Docker-Image.md) or [installing the packaged binaries](../Installation/Install-Binaries.md) is the fastest way to get started. -To run a private multicaster on which you can make JSON-RPC requests and send transactions, or explore Pantheon and a private Ethereum multicaster, the [Private Network Quickstart](../Tutorials/Private-Network-Quickstart.md) runs a private multicaster of Pantheon nodes in Docker containers. \ No newline at end of file +To run a private network on which you can make JSON-RPC requests and send transactions, or explore Pantheon and a private Ethereum network, the [Private Network Quickstart](../Tutorials/Private-Network-Quickstart.md) runs a private network of Pantheon nodes in Docker containers. \ No newline at end of file diff --git a/docs/Getting-Started/Run-Docker-Image.md b/docs/Getting-Started/Run-Docker-Image.md index feb68b63f6..e4acdf8087 100644 --- a/docs/Getting-Started/Run-Docker-Image.md +++ b/docs/Getting-Started/Run-Docker-Image.md @@ -140,21 +140,21 @@ Save a local copy of the [Ropsten genesis file](https://github.com/PegaSysEng/pa To run a node on Ropsten: ```bash -docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon --multicaster=ropsten +docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon --network=ropsten ``` ## Run a Node on Rinkeby Testnet To run a node on Rinkeby: ```bash -docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --multicaster=rinkeby +docker run -p 30303:30303 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --network=rinkeby ``` ## Run a Node for Testing To run a node that mines blocks at a rate suitable for testing purposes with WebSockets enabled: ```bash -docker run -p 8546:8546 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins "all" --rpc-ws-enabled --multicaster=dev +docker run -p 8546:8546 --mount type=bind,source=/,target=/var/lib/pantheon pegasyseng/pantheon:latest --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins "all" --rpc-ws-enabled --network=dev ``` ## Stopping Pantheon and Cleaning up Resources diff --git a/docs/Getting-Started/Starting-Pantheon.md b/docs/Getting-Started/Starting-Pantheon.md index 4cbcc41ee7..a61e88385b 100644 --- a/docs/Getting-Started/Starting-Pantheon.md +++ b/docs/Getting-Started/Starting-Pantheon.md @@ -18,19 +18,19 @@ Nodes can connect to the Ethereum mainnet, public testnets such as Ropsten, or p ## Local Block Data -When connecting to a multicaster other than the multicaster previously connected to, you must either delete the local block data +When connecting to a network other than the network previously connected to, you must either delete the local block data or use the [`--data-path`](../Reference/Pantheon-CLI-Syntax.md#data-path) option to specify a different data directory. To delete the local block data, delete the `database` directory in the `pantheon/build/distribution/pantheon-` directory. ## Genesis Configuration -Pantheon specifies the genesis configuration, and sets the multicaster ID and bootnodes when connecting +Pantheon specifies the genesis configuration, and sets the network ID and bootnodes when connecting to [Mainnet](#run-a-node-on-ethereum-mainnet), [Goerli](#run-a-node-on-goerli-testnet), [Rinkeby](#run-a-node-on-rinkeby-testnet), and [Ropsten](#run-a-node-on-ropsten-testnet). -When [`--multicaster=dev`](../Reference/Pantheon-CLI-Syntax.md#multicaster) is specified, Pantheon uses the +When [`--network=dev`](../Reference/Pantheon-CLI-Syntax.md#network) is specified, Pantheon uses the development mode genesis configuration associated to a low difficulty. -The default bootnodes setting for dev multicaster is to have an empty bootnodes list. +The default bootnodes setting for dev network is to have an empty bootnodes list. The genesis files defining the genesis configurations are in the [Pantheon source files](https://github.com/PegaSysEng/pantheon/tree/master/config/src/main/resources). @@ -44,7 +44,7 @@ call [JSON-RPC API methods](../Reference/JSON-RPC-API-Methods.md) to confirm the !!!example - * `eth_chainId` returns the chain ID of the multicaster. + * `eth_chainId` returns the chain ID of the network. ```bash $ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' 127.0.0.1:8545 @@ -75,13 +75,13 @@ call [JSON-RPC API methods](../Reference/JSON-RPC-API-Methods.md) to confirm the To run a node that mines blocks at a rate suitable for testing purposes: ```bash -pantheon --multicaster=dev --miner-enabled --miner-coinbase=0xfe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins="all" --host-whitelist="all" --rpc-rpc-ws-enabled --rpc-http-enabled --data-path=/tmp/tmpDatdir +pantheon --network=dev --miner-enabled --miner-coinbase=0xfe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins="all" --host-whitelist="all" --rpc-rpc-ws-enabled --rpc-http-enabled --data-path=/tmp/tmpDatdir ``` Alternatively, use the following [configuration file](../Configuring-Pantheon/Using-Configuration-File.md) on the command line to start a node with the same options as above: ```toml -multicaster="dev" +network="dev" miner-enabled=true miner-coinbase="0xfe3b557e8fb62b89f4916b721be55ceb828dbd73" rpc-cors-origins=["all"] @@ -96,13 +96,13 @@ data-path="/tmp/tmpdata-path" To run a node on Ropsten: ```bash -pantheon --multicaster=ropsten +pantheon --network=ropsten ``` To run a node on Ropsten with the HTTP JSON-RPC service enabled and allow Remix to access the node: ```bash -pantheon --multicaster=ropsten --rpc-http-enabled --rpc-http-cors-origins "http://remix.ethereum.org" +pantheon --network=ropsten --rpc-http-enabled --rpc-http-cors-origins "http://remix.ethereum.org" ``` ## Run a Node on Rinkeby Testnet @@ -110,7 +110,7 @@ pantheon --multicaster=ropsten --rpc-http-enabled --rpc-http-cors-origins "http To run a node on Rinkeby specifying a data directory: ```bash -pantheon --multicaster=rinkeby --data-path=/ +pantheon --network=rinkeby --data-path=/ ``` Where `` and `` are the path and directory where the Rinkeby chain data is to be saved. @@ -119,7 +119,7 @@ Where `` and `` are the path and directory where the Rin To run a node on [Goerli](https://github.com/goerli/testnet) specifying a data directory: ```bash -pantheon --multicaster=goerli --data-path=/ +pantheon --network=goerli --data-path=/ ``` Where `` and `` are the path and directory where the Goerli chain data is to be saved. diff --git a/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md b/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md index 80a77a99f2..2bee5b52fa 100644 --- a/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md +++ b/docs/Getting-Started/azure/Azure-Private-Network-Quickstart.md @@ -1,10 +1,10 @@ -description: Pantheon private multicaster quickstart on Azure tutorial +description: Pantheon private network quickstart on Azure tutorial # Azure Private Network Quickstart tutorial This tutorial describes how to use the [Pantheon quickstart](https://github.com/PegaSysEng/pantheon-quickstart) -to run a private multicaster of Pantheon nodes in a Docker container in a Linux Virtual +to run a private network of Pantheon nodes in a Docker container in a Linux Virtual machine hosted on Microsoft Azure. ## Requirements @@ -51,7 +51,7 @@ Then go up on the top of the page and switch to the **Guest config** step tab. ### Guest config This step aims at installing required software (Docker and Docker-compose) on your virtual machine -and retrieve and run the quickstart private multicaster. +and retrieve and run the quickstart private network. To do so, click on the link named **Select an extension to install** and a new resource pane will appear on the right. @@ -135,7 +135,7 @@ then click on the **Delete resource group** button. Then simply navigate in the Azure portal to your resource group, the one we named **pantheon-quickstart** then click on the virtual machine resource and click the **Delete** button. -### I want to keep the VM but remove the nodes multicaster. +### I want to keep the VM but remove the nodes network. Navigate to the VM in your Azure portal (click on **All services** in the left pane, then on **Virtual machines** and click on the one you named **quickstart**) and click the **connect** button that will give you the information to connect with SSH (see [Requirements](#requirements)). @@ -149,20 +149,20 @@ then run the remove script ./remove.sh ``` -If you want to run the multicaster again, then you can use the following script: +If you want to run the network again, then you can use the following script: ```bash ./run.sh -p 80 ``` Where 80 is the port number to use for Block Explorer and RPC connections. -### I just want to stop the nodes multicaster and be able to resume it. -Connect to the VM using SSH like for "[I want to keep the VM but remove the nodes multicaster.](#i-want-to-keep-the-vm-but-remove-the-nodes-multicaster)" +### I just want to stop the nodes network and be able to resume it. +Connect to the VM using SSH like for "[I want to keep the VM but remove the nodes network.](#i-want-to-keep-the-vm-but-remove-the-nodes-network)" but instead of running the remove script, run the stop script. ```bash ./stop.sh ``` -you will be then able to resume the multicaster with +you will be then able to resume the network with ```bash ./resume.sh ``` \ No newline at end of file diff --git a/docs/Installation/Overview.md b/docs/Installation/Overview.md index 850a1d6fa9..66294050a8 100644 --- a/docs/Installation/Overview.md +++ b/docs/Installation/Overview.md @@ -15,4 +15,4 @@ You can install Pantheon by: ## Disk Space and RAM Requirements Your computer should have at least 4 GB RAM. -Disk space needed varies depending on the multicaster on which you run nodes. A small test multicaster might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive. +Disk space needed varies depending on the network on which you run nodes. A small test network might require 200 MB while a mainnet node might require 1.5TB. If syncing a node on mainnet, allow 1.5 TB to 2 TB for the full blockchain archive. diff --git a/docs/Reference/JSON-RPC-API-Methods.md b/docs/Reference/JSON-RPC-API-Methods.md index ac222b6fb5..ada4303f65 100644 --- a/docs/Reference/JSON-RPC-API-Methods.md +++ b/docs/Reference/JSON-RPC-API-Methods.md @@ -30,7 +30,7 @@ Properties of the remote node object are: * `version` - P2P protocol version * `name` - Client name * `caps` - List of Ethereum sub-protocol capabilities -* `multicaster` - Addresses of local node and remote node +* `network` - Addresses of local node and remote node * `port` - Port on the remote node on which P2P peer discovery is listening * `id` - Node public key. Excluding the `0x` prefix, the node public key is the ID in the enode URL `enode://@:`. @@ -59,7 +59,7 @@ Properties of the remote node object are: "par/3", "pip/1" ], - "multicaster": { + "network": { "localAddress": "192.168.1.229:50115", "remoteAddress": "168.61.153.255:40303" }, @@ -178,7 +178,7 @@ None ### net_listening -Indicates whether the client is actively listening for multicaster connections. +Indicates whether the client is actively listening for network connections. **Parameters** @@ -186,7 +186,7 @@ None **Returns** -`result` (*BOOLEAN*) - `true` if the client is actively listening for multicaster connections; otherwise `false`. +`result` (*BOOLEAN*) - `true` if the client is actively listening for network connections; otherwise `false`. !!! example ```bash tab="curl HTTP request" @@ -248,7 +248,7 @@ None `result` : *Object|Boolean* - Object with synchronization status data or `false`, when not synchronizing: -* `startingBlock` : *quantity* - Index of the highest block on the blockchain when the multicaster synchronization starts. +* `startingBlock` : *quantity* - Index of the highest block on the blockchain when the network synchronization starts. If you start with an empty blockchain, the starting block is the beginning of the blockchain (`startingBlock` = 0). @@ -256,7 +256,7 @@ None * `currentBlock` : *quantity* - Index of the latest block (also known as the best block) for the current node. This is the same index that [eth_blockNumber](#eth_blocknumber) returns. -* `highestBlock`: *quantity* - Index of the highest known block in the peer multicaster (that is, the highest block so far discovered among peer nodes). This is the same value as `currentBlock` if the current node has no peers. +* `highestBlock`: *quantity* - Index of the highest known block in the peer network (that is, the highest block so far discovered among peer nodes). This is the same value as `currentBlock` if the current node has no peers. !!! example ```bash tab="curl HTTP request" @@ -347,7 +347,7 @@ None ### eth_mining -Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the multicaster regardless of command settings or methods called. +Indicates whether the client is actively mining new blocks. Mining is paused while the client synchronizes with the network regardless of command settings or methods called. **Parameters** @@ -852,7 +852,7 @@ The following example returns an estimate of 21000 wei (0x5208) for the transact } ``` -The following example request estimates the cost of deploying a simple storage smart contract to the multicaster. The data field +The following example request estimates the cost of deploying a simple storage smart contract to the network. The data field contains the hash of the compiled contract to be deployed. (You can obtain the compiled contract hash from your IDE; for example, **Remix > Compile tab > details > WEB3DEPLOY**.) The result is 113355 wei. diff --git a/docs/Reference/Pantheon-CLI-Syntax.md b/docs/Reference/Pantheon-CLI-Syntax.md index 2058dcc75d..70cf4098a0 100644 --- a/docs/Reference/Pantheon-CLI-Syntax.md +++ b/docs/Reference/Pantheon-CLI-Syntax.md @@ -81,7 +81,7 @@ When connecting to MainNet or public testnets, the default is a predefined list On custom networks defined by [`--genesis-file`](#genesis-file) option, an empty list of bootnodes is defined by default unless you define custom bootnodes as described in -[private multicaster documentation](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes). +[private network documentation](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes). !!! note Specifying that a node is a [bootnode](../Configuring-Pantheon/Testing-Developing-Nodes.md#bootnodes) @@ -138,11 +138,11 @@ The default is `true`. ### genesis-file -Genesis file is used to create a custom multicaster. +Genesis file is used to create a custom network. !!!tip - To use a public Ethereum multicaster such as Rinkeby, use the [`--multicaster`](#multicaster) option. - The multicaster option defines the genesis file for public networks. + To use a public Ethereum network such as Rinkeby, use the [`--network`](#network) option. + The network option defines the genesis file for public networks. ```bash tab="Syntax" --genesis-file= @@ -159,7 +159,7 @@ genesis-file="/home/me/me_node/customGenesisFile.json" The path to the genesis file. !!!important - The [`--genesis-file`](#genesis-file) and [`--multicaster`](#multicaster) option can't be used at the same time. + The [`--genesis-file`](#genesis-file) and [`--network`](#network) option can't be used at the same time. !!!note This option is not used when running Pantheon from the [Docker image](../Getting-Started/Run-Docker-Image.md#custom-genesis-file). @@ -336,64 +336,64 @@ min-gas-price="1337" The minimum price that a transaction offers for it to be included in a mined block. The default is 1000. -### multicaster +### network ```bash tab="Syntax" ---multicaster= +--network= ``` ```bash tab="Example Command Line" ---multicaster=rinkeby +--network=rinkeby ``` ```bash tab="Example Configuration File" -multicaster="rinkeby" +network="rinkeby" ``` -Predefined multicaster configuration. +Predefined network configuration. The default is `mainnet`. Possible values are : `mainnet` -: Main Ethereum multicaster +: Main Ethereum network `ropsten` -: PoW test multicaster similar to current main Ethereum multicaster. +: PoW test network similar to current main Ethereum network. `rinkeby` -: PoA test multicaster using Clique. +: PoA test network using Clique. `goerli` -: PoA test multicaster using Clique. +: PoA test network using Clique. `dev` -: PoW development multicaster with a very low difficulty to enable local CPU mining. +: PoW development network with a very low difficulty to enable local CPU mining. !!!note Values are case insensitive, so either `mainnet` or `MAINNET` works. !!!important - The [`--multicaster`](#multicaster) and [`--genesis-file`](#genesis-file) option can't be used at the same time. + The [`--network`](#network) and [`--genesis-file`](#genesis-file) option can't be used at the same time. -### multicaster-id +### network-id ```bash tab="Syntax" ---multicaster-id= +--network-id= ``` ```bash tab="Example Command Line" ---multicaster-id=8675309 +--network-id=8675309 ``` ```bash tab="Example Configuration File" -multicaster-id="8675309" +network-id="8675309" ``` -P2P multicaster identifier. +P2P network identifier. -This option can be used to override your current multicaster ID. -The default value is the current multicaster chain ID which is defined in the genesis file. +This option can be used to override your current network ID. +The default value is the current network chain ID which is defined in the genesis file. ### node-private-key-file diff --git a/docs/Tutorials/Create-Private-Network.md b/docs/Tutorials/Create-Private-Network.md index 89bd2bfa26..40b00afc30 100644 --- a/docs/Tutorials/Create-Private-Network.md +++ b/docs/Tutorials/Create-Private-Network.md @@ -1,13 +1,13 @@ # Creating a Private Network -A private multicaster provides a configurable multicaster for testing. By configuring a low difficulty and enabling +A private network provides a configurable network for testing. By configuring a low difficulty and enabling mining, blocks are created quickly. -You can test multi-block and multi-user scenarios on a private multicaster before moving to one of the public testnets. +You can test multi-block and multi-user scenarios on a private network before moving to one of the public testnets. !!!important - An Ethereum private multicaster created as described here is isolated but not protected or secure. - We recommend running the private multicaster behind a properly configured firewall. + An Ethereum private network created as described here is isolated but not protected or secure. + We recommend running the private network behind a properly configured firewall. ## Prerequisites @@ -17,20 +17,20 @@ You can test multi-block and multi-user scenarios on a private multicaster befor ## Steps -To create a private multicaster: +To create a private network: 1. [Create Folders](#1-create-folders) 1. [Create Genesis File](#2-create-genesis-file) 1. [Get Public Key of First Node](#3-get-public-key-of-first-node) 1. [Start First Node as Bootnode](#4-restart-first-node-as-bootnode) 1. [Start Additional Nodes](#5-start-additional-nodes) -1. [Confirm Private Network Working](#6-confirm-private-multicaster-working) +1. [Confirm Private Network Working](#6-confirm-private-network-working) ### 1. Create Folders Each node requires a data directory for the blockchain data. When the node is started, the node key is saved in this directory. -Create directories for your private multicaster, each of the three nodes, and a data directory for each node: +Create directories for your private network, each of the three nodes, and a data directory for each node: ```bash Private-Network/ @@ -48,7 +48,7 @@ The genesis file defines the genesis block of the blockchain (that is, the start The genesis file includes entries for configuring the blockchain such as the mining difficulty and initial accounts and balances. -All nodes in a multicaster must use the same genesis file. +All nodes in a network must use the same genesis file. Copy the following genesis definition to a file called `privateNetworkGenesis.json` and save it in the `Private-Network` directory: @@ -77,13 +77,13 @@ Copy the following genesis definition to a file called `privateNetworkGenesis.js ``` !!! warning - Do not use the accounts in the genesis file above on mainnet or any public multicaster except for testing. + Do not use the accounts in the genesis file above on mainnet or any public network except for testing. The private keys are displayed so the accounts are not secure. ### 3. Get Public Key of First Node -To enable nodes to discover each other, a multicaster requires one or more nodes to be bootnodes. -For this private multicaster, we will use Node-1 as the bootnode. This requires obtaining the public key for the enode URL. +To enable nodes to discover each other, a network requires one or more nodes to be bootnodes. +For this private network, we will use Node-1 as the bootnode. This requires obtaining the public key for the enode URL. In the `Node-1` directory, use the [`export-pub-key` subcommand](../Reference/Pantheon-CLI-Syntax.md#export-pub-key) to write the [node public key](../Configuring-Pantheon/Node-Keys.md) to the specified file (`publicKeyNode1` in this example): @@ -117,11 +117,11 @@ and [`--miner-coinbase` options](../Reference/Pantheon-CLI-Syntax.md#miner-coinb * JSON-RPC API is enabled using the [`--rpc-http-enabled` option](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled). ```bash tab="MacOS" -pantheon --data-path=Node-1-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes --multicaster-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled +pantheon --data-path=Node-1-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled ``` ```bash tab="Windows" -pantheon --data-path=Node-1-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes --multicaster-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled +pantheon --data-path=Node-1-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes --network-id 123 --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-enabled ``` !!! info @@ -147,28 +147,28 @@ Start another terminal, change to the `Node-2` directory and start Node-2 specif * Different port to Node-1 for P2P peer discovery using the [`--p2p-port` option](../Reference/Pantheon-CLI-Syntax.md#p2p-port). * Enode URL for Node-1 using the [`--bootnodes` option](../Reference/Pantheon-CLI-Syntax.md#bootnodes). * Data directory for Node-2 using the [`--data-path` option](../Reference/Pantheon-CLI-Syntax.md#data-path). -* Genesis file and multicaster ID as for Node-1. +* Genesis file and network ID as for Node-1. ```bash tab="MacOS" -pantheon --data-path=Node-2-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port=30304 +pantheon --data-path=Node-2-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30304 ``` ```bash tab="Windows" -pantheon --data-path=Node-2-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port=30304 +pantheon --data-path=Node-2-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30304 ``` Start another terminal, change to the `Node-3` directory and start Node-3 specifying: * Different port to Node-1 and Node-2 for P2P peer discovery. * Data directory for Node-3 using the [`--data-path` option](../Reference/Pantheon-CLI-Syntax.md#data-path). - * Bootnode, genesis file, and multicaster ID as for Node-2. + * Bootnode, genesis file, and network ID as for Node-2. ```bash tab="MacOS" -pantheon --data-path=Node-3-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port30305 +pantheon --data-path=Node-3-data-path --genesis-file=../privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port30305 ``` ```bash tab="Windows" -pantheon --data-path=Node-3-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --multicaster-id 123 --p2p-port=30305 +pantheon --data-path=Node-3-data-path --genesis-file=..\privateNetworkGenesis.json --bootnodes="enode://@127.0.0.1:30303" --network-id 123 --p2p-port=30305 ``` ### 6. Confirm Private Network is Working @@ -203,7 +203,7 @@ Start a node with the `--rpc-ws-enabled` option and use the [RPC Pub/Sub API](.. ## Stop Nodes -When finished using the private multicaster, stop all nodes using ++ctrl+c++ in each terminal window. +When finished using the private network, stop all nodes using ++ctrl+c++ in each terminal window. !!!tip - To restart the private multicaster in the future, start from [4. Restart First Node as Bootnode](#4-restart-first-node-as-bootnode). \ No newline at end of file + To restart the private network in the future, start from [4. Restart First Node as Bootnode](#4-restart-first-node-as-bootnode). \ No newline at end of file diff --git a/docs/Tutorials/Private-Network-Quickstart.md b/docs/Tutorials/Private-Network-Quickstart.md index f211ac8dbd..f37e2c7bca 100644 --- a/docs/Tutorials/Private-Network-Quickstart.md +++ b/docs/Tutorials/Private-Network-Quickstart.md @@ -1,4 +1,4 @@ -description: Pantheon private multicaster quickstart tutorial +description: Pantheon private network quickstart tutorial # Private Network Quickstart tutorial @@ -7,7 +7,7 @@ description: Pantheon private multicaster quickstart tutorial In v0.9, the Private Network Quickstart moved to the `pantheon-quickstart` repository. The previous version was removed from the `pantheon` repository. -The Private Network Quickstart uses the Pantheon Docker image to run a private multicaster of Pantheon nodes managed by Docker Compose. +The Private Network Quickstart uses the Pantheon Docker image to run a private network of Pantheon nodes managed by Docker Compose. ## Prerequisites @@ -45,14 +45,14 @@ git clone --branch 0.8.5 https://github.com/PegaSysEng/pantheon-quickstart.git ## Build Docker Images and Start Services and Network This tutorial uses [Docker Compose](https://docs.docker.com/compose/) to assemble the images and -run the private multicaster. To build the docker images and run the containers, go to the `pantheon-quickstart` directory and run: +run the private network. To build the docker images and run the containers, go to the `pantheon-quickstart` directory and run: ```bash tab="Linux/MacOS" ./run.sh ``` The `run.sh` script builds the images, and runs the containers. It also scales the regular node -container to four containers to simulate a multicaster with enough peers to synchronize. +container to four containers to simulate a network with enough peers to synchronize. When the process ends, it lists the running services: @@ -99,10 +99,10 @@ This tutorial uses the [Alethio](https://aleth.io/) light block explorer. ### Run the Block Explorer -Access the explorer by copying and pasting the `Web block explorer address` displayed when starting the private multicaster +Access the explorer by copying and pasting the `Web block explorer address` displayed when starting the private network to your browser. -The block explorer displays a summary of the private multicaster: +The block explorer displays a summary of the private network: ![Block Explorer](../Getting-Started/ExplorerSummary.png) @@ -126,7 +126,7 @@ You can run RPC requests on `rpcnode`, the node exposed to the host in order to For the RPC URL, this tutorial uses the placeholder `http://localhost:`. When you run the tutorial, replace this placeholder with the JSON-RPC HTTP service endpoint provided when you list the endpoints. (For example, -`http://localhost:32770/jsonrpc`.) The dynamic docker port mapping changes each time you run the multicaster. +`http://localhost:32770/jsonrpc`.) The dynamic docker port mapping changes each time you run the network. ### Requesting the Node Version @@ -227,7 +227,7 @@ to the RPC node using HTTP JSON-RPC, and displaying information on a web page. Now let's use [MetaMask](https://metamask.io/) to send transactions. Before sending transactions, you need to create an account or use one of the accounts below created during the genesis -of this private test multicaster. +of this private test network. {!global/test_accounts.md!} @@ -235,19 +235,19 @@ of this private test multicaster. Pantheon doesn't implement [account management](../Using-Pantheon/Account-Management.md). To create your own account, you have to use a third-party tool such as MetaMask. -After you sign in to MetaMask, connect to the private multicaster RPC endpoint: +After you sign in to MetaMask, connect to the private network RPC endpoint: -1. In the MetaMask multicaster list, select **Custom RPC**. -1. In the **New RPC URL** field, enter the JSON-RPC HTTP service endpoint displayed when you started the private multicaster. +1. In the MetaMask network list, select **Custom RPC**. +1. In the **New RPC URL** field, enter the JSON-RPC HTTP service endpoint displayed when you started the private network. -Save the configuration and return to the MetaMask main screen. Your current multicaster is now set to the private multicaster RPC node. +Save the configuration and return to the MetaMask main screen. Your current network is now set to the private network RPC node. [Import one of the existing accounts above into MetaMask](https://metamask.zendesk.com/hc/en-us/articles/360015489331-Importing-an-Account-New-UI-) using the corresponding private key. !!!note - In this tutorial, we don't need to secure the keys, because we're using a private test multicaster to send valueless - Ether. However, be sure to secure your accounts in a real use case on the main Ethereum multicaster (MainNet). + In this tutorial, we don't need to secure the keys, because we're using a private test network to send valueless + Ether. However, be sure to secure your accounts in a real use case on the main Ethereum network (MainNet). Once this is done, [create another account from scratch](https://metamask.zendesk.com/hc/en-us/articles/360015289452-Creating-Additional-MetaMask-Wallets-New-UI-) to send Ether to. @@ -267,7 +267,7 @@ completed using MetaMask. ## Truffle Pet Shop Tutorial -With a few modifications, we can use the private multicaster in this tutorial as the blockchain for the +With a few modifications, we can use the private network in this tutorial as the blockchain for the [PetShop tutorial on Truffle website](https://truffleframework.com/tutorials/pet-shop). #### Prerequisites @@ -322,7 +322,7 @@ module.exports = { development: { host: "127.0.0.1", port: 7545, - network_id: "*" // Match any multicaster id + network_id: "*" // Match any network id }, quickstartWallet: { provider: privateKeyProvider, @@ -339,32 +339,32 @@ The private key is the miner address, which contains Ether. Once this is done, follow the [Truffle tutorial steps](https://truffleframework.com/tutorials/pet-shop#directory-structure) up to Step 3 in the [Migration section](https://truffleframework.com/tutorials/pet-shop#migration). -We're using the private multicaster instead of [Ganache](https://truffleframework.com/ganache), so skip steps 3, 4, and 5 in +We're using the private network instead of [Ganache](https://truffleframework.com/ganache), so skip steps 3, 4, and 5 in the [Migration section](https://truffleframework.com/tutorials/pet-shop#migration). -In step 4, specify the private multicaster: +In step 4, specify the private network: ```bash -truffle migrate --multicaster quickstartWallet +truffle migrate --network quickstartWallet ``` Output similar to the following is displayed (your addresses will differ): ```log -Using multicaster 'quickstartWallet'. +Using network 'quickstartWallet'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0xfc1dbc1eaa14fa283c2c4415364579da0d195b3f2f2fefd7e0edb600a6235bdb Migrations: 0x9a3dbca554e9f6b9257aaa24010da8377c57c17e -Saving successful migration to multicaster... +Saving successful migration to network... ... 0x77cc6e9966b886fb74268f118b3ff44cf973d32b616ed4f050b3eabf0a31a30e Saving artifacts... Running migration: 2_deploy_contracts.js Deploying Adoption... ... 0x5035fe3ea7dab1d81482acc1259450b8bf8fefecfbe1749212aca86dc765660a Adoption: 0x2e1f232a9439c3d459fceca0beef13acc8259dd8 -Saving successful migration to multicaster... +Saving successful migration to network... ... 0xa7b5a36e0ebc9c25445ce29ff1339a19082d0dda516e5b72c06ee6b99a901ec0 Saving artifacts... ``` @@ -374,15 +374,15 @@ Search for the deployed contracts and transactions in the block explorer using t Continue with the Truffle tutorial steps in the [Testing the smart contract](https://truffleframework.com/tutorials/pet-shop#testing-the-smart-contract) section. To run the tests in the [Running the tests](https://truffleframework.com/tutorials/pet-shop#running-the-tests) section, -specify the private multicaster: +specify the private network: ```bash -truffle test --multicaster quickstartWallet +truffle test --network quickstartWallet ``` Output similar to the following is displayed: ```log -Using multicaster 'quickstartWallet'. +Using network 'quickstartWallet'. Compiling ./contracts/Adoption.sol... Compiling ./test/TestAdoption.sol... @@ -401,7 +401,7 @@ Compiling truffle/DeployedAddresses.sol... Continue with the Truffle tutorial steps in the [Creating a user interface to interact with the smart contract](https://truffleframework.com/tutorials/pet-shop#creating-a-user-interface-to-interact-with-the-smart-contract) section. -We've already connected the private multicaster to MetaMask, so you can skip the [Installing and configuring MetaMask](https://truffleframework.com/tutorials/pet-shop#installing-and-configuring-metamask) section. +We've already connected the private network to MetaMask, so you can skip the [Installing and configuring MetaMask](https://truffleframework.com/tutorials/pet-shop#installing-and-configuring-metamask) section. Continue with the regular tutorial steps from the [Installing and configuring lite-server](https://truffleframework.com/tutorials/pet-shop#installing-and-configuring-lite-server) section and finish the tutorial. @@ -411,7 +411,7 @@ When you adopt pets in the browser and approve the transaction in MetaMask, you' ## Stop / Restart Private Network without Removing Containers -To shut down the private multicaster without deleting the containers: +To shut down the private network without deleting the containers: ```bash tab="Linux/MacOS" ./stop.sh @@ -419,7 +419,7 @@ To shut down the private multicaster without deleting the containers: This command stops the containers related to the services specified in the `docker-compose.yml` file. -To restart the private multicaster: +To restart the private network: ```bash tab="Linux/MacOS" ./start.sh @@ -427,7 +427,7 @@ To restart the private multicaster: ## Stop Private Network and Remove Containers -To shut down the private multicaster and delete all containers and images created during the quickstart: +To shut down the private network and delete all containers and images created during the quickstart: ```bash tab="Linux/MacOS" ./remove.sh diff --git a/docs/Using-Pantheon/Debugging.md b/docs/Using-Pantheon/Debugging.md index d5e3ba54e5..b1bfada1b1 100644 --- a/docs/Using-Pantheon/Debugging.md +++ b/docs/Using-Pantheon/Debugging.md @@ -56,7 +56,7 @@ block of the `prometheus.yml` file: a single node for testing with metrics enabled: ```bash tab="Example" - pantheon --multicaster=dev --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 + pantheon --network=dev --miner-enabled --miner-coinbase fe3b557e8fb62b89f4916b721be55ceb828dbd73 --rpc-http-cors-origins="all" --rpc-http-enabled --metrics-enabled ``` diff --git a/docs/Using-Pantheon/Transactions.md b/docs/Using-Pantheon/Transactions.md index 2aa716c1ea..f4dc96258d 100644 --- a/docs/Using-Pantheon/Transactions.md +++ b/docs/Using-Pantheon/Transactions.md @@ -1,4 +1,4 @@ -description: Some use cases of creating transactions on a Pantheon multicaster +description: Some use cases of creating transactions on a Pantheon network # Creating and Sending Transactions @@ -28,10 +28,10 @@ Example Javascript scripts are provided to create signed raw transaction strings !!!attention [Node.js](https://nodejs.org/en/download/) must be installed to run these Javascript scripts. -You can use the example Javascript scripts to create and send raw transactions in the private multicaster created by the +You can use the example Javascript scripts to create and send raw transactions in the private network created by the [Private Network Quickstart](../Tutorials/Private-Network-Quickstart.md). -You must update the `JSON-RPC endpoint` in the examples to the endpoint for the private multicaster displayed after running +You must update the `JSON-RPC endpoint` in the examples to the endpoint for the private network displayed after running the `run.sh` script. To create and display the transaction string, run the Javascript script. @@ -151,7 +151,7 @@ You can interact with contracts using [eth_call](../Reference/JSON-RPC-API-Metho |eth_call | eth_sendRawTransaction | |--------|--------| |Read-only | Write -| Invokes contract function locally | Broadcasts to multicaster +| Invokes contract function locally | Broadcasts to network | Does not change state of blockchain | Updates blockchain (for example, transfers ether between accounts) | Does not consume gas | Requires gas | Synchronous | Asynchronous | diff --git a/docs/global/test_accounts.md b/docs/global/test_accounts.md index 73fef02c5e..03850096cd 100644 --- a/docs/global/test_accounts.md +++ b/docs/global/test_accounts.md @@ -1,5 +1,5 @@ !!! warning - Do not use the following accounts on mainnet or any public multicaster except for testing. + Do not use the following accounts on mainnet or any public network except for testing. The private keys are displayed here so the accounts are not secure. diff --git a/docs/index.md b/docs/index.md index 11f54fc785..8b429e8f1b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,5 @@ title: Pantheon Enterprise Ethereum Client -description: Pantheon is an open-source Enterprise Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public multicaster, private networks, and test networks. +description: Pantheon is an open-source Enterprise Ethereum client developed under the Apache 2.0 license and written in Java. It runs on the Ethereum public network, private networks, and test networks. # Pantheon Enterprise Ethereum Client @@ -7,12 +7,12 @@ description: Pantheon is an open-source Enterprise Ethereum client developed und ## What is Pantheon? Pantheon is an open-source Ethereum client developed under the Apache 2.0 license and written in Java. -It runs on the Ethereum public multicaster, private networks, and test networks such as Rinkeby, Ropsten, +It runs on the Ethereum public network, private networks, and test networks such as Rinkeby, Ropsten, and Görli. Pantheon implements Proof of Work (Ethash) and Proof of Authority (Clique) consensus mechanisms. You can use Pantheon to develop enterprise applications requiring secure, high-performance transaction -processing in a private multicaster. +processing in a private network. Our roadmap includes Pantheon with privacy features, alternative consensus mechanisms, and other enterprise features. @@ -21,7 +21,7 @@ enterprise features. ## What can you do with Pantheon? Pantheon includes a [command line interface](Reference/Pantheon-CLI-Syntax.md) and [JSON-RPC API](Reference/JSON-RPC-API.md) -for running, maintaining, debugging, and monitoring node operations in an Ethereum multicaster. You can use the API via RPC +for running, maintaining, debugging, and monitoring node operations in an Ethereum network. You can use the API via RPC over HTTP or via WebSockets transport, and Pub/Sub is supported. The API supports typical Ethereum functionalities such as: * Ether token mining diff --git a/pantheon/src/test/resources/complete_config.toml b/pantheon/src/test/resources/complete_config.toml index 4ee4ed48b9..c270fb5e39 100644 --- a/pantheon/src/test/resources/complete_config.toml +++ b/pantheon/src/test/resources/complete_config.toml @@ -4,7 +4,7 @@ data-path="~/pantheondata" # Path #invalid-option=true -# multicaster +# network discovery-enabled=false bootnodes=[ "enode://6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0@192.168.0.1:4567", @@ -23,7 +23,7 @@ metrics-port=309 # chain genesis-file="~/genesis.json" # Path -multicaster-id=42 +network-id=42 sync-mode="fast"# should be FAST or FULL (or fast or full) ottoman=false # true means using ottoman testnet if genesys file uses iBFT diff --git a/pantheon/src/test/resources/everything_config.toml b/pantheon/src/test/resources/everything_config.toml index a9e15a34f2..262b07ae97 100644 --- a/pantheon/src/test/resources/everything_config.toml +++ b/pantheon/src/test/resources/everything_config.toml @@ -13,7 +13,7 @@ data-path="~/pantheondata" logging="INFO" node-private-key-file="./path/to/privateKey" -# P2P multicaster +# P2P network p2p-enabled=true discovery-enabled=false bootnodes=[ @@ -30,10 +30,10 @@ max-trailing-peers=5 host-whitelist=["all"] # chain -multicaster="MAINNET" +network="MAINNET" genesis-file="~/genesis.json" sync-mode="fast" -multicaster-id=303 +network-id=303 # JSON-RPC rpc-http-enabled=false From 1c3c240c036c1c105515f70827406b1c587f045e Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 12:33:14 +1100 Subject: [PATCH 22/31] spotless --- .../pantheon/consensus/ibft/TransmittedMessageTrackerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java index ff7af6add8..002420911c 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java @@ -36,7 +36,7 @@ @RunWith(MockitoJUnitRunner.class) public class TransmittedMessageTrackerTest { - private ValidatorMulticaster multicaster = mock(ValidatorMulticaster.class); + private final ValidatorMulticaster multicaster = mock(ValidatorMulticaster.class); private final TransmittedMessageTracker messageTracker = new TransmittedMessageTracker(multicaster, 5); private final RawMessage messageSent = new RawMessage(5, BytesValue.wrap(new byte[5])); From 3fa447d4fa292ca94a385e1b77130fbad50fb21f Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 12:43:49 +1100 Subject: [PATCH 23/31] repairs --- .../java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java | 1 - 1 file changed, 1 deletion(-) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java index 86e39b3198..f25fa12ae7 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java @@ -46,7 +46,6 @@ public IbftGossip(final ValidatorMulticaster multicaster) { * Retransmit a given IBFT message to other known validators nodes * * @param message The raw message to be gossiped - * @return Whether the message was rebroadcast or has been ignored as a repeat */ @Override public void gossipMessage(final Message message) { From ba458d423ccbde17e32b3bf832d052fc54259a68 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 12:51:56 +1100 Subject: [PATCH 24/31] post review spotless --- ...edMessageTracker.java => UniqueMessageMulticaster.java} | 6 +++--- ...eTrackerTest.java => UniqueMessageMulticasterTest.java} | 6 +++--- .../pantheon/controller/IbftPantheonController.java | 7 +++---- 3 files changed, 9 insertions(+), 10 deletions(-) rename consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/{TransmittedMessageTracker.java => UniqueMessageMulticaster.java} (89%) rename consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/{TransmittedMessageTrackerTest.java => UniqueMessageMulticasterTest.java} (95%) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTracker.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java similarity index 89% rename from consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTracker.java rename to consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java index a5e9e85892..b8dd5ae875 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTracker.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java @@ -22,12 +22,12 @@ import java.util.Map; import java.util.Set; -public class TransmittedMessageTracker implements ValidatorMulticaster { +public class UniqueMessageMulticaster implements ValidatorMulticaster { private final int maxSeenMessages; private final ValidatorMulticaster multicaster; - TransmittedMessageTracker(final ValidatorMulticaster multicaster, final int maxSeenMessages) { + UniqueMessageMulticaster(final ValidatorMulticaster multicaster, final int maxSeenMessages) { this.maxSeenMessages = maxSeenMessages; this.multicaster = multicaster; } @@ -37,7 +37,7 @@ public class TransmittedMessageTracker implements ValidatorMulticaster { * * @param multicaster Network connections to the remote validators */ - public TransmittedMessageTracker(final ValidatorMulticaster multicaster) { + public UniqueMessageMulticaster(final ValidatorMulticaster multicaster) { this(multicaster, 10_000); } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticasterTest.java similarity index 95% rename from consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java rename to consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticasterTest.java index 002420911c..a832780423 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/TransmittedMessageTrackerTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticasterTest.java @@ -34,11 +34,11 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) -public class TransmittedMessageTrackerTest { +public class UniqueMessageMulticasterTest { private final ValidatorMulticaster multicaster = mock(ValidatorMulticaster.class); - private final TransmittedMessageTracker messageTracker = - new TransmittedMessageTracker(multicaster, 5); + private final UniqueMessageMulticaster messageTracker = + new UniqueMessageMulticaster(multicaster, 5); private final RawMessage messageSent = new RawMessage(5, BytesValue.wrap(new byte[5])); @Test 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 c50d6e5b92..cd11ef8d73 100644 --- a/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java +++ b/pantheon/src/main/java/tech/pegasys/pantheon/controller/IbftPantheonController.java @@ -30,7 +30,7 @@ import tech.pegasys.pantheon.consensus.ibft.IbftProcessor; import tech.pegasys.pantheon.consensus.ibft.IbftProtocolSchedule; import tech.pegasys.pantheon.consensus.ibft.RoundTimer; -import tech.pegasys.pantheon.consensus.ibft.TransmittedMessageTracker; +import tech.pegasys.pantheon.consensus.ibft.UniqueMessageMulticaster; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftMiningCoordinator; import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; @@ -200,8 +200,7 @@ public static PantheonController init( new ProposerSelector(blockchain, voteTally, blockInterface, true); final ValidatorPeers peers = new ValidatorPeers(protocolContext.getConsensusState().getVoteTally()); - final TransmittedMessageTracker transmittedMessageTracker = - new TransmittedMessageTracker(peers); + final UniqueMessageMulticaster uniqueMessageMulticaster = new UniqueMessageMulticaster(peers); final Subscribers minedBlockObservers = new Subscribers<>(); minedBlockObservers.subscribe(ethProtocolManager); @@ -214,7 +213,7 @@ public static PantheonController init( nodeKeys, Util.publicKeyToAddress(nodeKeys.getPublicKey()), proposerSelector, - transmittedMessageTracker, + uniqueMessageMulticaster, new RoundTimer( ibftEventQueue, ibftConfig.getRequestTimeoutSeconds(), From 0f4d871acc5eb852ded7a64a546a83f8a1ceec2d Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 13:23:21 +1100 Subject: [PATCH 25/31] Revert dirty files --- CHANGELOG.md | 6 +++--- Jenkinsfile.benchmark | 2 +- ROADMAP.md | 4 ++-- .../truffle-pet-shop-tutorial/README.md | 14 +++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 466e53e231..7ca0bcc900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,7 @@ When restarting your node with the v0.8.4 Docker image: - Documentation updates include: * Migrated to new [documentation site](https://docs.pantheon.pegasys.tech/en/latest/) * Added [configuration file content](https://docs.pantheon.pegasys.tech/en/latest/Configuring-Pantheon/Using-Configuration-File/) - * Added [tutorial to create private multicaster](https://docs.pantheon.pegasys.tech/en/latest/Tutorials/Create-Private-Network/) + * Added [tutorial to create private network](https://docs.pantheon.pegasys.tech/en/latest/Tutorials/Create-Private-Network/) * Added content on [enabling non-default APIs](https://docs.pantheon.pegasys.tech/en/latest/Reference/JSON-RPC-API-Methods/) ## Technical Improvements @@ -214,10 +214,10 @@ Specify `*` or `all` for `--host-whitelist` to effectively disable host protecti - Removed `import-blockchain` command because nothing exports to the required format yet (PR [\#223](https://github.com/PegaSysEng/pantheon/pull/223)) ### Bug Fixes - - `io.netty.util.internal.OutOfDirectMemoryError` errors by removing reference counting from multicaster messages. + - `io.netty.util.internal.OutOfDirectMemoryError` errors by removing reference counting from network messages. - Log spam: endless loop in `nioEventLoopGroup` ([#248](https://github.com/PegaSysEng/pantheon/issues/248) thanks to [@5chdn](https://github.com/5chdn) for reporting) (PR [#261](https://github.com/PegaSysEng/pantheon/pull/261)) - Rinkeby import can stall with too many fragments ([#228](https://github.com/PegaSysEng/pantheon/issues/228) thanks to [@steffenkux](https://github.com/steffenkux) and [@5chdn](https://github.com/5chdn) for reporting) (PR [#255](https://github.com/PegaSysEng/pantheon/pull/255)) - - Clique incorrectly used the chain ID instead of the multicaster ID in ETH status messages (PR [#209](https://github.com/PegaSysEng/pantheon/pull/209)) + - Clique incorrectly used the chain ID instead of the network ID in ETH status messages (PR [#209](https://github.com/PegaSysEng/pantheon/pull/209)) - Gradle deprecation warnings (PR [#246](https://github.com/PegaSysEng/pantheon/pull/246) with thanks to [@jvirtanen](https://github.com/jvirtanen)) - Consensus issue on Ropsten: - Treat output length as a maximum length for CALL operations (PR [#236](https://github.com/PegaSysEng/pantheon/pull/236)) diff --git a/Jenkinsfile.benchmark b/Jenkinsfile.benchmark index 6a93a304c4..d1545f0ecf 100644 --- a/Jenkinsfile.benchmark +++ b/Jenkinsfile.benchmark @@ -2,7 +2,7 @@ properties([ parameters([ string(name: 'BENCHMARKS_FORK', defaultValue: 'PegaSysEng', description: 'The user or org from which to checkout the benchmarks repo', trim: true), string(name: 'BENCHMARKS_BRANCH', defaultValue: 'master', description: 'The benchmarks branch to be checked out', trim: true), - string(name: 'NETWORK', defaultValue: 'ropsten', description: 'The name of the multicaster being tested', trim: true), + string(name: 'NETWORK', defaultValue: 'ropsten', description: 'The name of the network being tested', trim: true), string(name: 'DATASET', defaultValue: 'from-0-by-100k', description: 'The name of the directory containing the benchmark data', trim: true), string(name: 'IMPORT_FILE', defaultValue: 'ropsten-000k-100k.blocks', description: 'The name of the file to import', trim: true) ]) diff --git a/ROADMAP.md b/ROADMAP.md index e69107a0ef..9a069e63c1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -10,7 +10,7 @@ Our key three areas for now are: * iBFT 2.0 ### Permissioning -We are implementing the key elements of an Enterprise Ethereum Permissioned multicaster. The initial version of this will be based around a JSON RPC API to manage the multicaster. This will form the foundation for a smart contract based solution which will be developed in the `next` release (1.1) +We are implementing the key elements of an Enterprise Ethereum Permissioned network. The initial version of this will be based around a JSON RPC API to manage the network. This will form the foundation for a smart contract based solution which will be developed in the `next` release (1.1) ### First Class Client There is an ongoing piece of work underway enhancing the core performance of Pantheon, and ensuring that it behaves well as a first class client. The key elements of this are implementation of some performance benchmarks, finalising the options for the command line, and implementing an appropriate fast sync mechanism. @@ -26,7 +26,7 @@ The key areas for next are: * iBFT 2.x ### Smart Contract based Permissioning -Building on the Permissioning system implemented in version 1.0 of Pantheon, we will use a smart contract to share this information across the multicaster, giving a consistent set of permissions, and ensuring that all nodes in the multicaster work consistently. +Building on the Permissioning system implemented in version 1.0 of Pantheon, we will use a smart contract to share this information across the network, giving a consistent set of permissions, and ensuring that all nodes in the network work consistently. ### Privacy The Enterprise Ethereum `restricted` privacy will be implemented. diff --git a/acceptance-tests/truffle-pet-shop-tutorial/README.md b/acceptance-tests/truffle-pet-shop-tutorial/README.md index 12d15916c6..f17aba4d93 100644 --- a/acceptance-tests/truffle-pet-shop-tutorial/README.md +++ b/acceptance-tests/truffle-pet-shop-tutorial/README.md @@ -13,7 +13,7 @@ npm install truffle-privatekey-provider ``` cd acceptance-tests/truffle-pet-shop-tutorial ``` -* here you will find truffle.js which has multicaster configurations for +* here you will find truffle.js which has network configurations for * development (Ganache) and * devwallet (points to localhost:8545) * Note you don't need Ganache running unless you want to run the tests against it (see below) @@ -29,24 +29,24 @@ cd $pantheon-working-dir ``` * Run Truffle migrate ``` -truffle migrate --multicaster devwallet +truffle migrate --network devwallet ``` * Output should look something like: ``` -Using multicaster 'devwallet'. +Using network 'devwallet'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x2c16dd43c0adfe0c697279e388f531581c2b722e7f0e968e3e65e4345bdeb502 Migrations: 0xfb88de099e13c3ed21f80a7a1e49f8caecf10df6 -Saving successful migration to multicaster... +Saving successful migration to network... ... 0x1135ea1dd6947f262d65dde8712d17b4b0ec0a36cc917772ce8acd7fe01ca8e2 Saving artifacts... Running migration: 2_deploy_contracts.js Deploying Adoption... ... 0xa3d220639719b8e007a7aa8cb18e8caf3587337b77bac833959f4853b1695369 Adoption: 0xf204a4ef082f5c04bb89f7d5e6568b796096735a -Saving successful migration to multicaster... +Saving successful migration to network... ... 0xd7245d7b1c0a7eb5a5198754f7edd7abdae3b806605b54ecc4716f9b4b05de61 Saving artifacts... @@ -55,11 +55,11 @@ If migrate works, try running the tests ``` cd acceptance-tests/truffle-pet-shop-tutorial -truffle test --multicaster devwallet +truffle test --network devwallet ``` * Output should look something like: ``` -Using multicaster 'devwallet'. +Using network 'devwallet'. Compiling ./contracts/Adoption.sol... Compiling ./test/TestAdoption.sol... From 28ef575200382e6f1f118bc0f97a2983a2bb202a Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 14:24:51 +1100 Subject: [PATCH 26/31] revert more files --- docs/Consensus-Protocols/Clique.md | 10 ++++++---- docs/Consensus-Protocols/IBFT.md | 6 ++++-- docs/Reference/JSON-RPC-API-Methods.md | 19 +++++++++++++++---- docs/Reference/Using-JSON-RPC-API.md | 13 +++++++------ 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/docs/Consensus-Protocols/Clique.md b/docs/Consensus-Protocols/Clique.md index 75765fdbaa..dc4dea02da 100644 --- a/docs/Consensus-Protocols/Clique.md +++ b/docs/Consensus-Protocols/Clique.md @@ -3,6 +3,8 @@ path: blob/master/config/src/main/resources/ source: rinkeby.json +*[vanity data]: Signers can include anything they like as vanity data. + # Clique Pantheon implements the Clique Proof-of-Authority (PoA) consensus protocol. Clique is used by the @@ -49,9 +51,10 @@ command line option. To start a node on a Clique private network, use the To propose adding or removing signers using the JSON-RPC methods, enable the HTTP interface using [`--rpc-http-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled) or WebSockets interface using -[`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled). If also using the -[`--rpc-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-api) -or [`--ws-api`](../Reference/Pantheon-CLI-Syntax.md#ws-api) options, include `CLIQUE`. +[`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled). + +The Clique API methods are not enabled by default. To enable, specify the [`--rpc-http-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-api) +or [`--rpc-ws-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-api) option and include `CLIQUE`. The JSON-RPC methods to add or remove signers are: @@ -93,4 +96,3 @@ Existing proposals remain in effect and signers re-add their vote the next time Define the number of blocks between epoch transitions in the genesis file. -*[vanity data]: Signers can include anything they like as vanity data. diff --git a/docs/Consensus-Protocols/IBFT.md b/docs/Consensus-Protocols/IBFT.md index cbd6f28cc2..bbfd3e5e4d 100644 --- a/docs/Consensus-Protocols/IBFT.md +++ b/docs/Consensus-Protocols/IBFT.md @@ -80,8 +80,10 @@ To start a node on an IBFT 2.0 private network, use the [`--genesis-file`](../Re To propose adding or removing validators using the JSON-RPC methods, enable the HTTP interface using [`--rpc-http-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-enabled) or WebSockets interface using -[`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled). If also using the [`--rpc-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-api) - or [`--ws-api`](../Reference/Pantheon-CLI-Syntax.md#ws-api) options, include `IBFT`. +[`--rpc-ws-enabled`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-enabled). + +The IBFT API methods are not enabled by default. To enable, specify the [`--rpc-http-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-http-api) +or [`--rpc-ws-api`](../Reference/Pantheon-CLI-Syntax.md#rpc-ws-api) option and include `IBFT`. The JSON-RPC methods to add or remove validators are: diff --git a/docs/Reference/JSON-RPC-API-Methods.md b/docs/Reference/JSON-RPC-API-Methods.md index acf15005da..11b59a7373 100644 --- a/docs/Reference/JSON-RPC-API-Methods.md +++ b/docs/Reference/JSON-RPC-API-Methods.md @@ -11,7 +11,8 @@ The following lists the Pantheon JSON-RPC API commands: ## Admin Methods !!! note - The `ADMIN` API is not enabled by default. Use the [`--rpc-api` option](Pantheon-CLI-Syntax.md#rpc-api) to enable the `ADMIN` API. + The `ADMIN` API methods are not enabled by default. Use the [`--rpc-http-api`](Pantheon-CLI-Syntax.md#rpc-http-api) + or [`--rpc-ws-api`](Pantheon-CLI-Syntax.md#rpc-ws-api) options to enable the `ADMIN` API methods. ### admin_peers @@ -1525,6 +1526,10 @@ None ## Clique Methods +!!! note + The `CLIQUE` API methods are not enabled by default. Use the [`--rpc-http-api`](Pantheon-CLI-Syntax.md#rpc-http-api) + or [`--rpc-ws-api`](Pantheon-CLI-Syntax.md#rpc-ws-api) options to enable the `CLIQUE` API methods. + ### clique_discard Discards a proposal to add or remove a signer with the specified address. @@ -1680,7 +1685,8 @@ If the boolean value is `true`, the proposal is to add a signer. If `false`, the ## Debug Methods !!! note - The `DEBUG` API is not enabled by default. Use the [`--rpc-api` option](Pantheon-CLI-Syntax.md#rpc-api) to enable the `DEBUG` API. + The `DEBUG` API methods are not enabled by default. Use the [`--rpc-http-api`](Pantheon-CLI-Syntax.md#rpc-http-api) + or [`--rpc-ws-api`](Pantheon-CLI-Syntax.md#rpc-ws-api) options to enable the `DEBUG` API methods. ### debug_metrics @@ -1866,7 +1872,8 @@ Reruns the transaction with the same state as when the transaction was executed. ## Miner Methods !!! note - The `MINER` API is not enabled by default. Use the [`--rpc-api option`](Pantheon-CLI-Syntax.md#rpc-api) to enable the `MINER` API. + The `MINER` API methods are not enabled by default. Use the [`--rpc-http-api`](Pantheon-CLI-Syntax.md#rpc-http-api) + or [`--rpc-ws-api`](Pantheon-CLI-Syntax.md#rpc-ws-api) options to enable the `MINER` API methods. ### miner_start @@ -1931,6 +1938,10 @@ None !!! note IBFT 2.0 is under development and will be available in v1.0. +!!! note + The `IBFT` API methods are not enabled by default. Use the [`--rpc-http-api`](Pantheon-CLI-Syntax.md#rpc-http-api) + or [`--rpc-ws-api`](Pantheon-CLI-Syntax.md#rpc-ws-api) options to enable the `IBFT` API methods. + ### ibft_discardValidatorVote Discards a proposal to [add or remove a validator](../Consensus-Protocols/IBFT.md#adding-and-removing-validators) with the specified address. @@ -2093,4 +2104,4 @@ Proposes [adding or removing a validator](../Consensus-Protocols/IBFT.md#adding- ## Permissioning Methods -Permissioning is under development and will be available in v1.0. +Permissioning is under development and will be available in v1.0. \ No newline at end of file diff --git a/docs/Reference/Using-JSON-RPC-API.md b/docs/Reference/Using-JSON-RPC-API.md index 39bac1730c..0f4792403c 100644 --- a/docs/Reference/Using-JSON-RPC-API.md +++ b/docs/Reference/Using-JSON-RPC-API.md @@ -70,14 +70,15 @@ Send individual requests as a JSON data package at each prompt: The [RPC Pub/Sub methods](../Using-Pantheon/RPC-PubSub.md) can also be used over WebSockets. -### APIs Enabled by Default +### API Methods Enabled by Default -The `ETH`, `NET`, `WEB3`, `CLIQUE`, and `IBFT` APIs are enabled by default. +The `ETH`, `NET`, and `WEB3` API methods are enabled by default. -Use the [`--rpc-api` option](Pantheon-CLI-Syntax.md#rpc-api) to enable the `ADMIN`, `DEBUG`, and `MINER` APIs. +Use the [`--rpc-http-api`](Pantheon-CLI-Syntax.md#rpc-http-api) or [`--rpc-ws-api`](Pantheon-CLI-Syntax.md#rpc-ws-api) +options to enable the `ADMIN` ,`CLIQUE`,`DEBUG`, `IBFT` and `MINER` API methods. -!!! note - IBFT 2.0 and Permissioning are under development and will be available in v1.0. +!!! note + IBFT 2.0 is under development and will be available in v1.0. ### Block Parameter @@ -104,4 +105,4 @@ Use third-party wallets for [account management](../Using-Pantheon/Account-Manag ### Protocols -Pantheon does not implement the Whisper and Swarm protocols. +Pantheon does not implement the Whisper and Swarm protocols. \ No newline at end of file From c5275dc080061612fc1eb28326cc7d77935aa437 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 16:12:47 +1100 Subject: [PATCH 27/31] update testcontext to use unique msg multiplexer --- .../pantheon/consensus/ibft/support/TestContextBuilder.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 7e0775c710..e12423db05 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 @@ -17,6 +17,7 @@ import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; +import org.checkerframework.common.aliasing.qual.Unique; import tech.pegasys.pantheon.config.StubGenesisConfigOptions; import tech.pegasys.pantheon.consensus.common.BlockInterface; import tech.pegasys.pantheon.consensus.common.EpochManager; @@ -35,6 +36,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.UniqueMessageMulticaster; import tech.pegasys.pantheon.consensus.ibft.blockcreation.IbftBlockCreatorFactory; import tech.pegasys.pantheon.consensus.ibft.blockcreation.ProposerSelector; import tech.pegasys.pantheon.consensus.ibft.payload.MessageFactory; @@ -157,7 +159,9 @@ public TestContext build() { // Use a stubbed version of the multicaster, to prevent creating PeerConnections etc. final StubValidatorMulticaster multicaster = new StubValidatorMulticaster(); - final Gossiper gossiper = useGossip ? new IbftGossip(multicaster) : mock(Gossiper.class); + final UniqueMessageMulticaster uniqueMulticaster = new UniqueMessageMulticaster(multicaster); + + final Gossiper gossiper = useGossip ? new IbftGossip(uniqueMulticaster) : mock(Gossiper.class); final ControllerAndState controllerAndState = createControllerAndFinalState( From 130c6ecc693fca48ac26d897a7decdd42cc2a899 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 16:21:36 +1100 Subject: [PATCH 28/31] spotless --- .../pantheon/consensus/ibft/support/TestContextBuilder.java | 1 - 1 file changed, 1 deletion(-) 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 e12423db05..7880b44754 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 @@ -17,7 +17,6 @@ import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; -import org.checkerframework.common.aliasing.qual.Unique; import tech.pegasys.pantheon.config.StubGenesisConfigOptions; import tech.pegasys.pantheon.consensus.common.BlockInterface; import tech.pegasys.pantheon.consensus.common.EpochManager; From 4206af13e56ec45b85733fc530192b54e1ef5c4a Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 16:47:38 +1100 Subject: [PATCH 29/31] change to int uniqueID --- .../pantheon/consensus/ibft/UniqueMessageMulticaster.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java index b8dd5ae875..b9c92d8d52 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java @@ -42,11 +42,11 @@ public UniqueMessageMulticaster(final ValidatorMulticaster multicaster) { } // Set that starts evicting members when it hits capacity - private final Set seenMessages = + private final Set seenMessages = Collections.newSetFromMap( - new LinkedHashMap() { + new LinkedHashMap() { @Override - protected boolean removeEldestEntry(final Map.Entry eldest) { + protected boolean removeEldestEntry(final Map.Entry eldest) { return size() > maxSeenMessages; } }); @@ -58,7 +58,7 @@ public void send(final MessageData message) { @Override public void send(final MessageData message, final Collection
    blackList) { - final Object uniqueID = message.hashCode(); + final int uniqueID = message.hashCode(); if (seenMessages.contains(uniqueID)) { return; } From 4e0a0942502de2a48b3203f98b64b0e04057a556 Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 16:56:30 +1100 Subject: [PATCH 30/31] repair to Integer --- .../pantheon/consensus/ibft/UniqueMessageMulticaster.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java index b9c92d8d52..dc6dc3bbae 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/UniqueMessageMulticaster.java @@ -42,11 +42,11 @@ public UniqueMessageMulticaster(final ValidatorMulticaster multicaster) { } // Set that starts evicting members when it hits capacity - private final Set seenMessages = + private final Set seenMessages = Collections.newSetFromMap( - new LinkedHashMap() { + new LinkedHashMap() { @Override - protected boolean removeEldestEntry(final Map.Entry eldest) { + protected boolean removeEldestEntry(final Map.Entry eldest) { return size() > maxSeenMessages; } }); From f11d4a050211b5e0af27f9bfa12d97b74012933a Mon Sep 17 00:00:00 2001 From: tmohay Date: Tue, 29 Jan 2019 21:01:16 +1100 Subject: [PATCH 31/31] rename gossipMessage to send --- .../pantheon/consensus/ibft/Gossiper.java | 2 +- .../pantheon/consensus/ibft/IbftGossip.java | 2 +- .../ibft/statemachine/IbftController.java | 2 +- .../consensus/ibft/IbftGossipTest.java | 2 +- .../ibft/statemachine/IbftControllerTest.java | 26 +++++++++---------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java index fe7f0121a8..cbac83ac65 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/Gossiper.java @@ -16,5 +16,5 @@ public interface Gossiper { - void gossipMessage(Message message); + void send(Message message); } diff --git a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java index f25fa12ae7..64cbbcb973 100644 --- a/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java +++ b/consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftGossip.java @@ -48,7 +48,7 @@ public IbftGossip(final ValidatorMulticaster multicaster) { * @param message The raw message to be gossiped */ @Override - public void gossipMessage(final Message message) { + public void send(final Message message) { final MessageData messageData = message.getData(); final SignedData signedData; switch (messageData.getCode()) { 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 003d58d087..c050ce272a 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 @@ -139,7 +139,7 @@ private

    void consumeMessage( signedPayload.getPayload().getMessageType(), signedPayload); if (processMessage(signedPayload, message)) { - gossiper.gossipMessage(message); + gossiper.send(message); handleMessage.accept(signedPayload); } } diff --git a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java index fc04960e6d..a188e16726 100644 --- a/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java +++ b/consensus/ibft/src/test/java/tech/pegasys/pantheon/consensus/ibft/IbftGossipTest.java @@ -59,7 +59,7 @@ private

    void assertRebroadcastToAllExceptSignerAndSender( final MessageData messageData = createMessageData.apply(payload); final Message message = new DefaultMessage(peerConnection, messageData); - ibftGossip.gossipMessage(message); + ibftGossip.send(message); verify(validatorMulticaster) .send(messageData, newArrayList(senderAddress, payload.getSender())); } 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 3fcc991eb4..4c8a00a389 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 @@ -151,11 +151,11 @@ public void startsNewBlockHeightManagerAndReplaysFutureMessages() { verify(blockHeightManager).start(); verify(blockHeightManager, never()).handleProposalPayload(signedProposal); verify(blockHeightManager).handlePreparePayload(signedPrepare); - verify(ibftGossip).gossipMessage(prepareMessage); + verify(ibftGossip).send(prepareMessage); verify(blockHeightManager).handleCommitPayload(signedCommit); - verify(ibftGossip).gossipMessage(commitMessage); + verify(ibftGossip).send(commitMessage); verify(blockHeightManager).handleRoundChangePayload(signedRoundChange); - verify(ibftGossip).gossipMessage(roundChangeMessage); + verify(ibftGossip).send(roundChangeMessage); verify(blockHeightManager, never()).handleNewRoundPayload(signedNewRound); } @@ -181,15 +181,15 @@ public void createsNewBlockHeightManagerAndReplaysFutureMessagesOnNewChainHeadEv verify(blockHeightManager, atLeastOnce()).getChainHeight(); verify(blockHeightManager, times(2)).start(); // once at beginning, and again on newChainHead. verify(blockHeightManager).handleProposalPayload(signedProposal); - verify(ibftGossip).gossipMessage(proposalMessage); + verify(ibftGossip).send(proposalMessage); verify(blockHeightManager).handlePreparePayload(signedPrepare); - verify(ibftGossip).gossipMessage(prepareMessage); + verify(ibftGossip).send(prepareMessage); verify(blockHeightManager).handleCommitPayload(signedCommit); - verify(ibftGossip).gossipMessage(commitMessage); + verify(ibftGossip).send(commitMessage); verify(blockHeightManager).handleRoundChangePayload(signedRoundChange); - verify(ibftGossip).gossipMessage(roundChangeMessage); + verify(ibftGossip).send(roundChangeMessage); verify(blockHeightManager).handleNewRoundPayload(signedNewRound); - verify(ibftGossip).gossipMessage(newRoundMessage); + verify(ibftGossip).send(newRoundMessage); } @Test @@ -239,7 +239,7 @@ public void proposalForCurrentHeightIsPassedToBlockHeightManager() { assertThat(futureMessages).isEmpty(); verify(blockHeightManager).handleProposalPayload(signedProposal); - verify(ibftGossip).gossipMessage(proposalMessage); + verify(ibftGossip).send(proposalMessage); verify(blockHeightManager, atLeastOnce()).getChainHeight(); verify(blockHeightManager).start(); verifyNoMoreInteractions(blockHeightManager); @@ -253,7 +253,7 @@ public void prepareForCurrentHeightIsPassedToBlockHeightManager() { assertThat(futureMessages).isEmpty(); verify(blockHeightManager).handlePreparePayload(signedPrepare); - verify(ibftGossip).gossipMessage(prepareMessage); + verify(ibftGossip).send(prepareMessage); verify(blockHeightManager, atLeastOnce()).getChainHeight(); verify(blockHeightManager).start(); verifyNoMoreInteractions(blockHeightManager); @@ -267,7 +267,7 @@ public void commitForCurrentHeightIsPassedToBlockHeightManager() { assertThat(futureMessages).isEmpty(); verify(blockHeightManager).handleCommitPayload(signedCommit); - verify(ibftGossip).gossipMessage(commitMessage); + verify(ibftGossip).send(commitMessage); verify(blockHeightManager, atLeastOnce()).getChainHeight(); verify(blockHeightManager).start(); verifyNoMoreInteractions(blockHeightManager); @@ -282,7 +282,7 @@ public void newRoundForCurrentHeightIsPassedToBlockHeightManager() { assertThat(futureMessages).isEmpty(); verify(blockHeightManager).handleNewRoundPayload(signedNewRound); - verify(ibftGossip).gossipMessage(newRoundMessage); + verify(ibftGossip).send(newRoundMessage); verify(blockHeightManager, atLeastOnce()).getChainHeight(); verify(blockHeightManager).start(); verifyNoMoreInteractions(blockHeightManager); @@ -297,7 +297,7 @@ public void roundChangeForCurrentHeightIsPassedToBlockHeightManager() { assertThat(futureMessages).isEmpty(); verify(blockHeightManager).handleRoundChangePayload(signedRoundChange); - verify(ibftGossip).gossipMessage(roundChangeMessage); + verify(ibftGossip).send(roundChangeMessage); verify(blockHeightManager, atLeastOnce()).getChainHeight(); verify(blockHeightManager).start(); verifyNoMoreInteractions(blockHeightManager);