From 183744bbc292dd5c037613d8ea535726a5db9a2d Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 18 Apr 2019 10:19:03 +0300 Subject: [PATCH 01/96] Fix BenchmarkLauncher compilation --- .../ethereum/beacon/simulator/BenchmarkLauncher.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java index 3faef86cf..3734ed81c 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java @@ -184,13 +184,14 @@ public void run() { BLS381Credentials.createWithDummySigner(keyPairs.get(i)); } + BeaconChainSpec spec = specBuilder.buildSpec(); Launcher launcher = new Launcher( - specBuilder.buildSpec(), + spec, depositContract, Collections.singletonList(bls), wireApi, - new MemBeaconChainStorageFactory(), + new MemBeaconChainStorageFactory(spec.getObjectHasher()), schedulers, proposeTimeCollector); @@ -205,14 +206,15 @@ public void run() { for (int i = 0; i < observers.size(); i++) { PeersConfig config = observers.get(i); String name = "O" + i; + BeaconChainSpec spec = specBuilder.buildSpec(); Launcher launcher = new Launcher( - specBuilder.buildSpec(), + spec, depositContract, null, localWireHub.createNewPeer( name, config.getWireInboundDelay(), config.getWireOutboundDelay()), - new MemBeaconChainStorageFactory(), + new MemBeaconChainStorageFactory(spec.getObjectHasher()), controlledSchedulers.createNew(name, config.getSystemTimeShift())); peers.add(launcher); } @@ -252,7 +254,7 @@ public void run() { depositContract, null, wireApi, - new MemBeaconChainStorageFactory(), + new MemBeaconChainStorageFactory(spec.getObjectHasher()), schedulers); peers.add(observer); From 6c16c1349ec6862e39ce8966219c98feff36706d Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 18 Apr 2019 10:20:28 +0300 Subject: [PATCH 02/96] Add a dedicated storage for blockHeaders. For now it just refers to blockStorage --- .../chain/storage/BeaconChainStorage.java | 4 ++ .../storage/impl/BeaconChainStorageImpl.java | 10 ++++ .../impl/DelegateBlockHeaderStorageImpl.java | 54 +++++++++++++++++++ .../impl/MemBeaconChainStorageFactory.java | 9 +++- .../impl/SSZBeaconChainStorageFactory.java | 7 ++- .../chain/storage/impl/SerializerFactory.java | 3 +- 6 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 chain/src/main/java/org/ethereum/beacon/chain/storage/impl/DelegateBlockHeaderStorageImpl.java diff --git a/chain/src/main/java/org/ethereum/beacon/chain/storage/BeaconChainStorage.java b/chain/src/main/java/org/ethereum/beacon/chain/storage/BeaconChainStorage.java index d6f88c956..1fe9e21f9 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/storage/BeaconChainStorage.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/storage/BeaconChainStorage.java @@ -1,5 +1,7 @@ package org.ethereum.beacon.chain.storage; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.db.source.DataSource; import org.ethereum.beacon.db.source.SingleValueSource; import tech.pegasys.artemis.ethereum.core.Hash32; @@ -7,6 +9,8 @@ public interface BeaconChainStorage { BeaconBlockStorage getBlockStorage(); + DataSource getBlockHeaderStorage(); + BeaconStateStorage getStateStorage(); BeaconTupleStorage getTupleStorage(); diff --git a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/BeaconChainStorageImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/BeaconChainStorageImpl.java index 49fb87c81..748ceb2c2 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/BeaconChainStorageImpl.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/BeaconChainStorageImpl.java @@ -4,6 +4,8 @@ import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.chain.storage.BeaconStateStorage; import org.ethereum.beacon.chain.storage.BeaconTupleStorage; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.db.source.DataSource; import org.ethereum.beacon.db.source.SingleValueSource; import tech.pegasys.artemis.ethereum.core.Hash32; @@ -11,6 +13,7 @@ public class BeaconChainStorageImpl implements BeaconChainStorage { private final BeaconBlockStorage blockStorage; + private final DataSource blockHeaderStorage; private final BeaconStateStorage stateStorage; private final BeaconTupleStorage tupleStorage; private final SingleValueSource justifiedStorage; @@ -18,11 +21,13 @@ public class BeaconChainStorageImpl implements BeaconChainStorage { public BeaconChainStorageImpl( BeaconBlockStorage blockStorage, + DataSource blockHeaderStorage, BeaconStateStorage stateStorage, BeaconTupleStorage tupleStorage, SingleValueSource justifiedStorage, SingleValueSource finalizedStorage) { this.blockStorage = blockStorage; + this.blockHeaderStorage = blockHeaderStorage; this.stateStorage = stateStorage; this.tupleStorage = tupleStorage; this.justifiedStorage = justifiedStorage; @@ -34,6 +39,11 @@ public BeaconBlockStorage getBlockStorage() { return blockStorage; } + @Override + public DataSource getBlockHeaderStorage() { + return blockHeaderStorage; + } + @Override public BeaconStateStorage getStateStorage() { return stateStorage; diff --git a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/DelegateBlockHeaderStorageImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/DelegateBlockHeaderStorageImpl.java new file mode 100644 index 000000000..767f7f8d0 --- /dev/null +++ b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/DelegateBlockHeaderStorageImpl.java @@ -0,0 +1,54 @@ +package org.ethereum.beacon.chain.storage.impl; + +import java.util.Optional; +import javax.annotation.Nonnull; +import org.ethereum.beacon.chain.storage.BeaconBlockStorage; +import org.ethereum.beacon.consensus.hasher.ObjectHasher; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.db.source.DataSource; +import tech.pegasys.artemis.ethereum.core.Hash32; + +public class DelegateBlockHeaderStorageImpl implements DataSource { + + private final BeaconBlockStorage delegateBlockStorage; + private final ObjectHasher objectHasher; + + public DelegateBlockHeaderStorageImpl( + BeaconBlockStorage delegateBlockStorage, + ObjectHasher objectHasher) { + this.delegateBlockStorage = delegateBlockStorage; + this.objectHasher = objectHasher; + } + + @Override + public Optional get(@Nonnull Hash32 key) { + return delegateBlockStorage + .get(key) + .map(this::createHeader); + } + + private BeaconBlockHeader createHeader(BeaconBlock block) { + return new BeaconBlockHeader( + block.getSlot(), + block.getPreviousBlockRoot(), + block.getStateRoot(), + objectHasher.getHash(block.getBody()), + block.getSignature()); + } + + @Override + public void put(@Nonnull Hash32 key, @Nonnull BeaconBlockHeader value) { + throw new UnsupportedOperationException(); + } + + @Override + public void remove(@Nonnull Hash32 key) { + throw new UnsupportedOperationException(); + } + + @Override + public void flush() { + throw new UnsupportedOperationException(); + } +} diff --git a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/MemBeaconChainStorageFactory.java b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/MemBeaconChainStorageFactory.java index ab0d8f7cd..04d716a74 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/MemBeaconChainStorageFactory.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/MemBeaconChainStorageFactory.java @@ -27,7 +27,12 @@ public BeaconChainStorage create(Database database) { new BeaconStateStorageImpl(new HashMapDataSource<>(), objectHasher); BeaconTupleStorage tupleStorage = new BeaconTupleStorageImpl(blockStorage, stateStorage); - return new BeaconChainStorageImpl(blockStorage, stateStorage, tupleStorage, - SingleValueSource.memSource(), SingleValueSource.memSource()); + return new BeaconChainStorageImpl( + blockStorage, + new DelegateBlockHeaderStorageImpl(blockStorage, objectHasher), + stateStorage, + tupleStorage, + SingleValueSource.memSource(), + SingleValueSource.memSource()); } } diff --git a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SSZBeaconChainStorageFactory.java b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SSZBeaconChainStorageFactory.java index 8f7b8d494..57e9f390e 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SSZBeaconChainStorageFactory.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SSZBeaconChainStorageFactory.java @@ -41,7 +41,12 @@ public BeaconChainStorage create(Database database) { SingleValueSource finalizedStorage = createHash32Storage(database, "finalized-hash"); return new BeaconChainStorageImpl( - blockStorage, stateStorage, tupleStorage, justifiedStorage, finalizedStorage); + blockStorage, + new DelegateBlockHeaderStorageImpl(blockStorage, objectHasher), + stateStorage, + tupleStorage, + justifiedStorage, + finalizedStorage); } private SingleValueSource createHash32Storage(Database database, String name) { diff --git a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SerializerFactory.java b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SerializerFactory.java index bd237d46d..de4c23857 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SerializerFactory.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/storage/impl/SerializerFactory.java @@ -13,8 +13,7 @@ public interface SerializerFactory { Function getSerializer(Class objectClass); static SerializerFactory createSSZ(SpecConstants specConstants) { - return new SSZSerializerFactory( - new SSZBuilder() + return new SSZSerializerFactory(new SSZBuilder() .withExternalVarResolver(new SpecConstantsResolver(specConstants)) .buildSerializer()); } From 8cc3cc5676c38e3e07012b4ebffa47efa2e2f66a Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 18 Apr 2019 10:25:29 +0300 Subject: [PATCH 03/96] Initial draft drop of wire implementation --- wire/build.gradle | 3 + .../ethereum/beacon/wire/LocalWireHub.java | 25 +++++ .../java/org/ethereum/beacon/wire/Peer.java | 24 ++++ .../org/ethereum/beacon/wire/PeerManager.java | 15 +++ .../org/ethereum/beacon/wire/WireApi.java | 13 +++ .../org/ethereum/beacon/wire/WireApi2.java | 13 +++ .../org/ethereum/beacon/wire/WireApiSync.java | 22 ++++ .../beacon/wire/WireApiSyncServer.java | 106 ++++++++++++++++++ .../beacon/wire/exceptions/WireException.java | 16 +++ .../WireIllegalArgumentsException.java | 8 ++ .../message/BlockBodiesRequestMessage.java | 22 ++++ .../message/BlockBodiesResponseMessage.java | 20 ++++ .../message/BlockHeadersRequestMessage.java | 40 +++++++ .../message/BlockHeadersResponseMessage.java | 22 ++++ .../message/BlockRootsRequestMessage.java | 25 +++++ .../message/BlockRootsResponseMessage.java | 42 +++++++ .../beacon/wire/message/GoodbyeMessage.java | 22 ++++ .../beacon/wire/message/HelloMessage.java | 53 +++++++++ .../ethereum/beacon/wire/message/Message.java | 4 + .../message/NotifyNewAttestationMessage.java | 19 ++++ .../wire/message/NotifyNewBlockMessage.java | 19 ++++ 21 files changed, 533 insertions(+) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/Peer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireException.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/Message.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java diff --git a/wire/build.gradle b/wire/build.gradle index 3eb7d631c..eaac352ee 100644 --- a/wire/build.gradle +++ b/wire/build.gradle @@ -2,6 +2,9 @@ dependencies { implementation project(':types') implementation project(':core') implementation project(':util') + implementation project(':ssz') + implementation project(':chain') + implementation project(':db:core') implementation 'com.google.guava:guava' implementation 'io.projectreactor:reactor-core' diff --git a/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java b/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java index 2c09d0d1b..2accdc37a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java @@ -4,10 +4,17 @@ import java.util.Date; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Future; import java.util.function.Consumer; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.schedulers.Schedulers; +import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; import org.reactivestreams.Processor; import org.reactivestreams.Publisher; import reactor.core.publisher.DirectProcessor; @@ -84,6 +91,24 @@ public Publisher inboundAttestationsStream() { .delayElements(Duration.ofMillis(inboundDelay), schedulers.reactorEvents()); } } + + @Override + public Future requestBlockRoots( + BlockHeadersRequestMessage requestMessage) { + throw new UnsupportedOperationException(); + } + + @Override + public Future requestBlockHeaders( + BlockRootsRequestMessage requestMessage) { + throw new UnsupportedOperationException(); + } + + @Override + public Future requestBlockBodies( + BlockBodiesRequestMessage requestMessage) { + throw new UnsupportedOperationException(); + } } List peers = new CopyOnWriteArrayList<>(); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java new file mode 100644 index 000000000..816e75d72 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java @@ -0,0 +1,24 @@ +package org.ethereum.beacon.wire; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import org.ethereum.beacon.wire.message.GoodbyeMessage; +import org.ethereum.beacon.wire.message.Message; +import org.reactivestreams.Publisher; + +public interface Peer { + + Publisher getInboundMessageStream(); + + void sendMessage(Message message); + + boolean isRemoteInitiated(); + + CompletableFuture getDisconnectFuture(); + + void disconnect(); + + default boolean isConnected() { + return !getDisconnectFuture().isDone(); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java b/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java new file mode 100644 index 000000000..898e00a34 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java @@ -0,0 +1,15 @@ +package org.ethereum.beacon.wire; + +import java.util.Collection; +import org.reactivestreams.Publisher; + +public interface PeerManager { + + Publisher connectedPeerStream(); + + Publisher disconnectedPeerStream(); + + Publisher activePeerStream(); + + Collection getActivePeers(); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApi.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApi.java index 2ab40b2f7..d054a40ad 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApi.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApi.java @@ -1,7 +1,14 @@ package org.ethereum.beacon.wire; +import java.util.concurrent.Future; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; import org.reactivestreams.Publisher; public interface WireApi { @@ -13,4 +20,10 @@ public interface WireApi { Publisher inboundBlocksStream(); Publisher inboundAttestationsStream(); + + Future requestBlockRoots(BlockHeadersRequestMessage requestMessage); + + Future requestBlockHeaders(BlockRootsRequestMessage requestMessage); + + Future requestBlockBodies(BlockBodiesRequestMessage requestMessage); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java new file mode 100644 index 000000000..9cae32411 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java @@ -0,0 +1,13 @@ +package org.ethereum.beacon.wire; + +import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; + +public interface WireApi2 { + + CompletableFuture newBlock(BeaconBlock block); + + CompletableFuture newAttestation(Attestation attestation); + +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java new file mode 100644 index 000000000..c61598e5e --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -0,0 +1,22 @@ +package org.ethereum.beacon.wire; + +import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; + +public interface WireApiSync { + int MAX_BLOCK_ROOTS_COUNT = 32768; + + CompletableFuture requestBlockRoots( + BlockRootsRequestMessage requestMessage); + + CompletableFuture requestBlockHeaders( + BlockHeadersRequestMessage requestMessage); + + CompletableFuture requestBlockBodies( + BlockBodiesRequestMessage requestMessage); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java new file mode 100644 index 000000000..d10434ccf --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -0,0 +1,106 @@ +package org.ethereum.beacon.wire; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import org.ethereum.beacon.chain.storage.BeaconChainStorage; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.BeaconBlockBody; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.wire.exceptions.WireIllegalArgumentsException; +import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; +import org.ethereum.beacon.wire.message.BlockRootsResponseMessage.BlockRootSlot; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +public class WireApiSyncServer implements WireApiSync { + + private final BeaconChainStorage storage; + + public WireApiSyncServer(BeaconChainStorage storage) { + this.storage = storage; + } + + @Override + public CompletableFuture requestBlockRoots( + BlockRootsRequestMessage requestMessage) { + CompletableFuture ret = new CompletableFuture(); + if (requestMessage.getCount().compareTo(UInt64.valueOf(MAX_BLOCK_ROOTS_COUNT)) > 0) { + ret.completeExceptionally(new WireIllegalArgumentsException( + "Too many block roots requested: " + requestMessage.getCount())); + } else { + List roots = new ArrayList<>(); + for (SlotNumber slot : requestMessage.getStartSlot().iterateTo( + requestMessage.getStartSlot().plus(requestMessage.getCount()))) { + List slotRoots = storage.getBlockStorage().getSlotBlocks(slot); + for (Hash32 slotRoot : slotRoots) { + roots.add(new BlockRootSlot(slotRoot, slot)); + } + } + ret.complete(new BlockRootsResponseMessage(roots)); + } + return ret; + } + + @Override + public CompletableFuture requestBlockHeaders( + BlockHeadersRequestMessage requestMessage) { + CompletableFuture ret = new CompletableFuture<>(); + Optional blockOpt = storage.getBlockStorage() + .get(requestMessage.getStartRoot()); + if (blockOpt.isPresent()) { + BeaconBlock block = blockOpt.get(); + if (!block.getSlot().equals(requestMessage.getStartSlot())) { + ret.completeExceptionally( + new WireIllegalArgumentsException("Requested start slot doesn't match block root: " + + requestMessage.getStartRoot() + ", " + requestMessage.getStartSlot())); + } else { + List headers = new ArrayList<>(); + int increment = requestMessage.getSkipSlots().getIntValue() + 1; + SlotNumber maxSlot = storage.getBlockStorage().getMaxSlot(); + SlotNumber slot = requestMessage.getStartSlot(); + SlotNumber prevSlot = SlotNumber.ZERO; + for(int i = 0; i < requestMessage.getMaxHeaders().intValue(); i++) { + + List slotBlocks = Collections.emptyList(); + SlotNumber nonEmptySlot = slot; + while (slotBlocks.isEmpty() && nonEmptySlot.greater(prevSlot)) { + slotBlocks = storage.getBlockStorage().getSlotBlocks(nonEmptySlot); + nonEmptySlot = nonEmptySlot.decrement(); + } + headers.add(storage.getBlockHeaderStorage().get(slotBlocks.get(0)).get()); + slot = slot.plus(increment); + if (slot.greater(maxSlot)) { + break; + } + prevSlot = nonEmptySlot; + } + ret.complete(new BlockHeadersResponseMessage(headers)); + } + } else { + ret.complete(new BlockHeadersResponseMessage(Collections.emptyList())); + } + return ret; + } + + @Override + public CompletableFuture requestBlockBodies( + BlockBodiesRequestMessage requestMessage) { + + List bodyList = requestMessage.getBlockTreeRoots().stream() + .map(blockRoot -> storage.getBlockStorage().get(blockRoot)) + .map(opt -> opt.map(BeaconBlock::getBody).orElse(BeaconBlockBody.EMPTY)) + .collect(Collectors.toList()); + return CompletableFuture.completedFuture(new BlockBodiesResponseMessage(bodyList)); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireException.java new file mode 100644 index 000000000..d5032da8a --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireException.java @@ -0,0 +1,16 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireException extends RuntimeException { + + public WireException(String message) { + super(message); + } + + public WireException(String message, Throwable cause) { + super(message, cause); + } + + public WireException(Throwable cause) { + super(cause); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java new file mode 100644 index 000000000..8c3dd87b4 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java @@ -0,0 +1,8 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireIllegalArgumentsException extends WireException { + + public WireIllegalArgumentsException(String message) { + super(message); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java new file mode 100644 index 000000000..f336d1b1c --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java @@ -0,0 +1,22 @@ +package org.ethereum.beacon.wire.message; + +import java.util.List; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class BlockBodiesRequestMessage extends Message { + @SSZ private final List blockTreeRoots; + + public BlockBodiesRequestMessage( + List blockTreeRoots) { + this.blockTreeRoots = blockTreeRoots; + } + + public List getBlockTreeRoots() { + return blockTreeRoots; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java new file mode 100644 index 000000000..17897b1af --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java @@ -0,0 +1,20 @@ +package org.ethereum.beacon.wire.message; + +import java.util.List; +import org.ethereum.beacon.core.BeaconBlockBody; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.ethereum.core.Hash32; + +@SSZSerializable +public class BlockBodiesResponseMessage extends Message { + @SSZ private final List blockBodies; + + public BlockBodiesResponseMessage(List blockBodies) { + this.blockBodies = blockBodies; + } + + public List getBlockBodies() { + return blockBodies; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java new file mode 100644 index 000000000..be854cbff --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java @@ -0,0 +1,40 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class BlockHeadersRequestMessage extends Message { + + @SSZ private final Hash32 startRoot; + @SSZ private final SlotNumber startSlot; + @SSZ private final UInt64 maxHeaders; + @SSZ private final UInt64 skipSlots; + + public BlockHeadersRequestMessage(Hash32 startRoot, + SlotNumber startSlot, UInt64 maxHeaders, UInt64 skipSlots) { + this.startRoot = startRoot; + this.startSlot = startSlot; + this.maxHeaders = maxHeaders; + this.skipSlots = skipSlots; + } + + public Hash32 getStartRoot() { + return startRoot; + } + + public SlotNumber getStartSlot() { + return startSlot; + } + + public UInt64 getMaxHeaders() { + return maxHeaders; + } + + public UInt64 getSkipSlots() { + return skipSlots; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java new file mode 100644 index 000000000..3abe6bce6 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java @@ -0,0 +1,22 @@ +package org.ethereum.beacon.wire.message; + +import java.util.List; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.ethereum.core.Hash32; + +@SSZSerializable +public class BlockHeadersResponseMessage extends Message { + + @SSZ private final List headers; + + public BlockHeadersResponseMessage(List headers) { + this.headers = headers; + } + + public List getHeaders() { + return headers; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java new file mode 100644 index 000000000..197fe1164 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java @@ -0,0 +1,25 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class BlockRootsRequestMessage extends Message { + @SSZ private final SlotNumber startSlot; + @SSZ private final UInt64 count; + + public BlockRootsRequestMessage(SlotNumber startSlot, UInt64 count) { + this.startSlot = startSlot; + this.count = count; + } + + public SlotNumber getStartSlot() { + return startSlot; + } + + public UInt64 getCount() { + return count; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java new file mode 100644 index 000000000..c9494e589 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java @@ -0,0 +1,42 @@ +package org.ethereum.beacon.wire.message; + +import java.util.List; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class BlockRootsResponseMessage extends Message { + + @SSZSerializable + public static class BlockRootSlot { + @SSZ private final Hash32 blockRoot; + @SSZ private final SlotNumber slot; + + public BlockRootSlot(Hash32 blockRoot, SlotNumber slot) { + this.blockRoot = blockRoot; + this.slot = slot; + } + + public Hash32 getBlockRoot() { + return blockRoot; + } + + public SlotNumber getSlot() { + return slot; + } + } + + @SSZ private final List roots; + + public BlockRootsResponseMessage( + List roots) { + this.roots = roots; + } + + public List getRoots() { + return roots; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java new file mode 100644 index 000000000..be84bfcad --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java @@ -0,0 +1,22 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class GoodbyeMessage extends Message { + public static final UInt64 CLIENT_SHUTDOWN = UInt64.valueOf(1); + public static final UInt64 IRRELEVANT_NETWORK = UInt64.valueOf(2); + public static final UInt64 ERROR = UInt64.valueOf(3); + + @SSZ private final UInt64 reason; + + public GoodbyeMessage(UInt64 reason) { + this.reason = reason; + } + + public UInt64 getReason() { + return reason; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java new file mode 100644 index 000000000..a80cb44f5 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java @@ -0,0 +1,53 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.core.types.EpochNumber; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class HelloMessage extends Message { + @SSZ private final byte networkId; + @SSZ private final UInt64 chainId; + @SSZ private final Hash32 latestFinalizedRoot; + @SSZ private final EpochNumber latestFinalizedEpoch; + @SSZ private final Hash32 bestRoot; + @SSZ private final SlotNumber bestSlot; + + public HelloMessage(byte networkId, UInt64 chainId, + Hash32 latestFinalizedRoot, EpochNumber latestFinalizedEpoch, + Hash32 bestRoot, SlotNumber bestSlot) { + this.networkId = networkId; + this.chainId = chainId; + this.latestFinalizedRoot = latestFinalizedRoot; + this.latestFinalizedEpoch = latestFinalizedEpoch; + this.bestRoot = bestRoot; + this.bestSlot = bestSlot; + } + + public byte getNetworkId() { + return networkId; + } + + public UInt64 getChainId() { + return chainId; + } + + public Hash32 getLatestFinalizedRoot() { + return latestFinalizedRoot; + } + + public EpochNumber getLatestFinalizedEpoch() { + return latestFinalizedEpoch; + } + + public Hash32 getBestRoot() { + return bestRoot; + } + + public SlotNumber getBestSlot() { + return bestSlot; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java new file mode 100644 index 000000000..8606683bc --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java @@ -0,0 +1,4 @@ +package org.ethereum.beacon.wire.message; + +public abstract class Message { +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java new file mode 100644 index 000000000..f817bad6d --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java @@ -0,0 +1,19 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; + +@SSZSerializable +public class NotifyNewAttestationMessage extends Message { + @SSZ private final Attestation attestation; + + public NotifyNewAttestationMessage(Attestation attestation) { + this.attestation = attestation; + } + + public Attestation getAttestation() { + return attestation; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java new file mode 100644 index 000000000..631febd15 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java @@ -0,0 +1,19 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; + +@SSZSerializable +public class NotifyNewBlockMessage extends Message { + @SSZ private final BeaconBlock block; + + public NotifyNewBlockMessage(BeaconBlock block) { + this.block = block; + } + + public BeaconBlock getBlock() { + return block; + } +} From 97ac010b95be1e5f1254489766c2f78aeb3d98d6 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 18 Apr 2019 15:59:06 +0300 Subject: [PATCH 04/96] Differentiate raw Message and MessagePayload. Minor wire refactorings and renamings --- .../java/org/ethereum/beacon/Launcher.java | 12 +++--- .../beacon/simulator/BenchmarkLauncher.java | 6 +-- .../beacon/simulator/SimulatorLauncher.java | 6 +-- .../ethereum/beacon/wire/LocalWireHub.java | 24 ++--------- .../java/org/ethereum/beacon/wire/Peer.java | 1 - .../org/ethereum/beacon/wire/WireApi.java | 29 -------------- .../org/ethereum/beacon/wire/WireApi2.java | 13 ------ .../org/ethereum/beacon/wire/WireApiSub.java | 16 ++++++++ .../beacon/wire/WireApiSyncServer.java | 1 - .../message/BlockBodiesRequestMessage.java | 4 +- .../message/BlockBodiesResponseMessage.java | 3 +- .../message/BlockHeadersRequestMessage.java | 2 +- .../message/BlockHeadersResponseMessage.java | 4 +- .../message/BlockRootsRequestMessage.java | 2 +- .../message/BlockRootsResponseMessage.java | 3 +- .../beacon/wire/message/GoodbyeMessage.java | 2 +- .../beacon/wire/message/HelloMessage.java | 2 +- .../ethereum/beacon/wire/message/Message.java | 4 ++ .../beacon/wire/message/MessagePayload.java | 4 ++ .../message/NotifyNewAttestationMessage.java | 3 +- .../wire/message/NotifyNewBlockMessage.java | 3 +- .../beacon/wire/message/RequestMessage.java | 39 ++++++++++++++++++ .../beacon/wire/message/ResponseMessage.java | 40 +++++++++++++++++++ 23 files changed, 127 insertions(+), 96 deletions(-) delete mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApi.java delete mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java diff --git a/start/common/src/main/java/org/ethereum/beacon/Launcher.java b/start/common/src/main/java/org/ethereum/beacon/Launcher.java index 5c338117d..05644214c 100644 --- a/start/common/src/main/java/org/ethereum/beacon/Launcher.java +++ b/start/common/src/main/java/org/ethereum/beacon/Launcher.java @@ -1,7 +1,5 @@ package org.ethereum.beacon; -import static java.util.Collections.singletonList; - import java.util.List; import org.ethereum.beacon.chain.DefaultBeaconChain; import org.ethereum.beacon.chain.MutableBeaconChain; @@ -33,7 +31,7 @@ import org.ethereum.beacon.validator.attester.BeaconChainAttesterImpl; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.validator.proposer.BeaconChainProposerImpl; -import org.ethereum.beacon.wire.WireApi; +import org.ethereum.beacon.wire.WireApiSub; import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -42,7 +40,7 @@ public class Launcher { private final BeaconChainSpec spec; private final DepositContract depositContract; private final List validatorCred; - private final WireApi wireApi; + private final WireApiSub wireApi; private final BeaconChainStorageFactory storageFactory; private final Schedulers schedulers; @@ -71,7 +69,7 @@ public Launcher( BeaconChainSpec spec, DepositContract depositContract, List validatorCred, - WireApi wireApi, + WireApiSub wireApi, BeaconChainStorageFactory storageFactory, Schedulers schedulers) { this(spec, depositContract, validatorCred, wireApi, storageFactory, schedulers, new TimeCollector()); @@ -81,7 +79,7 @@ public Launcher( BeaconChainSpec spec, DepositContract depositContract, List validatorCred, - WireApi wireApi, + WireApiSub wireApi, BeaconChainStorageFactory storageFactory, Schedulers schedulers, TimeCollector proposeTimeCollector) { @@ -190,7 +188,7 @@ public List getValidatorCred() { return validatorCred; } - public WireApi getWireApi() { + public WireApiSub getWireApi() { return wireApi; } diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java index 3734ed81c..1d6b914f9 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java @@ -54,7 +54,7 @@ import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; -import org.ethereum.beacon.wire.WireApi; +import org.ethereum.beacon.wire.WireApiSub; import org.javatuples.Pair; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -169,7 +169,7 @@ public void run() { for (int i = 0; i < validators.size(); i++) { ControlledSchedulers schedulers = controlledSchedulers.createNew("V" + i, validators.get(i).getSystemTimeShift()); - WireApi wireApi = + WireApiSub wireApi = localWireHub.createNewPeer( "" + i, validators.get(i).getWireInboundDelay(), @@ -246,7 +246,7 @@ public void run() { // system observer ControlledSchedulers schedulers = controlledSchedulers.createNew("X"); - WireApi wireApi = localWireHub.createNewPeer("X"); + WireApiSub wireApi = localWireHub.createNewPeer("X"); Launcher observer = new Launcher( diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java index c1a0f7fc3..cd87bfe46 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java @@ -55,7 +55,7 @@ import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; -import org.ethereum.beacon.wire.WireApi; +import org.ethereum.beacon.wire.WireApiSub; import org.javatuples.Pair; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -170,7 +170,7 @@ public void run() { for (int i = 0; i < validators.size(); i++) { ControlledSchedulers schedulers = controlledSchedulers.createNew("V" + i, validators.get(i).getSystemTimeShift()); - WireApi wireApi = + WireApiSub wireApi = localWireHub.createNewPeer( "" + i, validators.get(i).getWireInboundDelay(), @@ -247,7 +247,7 @@ public void run() { // system observer ControlledSchedulers schedulers = controlledSchedulers.createNew("X"); - WireApi wireApi = localWireHub.createNewPeer("X"); + WireApiSub wireApi = localWireHub.createNewPeer("X"); Launcher observer = new Launcher( diff --git a/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java b/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java index 2accdc37a..968ded787 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java @@ -21,7 +21,7 @@ import reactor.core.publisher.Flux; public class LocalWireHub { - private class WireImpl implements WireApi { + private class WireImpl implements WireApiSub { Processor blocks = DirectProcessor.create(); Processor attestations = DirectProcessor.create(); String name; @@ -91,24 +91,6 @@ public Publisher inboundAttestationsStream() { .delayElements(Duration.ofMillis(inboundDelay), schedulers.reactorEvents()); } } - - @Override - public Future requestBlockRoots( - BlockHeadersRequestMessage requestMessage) { - throw new UnsupportedOperationException(); - } - - @Override - public Future requestBlockHeaders( - BlockRootsRequestMessage requestMessage) { - throw new UnsupportedOperationException(); - } - - @Override - public Future requestBlockBodies( - BlockBodiesRequestMessage requestMessage) { - throw new UnsupportedOperationException(); - } } List peers = new CopyOnWriteArrayList<>(); @@ -120,11 +102,11 @@ public LocalWireHub(Consumer logger, Schedulers schedulers) { this.schedulers = schedulers; } - public WireApi createNewPeer(String name) { + public WireApiSub createNewPeer(String name) { return createNewPeer(name, 0, 0); } - public WireApi createNewPeer(String name, long inboundDelay, long outboundDelay) { + public WireApiSub createNewPeer(String name, long inboundDelay, long outboundDelay) { WireImpl ret = new WireImpl(name, inboundDelay, outboundDelay); peers.add(ret); return ret; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java index 816e75d72..5d9f3b513 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java @@ -1,7 +1,6 @@ package org.ethereum.beacon.wire; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; import org.ethereum.beacon.wire.message.GoodbyeMessage; import org.ethereum.beacon.wire.message.Message; import org.reactivestreams.Publisher; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApi.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApi.java deleted file mode 100644 index d054a40ad..000000000 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApi.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.ethereum.beacon.wire; - -import java.util.concurrent.Future; -import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.core.operations.Attestation; -import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; -import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; -import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; -import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; -import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; -import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; -import org.reactivestreams.Publisher; - -public interface WireApi { - - void sendProposedBlock(BeaconBlock block); - - void sendAttestation(Attestation attestation); - - Publisher inboundBlocksStream(); - - Publisher inboundAttestationsStream(); - - Future requestBlockRoots(BlockHeadersRequestMessage requestMessage); - - Future requestBlockHeaders(BlockRootsRequestMessage requestMessage); - - Future requestBlockBodies(BlockBodiesRequestMessage requestMessage); -} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java deleted file mode 100644 index 9cae32411..000000000 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApi2.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.ethereum.beacon.wire; - -import java.util.concurrent.CompletableFuture; -import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.core.operations.Attestation; - -public interface WireApi2 { - - CompletableFuture newBlock(BeaconBlock block); - - CompletableFuture newAttestation(Attestation attestation); - -} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java new file mode 100644 index 000000000..5ac2d7fd8 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java @@ -0,0 +1,16 @@ +package org.ethereum.beacon.wire; + +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; +import org.reactivestreams.Publisher; + +public interface WireApiSub { + + void sendProposedBlock(BeaconBlock block); + + void sendAttestation(Attestation attestation); + + Publisher inboundBlocksStream(); + + Publisher inboundAttestationsStream(); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index d10434ccf..8a5c5dacc 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -7,7 +7,6 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import org.ethereum.beacon.chain.storage.BeaconChainStorage; -import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.BeaconBlockBody; import org.ethereum.beacon.core.BeaconBlockHeader; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java index f336d1b1c..b8d418356 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java @@ -1,14 +1,12 @@ package org.ethereum.beacon.wire.message; import java.util.List; -import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; import tech.pegasys.artemis.ethereum.core.Hash32; -import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class BlockBodiesRequestMessage extends Message { +public class BlockBodiesRequestMessage extends MessagePayload { @SSZ private final List blockTreeRoots; public BlockBodiesRequestMessage( diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java index 17897b1af..de5bc3259 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java @@ -4,10 +4,9 @@ import org.ethereum.beacon.core.BeaconBlockBody; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; -import tech.pegasys.artemis.ethereum.core.Hash32; @SSZSerializable -public class BlockBodiesResponseMessage extends Message { +public class BlockBodiesResponseMessage extends MessagePayload { @SSZ private final List blockBodies; public BlockBodiesResponseMessage(List blockBodies) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java index be854cbff..7d968b639 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java @@ -7,7 +7,7 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class BlockHeadersRequestMessage extends Message { +public class BlockHeadersRequestMessage extends MessagePayload { @SSZ private final Hash32 startRoot; @SSZ private final SlotNumber startSlot; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java index 3abe6bce6..5a229a3b8 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java @@ -2,13 +2,11 @@ import java.util.List; import org.ethereum.beacon.core.BeaconBlockHeader; -import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; -import tech.pegasys.artemis.ethereum.core.Hash32; @SSZSerializable -public class BlockHeadersResponseMessage extends Message { +public class BlockHeadersResponseMessage extends MessagePayload { @SSZ private final List headers; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java index 197fe1164..2489b6e09 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java @@ -6,7 +6,7 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class BlockRootsRequestMessage extends Message { +public class BlockRootsRequestMessage extends MessagePayload { @SSZ private final SlotNumber startSlot; @SSZ private final UInt64 count; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java index c9494e589..d351e908b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java @@ -5,10 +5,9 @@ import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; import tech.pegasys.artemis.ethereum.core.Hash32; -import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class BlockRootsResponseMessage extends Message { +public class BlockRootsResponseMessage extends MessagePayload { @SSZSerializable public static class BlockRootSlot { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java index be84bfcad..8b771639d 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java @@ -5,7 +5,7 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class GoodbyeMessage extends Message { +public class GoodbyeMessage extends MessagePayload { public static final UInt64 CLIENT_SHUTDOWN = UInt64.valueOf(1); public static final UInt64 IRRELEVANT_NETWORK = UInt64.valueOf(2); public static final UInt64 ERROR = UInt64.valueOf(3); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java index a80cb44f5..107609948 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java @@ -8,7 +8,7 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class HelloMessage extends Message { +public class HelloMessage extends MessagePayload { @SSZ private final byte networkId; @SSZ private final UInt64 chainId; @SSZ private final Hash32 latestFinalizedRoot; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java index 8606683bc..f3d96cd92 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java @@ -1,4 +1,8 @@ package org.ethereum.beacon.wire.message; +import tech.pegasys.artemis.util.bytes.BytesValue; + public abstract class Message { + + public abstract BytesValue getPayloadBytes(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java b/wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java new file mode 100644 index 000000000..f964bbac2 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java @@ -0,0 +1,4 @@ +package org.ethereum.beacon.wire.message; + +public abstract class MessagePayload { +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java index f817bad6d..ab91d274b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java @@ -1,12 +1,11 @@ package org.ethereum.beacon.wire.message; -import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; @SSZSerializable -public class NotifyNewAttestationMessage extends Message { +public class NotifyNewAttestationMessage extends MessagePayload { @SSZ private final Attestation attestation; public NotifyNewAttestationMessage(Attestation attestation) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java index 631febd15..575e9fe8c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java @@ -1,12 +1,11 @@ package org.ethereum.beacon.wire.message; import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.core.BeaconBlockHeader; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; @SSZSerializable -public class NotifyNewBlockMessage extends Message { +public class NotifyNewBlockMessage extends MessagePayload { @SSZ private final BeaconBlock block; public NotifyNewBlockMessage(BeaconBlock block) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java new file mode 100644 index 000000000..a18e7f380 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java @@ -0,0 +1,39 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class RequestMessage extends Message { + @SSZ + private final UInt64 id; + @SSZ(type = "uint16") + private final int methodId; + @SSZ + private final BytesValue body; + + public RequestMessage(UInt64 id, int methodId, BytesValue body) { + this.id = id; + this.methodId = methodId; + this.body = body; + } + + public UInt64 getId() { + return id; + } + + public int getMethodId() { + return methodId; + } + + public BytesValue getBody() { + return body; + } + + @Override + public BytesValue getPayloadBytes() { + return getBody(); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java new file mode 100644 index 000000000..0b8372ede --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java @@ -0,0 +1,40 @@ +package org.ethereum.beacon.wire.message; + +import org.ethereum.beacon.ssz.annotation.SSZ; +import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; + +@SSZSerializable +public class ResponseMessage extends Message { + @SSZ + private final UInt64 id; + @SSZ(type = "uint16") + private final int responseCode; + @SSZ + private final BytesValue result; + + public ResponseMessage(UInt64 id, int responseCode, + BytesValue result) { + this.id = id; + this.responseCode = responseCode; + this.result = result; + } + + public UInt64 getId() { + return id; + } + + public int getResponseCode() { + return responseCode; + } + + public BytesValue getResult() { + return result; + } + + @Override + public BytesValue getPayloadBytes() { + return getResult(); + } +} From b01d4193a5653b7ad9cefed3b1a8a9c21c1af3f0 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 18 Apr 2019 18:59:13 +0300 Subject: [PATCH 05/96] Differentiate Response and Request MessagePayload. --- .../ethereum/beacon/wire/MessageSerializer.java | 11 +++++++++++ .../ethereum/beacon/wire/PayloadSerializer.java | 11 +++++++++++ .../ethereum/beacon/wire/WireApiSyncServer.java | 8 ++++---- .../wire/message/BlockBodiesRequestMessage.java | 10 +++++++++- .../wire/message/BlockBodiesResponseMessage.java | 8 ++++++-- .../wire/message/BlockHeadersRequestMessage.java | 9 ++++++++- .../wire/message/BlockHeadersResponseMessage.java | 7 +++++-- .../wire/message/BlockRootsRequestMessage.java | 9 ++++++++- .../wire/message/BlockRootsResponseMessage.java | 5 +++-- .../beacon/wire/message/GoodbyeMessage.java | 9 ++++++++- .../beacon/wire/message/HelloMessage.java | 9 ++++++++- .../beacon/wire/message/MessagePayload.java | 2 ++ .../wire/message/NotifyNewAttestationMessage.java | 1 + .../wire/message/RequestMessagePayload.java | 8 ++++++++ .../wire/message/ResponseMessagePayload.java | 15 +++++++++++++++ 15 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java diff --git a/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java b/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java new file mode 100644 index 000000000..611d7add3 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java @@ -0,0 +1,11 @@ +package org.ethereum.beacon.wire; + +import org.ethereum.beacon.wire.message.Message; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public interface MessageSerializer { + + BytesValue serialize(C message); + + C deserialize(Class messageClass, BytesValue messageBytes); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java b/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java new file mode 100644 index 000000000..e9b755db7 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java @@ -0,0 +1,11 @@ +package org.ethereum.beacon.wire; + +import org.ethereum.beacon.wire.message.MessagePayload; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public interface PayloadSerializer { + + BytesValue serialize(C message); + + C deserialize(Class messageClass, BytesValue messageBytes); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index 8a5c5dacc..dfbc9252f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -46,7 +46,7 @@ public CompletableFuture requestBlockRoots( roots.add(new BlockRootSlot(slotRoot, slot)); } } - ret.complete(new BlockRootsResponseMessage(roots)); + ret.complete(new BlockRootsResponseMessage(requestMessage, roots)); } return ret; } @@ -84,10 +84,10 @@ public CompletableFuture requestBlockHeaders( } prevSlot = nonEmptySlot; } - ret.complete(new BlockHeadersResponseMessage(headers)); + ret.complete(new BlockHeadersResponseMessage(requestMessage, headers)); } } else { - ret.complete(new BlockHeadersResponseMessage(Collections.emptyList())); + ret.complete(new BlockHeadersResponseMessage(requestMessage, Collections.emptyList())); } return ret; } @@ -100,6 +100,6 @@ public CompletableFuture requestBlockBodies( .map(blockRoot -> storage.getBlockStorage().get(blockRoot)) .map(opt -> opt.map(BeaconBlock::getBody).orElse(BeaconBlockBody.EMPTY)) .collect(Collectors.toList()); - return CompletableFuture.completedFuture(new BlockBodiesResponseMessage(bodyList)); + return CompletableFuture.completedFuture(new BlockBodiesResponseMessage(requestMessage, bodyList)); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java index b8d418356..3badb2c56 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java @@ -4,9 +4,12 @@ import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class BlockBodiesRequestMessage extends MessagePayload { +public class BlockBodiesRequestMessage extends RequestMessagePayload { + public static final UInt64 METHOD_ID = UInt64.valueOf(0x0E); + @SSZ private final List blockTreeRoots; public BlockBodiesRequestMessage( @@ -14,6 +17,11 @@ public BlockBodiesRequestMessage( this.blockTreeRoots = blockTreeRoots; } + @Override + public UInt64 getMethodId() { + return METHOD_ID; + } + public List getBlockTreeRoots() { return blockTreeRoots; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java index de5bc3259..8db1e9ed7 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java @@ -6,10 +6,14 @@ import org.ethereum.beacon.ssz.annotation.SSZSerializable; @SSZSerializable -public class BlockBodiesResponseMessage extends MessagePayload { +public class BlockBodiesResponseMessage extends ResponseMessagePayload { + @SSZ private final List blockBodies; - public BlockBodiesResponseMessage(List blockBodies) { + public BlockBodiesResponseMessage( + BlockBodiesRequestMessage request, + List blockBodies) { + super(request); this.blockBodies = blockBodies; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java index 7d968b639..56d5fcebf 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java @@ -7,7 +7,9 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class BlockHeadersRequestMessage extends MessagePayload { +public class BlockHeadersRequestMessage extends RequestMessagePayload { + public static final UInt64 METHOD_ID = UInt64.valueOf(0x0D); + @SSZ private final Hash32 startRoot; @SSZ private final SlotNumber startSlot; @@ -22,6 +24,11 @@ public BlockHeadersRequestMessage(Hash32 startRoot, this.skipSlots = skipSlots; } + @Override + public UInt64 getMethodId() { + return METHOD_ID; + } + public Hash32 getStartRoot() { return startRoot; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java index 5a229a3b8..0f6324d82 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java @@ -6,11 +6,14 @@ import org.ethereum.beacon.ssz.annotation.SSZSerializable; @SSZSerializable -public class BlockHeadersResponseMessage extends MessagePayload { +public class BlockHeadersResponseMessage extends ResponseMessagePayload { @SSZ private final List headers; - public BlockHeadersResponseMessage(List headers) { + public BlockHeadersResponseMessage( + BlockHeadersRequestMessage request, + List headers) { + super(request); this.headers = headers; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java index 2489b6e09..4b6a1ef7c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java @@ -6,7 +6,9 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class BlockRootsRequestMessage extends MessagePayload { +public class BlockRootsRequestMessage extends RequestMessagePayload { + public static final UInt64 METHOD_ID = UInt64.valueOf(0x0); + @SSZ private final SlotNumber startSlot; @SSZ private final UInt64 count; @@ -15,6 +17,11 @@ public BlockRootsRequestMessage(SlotNumber startSlot, UInt64 count) { this.count = count; } + @Override + public UInt64 getMethodId() { + return METHOD_ID; + } + public SlotNumber getStartSlot() { return startSlot; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java index d351e908b..0576d8791 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java @@ -7,7 +7,7 @@ import tech.pegasys.artemis.ethereum.core.Hash32; @SSZSerializable -public class BlockRootsResponseMessage extends MessagePayload { +public class BlockRootsResponseMessage extends ResponseMessagePayload { @SSZSerializable public static class BlockRootSlot { @@ -30,8 +30,9 @@ public SlotNumber getSlot() { @SSZ private final List roots; - public BlockRootsResponseMessage( + public BlockRootsResponseMessage(BlockRootsRequestMessage request, List roots) { + super(request); this.roots = roots; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java index 8b771639d..cd0e10d69 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java @@ -5,7 +5,9 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class GoodbyeMessage extends MessagePayload { +public class GoodbyeMessage extends RequestMessagePayload { + public static final UInt64 METHOD_ID = UInt64.valueOf(0x1); + public static final UInt64 CLIENT_SHUTDOWN = UInt64.valueOf(1); public static final UInt64 IRRELEVANT_NETWORK = UInt64.valueOf(2); public static final UInt64 ERROR = UInt64.valueOf(3); @@ -16,6 +18,11 @@ public GoodbyeMessage(UInt64 reason) { this.reason = reason; } + @Override + public UInt64 getMethodId() { + return METHOD_ID; + } + public UInt64 getReason() { return reason; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java index 107609948..8ae4ee3fe 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java @@ -8,7 +8,9 @@ import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable -public class HelloMessage extends MessagePayload { +public class HelloMessage extends RequestMessagePayload { + public static final UInt64 METHOD_ID = UInt64.valueOf(0x0); + @SSZ private final byte networkId; @SSZ private final UInt64 chainId; @SSZ private final Hash32 latestFinalizedRoot; @@ -27,6 +29,11 @@ public HelloMessage(byte networkId, UInt64 chainId, this.bestSlot = bestSlot; } + @Override + public UInt64 getMethodId() { + return METHOD_ID; + } + public byte getNetworkId() { return networkId; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java b/wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java index f964bbac2..140757443 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/MessagePayload.java @@ -1,4 +1,6 @@ package org.ethereum.beacon.wire.message; +import tech.pegasys.artemis.util.uint.UInt64; + public abstract class MessagePayload { } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java index ab91d274b..d60110dc6 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java @@ -6,6 +6,7 @@ @SSZSerializable public class NotifyNewAttestationMessage extends MessagePayload { + @SSZ private final Attestation attestation; public NotifyNewAttestationMessage(Attestation attestation) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java new file mode 100644 index 000000000..332a98517 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java @@ -0,0 +1,8 @@ +package org.ethereum.beacon.wire.message; + +import tech.pegasys.artemis.util.uint.UInt64; + +public abstract class RequestMessagePayload extends MessagePayload { + + public abstract UInt64 getMethodId(); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java new file mode 100644 index 000000000..58f123513 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java @@ -0,0 +1,15 @@ +package org.ethereum.beacon.wire.message; + +public abstract class ResponseMessagePayload + extends MessagePayload { + + private final RequestType request; + + public ResponseMessagePayload(RequestType request) { + this.request = request; + } + + public RequestType getRequest() { + return request; + } +} From 24c7cd3b76568cd5d6c3fef89d54ed8dc6c80ecc Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 22 Apr 2019 16:23:01 +0300 Subject: [PATCH 06/96] Draft intermediate commit. Add some sync related code --- .../org/ethereum/beacon/core/BeaconBlock.java | 9 ++++ wire/build.gradle | 1 + .../org/ethereum/beacon/wire/WireApiSync.java | 44 ++++++++++++++++ .../WireInvalidResponseException.java | 8 +++ .../ethereum/beacon/wire/message/Message.java | 2 +- .../beacon/wire/message/RequestMessage.java | 4 +- .../beacon/wire/message/ResponseMessage.java | 4 +- .../beacon/wire/sync/SyncManager.java | 51 +++++++++++++++++++ 8 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java diff --git a/core/src/main/java/org/ethereum/beacon/core/BeaconBlock.java b/core/src/main/java/org/ethereum/beacon/core/BeaconBlock.java index 524a2b8ee..a0c26d28c 100644 --- a/core/src/main/java/org/ethereum/beacon/core/BeaconBlock.java +++ b/core/src/main/java/org/ethereum/beacon/core/BeaconBlock.java @@ -59,6 +59,15 @@ public BeaconBlock( this.signature = signature; } + public BeaconBlock(BeaconBlockHeader header, BeaconBlockBody body) { + this( + header.getSlot(), + header.getPreviousBlockRoot(), + header.getStateRoot(), + body, + header.getSignature()); + } + @Override public Optional getHash() { return Optional.ofNullable(hashCache); diff --git a/wire/build.gradle b/wire/build.gradle index eaac352ee..33bb5985f 100644 --- a/wire/build.gradle +++ b/wire/build.gradle @@ -1,6 +1,7 @@ dependencies { implementation project(':types') implementation project(':core') + implementation project(':consensus') implementation project(':util') implementation project(':ssz') implementation project(':chain') diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java index c61598e5e..2abfb451c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -1,12 +1,21 @@ package org.ethereum.beacon.wire; +import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import org.ethereum.beacon.consensus.hasher.ObjectHasher; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.BeaconBlockBody; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.wire.exceptions.WireInvalidResponseException; import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; +import tech.pegasys.artemis.ethereum.core.Hash32; public interface WireApiSync { int MAX_BLOCK_ROOTS_COUNT = 32768; @@ -19,4 +28,39 @@ CompletableFuture requestBlockHeaders( CompletableFuture requestBlockBodies( BlockBodiesRequestMessage requestMessage); + + default CompletableFuture> requestBlocks( + BlockHeadersRequestMessage requestMessage, ObjectHasher hasher) { + + CompletableFuture> headersFuture = requestBlockHeaders( + requestMessage).thenApply(BlockHeadersResponseMessage::getHeaders); + + CompletableFuture> bodiesFuture = headersFuture + .thenCompose(headers -> { + List blockHashes = headers.stream() + .map(BeaconBlockHeader::getBlockBodyRoot) + .collect(Collectors.toList()); + return requestBlockBodies(new BlockBodiesRequestMessage(blockHashes)) + .thenApply(BlockBodiesResponseMessage::getBlockBodies); + }); + + return headersFuture.thenCombine(bodiesFuture, + (headers, bodies) -> { + Map bodyMap = + bodies.stream().collect(Collectors.toMap(hasher::getHash, b -> b)); + return headers + .stream() + .map( + h -> { + BeaconBlockBody body = bodyMap.get(h.getBlockBodyRoot()); + if (body != null) { + return new BeaconBlock(h, body); + } else { + return null; + } + }) + .collect(Collectors.toList()); + }); + } + } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java new file mode 100644 index 000000000..a0aba3b1f --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java @@ -0,0 +1,8 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireInvalidResponseException extends WireException { + + public WireInvalidResponseException(String message) { + super(message); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java index f3d96cd92..50a7d886e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java @@ -4,5 +4,5 @@ public abstract class Message { - public abstract BytesValue getPayloadBytes(); + public abstract MessagePayload getPayload(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java index a18e7f380..fba861d6e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java @@ -33,7 +33,7 @@ public BytesValue getBody() { } @Override - public BytesValue getPayloadBytes() { - return getBody(); + public RequestMessagePayload getPayload() { + return null; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java index 0b8372ede..fa772d166 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java @@ -34,7 +34,7 @@ public BytesValue getResult() { } @Override - public BytesValue getPayloadBytes() { - return getResult(); + public ResponseMessagePayload getPayload() { + return null; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java new file mode 100644 index 000000000..9794c3306 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -0,0 +1,51 @@ +package org.ethereum.beacon.wire.sync; + +import java.time.Duration; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.chain.MutableBeaconChain; +import org.ethereum.beacon.chain.storage.BeaconChainStorage; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.types.EpochNumber; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +public class SyncManager { + MutableBeaconChain chain; + BeaconChainStorage storage; + WireApiSync syncApi; + BeaconChainSpec spec; + + int maxBlockRequest; + + public void start() { + Flux finalizedEpochStream = Flux.from(chain.getBlockStatesStream()) + .map(bs -> bs.getFinalState().getFinalizedEpoch()) + .distinct(); + Flux finalizedSlotStream = finalizedEpochStream. + map(epoch -> spec.get_epoch_start_slot(epoch) + .plus(spec.getConstants().getSlotsPerEpoch())) + .distinct(); + Flux finalizedBlockRootStream = Flux + .from(chain.getBlockStatesStream()) + .map(bs -> bs.getFinalState().getFinalizedRoot()) + .distinct(); + + Hash32 finalBlock = finalizedBlockRootStream.last().block(Duration.ofSeconds(10)); + + CompletableFuture> blocksFut = syncApi.requestBlocks( + new BlockHeadersRequestMessage( + finalBlock, SlotNumber.ZERO, UInt64.valueOf(maxBlockRequest), UInt64.ZERO), + spec.getObjectHasher()); + + blocksFut.thenAccept(blocks -> blocks.forEach(chain::insert)); + + } + +} From a2edd670e2d1dd85cbc1327bed383bb5395c5391 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 23 Apr 2019 19:24:29 +0300 Subject: [PATCH 07/96] Intermediate sync manager draft commit --- .../org/ethereum/beacon/wire/Feedback.java | 61 +++++++++++++++++++ .../org/ethereum/beacon/wire/WireApiSync.java | 16 +++-- .../WireInvalidConsensusDataException.java | 12 ++++ .../beacon/wire/sync/SyncManager.java | 48 ++++++++------- .../ethereum/beacon/wire/sync/SyncQueue.java | 34 +++++++++++ 5 files changed, 139 insertions(+), 32 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/Feedback.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java b/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java new file mode 100644 index 000000000..ed3d43517 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java @@ -0,0 +1,61 @@ +package org.ethereum.beacon.wire; + +import java.util.concurrent.CompletableFuture; + +public interface Feedback { + + static Feedback of(T result) { + return new Impl<>(result); + } + + TResult get(); + + void feedbackSuccess(); + + void feedbackError(Throwable e); + + CompletableFuture getFeedback(); + + Feedback delegate(TOtherResult otherResult); + + class Impl implements Feedback { + private final TResult result; + private final CompletableFuture feedback = new CompletableFuture<>(); + + private Impl(TResult result) { + this.result = result; + } + + @Override + public TResult get() { + return result; + } + + @Override + public void feedbackSuccess() { + feedback.complete(null); + } + + @Override + public void feedbackError(Throwable e) { + feedback.completeExceptionally(e); + } + + public CompletableFuture getFeedback() { + return feedback; + } + + @Override + public Feedback delegate(TOtherResult otherResult) { + Impl ret = new Impl<>(otherResult); + ret.getFeedback().whenComplete((v, t) -> { + if (t == null) { + feedbackSuccess(); + } else { + feedbackError(t); + } + }); + return ret; + } + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java index 2abfb451c..3843695e9 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -8,7 +8,6 @@ import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.BeaconBlockBody; import org.ethereum.beacon.core.BeaconBlockHeader; -import org.ethereum.beacon.wire.exceptions.WireInvalidResponseException; import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; @@ -26,29 +25,29 @@ CompletableFuture requestBlockRoots( CompletableFuture requestBlockHeaders( BlockHeadersRequestMessage requestMessage); - CompletableFuture requestBlockBodies( + CompletableFuture> requestBlockBodies( BlockBodiesRequestMessage requestMessage); - default CompletableFuture> requestBlocks( + default CompletableFuture>> requestBlocks( BlockHeadersRequestMessage requestMessage, ObjectHasher hasher) { CompletableFuture> headersFuture = requestBlockHeaders( requestMessage).thenApply(BlockHeadersResponseMessage::getHeaders); - CompletableFuture> bodiesFuture = headersFuture + CompletableFuture>> bodiesFuture = headersFuture .thenCompose(headers -> { List blockHashes = headers.stream() .map(BeaconBlockHeader::getBlockBodyRoot) .collect(Collectors.toList()); return requestBlockBodies(new BlockBodiesRequestMessage(blockHashes)) - .thenApply(BlockBodiesResponseMessage::getBlockBodies); + .thenApply(bb -> bb.delegate(bb.get().getBlockBodies())); }); return headersFuture.thenCombine(bodiesFuture, (headers, bodies) -> { Map bodyMap = - bodies.stream().collect(Collectors.toMap(hasher::getHash, b -> b)); - return headers + bodies.get().stream().collect(Collectors.toMap(hasher::getHash, b -> b)); + return bodies.delegate(headers .stream() .map( h -> { @@ -59,8 +58,7 @@ default CompletableFuture> requestBlocks( return null; } }) - .collect(Collectors.toList()); + .collect(Collectors.toList())); }); } - } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java new file mode 100644 index 000000000..b80e17d3a --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java @@ -0,0 +1,12 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireInvalidConsensusDataException extends WireException { + + public WireInvalidConsensusDataException(String message) { + super(message); + } + + public WireInvalidConsensusDataException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index 9794c3306..a61bbb855 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -1,20 +1,16 @@ package org.ethereum.beacon.wire.sync; -import java.time.Duration; -import java.util.List; -import java.util.concurrent.CompletableFuture; import org.ethereum.beacon.chain.MutableBeaconChain; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.core.types.EpochNumber; -import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.exceptions.WireInvalidConsensusDataException; import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import tech.pegasys.artemis.ethereum.core.Hash32; -import tech.pegasys.artemis.util.uint.UInt64; public class SyncManager { MutableBeaconChain chain; @@ -24,28 +20,34 @@ public class SyncManager { int maxBlockRequest; + SyncQueue syncQueue; + public void start() { - Flux finalizedEpochStream = Flux.from(chain.getBlockStatesStream()) - .map(bs -> bs.getFinalState().getFinalizedEpoch()) - .distinct(); - Flux finalizedSlotStream = finalizedEpochStream. - map(epoch -> spec.get_epoch_start_slot(epoch) - .plus(spec.getConstants().getSlotsPerEpoch())) - .distinct(); + Flux finalizedBlockRootStream = Flux .from(chain.getBlockStatesStream()) .map(bs -> bs.getFinalState().getFinalizedRoot()) .distinct(); - Hash32 finalBlock = finalizedBlockRootStream.last().block(Duration.ofSeconds(10)); - - CompletableFuture> blocksFut = syncApi.requestBlocks( - new BlockHeadersRequestMessage( - finalBlock, SlotNumber.ZERO, UInt64.valueOf(maxBlockRequest), UInt64.ZERO), - spec.getObjectHasher()); - - blocksFut.thenAccept(blocks -> blocks.forEach(chain::insert)); - + syncQueue.subscribeToFinalBlocks(finalizedBlockRootStream); + + Flux.from(syncQueue.getBlocksStream()).subscribe(block -> { + if (!chain.insert(block.get())) { + block.feedbackError(new WireInvalidConsensusDataException("Couldn't insert block: " + block.get())); + } else { + block.feedbackSuccess(); + } + }); + Flux> wireBlocksStream = Flux + .from(syncQueue.getBlockRequestsStream()) + .map(req -> new BlockHeadersRequestMessage( + req.getStartRoot().get(), + req.getStartSlot().get(), + req.getMaxCount(), + req.getStep())) + .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher()))) + .flatMap(resp -> Flux.fromStream(resp.get().stream().map(resp::delegate))); + + syncQueue.subscribeToNewBlocks(wireBlocksStream); } - } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java new file mode 100644 index 000000000..fd918ad62 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java @@ -0,0 +1,34 @@ +package org.ethereum.beacon.wire.sync; + +import java.util.List; +import java.util.Optional; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.wire.Feedback; +import org.reactivestreams.Publisher; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +public interface SyncQueue { + + interface BlockRequest { + + Optional getStartSlot(); + + Optional getStartRoot(); + + UInt64 getMaxCount(); + + boolean isReverse(); + + UInt64 getStep(); + } + + Publisher getBlockRequestsStream(); + + Publisher> getBlocksStream(); + + void subscribeToFinalBlocks(Publisher finalBlockRootStream); + + void subscribeToNewBlocks(Publisher> blocksStream); +} From ad2261d6c26335363eb1ea7c9511fbd65117173b Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 25 Apr 2019 12:25:18 +0300 Subject: [PATCH 08/96] Implement simple sync strategy --- .../beacon/wire/WireApiSyncServer.java | 7 +- .../beacon/wire/sync/AbstractBlockTree.java | 97 +++++++++++++++++++ .../beacon/wire/sync/BeaconBlockTree.java | 49 ++++++++++ .../ethereum/beacon/wire/sync/BlockTree.java | 33 +++++++ .../beacon/wire/sync/SyncManager.java | 23 ++++- .../ethereum/beacon/wire/sync/SyncQueue.java | 57 ++++++++--- .../beacon/wire/sync/SyncQueueImpl.java | 88 +++++++++++++++++ 7 files changed, 331 insertions(+), 23 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index dfbc9252f..3e0ee5dc1 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -92,14 +92,17 @@ public CompletableFuture requestBlockHeaders( return ret; } + + @Override - public CompletableFuture requestBlockBodies( + public CompletableFuture> requestBlockBodies( BlockBodiesRequestMessage requestMessage) { List bodyList = requestMessage.getBlockTreeRoots().stream() .map(blockRoot -> storage.getBlockStorage().get(blockRoot)) .map(opt -> opt.map(BeaconBlock::getBody).orElse(BeaconBlockBody.EMPTY)) .collect(Collectors.toList()); - return CompletableFuture.completedFuture(new BlockBodiesResponseMessage(requestMessage, bodyList)); + return CompletableFuture.completedFuture( + Feedback.of(new BlockBodiesResponseMessage(requestMessage, bodyList))); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java new file mode 100644 index 000000000..6a765c668 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java @@ -0,0 +1,97 @@ +package org.ethereum.beacon.wire.sync; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.ethereum.beacon.wire.sync.AbstractBlockTree.BlockWrap; + +public abstract class AbstractBlockTree, TRawBlock> + implements BlockTree { + + interface BlockWrap extends Block { + TRawBlock get(); + } + + private TBlock topBlock; + private final Map hashMap = new HashMap<>(); + private final Map> childrenMap = new HashMap<>(); + + protected abstract TBlock wrap(TRawBlock origBlock); + + public List addBlock(TRawBlock block) { + return addBlock(wrap(block)).stream().map(BlockWrap::get).collect(Collectors.toList()); + } + + public void setTopBlock(TRawBlock block) { + setTopBlock(wrap(block)); + } + + @Nonnull + @Override + public List addBlock(@Nonnull TBlock block) { + if (topBlock == null) { + throw new IllegalStateException("Top block should be set first"); + } + if (hashMap.containsKey(block.getHash())) return Collections.emptyList(); + if (topBlock.getHeight() >= block.getHeight()) return Collections.emptyList(); + hashMap.put(block.getHash(), block); + childrenMap.computeIfAbsent(block.getParentHash(), r -> new ArrayList<>()).add(block.getHash()); + + List ret = new ArrayList<>(); + if (isRootSuccessor(block)) { + ret.add(block); + addChildrenRecursively(block.getHash(), ret); + } + return ret; + } + + private boolean isRootSuccessor(TBlock block) { + while (block != null) { + if (block.getParentHash().equals(topBlock.getHash())) { + return true; + } + block = hashMap.get(block.getParentHash()); + } + return false; + } + + private void addChildrenRecursively(THash blockHash, List successors) { + List blockChildren = childrenMap.getOrDefault(blockHash, Collections.emptyList()); + for (THash childHash : blockChildren) { + successors.add(hashMap.get(childHash)); + addChildrenRecursively(childHash, successors); + } + } + + @Override + public void setTopBlock(@Nonnull TBlock block) { + if (topBlock == null) { + topBlock = block; + return; + } + if (!hashMap.containsKey(block.getHash())) { + throw new IllegalArgumentException("setTopBlock() should be called with existing block or to initialize"); + } + Iterator> iterator = hashMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + if (entry.getValue().getHeight() <= block.getHeight() + && !entry.getKey().equals(block.getHash())) { + iterator.remove(); + childrenMap.remove(entry.getKey()); + } + } + } + + @Nonnull + @Override + public TBlock getTopBlock() { + return topBlock; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java new file mode 100644 index 000000000..7c877d255 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java @@ -0,0 +1,49 @@ +package org.ethereum.beacon.wire.sync; + +import org.ethereum.beacon.consensus.hasher.ObjectHasher; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.wire.Feedback; +import org.ethereum.beacon.wire.sync.BeaconBlockTree.BlockWrapper; +import tech.pegasys.artemis.ethereum.core.Hash32; + +public class BeaconBlockTree extends AbstractBlockTree> { + + private final ObjectHasher hasher; + + protected class BlockWrapper implements BlockWrap> { + private final Feedback block; + + public BlockWrapper(Feedback block) { + this.block = block; + } + + @Override + public Hash32 getHash() { + return hasher.getHash(block.get()); + } + + @Override + public Hash32 getParentHash() { + return block.get().getPreviousBlockRoot(); + } + + @Override + public long getHeight() { + return block.get().getSlot().longValue(); + } + + @Override + public Feedback get() { + return block; + } + } + + public BeaconBlockTree(ObjectHasher hasher) { + this.hasher = hasher; + } + + @Override + protected BlockWrapper wrap(Feedback origBlock) { + return new BlockWrapper(origBlock); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java new file mode 100644 index 000000000..907c59c98 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java @@ -0,0 +1,33 @@ +package org.ethereum.beacon.wire.sync; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import javax.annotation.Nonnull; +import org.ethereum.beacon.wire.sync.BlockTree.Block; + +public interface BlockTree> { + + interface Block { + + THash getHash(); + + THash getParentHash(); + + long getHeight(); + } + + @Nonnull List addBlock(@Nonnull TBlock block); + + default List addChainedBlocks(Collection blocks) { + List ret = new ArrayList<>(); + for (TBlock block : blocks) { + ret.addAll(addBlock(block)); + } + return ret; + } + + void setTopBlock(@Nonnull TBlock block); + + @Nonnull TBlock getTopBlock(); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index a61bbb855..4b605642a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -1,5 +1,9 @@ package org.ethereum.beacon.wire.sync; +import java.util.List; +import java.util.Optional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ethereum.beacon.chain.MutableBeaconChain; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.consensus.BeaconChainSpec; @@ -13,12 +17,14 @@ import tech.pegasys.artemis.ethereum.core.Hash32; public class SyncManager { + private static final Logger logger = LogManager.getLogger(SyncManager.class); + MutableBeaconChain chain; BeaconChainStorage storage; WireApiSync syncApi; BeaconChainSpec spec; - int maxBlockRequest; + int maxConcurrentBlockRequests = 32; SyncQueue syncQueue; @@ -29,7 +35,12 @@ public void start() { .map(bs -> bs.getFinalState().getFinalizedRoot()) .distinct(); - syncQueue.subscribeToFinalBlocks(finalizedBlockRootStream); + Flux finalizedBlockStream = + finalizedBlockRootStream.map( + root -> + storage.getBlockStorage().get(root).orElseThrow(() -> new IllegalStateException())); + + syncQueue.subscribeToFinalBlocks(finalizedBlockStream); Flux.from(syncQueue.getBlocksStream()).subscribe(block -> { if (!chain.insert(block.get())) { @@ -38,15 +49,17 @@ public void start() { block.feedbackSuccess(); } }); - Flux> wireBlocksStream = Flux + + Flux>> wireBlocksStream = Flux .from(syncQueue.getBlockRequestsStream()) .map(req -> new BlockHeadersRequestMessage( req.getStartRoot().get(), req.getStartSlot().get(), req.getMaxCount(), req.getStep())) - .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher()))) - .flatMap(resp -> Flux.fromStream(resp.get().stream().map(resp::delegate))); + .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher())), + maxConcurrentBlockRequests) + .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o)); syncQueue.subscribeToNewBlocks(wireBlocksStream); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java index fd918ad62..99adc5427 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java @@ -6,29 +6,54 @@ import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.wire.Feedback; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; public interface SyncQueue { - interface BlockRequest { - - Optional getStartSlot(); - - Optional getStartRoot(); - - UInt64 getMaxCount(); - - boolean isReverse(); - - UInt64 getStep(); - } - Publisher getBlockRequestsStream(); Publisher> getBlocksStream(); - void subscribeToFinalBlocks(Publisher finalBlockRootStream); - - void subscribeToNewBlocks(Publisher> blocksStream); + void subscribeToFinalBlocks(Flux finalBlockRootStream); + + void subscribeToNewBlocks(Publisher>> blocksStream); + + class BlockRequest { + private final SlotNumber startSlot; + private final Hash32 startRoot; + private final UInt64 maxCount; + private final boolean reverse; + private final UInt64 step; + + public BlockRequest(SlotNumber startSlot, Hash32 startRoot, + int maxCount, boolean reverse, int step) { + this.startSlot = startSlot; + this.startRoot = startRoot; + this.maxCount = UInt64.valueOf(maxCount); + this.reverse = reverse; + this.step = UInt64.valueOf(step); + } + + public Optional getStartSlot() { + return Optional.ofNullable(startSlot); + } + + public Optional getStartRoot() { + return Optional.ofNullable(startRoot); + } + + public UInt64 getMaxCount() { + return maxCount; + } + + public boolean isReverse() { + return reverse; + } + + public UInt64 getStep() { + return step; + } + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java new file mode 100644 index 000000000..09a80b414 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java @@ -0,0 +1,88 @@ +package org.ethereum.beacon.wire.sync; + +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.wire.Feedback; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.ReplayProcessor; + +public class SyncQueueImpl implements SyncQueue { + private static final Logger logger = LogManager.getLogger(SyncQueueImpl.class); + + private final BeaconBlockTree blockTree; + private final int maxBlocksRequest; + private final int maxHeightFromFinal; + + private final ReplayProcessor> readyBlocks = ReplayProcessor.cacheLast(); + private final ReplayProcessor> blockRequests = ReplayProcessor.cacheLast(); + private BeaconBlock finalBlock; + + public SyncQueueImpl(BeaconBlockTree blockTree, int maxBlocksRequest, int maxHeightFromFinal) { + this.blockTree = blockTree; + this.maxBlocksRequest = maxBlocksRequest; + this.maxHeightFromFinal = maxHeightFromFinal; + } + + public SyncQueueImpl(BeaconBlockTree blockTree) { + this(blockTree, 128, 4096); + } + + @Override + public Publisher getBlockRequestsStream() { + return Flux.switchOnNext(blockRequests, 1); + } + + @Override + public Publisher> getBlocksStream() { + return readyBlocks; + } + + protected Flux createBlockRequests() { + return Flux.generate( + () -> finalBlock.getSlot(), + (slot, sink) -> { + if (slot.greater(finalBlock.getSlot().plus(maxHeightFromFinal))) { + slot = finalBlock.getSlot(); + } + sink.next(new BlockRequest(slot, null, maxBlocksRequest, false, 0)); + return slot.plus(maxBlocksRequest); + }); + } + + protected void onNewFinalBlock(BeaconBlock finalBlock) { + blockTree.setTopBlock(Feedback.of(finalBlock)); + if (this.finalBlock == null) { + this.finalBlock = finalBlock; +// blockRequests.onNext(createBlockRequests()); + } + } + + protected void onInvalidBlock(BeaconBlock block) { + logger.warn("Invalid block received: " + block); + } + + protected void onNewBlock(Feedback block) { + block.getFeedback().whenComplete((v,t) -> { + if (t != null) { + onInvalidBlock(block.get()); + } + }); + + blockTree.addBlock(block).forEach(readyBlocks::onNext); + } + + @Override + public void subscribeToFinalBlocks(Flux finalBlockRootStream) { + Flux.from(finalBlockRootStream).subscribe(this::onNewFinalBlock); + } + + @Override + public void subscribeToNewBlocks(Publisher>> blocksStream) { + Flux.from(blocksStream) + .flatMap(resp -> Flux.fromStream(resp.get().stream().map(resp::delegate))) + .subscribe(this::onNewBlock); + } +} From 98c922a66a887314271b910e1ed47a18735a9db2 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 26 Apr 2019 18:26:20 +0300 Subject: [PATCH 09/96] Channel draft implementation --- .../ethereum/beacon/wire/LocalWireHub.java | 7 -- .../beacon/wire/MessageSerializer.java | 4 +- .../beacon/wire/PayloadSerializer.java | 4 +- .../java/org/ethereum/beacon/wire/Peer.java | 2 +- .../org/ethereum/beacon/wire/WireApiSync.java | 12 +- .../beacon/wire/WireApiSyncServer.java | 14 +-- .../beacon/wire/channel/BeaconPipeline.java | 114 ++++++++++++++++++ .../ethereum/beacon/wire/channel/Channel.java | 11 ++ .../beacon/wire/channel/ChannelCodec.java | 35 ++++++ .../beacon/wire/channel/ChannelFilter.java | 30 +++++ .../beacon/wire/channel/ChannelOp.java | 6 + .../beacon/wire/channel/ChannelSplitter.java | 9 ++ .../beacon/wire/channel/IdentityChannel.java | 32 +++++ .../beacon/wire/channel/RpcChannel.java | 8 ++ .../wire/channel/RpcChannelAdapter.java | 74 ++++++++++++ .../wire/channel/RpcChannelClassFilter.java | 47 ++++++++ .../beacon/wire/channel/RpcChannelMapper.java | 86 +++++++++++++ .../beacon/wire/channel/RpcMessage.java | 97 +++++++++++++++ .../wire/message/ResponseMessagePayload.java | 8 +- .../BlockBodiesRequestMessage.java | 3 +- .../BlockBodiesResponseMessage.java | 5 +- .../BlockHeadersRequestMessage.java | 3 +- .../BlockHeadersResponseMessage.java | 5 +- .../BlockRootsRequestMessage.java | 3 +- .../BlockRootsResponseMessage.java | 5 +- .../message/{ => payload}/GoodbyeMessage.java | 3 +- .../message/{ => payload}/HelloMessage.java | 3 +- .../beacon/wire/message/payload/Messages.java | 47 ++++++++ .../NotifyNewAttestationMessage.java | 3 +- .../{ => payload}/NotifyNewBlockMessage.java | 3 +- .../beacon/wire/sync/SyncManager.java | 3 +- 31 files changed, 642 insertions(+), 44 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelCodec.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelFilter.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/BlockBodiesRequestMessage.java (86%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/BlockBodiesResponseMessage.java (82%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/BlockHeadersRequestMessage.java (91%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/BlockHeadersResponseMessage.java (82%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/BlockRootsRequestMessage.java (86%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/BlockRootsResponseMessage.java (88%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/GoodbyeMessage.java (86%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/HelloMessage.java (93%) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/payload/Messages.java rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/NotifyNewAttestationMessage.java (81%) rename wire/src/main/java/org/ethereum/beacon/wire/message/{ => payload}/NotifyNewBlockMessage.java (79%) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java b/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java index 968ded787..98a63e099 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/LocalWireHub.java @@ -4,17 +4,10 @@ import java.util.Date; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Future; import java.util.function.Consumer; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.schedulers.Schedulers; -import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; -import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; -import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; -import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; -import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; -import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; import org.reactivestreams.Processor; import org.reactivestreams.Publisher; import reactor.core.publisher.DirectProcessor; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java b/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java index 611d7add3..d4b33394e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java @@ -5,7 +5,7 @@ public interface MessageSerializer { - BytesValue serialize(C message); + BytesValue serialize(Message message); - C deserialize(Class messageClass, BytesValue messageBytes); + Message deserialize(BytesValue messageBytes); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java b/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java index e9b755db7..f32ae405c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java @@ -5,7 +5,7 @@ public interface PayloadSerializer { - BytesValue serialize(C message); + BytesValue serialize(MessagePayload message); - C deserialize(Class messageClass, BytesValue messageBytes); + MessagePayload deserialize(BytesValue messageBytes); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java index 5d9f3b513..7034ce81c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java @@ -1,7 +1,7 @@ package org.ethereum.beacon.wire; import java.util.concurrent.CompletableFuture; -import org.ethereum.beacon.wire.message.GoodbyeMessage; +import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; import org.ethereum.beacon.wire.message.Message; import org.reactivestreams.Publisher; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java index 3843695e9..3779ca551 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -8,12 +8,12 @@ import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.BeaconBlockBody; import org.ethereum.beacon.core.BeaconBlockHeader; -import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; -import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; -import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; -import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; -import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; -import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; import tech.pegasys.artemis.ethereum.core.Hash32; public interface WireApiSync { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index 3e0ee5dc1..82741c2be 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -12,13 +12,13 @@ import org.ethereum.beacon.core.BeaconBlockHeader; import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.wire.exceptions.WireIllegalArgumentsException; -import org.ethereum.beacon.wire.message.BlockBodiesRequestMessage; -import org.ethereum.beacon.wire.message.BlockBodiesResponseMessage; -import org.ethereum.beacon.wire.message.BlockRootsRequestMessage; -import org.ethereum.beacon.wire.message.BlockHeadersResponseMessage; -import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; -import org.ethereum.beacon.wire.message.BlockRootsResponseMessage; -import org.ethereum.beacon.wire.message.BlockRootsResponseMessage.BlockRootSlot; +import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage.BlockRootSlot; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java new file mode 100644 index 000000000..fa2d076bc --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java @@ -0,0 +1,114 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import org.ethereum.beacon.wire.Feedback; +import org.ethereum.beacon.wire.MessageSerializer; +import org.ethereum.beacon.wire.PayloadSerializer; +import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.message.Message; +import org.ethereum.beacon.wire.message.MessagePayload; +import org.ethereum.beacon.wire.message.RequestMessage; +import org.ethereum.beacon.wire.message.RequestMessagePayload; +import org.ethereum.beacon.wire.message.ResponseMessage; +import org.ethereum.beacon.wire.message.ResponseMessagePayload; +import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class BeaconPipeline { + + Channel rawChannel; + MessageSerializer messageSerializer; + PayloadSerializer payloadSerializer; + + void create() { + Channel messageChannel = new ChannelCodec<>(rawChannel, + messageSerializer::deserialize, messageSerializer::serialize); + RpcChannelMapper rpcMessageChannel = null; // = TODO + ChannelCodec< + RpcMessage, + RpcMessage> + payloadCodec = null; // TODO + // new ChannelCodec<>(rpcMessageChannel, msg -> { + // if (msg.isRequest()) { + // return msg.copyWithRequest( + // (RequestMessagePayload) + // payloadSerializer.deserialize(msg.getRequest().getBody())); + // } else { + // return msg.copyWithResponse((ResponseMessagePayload) + // payloadSerializer + // .deserialize(msg.getResponse().get().getResult())); + // } + // }, msg -> { + // if (msg.isRequest()) { + // return new + // RequestMessage()msg.copyWithRequest(payloadSerializer.serialize(msg.getRequest())); + // } else { + // return msg.copyWithResponse(payloadSerializer.serialize(msg.getResponse().get())); + // } + // }); + RpcChannel inboundResponsePayloadValidator = RpcChannel + .from(new IdentityChannel>( + payloadCodec) { + @Override + protected void onOutbound(RpcMessage msg) + throws RuntimeException { + if (msg.isRequest()) { + msg.pushRequestContext("validatorRequestMessgae", msg.getRequest()); + } + } + + @Override + protected void onInbound(RpcMessage msg) + throws RuntimeException { + if (msg.isResponse()) { + RequestMessagePayload request = (RequestMessagePayload) msg + .popRequestContext("validatorRequestMessgae"); + ResponseMessagePayload response = msg.getResponse().get(); + + // validate response against request + } + } + }); + + WireApiSync syncServer = null; + + RpcChannelAdapter blockRootsAsync = + new RpcChannelAdapter<>(RpcChannel.from( + new RpcChannelClassFilter<>(inboundResponsePayloadValidator, BlockRootsRequestMessage.class)), + syncServer::requestBlockRoots); + RpcChannelAdapter blockHeadersAsync = + new RpcChannelAdapter<>(RpcChannel.from( + new RpcChannelClassFilter<>(inboundResponsePayloadValidator, BlockHeadersRequestMessage.class)), + syncServer::requestBlockHeaders); + RpcChannelAdapter blockBodiesAsync = + new RpcChannelAdapter<>(RpcChannel.from( + new RpcChannelClassFilter<>(inboundResponsePayloadValidator, BlockBodiesRequestMessage.class)), + req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get)); + + WireApiSync syncClient = new WireApiSync() { + @Override + public CompletableFuture requestBlockRoots( + BlockRootsRequestMessage requestMessage) { + return blockRootsAsync.invokeRemote(requestMessage); + } + + @Override + public CompletableFuture requestBlockHeaders( + BlockHeadersRequestMessage requestMessage) { + return blockHeadersAsync.invokeRemote(requestMessage); + } + + @Override + public CompletableFuture> requestBlockBodies( + BlockBodiesRequestMessage requestMessage) { + return blockBodiesAsync.invokeRemote(requestMessage).thenApply(Feedback::of); + } + }; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java new file mode 100644 index 000000000..ebdfc6600 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java @@ -0,0 +1,11 @@ +package org.ethereum.beacon.wire.channel; + +import org.reactivestreams.Publisher; + +public interface Channel { + + Publisher inboundMessageStream(); + + void subscribeToOutbound(Publisher outboundMessageStream); + +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelCodec.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelCodec.java new file mode 100644 index 000000000..6c26434f6 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelCodec.java @@ -0,0 +1,35 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.function.Function; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +public class ChannelCodec implements ChannelOp { + + private final Channel inChannel; + private final Function messageDecoder; + private final Function messageEncoder; + + public ChannelCodec(Channel inChannel, + Function messageDecoder, + Function messageEncoder) { + this.inChannel = inChannel; + this.messageDecoder = messageDecoder; + this.messageEncoder = messageEncoder; + } + + @Override + public Publisher inboundMessageStream() { + return Flux.from(inChannel.inboundMessageStream()).map(messageDecoder); + } + + @Override + public void subscribeToOutbound(Publisher outboundMessageStream) { + inChannel.subscribeToOutbound(Flux.from(outboundMessageStream).map(messageEncoder)); + } + + @Override + public Channel getInChannel() { + return inChannel; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelFilter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelFilter.java new file mode 100644 index 000000000..51198c405 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelFilter.java @@ -0,0 +1,30 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.function.Predicate; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +public class ChannelFilter implements ChannelOp { + private final Channel inChannel; + private final Predicate filter; + + public ChannelFilter(Channel inChannel, Predicate filter) { + this.inChannel = inChannel; + this.filter = filter; + } + + @Override + public Channel getInChannel() { + return inChannel; + } + + @Override + public Publisher inboundMessageStream() { + return Flux.from(getInChannel().inboundMessageStream()).filter(filter); + } + + @Override + public void subscribeToOutbound(Publisher outboundMessageStream) { + getInChannel().subscribeToOutbound(Flux.from(outboundMessageStream).filter(filter)); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java new file mode 100644 index 000000000..9953f16dc --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java @@ -0,0 +1,6 @@ +package org.ethereum.beacon.wire.channel; + +public interface ChannelOp extends Channel { + + Channel getInChannel(); +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java new file mode 100644 index 000000000..9d824b01e --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java @@ -0,0 +1,9 @@ +package org.ethereum.beacon.wire.channel; + +public interface ChannelSplitter { + + Channel getInChannel(); + + + +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java new file mode 100644 index 000000000..c150b4bc3 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java @@ -0,0 +1,32 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.function.Predicate; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +public abstract class IdentityChannel implements ChannelOp { + private final Channel inChannel; + + public IdentityChannel(Channel inChannel) { + this.inChannel = inChannel; + } + + @Override + public Channel getInChannel() { + return inChannel; + } + + @Override + public Publisher inboundMessageStream() { + return Flux.from(getInChannel().inboundMessageStream()); + } + + @Override + public void subscribeToOutbound(Publisher outboundMessageStream) { + getInChannel().subscribeToOutbound(Flux.from(outboundMessageStream)); + } + + protected void onInbound(TMessage msg) throws RuntimeException {} + + protected void onOutbound(TMessage msg)throws RuntimeException {} +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java new file mode 100644 index 000000000..24c7f27ca --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java @@ -0,0 +1,8 @@ +package org.ethereum.beacon.wire.channel; + +public interface RpcChannel extends Channel> { + + static RpcChannel from(Channel> channel) { + return (RpcChannel) channel; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java new file mode 100644 index 000000000..cd1c92467 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java @@ -0,0 +1,74 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; + +public class RpcChannelAdapter { + + private static final Object CONTEXT_KEY_FUTURE = new Object(); + + private final RpcChannel inChannel; + private final Function> serverHandler; + private final DirectProcessor> outboundStream = + DirectProcessor.create(); + + + public RpcChannelAdapter(RpcChannel inChannel, + Function> serverHandler) { + this.inChannel = inChannel; + this.serverHandler = serverHandler; + inChannel.subscribeToOutbound(outboundStream); + Flux.from(inChannel.inboundMessageStream()).subscribe(this::onInbound); + } + + private void onInbound(RpcMessage msg) { + if (msg.isRequest()) { + if (msg.isNotification()) { + handleNotify(msg.getRequest()); + } else { + handleInvoke(msg.getRequest()); + } + } else { + handleResponse(msg); + } + } + + private void handleResponse(RpcMessage msg) { + CompletableFuture respFut = + (CompletableFuture) msg.popRequestContext(CONTEXT_KEY_FUTURE); + + if (msg.getResponse().isPresent()) { + respFut.complete(msg.getResponse().get()); + } else { + respFut.completeExceptionally(msg.getError().get()); + } + } + + private void handleNotify(TRequestMessage msg) { + serverHandler.apply(msg); + } + private void handleInvoke(TRequestMessage msg) { + try { + CompletableFuture fut = serverHandler.apply(msg); + fut.whenComplete((t, r) -> outboundStream.onNext( + t != null ? new RpcMessage<>(msg, t) : new RpcMessage<>(msg, r))); + } catch (Exception e) { + outboundStream.onNext(new RpcMessage<>(msg, e)); + } + } + + public CompletableFuture invokeRemote(TRequestMessage request) { + RpcMessage requestRpcMsg = + new RpcMessage<>(request, false); + CompletableFuture ret = new CompletableFuture<>(); + requestRpcMsg.pushRequestContext(CONTEXT_KEY_FUTURE, ret); + outboundStream.onNext(requestRpcMsg); + return ret; + } + + public void notifyRemote(TRequestMessage request) { + outboundStream.onNext(new RpcMessage<>(request, true)); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java new file mode 100644 index 000000000..e500248ad --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java @@ -0,0 +1,47 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.function.Predicate; +import org.ethereum.beacon.consensus.hasher.ObjectHasher; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +public class RpcChannelClassFilter + implements ChannelOp, RpcMessage> { + private static final Object CONTEXT_KEY_REQ_CLASS = new Object(); + + private final RpcChannel inChannel; + private final Class requestMessageClass; + + public RpcChannelClassFilter(RpcChannel inChannel, Class requestMessageClass) { + this.inChannel = inChannel; + this.requestMessageClass = requestMessageClass; + } + + @Override + public Publisher> inboundMessageStream() { + return Flux.from(inChannel.inboundMessageStream()) + .filter(rpcMsg -> rpcMsg.isRequest() && requestMessageClass.isInstance(rpcMsg.getRequest())) + .filter(rpcMsg -> rpcMsg.isResponse() && requestMessageClass == rpcMsg.popRequestContext(CONTEXT_KEY_REQ_CLASS)) + .map(msg -> (RpcMessage) msg); + } + + @Override + public void subscribeToOutbound(Publisher> outboundMessageStream) { + inChannel.subscribeToOutbound(Flux.from(outboundMessageStream) + .doOnNext(rpcMsg -> { + if (rpcMsg.isRequest()) { + if (!requestMessageClass.isInstance(rpcMsg.getRequest())) { + throw new IllegalArgumentException("Invalid request class: " + rpcMsg.getRequest().getClass() + ", expected " + requestMessageClass); + } + rpcMsg.pushRequestContext(CONTEXT_KEY_REQ_CLASS, requestMessageClass); + } + }) + .map(msg -> (RpcMessage) msg) + ); + } + + @Override + public Channel> getInChannel() { + return inChannel; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java new file mode 100644 index 000000000..48d7c8441 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java @@ -0,0 +1,86 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.ethereum.beacon.wire.exceptions.WireException; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +public abstract class RpcChannelMapper + implements RpcChannel, + ChannelOp> { + + private static final Object CONTEXT_ID_KEY = new Object(); + + private final Map> idToContextMap = new ConcurrentHashMap<>(); + private final Channel inChannel; + + protected RpcChannelMapper(Channel inChannel) { + this.inChannel = inChannel; + } + + @Override + public Publisher> inboundMessageStream() { + return Flux.from(inChannel.inboundMessageStream()).map(this::fromIn); + } + + protected RpcMessage fromIn(TInMessage msg) { + if (isRequest(msg)) { + if (isNotification(msg)) { + return new RpcMessage((TOutRequest) msg, true); + } else { + RpcMessage rpcMessage = new RpcMessage<>( + (TOutRequest) msg, false); + rpcMessage.pushRequestContext(CONTEXT_ID_KEY, getId(msg)); + return rpcMessage; + } + } else { + Object id = getId(msg); + Map requestContext = idToContextMap.remove(id); + if (requestContext == null) { + throw new WireException("Invalid response from remote: can't find request ID: " + id + ", " + msg); + } + RpcMessage rpcMessage = new RpcMessage<>(null, (TOutResponse) msg); + rpcMessage.getRequestContext().putAll(requestContext); + return rpcMessage; + } + } + + protected TInMessage toIn(RpcMessage msg) { + if (msg.isRequest()) { + if (msg.isNotification()) { + return (TInMessage) msg.getRequest(); + } else { + TInMessage inMessage = (TInMessage) msg.getRequest(); + Object id = generateNextId(); + setId(inMessage, id); + idToContextMap.put(id, msg.getRequestContext()); + return inMessage; + } + } else { + TInMessage inMessage = (TInMessage) msg.getResponse(); + setId(inMessage, msg.popRequestContext(CONTEXT_ID_KEY)); + return inMessage; + } + } + + @Override + public void subscribeToOutbound(Publisher> outboundMessageStream) { + inChannel.subscribeToOutbound(Flux.from(outboundMessageStream).map(this::toIn)); + } + + public Channel getInChannel() { + return inChannel; + } + + protected abstract boolean isRequest(TInMessage msg); + + protected abstract boolean isNotification(TInMessage msg); + + protected abstract Object generateNextId(); + + protected abstract Object getId(TInMessage msg); + + protected abstract void setId(TInMessage msg, Object id); + +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java new file mode 100644 index 000000000..e33547fed --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java @@ -0,0 +1,97 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class RpcMessage { + private final TRequest request; + private final boolean isNotification; + private final TResponse response; + private final Throwable error; + private final Map requestContext = new HashMap<>(); + + public RpcMessage(TRequest request, boolean isNotification) { + this(request, isNotification, null, null); + } + + public RpcMessage(TRequest request, TResponse response) { + this(request, false, response, null); + } + + public RpcMessage(TRequest request, Throwable error) { + this(request, false, null, error); + } + + private RpcMessage(TRequest request, boolean isNotification, TResponse response, Throwable error) { + this.request = request; + this.isNotification = isNotification; + this.response = response; + this.error = error; + } + + public TRequest getRequest() { + return request; + } + + public boolean isNotification() { + return isNotification; + } + + public Optional getResponse() { + return Optional.ofNullable(response); + } + + public Optional getError() { + return Optional.ofNullable(error); + } + + public boolean isRequest() { + return !(getResponse().isPresent() || getError().isPresent()); + } + + public boolean isResponse() { + return !isRequest(); + } + + public RpcMessage copyWithRequest(TNewRequest newRequest) { + if (!isRequest()) { + throw new IllegalStateException(""); + } + RpcMessage ret = new RpcMessage<>(newRequest, + isNotification, null, null); + ret.requestContext.putAll(requestContext); + return ret; + } + + public RpcMessage copyWithResponse(TNewResponse newResponse) { + if (!isResponse()) { + throw new IllegalStateException(""); + } + if (getError().isPresent()) { + throw new IllegalStateException(""); + } + RpcMessage ret = new RpcMessage<>(null, + false, newResponse, null); + ret.requestContext.putAll(requestContext); + return ret; + } + + public void pushRequestContext(Object key, Object value) { + if (!isRequest()) { + throw new IllegalStateException("Context can be added to request only"); + } + requestContext.put(key, value); + } + + public Object popRequestContext(Object key) { + if (!isResponse()) { + throw new IllegalStateException("Context can be pushed from response only"); + } + return requestContext.remove(key); + } + + Map getRequestContext() { + return requestContext; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java index 58f123513..5bab03310 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessagePayload.java @@ -1,15 +1,15 @@ package org.ethereum.beacon.wire.message; -public abstract class ResponseMessagePayload +public abstract class ResponseMessagePayload extends MessagePayload { - private final RequestType request; + private final Object request; - public ResponseMessagePayload(RequestType request) { + public ResponseMessagePayload(Object request) { this.request = request; } - public RequestType getRequest() { + public Object getRequest() { return request; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java similarity index 86% rename from wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java index 3badb2c56..e5b32007b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java @@ -1,8 +1,9 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import java.util.List; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.RequestMessagePayload; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesResponseMessage.java similarity index 82% rename from wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesResponseMessage.java index 8db1e9ed7..308852778 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockBodiesResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesResponseMessage.java @@ -1,12 +1,13 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import java.util.List; import org.ethereum.beacon.core.BeaconBlockBody; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.ResponseMessagePayload; @SSZSerializable -public class BlockBodiesResponseMessage extends ResponseMessagePayload { +public class BlockBodiesResponseMessage extends ResponseMessagePayload { @SSZ private final List blockBodies; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java similarity index 91% rename from wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java index 56d5fcebf..9516c2c10 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java @@ -1,8 +1,9 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.RequestMessagePayload; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java similarity index 82% rename from wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java index 0f6324d82..954b4ff28 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockHeadersResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java @@ -1,12 +1,13 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import java.util.List; import org.ethereum.beacon.core.BeaconBlockHeader; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.ResponseMessagePayload; @SSZSerializable -public class BlockHeadersResponseMessage extends ResponseMessagePayload { +public class BlockHeadersResponseMessage extends ResponseMessagePayload { @SSZ private final List headers; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java similarity index 86% rename from wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java index 4b6a1ef7c..9dce7418f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java @@ -1,8 +1,9 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.RequestMessagePayload; import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java similarity index 88% rename from wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java index 0576d8791..124af7d43 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/BlockRootsResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java @@ -1,13 +1,14 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import java.util.List; import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.ResponseMessagePayload; import tech.pegasys.artemis.ethereum.core.Hash32; @SSZSerializable -public class BlockRootsResponseMessage extends ResponseMessagePayload { +public class BlockRootsResponseMessage extends ResponseMessagePayload { @SSZSerializable public static class BlockRootSlot { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java similarity index 86% rename from wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java index cd0e10d69..46f6b800a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/GoodbyeMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java @@ -1,7 +1,8 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.RequestMessagePayload; import tech.pegasys.artemis.util.uint.UInt64; @SSZSerializable diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java similarity index 93% rename from wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java index 8ae4ee3fe..3bc1df47b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/HelloMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java @@ -1,9 +1,10 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import org.ethereum.beacon.core.types.EpochNumber; import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.RequestMessagePayload; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/Messages.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/Messages.java new file mode 100644 index 000000000..6fac73de0 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/Messages.java @@ -0,0 +1,47 @@ +package org.ethereum.beacon.wire.message.payload; + +import org.ethereum.beacon.wire.message.RequestMessagePayload; +import org.ethereum.beacon.wire.message.ResponseMessagePayload; +import tech.pegasys.artemis.util.uint.UInt64; + +public enum Messages { + + Hello(HelloMessage.METHOD_ID, HelloMessage.class, null), + Goodbye(GoodbyeMessage.METHOD_ID, GoodbyeMessage.class, null), + BlockRoots(BlockRootsRequestMessage.METHOD_ID, BlockRootsRequestMessage.class, BlockRootsResponseMessage.class), + BlockHeaders(BlockHeadersRequestMessage.METHOD_ID, BlockHeadersRequestMessage.class, BlockHeadersResponseMessage.class), + BlockBodies(BlockBodiesRequestMessage.METHOD_ID, BlockBodiesRequestMessage.class, BlockBodiesResponseMessage.class); + + public static Messages getById(UInt64 id) { + for (Messages message : Messages.values()) { + if (id.equals(message.id)) { + return message; + } + } + return null; + } + + private final UInt64 id; + private final Class requestClass; + private final Class responseClass; + + Messages(UInt64 id, + Class requestClass, + Class responseClass) { + this.id = id; + this.requestClass = requestClass; + this.responseClass = responseClass; + } + + public UInt64 getId() { + return id; + } + + public Class getRequestClass() { + return requestClass; + } + + public Class getResponseClass() { + return responseClass; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java similarity index 81% rename from wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java index d60110dc6..1c4814d99 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewAttestationMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java @@ -1,8 +1,9 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.MessagePayload; @SSZSerializable public class NotifyNewAttestationMessage extends MessagePayload { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java similarity index 79% rename from wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java index 575e9fe8c..e1071fc28 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/NotifyNewBlockMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java @@ -1,8 +1,9 @@ -package org.ethereum.beacon.wire.message; +package org.ethereum.beacon.wire.message.payload; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; +import org.ethereum.beacon.wire.message.MessagePayload; @SSZSerializable public class NotifyNewBlockMessage extends MessagePayload { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index 4b605642a..7351d23e8 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -1,7 +1,6 @@ package org.ethereum.beacon.wire.sync; import java.util.List; -import java.util.Optional; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ethereum.beacon.chain.MutableBeaconChain; @@ -11,7 +10,7 @@ import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.WireApiSync; import org.ethereum.beacon.wire.exceptions.WireInvalidConsensusDataException; -import org.ethereum.beacon.wire.message.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import tech.pegasys.artemis.ethereum.core.Hash32; From 731571e4ad3f39713e942f66418407c146dcec2d Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 30 Apr 2019 15:29:05 +0300 Subject: [PATCH 10/96] Complete BeaconPipeline. Bunch of Channel fixes. Create and pass BeaconPipelineChannelTest --- .../beacon/wire/WireApiSyncServer.java | 2 +- .../beacon/wire/channel/BeaconPipeline.java | 170 ++++++++++++++---- .../beacon/wire/channel/ChannelHub.java | 25 +++ .../beacon/wire/channel/ChannelSplitter.java | 9 - .../beacon/wire/channel/IdentityChannel.java | 6 +- .../beacon/wire/channel/RpcChannel.java | 15 +- .../wire/channel/RpcChannelAdapter.java | 19 +- .../wire/channel/RpcChannelClassFilter.java | 10 +- .../beacon/wire/channel/RpcChannelMapper.java | 2 +- .../beacon/wire/channel/RpcMessage.java | 11 +- .../wire/exceptions/WireRemoteRpcError.java | 8 + .../ethereum/beacon/wire/message/Message.java | 7 +- .../beacon/wire/message/RequestMessage.java | 11 +- .../wire/message/RequestMessagePayload.java | 4 +- .../beacon/wire/message/ResponseMessage.java | 13 +- .../payload/BlockBodiesRequestMessage.java | 4 +- .../payload/BlockHeadersRequestMessage.java | 4 +- .../payload/BlockRootsRequestMessage.java | 4 +- .../payload/BlockRootsResponseMessage.java | 5 +- .../wire/message/payload/GoodbyeMessage.java | 4 +- .../wire/message/payload/HelloMessage.java | 4 +- .../{Messages.java => MessageType.java} | 30 +++- .../beacon/wire/sync/BeaconBlockTree.java | 2 +- .../channel/BeaconPipelineChannelTest.java | 98 ++++++++++ .../ethereum/beacon/wire/sync/SyncTest.java | 5 + 25 files changed, 380 insertions(+), 92 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java delete mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java rename wire/src/main/java/org/ethereum/beacon/wire/message/payload/{Messages.java => MessageType.java} (66%) create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index 82741c2be..188fc6a7a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -46,7 +46,7 @@ public CompletableFuture requestBlockRoots( roots.add(new BlockRootSlot(slotRoot, slot)); } } - ret.complete(new BlockRootsResponseMessage(requestMessage, roots)); + ret.complete(new BlockRootsResponseMessage(roots)); } return ret; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java index fa2d076bc..543a67b71 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java @@ -1,13 +1,13 @@ package org.ethereum.beacon.wire.channel; import java.util.concurrent.CompletableFuture; -import java.util.function.Function; +import java.util.concurrent.atomic.AtomicLong; +import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.MessageSerializer; -import org.ethereum.beacon.wire.PayloadSerializer; import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.exceptions.WireRemoteRpcError; import org.ethereum.beacon.wire.message.Message; -import org.ethereum.beacon.wire.message.MessagePayload; import org.ethereum.beacon.wire.message.RequestMessage; import org.ethereum.beacon.wire.message.RequestMessagePayload; import org.ethereum.beacon.wire.message.ResponseMessage; @@ -18,40 +18,140 @@ import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import org.ethereum.beacon.wire.message.payload.MessageType; import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; public class BeaconPipeline { - Channel rawChannel; MessageSerializer messageSerializer; - PayloadSerializer payloadSerializer; + SSZSerializer sszSerializer; + WireApiSync syncServer = null; + + WireApiSync syncClient; + + + static class BeaconRpcMapper extends RpcChannelMapper { + private AtomicLong idGen = new AtomicLong(1); + + public BeaconRpcMapper(Channel inChannel) { + super(inChannel); + } + + @Override + protected boolean isRequest(Message msg) { + return msg instanceof RequestMessage; + } + + @Override + protected boolean isNotification(Message msg) { + return MessageType.getById(((RequestMessage) msg).getMethodId()).isNotification(); + } + + @Override + protected Object generateNextId() { + return UInt64.valueOf(idGen.getAndIncrement()); + } + + @Override + protected Object getId(Message msg) { + return msg.getId(); + } + + @Override + protected void setId(Message msg, Object id) { + msg.setId((UInt64) id); + } + } + + static class BeaconPayloadCodec extends ChannelCodec< + RpcMessage, + RpcMessage> { + + private static final Object CONTEXT_REQUEST_MESSAGE_ID = new Object(); + + public BeaconPayloadCodec( + Channel> inChannel, + SSZSerializer sszSerializer) { + + super(inChannel, msg -> decode(sszSerializer, msg), msg -> encode(sszSerializer, msg)); + } + + static RpcMessage decode( + SSZSerializer sszSerializer, RpcMessage msg) { + + if (msg.isRequest()) { + MessageType messageType = MessageType.getById(msg.getRequest().getMethodId()); + RequestMessagePayload messagePayload = sszSerializer + .decode(msg.getRequest().getBody(), messageType.getRequestClass()); + return msg.copyWithRequest(messagePayload); + } else { + int methodId = (int) msg.popRequestContext(CONTEXT_REQUEST_MESSAGE_ID); + if (msg.getResponse().get().getResponseCode() == 0) { + ResponseMessagePayload messagePayload = + sszSerializer.decode( + msg.getResponse().get().getResult(), + MessageType.getById(methodId).getResponseClass()); + return msg.copyWithResponse(messagePayload); + } else { + return msg.copyWithResponseError(deserializeError(sszSerializer, + msg.getResponse().get().getResponseCode(), msg.getResponse().get().getResult())); + } + } + } + + static RpcMessage encode( + SSZSerializer sszSerializer, RpcMessage msg) { + if (msg.isRequest()) { + int methodId = msg.getRequest().getMethodId(); + msg.pushRequestContext(CONTEXT_REQUEST_MESSAGE_ID, methodId); + BytesValue payloadBytes = sszSerializer.encode2(msg.getRequest()); + return msg.copyWithRequest(new RequestMessage(methodId, payloadBytes)); + } else { + if (msg.getError().isPresent()) { + return msg.copyWithResponse(serializeError(sszSerializer, msg.getError().get())); + } else { + BytesValue payloadBytes = sszSerializer.encode2(msg.getResponse().get()); + return msg.copyWithResponse(new ResponseMessage(0, payloadBytes)); + } + } + } + + protected static ResponseMessage serializeError(SSZSerializer sszSerializer, Throwable t) { + return new ResponseMessage(0xFF, BytesValue.EMPTY); + } + + protected static Throwable deserializeError(SSZSerializer sszSerializer, int respCode, BytesValue data) { + return new WireRemoteRpcError("Remote peer call error: code = " + respCode + ", payload: " + data); + } + } + + public BeaconPipeline(SSZSerializer sszSerializer, WireApiSync syncServer) { + this.sszSerializer = sszSerializer; + this.syncServer = syncServer; + } + + public WireApiSync getSyncClient() { + return syncClient; + } + + public void createFromBytesChannel(Channel rawChannel) { - void create() { Channel messageChannel = new ChannelCodec<>(rawChannel, messageSerializer::deserialize, messageSerializer::serialize); - RpcChannelMapper rpcMessageChannel = null; // = TODO + + createFromMessageChannel(messageChannel); + } + + public void createFromMessageChannel(Channel messageChannel) { + RpcChannelMapper rpcMessageChannel = + new BeaconRpcMapper(messageChannel); + ChannelCodec< RpcMessage, RpcMessage> - payloadCodec = null; // TODO - // new ChannelCodec<>(rpcMessageChannel, msg -> { - // if (msg.isRequest()) { - // return msg.copyWithRequest( - // (RequestMessagePayload) - // payloadSerializer.deserialize(msg.getRequest().getBody())); - // } else { - // return msg.copyWithResponse((ResponseMessagePayload) - // payloadSerializer - // .deserialize(msg.getResponse().get().getResult())); - // } - // }, msg -> { - // if (msg.isRequest()) { - // return new - // RequestMessage()msg.copyWithRequest(payloadSerializer.serialize(msg.getRequest())); - // } else { - // return msg.copyWithResponse(payloadSerializer.serialize(msg.getResponse().get())); - // } - // }); + payloadCodec = new BeaconPayloadCodec(rpcMessageChannel, sszSerializer); + RpcChannel inboundResponsePayloadValidator = RpcChannel .from(new IdentityChannel>( payloadCodec) { @@ -76,22 +176,20 @@ protected void onInbound(RpcMessage hub = RpcChannel.from(new ChannelHub<>( + inboundResponsePayloadValidator)); RpcChannelAdapter blockRootsAsync = - new RpcChannelAdapter<>(RpcChannel.from( - new RpcChannelClassFilter<>(inboundResponsePayloadValidator, BlockRootsRequestMessage.class)), + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(hub, BlockRootsRequestMessage.class), syncServer::requestBlockRoots); - RpcChannelAdapter blockHeadersAsync = - new RpcChannelAdapter<>(RpcChannel.from( - new RpcChannelClassFilter<>(inboundResponsePayloadValidator, BlockHeadersRequestMessage.class)), + RpcChannelAdapter blockHeadersAsync = null; + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(hub, BlockHeadersRequestMessage.class), syncServer::requestBlockHeaders); - RpcChannelAdapter blockBodiesAsync = - new RpcChannelAdapter<>(RpcChannel.from( - new RpcChannelClassFilter<>(inboundResponsePayloadValidator, BlockBodiesRequestMessage.class)), + RpcChannelAdapter blockBodiesAsync = null; + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(hub, BlockBodiesRequestMessage.class), req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get)); - WireApiSync syncClient = new WireApiSync() { + syncClient = new WireApiSync() { @Override public CompletableFuture requestBlockRoots( BlockRootsRequestMessage requestMessage) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java new file mode 100644 index 000000000..91fa61beb --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java @@ -0,0 +1,25 @@ +package org.ethereum.beacon.wire.channel; + +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +public class ChannelHub implements Channel { + + Channel inChannel; + Flux inMessagePublisher; + + public ChannelHub(Channel inChannel) { + this.inChannel = inChannel; + inMessagePublisher = Flux.from(inChannel.inboundMessageStream()).publish().autoConnect(); + } + + @Override + public Publisher inboundMessageStream() { + return inMessagePublisher; + } + + @Override + public void subscribeToOutbound(Publisher outboundMessageStream) { + inChannel.subscribeToOutbound(outboundMessageStream); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java deleted file mode 100644 index 9d824b01e..000000000 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelSplitter.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.ethereum.beacon.wire.channel; - -public interface ChannelSplitter { - - Channel getInChannel(); - - - -} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java index c150b4bc3..c10712db4 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/IdentityChannel.java @@ -18,12 +18,14 @@ public Channel getInChannel() { @Override public Publisher inboundMessageStream() { - return Flux.from(getInChannel().inboundMessageStream()); + return Flux.from(getInChannel().inboundMessageStream()) + .doOnNext(this::onInbound); } @Override public void subscribeToOutbound(Publisher outboundMessageStream) { - getInChannel().subscribeToOutbound(Flux.from(outboundMessageStream)); + getInChannel().subscribeToOutbound(Flux.from(outboundMessageStream) + .doOnNext(this::onOutbound)); } protected void onInbound(TMessage msg) throws RuntimeException {} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java index 24c7f27ca..45a323e46 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannel.java @@ -1,8 +1,21 @@ package org.ethereum.beacon.wire.channel; +import org.reactivestreams.Publisher; + public interface RpcChannel extends Channel> { static RpcChannel from(Channel> channel) { - return (RpcChannel) channel; + return new RpcChannel() { + @Override + public Publisher> inboundMessageStream() { + return channel.inboundMessageStream(); + } + + @Override + public void subscribeToOutbound( + Publisher> outboundMessageStream) { + channel.subscribeToOutbound(outboundMessageStream); + } + } ; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java index cd1c92467..bbdb6e918 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java @@ -28,7 +28,7 @@ private void onInbound(RpcMessage msg) { if (msg.isNotification()) { handleNotify(msg.getRequest()); } else { - handleInvoke(msg.getRequest()); + handleInvoke(msg); } } else { handleResponse(msg); @@ -49,13 +49,20 @@ private void handleResponse(RpcMessage msg) { private void handleNotify(TRequestMessage msg) { serverHandler.apply(msg); } - private void handleInvoke(TRequestMessage msg) { + + private void handleInvoke(RpcMessage msg) { try { - CompletableFuture fut = serverHandler.apply(msg); - fut.whenComplete((t, r) -> outboundStream.onNext( - t != null ? new RpcMessage<>(msg, t) : new RpcMessage<>(msg, r))); + CompletableFuture fut = serverHandler.apply(msg.getRequest()); + fut.whenComplete( + (r, t) -> + outboundStream.onNext( + t != null ? msg.copyWithResponseError(t) : msg.copyWithResponse(r))) + .whenComplete( + (r, t) -> { + if (t != null) t.printStackTrace(); + }); } catch (Exception e) { - outboundStream.onNext(new RpcMessage<>(msg, e)); + outboundStream.onNext(msg.copyWithResponseError(e)); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java index e500248ad..5c77b29fe 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java @@ -1,12 +1,12 @@ package org.ethereum.beacon.wire.channel; -import java.util.function.Predicate; -import org.ethereum.beacon.consensus.hasher.ObjectHasher; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; public class RpcChannelClassFilter - implements ChannelOp, RpcMessage> { + implements ChannelOp, RpcMessage>, + RpcChannel { + private static final Object CONTEXT_KEY_REQ_CLASS = new Object(); private final RpcChannel inChannel; @@ -20,8 +20,8 @@ public RpcChannelClassFilter(RpcChannel inChannel, Class> inboundMessageStream() { return Flux.from(inChannel.inboundMessageStream()) - .filter(rpcMsg -> rpcMsg.isRequest() && requestMessageClass.isInstance(rpcMsg.getRequest())) - .filter(rpcMsg -> rpcMsg.isResponse() && requestMessageClass == rpcMsg.popRequestContext(CONTEXT_KEY_REQ_CLASS)) + .filter(rpcMsg -> rpcMsg.isRequest() && requestMessageClass.isInstance(rpcMsg.getRequest()) + || rpcMsg.isResponse() && requestMessageClass == rpcMsg.popRequestContext(CONTEXT_KEY_REQ_CLASS)) .map(msg -> (RpcMessage) msg); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java index 48d7c8441..0d01bd8cd 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java @@ -58,7 +58,7 @@ protected TInMessage toIn(RpcMessage msg) { return inMessage; } } else { - TInMessage inMessage = (TInMessage) msg.getResponse(); + TInMessage inMessage = (TInMessage) msg.getResponse().get(); setId(inMessage, msg.popRequestContext(CONTEXT_ID_KEY)); return inMessage; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java index e33547fed..3b626022c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java @@ -65,14 +65,21 @@ public RpcMessage copyWit } public RpcMessage copyWithResponse(TNewResponse newResponse) { - if (!isResponse()) { + if (getError().isPresent()) { throw new IllegalStateException(""); } + RpcMessage ret = new RpcMessage<>(null, + false, newResponse, null); + ret.requestContext.putAll(requestContext); + return ret; + } + + public RpcMessage copyWithResponseError(Throwable error) { if (getError().isPresent()) { throw new IllegalStateException(""); } RpcMessage ret = new RpcMessage<>(null, - false, newResponse, null); + false, null, error); ret.requestContext.putAll(requestContext); return ret; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java new file mode 100644 index 000000000..e10d47bef --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java @@ -0,0 +1,8 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireRemoteRpcError extends WireException { + + public WireRemoteRpcError(String message) { + super(message); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java index 50a7d886e..99dd73b9a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/Message.java @@ -1,8 +1,13 @@ package org.ethereum.beacon.wire.message; -import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; public abstract class Message { public abstract MessagePayload getPayload(); + + public abstract UInt64 getId(); + + public abstract void setId(UInt64 id); + } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java index fba861d6e..6bead59db 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessage.java @@ -8,12 +8,17 @@ @SSZSerializable public class RequestMessage extends Message { @SSZ - private final UInt64 id; + private UInt64 id; @SSZ(type = "uint16") private final int methodId; @SSZ private final BytesValue body; + public RequestMessage(int methodId, BytesValue body) { + this.methodId = methodId; + this.body = body; + } + public RequestMessage(UInt64 id, int methodId, BytesValue body) { this.id = id; this.methodId = methodId; @@ -24,6 +29,10 @@ public UInt64 getId() { return id; } + public void setId(UInt64 id) { + this.id = id; + } + public int getMethodId() { return methodId; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java index 332a98517..8a6ae50cd 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/RequestMessagePayload.java @@ -1,8 +1,6 @@ package org.ethereum.beacon.wire.message; -import tech.pegasys.artemis.util.uint.UInt64; - public abstract class RequestMessagePayload extends MessagePayload { - public abstract UInt64 getMethodId(); + public abstract int getMethodId(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java index fa772d166..a78d1bf3c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/ResponseMessage.java @@ -8,12 +8,17 @@ @SSZSerializable public class ResponseMessage extends Message { @SSZ - private final UInt64 id; + private UInt64 id; @SSZ(type = "uint16") private final int responseCode; @SSZ private final BytesValue result; + public ResponseMessage(int responseCode, BytesValue result) { + this.responseCode = responseCode; + this.result = result; + } + public ResponseMessage(UInt64 id, int responseCode, BytesValue result) { this.id = id; @@ -25,6 +30,10 @@ public UInt64 getId() { return id; } + public void setId(UInt64 id) { + this.id = id; + } + public int getResponseCode() { return responseCode; } @@ -34,7 +43,7 @@ public BytesValue getResult() { } @Override - public ResponseMessagePayload getPayload() { + public ResponseMessagePayload getPayload() { return null; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java index e5b32007b..d99f78844 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java @@ -9,7 +9,7 @@ @SSZSerializable public class BlockBodiesRequestMessage extends RequestMessagePayload { - public static final UInt64 METHOD_ID = UInt64.valueOf(0x0E); + public static final int METHOD_ID = 0x0E; @SSZ private final List blockTreeRoots; @@ -19,7 +19,7 @@ public BlockBodiesRequestMessage( } @Override - public UInt64 getMethodId() { + public int getMethodId() { return METHOD_ID; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java index 9516c2c10..66a714ac5 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java @@ -9,7 +9,7 @@ @SSZSerializable public class BlockHeadersRequestMessage extends RequestMessagePayload { - public static final UInt64 METHOD_ID = UInt64.valueOf(0x0D); + public static final int METHOD_ID = 0x0D; @SSZ private final Hash32 startRoot; @@ -26,7 +26,7 @@ public BlockHeadersRequestMessage(Hash32 startRoot, } @Override - public UInt64 getMethodId() { + public int getMethodId() { return METHOD_ID; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java index 9dce7418f..4325ce17b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java @@ -8,7 +8,7 @@ @SSZSerializable public class BlockRootsRequestMessage extends RequestMessagePayload { - public static final UInt64 METHOD_ID = UInt64.valueOf(0x0); + public static final int METHOD_ID = 0x0F; @SSZ private final SlotNumber startSlot; @SSZ private final UInt64 count; @@ -19,7 +19,7 @@ public BlockRootsRequestMessage(SlotNumber startSlot, UInt64 count) { } @Override - public UInt64 getMethodId() { + public int getMethodId() { return METHOD_ID; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java index 124af7d43..c0247e362 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java @@ -31,9 +31,8 @@ public SlotNumber getSlot() { @SSZ private final List roots; - public BlockRootsResponseMessage(BlockRootsRequestMessage request, - List roots) { - super(request); + public BlockRootsResponseMessage(List roots) { + super(null); this.roots = roots; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java index 46f6b800a..e5a241533 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java @@ -7,7 +7,7 @@ @SSZSerializable public class GoodbyeMessage extends RequestMessagePayload { - public static final UInt64 METHOD_ID = UInt64.valueOf(0x1); + public static final int METHOD_ID = 0x1; public static final UInt64 CLIENT_SHUTDOWN = UInt64.valueOf(1); public static final UInt64 IRRELEVANT_NETWORK = UInt64.valueOf(2); @@ -20,7 +20,7 @@ public GoodbyeMessage(UInt64 reason) { } @Override - public UInt64 getMethodId() { + public int getMethodId() { return METHOD_ID; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java index 3bc1df47b..544e322e3 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java @@ -10,7 +10,7 @@ @SSZSerializable public class HelloMessage extends RequestMessagePayload { - public static final UInt64 METHOD_ID = UInt64.valueOf(0x0); + public static final int METHOD_ID = 0x0; @SSZ private final byte networkId; @SSZ private final UInt64 chainId; @@ -31,7 +31,7 @@ public HelloMessage(byte networkId, UInt64 chainId, } @Override - public UInt64 getMethodId() { + public int getMethodId() { return METHOD_ID; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/Messages.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/MessageType.java similarity index 66% rename from wire/src/main/java/org/ethereum/beacon/wire/message/payload/Messages.java rename to wire/src/main/java/org/ethereum/beacon/wire/message/payload/MessageType.java index 6fac73de0..f95cc57cd 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/Messages.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/MessageType.java @@ -2,9 +2,8 @@ import org.ethereum.beacon.wire.message.RequestMessagePayload; import org.ethereum.beacon.wire.message.ResponseMessagePayload; -import tech.pegasys.artemis.util.uint.UInt64; -public enum Messages { +public enum MessageType { Hello(HelloMessage.METHOD_ID, HelloMessage.class, null), Goodbye(GoodbyeMessage.METHOD_ID, GoodbyeMessage.class, null), @@ -12,20 +11,31 @@ public enum Messages { BlockHeaders(BlockHeadersRequestMessage.METHOD_ID, BlockHeadersRequestMessage.class, BlockHeadersResponseMessage.class), BlockBodies(BlockBodiesRequestMessage.METHOD_ID, BlockBodiesRequestMessage.class, BlockBodiesResponseMessage.class); - public static Messages getById(UInt64 id) { - for (Messages message : Messages.values()) { - if (id.equals(message.id)) { + public static MessageType getById(int id) { + for (MessageType message : MessageType.values()) { + if (id == message.id) { return message; } } return null; } - private final UInt64 id; + public static MessageType getByClass(Class messageClass) { + for (MessageType message : MessageType.values()) { + if (message.requestClass.isAssignableFrom(messageClass) + || (message.responseClass != null + && message.responseClass.isAssignableFrom(messageClass))) { + return message; + } + } + return null; + } + + private final int id; private final Class requestClass; private final Class responseClass; - Messages(UInt64 id, + MessageType(int id, Class requestClass, Class responseClass) { this.id = id; @@ -33,7 +43,7 @@ public static Messages getById(UInt64 id) { this.responseClass = responseClass; } - public UInt64 getId() { + public int getId() { return id; } @@ -44,4 +54,8 @@ public Class getRequestClass() { public Class getResponseClass() { return responseClass; } + + public boolean isNotification() { + return getResponseClass() == null; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java index 7c877d255..efbb70dae 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java @@ -10,7 +10,7 @@ public class BeaconBlockTree extends AbstractBlockTree hasher; - protected class BlockWrapper implements BlockWrap> { + protected class BlockWrapper implements AbstractBlockTree.BlockWrap> { private final Feedback block; public BlockWrapper(Feedback block) { diff --git a/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java new file mode 100644 index 000000000..b48be84e2 --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java @@ -0,0 +1,98 @@ +package org.ethereum.beacon.wire.channel; + +import java.util.Collections; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.ssz.SSZBuilder; +import org.ethereum.beacon.ssz.SSZSerializer; +import org.ethereum.beacon.wire.Feedback; +import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.message.Message; +import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage.BlockRootSlot; +import org.junit.Assert; +import org.junit.Test; +import org.reactivestreams.Processor; +import org.reactivestreams.Publisher; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; + +public class BeaconPipelineChannelTest { + + + static class SimpleChannel implements Channel { + Processor in; + Processor out; + + public SimpleChannel(Processor in, Processor out) { + this.in = in; + this.out = out; + } + + @Override + public Publisher inboundMessageStream() { + return Flux.from(in).doOnError(Throwable::printStackTrace); + } + + @Override + public void subscribeToOutbound(Publisher outboundMessageStream) { + Flux.from(outboundMessageStream).doOnError(Throwable::printStackTrace).subscribe(out); + } + } + + @Test + public void simpleTest1() throws Exception { + WireApiSync dummyServer = new WireApiSync() { + @Override + public CompletableFuture requestBlockRoots( + BlockRootsRequestMessage requestMessage) { + + CompletableFuture ret = new CompletableFuture<>(); + ret.complete(new BlockRootsResponseMessage(Collections.singletonList(new BlockRootSlot( + Hash32.ZERO, SlotNumber.of(666))))); + return ret; + } + + @Override + public CompletableFuture requestBlockHeaders( + BlockHeadersRequestMessage requestMessage) { + return null; + } + + @Override + public CompletableFuture> requestBlockBodies( + BlockBodiesRequestMessage requestMessage) { + return null; + } + }; + + DirectProcessor _1to2 = DirectProcessor.create(); + DirectProcessor _2to1 = DirectProcessor.create(); + + SSZSerializer sszSerializer = new SSZBuilder().buildSerializer(); + + BeaconPipeline peer1Pipeline = new BeaconPipeline(sszSerializer, dummyServer); + SimpleChannel peer1Channel = new SimpleChannel<>(_2to1, _1to2); + peer1Pipeline.createFromMessageChannel(peer1Channel); + + BeaconPipeline peer2Pipeline = new BeaconPipeline(sszSerializer, dummyServer); + SimpleChannel peer2Channel = new SimpleChannel<>(_1to2, _2to1); + peer2Pipeline.createFromMessageChannel(peer2Channel); + + WireApiSync peer2SyncClient = peer2Pipeline.getSyncClient(); + + CompletableFuture resp = peer2SyncClient + .requestBlockRoots(new BlockRootsRequestMessage(SlotNumber.ZERO, UInt64.ZERO)); + BlockRootsResponseMessage responseMessage = resp.get(1, TimeUnit.SECONDS); + System.out.println(responseMessage); + Assert.assertEquals(SlotNumber.of(666), responseMessage.getRoots().get(0).getSlot()); + } +} diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java new file mode 100644 index 000000000..7b6517e59 --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -0,0 +1,5 @@ +package org.ethereum.beacon.wire.sync; + +public class SyncTest { + +} From 698a3443268a99c61ba6ae58c0104e96abf182dd Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 30 Apr 2019 16:29:21 +0300 Subject: [PATCH 11/96] Fix assertion --- .../ethereum/beacon/chain/observer/PendingOperationsState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/observer/PendingOperationsState.java b/chain/src/main/java/org/ethereum/beacon/chain/observer/PendingOperationsState.java index ec5b4a76f..7c67d03dd 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/observer/PendingOperationsState.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/observer/PendingOperationsState.java @@ -71,7 +71,7 @@ public List peekAggregatedAttestations( private Attestation aggregateAttestations(List attestations) { assert !attestations.isEmpty(); - assert attestations.stream().skip(1).allMatch(a -> a.equals(attestations.get(0))); + assert attestations.stream().skip(1).allMatch(a -> a.getData().equals(attestations.get(0).getData())); Bitfield participants = attestations.stream() From c3303265ed25b18d7158759ea120e6a264bd27b9 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 6 May 2019 17:08:54 +0300 Subject: [PATCH 12/96] Add toString() --- .../java/org/ethereum/beacon/chain/BeaconTupleDetails.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/BeaconTupleDetails.java b/chain/src/main/java/org/ethereum/beacon/chain/BeaconTupleDetails.java index 1a7e57796..c77473f04 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/BeaconTupleDetails.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/BeaconTupleDetails.java @@ -38,4 +38,8 @@ public BeaconStateEx getFinalState() { return getState(); } + @Override + public String toString() { + return getFinalState().toString(); + } } From 7ee4d64b117c6cde208d6c93875bb01944803fe9 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 6 May 2019 17:09:28 +0300 Subject: [PATCH 13/96] Refactor SimulatorLauncher for using from tests --- .../beacon/simulator/SimulatorLauncher.java | 153 +++++++++++------- 1 file changed, 93 insertions(+), 60 deletions(-) diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java index cd87bfe46..03bdc1d22 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java @@ -76,6 +76,17 @@ public class SimulatorLauncher implements Runnable { private final Level logLevel; private final SpecBuilder specBuilder; + private Random rnd; + private Time genesisTime; + private MDCControlledSchedulers controlledSchedulers; + private LocalWireHub localWireHub; + private List keyPairs; + private Eth1Data eth1Data; + private DepositContract depositContract; + private TimeCollector proposeTimeCollector; + + private List peers; + /** * Creates Simulator launcher with following settings * @@ -99,6 +110,8 @@ public SimulatorLauncher( this.validators = validators; this.observers = observers; this.logLevel = logLevel; + + init(); } private void setupLogging() { @@ -137,44 +150,70 @@ private Pair, List> getValidatorDeposits(Random rn return deposits; } - public void run() { - logger.info("Simulation parameters:\n{}", simulationPlan); - if (config.getChainSpec().isDefined()) - logger.info("Overridden beacon chain parameters:\n{}", config.getChainSpec()); - - Random rnd = new Random(simulationPlan.getSeed()); + public void init() { + rnd = new Random(simulationPlan.getSeed()); setupLogging(); Pair, List> validatorDeposits = getValidatorDeposits(rnd); List deposits = validatorDeposits.getValue0().stream() .filter(Objects::nonNull).collect(Collectors.toList()); - List keyPairs = validatorDeposits.getValue1(); + keyPairs = validatorDeposits.getValue1(); - Time genesisTime = Time.of(simulationPlan.getGenesisTime()); + genesisTime = Time.of(simulationPlan.getGenesisTime()); - MDCControlledSchedulers controlledSchedulers = new MDCControlledSchedulers(); + controlledSchedulers = new MDCControlledSchedulers(); controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); - Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); + eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); - LocalWireHub localWireHub = - new LocalWireHub(s -> wire.trace(s), controlledSchedulers.createNew("wire")); + localWireHub = new LocalWireHub(s -> wire.trace(s), controlledSchedulers.createNew("wire")); DepositContract.ChainStart chainStart = new DepositContract.ChainStart(genesisTime, eth1Data, deposits); - DepositContract depositContract = new SimpleDepositContract(chainStart); + depositContract = new SimpleDepositContract(chainStart); + + proposeTimeCollector = new TimeCollector(); + } + + public Launcher createPeer(String name) { + return createPeer(new PeersConfig(), null, name); + } + public Launcher createPeer(PeersConfig config, BLS381Credentials bls, String name) { + WireApiSub wireApi = + localWireHub.createNewPeer( + name, + config.getWireInboundDelay(), + config.getWireOutboundDelay()); + return createPeer(config, bls, wireApi, name); + } + + public Launcher createPeer(PeersConfig config, BLS381Credentials bls, WireApiSub wireApi, String name) { + ControlledSchedulers schedulers = + controlledSchedulers.createNew(name, config.getSystemTimeShift()); + + BeaconChainSpec spec = specBuilder.buildSpec(); + return new Launcher( + spec, + depositContract, + bls == null ? null : Collections.singletonList(bls), + wireApi, + new MemBeaconChainStorageFactory(spec.getObjectHasher()), + schedulers, + proposeTimeCollector); + } + + public void run() { + run(Integer.MAX_VALUE); + } + + public void run(int slotsCount) { + logger.info("Simulation parameters:\n{}", simulationPlan); + if (config.getChainSpec().isDefined()) + logger.info("Overridden beacon chain parameters:\n{}", config.getChainSpec()); - List peers = new ArrayList<>(); + peers = new ArrayList<>(); logger.info("Creating validators..."); - TimeCollector proposeTimeCollector = new TimeCollector(); for (int i = 0; i < validators.size(); i++) { - ControlledSchedulers schedulers = - controlledSchedulers.createNew("V" + i, validators.get(i).getSystemTimeShift()); - WireApiSub wireApi = - localWireHub.createNewPeer( - "" + i, - validators.get(i).getWireInboundDelay(), - validators.get(i).getWireOutboundDelay()); BLS381Credentials bls; if (keyPairs.get(i) == null) { @@ -185,18 +224,7 @@ public void run() { BLS381Credentials.createWithDummySigner(keyPairs.get(i)); } - BeaconChainSpec spec = specBuilder.buildSpec(); - Launcher launcher = - new Launcher( - spec, - depositContract, - Collections.singletonList(bls), - wireApi, - new MemBeaconChainStorageFactory(spec.getObjectHasher()), - schedulers, - proposeTimeCollector); - - peers.add(launcher); + peers.add(createPeer(validators.get(i), bls, "V" + i)); if ((i + 1) % 100 == 0) logger.info("{} validators created", (i + 1)); @@ -205,19 +233,7 @@ public void run() { logger.info("Creating observer peers..."); for (int i = 0; i < observers.size(); i++) { - PeersConfig config = observers.get(i); - String name = "O" + i; - BeaconChainSpec spec = specBuilder.buildSpec(); - Launcher launcher = - new Launcher( - spec, - depositContract, - null, - localWireHub.createNewPeer( - name, config.getWireInboundDelay(), config.getWireOutboundDelay()), - new MemBeaconChainStorageFactory(spec.getObjectHasher()), - controlledSchedulers.createNew(name, config.getSystemTimeShift())); - peers.add(launcher); + peers.add(createPeer(observers.get(i), null, "O" + i)); } Map latestStates = new HashMap<>(); @@ -246,18 +262,7 @@ public void run() { } // system observer - ControlledSchedulers schedulers = controlledSchedulers.createNew("X"); - WireApiSub wireApi = localWireHub.createNewPeer("X"); - - Launcher observer = - new Launcher( - spec, - depositContract, - null, - wireApi, - new MemBeaconChainStorageFactory(spec.getObjectHasher()), - schedulers); - + Launcher observer = createPeer("X"); peers.add(observer); List slots = new ArrayList<>(); @@ -291,7 +296,7 @@ public void run() { logger.info("Time starts running ..."); controlledSchedulers.setCurrentTime( genesisTime.plus(specConstants.getSecondsPerSlot()).getMillis().getValue() - 9); - while (true) { + for (int i = 0; i < slotsCount; i++) { controlledSchedulers.addTime( Duration.ofMillis(specConstants.getSecondsPerSlot().getMillis().getValue())); @@ -364,6 +369,34 @@ public void run() { } } + public List getPeers() { + return peers; + } + + public BeaconChainSpec getSpec() { + return spec; + } + + public Random getRnd() { + return rnd; + } + + public Time getGenesisTime() { + return genesisTime; + } + + public MDCControlledSchedulers getControlledSchedulers() { + return controlledSchedulers; + } + + public LocalWireHub getLocalWireHub() { + return localWireHub; + } + + public DepositContract getDepositContract() { + return depositContract; + } + private static String getValidators(String info, Map records) { if (records.isEmpty()) return ""; return info + " [" From 98fe2f38449670d1e5eae407bca38d3f202752b8 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 6 May 2019 17:09:57 +0300 Subject: [PATCH 14/96] Add SyncTest --- wire/build.gradle | 3 ++ .../ethereum/beacon/wire/sync/SyncTest.java | 41 ++++++++++++++++++ wire/src/test/resources/log4j2.xml | 43 +++++++++++++++++++ .../test/resources/sync-simulation-config.yml | 36 ++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 wire/src/test/resources/log4j2.xml create mode 100644 wire/src/test/resources/sync-simulation-config.yml diff --git a/wire/build.gradle b/wire/build.gradle index 33bb5985f..df1b81b98 100644 --- a/wire/build.gradle +++ b/wire/build.gradle @@ -11,6 +11,9 @@ dependencies { implementation 'io.projectreactor:reactor-core' testImplementation 'org.mockito:mockito-core' + testImplementation project(':start:common') + testImplementation project(':start:simulator') + testImplementation project(':start:simulator').sourceSets.test.output // Gradle does not import test sources alongside with main sources // use a workaround until better solution will be found diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java index 7b6517e59..b2de451f4 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -1,5 +1,46 @@ package org.ethereum.beacon.wire.sync; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.Launcher; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.simulator.SimulatorLauncher; +import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; +import org.ethereum.beacon.wire.WireApiSyncServer; +import org.junit.Test; +import reactor.core.publisher.Flux; + public class SyncTest { + private static final Logger logger = LogManager.getLogger(SyncTest.class); + + @Test + public void test1() throws Exception { + SimulatorLauncher simulatorLauncher = new Builder() + .withConfigFromResource("/sync-simulation-config.yml") + .build(); + simulatorLauncher.run(32); + Launcher peer0 = simulatorLauncher.getPeers().get(0); + System.out.println(peer0); + + WireApiSyncServer syncServer = new WireApiSyncServer(peer0.getBeaconChainStorage()); + + Launcher testPeer = simulatorLauncher.createPeer("test"); + + BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); + SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); + + Flux.from(testPeer.getBeaconChain().getBlockStatesStream()) + .subscribe(s -> System.out.println(s)); + + SyncManager syncManager = new SyncManager( + testPeer.getBeaconChain(), + testPeer.getBeaconChainStorage(), + testPeer.getSpec(), + syncServer, + syncQueue, + 1); + syncManager.start(); + System.out.println("Done"); + } } diff --git a/wire/src/test/resources/log4j2.xml b/wire/src/test/resources/log4j2.xml new file mode 100644 index 000000000..8ed5b2cba --- /dev/null +++ b/wire/src/test/resources/log4j2.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + %d{HH:mm:ss.SSS} [%X{validatorTime}] %p %c{1.} [%t] %m%n + + + + + + + + + %d{HH:mm:ss.SSS} [%X{validatorTime}] %p %c{1.} [%t] #%X{validatorIndex} %m%n + + + + + + %d{HH:mm:ss.SSS} #%X{validatorIndex} %X{validatorTime} %-5level - %msg%n + + + + + + + + + + + + + diff --git a/wire/src/test/resources/sync-simulation-config.yml b/wire/src/test/resources/sync-simulation-config.yml new file mode 100644 index 000000000..10911a81d --- /dev/null +++ b/wire/src/test/resources/sync-simulation-config.yml @@ -0,0 +1,36 @@ +plan: !simulation + seed: 1 + genesisTime: 600 + peers: + - count: 8 + validator: true + systemTimeShift: 0 + wireInboundDelay: 0 + wireOutboundDelay: 0 + - count: 1 + validator: false + +chainSpec: + specConstants: + initialValues: + GENESIS_SLOT: 1000000 + miscParameters: + SHARD_COUNT: 4 + TARGET_COMMITTEE_SIZE: 2 + timeParameters: + SECONDS_PER_SLOT: 10 + MIN_ATTESTATION_INCLUSION_DELAY: 1 + SLOTS_PER_EPOCH: 4 + SLOTS_PER_HISTORICAL_ROOT: 64 + + honestValidatorParameters: + ETH1_FOLLOW_DISTANCE: 1 + stateListLengths: + LATEST_RANDAO_MIXES_LENGTH: 64 + LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 64 + LATEST_SLASHED_EXIT_LENGTH: 64 + + specHelpersOptions: + blsVerify: false + blsVerifyProofOfPossession: false + blsSign: false From d41de50bb8431b76788f5512e2757aef5760b16a Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 6 May 2019 17:11:08 +0300 Subject: [PATCH 15/96] Fix block root calculation (should be truncated hash) --- wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java | 2 +- .../java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java index 3779ca551..7da10a883 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -37,7 +37,7 @@ default CompletableFuture>> requestBlocks( CompletableFuture>> bodiesFuture = headersFuture .thenCompose(headers -> { List blockHashes = headers.stream() - .map(BeaconBlockHeader::getBlockBodyRoot) + .map(hasher::getHashTruncateLast) .collect(Collectors.toList()); return requestBlockBodies(new BlockBodiesRequestMessage(blockHashes)) .thenApply(bb -> bb.delegate(bb.get().getBlockBodies())); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java index efbb70dae..43d1165e4 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/BeaconBlockTree.java @@ -19,7 +19,7 @@ public BlockWrapper(Feedback block) { @Override public Hash32 getHash() { - return hasher.getHash(block.get()); + return hasher.getHashTruncateLast(block.get()); } @Override From 313da295503741fd1493d1ac83729950672f5745 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 6 May 2019 17:11:51 +0300 Subject: [PATCH 16/96] Fix several sync bugs --- .../beacon/wire/WireApiSyncServer.java | 58 +++++++++---------- .../payload/BlockHeadersRequestMessage.java | 3 +- .../beacon/wire/sync/SyncManager.java | 27 +++++++-- .../beacon/wire/sync/SyncQueueImpl.java | 2 +- 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index 188fc6a7a..f0d24a1be 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -6,6 +6,7 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.BeaconBlockBody; @@ -55,37 +56,36 @@ public CompletableFuture requestBlockRoots( public CompletableFuture requestBlockHeaders( BlockHeadersRequestMessage requestMessage) { CompletableFuture ret = new CompletableFuture<>(); - Optional blockOpt = storage.getBlockStorage() - .get(requestMessage.getStartRoot()); - if (blockOpt.isPresent()) { - BeaconBlock block = blockOpt.get(); - if (!block.getSlot().equals(requestMessage.getStartSlot())) { - ret.completeExceptionally( - new WireIllegalArgumentsException("Requested start slot doesn't match block root: " - + requestMessage.getStartRoot() + ", " + requestMessage.getStartSlot())); - } else { - List headers = new ArrayList<>(); - int increment = requestMessage.getSkipSlots().getIntValue() + 1; - SlotNumber maxSlot = storage.getBlockStorage().getMaxSlot(); - SlotNumber slot = requestMessage.getStartSlot(); - SlotNumber prevSlot = SlotNumber.ZERO; - for(int i = 0; i < requestMessage.getMaxHeaders().intValue(); i++) { - List slotBlocks = Collections.emptyList(); - SlotNumber nonEmptySlot = slot; - while (slotBlocks.isEmpty() && nonEmptySlot.greater(prevSlot)) { - slotBlocks = storage.getBlockStorage().getSlotBlocks(nonEmptySlot); - nonEmptySlot = nonEmptySlot.decrement(); - } - headers.add(storage.getBlockHeaderStorage().get(slotBlocks.get(0)).get()); - slot = slot.plus(increment); - if (slot.greater(maxSlot)) { - break; - } - prevSlot = nonEmptySlot; + SlotNumber slot; + if (!BlockHeadersRequestMessage.NULL_START_SLOT.equals(requestMessage.getStartSlot())) { + slot = requestMessage.getStartSlot(); + } else { + Optional blockOpt = storage.getBlockStorage().get(requestMessage.getStartRoot()); + slot = blockOpt.map(BeaconBlock::getSlot).orElse(null); + } + + if (slot != null) { + List headers = new ArrayList<>(); + int increment = requestMessage.getSkipSlots().getIntValue() + 1; + SlotNumber maxSlot = storage.getBlockStorage().getMaxSlot(); + SlotNumber prevSlot = SlotNumber.ZERO; + for(int i = 0; i < requestMessage.getMaxHeaders().intValue(); i++) { + + List slotBlocks = Collections.emptyList(); + SlotNumber nonEmptySlot = slot; + while (slotBlocks.isEmpty() && nonEmptySlot.greater(prevSlot)) { + slotBlocks = storage.getBlockStorage().getSlotBlocks(nonEmptySlot); + nonEmptySlot = nonEmptySlot.decrement(); + } + headers.add(storage.getBlockHeaderStorage().get(slotBlocks.get(0)).get()); + slot = slot.plus(increment); + if (slot.greater(maxSlot)) { + break; } - ret.complete(new BlockHeadersResponseMessage(requestMessage, headers)); + prevSlot = nonEmptySlot; } + ret.complete(new BlockHeadersResponseMessage(requestMessage, headers)); } else { ret.complete(new BlockHeadersResponseMessage(requestMessage, Collections.emptyList())); } @@ -100,7 +100,7 @@ public CompletableFuture> requestBlockBodie List bodyList = requestMessage.getBlockTreeRoots().stream() .map(blockRoot -> storage.getBlockStorage().get(blockRoot)) - .map(opt -> opt.map(BeaconBlock::getBody).orElse(BeaconBlockBody.EMPTY)) + .flatMap(opt -> opt.isPresent() ? Stream.of(opt.map(BeaconBlock::getBody).get()) : Stream.empty()) .collect(Collectors.toList()); return CompletableFuture.completedFuture( Feedback.of(new BlockBodiesResponseMessage(requestMessage, bodyList))); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java index 66a714ac5..a412681aa 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java @@ -10,7 +10,8 @@ @SSZSerializable public class BlockHeadersRequestMessage extends RequestMessagePayload { public static final int METHOD_ID = 0x0D; - + public static final Hash32 NULL_START_ROOT = Hash32.fromHexString("11223344556677889900aabbccddeeff11223344556677889900aabbccddeeff"); + public static final SlotNumber NULL_START_SLOT = SlotNumber.castFrom(UInt64.MAX_VALUE); @SSZ private final Hash32 startRoot; @SSZ private final SlotNumber startSlot; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index 7351d23e8..47daea067 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -20,19 +20,34 @@ public class SyncManager { MutableBeaconChain chain; BeaconChainStorage storage; - WireApiSync syncApi; BeaconChainSpec spec; + WireApiSync syncApi; + SyncQueue syncQueue; + int maxConcurrentBlockRequests = 32; - SyncQueue syncQueue; + public SyncManager(MutableBeaconChain chain, + BeaconChainStorage storage, BeaconChainSpec spec, WireApiSync syncApi, + SyncQueue syncQueue, int maxConcurrentBlockRequests) { + this.chain = chain; + this.storage = storage; + this.spec = spec; + this.syncApi = syncApi; + this.syncQueue = syncQueue; + this.maxConcurrentBlockRequests = maxConcurrentBlockRequests; + } public void start() { + Hash32 genesisBlockRoot = + storage.getBlockStorage().getSlotBlocks(spec.getConstants().getGenesisSlot()).get(0); + Flux finalizedBlockRootStream = Flux .from(chain.getBlockStatesStream()) .map(bs -> bs.getFinalState().getFinalizedRoot()) - .distinct(); + .distinct() + .map(br -> Hash32.ZERO.equals(br) ? genesisBlockRoot : br); Flux finalizedBlockStream = finalizedBlockRootStream.map( @@ -52,13 +67,13 @@ public void start() { Flux>> wireBlocksStream = Flux .from(syncQueue.getBlockRequestsStream()) .map(req -> new BlockHeadersRequestMessage( - req.getStartRoot().get(), - req.getStartSlot().get(), + req.getStartRoot().orElse(BlockHeadersRequestMessage.NULL_START_ROOT), + req.getStartSlot().orElse(BlockHeadersRequestMessage.NULL_START_SLOT), req.getMaxCount(), req.getStep())) .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher())), maxConcurrentBlockRequests) - .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o)); + .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o, t)); syncQueue.subscribeToNewBlocks(wireBlocksStream); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java index 09a80b414..518cfb6d8 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java @@ -56,7 +56,7 @@ protected void onNewFinalBlock(BeaconBlock finalBlock) { blockTree.setTopBlock(Feedback.of(finalBlock)); if (this.finalBlock == null) { this.finalBlock = finalBlock; -// blockRequests.onNext(createBlockRequests()); + blockRequests.onNext(createBlockRequests()); } } From 06272b529200f3565750abe58782e760e7384fb4 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 6 May 2019 17:44:58 +0300 Subject: [PATCH 17/96] Fix another couple of bugs in sync code --- .../beacon/wire/sync/AbstractBlockTree.java | 28 +++++++++---------- .../beacon/wire/sync/SyncQueueImpl.java | 6 ++-- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java index 6a765c668..19e89b3de 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java @@ -71,22 +71,22 @@ private void addChildrenRecursively(THash blockHash, List successors) { @Override public void setTopBlock(@Nonnull TBlock block) { - if (topBlock == null) { - topBlock = block; - return; - } - if (!hashMap.containsKey(block.getHash())) { - throw new IllegalArgumentException("setTopBlock() should be called with existing block or to initialize"); - } - Iterator> iterator = hashMap.entrySet().iterator(); - while (iterator.hasNext()) { - Entry entry = iterator.next(); - if (entry.getValue().getHeight() <= block.getHeight() - && !entry.getKey().equals(block.getHash())) { - iterator.remove(); - childrenMap.remove(entry.getKey()); + if (topBlock != null) { + if (!hashMap.containsKey(block.getHash())) { + throw new IllegalArgumentException( + "setTopBlock() should be called with existing block or to initialize"); + } + Iterator> iterator = hashMap.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + if (entry.getValue().getHeight() <= block.getHeight() + && !entry.getKey().equals(block.getHash())) { + iterator.remove(); + childrenMap.remove(entry.getKey()); + } } } + topBlock = block; } @Nonnull diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java index 518cfb6d8..8c00672ee 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java @@ -54,10 +54,8 @@ protected Flux createBlockRequests() { protected void onNewFinalBlock(BeaconBlock finalBlock) { blockTree.setTopBlock(Feedback.of(finalBlock)); - if (this.finalBlock == null) { - this.finalBlock = finalBlock; - blockRequests.onNext(createBlockRequests()); - } + this.finalBlock = finalBlock; + blockRequests.onNext(createBlockRequests()); } protected void onInvalidBlock(BeaconBlock block) { From d708f5c4cc9bb87c60c6048555f09da874034247 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 7 May 2019 11:28:12 +0300 Subject: [PATCH 18/96] Complete the SyncTest simple case --- .../beacon/simulator/SimulatorLauncher.java | 4 +- .../beacon/wire/sync/SyncManager.java | 50 +++++++++--- .../ethereum/beacon/wire/sync/SyncQueue.java | 5 +- .../beacon/wire/sync/SyncQueueImpl.java | 9 ++- .../ethereum/beacon/wire/sync/SyncTest.java | 77 +++++++++++++++++-- 5 files changed, 119 insertions(+), 26 deletions(-) diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java index 03bdc1d22..fe61bad8b 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java @@ -466,11 +466,11 @@ public void setCurrentTime(long time) { timeController.setTime(time); } - void addTime(Duration duration) { + public void addTime(Duration duration) { addTime(duration.toMillis()); } - void addTime(long millis) { + public void addTime(long millis) { setCurrentTime(timeController.getTime() + millis); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index 47daea067..ade312092 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -3,6 +3,7 @@ import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.chain.BeaconTupleDetails; import org.ethereum.beacon.chain.MutableBeaconChain; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.consensus.BeaconChainSpec; @@ -11,6 +12,8 @@ import org.ethereum.beacon.wire.WireApiSync; import org.ethereum.beacon.wire.exceptions.WireInvalidConsensusDataException; import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.reactivestreams.Publisher; +import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import tech.pegasys.artemis.ethereum.core.Hash32; @@ -18,19 +21,39 @@ public class SyncManager { private static final Logger logger = LogManager.getLogger(SyncManager.class); - MutableBeaconChain chain; - BeaconChainStorage storage; - BeaconChainSpec spec; + private final MutableBeaconChain chain; + private final Publisher blockStatesStream; + private final BeaconChainStorage storage; + private final BeaconChainSpec spec; - WireApiSync syncApi; - SyncQueue syncQueue; + private final WireApiSync syncApi; + private final SyncQueue syncQueue; + + Disposable wireBlocksStreamSub; + Disposable finalizedBlockStreamSub; + Disposable readyBlocksStreamSub; int maxConcurrentBlockRequests = 32; public SyncManager(MutableBeaconChain chain, BeaconChainStorage storage, BeaconChainSpec spec, WireApiSync syncApi, SyncQueue syncQueue, int maxConcurrentBlockRequests) { + this( + chain, + chain.getBlockStatesStream(), + storage, + spec, + syncApi, + syncQueue, + maxConcurrentBlockRequests); + } + + public SyncManager(MutableBeaconChain chain, + Publisher blockStatesStream, + BeaconChainStorage storage, BeaconChainSpec spec, WireApiSync syncApi, + SyncQueue syncQueue, int maxConcurrentBlockRequests) { this.chain = chain; + this.blockStatesStream = blockStatesStream; this.storage = storage; this.spec = spec; this.syncApi = syncApi; @@ -44,7 +67,7 @@ public void start() { storage.getBlockStorage().getSlotBlocks(spec.getConstants().getGenesisSlot()).get(0); Flux finalizedBlockRootStream = Flux - .from(chain.getBlockStatesStream()) + .from(blockStatesStream) .map(bs -> bs.getFinalState().getFinalizedRoot()) .distinct() .map(br -> Hash32.ZERO.equals(br) ? genesisBlockRoot : br); @@ -54,11 +77,12 @@ public void start() { root -> storage.getBlockStorage().get(root).orElseThrow(() -> new IllegalStateException())); - syncQueue.subscribeToFinalBlocks(finalizedBlockStream); + finalizedBlockStreamSub = syncQueue.subscribeToFinalBlocks(finalizedBlockStream); - Flux.from(syncQueue.getBlocksStream()).subscribe(block -> { + readyBlocksStreamSub = Flux.from(syncQueue.getBlocksStream()).subscribe(block -> { if (!chain.insert(block.get())) { - block.feedbackError(new WireInvalidConsensusDataException("Couldn't insert block: " + block.get())); + block.feedbackError( + new WireInvalidConsensusDataException("Couldn't insert block: " + block.get())); } else { block.feedbackSuccess(); } @@ -75,6 +99,12 @@ public void start() { maxConcurrentBlockRequests) .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o, t)); - syncQueue.subscribeToNewBlocks(wireBlocksStream); + wireBlocksStreamSub = syncQueue.subscribeToNewBlocks(wireBlocksStream); + } + + public void stop() { + wireBlocksStreamSub.dispose(); + finalizedBlockStreamSub.dispose(); + readyBlocksStreamSub.dispose(); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java index 99adc5427..a61c58965 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java @@ -6,6 +6,7 @@ import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.wire.Feedback; import org.reactivestreams.Publisher; +import reactor.core.Disposable; import reactor.core.publisher.Flux; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; @@ -16,9 +17,9 @@ public interface SyncQueue { Publisher> getBlocksStream(); - void subscribeToFinalBlocks(Flux finalBlockRootStream); + Disposable subscribeToFinalBlocks(Flux finalBlockRootStream); - void subscribeToNewBlocks(Publisher>> blocksStream); + Disposable subscribeToNewBlocks(Publisher>> blocksStream); class BlockRequest { private final SlotNumber startSlot; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java index 8c00672ee..3e9efc94d 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java @@ -6,6 +6,7 @@ import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.wire.Feedback; import org.reactivestreams.Publisher; +import reactor.core.Disposable; import reactor.core.publisher.Flux; import reactor.core.publisher.ReplayProcessor; @@ -73,13 +74,13 @@ protected void onNewBlock(Feedback block) { } @Override - public void subscribeToFinalBlocks(Flux finalBlockRootStream) { - Flux.from(finalBlockRootStream).subscribe(this::onNewFinalBlock); + public Disposable subscribeToFinalBlocks(Flux finalBlockRootStream) { + return Flux.from(finalBlockRootStream).subscribe(this::onNewFinalBlock); } @Override - public void subscribeToNewBlocks(Publisher>> blocksStream) { - Flux.from(blocksStream) + public Disposable subscribeToNewBlocks(Publisher>> blocksStream) { + return Flux.from(blocksStream) .flatMap(resp -> Flux.fromStream(resp.get().stream().map(resp::delegate))) .subscribe(this::onNewBlock); } diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java index b2de451f4..302588208 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -1,45 +1,106 @@ package org.ethereum.beacon.wire.sync; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ethereum.beacon.Launcher; +import org.ethereum.beacon.chain.BeaconTupleDetails; import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.schedulers.Scheduler; import org.ethereum.beacon.simulator.SimulatorLauncher; import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; +import org.ethereum.beacon.wire.Feedback; +import org.ethereum.beacon.wire.WireApiSync; import org.ethereum.beacon.wire.WireApiSyncServer; +import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import org.junit.Assert; import org.junit.Test; +import reactor.core.publisher.ConnectableFlux; import reactor.core.publisher.Flux; public class SyncTest { private static final Logger logger = LogManager.getLogger(SyncTest.class); - @Test + static class AsyncWireApiSync implements WireApiSync { + WireApiSync delegate; + Scheduler scheduler; + Duration delay; + + public AsyncWireApiSync(WireApiSync delegate, Scheduler scheduler, Duration delay) { + this.delegate = delegate; + this.scheduler = scheduler; + this.delay = delay; + } + + @Override + public CompletableFuture requestBlockRoots( + BlockRootsRequestMessage requestMessage) { + return scheduler.executeWithDelay(delay, () -> delegate.requestBlockRoots(requestMessage).get()); + } + + @Override + public CompletableFuture requestBlockHeaders( + BlockHeadersRequestMessage requestMessage) { + return scheduler.executeWithDelay(delay, () -> delegate.requestBlockHeaders(requestMessage).get()); + } + + @Override + public CompletableFuture> requestBlockBodies( + BlockBodiesRequestMessage requestMessage) { + return scheduler.executeWithDelay(delay, () -> delegate.requestBlockBodies(requestMessage).get()); + } + } + + @Test(timeout = 30000) public void test1() throws Exception { + int slotCount = 64; SimulatorLauncher simulatorLauncher = new Builder() .withConfigFromResource("/sync-simulation-config.yml") .build(); - simulatorLauncher.run(32); + simulatorLauncher.run(slotCount); Launcher peer0 = simulatorLauncher.getPeers().get(0); System.out.println(peer0); - WireApiSyncServer syncServer = new WireApiSyncServer(peer0.getBeaconChainStorage()); - Launcher testPeer = simulatorLauncher.createPeer("test"); + WireApiSyncServer syncServer = new WireApiSyncServer(peer0.getBeaconChainStorage()); + AsyncWireApiSync asyncSyncServer = new AsyncWireApiSync(syncServer, + testPeer.getSchedulers().blocking(), Duration.ofMillis(10)); + BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); - Flux.from(testPeer.getBeaconChain().getBlockStatesStream()) - .subscribe(s -> System.out.println(s)); - SyncManager syncManager = new SyncManager( testPeer.getBeaconChain(), testPeer.getBeaconChainStorage(), testPeer.getSpec(), - syncServer, + asyncSyncServer, syncQueue, 1); + + AtomicBoolean synced = new AtomicBoolean(); + Flux.from(testPeer.getBeaconChain().getBlockStatesStream()) + .subscribe(s -> { + System.out.println(s); + if (s.getFinalState().getSlot().equals( + simulatorLauncher.getSpec().getConstants().getGenesisSlot().plus(slotCount))) { + syncManager.stop(); + synced.set(true); + } + }); + syncManager.start(); + + simulatorLauncher.getControlledSchedulers().addTime(3000); + + Assert.assertTrue(synced.get()); System.out.println("Done"); } From 597e474040fb66ad1502e915570bb04629523bc3 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 7 May 2019 17:43:03 +0300 Subject: [PATCH 19/96] Add missing WireApi's. Add notified new blocks to the SyncManager --- .../org/ethereum/beacon/stream/RxUtil.java | 11 ++ .../org/ethereum/beacon/wire/Feedback.java | 5 + .../org/ethereum/beacon/wire/WireApiPeer.java | 12 ++ .../org/ethereum/beacon/wire/WireApiSub2.java | 13 +++ .../beacon/wire/channel/BeaconPipeline.java | 106 ++++++++++++++---- .../wire/message/payload/MessageType.java | 4 +- .../payload/NotifyNewAttestationMessage.java | 9 +- .../payload/NotifyNewBlockMessage.java | 10 +- .../beacon/wire/sync/SyncManager.java | 27 +++-- .../channel/BeaconPipelineChannelTest.java | 10 +- 10 files changed, 168 insertions(+), 39 deletions(-) create mode 100644 util/src/main/java/org/ethereum/beacon/stream/RxUtil.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApiSub2.java diff --git a/util/src/main/java/org/ethereum/beacon/stream/RxUtil.java b/util/src/main/java/org/ethereum/beacon/stream/RxUtil.java new file mode 100644 index 000000000..8223d4718 --- /dev/null +++ b/util/src/main/java/org/ethereum/beacon/stream/RxUtil.java @@ -0,0 +1,11 @@ +package org.ethereum.beacon.stream; + +import org.reactivestreams.Publisher; + +public class RxUtil { + + + public static Publisher join(Publisher s1, Publisher s2, int bufferLen) { + throw new UnsupportedOperationException(); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java b/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java index ed3d43517..387eb4a1c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java @@ -1,6 +1,7 @@ package org.ethereum.beacon.wire; import java.util.concurrent.CompletableFuture; +import java.util.function.Function; public interface Feedback { @@ -18,6 +19,10 @@ static Feedback of(T result) { Feedback delegate(TOtherResult otherResult); + default Feedback map(Function mapper) { + return delegate(mapper.apply(get())); + } + class Impl implements Feedback { private final TResult result; private final CompletableFuture feedback = new CompletableFuture<>(); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java new file mode 100644 index 000000000..4e5fe70dc --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java @@ -0,0 +1,12 @@ +package org.ethereum.beacon.wire; + +import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; +import org.ethereum.beacon.wire.message.payload.HelloMessage; + +public interface WireApiPeer { + + void hello(HelloMessage message); + + void goodbye(GoodbyeMessage message); + +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub2.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub2.java new file mode 100644 index 000000000..b32dd62c1 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub2.java @@ -0,0 +1,13 @@ +package org.ethereum.beacon.wire; + +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; +import org.reactivestreams.Publisher; + +public interface WireApiSub2 { + + void newBlock(BeaconBlock block); + + void newAttestation(Attestation attestation); + +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java index 543a67b71..9c022c9db 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java @@ -2,9 +2,13 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicLong; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.MessageSerializer; +import org.ethereum.beacon.wire.WireApiPeer; +import org.ethereum.beacon.wire.WireApiSub2; import org.ethereum.beacon.wire.WireApiSync; import org.ethereum.beacon.wire.exceptions.WireRemoteRpcError; import org.ethereum.beacon.wire.message.Message; @@ -18,19 +22,16 @@ import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; +import org.ethereum.beacon.wire.message.payload.HelloMessage; import org.ethereum.beacon.wire.message.payload.MessageType; +import org.ethereum.beacon.wire.message.payload.NotifyNewAttestationMessage; +import org.ethereum.beacon.wire.message.payload.NotifyNewBlockMessage; import tech.pegasys.artemis.util.bytes.BytesValue; import tech.pegasys.artemis.util.uint.UInt64; public class BeaconPipeline { - MessageSerializer messageSerializer; - SSZSerializer sszSerializer; - WireApiSync syncServer = null; - - WireApiSync syncClient; - - static class BeaconRpcMapper extends RpcChannelMapper { private AtomicLong idGen = new AtomicLong(1); @@ -126,24 +127,22 @@ protected static Throwable deserializeError(SSZSerializer sszSerializer, int res } } - public BeaconPipeline(SSZSerializer sszSerializer, WireApiSync syncServer) { - this.sszSerializer = sszSerializer; - this.syncServer = syncServer; - } + private final SSZSerializer sszSerializer; + private RpcChannel rpcHub; - public WireApiSync getSyncClient() { - return syncClient; + public BeaconPipeline(SSZSerializer sszSerializer) { + this.sszSerializer = sszSerializer; } - public void createFromBytesChannel(Channel rawChannel) { + public void initFromBytesChannel(Channel rawChannel, MessageSerializer messageSerializer) { Channel messageChannel = new ChannelCodec<>(rawChannel, messageSerializer::deserialize, messageSerializer::serialize); - createFromMessageChannel(messageChannel); + initFromMessageChannel(messageChannel); } - public void createFromMessageChannel(Channel messageChannel) { + public void initFromMessageChannel(Channel messageChannel) { RpcChannelMapper rpcMessageChannel = new BeaconRpcMapper(messageChannel); @@ -176,20 +175,21 @@ protected void onInbound(RpcMessage hub = RpcChannel.from(new ChannelHub<>( - inboundResponsePayloadValidator)); + rpcHub = RpcChannel.from(new ChannelHub<>(inboundResponsePayloadValidator)); + } + public WireApiSync createWireApiSync(WireApiSync syncServer) { RpcChannelAdapter blockRootsAsync = - new RpcChannelAdapter<>(new RpcChannelClassFilter<>(hub, BlockRootsRequestMessage.class), + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockRootsRequestMessage.class), syncServer::requestBlockRoots); - RpcChannelAdapter blockHeadersAsync = null; - new RpcChannelAdapter<>(new RpcChannelClassFilter<>(hub, BlockHeadersRequestMessage.class), + RpcChannelAdapter blockHeadersAsync = + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockHeadersRequestMessage.class), syncServer::requestBlockHeaders); - RpcChannelAdapter blockBodiesAsync = null; - new RpcChannelAdapter<>(new RpcChannelClassFilter<>(hub, BlockBodiesRequestMessage.class), + RpcChannelAdapter blockBodiesAsync = + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockBodiesRequestMessage.class), req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get)); - syncClient = new WireApiSync() { + WireApiSync syncClient = new WireApiSync() { @Override public CompletableFuture requestBlockRoots( BlockRootsRequestMessage requestMessage) { @@ -208,5 +208,63 @@ public CompletableFuture> requestBlockBodie return blockBodiesAsync.invokeRemote(requestMessage).thenApply(Feedback::of); } }; + + return syncClient; + } + + public WireApiSub2 createWireApiSub(WireApiSub2 subServer) { + RpcChannelAdapter blocks = + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, NotifyNewBlockMessage.class), + newBlock -> { + subServer.newBlock(newBlock.getBlock()); + return null; + }); + + RpcChannelAdapter attestations = + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, NotifyNewAttestationMessage.class), + newAttest -> { + subServer.newAttestation(newAttest.getAttestation()); + return null; + }); + + return new WireApiSub2() { + @Override + public void newBlock(BeaconBlock block) { + blocks.notifyRemote(new NotifyNewBlockMessage(block)); + } + + @Override + public void newAttestation(Attestation attestation) { + attestations.notifyRemote(new NotifyNewAttestationMessage(attestation)); + } + }; + } + + public WireApiPeer createWireApiPeer(WireApiPeer peerServer) { + RpcChannelAdapter helloRpc = + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, HelloMessage.class), + msg -> { + peerServer.hello(msg); + return null; + }); + + RpcChannelAdapter goodbyeRpc = + new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, GoodbyeMessage.class), + msg -> { + peerServer.goodbye(msg); + return null; + }); + + return new WireApiPeer() { + @Override + public void hello(HelloMessage message) { + helloRpc.notifyRemote(message); + } + + @Override + public void goodbye(GoodbyeMessage message) { + goodbyeRpc.notifyRemote(message); + } + }; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/MessageType.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/MessageType.java index f95cc57cd..b4dea178b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/MessageType.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/MessageType.java @@ -9,7 +9,9 @@ public enum MessageType { Goodbye(GoodbyeMessage.METHOD_ID, GoodbyeMessage.class, null), BlockRoots(BlockRootsRequestMessage.METHOD_ID, BlockRootsRequestMessage.class, BlockRootsResponseMessage.class), BlockHeaders(BlockHeadersRequestMessage.METHOD_ID, BlockHeadersRequestMessage.class, BlockHeadersResponseMessage.class), - BlockBodies(BlockBodiesRequestMessage.METHOD_ID, BlockBodiesRequestMessage.class, BlockBodiesResponseMessage.class); + BlockBodies(BlockBodiesRequestMessage.METHOD_ID, BlockBodiesRequestMessage.class, BlockBodiesResponseMessage.class), + NewBlock(NotifyNewBlockMessage.METHOD_ID, NotifyNewBlockMessage.class, null), + NewAttestation(NotifyNewAttestationMessage.METHOD_ID, NotifyNewAttestationMessage.class, null); public static MessageType getById(int id) { for (MessageType message : MessageType.values()) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java index 1c4814d99..a2c9956a6 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java @@ -4,9 +4,11 @@ import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; import org.ethereum.beacon.wire.message.MessagePayload; +import org.ethereum.beacon.wire.message.RequestMessagePayload; @SSZSerializable -public class NotifyNewAttestationMessage extends MessagePayload { +public class NotifyNewAttestationMessage extends RequestMessagePayload { + public static final int METHOD_ID = 0xF02; @SSZ private final Attestation attestation; @@ -17,4 +19,9 @@ public NotifyNewAttestationMessage(Attestation attestation) { public Attestation getAttestation() { return attestation; } + + @Override + public int getMethodId() { + return METHOD_ID; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java index e1071fc28..1a5f1138b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java @@ -4,9 +4,12 @@ import org.ethereum.beacon.ssz.annotation.SSZ; import org.ethereum.beacon.ssz.annotation.SSZSerializable; import org.ethereum.beacon.wire.message.MessagePayload; +import org.ethereum.beacon.wire.message.RequestMessagePayload; @SSZSerializable -public class NotifyNewBlockMessage extends MessagePayload { +public class NotifyNewBlockMessage extends RequestMessagePayload { + public static final int METHOD_ID = 0xF01; + @SSZ private final BeaconBlock block; public NotifyNewBlockMessage(BeaconBlock block) { @@ -16,4 +19,9 @@ public NotifyNewBlockMessage(BeaconBlock block) { public BeaconBlock getBlock() { return block; } + + @Override + public int getMethodId() { + return METHOD_ID; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index ade312092..ab732e770 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -1,13 +1,16 @@ package org.ethereum.beacon.wire.sync; +import java.util.Collections; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.chain.BeaconTuple; import org.ethereum.beacon.chain.BeaconTupleDetails; import org.ethereum.beacon.chain.MutableBeaconChain; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.stream.RxUtil; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.WireApiSync; import org.ethereum.beacon.wire.exceptions.WireInvalidConsensusDataException; @@ -27,11 +30,12 @@ public class SyncManager { private final BeaconChainSpec spec; private final WireApiSync syncApi; + private Publisher> newBlocks; private final SyncQueue syncQueue; - Disposable wireBlocksStreamSub; - Disposable finalizedBlockStreamSub; - Disposable readyBlocksStreamSub; + private Disposable wireBlocksStreamSub; + private Disposable finalizedBlockStreamSub; + private Disposable readyBlocksStreamSub; int maxConcurrentBlockRequests = 32; @@ -72,10 +76,8 @@ public void start() { .distinct() .map(br -> Hash32.ZERO.equals(br) ? genesisBlockRoot : br); - Flux finalizedBlockStream = - finalizedBlockRootStream.map( - root -> - storage.getBlockStorage().get(root).orElseThrow(() -> new IllegalStateException())); + Flux finalizedBlockStream = finalizedBlockRootStream.map( + root -> storage.getBlockStorage().get(root).orElseThrow(() -> new IllegalStateException())); finalizedBlockStreamSub = syncQueue.subscribeToFinalBlocks(finalizedBlockStream); @@ -100,6 +102,17 @@ public void start() { .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o, t)); wireBlocksStreamSub = syncQueue.subscribeToNewBlocks(wireBlocksStream); + + if (newBlocks != null) { + syncQueue.subscribeToNewBlocks(Flux.from(newBlocks) + .map(blockF -> blockF.map(Collections::singletonList))); + + Publisher freshBlocks = + RxUtil.join( + Flux.from(newBlocks).map(Feedback::get), + Flux.from(blockStatesStream).map(BeaconTuple::getBlock), + 16); + } } public void stop() { diff --git a/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java index b48be84e2..76d04c844 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java @@ -79,15 +79,15 @@ public CompletableFuture> requestBlockBodie SSZSerializer sszSerializer = new SSZBuilder().buildSerializer(); - BeaconPipeline peer1Pipeline = new BeaconPipeline(sszSerializer, dummyServer); + BeaconPipeline peer1Pipeline = new BeaconPipeline(sszSerializer); SimpleChannel peer1Channel = new SimpleChannel<>(_2to1, _1to2); - peer1Pipeline.createFromMessageChannel(peer1Channel); + peer1Pipeline.initFromMessageChannel(peer1Channel); - BeaconPipeline peer2Pipeline = new BeaconPipeline(sszSerializer, dummyServer); + BeaconPipeline peer2Pipeline = new BeaconPipeline(sszSerializer); SimpleChannel peer2Channel = new SimpleChannel<>(_1to2, _2to1); - peer2Pipeline.createFromMessageChannel(peer2Channel); + peer2Pipeline.initFromMessageChannel(peer2Channel); - WireApiSync peer2SyncClient = peer2Pipeline.getSyncClient(); + WireApiSync peer2SyncClient = peer2Pipeline.createWireApiSync(dummyServer); CompletableFuture resp = peer2SyncClient .requestBlockRoots(new BlockRootsRequestMessage(SlotNumber.ZERO, UInt64.ZERO)); From 36491f7ce86af4c7854056cbba7cd8ad5d41ed13 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 7 May 2019 22:51:09 +0300 Subject: [PATCH 20/96] Add simple netty server/client Channel implementation --- versions.gradle | 1 + wire/build.gradle | 2 + .../beacon/wire/net/NettyChannel.java | 71 ++++++++++++++ .../wire/net/NettyChannelInitializer.java | 44 +++++++++ .../ethereum/beacon/wire/net/NettyClient.java | 45 +++++++++ .../ethereum/beacon/wire/net/NettyServer.java | 95 +++++++++++++++++++ .../beacon/wire/net/NettyChannelTest.java | 82 ++++++++++++++++ 7 files changed, 340 insertions(+) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java diff --git a/versions.gradle b/versions.gradle index 529a087ee..a5d07a05e 100644 --- a/versions.gradle +++ b/versions.gradle @@ -32,5 +32,6 @@ dependencyManagement { dependency "commons-beanutils:commons-beanutils:1.9.3" dependency "info.picocli:picocli:3.9.4" + dependency "io.netty:netty-all:4.1.36.Final" } } diff --git a/wire/build.gradle b/wire/build.gradle index df1b81b98..665e49ec0 100644 --- a/wire/build.gradle +++ b/wire/build.gradle @@ -9,6 +9,8 @@ dependencies { implementation 'com.google.guava:guava' implementation 'io.projectreactor:reactor-core' + implementation 'io.netty:netty-all' + implementation 'io.vertx:vertx-core' testImplementation 'org.mockito:mockito-core' testImplementation project(':start:common') diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java new file mode 100644 index 000000000..ee53112eb --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java @@ -0,0 +1,71 @@ +package org.ethereum.beacon.wire.net; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import java.util.function.Consumer; +import org.ethereum.beacon.wire.channel.Channel; +import org.reactivestreams.Publisher; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.ReplayProcessor; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class NettyChannel extends SimpleChannelInboundHandler implements Channel { + + private final Consumer activeChannelListener; + private final ReplayProcessor inMessages = ReplayProcessor.create(); + private final FluxSink inMessagesSink = inMessages.sink(); + private ChannelHandlerContext ctx; + private Disposable outboundSubscription; + + public NettyChannel(Consumer activeChannelListener) { + this.activeChannelListener = activeChannelListener; + } + + @Override + public Publisher inboundMessageStream() { + return inMessages; + } + + @Override + public void subscribeToOutbound(Publisher outboundMessageStream) { + outboundSubscription = Flux.from(outboundMessageStream).subscribe(this::send); + } + + private void send(BytesValue bytesValue) { + ByteBuf buffer = ctx.alloc().buffer(bytesValue.size()); + bytesValue.appendTo(buffer); + ctx.writeAndFlush(buffer); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + this.ctx = ctx; + activeChannelListener.accept(this); + ctx.channel().closeFuture().addListener((ChannelFutureListener) future -> closed()); + } + + private void closed() { + inMessagesSink.complete(); + if (outboundSubscription != null) { + outboundSubscription.dispose(); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + inMessagesSink.error(cause); + } + + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception { + // can't do BytesValue.wrapBuffer since no control over BytesValue instance lifecycle + byte[] copy = new byte[byteBuf.readableBytes()]; + byteBuf.readBytes(copy); + inMessagesSink.next(BytesValue.wrap(copy)); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java new file mode 100644 index 000000000..41f4383da --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java @@ -0,0 +1,44 @@ +package org.ethereum.beacon.wire.net; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.channel.FixedRecvByteBufAllocator; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.LengthFieldPrepender; +import io.netty.handler.codec.LineBasedFrameDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.ReadTimeoutHandler; +import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +class NettyChannelInitializer extends ChannelInitializer { + private static final int READ_TIMEOUT_SEC = 60; + private static final Logger logger = LogManager.getLogger(NettyChannelInitializer.class); + + private final Consumer activeChannelListener; + + public NettyChannelInitializer(Consumer activeChannelListener) { + this.activeChannelListener = activeChannelListener; + } + + @Override + protected void initChannel(NioSocketChannel ch) throws Exception { + ch.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(256 * 1024)); + ch.config().setOption(ChannelOption.SO_RCVBUF, 256 * 1024); + ch.config().setOption(ChannelOption.SO_BACKLOG, 1024); + + ch.pipeline().addFirst(new ReadTimeoutHandler(READ_TIMEOUT_SEC)); + ch.pipeline().addLast(new LengthFieldPrepender(4)); + ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4)); + ch.pipeline().addLast(new NettyChannel(activeChannelListener)); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.error("Unexpected error during channel initialization: " + ctx.channel(), cause); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java new file mode 100644 index 000000000..368a022ce --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java @@ -0,0 +1,45 @@ +package org.ethereum.beacon.wire.net; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultMessageSizeEstimator; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import java.util.concurrent.CompletableFuture; + +public class NettyClient { + private final NioEventLoopGroup workerGroup; + + public NettyClient() { + workerGroup = new NioEventLoopGroup(2, + new ThreadFactoryBuilder().setNameFormat("netty-client-worker-%d").build()); + } + + public CompletableFuture connect(String host, int port) { + Bootstrap b = new Bootstrap(); + b.group(workerGroup); + b.channel(NioSocketChannel.class); + + b.option(ChannelOption.SO_KEEPALIVE, true); + b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT); + b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15 * 1000); + b.remoteAddress(host, port); + + CompletableFuture ret = new CompletableFuture<>(); + + b.handler(new NettyChannelInitializer(ret::complete)); + + // Start the client. + b.connect().addListener((ChannelFutureListener) future -> { + try { + future.get(); + } catch (Exception e) { + ret.completeExceptionally(e); + } + }); + + return ret; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java new file mode 100644 index 000000000..d5cb87f7f --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java @@ -0,0 +1,95 @@ +package org.ethereum.beacon.wire.net; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultMessageSizeEstimator; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.logging.LoggingHandler; +import java.util.concurrent.ExecutionException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.reactivestreams.Publisher; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.ReplayProcessor; +import reactor.core.publisher.UnicastProcessor; + +public class NettyServer { + private static final Logger logger = LogManager.getLogger(NettyServer.class); + + private UnicastProcessor channels = UnicastProcessor.create(); + private FluxSink channelsSink = channels.sink(); + private final int port; + private ChannelFuture channelFuture; + + + public NettyServer(int port) { + this.port = port; + } + + public Publisher channelsStream() { + return channels; + } + + private void onChannelActive(NettyChannel channel) { + channelsSink.next(channel); + } + + public ChannelFuture start() { + NioEventLoopGroup bossGroup = new NioEventLoopGroup(1, + new ThreadFactoryBuilder().setNameFormat("netty-service-boss-%d").build()); + NioEventLoopGroup workerGroup = new NioEventLoopGroup(16, + new ThreadFactoryBuilder().setNameFormat("netty-service-worker-%d").build()); + + try { + ServerBootstrap b = new ServerBootstrap(); + + b.group(bossGroup, workerGroup); + b.channel(NioServerSocketChannel.class); + + b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT); + b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 1000); + + b.handler(new LoggingHandler()); + b.childHandler(new NettyChannelInitializer(this::onChannelActive)); + + channelFuture = b.bind(port); + + channelFuture.addListener((ChannelFutureListener) + future -> { + logger.info("Listening for incoming connections, port: " + port); + try { + future.get(); + } catch (Exception e) { + channelsSink.error(e); + channelsSink.complete(); + } + }); + + channelFuture.channel().closeFuture().addListener(aa -> { + logger.debug("Incoming port is closed: " + port); + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + channelsSink.complete(); + }); + + return channelFuture; + } catch (Exception e) { + logger.debug("Exception: {} ({})", e.getMessage(), e.getClass().getName()); + throw new RuntimeException("Can't bind the port", e); + } + } + + public void stop() { + if (channelFuture == null) { + throw new IllegalStateException("Not started"); + } + channelFuture.addListener((ChannelFutureListener) future -> { + logger.info("Stopping listening on port " + port + "..."); + future.channel().close(); + }); + } +} diff --git a/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java b/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java new file mode 100644 index 000000000..8ce1e29d4 --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java @@ -0,0 +1,82 @@ +package org.ethereum.beacon.wire.net; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.junit.Assert; +import org.junit.Test; +import reactor.core.publisher.Flux; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class NettyChannelTest { + + @Test + public void test1() throws Exception { + NettyServer nettyServer = new NettyServer(26666); + NettyClient nettyClient = new NettyClient(); + + System.out.println("Starting server..."); + nettyServer.start().await(); + System.out.println("Server started"); + + System.out.println("Connecting 1..."); + CompletableFuture chFut1 = nettyClient.connect("localhost", 26666); + NettyChannel ch1 = chFut1.get(5, TimeUnit.SECONDS); + System.out.println("Client channel 1 created"); + + Flux.from(ch1.inboundMessageStream()) + .subscribe( + msg -> System.out.println("Client channel 1 message: " + msg), + err -> System.out.println("Client channel 1 error: " + err), + () -> System.out.println("Client channel 1 closed")); + ch1.subscribeToOutbound( + Flux.just(BytesValue.fromHexString("0x1111")) + .repeat(5) + .delayElements(Duration.ofMillis(20)) + .doOnNext(bb -> System.out.println("Sending msg from client 1..."))); + + Thread.sleep(200); + + CountDownLatch msgLatch = new CountDownLatch(10); + Flux.from(nettyServer.channelsStream()) + .subscribe( + nettyChannel -> { + System.out.println("Server channel created."); + Flux.from(nettyChannel.inboundMessageStream()) + .subscribe( + msg -> { + System.out.println("Server channel message: " + msg); + msgLatch.countDown(); + }, + err -> System.out.println("Server channel error: " + err), + () -> System.out.println("Server channel closed")); + }, + err -> System.out.println("Server error: " + err), + () -> System.out.println("Server socket closed")); + + System.out.println("Connecting 2..."); + CompletableFuture chFut2 = nettyClient.connect("localhost", 26666); + NettyChannel ch2 = chFut2.get(5, TimeUnit.SECONDS); + System.out.println("Client channel 2 created"); + + Flux.from(ch2.inboundMessageStream()) + .subscribe( + msg -> System.out.println("Client channel 2 message: " + msg), + err -> System.out.println("Client channel 2 error: " + err), + () -> System.out.println("Client channel 2 closed")); + ch2.subscribeToOutbound( + Flux.just(BytesValue.fromHexString("0x2222")) + .repeat(5) + .delayElements(Duration.ofMillis(20)) + .doOnNext(bb -> System.out.println("Sending msg from client 2..."))); + + System.out.println("Waiting for all messages on the server..."); + Assert.assertTrue(msgLatch.await(2, TimeUnit.SECONDS)); + System.out.println("Stopping server..."); + nettyServer.stop(); + + Thread.sleep(1000); + System.out.println("Complete"); + } +} From c243cca33c8bff2e24e64302b9ae02a28429ef09 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 8 May 2019 14:56:24 +0300 Subject: [PATCH 21/96] Extract Server/Client interfaces. Fix a couple of issues --- .../java/org/ethereum/beacon/util/Utils.java | 16 +++++++++++++ .../java/org/ethereum/beacon/wire/Peer.java | 15 +++--------- .../org/ethereum/beacon/wire/WireApiSync.java | 24 +++++++++---------- .../beacon/wire/WireApiSyncServer.java | 11 +++++---- .../org/ethereum/beacon/wire/net/Client.java | 11 +++++++++ .../beacon/wire/net/NettyChannel.java | 10 ++++++++ .../ethereum/beacon/wire/net/NettyClient.java | 8 ++++--- .../ethereum/beacon/wire/net/NettyServer.java | 7 +++--- .../org/ethereum/beacon/wire/net/Server.java | 15 ++++++++++++ .../beacon/wire/net/NettyChannelTest.java | 18 ++++++++++++-- 10 files changed, 97 insertions(+), 38 deletions(-) create mode 100644 util/src/main/java/org/ethereum/beacon/util/Utils.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/net/Client.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/net/Server.java diff --git a/util/src/main/java/org/ethereum/beacon/util/Utils.java b/util/src/main/java/org/ethereum/beacon/util/Utils.java new file mode 100644 index 000000000..1cf05e388 --- /dev/null +++ b/util/src/main/java/org/ethereum/beacon/util/Utils.java @@ -0,0 +1,16 @@ +package org.ethereum.beacon.util; + +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +public class Utils { + + public static Function, Stream> optionalFlatMap(Function func) { + return opt -> opt.map(a -> Stream.of(func.apply(a))).orElseGet(Stream::empty); + } + + public static Function> nullableFlatMap(Function func) { + return n -> n != null ? Stream.of(func.apply(n)) : Stream.empty(); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java index 7034ce81c..c83f940e8 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java @@ -1,23 +1,14 @@ package org.ethereum.beacon.wire; -import java.util.concurrent.CompletableFuture; -import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; +import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.message.Message; import org.reactivestreams.Publisher; +import tech.pegasys.artemis.util.bytes.BytesValue; public interface Peer { - Publisher getInboundMessageStream(); - - void sendMessage(Message message); + Channel getRawChannel(); boolean isRemoteInitiated(); - CompletableFuture getDisconnectFuture(); - - void disconnect(); - - default boolean isConnected() { - return !getDisconnectFuture().isDone(); - } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java index 7da10a883..654f69017 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -1,7 +1,10 @@ package org.ethereum.beacon.wire; +import static org.ethereum.beacon.util.Utils.optionalFlatMap; + import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import org.ethereum.beacon.consensus.hasher.ObjectHasher; @@ -43,22 +46,17 @@ default CompletableFuture>> requestBlocks( .thenApply(bb -> bb.delegate(bb.get().getBlockBodies())); }); - return headersFuture.thenCombine(bodiesFuture, + return headersFuture.thenCombine( + bodiesFuture, (headers, bodies) -> { Map bodyMap = bodies.get().stream().collect(Collectors.toMap(hasher::getHash, b -> b)); - return bodies.delegate(headers - .stream() - .map( - h -> { - BeaconBlockBody body = bodyMap.get(h.getBlockBodyRoot()); - if (body != null) { - return new BeaconBlock(h, body); - } else { - return null; - } - }) - .collect(Collectors.toList())); + return bodies.delegate( + headers.stream() + .map(h -> Optional.ofNullable(bodyMap.get(h.getBlockBodyRoot())) + .map(body -> new BeaconBlock(h, body))) + .flatMap(optionalFlatMap(b -> b)) + .collect(Collectors.toList())); }); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index f0d24a1be..0a6480959 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -1,12 +1,13 @@ package org.ethereum.beacon.wire; +import static org.ethereum.beacon.util.Utils.optionalFlatMap; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.BeaconBlockBody; @@ -15,9 +16,9 @@ import org.ethereum.beacon.wire.exceptions.WireIllegalArgumentsException; import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; -import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; -import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage.BlockRootSlot; import tech.pegasys.artemis.ethereum.core.Hash32; @@ -34,7 +35,7 @@ public WireApiSyncServer(BeaconChainStorage storage) { @Override public CompletableFuture requestBlockRoots( BlockRootsRequestMessage requestMessage) { - CompletableFuture ret = new CompletableFuture(); + CompletableFuture ret = new CompletableFuture<>(); if (requestMessage.getCount().compareTo(UInt64.valueOf(MAX_BLOCK_ROOTS_COUNT)) > 0) { ret.completeExceptionally(new WireIllegalArgumentsException( "Too many block roots requested: " + requestMessage.getCount())); @@ -100,7 +101,7 @@ public CompletableFuture> requestBlockBodie List bodyList = requestMessage.getBlockTreeRoots().stream() .map(blockRoot -> storage.getBlockStorage().get(blockRoot)) - .flatMap(opt -> opt.isPresent() ? Stream.of(opt.map(BeaconBlock::getBody).get()) : Stream.empty()) + .flatMap(optionalFlatMap(BeaconBlock::getBody)) .collect(Collectors.toList()); return CompletableFuture.completedFuture( Feedback.of(new BlockBodiesResponseMessage(requestMessage, bodyList))); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java b/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java new file mode 100644 index 000000000..55472f02a --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java @@ -0,0 +1,11 @@ +package org.ethereum.beacon.wire.net; + +import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.wire.channel.Channel; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public interface Client { + + CompletableFuture> connect(TAddress address); + +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java index ee53112eb..23eeec12c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java @@ -4,6 +4,8 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.function.Consumer; import org.ethereum.beacon.wire.channel.Channel; import org.reactivestreams.Publisher; @@ -68,4 +70,12 @@ protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf.readBytes(copy); inMessagesSink.next(BytesValue.wrap(copy)); } + + public SocketAddress getLocalAddress() { + return ctx.channel().localAddress(); + } + + public SocketAddress getRemoteAddress() { + return ctx.channel().remoteAddress(); + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java index 368a022ce..386930c13 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java @@ -7,9 +7,10 @@ import io.netty.channel.DefaultMessageSizeEstimator; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; +import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; -public class NettyClient { +public class NettyClient implements Client{ private final NioEventLoopGroup workerGroup; public NettyClient() { @@ -17,7 +18,8 @@ public NettyClient() { new ThreadFactoryBuilder().setNameFormat("netty-client-worker-%d").build()); } - public CompletableFuture connect(String host, int port) { + @Override + public CompletableFuture connect(SocketAddress address) { Bootstrap b = new Bootstrap(); b.group(workerGroup); b.channel(NioSocketChannel.class); @@ -25,7 +27,7 @@ public CompletableFuture connect(String host, int port) { b.option(ChannelOption.SO_KEEPALIVE, true); b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT); b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15 * 1000); - b.remoteAddress(host, port); + b.remoteAddress(address); CompletableFuture ret = new CompletableFuture<>(); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java index d5cb87f7f..55f44e069 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java @@ -9,15 +9,13 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LoggingHandler; -import java.util.concurrent.ExecutionException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.reactivestreams.Publisher; import reactor.core.publisher.FluxSink; -import reactor.core.publisher.ReplayProcessor; import reactor.core.publisher.UnicastProcessor; -public class NettyServer { +public class NettyServer implements Server { private static final Logger logger = LogManager.getLogger(NettyServer.class); private UnicastProcessor channels = UnicastProcessor.create(); @@ -30,6 +28,7 @@ public NettyServer(int port) { this.port = port; } + @Override public Publisher channelsStream() { return channels; } @@ -38,6 +37,7 @@ private void onChannelActive(NettyChannel channel) { channelsSink.next(channel); } + @Override public ChannelFuture start() { NioEventLoopGroup bossGroup = new NioEventLoopGroup(1, new ThreadFactoryBuilder().setNameFormat("netty-service-boss-%d").build()); @@ -83,6 +83,7 @@ public ChannelFuture start() { } } + @Override public void stop() { if (channelFuture == null) { throw new IllegalStateException("Not started"); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java b/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java new file mode 100644 index 000000000..71e6aa028 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java @@ -0,0 +1,15 @@ +package org.ethereum.beacon.wire.net; + +import io.netty.channel.ChannelFuture; +import org.ethereum.beacon.wire.channel.Channel; +import org.reactivestreams.Publisher; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public interface Server { + + Publisher> channelsStream(); + + ChannelFuture start(); + + void stop(); +} diff --git a/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java b/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java index 8ce1e29d4..dc97f3178 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java @@ -1,8 +1,10 @@ package org.ethereum.beacon.wire.net; +import java.net.InetSocketAddress; import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.junit.Assert; import org.junit.Test; @@ -21,7 +23,8 @@ public void test1() throws Exception { System.out.println("Server started"); System.out.println("Connecting 1..."); - CompletableFuture chFut1 = nettyClient.connect("localhost", 26666); + CompletableFuture chFut1 = + nettyClient.connect(InetSocketAddress.createUnresolved("localhost", 26666)); NettyChannel ch1 = chFut1.get(5, TimeUnit.SECONDS); System.out.println("Client channel 1 created"); @@ -56,7 +59,8 @@ public void test1() throws Exception { () -> System.out.println("Server socket closed")); System.out.println("Connecting 2..."); - CompletableFuture chFut2 = nettyClient.connect("localhost", 26666); + CompletableFuture chFut2 = + nettyClient.connect(InetSocketAddress.createUnresolved("localhost", 26666)); NettyChannel ch2 = chFut2.get(5, TimeUnit.SECONDS); System.out.println("Client channel 2 created"); @@ -79,4 +83,14 @@ public void test1() throws Exception { Thread.sleep(1000); System.out.println("Complete"); } + + @Test(expected = ExecutionException.class) + public void test2() throws Exception { + NettyClient nettyClient = new NettyClient(); + + System.out.println("Connecting 1..."); + CompletableFuture chFut1 = + nettyClient.connect(InetSocketAddress.createUnresolved("localhost", 26667)); + NettyChannel ch1 = chFut1.get(5, TimeUnit.SECONDS); + } } From 1dc9a837ceea76ce33835572c6f1566554ce5e4a Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 8 May 2019 18:32:58 +0300 Subject: [PATCH 22/96] Add Peer implementation and simple PeerManager --- .../beacon/wire/PayloadSerializer.java | 11 -- .../java/org/ethereum/beacon/wire/Peer.java | 6 +- .../org/ethereum/beacon/wire/PeerImpl.java | 142 ++++++++++++++++++ .../org/ethereum/beacon/wire/PeerManager.java | 4 + .../beacon/wire/SimplePeerManagerImpl.java | 118 +++++++++++++++ .../beacon/wire/channel/BeaconPipeline.java | 40 ++++- .../ethereum/beacon/wire/channel/Channel.java | 8 + .../beacon/wire/channel/ChannelHub.java | 15 +- .../beacon/wire/net/NettyChannel.java | 5 + 9 files changed, 328 insertions(+), 21 deletions(-) delete mode 100644 wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java b/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java deleted file mode 100644 index f32ae405c..000000000 --- a/wire/src/main/java/org/ethereum/beacon/wire/PayloadSerializer.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.ethereum.beacon.wire; - -import org.ethereum.beacon.wire.message.MessagePayload; -import tech.pegasys.artemis.util.bytes.BytesValue; - -public interface PayloadSerializer { - - BytesValue serialize(MessagePayload message); - - MessagePayload deserialize(BytesValue messageBytes); -} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java index c83f940e8..189c57b18 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java @@ -1,14 +1,14 @@ package org.ethereum.beacon.wire; import org.ethereum.beacon.wire.channel.Channel; -import org.ethereum.beacon.wire.message.Message; -import org.reactivestreams.Publisher; import tech.pegasys.artemis.util.bytes.BytesValue; public interface Peer { Channel getRawChannel(); - boolean isRemoteInitiated(); + WireApiSync getSyncApi(); + + WireApiSub getSubApi(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java new file mode 100644 index 000000000..ed35dcdc4 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java @@ -0,0 +1,142 @@ +package org.ethereum.beacon.wire; + +import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.ssz.SSZSerializer; +import org.ethereum.beacon.wire.channel.BeaconPipeline; +import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; +import org.ethereum.beacon.wire.message.payload.HelloMessage; +import org.reactivestreams.Publisher; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.ReplayProcessor; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class PeerImpl implements Peer { + + private static class WireApiSubHub implements WireApiSub, WireApiSub2 { + private final ReplayProcessor blockProcessor = ReplayProcessor.cacheLast(); + private final FluxSink blockSink = blockProcessor.sink(); + private final ReplayProcessor attestProcessor = ReplayProcessor.cacheLast(); + private final FluxSink attestSink = attestProcessor.sink(); + + private WireApiSub2 subClient; + + public void setSubClient(WireApiSub2 subClient) { + this.subClient = subClient; + } + + @Override + public void sendProposedBlock(BeaconBlock block) { + subClient.newBlock(block); + } + + @Override + public void sendAttestation(Attestation attestation) { + subClient.newAttestation(attestation); + } + + @Override + public Publisher inboundBlocksStream() { + return blockProcessor; + } + + @Override + public Publisher inboundAttestationsStream() { + return attestProcessor; + } + + @Override + public void newBlock(BeaconBlock block) { + blockSink.next(block); + } + + @Override + public void newAttestation(Attestation attestation) { + attestSink.next(attestation); + } + } + + private final Channel channel; + private final WireApiPeer apiPeerRemote; + private final HelloMessage localHelloMessage; + private final CompletableFuture remoteHelloMessageFut = new CompletableFuture<>(); + private final CompletableFuture peerActiveFut = new CompletableFuture<>(); + private final BeaconPipeline beaconPipeline; + private final WireApiSubHub wireApiSubHub = new WireApiSubHub(); + + private GoodbyeMessage remoteGoodbye; + private GoodbyeMessage localGoodbye; + + public PeerImpl( + Channel channel, + HelloMessage helloMessage, + SSZSerializer ssz, + MessageSerializer messageSerializer, + WireApiSync syncServer) { + + this.channel = channel; + this.localHelloMessage = helloMessage; + + beaconPipeline = new BeaconPipeline(ssz, new WireApiPeer() { + public void hello(HelloMessage message) { + onHello(message); + } + public void goodbye(GoodbyeMessage message) { + onGoodbye(message); + } + }, wireApiSubHub, syncServer); + beaconPipeline.initFromBytesChannel(channel, messageSerializer); + wireApiSubHub.setSubClient(beaconPipeline.getSubClient()); + + apiPeerRemote = beaconPipeline.getPeerClient(); + apiPeerRemote.hello(helloMessage); + } + + @Override + public Channel getRawChannel() { + return channel; + } + + public CompletableFuture getRemoteHelloMessage() { + return remoteHelloMessageFut; + } + + public CompletableFuture getPeerActiveFuture() { + return peerActiveFut; + } + + private void onHello(HelloMessage message) { + remoteHelloMessageFut.complete(message); + + if (localHelloMessage.getNetworkId() != message.getNetworkId()) { + disconnect(new GoodbyeMessage(GoodbyeMessage.IRRELEVANT_NETWORK)); + } + if (!localHelloMessage.getChainId().equals(message.getChainId())) { + disconnect(new GoodbyeMessage(GoodbyeMessage.IRRELEVANT_NETWORK)); + } + + peerActiveFut.complete(message); + } + + private void onGoodbye(GoodbyeMessage message) { + remoteGoodbye = message; + } + + public void disconnect(GoodbyeMessage message) { + localGoodbye = message; + apiPeerRemote.goodbye(message); + channel.close(); + } + + @Override + public WireApiSync getSyncApi() { + return beaconPipeline.getSyncClient(); + } + + @Override + public WireApiSub getSubApi() { + return wireApiSubHub; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java b/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java index 898e00a34..c24f4a309 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java @@ -12,4 +12,8 @@ public interface PeerManager { Publisher activePeerStream(); Collection getActivePeers(); + + WireApiSync getWireApiSync(); + + WireApiSub getWireApiSub(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java new file mode 100644 index 000000000..9185c477a --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java @@ -0,0 +1,118 @@ +package org.ethereum.beacon.wire; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.chain.BeaconTupleDetails; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.ssz.SSZSerializer; +import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.message.payload.HelloMessage; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; + +public class SimplePeerManagerImpl implements PeerManager { + private static final Logger logger = LogManager.getLogger(SimplePeerManagerImpl.class); + + private byte networkId; + private UInt64 chainId; + + Publisher> channelsStream; + SSZSerializer ssz; + BeaconChainSpec spec; + MessageSerializer messageSerializer; + WireApiSync syncServer; + Publisher headStream; + + Flux connectedPeersStream; + List activePeers = Collections.synchronizedList(new ArrayList<>()); + + public SimplePeerManagerImpl( + byte networkId, + UInt64 chainId, + Publisher> channelsStream, + SSZSerializer ssz, + BeaconChainSpec spec, + MessageSerializer messageSerializer, + WireApiSync syncServer, + Publisher headStream) { + + this.networkId = networkId; + this.chainId = chainId; + this.channelsStream = channelsStream; + this.ssz = ssz; + this.spec = spec; + this.messageSerializer = messageSerializer; + this.syncServer = syncServer; + this.headStream = headStream; + + connectedPeersStream = Flux.from(channelsStream).map(this::createPeer).publish().autoConnect(); + + Flux.from(activePeerStream()).subscribe(this::onNewActivePeer); + } + + protected HelloMessage createLocalHello() { + BeaconTupleDetails head = Flux.from(headStream).last().block(Duration.ZERO); + return new HelloMessage( + networkId, + chainId, + head.getFinalState().getFinalizedRoot(), + head.getFinalState().getFinalizedEpoch(), + spec.getObjectHasher().getHashTruncateLast(head.getBlock()), + head.getBlock().getSlot()); + } + + protected PeerImpl createPeer(Channel channel) { + return new PeerImpl(channel, createLocalHello(), ssz, messageSerializer, syncServer); + } + + @Override + public Publisher connectedPeerStream() { + return connectedPeersStream.map(p -> p); + } + + @Override + public Publisher disconnectedPeerStream() { + return connectedPeersStream.flatMap( + peer -> Mono.fromFuture(peer.getRawChannel().getCloseFuture().thenApply(v -> peer))); + } + + @Override + public Publisher activePeerStream() { + return connectedPeersStream.flatMap( + peer -> Mono.fromFuture(peer.getPeerActiveFuture().thenApply(v -> peer))); + } + + protected void onNewActivePeer(Peer peer) { + activePeers.add(peer); + peer.getRawChannel().getCloseFuture().thenAccept(v -> activePeers.remove(peer)); + } + + @Override + public Collection getActivePeers() { + return activePeers; + } + + @Override + public WireApiSync getWireApiSync() { + if (getActivePeers().isEmpty()) { + throw new IllegalStateException("No peers connected yet. This is naive implementation"); + } + return getActivePeers().iterator().next().getSyncApi(); + } + + @Override + public WireApiSub getWireApiSub() { + if (getActivePeers().isEmpty()) { + throw new IllegalStateException("No peers connected yet. This is naive implementation"); + } + return getActivePeers().iterator().next().getSubApi(); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java index 9c022c9db..8920e77a3 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java @@ -128,10 +128,21 @@ protected static Throwable deserializeError(SSZSerializer sszSerializer, int res } private final SSZSerializer sszSerializer; + private final WireApiPeer peerServer; + private final WireApiSub2 subServer; + private final WireApiSync syncServer; + private WireApiPeer peerClient; + private WireApiSub2 subClient; + private WireApiSync syncClient; + private RpcChannel rpcHub; - public BeaconPipeline(SSZSerializer sszSerializer) { + public BeaconPipeline(SSZSerializer sszSerializer, WireApiPeer peerServer, + WireApiSub2 subServer, WireApiSync syncServer) { this.sszSerializer = sszSerializer; + this.peerServer = peerServer; + this.subServer = subServer; + this.syncServer = syncServer; } public void initFromBytesChannel(Channel rawChannel, MessageSerializer messageSerializer) { @@ -175,10 +186,29 @@ protected void onInbound(RpcMessage(inboundResponsePayloadValidator)); + ChannelHub> channelHub = new ChannelHub<>( + inboundResponsePayloadValidator, false); + rpcHub = RpcChannel.from(channelHub); + syncClient = createWireApiSync(syncServer); + subClient = createWireApiSub(subServer); + peerClient = createWireApiPeer(peerServer); + + channelHub.connect(); + } + + public WireApiPeer getPeerClient() { + return peerClient; + } + + public WireApiSub2 getSubClient() { + return subClient; + } + + public WireApiSync getSyncClient() { + return syncClient; } - public WireApiSync createWireApiSync(WireApiSync syncServer) { + private WireApiSync createWireApiSync(WireApiSync syncServer) { RpcChannelAdapter blockRootsAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockRootsRequestMessage.class), syncServer::requestBlockRoots); @@ -212,7 +242,7 @@ public CompletableFuture> requestBlockBodie return syncClient; } - public WireApiSub2 createWireApiSub(WireApiSub2 subServer) { + private WireApiSub2 createWireApiSub(WireApiSub2 subServer) { RpcChannelAdapter blocks = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, NotifyNewBlockMessage.class), newBlock -> { @@ -240,7 +270,7 @@ public void newAttestation(Attestation attestation) { }; } - public WireApiPeer createWireApiPeer(WireApiPeer peerServer) { + private WireApiPeer createWireApiPeer(WireApiPeer peerServer) { RpcChannelAdapter helloRpc = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, HelloMessage.class), msg -> { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java index ebdfc6600..665cb2808 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java @@ -1,6 +1,8 @@ package org.ethereum.beacon.wire.channel; +import java.util.concurrent.CompletableFuture; import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; public interface Channel { @@ -8,4 +10,10 @@ public interface Channel { void subscribeToOutbound(Publisher outboundMessageStream); + default CompletableFuture getCloseFuture() { + return Mono.ignoreElements(inboundMessageStream()).toFuture().thenApply(ignore -> null); + } + + default void close() { + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java index 91fa61beb..8c38d348c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelHub.java @@ -1,16 +1,27 @@ package org.ethereum.beacon.wire.channel; import org.reactivestreams.Publisher; +import reactor.core.publisher.ConnectableFlux; import reactor.core.publisher.Flux; public class ChannelHub implements Channel { Channel inChannel; Flux inMessagePublisher; + ConnectableFlux connectableFlux; - public ChannelHub(Channel inChannel) { + public ChannelHub(Channel inChannel, boolean autoConnect) { this.inChannel = inChannel; - inMessagePublisher = Flux.from(inChannel.inboundMessageStream()).publish().autoConnect(); + connectableFlux = Flux.from(inChannel.inboundMessageStream()).publish(); + if (autoConnect) { + inMessagePublisher = connectableFlux.autoConnect(); + } else { + inMessagePublisher = connectableFlux; + } + } + + public void connect() { + connectableFlux.connect(); } @Override diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java index 23eeec12c..500adfd9c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java @@ -71,6 +71,11 @@ protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf inMessagesSink.next(BytesValue.wrap(copy)); } + @Override + public void close() { + ctx.channel().close(); + } + public SocketAddress getLocalAddress() { return ctx.channel().localAddress(); } From 27f12de827fd4c6b09ce977c4c213df0ce34b46a Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 9 May 2019 11:44:55 +0300 Subject: [PATCH 23/96] Some refactorings. --- .../org/ethereum/beacon/wire/PeerImpl.java | 59 +------- .../wire/channel/RpcChannelAdapter.java | 8 +- .../channel/beacon/BeaconPayloadCodec.java | 75 ++++++++++ .../channel/{ => beacon}/BeaconPipeline.java | 141 +++--------------- .../wire/channel/beacon/BeaconRpcMapper.java | 43 ++++++ .../channel/beacon/WireApiSubAdapter.java | 52 +++++++ .../beacon/WireApiSubRpc.java} | 4 +- .../channel/BeaconPipelineChannelTest.java | 7 +- 8 files changed, 213 insertions(+), 176 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java rename wire/src/main/java/org/ethereum/beacon/wire/channel/{ => beacon}/BeaconPipeline.java (61%) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconRpcMapper.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/WireApiSubAdapter.java rename wire/src/main/java/org/ethereum/beacon/wire/{WireApiSub2.java => channel/beacon/WireApiSubRpc.java} (73%) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java index ed35dcdc4..6bc20f3be 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java @@ -1,70 +1,23 @@ package org.ethereum.beacon.wire; import java.util.concurrent.CompletableFuture; -import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.ssz.SSZSerializer; -import org.ethereum.beacon.wire.channel.BeaconPipeline; +import org.ethereum.beacon.wire.channel.beacon.BeaconPipeline; import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.channel.beacon.WireApiSubAdapter; import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; import org.ethereum.beacon.wire.message.payload.HelloMessage; -import org.reactivestreams.Publisher; -import reactor.core.publisher.FluxSink; -import reactor.core.publisher.ReplayProcessor; import tech.pegasys.artemis.util.bytes.BytesValue; public class PeerImpl implements Peer { - private static class WireApiSubHub implements WireApiSub, WireApiSub2 { - private final ReplayProcessor blockProcessor = ReplayProcessor.cacheLast(); - private final FluxSink blockSink = blockProcessor.sink(); - private final ReplayProcessor attestProcessor = ReplayProcessor.cacheLast(); - private final FluxSink attestSink = attestProcessor.sink(); - - private WireApiSub2 subClient; - - public void setSubClient(WireApiSub2 subClient) { - this.subClient = subClient; - } - - @Override - public void sendProposedBlock(BeaconBlock block) { - subClient.newBlock(block); - } - - @Override - public void sendAttestation(Attestation attestation) { - subClient.newAttestation(attestation); - } - - @Override - public Publisher inboundBlocksStream() { - return blockProcessor; - } - - @Override - public Publisher inboundAttestationsStream() { - return attestProcessor; - } - - @Override - public void newBlock(BeaconBlock block) { - blockSink.next(block); - } - - @Override - public void newAttestation(Attestation attestation) { - attestSink.next(attestation); - } - } - private final Channel channel; private final WireApiPeer apiPeerRemote; private final HelloMessage localHelloMessage; private final CompletableFuture remoteHelloMessageFut = new CompletableFuture<>(); private final CompletableFuture peerActiveFut = new CompletableFuture<>(); private final BeaconPipeline beaconPipeline; - private final WireApiSubHub wireApiSubHub = new WireApiSubHub(); + private final WireApiSubAdapter wireApiSubAdapter = new WireApiSubAdapter(); private GoodbyeMessage remoteGoodbye; private GoodbyeMessage localGoodbye; @@ -86,9 +39,9 @@ public void hello(HelloMessage message) { public void goodbye(GoodbyeMessage message) { onGoodbye(message); } - }, wireApiSubHub, syncServer); + }, wireApiSubAdapter, syncServer); beaconPipeline.initFromBytesChannel(channel, messageSerializer); - wireApiSubHub.setSubClient(beaconPipeline.getSubClient()); + wireApiSubAdapter.setSubClient(beaconPipeline.getSubClient()); apiPeerRemote = beaconPipeline.getPeerClient(); apiPeerRemote.hello(helloMessage); @@ -137,6 +90,6 @@ public WireApiSync getSyncApi() { @Override public WireApiSub getSubApi() { - return wireApiSubHub; + return wireApiSubAdapter; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java index bbdb6e918..a7ea50932 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java @@ -2,6 +2,7 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; +import org.ethereum.beacon.wire.exceptions.WireException; import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; @@ -47,11 +48,16 @@ private void handleResponse(RpcMessage msg) { } private void handleNotify(TRequestMessage msg) { - serverHandler.apply(msg); + if (serverHandler != null) { + serverHandler.apply(msg); + } } private void handleInvoke(RpcMessage msg) { try { + if (serverHandler == null) { + throw new WireException("No server to process RPC invoke: " + msg); + } CompletableFuture fut = serverHandler.apply(msg.getRequest()); fut.whenComplete( (r, t) -> diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java new file mode 100644 index 000000000..52bc9df53 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java @@ -0,0 +1,75 @@ +package org.ethereum.beacon.wire.channel.beacon; + +import org.ethereum.beacon.ssz.SSZSerializer; +import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.channel.ChannelCodec; +import org.ethereum.beacon.wire.channel.RpcMessage; +import org.ethereum.beacon.wire.exceptions.WireRemoteRpcError; +import org.ethereum.beacon.wire.message.RequestMessage; +import org.ethereum.beacon.wire.message.RequestMessagePayload; +import org.ethereum.beacon.wire.message.ResponseMessage; +import org.ethereum.beacon.wire.message.ResponseMessagePayload; +import org.ethereum.beacon.wire.message.payload.MessageType; +import tech.pegasys.artemis.util.bytes.BytesValue; + +class BeaconPayloadCodec extends ChannelCodec< + RpcMessage, + RpcMessage> { + + private static final Object CONTEXT_REQUEST_MESSAGE_ID = new Object(); + + public BeaconPayloadCodec( + Channel> inChannel, + SSZSerializer sszSerializer) { + + super(inChannel, msg -> decode(sszSerializer, msg), msg -> encode(sszSerializer, msg)); + } + + static RpcMessage decode( + SSZSerializer sszSerializer, RpcMessage msg) { + + if (msg.isRequest()) { + MessageType messageType = MessageType.getById(msg.getRequest().getMethodId()); + RequestMessagePayload messagePayload = sszSerializer + .decode(msg.getRequest().getBody(), messageType.getRequestClass()); + return msg.copyWithRequest(messagePayload); + } else { + int methodId = (int) msg.popRequestContext(CONTEXT_REQUEST_MESSAGE_ID); + if (msg.getResponse().get().getResponseCode() == 0) { + ResponseMessagePayload messagePayload = + sszSerializer.decode( + msg.getResponse().get().getResult(), + MessageType.getById(methodId).getResponseClass()); + return msg.copyWithResponse(messagePayload); + } else { + return msg.copyWithResponseError(deserializeError(sszSerializer, + msg.getResponse().get().getResponseCode(), msg.getResponse().get().getResult())); + } + } + } + + static RpcMessage encode( + SSZSerializer sszSerializer, RpcMessage msg) { + if (msg.isRequest()) { + int methodId = msg.getRequest().getMethodId(); + msg.pushRequestContext(CONTEXT_REQUEST_MESSAGE_ID, methodId); + BytesValue payloadBytes = sszSerializer.encode2(msg.getRequest()); + return msg.copyWithRequest(new RequestMessage(methodId, payloadBytes)); + } else { + if (msg.getError().isPresent()) { + return msg.copyWithResponse(serializeError(sszSerializer, msg.getError().get())); + } else { + BytesValue payloadBytes = sszSerializer.encode2(msg.getResponse().get()); + return msg.copyWithResponse(new ResponseMessage(0, payloadBytes)); + } + } + } + + protected static ResponseMessage serializeError(SSZSerializer sszSerializer, Throwable t) { + return new ResponseMessage(0xFF, BytesValue.EMPTY); + } + + protected static Throwable deserializeError(SSZSerializer sszSerializer, int respCode, BytesValue data) { + return new WireRemoteRpcError("Remote peer call error: code = " + respCode + ", payload: " + data); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java similarity index 61% rename from wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java rename to wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java index 8920e77a3..8f656be55 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/BeaconPipeline.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java @@ -1,16 +1,22 @@ -package org.ethereum.beacon.wire.channel; +package org.ethereum.beacon.wire.channel.beacon; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicLong; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.MessageSerializer; import org.ethereum.beacon.wire.WireApiPeer; -import org.ethereum.beacon.wire.WireApiSub2; import org.ethereum.beacon.wire.WireApiSync; -import org.ethereum.beacon.wire.exceptions.WireRemoteRpcError; +import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.channel.ChannelCodec; +import org.ethereum.beacon.wire.channel.ChannelHub; +import org.ethereum.beacon.wire.channel.IdentityChannel; +import org.ethereum.beacon.wire.channel.RpcChannel; +import org.ethereum.beacon.wire.channel.RpcChannelAdapter; +import org.ethereum.beacon.wire.channel.RpcChannelClassFilter; +import org.ethereum.beacon.wire.channel.RpcChannelMapper; +import org.ethereum.beacon.wire.channel.RpcMessage; import org.ethereum.beacon.wire.message.Message; import org.ethereum.beacon.wire.message.RequestMessage; import org.ethereum.beacon.wire.message.RequestMessagePayload; @@ -24,121 +30,24 @@ import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; import org.ethereum.beacon.wire.message.payload.HelloMessage; -import org.ethereum.beacon.wire.message.payload.MessageType; import org.ethereum.beacon.wire.message.payload.NotifyNewAttestationMessage; import org.ethereum.beacon.wire.message.payload.NotifyNewBlockMessage; import tech.pegasys.artemis.util.bytes.BytesValue; -import tech.pegasys.artemis.util.uint.UInt64; public class BeaconPipeline { - static class BeaconRpcMapper extends RpcChannelMapper { - private AtomicLong idGen = new AtomicLong(1); - - public BeaconRpcMapper(Channel inChannel) { - super(inChannel); - } - - @Override - protected boolean isRequest(Message msg) { - return msg instanceof RequestMessage; - } - - @Override - protected boolean isNotification(Message msg) { - return MessageType.getById(((RequestMessage) msg).getMethodId()).isNotification(); - } - - @Override - protected Object generateNextId() { - return UInt64.valueOf(idGen.getAndIncrement()); - } - - @Override - protected Object getId(Message msg) { - return msg.getId(); - } - - @Override - protected void setId(Message msg, Object id) { - msg.setId((UInt64) id); - } - } - - static class BeaconPayloadCodec extends ChannelCodec< - RpcMessage, - RpcMessage> { - - private static final Object CONTEXT_REQUEST_MESSAGE_ID = new Object(); - - public BeaconPayloadCodec( - Channel> inChannel, - SSZSerializer sszSerializer) { - - super(inChannel, msg -> decode(sszSerializer, msg), msg -> encode(sszSerializer, msg)); - } - - static RpcMessage decode( - SSZSerializer sszSerializer, RpcMessage msg) { - - if (msg.isRequest()) { - MessageType messageType = MessageType.getById(msg.getRequest().getMethodId()); - RequestMessagePayload messagePayload = sszSerializer - .decode(msg.getRequest().getBody(), messageType.getRequestClass()); - return msg.copyWithRequest(messagePayload); - } else { - int methodId = (int) msg.popRequestContext(CONTEXT_REQUEST_MESSAGE_ID); - if (msg.getResponse().get().getResponseCode() == 0) { - ResponseMessagePayload messagePayload = - sszSerializer.decode( - msg.getResponse().get().getResult(), - MessageType.getById(methodId).getResponseClass()); - return msg.copyWithResponse(messagePayload); - } else { - return msg.copyWithResponseError(deserializeError(sszSerializer, - msg.getResponse().get().getResponseCode(), msg.getResponse().get().getResult())); - } - } - } - - static RpcMessage encode( - SSZSerializer sszSerializer, RpcMessage msg) { - if (msg.isRequest()) { - int methodId = msg.getRequest().getMethodId(); - msg.pushRequestContext(CONTEXT_REQUEST_MESSAGE_ID, methodId); - BytesValue payloadBytes = sszSerializer.encode2(msg.getRequest()); - return msg.copyWithRequest(new RequestMessage(methodId, payloadBytes)); - } else { - if (msg.getError().isPresent()) { - return msg.copyWithResponse(serializeError(sszSerializer, msg.getError().get())); - } else { - BytesValue payloadBytes = sszSerializer.encode2(msg.getResponse().get()); - return msg.copyWithResponse(new ResponseMessage(0, payloadBytes)); - } - } - } - - protected static ResponseMessage serializeError(SSZSerializer sszSerializer, Throwable t) { - return new ResponseMessage(0xFF, BytesValue.EMPTY); - } - - protected static Throwable deserializeError(SSZSerializer sszSerializer, int respCode, BytesValue data) { - return new WireRemoteRpcError("Remote peer call error: code = " + respCode + ", payload: " + data); - } - } - private final SSZSerializer sszSerializer; private final WireApiPeer peerServer; - private final WireApiSub2 subServer; + private final WireApiSubRpc subServer; private final WireApiSync syncServer; private WireApiPeer peerClient; - private WireApiSub2 subClient; + private WireApiSubRpc subClient; private WireApiSync syncClient; private RpcChannel rpcHub; public BeaconPipeline(SSZSerializer sszSerializer, WireApiPeer peerServer, - WireApiSub2 subServer, WireApiSync syncServer) { + WireApiSubRpc subServer, WireApiSync syncServer) { this.sszSerializer = sszSerializer; this.peerServer = peerServer; this.subServer = subServer; @@ -200,7 +109,7 @@ public WireApiPeer getPeerClient() { return peerClient; } - public WireApiSub2 getSubClient() { + public WireApiSubRpc getSubClient() { return subClient; } @@ -211,15 +120,15 @@ public WireApiSync getSyncClient() { private WireApiSync createWireApiSync(WireApiSync syncServer) { RpcChannelAdapter blockRootsAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockRootsRequestMessage.class), - syncServer::requestBlockRoots); + syncServer != null ? syncServer::requestBlockRoots : null); RpcChannelAdapter blockHeadersAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockHeadersRequestMessage.class), - syncServer::requestBlockHeaders); + syncServer != null ? syncServer::requestBlockHeaders : null); RpcChannelAdapter blockBodiesAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockBodiesRequestMessage.class), - req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get)); + syncServer != null ? req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get) : null); - WireApiSync syncClient = new WireApiSync() { + return new WireApiSync() { @Override public CompletableFuture requestBlockRoots( BlockRootsRequestMessage requestMessage) { @@ -238,26 +147,24 @@ public CompletableFuture> requestBlockBodie return blockBodiesAsync.invokeRemote(requestMessage).thenApply(Feedback::of); } }; - - return syncClient; } - private WireApiSub2 createWireApiSub(WireApiSub2 subServer) { + private WireApiSubRpc createWireApiSub(WireApiSubRpc subServer) { RpcChannelAdapter blocks = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, NotifyNewBlockMessage.class), - newBlock -> { + subServer == null ? null : newBlock -> { subServer.newBlock(newBlock.getBlock()); return null; }); RpcChannelAdapter attestations = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, NotifyNewAttestationMessage.class), - newAttest -> { + subServer == null ? null : newAttest -> { subServer.newAttestation(newAttest.getAttestation()); return null; }); - return new WireApiSub2() { + return new WireApiSubRpc() { @Override public void newBlock(BeaconBlock block) { blocks.notifyRemote(new NotifyNewBlockMessage(block)); @@ -273,14 +180,14 @@ public void newAttestation(Attestation attestation) { private WireApiPeer createWireApiPeer(WireApiPeer peerServer) { RpcChannelAdapter helloRpc = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, HelloMessage.class), - msg -> { + peerServer == null ? null : msg -> { peerServer.hello(msg); return null; }); RpcChannelAdapter goodbyeRpc = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, GoodbyeMessage.class), - msg -> { + peerServer == null ? null : msg -> { peerServer.goodbye(msg); return null; }); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconRpcMapper.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconRpcMapper.java new file mode 100644 index 000000000..20c7be6ec --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconRpcMapper.java @@ -0,0 +1,43 @@ +package org.ethereum.beacon.wire.channel.beacon; + +import java.util.concurrent.atomic.AtomicLong; +import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.channel.RpcChannelMapper; +import org.ethereum.beacon.wire.message.Message; +import org.ethereum.beacon.wire.message.RequestMessage; +import org.ethereum.beacon.wire.message.ResponseMessage; +import org.ethereum.beacon.wire.message.payload.MessageType; +import tech.pegasys.artemis.util.uint.UInt64; + +class BeaconRpcMapper extends RpcChannelMapper { + private AtomicLong idGen = new AtomicLong(1); + + public BeaconRpcMapper(Channel inChannel) { + super(inChannel); + } + + @Override + protected boolean isRequest(Message msg) { + return msg instanceof RequestMessage; + } + + @Override + protected boolean isNotification(Message msg) { + return MessageType.getById(((RequestMessage) msg).getMethodId()).isNotification(); + } + + @Override + protected Object generateNextId() { + return UInt64.valueOf(idGen.getAndIncrement()); + } + + @Override + protected Object getId(Message msg) { + return msg.getId(); + } + + @Override + protected void setId(Message msg, Object id) { + msg.setId((UInt64) id); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/WireApiSubAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/WireApiSubAdapter.java new file mode 100644 index 000000000..5fe7c6a68 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/WireApiSubAdapter.java @@ -0,0 +1,52 @@ +package org.ethereum.beacon.wire.channel.beacon; + +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.wire.WireApiSub; +import org.ethereum.beacon.wire.channel.beacon.WireApiSubRpc; +import org.reactivestreams.Publisher; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.ReplayProcessor; + +public class WireApiSubAdapter implements WireApiSub, WireApiSubRpc { + private final ReplayProcessor blockProcessor = ReplayProcessor.cacheLast(); + private final FluxSink blockSink = blockProcessor.sink(); + private final ReplayProcessor attestProcessor = ReplayProcessor.cacheLast(); + private final FluxSink attestSink = attestProcessor.sink(); + + private WireApiSubRpc subClient; + + public void setSubClient(WireApiSubRpc subClient) { + this.subClient = subClient; + } + + @Override + public void sendProposedBlock(BeaconBlock block) { + subClient.newBlock(block); + } + + @Override + public void sendAttestation(Attestation attestation) { + subClient.newAttestation(attestation); + } + + @Override + public Publisher inboundBlocksStream() { + return blockProcessor; + } + + @Override + public Publisher inboundAttestationsStream() { + return attestProcessor; + } + + @Override + public void newBlock(BeaconBlock block) { + blockSink.next(block); + } + + @Override + public void newAttestation(Attestation attestation) { + attestSink.next(attestation); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub2.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/WireApiSubRpc.java similarity index 73% rename from wire/src/main/java/org/ethereum/beacon/wire/WireApiSub2.java rename to wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/WireApiSubRpc.java index b32dd62c1..9467dc40e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub2.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/WireApiSubRpc.java @@ -1,10 +1,10 @@ -package org.ethereum.beacon.wire; +package org.ethereum.beacon.wire.channel.beacon; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; import org.reactivestreams.Publisher; -public interface WireApiSub2 { +public interface WireApiSubRpc { void newBlock(BeaconBlock block); diff --git a/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java index 76d04c844..49fdcaec0 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java @@ -8,6 +8,7 @@ import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.channel.beacon.BeaconPipeline; import org.ethereum.beacon.wire.message.Message; import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; @@ -79,15 +80,15 @@ public CompletableFuture> requestBlockBodie SSZSerializer sszSerializer = new SSZBuilder().buildSerializer(); - BeaconPipeline peer1Pipeline = new BeaconPipeline(sszSerializer); + BeaconPipeline peer1Pipeline = new BeaconPipeline(sszSerializer, null ,null, dummyServer); SimpleChannel peer1Channel = new SimpleChannel<>(_2to1, _1to2); peer1Pipeline.initFromMessageChannel(peer1Channel); - BeaconPipeline peer2Pipeline = new BeaconPipeline(sszSerializer); + BeaconPipeline peer2Pipeline = new BeaconPipeline(sszSerializer, null ,null, dummyServer); SimpleChannel peer2Channel = new SimpleChannel<>(_1to2, _2to1); peer2Pipeline.initFromMessageChannel(peer2Channel); - WireApiSync peer2SyncClient = peer2Pipeline.createWireApiSync(dummyServer); + WireApiSync peer2SyncClient = peer2Pipeline.getSyncClient(); CompletableFuture resp = peer2SyncClient .requestBlockRoots(new BlockRootsRequestMessage(SlotNumber.ZERO, UInt64.ZERO)); From bd6dea6bdf48fe8a27e45ba63541fc0ab2b1abb6 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 9 May 2019 20:29:50 +0300 Subject: [PATCH 24/96] Further work on sync by wire --- .../java/org/ethereum/beacon/util/Utils.java | 12 ++ .../beacon/wire/SimplePeerManagerImpl.java | 44 ++--- .../beacon/wire/WireApiSubRouter.java | 56 +++++++ .../beacon/wire/WireApiSyncServer.java | 6 +- .../wire/channel/RpcChannelAdapter.java | 4 +- .../wire/channel/RpcChannelClassFilter.java | 4 +- .../beacon/wire/channel/RpcChannelMapper.java | 10 +- .../beacon/wire/channel/RpcMessage.java | 6 +- .../channel/beacon/BeaconPayloadCodec.java | 8 +- .../wire/channel/beacon/BeaconPipeline.java | 25 ++- .../wire/message/ResponseMessagePayload.java | 10 -- .../wire/message/SSZMessageSerializer.java | 27 ++++ .../payload/BlockBodiesResponseMessage.java | 5 +- .../payload/BlockHeadersResponseMessage.java | 5 +- .../payload/BlockRootsResponseMessage.java | 1 - .../wire/message/payload/HelloMessage.java | 9 +- .../beacon/wire/net/ConnectionManager.java | 80 +++++++++ .../beacon/wire/net/NettyChannel.java | 4 + .../beacon/wire/sync/WireApiSyncRouter.java | 62 +++++++ .../org/ethereum/beacon/wire/PeersTest.java | 153 ++++++++++++++++++ 20 files changed, 469 insertions(+), 62 deletions(-) create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/message/SSZMessageSerializer.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java diff --git a/util/src/main/java/org/ethereum/beacon/util/Utils.java b/util/src/main/java/org/ethereum/beacon/util/Utils.java index 1cf05e388..4d4a92eb9 100644 --- a/util/src/main/java/org/ethereum/beacon/util/Utils.java +++ b/util/src/main/java/org/ethereum/beacon/util/Utils.java @@ -1,6 +1,7 @@ package org.ethereum.beacon.util; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Stream; @@ -13,4 +14,15 @@ public static Function, Stream> optionalFlatMap(Function Function> nullableFlatMap(Function func) { return n -> n != null ? Stream.of(func.apply(n)) : Stream.empty(); } + + public static void futureForward(CompletableFuture result, CompletableFuture forwardToFuture) { + result.whenComplete( + (res, t) -> { + if (t != null) { + forwardToFuture.completeExceptionally(t); + } else { + forwardToFuture.complete(res); + } + }); + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java index 9185c477a..3fdde38de 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java @@ -12,6 +12,7 @@ import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.message.payload.HelloMessage; +import org.ethereum.beacon.wire.sync.WireApiSyncRouter; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -21,18 +22,21 @@ public class SimplePeerManagerImpl implements PeerManager { private static final Logger logger = LogManager.getLogger(SimplePeerManagerImpl.class); - private byte networkId; - private UInt64 chainId; + private final byte networkId; + private final UInt64 chainId; - Publisher> channelsStream; - SSZSerializer ssz; - BeaconChainSpec spec; - MessageSerializer messageSerializer; - WireApiSync syncServer; - Publisher headStream; + private final Publisher> channelsStream; + private final SSZSerializer ssz; + private final BeaconChainSpec spec; + private final MessageSerializer messageSerializer; + private final WireApiSync syncServer; + private final Publisher headStream; + private final WireApiSyncRouter wireApiSyncRouter; + private final WireApiSubRouter wireApiSubRouter; - Flux connectedPeersStream; - List activePeers = Collections.synchronizedList(new ArrayList<>()); + + private final Flux connectedPeersStream; + private final List activePeers = Collections.synchronizedList(new ArrayList<>()); public SimplePeerManagerImpl( byte networkId, @@ -56,10 +60,18 @@ public SimplePeerManagerImpl( connectedPeersStream = Flux.from(channelsStream).map(this::createPeer).publish().autoConnect(); Flux.from(activePeerStream()).subscribe(this::onNewActivePeer); + + wireApiSyncRouter = new WireApiSyncRouter( + Flux.from(activePeerStream()).map(Peer::getSyncApi), + Flux.from(disconnectedPeerStream()).map(Peer::getSyncApi)); + + wireApiSubRouter = new WireApiSubRouter( + Flux.from(activePeerStream()).map(Peer::getSubApi), + Flux.from(disconnectedPeerStream()).map(Peer::getSubApi)); } protected HelloMessage createLocalHello() { - BeaconTupleDetails head = Flux.from(headStream).last().block(Duration.ZERO); + BeaconTupleDetails head = Mono.from(headStream).block(Duration.ofSeconds(10)); // TODO return new HelloMessage( networkId, chainId, @@ -102,17 +114,11 @@ public Collection getActivePeers() { @Override public WireApiSync getWireApiSync() { - if (getActivePeers().isEmpty()) { - throw new IllegalStateException("No peers connected yet. This is naive implementation"); - } - return getActivePeers().iterator().next().getSyncApi(); + return wireApiSyncRouter; } @Override public WireApiSub getWireApiSub() { - if (getActivePeers().isEmpty()) { - throw new IllegalStateException("No peers connected yet. This is naive implementation"); - } - return getActivePeers().iterator().next().getSubApi(); + return wireApiSubRouter; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java new file mode 100644 index 000000000..e6e238275 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java @@ -0,0 +1,56 @@ +package org.ethereum.beacon.wire; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +public class WireApiSubRouter implements WireApiSub { + + private final List activeApis = new CopyOnWriteArrayList<>(); + + private final Flux attestationsStream; + private final Flux blocksStream; + + public WireApiSubRouter( + Publisher addedPeersStream, + Publisher removedPeersStream) { + + blocksStream = Flux + .from(addedPeersStream) + .flatMap(WireApiSub::inboundBlocksStream) + .distinct(); + + attestationsStream = Flux + .from(addedPeersStream) + .flatMap(WireApiSub::inboundAttestationsStream) + .distinct(); + + Flux.from(addedPeersStream).subscribe(api -> activeApis.add(api)); + Flux.from(removedPeersStream).subscribe(api -> activeApis.remove(api)); + } + + @Override + public void sendProposedBlock(BeaconBlock block) { + activeApis.forEach(api -> api.sendProposedBlock(block)); + } + + @Override + public void sendAttestation(Attestation attestation) { + activeApis.forEach(api -> api.sendAttestation(attestation)); + } + + @Override + public Publisher inboundBlocksStream() { + return blocksStream; + } + + @Override + public Publisher inboundAttestationsStream() { + return attestationsStream; + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index 0a6480959..555acc818 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -86,9 +86,9 @@ public CompletableFuture requestBlockHeaders( } prevSlot = nonEmptySlot; } - ret.complete(new BlockHeadersResponseMessage(requestMessage, headers)); + ret.complete(new BlockHeadersResponseMessage(headers)); } else { - ret.complete(new BlockHeadersResponseMessage(requestMessage, Collections.emptyList())); + ret.complete(new BlockHeadersResponseMessage(Collections.emptyList())); } return ret; } @@ -104,6 +104,6 @@ public CompletableFuture> requestBlockBodie .flatMap(optionalFlatMap(BeaconBlock::getBody)) .collect(Collectors.toList()); return CompletableFuture.completedFuture( - Feedback.of(new BlockBodiesResponseMessage(requestMessage, bodyList))); + Feedback.of(new BlockBodiesResponseMessage(bodyList))); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java index a7ea50932..c1c07e84e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java @@ -38,7 +38,7 @@ private void onInbound(RpcMessage msg) { private void handleResponse(RpcMessage msg) { CompletableFuture respFut = - (CompletableFuture) msg.popRequestContext(CONTEXT_KEY_FUTURE); + (CompletableFuture) msg.getRequestContext(CONTEXT_KEY_FUTURE); if (msg.getResponse().isPresent()) { respFut.complete(msg.getResponse().get()); @@ -76,7 +76,7 @@ public CompletableFuture invokeRemote(TRequestMessage request) RpcMessage requestRpcMsg = new RpcMessage<>(request, false); CompletableFuture ret = new CompletableFuture<>(); - requestRpcMsg.pushRequestContext(CONTEXT_KEY_FUTURE, ret); + requestRpcMsg.setRequestContext(CONTEXT_KEY_FUTURE, ret); outboundStream.onNext(requestRpcMsg); return ret; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java index 5c77b29fe..3b35a9ff6 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelClassFilter.java @@ -21,7 +21,7 @@ public RpcChannelClassFilter(RpcChannel inChannel, Class> inboundMessageStream() { return Flux.from(inChannel.inboundMessageStream()) .filter(rpcMsg -> rpcMsg.isRequest() && requestMessageClass.isInstance(rpcMsg.getRequest()) - || rpcMsg.isResponse() && requestMessageClass == rpcMsg.popRequestContext(CONTEXT_KEY_REQ_CLASS)) + || rpcMsg.isResponse() && requestMessageClass == rpcMsg.getRequestContext(CONTEXT_KEY_REQ_CLASS)) .map(msg -> (RpcMessage) msg); } @@ -33,7 +33,7 @@ public void subscribeToOutbound(Publisher> outbo if (!requestMessageClass.isInstance(rpcMsg.getRequest())) { throw new IllegalArgumentException("Invalid request class: " + rpcMsg.getRequest().getClass() + ", expected " + requestMessageClass); } - rpcMsg.pushRequestContext(CONTEXT_KEY_REQ_CLASS, requestMessageClass); + rpcMsg.setRequestContext(CONTEXT_KEY_REQ_CLASS, requestMessageClass); } }) .map(msg -> (RpcMessage) msg) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java index 0d01bd8cd..a9bd0752c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelMapper.java @@ -31,7 +31,7 @@ protected RpcMessage fromIn(TInMessage msg) { } else { RpcMessage rpcMessage = new RpcMessage<>( (TOutRequest) msg, false); - rpcMessage.pushRequestContext(CONTEXT_ID_KEY, getId(msg)); + rpcMessage.setRequestContext(CONTEXT_ID_KEY, getId(msg)); return rpcMessage; } } else { @@ -48,18 +48,18 @@ protected RpcMessage fromIn(TInMessage msg) { protected TInMessage toIn(RpcMessage msg) { if (msg.isRequest()) { + TInMessage inMessage = (TInMessage) msg.getRequest(); + Object id = generateNextId(); + setId(inMessage, id); if (msg.isNotification()) { return (TInMessage) msg.getRequest(); } else { - TInMessage inMessage = (TInMessage) msg.getRequest(); - Object id = generateNextId(); - setId(inMessage, id); idToContextMap.put(id, msg.getRequestContext()); return inMessage; } } else { TInMessage inMessage = (TInMessage) msg.getResponse().get(); - setId(inMessage, msg.popRequestContext(CONTEXT_ID_KEY)); + setId(inMessage, msg.getRequestContext(CONTEXT_ID_KEY)); return inMessage; } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java index 3b626022c..9e7b2db1d 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcMessage.java @@ -84,18 +84,18 @@ public RpcMessage copyWit return ret; } - public void pushRequestContext(Object key, Object value) { + public void setRequestContext(Object key, Object value) { if (!isRequest()) { throw new IllegalStateException("Context can be added to request only"); } requestContext.put(key, value); } - public Object popRequestContext(Object key) { + public Object getRequestContext(Object key) { if (!isResponse()) { throw new IllegalStateException("Context can be pushed from response only"); } - return requestContext.remove(key); + return requestContext.get(key); } Map getRequestContext() { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java index 52bc9df53..2f792254e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPayloadCodec.java @@ -1,5 +1,7 @@ package org.ethereum.beacon.wire.channel.beacon; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.channel.ChannelCodec; @@ -16,6 +18,8 @@ class BeaconPayloadCodec extends ChannelCodec< RpcMessage, RpcMessage> { + private static final Logger logger = LogManager.getLogger(BeaconPayloadCodec.class); + private static final Object CONTEXT_REQUEST_MESSAGE_ID = new Object(); public BeaconPayloadCodec( @@ -34,7 +38,7 @@ static RpcMessage decode( .decode(msg.getRequest().getBody(), messageType.getRequestClass()); return msg.copyWithRequest(messagePayload); } else { - int methodId = (int) msg.popRequestContext(CONTEXT_REQUEST_MESSAGE_ID); + int methodId = (int) msg.getRequestContext(CONTEXT_REQUEST_MESSAGE_ID); if (msg.getResponse().get().getResponseCode() == 0) { ResponseMessagePayload messagePayload = sszSerializer.decode( @@ -52,7 +56,7 @@ static RpcMessage encode( SSZSerializer sszSerializer, RpcMessage msg) { if (msg.isRequest()) { int methodId = msg.getRequest().getMethodId(); - msg.pushRequestContext(CONTEXT_REQUEST_MESSAGE_ID, methodId); + msg.setRequestContext(CONTEXT_REQUEST_MESSAGE_ID, methodId); BytesValue payloadBytes = sszSerializer.encode2(msg.getRequest()); return msg.copyWithRequest(new RequestMessage(methodId, payloadBytes)); } else { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java index 8f656be55..c474c843f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java @@ -1,6 +1,8 @@ package org.ethereum.beacon.wire.channel.beacon; import java.util.concurrent.CompletableFuture; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.ssz.SSZSerializer; @@ -35,6 +37,7 @@ import tech.pegasys.artemis.util.bytes.BytesValue; public class BeaconPipeline { + private static final Logger logger = LogManager.getLogger(BeaconPipeline.class); private final SSZSerializer sszSerializer; private final WireApiPeer peerServer; @@ -71,14 +74,30 @@ public void initFromMessageChannel(Channel messageChannel) { RpcMessage> payloadCodec = new BeaconPayloadCodec(rpcMessageChannel, sszSerializer); + IdentityChannel> loggerChannel = + new IdentityChannel>( + payloadCodec) { + @Override + protected void onInbound(RpcMessage msg) + throws RuntimeException { + logger.debug(" ==> " + (msg.isRequest() ? msg.getRequest() : msg.getResponse().get())); + } + + @Override + protected void onOutbound(RpcMessage msg) + throws RuntimeException { + logger.debug(" <== " + (msg.isRequest() ? msg.getRequest() : msg.getResponse().get())); + } + }; + RpcChannel inboundResponsePayloadValidator = RpcChannel .from(new IdentityChannel>( - payloadCodec) { + loggerChannel) { @Override protected void onOutbound(RpcMessage msg) throws RuntimeException { if (msg.isRequest()) { - msg.pushRequestContext("validatorRequestMessgae", msg.getRequest()); + msg.setRequestContext("validatorRequestMessgae", msg.getRequest()); } } @@ -87,7 +106,7 @@ protected void onInbound(RpcMessage blockBodies; - public BlockBodiesResponseMessage( - BlockBodiesRequestMessage request, - List blockBodies) { - super(request); + public BlockBodiesResponseMessage(List blockBodies) { this.blockBodies = blockBodies; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java index 954b4ff28..09a31f4d7 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java @@ -11,10 +11,7 @@ public class BlockHeadersResponseMessage extends ResponseMessagePayload { @SSZ private final List headers; - public BlockHeadersResponseMessage( - BlockHeadersRequestMessage request, - List headers) { - super(request); + public BlockHeadersResponseMessage(List headers) { this.headers = headers; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java index c0247e362..ca4a8090f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java @@ -32,7 +32,6 @@ public SlotNumber getSlot() { @SSZ private final List roots; public BlockRootsResponseMessage(List roots) { - super(null); this.roots = roots; } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java index 544e322e3..6dbb6bcb2 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java @@ -12,14 +12,15 @@ public class HelloMessage extends RequestMessagePayload { public static final int METHOD_ID = 0x0; - @SSZ private final byte networkId; + @SSZ(type = "uint8") + private final int networkId; @SSZ private final UInt64 chainId; @SSZ private final Hash32 latestFinalizedRoot; @SSZ private final EpochNumber latestFinalizedEpoch; @SSZ private final Hash32 bestRoot; @SSZ private final SlotNumber bestSlot; - public HelloMessage(byte networkId, UInt64 chainId, + public HelloMessage(int networkId, UInt64 chainId, Hash32 latestFinalizedRoot, EpochNumber latestFinalizedEpoch, Hash32 bestRoot, SlotNumber bestSlot) { this.networkId = networkId; @@ -35,8 +36,8 @@ public int getMethodId() { return METHOD_ID; } - public byte getNetworkId() { - return networkId; + public int getNetworkId() { + return (byte) networkId; } public UInt64 getChainId() { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java new file mode 100644 index 000000000..d222f769b --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java @@ -0,0 +1,80 @@ +package org.ethereum.beacon.wire.net; + +import java.time.Duration; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.wire.channel.Channel; +import org.reactivestreams.Publisher; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class ConnectionManager { + private static final Logger logger = LogManager.getLogger(ConnectionManager.class); + + private final Server server; + private final Client client; + private final Scheduler rxScheduler; + + private final DirectProcessor> clientConnections = DirectProcessor.create(); + private final FluxSink> clientConnectionsSink = clientConnections.sink(); + private final Set activePeers = Collections.synchronizedSet(new HashSet<>()); + + public ConnectionManager(Server server, Client client, + Scheduler rxScheduler) { + this.server = server; + this.client = client; + this.rxScheduler = rxScheduler; + } + + public CompletableFuture> connect(TAddress peerAddress) { + return client.connect(peerAddress) + .thenApply(a -> { + clientConnectionsSink.next(a); + return a; + }); + } + + public void addActivePeer(TAddress peerAddress) { + activePeers.add(peerAddress); + // TODO make sure this woodoo magic actually works + Flux.just(peerAddress) + .doOnNext(addr -> logger.info("Connecting to active peer " + peerAddress)) + .map(client::connect) + .flatMap(Mono::fromFuture, 1, 1) + .onErrorContinue((t, o) -> logger.info("Couldn't connect to active peer " + peerAddress + ": " + t)) + .doOnNext(ch -> logger.info("Connected to active peer " + peerAddress)) + .doOnNext(clientConnectionsSink::next) + .map(Channel::getCloseFuture) + .flatMap(Mono::fromFuture, 1, 1) + .doOnNext(ch -> logger.info("Disconnected from active peer " + peerAddress)) + .repeat(() -> activePeers.contains(peerAddress)) + .delayElements(Duration.ofSeconds(1), rxScheduler) + .subscribe(); + } + + public void removeActivePeer(TAddress peerAddress) { + activePeers.remove(peerAddress); + } + + public Publisher> channelsStream() { + return Flux.merge( + server == null ? Flux.empty() : server.channelsStream(), + client == null ? Flux.empty() : clientConnections); + } + + public static void main(String[] args) throws Exception { + Flux.merge( + Flux.just(1).delayElements(Duration.ofMillis(1000)), + Flux.just(2).delayElements(Duration.ofMillis(100))) + .subscribe(i -> System.out.println(i)); + Thread.sleep(2000); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java index 500adfd9c..0b1a7ed5c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java @@ -7,6 +7,8 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.function.Consumer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ethereum.beacon.wire.channel.Channel; import org.reactivestreams.Publisher; import reactor.core.Disposable; @@ -16,6 +18,7 @@ import tech.pegasys.artemis.util.bytes.BytesValue; public class NettyChannel extends SimpleChannelInboundHandler implements Channel { + private static final Logger logger = LogManager.getLogger(NettyChannel.class); private final Consumer activeChannelListener; private final ReplayProcessor inMessages = ReplayProcessor.create(); @@ -60,6 +63,7 @@ private void closed() { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.warn("Exception caught", cause); inMessagesSink.error(cause); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java new file mode 100644 index 000000000..d372e4ddb --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java @@ -0,0 +1,62 @@ +package org.ethereum.beacon.wire.sync; + +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; +import java.util.function.Function; +import org.ethereum.beacon.util.Utils; +import org.ethereum.beacon.wire.Feedback; +import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.message.payload.BlockBodiesRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockBodiesResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; +import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import org.reactivestreams.Publisher; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.ReplayProcessor; + +public class WireApiSyncRouter implements WireApiSync { + + private final ReplayProcessor> tasks = ReplayProcessor.create(64); + private final FluxSink> tasksSink = tasks.sink(); + + public WireApiSyncRouter( + Publisher addedPeersStream, + Publisher removedPeersStream) { + + // TODO simple unlimited first peer here, need something more smart + Publisher freePeersStream = Flux.from(addedPeersStream) + .flatMap(api -> Flux.just(api).repeat()); + + Flux.zip(freePeersStream, tasks) + .subscribe(p -> p.getT2().accept(p.getT1())); + } + + private CompletableFuture submitAsyncTask(Function> task) { + CompletableFuture ret = new CompletableFuture<>(); + tasksSink.next(api -> Utils.futureForward(task.apply(api), ret)); + return ret; + } + + @Override + public CompletableFuture requestBlockRoots( + BlockRootsRequestMessage requestMessage) { + return submitAsyncTask(api -> api.requestBlockRoots(requestMessage)); + } + + @Override + public CompletableFuture requestBlockHeaders( + BlockHeadersRequestMessage requestMessage) { + return submitAsyncTask(api -> api.requestBlockHeaders(requestMessage)); + } + + @Override + public CompletableFuture> requestBlockBodies( + BlockBodiesRequestMessage requestMessage) { + return submitAsyncTask(api -> api.requestBlockBodies(requestMessage)); + } +} diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java new file mode 100644 index 000000000..0eba000fa --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -0,0 +1,153 @@ +package org.ethereum.beacon.wire; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.time.Duration; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; +import org.ethereum.beacon.Launcher; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.simulator.SimulatorLauncher; +import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; +import org.ethereum.beacon.ssz.SSZBuilder; +import org.ethereum.beacon.ssz.SSZSerializer; +import org.ethereum.beacon.stream.SimpleProcessor; +import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.channel.beacon.WireApiSubRpc; +import org.ethereum.beacon.wire.message.SSZMessageSerializer; +import org.ethereum.beacon.wire.net.Client; +import org.ethereum.beacon.wire.net.ConnectionManager; +import org.ethereum.beacon.wire.net.NettyClient; +import org.ethereum.beacon.wire.net.NettyServer; +import org.ethereum.beacon.wire.net.Server; +import org.ethereum.beacon.wire.sync.BeaconBlockTree; +import org.ethereum.beacon.wire.sync.SyncManager; +import org.ethereum.beacon.wire.sync.SyncQueue; +import org.ethereum.beacon.wire.sync.SyncQueueImpl; +import org.junit.Assert; +import org.junit.Test; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; + +public class PeersTest { + + @Test + public void test0() { + SimpleProcessor processor = new SimpleProcessor<>(Schedulers.single(), "aaa"); + processor.onNext(1); + Integer i = Mono.from(processor).block(Duration.ofMillis(300)); + System.out.println(i); + } + + @Test + public void test1() throws Exception { + int slotCount = 1; + SimulatorLauncher simulatorLauncher = new Builder() + .withConfigFromResource("/sync-simulation-config.yml") + .build(); + simulatorLauncher.run(slotCount); + Launcher peer0 = simulatorLauncher.getPeers().get(0); + System.out.println(peer0); + + Launcher peer1 = simulatorLauncher.createPeer("test"); + + { + // peer 0 + Server server = new NettyServer(40001); + server.start().await(); + System.out.println("Peer 0 listening on port 40001"); + ConnectionManager connectionManager = new ConnectionManager<>( + server, null, Schedulers.single()); + + SSZSerializer ssz = new SSZBuilder().buildSerializer(); + MessageSerializer messageSerializer = new SSZMessageSerializer(ssz); + WireApiSyncServer syncServer = new WireApiSyncServer(peer0.getBeaconChainStorage()); + SimplePeerManagerImpl peerManager = new SimplePeerManagerImpl( + (byte) 1, + UInt64.valueOf(1), + connectionManager.channelsStream(), + ssz, + peer0.getSpec(), + messageSerializer, + syncServer, + peer0.getBeaconChain().getBlockStatesStream()); + + Flux.from(peerManager.connectedPeerStream()) + .subscribe(peer -> System.out.println("Remote peer connected: " + peer)); + Flux.from(peerManager.activePeerStream()) + .subscribe(peer -> System.out.println("Remote peer active: " + peer)); + Flux.from(peerManager.disconnectedPeerStream()) + .subscribe(peer -> System.out.println("Remote peer disconnected: " + peer)); + System.out.println("Peer 0 is ready."); + } + + { + // peer 1 + ConnectionManager connectionManager = new ConnectionManager<>( + null, new NettyClient(), Schedulers.single()); + + SSZSerializer ssz = new SSZBuilder().buildSerializer(); + MessageSerializer messageSerializer = new SSZMessageSerializer(ssz); + SimplePeerManagerImpl peerManager = new SimplePeerManagerImpl( + (byte) 1, + UInt64.valueOf(1), + connectionManager.channelsStream(), + ssz, + peer1.getSpec(), + messageSerializer, + null, + peer1.getBeaconChain().getBlockStatesStream()); + Flux.from(peer1.getBeaconChain().getBlockStatesStream()) + .subscribe(s -> System.out.println("### " + s)); + + Flux.from(peerManager.connectedPeerStream()) + .subscribe(peer -> System.out.println("Peer 1 connected: " + peer)); + Flux.from(peerManager.activePeerStream()) + .subscribe(peer -> System.out.println("Remote peer active: " + peer)); + Flux.from(peerManager.disconnectedPeerStream()) + .subscribe(peer -> System.out.println("Remote peer disconnected: " + peer)); + + BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); + SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); + + SyncManager syncManager = new SyncManager( + peer1.getBeaconChain(), + peer1.getBeaconChainStorage(), + peer1.getSpec(), + peerManager.getWireApiSync(), + syncQueue, + 1); + + AtomicBoolean synced = new AtomicBoolean(); + Flux.from(peer1.getBeaconChain().getBlockStatesStream()) + .subscribe(s -> { + System.out.println(s); + if (s.getFinalState().getSlot().equals( + simulatorLauncher.getSpec().getConstants().getGenesisSlot().plus(slotCount))) { + syncManager.stop(); + synced.set(true); + } + }); + + System.out.println("Peer 1: starting sync manager"); + syncManager.start(); + + // simulatorLauncher.getControlledSchedulers().addTime(3000); + + System.out.println("Peer 1: connecting to peer 0 for syncing..."); + CompletableFuture> localhost = connectionManager + .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + localhost.get(); + System.out.println("Peer 1: connected to peer 0"); + + + Thread.sleep(10000000); + Assert.assertTrue(synced.get()); + System.out.println("Done"); + } + } +} From 494b72c6e5d6fdbe78864ab1c3dcba02c04a9ecd Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 10 May 2019 12:03:35 +0300 Subject: [PATCH 25/96] Add some toString's --- .../java/org/ethereum/beacon/wire/PeerImpl.java | 15 ++++++++++++++- .../payload/BlockBodiesRequestMessage.java | 7 +++++++ .../payload/BlockBodiesResponseMessage.java | 7 +++++++ .../payload/BlockHeadersRequestMessage.java | 10 ++++++++++ .../payload/BlockHeadersResponseMessage.java | 7 +++++++ .../message/payload/BlockRootsRequestMessage.java | 8 ++++++++ .../payload/BlockRootsResponseMessage.java | 7 +++++++ .../wire/message/payload/GoodbyeMessage.java | 7 +++++++ .../beacon/wire/message/payload/HelloMessage.java | 12 ++++++++++++ .../payload/NotifyNewAttestationMessage.java | 7 +++++++ .../message/payload/NotifyNewBlockMessage.java | 7 +++++++ .../ethereum/beacon/wire/net/NettyChannel.java | 5 +++++ 12 files changed, 98 insertions(+), 1 deletion(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java index 6bc20f3be..dc537b14b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java @@ -1,9 +1,10 @@ package org.ethereum.beacon.wire; import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.ssz.SSZSerializer; -import org.ethereum.beacon.wire.channel.beacon.BeaconPipeline; import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.channel.beacon.BeaconPipeline; import org.ethereum.beacon.wire.channel.beacon.WireApiSubAdapter; import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; import org.ethereum.beacon.wire.message.payload.HelloMessage; @@ -92,4 +93,16 @@ public WireApiSync getSyncApi() { public WireApiSub getSubApi() { return wireApiSubAdapter; } + + @Override + public String toString() { + String bestSlot; + try { + bestSlot = + getRemoteHelloMessage().isDone() ? getRemoteHelloMessage().get().getBestSlot().toString() : null; + } catch (Exception e) { + bestSlot = "(err )" + e; + } + return "Peer[" + channel + (bestSlot == null ? "" : ", slot: " + bestSlot) + "]"; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java index d99f78844..fe7ac5768 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesRequestMessage.java @@ -26,4 +26,11 @@ public int getMethodId() { public List getBlockTreeRoots() { return blockTreeRoots; } + + @Override + public String toString() { + return "BlockBodiesRequestMessage{" + + "blockTreeRoots=" + blockTreeRoots + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesResponseMessage.java index 992837c79..32d1a924e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockBodiesResponseMessage.java @@ -18,4 +18,11 @@ public BlockBodiesResponseMessage(List blockBodies) { public List getBlockBodies() { return blockBodies; } + + @Override + public String toString() { + return "BlockBodiesResponseMessage{" + + "blockBodies=" + blockBodies + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java index a412681aa..549f58fe1 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersRequestMessage.java @@ -46,4 +46,14 @@ public UInt64 getMaxHeaders() { public UInt64 getSkipSlots() { return skipSlots; } + + @Override + public String toString() { + return "BlockHeadersRequestMessage{" + + (NULL_START_ROOT.equals(startRoot) ? "" : "startRoot=" + startRoot + ", ") + + (NULL_START_SLOT.equals(startSlot) ? "" : "startSlot=" + startSlot + ", ") + + "maxHeaders=" + maxHeaders + + ", skipSlots=" + skipSlots + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java index 09a31f4d7..fa427fde3 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockHeadersResponseMessage.java @@ -18,4 +18,11 @@ public BlockHeadersResponseMessage(List headers) { public List getHeaders() { return headers; } + + @Override + public String toString() { + return "BlockHeadersResponseMessage{" + + "headers=" + headers + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java index 4325ce17b..31261b321 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsRequestMessage.java @@ -30,4 +30,12 @@ public SlotNumber getStartSlot() { public UInt64 getCount() { return count; } + + @Override + public String toString() { + return "BlockRootsRequestMessage{" + + "startSlot=" + startSlot + + ", count=" + count + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java index ca4a8090f..fa5f40329 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/BlockRootsResponseMessage.java @@ -38,4 +38,11 @@ public BlockRootsResponseMessage(List roots) { public List getRoots() { return roots; } + + @Override + public String toString() { + return "BlockRootsResponseMessage{" + + "roots=" + roots + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java index e5a241533..cb2b15497 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/GoodbyeMessage.java @@ -27,4 +27,11 @@ public int getMethodId() { public UInt64 getReason() { return reason; } + + @Override + public String toString() { + return "GoodbyeMessage{" + + "reason=" + reason + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java index 6dbb6bcb2..0b47eeffa 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/HelloMessage.java @@ -59,4 +59,16 @@ public Hash32 getBestRoot() { public SlotNumber getBestSlot() { return bestSlot; } + + @Override + public String toString() { + return "HelloMessage{" + + "networkId=" + networkId + + ", chainId=" + chainId + + ", latestFinalizedRoot=" + latestFinalizedRoot + + ", latestFinalizedEpoch=" + latestFinalizedEpoch + + ", bestRoot=" + bestRoot + + ", bestSlot=" + bestSlot + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java index a2c9956a6..8a5df9095 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewAttestationMessage.java @@ -24,4 +24,11 @@ public Attestation getAttestation() { public int getMethodId() { return METHOD_ID; } + + @Override + public String toString() { + return "NotifyNewAttestationMessage{" + + "attestation=" + attestation + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java index 1a5f1138b..88d40b84f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/message/payload/NotifyNewBlockMessage.java @@ -24,4 +24,11 @@ public BeaconBlock getBlock() { public int getMethodId() { return METHOD_ID; } + + @Override + public String toString() { + return "NotifyNewBlockMessage{" + + "block=" + block + + '}'; + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java index 0b1a7ed5c..4760ce902 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java @@ -87,4 +87,9 @@ public SocketAddress getLocalAddress() { public SocketAddress getRemoteAddress() { return ctx.channel().remoteAddress(); } + + @Override + public String toString() { + return "NettyChannel[" + ctx.channel() + "]"; + } } From 8222598be4a710377e9b4fbf3fde0938e7da1cc4 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 10 May 2019 19:02:17 +0300 Subject: [PATCH 26/96] Add SyncManager Long/Short mode --- .../beacon/stream/SimpleProcessor.java | 9 +- .../beacon/wire/net/NettyChannel.java | 2 +- .../wire/net/NettyChannelInitializer.java | 2 +- .../beacon/wire/sync/SyncManager.java | 104 +++++++++++++----- .../org/ethereum/beacon/wire/PeersTest.java | 43 +++++++- .../ethereum/beacon/wire/sync/SyncTest.java | 4 +- 6 files changed, 124 insertions(+), 40 deletions(-) diff --git a/util/src/main/java/org/ethereum/beacon/stream/SimpleProcessor.java b/util/src/main/java/org/ethereum/beacon/stream/SimpleProcessor.java index cbd0b349c..2c803afc9 100644 --- a/util/src/main/java/org/ethereum/beacon/stream/SimpleProcessor.java +++ b/util/src/main/java/org/ethereum/beacon/stream/SimpleProcessor.java @@ -5,17 +5,20 @@ import org.reactivestreams.Subscription; import reactor.core.publisher.Flux; import reactor.core.publisher.FluxProcessor; +import reactor.core.publisher.FluxSink; import reactor.core.publisher.ReplayProcessor; import reactor.core.scheduler.Scheduler; public class SimpleProcessor implements Processor { FluxProcessor subscriber; + FluxSink sink; Flux publisher; boolean subscribed; public SimpleProcessor(Scheduler scheduler, String name) { ReplayProcessor processor = ReplayProcessor.cacheLast(); subscriber = processor; + sink = subscriber.sink(); publisher = Flux.from(processor) .publishOn(scheduler) .onBackpressureError() @@ -54,16 +57,16 @@ public void onSubscribe(Subscription subscription) { @Override public void onNext(T t) { - subscriber.onNext(t); + sink.next(t); } @Override public void onError(Throwable throwable) { - subscriber.onError(throwable); + sink.error(throwable); } @Override public void onComplete() { - subscriber.onComplete(); + sink.complete(); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java index 4760ce902..661055978 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java @@ -64,7 +64,7 @@ private void closed() { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.warn("Exception caught", cause); - inMessagesSink.error(cause); +// inMessagesSink.error(cause); } @Override diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java index 41f4383da..93a4b66f9 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java @@ -16,7 +16,7 @@ import org.apache.logging.log4j.Logger; class NettyChannelInitializer extends ChannelInitializer { - private static final int READ_TIMEOUT_SEC = 60; + private static final int READ_TIMEOUT_SEC = 5; private static final Logger logger = LogManager.getLogger(NettyChannelInitializer.class); private final Consumer activeChannelListener; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index ab732e770..77ce95bc3 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -1,6 +1,9 @@ package org.ethereum.beacon.wire.sync; +import java.time.Duration; +import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -15,10 +18,13 @@ import org.ethereum.beacon.wire.WireApiSync; import org.ethereum.beacon.wire.exceptions.WireInvalidConsensusDataException; import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.sync.SyncQueue.BlockRequest; import org.reactivestreams.Publisher; import reactor.core.Disposable; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; import tech.pegasys.artemis.ethereum.core.Hash32; public class SyncManager { @@ -32,6 +38,9 @@ public class SyncManager { private final WireApiSync syncApi; private Publisher> newBlocks; private final SyncQueue syncQueue; + FluxSink> requestsStreams; + Flux blockRequestFlux; + Scheduler delayScheduler; private Disposable wireBlocksStreamSub; private Disposable finalizedBlockStreamSub; @@ -40,29 +49,37 @@ public class SyncManager { int maxConcurrentBlockRequests = 32; public SyncManager(MutableBeaconChain chain, + Publisher> newBlocks, BeaconChainStorage storage, BeaconChainSpec spec, WireApiSync syncApi, - SyncQueue syncQueue, int maxConcurrentBlockRequests) { - this( - chain, - chain.getBlockStatesStream(), - storage, - spec, - syncApi, - syncQueue, - maxConcurrentBlockRequests); - } - - public SyncManager(MutableBeaconChain chain, - Publisher blockStatesStream, - BeaconChainStorage storage, BeaconChainSpec spec, WireApiSync syncApi, - SyncQueue syncQueue, int maxConcurrentBlockRequests) { + SyncQueue syncQueue, int maxConcurrentBlockRequests, + Scheduler delayScheduler) { this.chain = chain; - this.blockStatesStream = blockStatesStream; + this.blockStatesStream = chain.getBlockStatesStream(); + this.newBlocks = newBlocks; this.storage = storage; this.spec = spec; this.syncApi = syncApi; this.syncQueue = syncQueue; this.maxConcurrentBlockRequests = maxConcurrentBlockRequests; + this.delayScheduler = delayScheduler; + + ModeDetector modeDetector = new ModeDetector( + Flux.from(chain.getBlockStatesStream()).map(BeaconTuple::getBlock), + Flux.from(newBlocks).map(Feedback::get)); + blockRequestFlux = Flux.from(modeDetector.getSyncModeStream()) + .doOnNext(mode -> logger.info("Switch sync to mode " + mode)) + .switchMap( + mode -> { + switch (mode) { + case Long: + return syncQueue.getBlockRequestsStream(); + case Short: + return Flux.from(syncQueue.getBlockRequestsStream()) + .delayElements(Duration.ofSeconds(1), delayScheduler); + default: + throw new IllegalStateException(); + } + }); } public void start() { @@ -90,8 +107,7 @@ public void start() { } }); - Flux>> wireBlocksStream = Flux - .from(syncQueue.getBlockRequestsStream()) + Flux>> wireBlocksStream = blockRequestFlux .map(req -> new BlockHeadersRequestMessage( req.getStartRoot().orElse(BlockHeadersRequestMessage.NULL_START_ROOT), req.getStartSlot().orElse(BlockHeadersRequestMessage.NULL_START_SLOT), @@ -101,18 +117,12 @@ public void start() { maxConcurrentBlockRequests) .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o, t)); - wireBlocksStreamSub = syncQueue.subscribeToNewBlocks(wireBlocksStream); - if (newBlocks != null) { - syncQueue.subscribeToNewBlocks(Flux.from(newBlocks) - .map(blockF -> blockF.map(Collections::singletonList))); - - Publisher freshBlocks = - RxUtil.join( - Flux.from(newBlocks).map(Feedback::get), - Flux.from(blockStatesStream).map(BeaconTuple::getBlock), - 16); + wireBlocksStream = wireBlocksStream.mergeWith( + Flux.from(newBlocks).map(blockF -> blockF.map(Collections::singletonList))); } + + wireBlocksStreamSub = syncQueue.subscribeToNewBlocks(wireBlocksStream); } public void stop() { @@ -120,4 +130,42 @@ public void stop() { finalizedBlockStreamSub.dispose(); readyBlocksStreamSub.dispose(); } + + enum SyncMode { + Long, + Short + } + + class ModeDetector { + Publisher syncModeStream; + + public ModeDetector( + Publisher importedBlocks, + Publisher onlineBlocks) { + + syncModeStream = Flux.combineLatest( + Flux.from(importedBlocks). + scan(new ArrayList<>(), (arr, block) -> listAddLimited(arr, block, 8)), + Flux.from(onlineBlocks). + scan(new ArrayList<>(), (arr, block) -> listAddLimited(arr, block, 8)), + (latestImported, latestOnline) -> { + HashSet s1 = new HashSet<>(latestImported); + HashSet s2 = new HashSet<>(latestOnline); + s1.retainAll(s2); + return s1.isEmpty() ? SyncMode.Long : SyncMode.Short; + }); + } + + private ArrayList listAddLimited(ArrayList list, A elem, int maxSize) { + list.add(elem); + if (list.size() > maxSize) { + list.remove(0); + } + return list; + } + + public Publisher getSyncModeStream() { + return syncModeStream; + } + } } diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index 0eba000fa..32824af85 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -28,7 +28,9 @@ import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; +import reactor.core.publisher.ReplayProcessor; import reactor.core.scheduler.Schedulers; import tech.pegasys.artemis.util.bytes.BytesValue; import tech.pegasys.artemis.util.uint.UInt64; @@ -43,6 +45,27 @@ public void test0() { System.out.println(i); } + @Test + public void test01() { + ReplayProcessor proc = ReplayProcessor.create(); + FluxSink sink = proc.sink(); + + sink.next(1); + sink.next(2); + + Flux.from(proc) + .onErrorContinue((t, o) -> System.out.println("Continue on error: " + t)) + .subscribe( + i -> System.out.println("Next: " + i) + ,t -> System.out.println("Err: " + t) + ,() -> System.out.println("Complete") + ); + + sink.error(new RuntimeException("Test")); + sink.next(3); + sink.complete(); + } + @Test public void test1() throws Exception { int slotCount = 1; @@ -77,7 +100,15 @@ public void test1() throws Exception { peer0.getBeaconChain().getBlockStatesStream()); Flux.from(peerManager.connectedPeerStream()) - .subscribe(peer -> System.out.println("Remote peer connected: " + peer)); + .subscribe( + peer -> { + System.out.println("Remote peer connected: " + peer); + Flux.from(peer.getRawChannel().inboundMessageStream()) + .doOnError(e -> System.out.println("#### Error: " + e)) + .doOnComplete(() -> System.out.println("#### Complete")) + .doOnNext(msg -> System.out.println("#### on message")) + .subscribe(); + }); Flux.from(peerManager.activePeerStream()) .subscribe(peer -> System.out.println("Remote peer active: " + peer)); Flux.from(peerManager.disconnectedPeerStream()) @@ -101,26 +132,26 @@ public void test1() throws Exception { messageSerializer, null, peer1.getBeaconChain().getBlockStatesStream()); - Flux.from(peer1.getBeaconChain().getBlockStatesStream()) - .subscribe(s -> System.out.println("### " + s)); Flux.from(peerManager.connectedPeerStream()) .subscribe(peer -> System.out.println("Peer 1 connected: " + peer)); Flux.from(peerManager.activePeerStream()) - .subscribe(peer -> System.out.println("Remote peer active: " + peer)); + .subscribe(peer -> System.out.println("Peer 1 active: " + peer)); Flux.from(peerManager.disconnectedPeerStream()) - .subscribe(peer -> System.out.println("Remote peer disconnected: " + peer)); + .subscribe(peer -> System.out.println("Peer 1 disconnected: " + peer)); BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); SyncManager syncManager = new SyncManager( peer1.getBeaconChain(), + Flux.from(peerManager.getWireApiSub().inboundBlocksStream()).map(Feedback::of), peer1.getBeaconChainStorage(), peer1.getSpec(), peerManager.getWireApiSync(), syncQueue, - 1); + 1, + peer1.getSchedulers().reactorEvents()); AtomicBoolean synced = new AtomicBoolean(); Flux.from(peer1.getBeaconChain().getBlockStatesStream()) diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java index 302588208..6f6e2600c 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -79,11 +79,13 @@ public void test1() throws Exception { SyncManager syncManager = new SyncManager( testPeer.getBeaconChain(), + Flux.never(), testPeer.getBeaconChainStorage(), testPeer.getSpec(), asyncSyncServer, syncQueue, - 1); + 1, + testPeer.getSchedulers().reactorEvents()); AtomicBoolean synced = new AtomicBoolean(); Flux.from(testPeer.getBeaconChain().getBlockStatesStream()) From 7ed10a749a1c546e265303ce97e63f5f2efcd6d8 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 13 May 2019 18:01:43 +0300 Subject: [PATCH 27/96] Move MDCControlledSchedulers to a separate class. Remove unnecessary manual Logger init --- .../beacon/simulator/BenchmarkLauncher.java | 46 +-------------- .../beacon/simulator/SimulatorLauncher.java | 56 +------------------ .../util/MDCControlledSchedulers.java | 49 ++++++++++++++++ 3 files changed, 51 insertions(+), 100 deletions(-) create mode 100644 start/simulator/src/main/java/org/ethereum/beacon/simulator/util/MDCControlledSchedulers.java diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java index 1d6b914f9..a3a40126e 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java @@ -50,6 +50,7 @@ import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.schedulers.TimeController; import org.ethereum.beacon.schedulers.TimeControllerImpl; +import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; import org.ethereum.beacon.simulator.util.SimulateUtils; import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; @@ -101,13 +102,6 @@ public BenchmarkLauncher( } private void setupLogging() { - try (InputStream inputStream = ClassLoader.class.getResourceAsStream("/log4j2.xml")) { - ConfigurationSource source = new ConfigurationSource(inputStream); - Configurator.initialize(null, source); - } catch (Exception e) { - throw new RuntimeException("Cannot read log4j default configuration", e); - } - // set logLevel if (logLevel != null) { LoggerContext context = @@ -407,44 +401,6 @@ public Optional getLatestEth1Data() { public void setDistanceFromHead(long distanceFromHead) {} } - public static class MDCControlledSchedulers { - private DateFormat localTimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); - - private TimeController timeController = new TimeControllerImpl(); - - public ControlledSchedulers createNew(String validatorId) { - return createNew(validatorId, 0); - } - - public ControlledSchedulers createNew(String validatorId, long timeShift) { - ControlledSchedulers[] newSched = new ControlledSchedulers[1]; - LoggerMDCExecutor mdcExecutor = new LoggerMDCExecutor() - .add("validatorTime", () -> localTimeFormat.format(new Date(newSched[0].getCurrentTime()))) - .add("validatorIndex", () -> "" + validatorId); - newSched[0] = Schedulers.createControlled(() -> mdcExecutor); - newSched[0].getTimeController().setParent(timeController); - newSched[0].getTimeController().setTimeShift(timeShift); - - return newSched[0]; - } - - public void setCurrentTime(long time) { - timeController.setTime(time); - } - - void addTime(Duration duration) { - addTime(duration.toMillis()); - } - - void addTime(long millis) { - setCurrentTime(timeController.getTime() + millis); - } - - public long getCurrentTime() { - return timeController.getTime(); - } - } - public static class Builder { private MainConfig config; private Level logLevel = Level.INFO; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java index fe61bad8b..1c6dbf22f 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java @@ -1,13 +1,9 @@ package org.ethereum.beacon.simulator; import java.io.File; -import java.io.InputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -20,8 +16,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.ConfigurationSource; -import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.LoggerConfig; import org.ethereum.beacon.Launcher; import org.ethereum.beacon.chain.observer.ObservableBeaconState; @@ -47,10 +41,7 @@ import org.ethereum.beacon.emulator.config.simulator.PeersConfig; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; -import org.ethereum.beacon.schedulers.LoggerMDCExecutor; -import org.ethereum.beacon.schedulers.Schedulers; -import org.ethereum.beacon.schedulers.TimeController; -import org.ethereum.beacon.schedulers.TimeControllerImpl; +import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; import org.ethereum.beacon.simulator.util.SimulateUtils; import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; @@ -115,13 +106,6 @@ public SimulatorLauncher( } private void setupLogging() { - try (InputStream inputStream = ClassLoader.class.getResourceAsStream("/log4j2.xml")) { - ConfigurationSource source = new ConfigurationSource(inputStream); - Configurator.initialize(null, source); - } catch (Exception e) { - throw new RuntimeException("Cannot read log4j default configuration", e); - } - // set logLevel if (logLevel != null) { LoggerContext context = @@ -441,44 +425,6 @@ public Optional getLatestEth1Data() { public void setDistanceFromHead(long distanceFromHead) {} } - public static class MDCControlledSchedulers { - private DateFormat localTimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); - - private TimeController timeController = new TimeControllerImpl(); - - public ControlledSchedulers createNew(String validatorId) { - return createNew(validatorId, 0); - } - - public ControlledSchedulers createNew(String validatorId, long timeShift) { - ControlledSchedulers[] newSched = new ControlledSchedulers[1]; - LoggerMDCExecutor mdcExecutor = new LoggerMDCExecutor() - .add("validatorTime", () -> localTimeFormat.format(new Date(newSched[0].getCurrentTime()))) - .add("validatorIndex", () -> "" + validatorId); - newSched[0] = Schedulers.createControlled(() -> mdcExecutor); - newSched[0].getTimeController().setParent(timeController); - newSched[0].getTimeController().setTimeShift(timeShift); - - return newSched[0]; - } - - public void setCurrentTime(long time) { - timeController.setTime(time); - } - - public void addTime(Duration duration) { - addTime(duration.toMillis()); - } - - public void addTime(long millis) { - setCurrentTime(timeController.getTime() + millis); - } - - public long getCurrentTime() { - return timeController.getTime(); - } - } - public static class Builder { private MainConfig config; private Level logLevel = Level.INFO; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/MDCControlledSchedulers.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/MDCControlledSchedulers.java new file mode 100644 index 000000000..95bb32d92 --- /dev/null +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/MDCControlledSchedulers.java @@ -0,0 +1,49 @@ +package org.ethereum.beacon.simulator.util; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.util.Date; +import org.ethereum.beacon.schedulers.ControlledSchedulers; +import org.ethereum.beacon.schedulers.LoggerMDCExecutor; +import org.ethereum.beacon.schedulers.Schedulers; +import org.ethereum.beacon.schedulers.TimeController; +import org.ethereum.beacon.schedulers.TimeControllerImpl; + +public class MDCControlledSchedulers { + private DateFormat localTimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); + + private TimeController timeController = new TimeControllerImpl(); + + public ControlledSchedulers createNew(String validatorId) { + return createNew(validatorId, 0); + } + + public ControlledSchedulers createNew(String validatorId, long timeShift) { + ControlledSchedulers[] newSched = new ControlledSchedulers[1]; + LoggerMDCExecutor mdcExecutor = new LoggerMDCExecutor() + .add("validatorTime", () -> localTimeFormat.format(new Date(newSched[0].getCurrentTime()))) + .add("validatorIndex", () -> "" + validatorId); + newSched[0] = Schedulers.createControlled(() -> mdcExecutor); + newSched[0].getTimeController().setParent(timeController); + newSched[0].getTimeController().setTimeShift(timeShift); + + return newSched[0]; + } + + public void setCurrentTime(long time) { + timeController.setTime(time); + } + + public void addTime(Duration duration) { + addTime(duration.toMillis()); + } + + public void addTime(long millis) { + setCurrentTime(timeController.getTime() + millis); + } + + public long getCurrentTime() { + return timeController.getTime(); + } +} From 062fa135804d3585c8bff45e450e7f499d3b43db Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 15 May 2019 14:12:45 +0300 Subject: [PATCH 28/96] Add NodeLauncher and NodeTest where one peer syncs to another by TCP. Add RPC disconnect and timeouts handling --- start/common/build.gradle | 2 + .../org/ethereum/beacon/NodeLauncher.java | 311 ++++++++++++++++++ .../beacon/simulator/BenchmarkLauncher.java | 51 +-- .../beacon/simulator/SimulatorLauncher.java | 41 +-- .../simulator/util/SimpleDepositContract.java | 48 +++ .../ethereum/beacon/schedulers/Scheduler.java | 8 + .../org/ethereum/beacon/wire/PeerImpl.java | 8 +- .../beacon/wire/SimplePeerManagerImpl.java | 6 +- .../wire/channel/RpcChannelAdapter.java | 57 +++- .../wire/channel/beacon/BeaconPipeline.java | 30 +- .../exceptions/WireRpcClosedException.java | 12 + .../wire/exceptions/WireRpcException.java | 12 + .../exceptions/WireRpcTimeoutException.java | 12 + .../beacon/wire/net/ConnectionManager.java | 12 +- .../wire/net/NettyChannelInitializer.java | 2 +- .../beacon/wire/sync/SyncManager.java | 163 +-------- .../beacon/wire/sync/SyncManagerImpl.java | 187 +++++++++++ .../beacon/wire/sync/SyncQueueImpl.java | 1 + .../org/ethereum/beacon/wire/NodeTest.java | 188 +++++++++++ .../org/ethereum/beacon/wire/PeersTest.java | 12 +- .../channel/BeaconPipelineChannelTest.java | 191 +++++++++-- .../ethereum/beacon/wire/sync/SyncTest.java | 6 +- wire/src/test/resources/log4j2.xml | 8 +- wire/src/test/resources/test-spec-config.yml | 23 ++ 24 files changed, 1070 insertions(+), 321 deletions(-) create mode 100644 start/common/src/main/java/org/ethereum/beacon/NodeLauncher.java create mode 100644 start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimpleDepositContract.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java create mode 100644 wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java create mode 100644 wire/src/test/resources/test-spec-config.yml diff --git a/start/common/build.gradle b/start/common/build.gradle index 3abb82135..e83a85628 100644 --- a/start/common/build.gradle +++ b/start/common/build.gradle @@ -6,6 +6,7 @@ dependencies { implementation project(':db:core') implementation project(':chain') implementation project(':pow:core') + implementation project(':ssz') implementation project(':validator') implementation project(':wire') implementation project(':util') @@ -22,4 +23,5 @@ dependencies { testImplementation project(':consensus').sourceSets.test.output testImplementation project(':chain').sourceSets.test.output testImplementation project(':pow:core').sourceSets.test.output + testImplementation project(':start:simulator') } diff --git a/start/common/src/main/java/org/ethereum/beacon/NodeLauncher.java b/start/common/src/main/java/org/ethereum/beacon/NodeLauncher.java new file mode 100644 index 000000000..4967d25b5 --- /dev/null +++ b/start/common/src/main/java/org/ethereum/beacon/NodeLauncher.java @@ -0,0 +1,311 @@ +package org.ethereum.beacon; + +import java.net.SocketAddress; +import java.util.List; +import org.ethereum.beacon.chain.DefaultBeaconChain; +import org.ethereum.beacon.chain.MutableBeaconChain; +import org.ethereum.beacon.chain.ProposedBlockProcessor; +import org.ethereum.beacon.chain.ProposedBlockProcessorImpl; +import org.ethereum.beacon.chain.SlotTicker; +import org.ethereum.beacon.chain.observer.ObservableStateProcessor; +import org.ethereum.beacon.chain.observer.ObservableStateProcessorImpl; +import org.ethereum.beacon.chain.storage.BeaconChainStorage; +import org.ethereum.beacon.chain.storage.BeaconChainStorageFactory; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.consensus.transition.EmptySlotTransition; +import org.ethereum.beacon.consensus.transition.ExtendedSlotTransition; +import org.ethereum.beacon.consensus.transition.InitialStateTransition; +import org.ethereum.beacon.consensus.transition.PerBlockTransition; +import org.ethereum.beacon.consensus.transition.PerEpochTransition; +import org.ethereum.beacon.consensus.transition.PerSlotTransition; +import org.ethereum.beacon.consensus.transition.StateCachingTransition; +import org.ethereum.beacon.consensus.verifier.BeaconBlockVerifier; +import org.ethereum.beacon.consensus.verifier.BeaconStateVerifier; +import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.db.InMemoryDatabase; +import org.ethereum.beacon.pow.DepositContract; +import org.ethereum.beacon.pow.DepositContract.ChainStart; +import org.ethereum.beacon.schedulers.Schedulers; +import org.ethereum.beacon.ssz.SSZBuilder; +import org.ethereum.beacon.ssz.SSZSerializer; +import org.ethereum.beacon.validator.BeaconChainProposer; +import org.ethereum.beacon.validator.MultiValidatorService; +import org.ethereum.beacon.validator.attester.BeaconChainAttesterImpl; +import org.ethereum.beacon.validator.crypto.BLS381Credentials; +import org.ethereum.beacon.validator.proposer.BeaconChainProposerImpl; +import org.ethereum.beacon.wire.Feedback; +import org.ethereum.beacon.wire.MessageSerializer; +import org.ethereum.beacon.wire.SimplePeerManagerImpl; +import org.ethereum.beacon.wire.WireApiSub; +import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.WireApiSyncServer; +import org.ethereum.beacon.wire.message.SSZMessageSerializer; +import org.ethereum.beacon.wire.net.ConnectionManager; +import org.ethereum.beacon.wire.sync.BeaconBlockTree; +import org.ethereum.beacon.wire.sync.SyncManagerImpl; +import org.ethereum.beacon.wire.sync.SyncQueue; +import org.ethereum.beacon.wire.sync.SyncQueueImpl; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import tech.pegasys.artemis.util.uint.UInt64; + +public class NodeLauncher { + private final BeaconChainSpec spec; + private final DepositContract depositContract; + private final List validatorCred; + private final BeaconChainStorageFactory storageFactory; + private final Schedulers schedulers; + + private InitialStateTransition initialTransition; + private StateCachingTransition stateCachingTransition; + private PerSlotTransition perSlotTransition; + private PerBlockTransition perBlockTransition; + private PerEpochTransition perEpochTransition; + private ExtendedSlotTransition extendedSlotTransition; + private EmptySlotTransition emptySlotTransition; + private BeaconBlockVerifier blockVerifier; + private BeaconStateVerifier stateVerifier; + + private InMemoryDatabase db; + private BeaconChainStorage beaconChainStorage; + private MutableBeaconChain beaconChain; + private SlotTicker slotTicker; + private ObservableStateProcessor observableStateProcessor; + private BeaconChainProposer beaconChainProposer; + private BeaconChainAttesterImpl beaconChainAttester; + private MultiValidatorService beaconChainValidator; + + private byte networkId = 1; + private UInt64 chainId = UInt64.valueOf(1); + private boolean startSyncManager = false; + + private WireApiSub wireApiSub; + private WireApiSync wireApiSyncRemote; + private final ConnectionManager connectionManager; + private SimplePeerManagerImpl peerManager; + private BeaconBlockTree blockTree; + private SyncQueue syncQueue; + private SyncManagerImpl syncManager; + private WireApiSyncServer syncServer; + + public NodeLauncher( + BeaconChainSpec spec, + DepositContract depositContract, + List validatorCred, + ConnectionManager connectionManager, + BeaconChainStorageFactory storageFactory, + Schedulers schedulers, + boolean startSyncManager) { + + this.spec = spec; + this.depositContract = depositContract; + this.validatorCred = validatorCred; + this.connectionManager = connectionManager; + this.storageFactory = storageFactory; + this.schedulers = schedulers; + this.startSyncManager = startSyncManager; + + if (depositContract != null) { + Mono.from(depositContract.getChainStartMono()).subscribe(this::chainStarted); + } + } + + void chainStarted(ChainStart chainStartEvent) { + initialTransition = new InitialStateTransition(chainStartEvent, spec); + stateCachingTransition = new StateCachingTransition(spec); + perSlotTransition = new PerSlotTransition(spec); + perBlockTransition = new PerBlockTransition(spec); + perEpochTransition = new PerEpochTransition(spec); + extendedSlotTransition = + new ExtendedSlotTransition( + stateCachingTransition, perEpochTransition, perSlotTransition, spec); + emptySlotTransition = new EmptySlotTransition(extendedSlotTransition); + + db = new InMemoryDatabase(); + beaconChainStorage = storageFactory.create(db); + + blockVerifier = BeaconBlockVerifier.createDefault(spec); + stateVerifier = BeaconStateVerifier.createDefault(spec); + + beaconChain = + new DefaultBeaconChain( + spec, + initialTransition, + emptySlotTransition, + perBlockTransition, + blockVerifier, + stateVerifier, + beaconChainStorage, + schedulers); + beaconChain.init(); + + slotTicker = + new SlotTicker(spec, beaconChain.getRecentlyProcessed().getState(), schedulers); + slotTicker.start(); + + DirectProcessor allAttestations = DirectProcessor.create(); + + observableStateProcessor = new ObservableStateProcessorImpl( + beaconChainStorage, + slotTicker.getTickerStream(), + allAttestations, + beaconChain.getBlockStatesStream(), + spec, + emptySlotTransition, + schedulers); + observableStateProcessor.start(); + + SSZSerializer ssz = new SSZBuilder().buildSerializer(); + MessageSerializer messageSerializer = new SSZMessageSerializer(ssz); + syncServer = new WireApiSyncServer(beaconChainStorage); + + peerManager = new SimplePeerManagerImpl( + networkId, + chainId, + connectionManager.channelsStream(), + ssz, + spec, + messageSerializer, + schedulers, + syncServer, + beaconChain.getBlockStatesStream()); + + wireApiSub = peerManager.getWireApiSub(); + wireApiSyncRemote = peerManager.getWireApiSync(); + + blockTree = new BeaconBlockTree(spec.getObjectHasher()); + syncQueue = new SyncQueueImpl(blockTree, 4, 16); + + syncManager = new SyncManagerImpl( + beaconChain, + Flux.from(wireApiSub.inboundBlocksStream()).map(Feedback::of), + beaconChainStorage, + spec, + wireApiSyncRemote, + syncQueue, + 1, + schedulers.reactorEvents()); + + if (startSyncManager) { + syncManager.start(); + } + + if (validatorCred != null) { + beaconChainProposer = new BeaconChainProposerImpl(spec, perBlockTransition, depositContract); + beaconChainAttester = new BeaconChainAttesterImpl(spec); + + beaconChainValidator = new MultiValidatorService( + validatorCred, + beaconChainProposer, + beaconChainAttester, + spec, + observableStateProcessor.getObservableStateStream(), + schedulers); + beaconChainValidator.start(); + + ProposedBlockProcessor proposedBlocksProcessor = new ProposedBlockProcessorImpl( + beaconChain, schedulers); + Flux.from(beaconChainValidator.getProposedBlocksStream()) + .subscribe(proposedBlocksProcessor::newBlockProposed); + Flux.from(proposedBlocksProcessor.processedBlocksStream()) + .subscribe(wireApiSub::sendProposedBlock); + + Flux.from(beaconChainValidator.getAttestationsStream()).subscribe(wireApiSub::sendAttestation); + Flux.from(beaconChainValidator.getAttestationsStream()).subscribe(allAttestations); + } + + Flux.from(wireApiSub.inboundAttestationsStream()) + .publishOn(schedulers.reactorEvents()) + .subscribe(allAttestations); + +// Flux.from(wireApiSub.inboundBlocksStream()) +// .publishOn(schedulers.reactorEvents()) +// .subscribe(beaconChain::insert); + } + + + public BeaconChainSpec getSpec() { + return spec; + } + + public DepositContract getDepositContract() { + return depositContract; + } + + public List getValidatorCred() { + return validatorCred; + } + + public WireApiSub getWireApiSub() { + return wireApiSub; + } + + public InitialStateTransition getInitialTransition() { + return initialTransition; + } + + public PerSlotTransition getPerSlotTransition() { + return perSlotTransition; + } + + public PerBlockTransition getPerBlockTransition() { + return perBlockTransition; + } + + public PerEpochTransition getPerEpochTransition() { + return perEpochTransition; + } + + public ExtendedSlotTransition getExtendedSlotTransition() { + return extendedSlotTransition; + } + + public BeaconBlockVerifier getBlockVerifier() { + return blockVerifier; + } + + public BeaconStateVerifier getStateVerifier() { + return stateVerifier; + } + + public InMemoryDatabase getDb() { + return db; + } + + public BeaconChainStorage getBeaconChainStorage() { + return beaconChainStorage; + } + + public MutableBeaconChain getBeaconChain() { + return beaconChain; + } + + public SlotTicker getSlotTicker() { + return slotTicker; + } + + public ObservableStateProcessor getObservableStateProcessor() { + return observableStateProcessor; + } + + public BeaconChainProposer getBeaconChainProposer() { + return beaconChainProposer; + } + + public BeaconChainAttesterImpl getBeaconChainAttester() { + return beaconChainAttester; + } + + public MultiValidatorService getValidatorService() { + return beaconChainValidator; + } + + public BeaconChainStorageFactory getStorageFactory() { + return storageFactory; + } + + public Schedulers getSchedulers() { + return schedulers; + } +} diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java index a3a40126e..9e72f255c 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java @@ -1,18 +1,13 @@ package org.ethereum.beacon.simulator; import java.io.File; -import java.io.InputStream; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Random; import java.util.stream.Collectors; import org.apache.logging.log4j.Level; @@ -20,8 +15,6 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.ConfigurationSource; -import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.LoggerConfig; import org.ethereum.beacon.Launcher; import org.ethereum.beacon.chain.observer.ObservableBeaconState; @@ -46,20 +39,15 @@ import org.ethereum.beacon.emulator.config.simulator.PeersConfig; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; -import org.ethereum.beacon.schedulers.LoggerMDCExecutor; -import org.ethereum.beacon.schedulers.Schedulers; -import org.ethereum.beacon.schedulers.TimeController; -import org.ethereum.beacon.schedulers.TimeControllerImpl; import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; +import org.ethereum.beacon.simulator.util.SimpleDepositContract; import org.ethereum.beacon.simulator.util.SimulateUtils; import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; import org.ethereum.beacon.wire.WireApiSub; import org.javatuples.Pair; -import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.bytes.Bytes32; @@ -364,43 +352,6 @@ private static String getValidators(String info, Map records) + "]"; } - private static class SimpleDepositContract implements DepositContract { - private final ChainStart chainStart; - - public SimpleDepositContract(ChainStart chainStart) { - this.chainStart = chainStart; - } - - @Override - public Publisher getChainStartMono() { - return Mono.just(chainStart); - } - - @Override - public Publisher getDepositStream() { - return Mono.empty(); - } - - @Override - public List peekDeposits( - int maxCount, Eth1Data fromDepositExclusive, Eth1Data tillDepositInclusive) { - return Collections.emptyList(); - } - - @Override - public boolean hasDepositRoot(Hash32 blockHash, Hash32 depositRoot) { - return true; - } - - @Override - public Optional getLatestEth1Data() { - return Optional.of(chainStart.getEth1Data()); - } - - @Override - public void setDistanceFromHead(long distanceFromHead) {} - } - public static class Builder { private MainConfig config; private Level logLevel = Level.INFO; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java index 1c6dbf22f..bb3645efb 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Random; import java.util.stream.Collectors; import org.apache.logging.log4j.Level; @@ -42,15 +41,14 @@ import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; +import org.ethereum.beacon.simulator.util.SimpleDepositContract; import org.ethereum.beacon.simulator.util.SimulateUtils; import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; import org.ethereum.beacon.wire.WireApiSub; import org.javatuples.Pair; -import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.bytes.Bytes32; @@ -388,43 +386,6 @@ private static String getValidators(String info, Map records) + "]"; } - private static class SimpleDepositContract implements DepositContract { - private final ChainStart chainStart; - - public SimpleDepositContract(ChainStart chainStart) { - this.chainStart = chainStart; - } - - @Override - public Publisher getChainStartMono() { - return Mono.just(chainStart); - } - - @Override - public Publisher getDepositStream() { - return Mono.empty(); - } - - @Override - public List peekDeposits( - int maxCount, Eth1Data fromDepositExclusive, Eth1Data tillDepositInclusive) { - return Collections.emptyList(); - } - - @Override - public boolean hasDepositRoot(Hash32 blockHash, Hash32 depositRoot) { - return true; - } - - @Override - public Optional getLatestEth1Data() { - return Optional.of(chainStart.getEth1Data()); - } - - @Override - public void setDistanceFromHead(long distanceFromHead) {} - } - public static class Builder { private MainConfig config; private Level logLevel = Level.INFO; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimpleDepositContract.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimpleDepositContract.java new file mode 100644 index 000000000..4bae1b4a9 --- /dev/null +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimpleDepositContract.java @@ -0,0 +1,48 @@ +package org.ethereum.beacon.simulator.util; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.ethereum.beacon.core.operations.Deposit; +import org.ethereum.beacon.core.state.Eth1Data; +import org.ethereum.beacon.pow.DepositContract; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; +import tech.pegasys.artemis.ethereum.core.Hash32; + +public class SimpleDepositContract implements DepositContract { + private final ChainStart chainStart; + + public SimpleDepositContract(ChainStart chainStart) { + this.chainStart = chainStart; + } + + @Override + public Publisher getChainStartMono() { + return Mono.just(chainStart); + } + + @Override + public Publisher getDepositStream() { + return Mono.empty(); + } + + @Override + public List peekDeposits( + int maxCount, Eth1Data fromDepositExclusive, Eth1Data tillDepositInclusive) { + return Collections.emptyList(); + } + + @Override + public boolean hasDepositRoot(Hash32 blockHash, Hash32 depositRoot) { + return true; + } + + @Override + public Optional getLatestEth1Data() { + return Optional.of(chainStart.getEth1Data()); + } + + @Override + public void setDistanceFromHead(long distanceFromHead) {} +} diff --git a/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java b/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java index e3d3c373f..4e6519647 100644 --- a/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java +++ b/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java @@ -3,6 +3,7 @@ import java.time.Duration; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; /** * Analog for standard ScheduledExecutorService @@ -22,4 +23,11 @@ default CompletableFuture execute(RunnableEx task) { default CompletableFuture executeWithDelay(Duration delay, RunnableEx task) { return executeWithDelay(delay, () -> {task.run(); return null;}); } + + default CompletableFuture orTimeout(CompletableFuture future, Duration futureTimeout, Supplier exceptionSupplier) { + return (CompletableFuture) CompletableFuture.anyOf( + future, + executeWithDelay(futureTimeout, + () -> {throw exceptionSupplier.get();})); + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java index dc537b14b..966569496 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/PeerImpl.java @@ -2,6 +2,7 @@ import java.util.concurrent.CompletableFuture; import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.channel.beacon.BeaconPipeline; @@ -19,6 +20,7 @@ public class PeerImpl implements Peer { private final CompletableFuture peerActiveFut = new CompletableFuture<>(); private final BeaconPipeline beaconPipeline; private final WireApiSubAdapter wireApiSubAdapter = new WireApiSubAdapter(); + private final Schedulers schedulers; private GoodbyeMessage remoteGoodbye; private GoodbyeMessage localGoodbye; @@ -28,10 +30,12 @@ public PeerImpl( HelloMessage helloMessage, SSZSerializer ssz, MessageSerializer messageSerializer, - WireApiSync syncServer) { + WireApiSync syncServer, + Schedulers schedulers) { this.channel = channel; this.localHelloMessage = helloMessage; + this.schedulers = schedulers; beaconPipeline = new BeaconPipeline(ssz, new WireApiPeer() { public void hello(HelloMessage message) { @@ -40,7 +44,7 @@ public void hello(HelloMessage message) { public void goodbye(GoodbyeMessage message) { onGoodbye(message); } - }, wireApiSubAdapter, syncServer); + }, wireApiSubAdapter, syncServer, schedulers); beaconPipeline.initFromBytesChannel(channel, messageSerializer); wireApiSubAdapter.setSubClient(beaconPipeline.getSubClient()); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java index 3fdde38de..936451b0d 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java @@ -9,6 +9,7 @@ import org.apache.logging.log4j.Logger; import org.ethereum.beacon.chain.BeaconTupleDetails; import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.message.payload.HelloMessage; @@ -29,6 +30,7 @@ public class SimplePeerManagerImpl implements PeerManager { private final SSZSerializer ssz; private final BeaconChainSpec spec; private final MessageSerializer messageSerializer; + private final Schedulers schedulers; private final WireApiSync syncServer; private final Publisher headStream; private final WireApiSyncRouter wireApiSyncRouter; @@ -45,6 +47,7 @@ public SimplePeerManagerImpl( SSZSerializer ssz, BeaconChainSpec spec, MessageSerializer messageSerializer, + Schedulers schedulers, WireApiSync syncServer, Publisher headStream) { @@ -54,6 +57,7 @@ public SimplePeerManagerImpl( this.ssz = ssz; this.spec = spec; this.messageSerializer = messageSerializer; + this.schedulers = schedulers; this.syncServer = syncServer; this.headStream = headStream; @@ -82,7 +86,7 @@ protected HelloMessage createLocalHello() { } protected PeerImpl createPeer(Channel channel) { - return new PeerImpl(channel, createLocalHello(), ssz, messageSerializer, syncServer); + return new PeerImpl(channel, createLocalHello(), ssz, messageSerializer, syncServer, schedulers); } @Override diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java index c1c07e84e..9ff61fad0 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java @@ -1,27 +1,51 @@ package org.ethereum.beacon.wire.channel; +import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.function.Function; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.schedulers.Scheduler; import org.ethereum.beacon.wire.exceptions.WireException; +import org.ethereum.beacon.wire.exceptions.WireRpcClosedException; +import org.ethereum.beacon.wire.exceptions.WireRpcTimeoutException; import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; public class RpcChannelAdapter { + private static final Logger logger = LogManager.getLogger(RpcChannelAdapter.class); private static final Object CONTEXT_KEY_FUTURE = new Object(); + public static final Duration DEFAULT_RPC_TIMEOUT = Duration.ofSeconds(10); + private final Scheduler timeoutScheduler; + private Duration rpcCallTimeout = DEFAULT_RPC_TIMEOUT; private final RpcChannel inChannel; private final Function> serverHandler; - private final DirectProcessor> outboundStream = - DirectProcessor.create(); + private FluxSink> outboundSink; + private volatile boolean closed; public RpcChannelAdapter(RpcChannel inChannel, - Function> serverHandler) { + Function> serverHandler, + Scheduler timeoutScheduler) { this.inChannel = inChannel; this.serverHandler = serverHandler; - inChannel.subscribeToOutbound(outboundStream); - Flux.from(inChannel.inboundMessageStream()).subscribe(this::onInbound); + this.timeoutScheduler = timeoutScheduler; + inChannel.subscribeToOutbound(Flux.create(s -> outboundSink = s)); + Flux.from(inChannel.inboundMessageStream()) + .subscribe(this::onInbound, err -> logger.warn("Unexpected error", err), this::onClose); + } + + public RpcChannelAdapter withRpcCallTimeout( + Duration rpcCallTimeout) { + this.rpcCallTimeout = rpcCallTimeout; + return this; + } + + private void onClose() { + closed = true; } private void onInbound(RpcMessage msg) { @@ -61,27 +85,34 @@ private void handleInvoke(RpcMessage msg) { CompletableFuture fut = serverHandler.apply(msg.getRequest()); fut.whenComplete( (r, t) -> - outboundStream.onNext( + outboundSink.next( t != null ? msg.copyWithResponseError(t) : msg.copyWithResponse(r))) .whenComplete( (r, t) -> { if (t != null) t.printStackTrace(); }); } catch (Exception e) { - outboundStream.onNext(msg.copyWithResponseError(e)); + outboundSink.next(msg.copyWithResponseError(e)); } } public CompletableFuture invokeRemote(TRequestMessage request) { - RpcMessage requestRpcMsg = - new RpcMessage<>(request, false); CompletableFuture ret = new CompletableFuture<>(); - requestRpcMsg.setRequestContext(CONTEXT_KEY_FUTURE, ret); - outboundStream.onNext(requestRpcMsg); - return ret; + + if(closed) { + ret.completeExceptionally(new WireRpcClosedException("Channel already closed: " + inChannel)); + return ret; + } else { + RpcMessage requestRpcMsg = + new RpcMessage<>(request, false); + requestRpcMsg.setRequestContext(CONTEXT_KEY_FUTURE, ret); + outboundSink.next(requestRpcMsg); + return timeoutScheduler.orTimeout( + ret, rpcCallTimeout, () -> new WireRpcTimeoutException("RPC call timeout.")); + } } public void notifyRemote(TRequestMessage request) { - outboundStream.onNext(new RpcMessage<>(request, true)); + outboundSink.next(new RpcMessage<>(request, true)); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java index c474c843f..27ab029bc 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java @@ -1,10 +1,12 @@ package org.ethereum.beacon.wire.channel.beacon; +import java.time.Duration; import java.util.concurrent.CompletableFuture; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.MessageSerializer; @@ -43,18 +45,27 @@ public class BeaconPipeline { private final WireApiPeer peerServer; private final WireApiSubRpc subServer; private final WireApiSync syncServer; + private final Schedulers schedulers; private WireApiPeer peerClient; private WireApiSubRpc subClient; private WireApiSync syncClient; + private Duration rpsTimeout = RpcChannelAdapter.DEFAULT_RPC_TIMEOUT; private RpcChannel rpcHub; public BeaconPipeline(SSZSerializer sszSerializer, WireApiPeer peerServer, - WireApiSubRpc subServer, WireApiSync syncServer) { + WireApiSubRpc subServer, WireApiSync syncServer, + Schedulers schedulers) { this.sszSerializer = sszSerializer; this.peerServer = peerServer; this.subServer = subServer; this.syncServer = syncServer; + this.schedulers = schedulers; + } + + public BeaconPipeline setRpsTimeout(Duration rpsTimeout) { + this.rpsTimeout = rpsTimeout; + return this; } public void initFromBytesChannel(Channel rawChannel, MessageSerializer messageSerializer) { @@ -69,6 +80,8 @@ public void initFromMessageChannel(Channel messageChannel) { RpcChannelMapper rpcMessageChannel = new BeaconRpcMapper(messageChannel); + messageChannel.getCloseFuture().thenAccept(v -> System.out.println("### Closed")); + ChannelCodec< RpcMessage, RpcMessage> @@ -139,13 +152,14 @@ public WireApiSync getSyncClient() { private WireApiSync createWireApiSync(WireApiSync syncServer) { RpcChannelAdapter blockRootsAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockRootsRequestMessage.class), - syncServer != null ? syncServer::requestBlockRoots : null); + syncServer != null ? syncServer::requestBlockRoots : null, schedulers.events()); RpcChannelAdapter blockHeadersAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockHeadersRequestMessage.class), - syncServer != null ? syncServer::requestBlockHeaders : null); + syncServer != null ? syncServer::requestBlockHeaders : null, schedulers.events()); RpcChannelAdapter blockBodiesAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockBodiesRequestMessage.class), - syncServer != null ? req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get) : null); + syncServer != null ? req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get) : null, + schedulers.events()); return new WireApiSync() { @Override @@ -174,14 +188,14 @@ private WireApiSubRpc createWireApiSub(WireApiSubRpc subServer) { subServer == null ? null : newBlock -> { subServer.newBlock(newBlock.getBlock()); return null; - }); + }, schedulers.events()); RpcChannelAdapter attestations = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, NotifyNewAttestationMessage.class), subServer == null ? null : newAttest -> { subServer.newAttestation(newAttest.getAttestation()); return null; - }); + }, schedulers.events()); return new WireApiSubRpc() { @Override @@ -202,14 +216,14 @@ private WireApiPeer createWireApiPeer(WireApiPeer peerServer) { peerServer == null ? null : msg -> { peerServer.hello(msg); return null; - }); + }, schedulers.events()); RpcChannelAdapter goodbyeRpc = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, GoodbyeMessage.class), peerServer == null ? null : msg -> { peerServer.goodbye(msg); return null; - }); + }, schedulers.events()); return new WireApiPeer() { @Override diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java new file mode 100644 index 000000000..fc07ee95e --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java @@ -0,0 +1,12 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireRpcClosedException extends WireRpcException { + + public WireRpcClosedException(String message) { + super(message); + } + + public WireRpcClosedException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java new file mode 100644 index 000000000..38e7c6cde --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java @@ -0,0 +1,12 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireRpcException extends WireException { + + public WireRpcException(String message) { + super(message); + } + + public WireRpcException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java new file mode 100644 index 000000000..13ca72ed6 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java @@ -0,0 +1,12 @@ +package org.ethereum.beacon.wire.exceptions; + +public class WireRpcTimeoutException extends WireRpcException { + + public WireRpcTimeoutException(String message) { + super(message); + } + + public WireRpcTimeoutException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java index d222f769b..3a96b5550 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java @@ -35,11 +35,13 @@ public ConnectionManager(Server server, Client client, } public CompletableFuture> connect(TAddress peerAddress) { - return client.connect(peerAddress) - .thenApply(a -> { - clientConnectionsSink.next(a); - return a; - }); + return client + .connect(peerAddress) + .thenApply( + a -> { + clientConnectionsSink.next(a); + return a; + }); } public void addActivePeer(TAddress peerAddress) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java index 93a4b66f9..fba21d037 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java @@ -16,7 +16,7 @@ import org.apache.logging.log4j.Logger; class NettyChannelInitializer extends ChannelInitializer { - private static final int READ_TIMEOUT_SEC = 5; + private static final int READ_TIMEOUT_SEC = 600; private static final Logger logger = LogManager.getLogger(NettyChannelInitializer.class); private final Consumer activeChannelListener; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index 77ce95bc3..1d6c82a22 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -1,171 +1,18 @@ package org.ethereum.beacon.wire.sync; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.ethereum.beacon.chain.BeaconTuple; -import org.ethereum.beacon.chain.BeaconTupleDetails; -import org.ethereum.beacon.chain.MutableBeaconChain; -import org.ethereum.beacon.chain.storage.BeaconChainStorage; -import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.stream.RxUtil; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.WireApiSync; -import org.ethereum.beacon.wire.exceptions.WireInvalidConsensusDataException; -import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; -import org.ethereum.beacon.wire.sync.SyncQueue.BlockRequest; import org.reactivestreams.Publisher; import reactor.core.Disposable; -import reactor.core.publisher.Flux; -import reactor.core.publisher.FluxSink; -import reactor.core.publisher.Mono; -import reactor.core.scheduler.Scheduler; -import tech.pegasys.artemis.ethereum.core.Hash32; -public class SyncManager { - private static final Logger logger = LogManager.getLogger(SyncManager.class); +public interface SyncManager { - private final MutableBeaconChain chain; - private final Publisher blockStatesStream; - private final BeaconChainStorage storage; - private final BeaconChainSpec spec; + Disposable subscribeToOnlineBlocks(Publisher> onlineBlocks); - private final WireApiSync syncApi; - private Publisher> newBlocks; - private final SyncQueue syncQueue; - FluxSink> requestsStreams; - Flux blockRequestFlux; - Scheduler delayScheduler; + Disposable subscribeToFinalizedBlocks(Publisher finalBlocks); - private Disposable wireBlocksStreamSub; - private Disposable finalizedBlockStreamSub; - private Disposable readyBlocksStreamSub; + void setSyncApi(WireApiSync syncApi); - int maxConcurrentBlockRequests = 32; - - public SyncManager(MutableBeaconChain chain, - Publisher> newBlocks, - BeaconChainStorage storage, BeaconChainSpec spec, WireApiSync syncApi, - SyncQueue syncQueue, int maxConcurrentBlockRequests, - Scheduler delayScheduler) { - this.chain = chain; - this.blockStatesStream = chain.getBlockStatesStream(); - this.newBlocks = newBlocks; - this.storage = storage; - this.spec = spec; - this.syncApi = syncApi; - this.syncQueue = syncQueue; - this.maxConcurrentBlockRequests = maxConcurrentBlockRequests; - this.delayScheduler = delayScheduler; - - ModeDetector modeDetector = new ModeDetector( - Flux.from(chain.getBlockStatesStream()).map(BeaconTuple::getBlock), - Flux.from(newBlocks).map(Feedback::get)); - blockRequestFlux = Flux.from(modeDetector.getSyncModeStream()) - .doOnNext(mode -> logger.info("Switch sync to mode " + mode)) - .switchMap( - mode -> { - switch (mode) { - case Long: - return syncQueue.getBlockRequestsStream(); - case Short: - return Flux.from(syncQueue.getBlockRequestsStream()) - .delayElements(Duration.ofSeconds(1), delayScheduler); - default: - throw new IllegalStateException(); - } - }); - } - - public void start() { - - Hash32 genesisBlockRoot = - storage.getBlockStorage().getSlotBlocks(spec.getConstants().getGenesisSlot()).get(0); - - Flux finalizedBlockRootStream = Flux - .from(blockStatesStream) - .map(bs -> bs.getFinalState().getFinalizedRoot()) - .distinct() - .map(br -> Hash32.ZERO.equals(br) ? genesisBlockRoot : br); - - Flux finalizedBlockStream = finalizedBlockRootStream.map( - root -> storage.getBlockStorage().get(root).orElseThrow(() -> new IllegalStateException())); - - finalizedBlockStreamSub = syncQueue.subscribeToFinalBlocks(finalizedBlockStream); - - readyBlocksStreamSub = Flux.from(syncQueue.getBlocksStream()).subscribe(block -> { - if (!chain.insert(block.get())) { - block.feedbackError( - new WireInvalidConsensusDataException("Couldn't insert block: " + block.get())); - } else { - block.feedbackSuccess(); - } - }); - - Flux>> wireBlocksStream = blockRequestFlux - .map(req -> new BlockHeadersRequestMessage( - req.getStartRoot().orElse(BlockHeadersRequestMessage.NULL_START_ROOT), - req.getStartSlot().orElse(BlockHeadersRequestMessage.NULL_START_SLOT), - req.getMaxCount(), - req.getStep())) - .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher())), - maxConcurrentBlockRequests) - .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o, t)); - - if (newBlocks != null) { - wireBlocksStream = wireBlocksStream.mergeWith( - Flux.from(newBlocks).map(blockF -> blockF.map(Collections::singletonList))); - } - - wireBlocksStreamSub = syncQueue.subscribeToNewBlocks(wireBlocksStream); - } - - public void stop() { - wireBlocksStreamSub.dispose(); - finalizedBlockStreamSub.dispose(); - readyBlocksStreamSub.dispose(); - } - - enum SyncMode { - Long, - Short - } - - class ModeDetector { - Publisher syncModeStream; - - public ModeDetector( - Publisher importedBlocks, - Publisher onlineBlocks) { - - syncModeStream = Flux.combineLatest( - Flux.from(importedBlocks). - scan(new ArrayList<>(), (arr, block) -> listAddLimited(arr, block, 8)), - Flux.from(onlineBlocks). - scan(new ArrayList<>(), (arr, block) -> listAddLimited(arr, block, 8)), - (latestImported, latestOnline) -> { - HashSet s1 = new HashSet<>(latestImported); - HashSet s2 = new HashSet<>(latestOnline); - s1.retainAll(s2); - return s1.isEmpty() ? SyncMode.Long : SyncMode.Short; - }); - } - - private ArrayList listAddLimited(ArrayList list, A elem, int maxSize) { - list.add(elem); - if (list.size() > maxSize) { - list.remove(0); - } - return list; - } - - public Publisher getSyncModeStream() { - return syncModeStream; - } - } + Publisher> getBlocksReadyToImport(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java new file mode 100644 index 000000000..1d5e62386 --- /dev/null +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java @@ -0,0 +1,187 @@ +package org.ethereum.beacon.wire.sync; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.chain.BeaconTuple; +import org.ethereum.beacon.chain.BeaconTupleDetails; +import org.ethereum.beacon.chain.MutableBeaconChain; +import org.ethereum.beacon.chain.storage.BeaconChainStorage; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.wire.Feedback; +import org.ethereum.beacon.wire.WireApiSync; +import org.ethereum.beacon.wire.exceptions.WireInvalidConsensusDataException; +import org.ethereum.beacon.wire.message.payload.BlockHeadersRequestMessage; +import org.ethereum.beacon.wire.sync.SyncQueue.BlockRequest; +import org.reactivestreams.Publisher; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import tech.pegasys.artemis.ethereum.core.Hash32; + +public class SyncManagerImpl { + private static final Logger logger = LogManager.getLogger(SyncManagerImpl.class); + + private final Publisher blockStatesStream; + private final BeaconChainSpec spec; + + private final WireApiSync syncApi; + private Publisher> newBlocks; + private final SyncQueue syncQueue; + FluxSink> requestsStreams; + Flux blockRequestFlux; + Scheduler delayScheduler; + Flux finalizedBlockStream; + + private Disposable wireBlocksStreamSub; + private Disposable finalizedBlockStreamSub; + private Disposable readyBlocksStreamSub; + + // TODO: make this parameter dynamic depending on active peers number + int maxConcurrentBlockRequests = 2; + + public SyncManagerImpl( + MutableBeaconChain chain, + Publisher> newBlocks, + BeaconChainStorage storage, + BeaconChainSpec spec, + WireApiSync syncApi, + SyncQueue syncQueue, + int maxConcurrentBlockRequests, + Scheduler delayScheduler) { + + this.blockStatesStream = chain.getBlockStatesStream(); + this.newBlocks = newBlocks; + this.spec = spec; + this.syncApi = syncApi; + this.syncQueue = syncQueue; + this.maxConcurrentBlockRequests = maxConcurrentBlockRequests; + this.delayScheduler = delayScheduler; + + ModeDetector modeDetector = + new ModeDetector( + Flux.from(chain.getBlockStatesStream()).map(BeaconTuple::getBlock), + Flux.from(newBlocks).map(Feedback::get)); + blockRequestFlux = + Flux.from(modeDetector.getSyncModeStream()) + .doOnNext(mode -> logger.info("Switch sync to mode " + mode)) + .switchMap( + mode -> { + switch (mode) { + case Long: + return syncQueue.getBlockRequestsStream(); + case Short: + return Flux.from(syncQueue.getBlockRequestsStream()) + .delayElements(Duration.ofSeconds(1), delayScheduler); + default: + throw new IllegalStateException(); + } + }, 1); + + Hash32 genesisBlockRoot = + storage.getBlockStorage().getSlotBlocks(spec.getConstants().getGenesisSlot()).get(0); + + Flux finalizedBlockRootStream = + Flux.from(blockStatesStream) + .map(bs -> bs.getFinalState().getFinalizedRoot()) + .distinct() + .map(br -> Hash32.ZERO.equals(br) ? genesisBlockRoot : br); + + finalizedBlockStream = + finalizedBlockRootStream.map( + root -> + storage.getBlockStorage().get(root).orElseThrow(() -> new IllegalStateException())); + + readyBlocksStreamSub = + Flux.from(syncQueue.getBlocksStream()) + .subscribe( + block -> { + if (!chain.insert(block.get())) { + block.feedbackError( + new WireInvalidConsensusDataException( + "Couldn't insert block: " + block.get())); + } else { + block.feedbackSuccess(); + } + }); + } + + public Publisher> getBlocksReadyToImport() { + return syncQueue.getBlocksStream(); + } + + public void start() { + + finalizedBlockStreamSub = syncQueue.subscribeToFinalBlocks(finalizedBlockStream); + + Flux>> wireBlocksStream = blockRequestFlux + .map(req -> new BlockHeadersRequestMessage( + req.getStartRoot().orElse(BlockHeadersRequestMessage.NULL_START_ROOT), + req.getStartSlot().orElse(BlockHeadersRequestMessage.NULL_START_SLOT), + req.getMaxCount(), + req.getStep())) + .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher())), + maxConcurrentBlockRequests) + .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o, t)); + + if (newBlocks != null) { + wireBlocksStream = wireBlocksStream.mergeWith( + Flux.from(newBlocks).map(blockF -> blockF.map(Collections::singletonList))); + } + + wireBlocksStreamSub = syncQueue.subscribeToNewBlocks(wireBlocksStream); + } + + public void stop() { + wireBlocksStreamSub.dispose(); + finalizedBlockStreamSub.dispose(); + readyBlocksStreamSub.dispose(); + } + + enum SyncMode { + Long, + Short + } + + class ModeDetector { + Publisher syncModeStream; + + public ModeDetector( + Publisher importedBlocks, + Publisher onlineBlocks) { + + syncModeStream = + Flux.combineLatest( + Flux.from(importedBlocks) + .scan(new ArrayList<>(), (arr, block) -> listAddLimited(arr, block, 8)), + Flux.from(onlineBlocks) + .scan(new ArrayList<>(), (arr, block) -> listAddLimited(arr, block, 8)), + (latestImported, latestOnline) -> { + HashSet s1 = new HashSet<>(latestImported); + HashSet s2 = new HashSet<>(latestOnline); + s1.retainAll(s2); + return s1.isEmpty() ? SyncMode.Long : SyncMode.Short; + }) + .distinct(); + } + + private ArrayList listAddLimited(ArrayList list, A elem, int maxSize) { + list.add(elem); + if (list.size() > maxSize) { + list.remove(0); + } + return list; + } + + public Publisher getSyncModeStream() { + return syncModeStream; + } + } +} diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java index 3e9efc94d..f07fef6dc 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueueImpl.java @@ -54,6 +54,7 @@ protected Flux createBlockRequests() { } protected void onNewFinalBlock(BeaconBlock finalBlock) { + logger.debug(() -> "New final bock: " + finalBlock); blockTree.setTopBlock(Feedback.of(finalBlock)); this.finalBlock = finalBlock; blockRequests.onNext(createBlockRequests()); diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java new file mode 100644 index 000000000..8529671f3 --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -0,0 +1,188 @@ +package org.ethereum.beacon.wire; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.time.Duration; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import org.ethereum.beacon.NodeLauncher; +import org.ethereum.beacon.chain.storage.impl.MemBeaconChainStorageFactory; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.core.operations.Deposit; +import org.ethereum.beacon.core.state.Eth1Data; +import org.ethereum.beacon.core.types.Time; +import org.ethereum.beacon.crypto.BLS381.KeyPair; +import org.ethereum.beacon.emulator.config.ConfigBuilder; +import org.ethereum.beacon.emulator.config.chainspec.SpecBuilder; +import org.ethereum.beacon.emulator.config.chainspec.SpecData; +import org.ethereum.beacon.pow.DepositContract; +import org.ethereum.beacon.schedulers.ControlledSchedulers; +import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; +import org.ethereum.beacon.simulator.util.SimpleDepositContract; +import org.ethereum.beacon.simulator.util.SimulateUtils; +import org.ethereum.beacon.validator.crypto.BLS381Credentials; +import org.ethereum.beacon.wire.channel.Channel; +import org.ethereum.beacon.wire.net.ConnectionManager; +import org.ethereum.beacon.wire.net.NettyChannel; +import org.ethereum.beacon.wire.net.NettyClient; +import org.ethereum.beacon.wire.net.NettyServer; +import org.javatuples.Pair; +import org.junit.Test; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class NodeTest { + + @Test + public void test1() throws ExecutionException, InterruptedException { + Random rnd = new Random(); + + ConfigBuilder specConfigBuilder = + new ConfigBuilder<>(SpecData.class) + .addYamlConfigFromResources("/config/spec-constants.yml") + .addYamlConfigFromResources("/test-spec-config.yml"); + SpecData specData = specConfigBuilder.build(); + SpecBuilder specBuilder = new SpecBuilder().withSpec(specData); + BeaconChainSpec spec = specBuilder.buildSpec(); + + Pair, List> depositPairs = + SimulateUtils.getAnyDeposits(rnd, spec, 16, false); + + Time genesisTime = Time.of(60000); + + MDCControlledSchedulers controlledSchedulers = new MDCControlledSchedulers(); + controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); + + Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); + + DepositContract.ChainStart chainStart = + new DepositContract.ChainStart(genesisTime, eth1Data, depositPairs.getValue0()); + SimpleDepositContract depositContract = new SimpleDepositContract(chainStart); + + // master node with all validators + { + ControlledSchedulers schedulers = controlledSchedulers.createNew("master"); + NettyServer nettyServer = new NettyServer(40001); + nettyServer.start(); + ConnectionManager connectionManager = new ConnectionManager<>( + nettyServer, null, schedulers.reactorEvents()); + NodeLauncher masterNode = new NodeLauncher( + specBuilder.buildSpec(), + depositContract, + depositPairs + .getValue1() + .stream() + .map(BLS381Credentials::createWithDummySigner) + .collect(Collectors.toList()), + connectionManager, + new MemBeaconChainStorageFactory(spec.getObjectHasher()), + schedulers, + false); + } + + // generate some blocks + controlledSchedulers.addTime(Duration.ofSeconds(64 * 10)); + + // slave node + ConnectionManager slaveConnectionManager; + CompletableFuture> connectFut; + { + ControlledSchedulers schedulers = controlledSchedulers.createNew("slave"); + NettyClient nettyClient = new NettyClient(); + slaveConnectionManager = new ConnectionManager<>( + null, nettyClient, schedulers.reactorEvents()); + NodeLauncher slaveNode = new NodeLauncher( + specBuilder.buildSpec(), + depositContract, + null, + slaveConnectionManager, + new MemBeaconChainStorageFactory(spec.getObjectHasher()), + schedulers, + true); + connectFut = slaveConnectionManager + .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + System.out.println("Connected! " + connectFut.get()); + } + + // generate some new blocks + System.out.println("Generating online blocks"); + controlledSchedulers.addTime(Duration.ofSeconds(3 * 10)); + + // 'realtime' mode + System.out.println("Some time in 'realtime' mode..."); + for (int i = 0; i < 50; i++) { + controlledSchedulers.addTime(Duration.ofSeconds(1)); + Thread.sleep(50); + } + + // disconnecting slave + System.out.println("Disconnecting slave"); + connectFut.get().close(); + + // generate new blocks on master + System.out.println("Generate new blocks on master"); + controlledSchedulers.addTime(Duration.ofSeconds(32 * 10)); + + // connect the slave again + System.out.println("Connect the slave again"); + CompletableFuture> connectFut1 = slaveConnectionManager + .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + connectFut1.get(); + System.out.println("Slave connected"); + + System.out.println("Some time in 'realtime' mode..."); + for (int i = 0; i < 50000; i++) { + controlledSchedulers.addTime(Duration.ofSeconds(1)); + Thread.sleep(50); + } + Thread.sleep(10000000); + } + + public static Integer getValue() { + System.out.println("I am called"); + // Simulating a long network call of 1 second in the worst case + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return 10; + } + + @Test + public void main() { + ScheduledExecutorService schedulerService = Executors + .newSingleThreadScheduledExecutor(); + ExecutorService executor = new ThreadPoolExecutor(10, 10, + 0L, TimeUnit.MILLISECONDS, + // This is an unbounded Queue. This should never be used + // in real life. That is the first step to failure. + new LinkedBlockingQueue()); + // We want to call the dummy service 10 times + CompletableFuture[] allFutures = new CompletableFuture[10]; + for (int i = 0; i < 10; ++i) { + CompletableFuture dependencyFuture = CompletableFuture + .supplyAsync(() -> getValue(), executor); + CompletableFuture futureTimeout = new CompletableFuture(); + schedulerService.schedule(() -> + futureTimeout.completeExceptionally(new TimeoutException()), 3000, TimeUnit.MILLISECONDS); + CompletableFuture result = CompletableFuture.anyOf(dependencyFuture, futureTimeout); + allFutures[i] = result; + } + // Finally wait for all futures to join + CompletableFuture.allOf(allFutures).join(); + System.out.println("All futures completed"); + System.out.println(executor.toString()); + + } +} \ No newline at end of file diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index 32824af85..3f3fd7edd 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -6,23 +6,19 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import org.ethereum.beacon.Launcher; -import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.simulator.SimulatorLauncher; import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; import org.ethereum.beacon.ssz.SSZBuilder; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.stream.SimpleProcessor; import org.ethereum.beacon.wire.channel.Channel; -import org.ethereum.beacon.wire.channel.beacon.WireApiSubRpc; import org.ethereum.beacon.wire.message.SSZMessageSerializer; -import org.ethereum.beacon.wire.net.Client; import org.ethereum.beacon.wire.net.ConnectionManager; import org.ethereum.beacon.wire.net.NettyClient; import org.ethereum.beacon.wire.net.NettyServer; import org.ethereum.beacon.wire.net.Server; import org.ethereum.beacon.wire.sync.BeaconBlockTree; -import org.ethereum.beacon.wire.sync.SyncManager; +import org.ethereum.beacon.wire.sync.SyncManagerImpl; import org.ethereum.beacon.wire.sync.SyncQueue; import org.ethereum.beacon.wire.sync.SyncQueueImpl; import org.junit.Assert; @@ -68,7 +64,7 @@ public void test01() { @Test public void test1() throws Exception { - int slotCount = 1; + int slotCount = 32; SimulatorLauncher simulatorLauncher = new Builder() .withConfigFromResource("/sync-simulation-config.yml") .build(); @@ -96,6 +92,7 @@ public void test1() throws Exception { ssz, peer0.getSpec(), messageSerializer, + peer0.getSchedulers(), syncServer, peer0.getBeaconChain().getBlockStatesStream()); @@ -130,6 +127,7 @@ public void test1() throws Exception { ssz, peer1.getSpec(), messageSerializer, + peer0.getSchedulers(), null, peer1.getBeaconChain().getBlockStatesStream()); @@ -143,7 +141,7 @@ public void test1() throws Exception { BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); - SyncManager syncManager = new SyncManager( + SyncManagerImpl syncManager = new SyncManagerImpl( peer1.getBeaconChain(), Flux.from(peerManager.getWireApiSub().inboundBlocksStream()).map(Feedback::of), peer1.getBeaconChainStorage(), diff --git a/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java index 49fdcaec0..e543987ec 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/channel/BeaconPipelineChannelTest.java @@ -1,9 +1,18 @@ package org.ethereum.beacon.wire.channel; +import java.time.Duration; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.schedulers.ControlledSchedulers; +import org.ethereum.beacon.schedulers.Scheduler; +import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.ssz.SSZBuilder; import org.ethereum.beacon.ssz.SSZSerializer; import org.ethereum.beacon.wire.Feedback; @@ -23,6 +32,7 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; @@ -30,12 +40,16 @@ public class BeaconPipelineChannelTest { static class SimpleChannel implements Channel { - Processor in; - Processor out; + DirectProcessor in; + DirectProcessor out; + FluxSink inSink; + FluxSink outSink; - public SimpleChannel(Processor in, Processor out) { + public SimpleChannel(DirectProcessor in, DirectProcessor out) { this.in = in; this.out = out; + inSink = in.sink(); + outSink = out.sink(); } @Override @@ -47,44 +61,70 @@ public Publisher inboundMessageStream() { public void subscribeToOutbound(Publisher outboundMessageStream) { Flux.from(outboundMessageStream).doOnError(Throwable::printStackTrace).subscribe(out); } + + public void close() { + inSink.complete(); + outSink.complete(); + } } - @Test - public void simpleTest1() throws Exception { - WireApiSync dummyServer = new WireApiSync() { - @Override - public CompletableFuture requestBlockRoots( - BlockRootsRequestMessage requestMessage) { - - CompletableFuture ret = new CompletableFuture<>(); - ret.complete(new BlockRootsResponseMessage(Collections.singletonList(new BlockRootSlot( - Hash32.ZERO, SlotNumber.of(666))))); - return ret; - } + static class DummyWireApiSync implements WireApiSync { - @Override - public CompletableFuture requestBlockHeaders( - BlockHeadersRequestMessage requestMessage) { - return null; - } + private final Scheduler scheduler; + private final Duration responseDuration; - @Override - public CompletableFuture> requestBlockBodies( - BlockBodiesRequestMessage requestMessage) { - return null; - } - }; + public DummyWireApiSync(Scheduler scheduler, Duration responseDuration) { + this.scheduler = scheduler; + this.responseDuration = responseDuration; + } + + @Override + public CompletableFuture requestBlockRoots( + BlockRootsRequestMessage requestMessage) { + + CompletableFuture ret = new CompletableFuture<>(); + scheduler.executeWithDelay( + responseDuration, + () -> + ret.complete( + new BlockRootsResponseMessage( + Collections.singletonList( + new BlockRootSlot(Hash32.ZERO, SlotNumber.of(666)))))); + return ret; + } + + @Override + public CompletableFuture requestBlockHeaders( + BlockHeadersRequestMessage requestMessage) { + return null; + } + + @Override + public CompletableFuture> requestBlockBodies( + BlockBodiesRequestMessage requestMessage) { + return null; + } + }; + + ; + + @Test + public void simpleTest1() throws Exception { + Schedulers schedulers = Schedulers.createDefault(); + DummyWireApiSync dummyServer = new DummyWireApiSync(schedulers.blocking(), Duration.ZERO); DirectProcessor _1to2 = DirectProcessor.create(); DirectProcessor _2to1 = DirectProcessor.create(); SSZSerializer sszSerializer = new SSZBuilder().buildSerializer(); - BeaconPipeline peer1Pipeline = new BeaconPipeline(sszSerializer, null ,null, dummyServer); + BeaconPipeline peer1Pipeline = + new BeaconPipeline(sszSerializer, null, null, dummyServer, schedulers); SimpleChannel peer1Channel = new SimpleChannel<>(_2to1, _1to2); peer1Pipeline.initFromMessageChannel(peer1Channel); - BeaconPipeline peer2Pipeline = new BeaconPipeline(sszSerializer, null ,null, dummyServer); + BeaconPipeline peer2Pipeline = + new BeaconPipeline(sszSerializer, null, null, dummyServer, schedulers); SimpleChannel peer2Channel = new SimpleChannel<>(_1to2, _2to1); peer2Pipeline.initFromMessageChannel(peer2Channel); @@ -96,4 +136,99 @@ public CompletableFuture> requestBlockBodie System.out.println(responseMessage); Assert.assertEquals(SlotNumber.of(666), responseMessage.getRoots().get(0).getSlot()); } + + @Test + public void closeTest1() throws Exception { + + DummyWireApiSync dummyServer = new DummyWireApiSync(Schedulers.createDefault().blocking(), Duration.ofMillis(50)); + + DirectProcessor _1to2 = DirectProcessor.create(); + DirectProcessor _2to1 = DirectProcessor.create(); + + SSZSerializer sszSerializer = new SSZBuilder().buildSerializer(); + ControlledSchedulers schedulers = Schedulers.createControlled(); + + BeaconPipeline peer1Pipeline = + new BeaconPipeline(sszSerializer, null, null, dummyServer, schedulers); + SimpleChannel peer1Channel = new SimpleChannel<>(_2to1, _1to2); + peer1Pipeline.initFromMessageChannel(peer1Channel); + + BeaconPipeline peer2Pipeline = + new BeaconPipeline(sszSerializer, null, null, dummyServer, schedulers); + SimpleChannel peer2Channel = new SimpleChannel<>(_1to2, _2to1); + peer2Pipeline.initFromMessageChannel(peer2Channel); + + WireApiSync peer2SyncClient = peer2Pipeline.getSyncClient(); + + CountDownLatch closeLatch = new CountDownLatch(10); + new Thread(() -> { + try { + closeLatch.await(); + System.out.println("Closing the channel"); + peer1Channel.close(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + + List> futs = new ArrayList<>(); + + for (int i = 0; i < 20; i++) { + System.out.println("Sending request #" + i); + CompletableFuture resp = peer2SyncClient + .requestBlockRoots(new BlockRootsRequestMessage(SlotNumber.ZERO, UInt64.ZERO)); + futs.add(resp); + int finalI = i; + resp.whenComplete( + (r, t) -> { + System.out.println("Call #" + finalI + " complete with " + r + ", " + t); + closeLatch.countDown(); + }); + Thread.sleep(10); + } + + schedulers.addTime(Duration.ofMinutes(10)); + + Assert.assertTrue(futs.stream().allMatch(fut -> fut.isDone())); + Assert.assertTrue(futs.stream().anyMatch(fut -> fut.isCompletedExceptionally())); + Assert.assertTrue(futs.stream().anyMatch(fut -> !fut.isCompletedExceptionally())); + } + + @Test + public void timeoutTest1() throws Exception { + + DummyWireApiSync dummyServer = new DummyWireApiSync(Schedulers.createDefault().blocking(), Duration.ofDays(100500)); + + DirectProcessor _1to2 = DirectProcessor.create(); + DirectProcessor _2to1 = DirectProcessor.create(); + + SSZSerializer sszSerializer = new SSZBuilder().buildSerializer(); + ControlledSchedulers schedulers = Schedulers.createControlled(); + + BeaconPipeline peer1Pipeline = + new BeaconPipeline(sszSerializer, null, null, dummyServer, schedulers); + SimpleChannel peer1Channel = new SimpleChannel<>(_2to1, _1to2); + peer1Pipeline.initFromMessageChannel(peer1Channel); + + BeaconPipeline peer2Pipeline = + new BeaconPipeline(sszSerializer, null, null, dummyServer, schedulers); + SimpleChannel peer2Channel = new SimpleChannel<>(_1to2, _2to1); + peer2Pipeline.initFromMessageChannel(peer2Channel); + + WireApiSync peer2SyncClient = peer2Pipeline.getSyncClient(); + + System.out.println("Sending request..."); + CompletableFuture resp = peer2SyncClient + .requestBlockRoots(new BlockRootsRequestMessage(SlotNumber.ZERO, UInt64.ZERO)); + resp.whenComplete((r, t) -> System.out.println("Call complete with " + r + ", " + t)); + + schedulers.addTime(Duration.ofMillis(500)); + + Assert.assertFalse(resp.isDone()); + + schedulers.addTime(Duration.ofMinutes(10)); + + Assert.assertTrue(resp.isDone()); + Assert.assertTrue(resp.isCompletedExceptionally()); + } } diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java index 6f6e2600c..bc4c1ed16 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -6,8 +6,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ethereum.beacon.Launcher; -import org.ethereum.beacon.chain.BeaconTupleDetails; -import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.schedulers.Scheduler; import org.ethereum.beacon.simulator.SimulatorLauncher; import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; @@ -22,7 +20,6 @@ import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; import org.junit.Assert; import org.junit.Test; -import reactor.core.publisher.ConnectableFlux; import reactor.core.publisher.Flux; public class SyncTest { @@ -63,6 +60,7 @@ public void test1() throws Exception { int slotCount = 64; SimulatorLauncher simulatorLauncher = new Builder() .withConfigFromResource("/sync-simulation-config.yml") + .withLogLevel(null) .build(); simulatorLauncher.run(slotCount); Launcher peer0 = simulatorLauncher.getPeers().get(0); @@ -77,7 +75,7 @@ public void test1() throws Exception { BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); - SyncManager syncManager = new SyncManager( + SyncManagerImpl syncManager = new SyncManagerImpl( testPeer.getBeaconChain(), Flux.never(), testPeer.getBeaconChainStorage(), diff --git a/wire/src/test/resources/log4j2.xml b/wire/src/test/resources/log4j2.xml index 8ed5b2cba..b8d50f0b5 100644 --- a/wire/src/test/resources/log4j2.xml +++ b/wire/src/test/resources/log4j2.xml @@ -28,13 +28,13 @@ - - + + diff --git a/wire/src/test/resources/test-spec-config.yml b/wire/src/test/resources/test-spec-config.yml new file mode 100644 index 000000000..89f9587fb --- /dev/null +++ b/wire/src/test/resources/test-spec-config.yml @@ -0,0 +1,23 @@ +specConstants: + initialValues: + GENESIS_SLOT: 1000000 + miscParameters: + SHARD_COUNT: 4 + TARGET_COMMITTEE_SIZE: 2 + timeParameters: + SECONDS_PER_SLOT: 10 + MIN_ATTESTATION_INCLUSION_DELAY: 1 + SLOTS_PER_EPOCH: 4 + SLOTS_PER_HISTORICAL_ROOT: 64 + + honestValidatorParameters: + ETH1_FOLLOW_DISTANCE: 1 + stateListLengths: + LATEST_RANDAO_MIXES_LENGTH: 64 + LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 64 + LATEST_SLASHED_EXIT_LENGTH: 64 + +specHelpersOptions: + blsVerify: false + blsVerifyProofOfPossession: false + blsSign: false From cc62328fc742d21e7768b6d455779ac3b60b4b57 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 15 May 2019 19:37:21 +0300 Subject: [PATCH 29/96] Refactor: move netty related code to a separate package --- .../ethereum/beacon/wire/net/{ => netty}/NettyChannel.java | 3 +-- .../beacon/wire/net/{ => netty}/NettyChannelInitializer.java | 5 +---- .../ethereum/beacon/wire/net/{ => netty}/NettyClient.java | 5 +++-- .../ethereum/beacon/wire/net/{ => netty}/NettyServer.java | 3 ++- wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java | 5 ++--- wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java | 4 ++-- .../java/org/ethereum/beacon/wire/net/NettyChannelTest.java | 3 +++ 7 files changed, 14 insertions(+), 14 deletions(-) rename wire/src/main/java/org/ethereum/beacon/wire/net/{ => netty}/NettyChannel.java (97%) rename wire/src/main/java/org/ethereum/beacon/wire/net/{ => netty}/NettyChannelInitializer.java (90%) rename wire/src/main/java/org/ethereum/beacon/wire/net/{ => netty}/NettyClient.java (90%) rename wire/src/main/java/org/ethereum/beacon/wire/net/{ => netty}/NettyServer.java (97%) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyChannel.java similarity index 97% rename from wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java rename to wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyChannel.java index 661055978..078be81ad 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyChannel.java @@ -1,10 +1,9 @@ -package org.ethereum.beacon.wire.net; +package org.ethereum.beacon.wire.net.netty; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.function.Consumer; import org.apache.logging.log4j.LogManager; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyChannelInitializer.java similarity index 90% rename from wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java rename to wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyChannelInitializer.java index fba21d037..3e7fe67da 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyChannelInitializer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyChannelInitializer.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon.wire.net; +package org.ethereum.beacon.wire.net.netty; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; @@ -7,10 +7,7 @@ import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.codec.LineBasedFrameDecoder; -import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.timeout.ReadTimeoutHandler; -import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyClient.java similarity index 90% rename from wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java rename to wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyClient.java index 386930c13..dd1a5590f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyClient.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyClient.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon.wire.net; +package org.ethereum.beacon.wire.net.netty; import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.bootstrap.Bootstrap; @@ -9,8 +9,9 @@ import io.netty.channel.socket.nio.NioSocketChannel; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.wire.net.Client; -public class NettyClient implements Client{ +public class NettyClient implements Client { private final NioEventLoopGroup workerGroup; public NettyClient() { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyServer.java similarity index 97% rename from wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java rename to wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyServer.java index 55f44e069..d7e45bba4 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/NettyServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyServer.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon.wire.net; +package org.ethereum.beacon.wire.net.netty; import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.bootstrap.ServerBootstrap; @@ -11,6 +11,7 @@ import io.netty.handler.logging.LoggingHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ethereum.beacon.wire.net.Server; import org.reactivestreams.Publisher; import reactor.core.publisher.FluxSink; import reactor.core.publisher.UnicastProcessor; diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java index 8529671f3..a1614c9c6 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -33,9 +33,8 @@ import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.net.ConnectionManager; -import org.ethereum.beacon.wire.net.NettyChannel; -import org.ethereum.beacon.wire.net.NettyClient; -import org.ethereum.beacon.wire.net.NettyServer; +import org.ethereum.beacon.wire.net.netty.NettyClient; +import org.ethereum.beacon.wire.net.netty.NettyServer; import org.javatuples.Pair; import org.junit.Test; import tech.pegasys.artemis.ethereum.core.Hash32; diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index 3f3fd7edd..3eea57f57 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -14,8 +14,8 @@ import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.message.SSZMessageSerializer; import org.ethereum.beacon.wire.net.ConnectionManager; -import org.ethereum.beacon.wire.net.NettyClient; -import org.ethereum.beacon.wire.net.NettyServer; +import org.ethereum.beacon.wire.net.netty.NettyClient; +import org.ethereum.beacon.wire.net.netty.NettyServer; import org.ethereum.beacon.wire.net.Server; import org.ethereum.beacon.wire.sync.BeaconBlockTree; import org.ethereum.beacon.wire.sync.SyncManagerImpl; diff --git a/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java b/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java index dc97f3178..c227702df 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/net/NettyChannelTest.java @@ -6,6 +6,9 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import org.ethereum.beacon.wire.net.netty.NettyChannel; +import org.ethereum.beacon.wire.net.netty.NettyClient; +import org.ethereum.beacon.wire.net.netty.NettyServer; import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Flux; From 5f1850c3786078bb2cace20b2a1812f39ee7cdd9 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 16 May 2019 16:25:45 +0300 Subject: [PATCH 30/96] WireApiSyncRouter feeds active peers in cycle --- .../org/ethereum/beacon/stream/RxUtil.java | 24 +++++++++ versions.gradle | 1 + wire/build.gradle | 1 + .../wire/channel/RpcChannelAdapter.java | 6 ++- .../beacon/wire/sync/WireApiSyncRouter.java | 15 +++++- .../java/org/ethereum/beacon/wire/RxTest.java | 52 +++++++++++++++++++ 6 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/RxTest.java diff --git a/util/src/main/java/org/ethereum/beacon/stream/RxUtil.java b/util/src/main/java/org/ethereum/beacon/stream/RxUtil.java index 8223d4718..32ee5d523 100644 --- a/util/src/main/java/org/ethereum/beacon/stream/RxUtil.java +++ b/util/src/main/java/org/ethereum/beacon/stream/RxUtil.java @@ -1,6 +1,10 @@ package org.ethereum.beacon.stream; +import java.util.ArrayList; +import java.util.List; +import org.javatuples.Pair; import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; public class RxUtil { @@ -8,4 +12,24 @@ public class RxUtil { public static Publisher join(Publisher s1, Publisher s2, int bufferLen) { throw new UnsupportedOperationException(); } + + enum Op { + ADDED, REMOVED + } + + public static Flux> collect(Publisher addedStream, Publisher removedStream) { + return Flux.merge( + Flux.from(addedStream).map(e -> Pair.with(Op.ADDED, e)), + Flux.from(removedStream).map(e -> Pair.with(Op.REMOVED, e)) + ).scan(new ArrayList(), (arr, op) -> { + ArrayList ret = new ArrayList<>(arr); + if (op.getValue0() == Op.ADDED) { + ret.add(op.getValue1()); + } else { + ret.remove(op.getValue1()); + } + return ret; + }); + } + } diff --git a/versions.gradle b/versions.gradle index a5d07a05e..3becc7f04 100644 --- a/versions.gradle +++ b/versions.gradle @@ -25,6 +25,7 @@ dependencyManagement { dependency "net.consensys.cava:cava-ssz:${cavaVersion}" dependency "net.consensys.cava:cava-units:${cavaVersion}" dependency "io.projectreactor:reactor-core:3.2.5.RELEASE" + dependency "io.projectreactor:reactor-test:3.2.5.RELEASE" dependency "org.reactivestreams:reactive-streams:1.0.2" dependency "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonVersion}" diff --git a/wire/build.gradle b/wire/build.gradle index 665e49ec0..d491b1c57 100644 --- a/wire/build.gradle +++ b/wire/build.gradle @@ -13,6 +13,7 @@ dependencies { implementation 'io.vertx:vertx-core' testImplementation 'org.mockito:mockito-core' + testImplementation 'io.projectreactor:reactor-test' testImplementation project(':start:common') testImplementation project(':start:simulator') testImplementation project(':start:simulator').sourceSets.test.output diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java index 9ff61fad0..dafb4ff82 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java @@ -108,7 +108,11 @@ public CompletableFuture invokeRemote(TRequestMessage request) requestRpcMsg.setRequestContext(CONTEXT_KEY_FUTURE, ret); outboundSink.next(requestRpcMsg); return timeoutScheduler.orTimeout( - ret, rpcCallTimeout, () -> new WireRpcTimeoutException("RPC call timeout.")); + ret, + rpcCallTimeout, + () -> closed + ? new WireRpcClosedException("Channel was closed during call execution") + : new WireRpcTimeoutException("RPC call timeout.")); } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java index d372e4ddb..1d752c36f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java @@ -1,9 +1,11 @@ package org.ethereum.beacon.wire.sync; +import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; +import org.ethereum.beacon.stream.RxUtil; import org.ethereum.beacon.util.Utils; import org.ethereum.beacon.wire.Feedback; import org.ethereum.beacon.wire.WireApiSync; @@ -13,6 +15,7 @@ import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; +import org.javatuples.Pair; import org.reactivestreams.Publisher; import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; @@ -24,13 +27,21 @@ public class WireApiSyncRouter implements WireApiSync { private final ReplayProcessor> tasks = ReplayProcessor.create(64); private final FluxSink> tasksSink = tasks.sink(); + enum Op { + ADDED, REMOVED + }; + public WireApiSyncRouter( Publisher addedPeersStream, Publisher removedPeersStream) { // TODO simple unlimited first peer here, need something more smart - Publisher freePeersStream = Flux.from(addedPeersStream) - .flatMap(api -> Flux.just(api).repeat()); + Publisher freePeersStream = + RxUtil.collect(addedPeersStream, removedPeersStream) + .switchMap( + activePeers -> + activePeers.isEmpty() ? Flux.never() : Flux.fromIterable(activePeers).repeat(), + 1); Flux.zip(freePeersStream, tasks) .subscribe(p -> p.getT2().accept(p.getT1())); diff --git a/wire/src/test/java/org/ethereum/beacon/wire/RxTest.java b/wire/src/test/java/org/ethereum/beacon/wire/RxTest.java new file mode 100644 index 000000000..b9dfdadaa --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/RxTest.java @@ -0,0 +1,52 @@ +package org.ethereum.beacon.wire; + +import static java.util.Arrays.asList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.ethereum.beacon.stream.RxUtil; +import org.javatuples.Pair; +import org.junit.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; +import reactor.test.StepVerifier; +import reactor.test.StepVerifier.FirstStep; + +public class RxTest { + + FluxSink addSink; + FluxSink removeSink; + + @Test + public void test1() { + Publisher addedPeersStream = Flux.create(e -> addSink = e); + Publisher removedPeersStream = Flux.create(e -> removeSink = e); + + Flux> activeList = RxUtil.collect(addedPeersStream, removedPeersStream); + + activeList.subscribe(l -> System.out.println(l)); + FirstStep> test = StepVerifier.create(activeList); + + addSink.next(1); + + test.expectNext(new ArrayList<>(asList(1))); + + addSink.next(2); + addSink.next(3); + + test.expectNext(new ArrayList<>(asList(1, 2, 3))); + + removeSink.next(2); + + test.expectNext(new ArrayList<>(asList(1, 3))); + } + + @Test + public void test2() { + Flux.fromIterable(Collections.emptyList()).repeat().subscribe(e -> System.out.println("a")); + System.out.println("Complete"); + } +} From 1bf6cd0abf24bef9868aa41fbe069c6e44d08ef7 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 16 May 2019 20:18:08 +0300 Subject: [PATCH 31/96] Add missing hashCode() methods --- .../org/ethereum/beacon/core/operations/Deposit.java | 8 ++++++++ .../beacon/core/operations/ProposerSlashing.java | 8 ++++++++ .../ethereum/beacon/core/operations/VoluntaryExit.java | 8 ++++++++ .../beacon/core/operations/deposit/DepositData.java | 8 ++++++++ .../beacon/core/operations/deposit/DepositInput.java | 8 ++++++++ .../core/operations/slashing/AttesterSlashing.java | 7 +++++++ .../core/operations/slashing/SlashableAttestation.java | 9 +++++++++ .../java/org/ethereum/beacon/core/state/Eth1Data.java | 7 +++++++ 8 files changed, 63 insertions(+) diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/Deposit.java b/core/src/main/java/org/ethereum/beacon/core/operations/Deposit.java index 6d2f0c66e..87a9832ea 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/Deposit.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/Deposit.java @@ -56,6 +56,14 @@ public boolean equals(Object o) { && Objects.equal(depositData, deposit.depositData); } + @Override + public int hashCode() { + int result = proof.hashCode(); + result = 31 * result + index.hashCode(); + result = 31 * result + depositData.hashCode(); + return result; + } + @Override public String toString() { return "Deposit[" diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/ProposerSlashing.java b/core/src/main/java/org/ethereum/beacon/core/operations/ProposerSlashing.java index e3d04a8e5..bb9d20efd 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/ProposerSlashing.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/ProposerSlashing.java @@ -46,6 +46,14 @@ public boolean equals(Object o) { && Objects.equal(header2, that.header2); } + @Override + public int hashCode() { + int result = proposerIndex.hashCode(); + result = 31 * result + header1.hashCode(); + result = 31 * result + header2.hashCode(); + return result; + } + @Override public String toString() { return toString(null, null); diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/VoluntaryExit.java b/core/src/main/java/org/ethereum/beacon/core/operations/VoluntaryExit.java index 481bce595..d8c75c163 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/VoluntaryExit.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/VoluntaryExit.java @@ -67,6 +67,14 @@ public boolean equals(Object o) { && Objects.equal(signature, voluntaryExit.signature); } + @Override + public int hashCode() { + int result = epoch.hashCode(); + result = 31 * result + validatorIndex.hashCode(); + result = 31 * result + signature.hashCode(); + return result; + } + @Override public String toString() { return toString(null); diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositData.java b/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositData.java index 0ec9300a5..46ab26b3f 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositData.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositData.java @@ -53,4 +53,12 @@ public boolean equals(Object o) { && Objects.equal(amount, that.amount) && Objects.equal(timestamp, that.timestamp); } + + @Override + public int hashCode() { + int result = amount.hashCode(); + result = 31 * result + timestamp.hashCode(); + result = 31 * result + depositInput.hashCode(); + return result; + } } diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositInput.java b/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositInput.java index d36b52550..a5dad8ddd 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositInput.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/deposit/DepositInput.java @@ -55,4 +55,12 @@ public boolean equals(Object o) { && Objects.equal(withdrawalCredentials, that.withdrawalCredentials) && Objects.equal(proofOfPossession, that.proofOfPossession); } + + @Override + public int hashCode() { + int result = pubKey.hashCode(); + result = 31 * result + withdrawalCredentials.hashCode(); + result = 31 * result + proofOfPossession.hashCode(); + return result; + } } diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/slashing/AttesterSlashing.java b/core/src/main/java/org/ethereum/beacon/core/operations/slashing/AttesterSlashing.java index 437794ada..93ff9b7a5 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/slashing/AttesterSlashing.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/slashing/AttesterSlashing.java @@ -35,6 +35,13 @@ public boolean equals(Object o) { return slashableAttestation2.equals(that.slashableAttestation2); } + @Override + public int hashCode() { + int result = slashableAttestation1.hashCode(); + result = 31 * result + slashableAttestation2.hashCode(); + return result; + } + @Override public String toString() { return toString(null, null); diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/slashing/SlashableAttestation.java b/core/src/main/java/org/ethereum/beacon/core/operations/slashing/SlashableAttestation.java index 584e43c07..770a91db6 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/slashing/SlashableAttestation.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/slashing/SlashableAttestation.java @@ -76,6 +76,15 @@ public boolean equals(Object o) { return aggregateSingature.equals(that.aggregateSingature); } + @Override + public int hashCode() { + int result = validatorIndicesList.hashCode(); + result = 31 * result + data.hashCode(); + result = 31 * result + custodyBitfield.hashCode(); + result = 31 * result + aggregateSingature.hashCode(); + return result; + } + @Override public String toString() { return toString(null, null); diff --git a/core/src/main/java/org/ethereum/beacon/core/state/Eth1Data.java b/core/src/main/java/org/ethereum/beacon/core/state/Eth1Data.java index 32bcfea6d..abb1e278f 100644 --- a/core/src/main/java/org/ethereum/beacon/core/state/Eth1Data.java +++ b/core/src/main/java/org/ethereum/beacon/core/state/Eth1Data.java @@ -49,6 +49,13 @@ public boolean equals(Object o) { && Objects.equal(blockHash, eth1Data.blockHash); } + @Override + public int hashCode() { + int result = depositRoot.hashCode(); + result = 31 * result + blockHash.hashCode(); + return result; + } + @Override public String toString() { return "Eth1Data[deposit=" + depositRoot.toStringShort() + ", block=" + blockHash.toStringShort() + "]"; From b9156a676fe264f8d76024366474f3bdb93fc64f Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 16 May 2019 20:47:18 +0300 Subject: [PATCH 32/96] Implement flood pub for new blocks and attestations --- .../beacon/core/ModelsSerializeTest.java | 40 +++--- .../java/org/ethereum/beacon/util/Utils.java | 12 ++ .../beacon/wire/WireApiSubRouter.java | 93 +++++++++--- .../wire/channel/RpcChannelAdapter.java | 5 +- .../beacon/wire/WireApiSubRouterTest.java | 136 ++++++++++++++++++ 5 files changed, 253 insertions(+), 33 deletions(-) create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java diff --git a/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java b/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java index ad7e587aa..46fa98b46 100644 --- a/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java +++ b/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java @@ -59,7 +59,7 @@ public void setup() { .buildSerializer(); } - private AttestationData createAttestationData() { + public static AttestationData createAttestationData() { AttestationData expected = new AttestationData( SlotNumber.of(123), @@ -82,11 +82,15 @@ public void attestationDataTest() { assertEquals(expected, reconstructed); } - private Attestation createAttestation() { + public static Attestation createAttestation() { + return createAttestation(BytesValue.fromHexString("aa")); + } + + public static Attestation createAttestation(BytesValue someValue) { AttestationData attestationData = createAttestationData(); Attestation attestation = new Attestation( - Bitfield.of(BytesValue.fromHexString("aa")), + Bitfield.of(someValue), attestationData, Bitfield.of(BytesValue.fromHexString("bb")), BLSSignature.wrap(Bytes96.fromHexString("cc"))); @@ -102,7 +106,7 @@ public void attestationTest() { assertEquals(expected, reconstructed); } - private DepositInput createDepositInput() { + public static DepositInput createDepositInput() { DepositInput depositInput = new DepositInput( BLSPubkey.wrap(Bytes48.TRUE), @@ -120,7 +124,7 @@ public void depositInputTest() { assertEquals(expected, reconstructed); } - private DepositData createDepositData() { + public static DepositData createDepositData() { DepositData depositData = new DepositData(Gwei.ZERO, Time.castFrom(UInt64.valueOf(123)), createDepositInput()); @@ -135,13 +139,13 @@ public void depositDataTest() { assertEquals(expected, reconstructed); } - private Deposit createDeposit1() { + public static Deposit createDeposit1() { Deposit deposit = new Deposit(Collections.emptyList(), UInt64.ZERO, createDepositData()); return deposit; } - private Deposit createDeposit2() { + public static Deposit createDeposit2() { ArrayList hashes = new ArrayList<>(); hashes.add(Hashes.keccak256(BytesValue.fromHexString("aa"))); hashes.add(Hashes.keccak256(BytesValue.fromHexString("bb"))); @@ -162,7 +166,7 @@ public void depositTest() { assertEquals(expected2, reconstructed2); } - private VoluntaryExit createExit() { + public static VoluntaryExit createExit() { VoluntaryExit voluntaryExit = new VoluntaryExit(EpochNumber.of(123), ValidatorIndex.MAX, BLSSignature.wrap(Bytes96.fromHexString("aa"))); return voluntaryExit; @@ -185,7 +189,7 @@ public void beaconBlockHeaderTest() { assertEquals(expected, reconstructed); } - private ProposerSlashing createProposerSlashing(Random random) { + public static ProposerSlashing createProposerSlashing(Random random) { ProposerSlashing proposerSlashing = new ProposerSlashing( ValidatorIndex.MAX, @@ -197,15 +201,15 @@ private ProposerSlashing createProposerSlashing(Random random) { @Test public void proposerSlashingTest() { - Random random = new Random(); + Random random = new Random(1); ProposerSlashing expected = createProposerSlashing(random); BytesValue encoded = sszSerializer.encode2(expected); ProposerSlashing reconstructed = sszSerializer.decode(encoded, ProposerSlashing.class); assertEquals(expected, reconstructed); } - private BeaconBlockBody createBeaconBlockBody() { - Random random = new Random(); + public static BeaconBlockBody createBeaconBlockBody() { + Random random = new Random(1); List proposerSlashings = new ArrayList<>(); proposerSlashings.add(createProposerSlashing(random)); List attesterSlashings = new ArrayList<>(); @@ -234,13 +238,13 @@ private BeaconBlockBody createBeaconBlockBody() { return beaconBlockBody; } - private AttesterSlashing createAttesterSlashings() { + public static AttesterSlashing createAttesterSlashings() { return new AttesterSlashing( createSlashableAttestation(), createSlashableAttestation()); } - private SlashableAttestation createSlashableAttestation() { + public static SlashableAttestation createSlashableAttestation() { return new SlashableAttestation( Arrays.asList(ValidatorIndex.of(234), ValidatorIndex.of(678)), createAttestationData(), @@ -272,11 +276,15 @@ public void beaconBlockBodyTest() { assertEquals(expected, reconstructed); } - private BeaconBlock createBeaconBlock() { + public static BeaconBlock createBeaconBlock() { + return createBeaconBlock(BytesValue.fromHexString("aa")); + } + + public static BeaconBlock createBeaconBlock(BytesValue someValue) { BeaconBlock beaconBlock = new BeaconBlock( SlotNumber.castFrom(UInt64.MAX_VALUE), - Hashes.keccak256(BytesValue.fromHexString("aa")), + Hashes.keccak256(someValue), Hashes.keccak256(BytesValue.fromHexString("bb")), createBeaconBlockBody(), BLSSignature.wrap(Bytes96.fromHexString("aa"))); diff --git a/util/src/main/java/org/ethereum/beacon/util/Utils.java b/util/src/main/java/org/ethereum/beacon/util/Utils.java index 4d4a92eb9..668498c23 100644 --- a/util/src/main/java/org/ethereum/beacon/util/Utils.java +++ b/util/src/main/java/org/ethereum/beacon/util/Utils.java @@ -1,6 +1,10 @@ package org.ethereum.beacon.util; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.stream.Stream; @@ -25,4 +29,12 @@ public static void futureForward(CompletableFuture result, CompletableFut } }); } + + public static Set newLRUSet(int size) { + return Collections.newSetFromMap(new LinkedHashMap() { + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > size; + } + }); + } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java index e6e238275..1a2f70f3a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java @@ -1,56 +1,117 @@ package org.ethereum.beacon.wire; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; +import java.util.Set; +import java.util.function.Function; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.stream.RxUtil; +import org.ethereum.beacon.util.Utils; +import org.javatuples.Pair; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; public class WireApiSubRouter implements WireApiSub { + private static final int DUPLICATE_DETECTION_SET_SIZE = 64; - private final List activeApis = new CopyOnWriteArrayList<>(); + private final Flux inboundAttestationsStream; + private final Flux inboundBlocksStream; + private FluxSink localAttestationSink; + private FluxSink localBlocksSink; - private final Flux attestationsStream; - private final Flux blocksStream; + private volatile List activeApis = Collections.emptyList(); + + Set seenBlocks; public WireApiSubRouter( Publisher addedPeersStream, Publisher removedPeersStream) { - blocksStream = Flux - .from(addedPeersStream) + RxUtil.collect(addedPeersStream, removedPeersStream) + .subscribe(l -> activeApis = l); + + // flood pub realization: upon receiving new block from remote or local + // broadcasting it to all except sender + // also filtering already known blocks + Flux localBlocks = Flux.create(e -> localBlocksSink = e); + + Flux> allNewBlocks = Flux.from(addedPeersStream) + .flatMap(api -> Flux.from(api.inboundBlocksStream()).map(block -> new ApiData<>(api, block))) + .mergeWith(localBlocks.map(block -> new ApiData<>(this, block))) + .distinct(ApiData::getData, () -> seenBlocks = Utils.newLRUSet(DUPLICATE_DETECTION_SET_SIZE)); + + allNewBlocks.subscribe( + p -> { + activeApis + .stream() + .filter(api -> !api.equals(p.getApi())) + .forEach(api -> api.sendProposedBlock(p.getData())); + }); + + // the same flood pub for attestations + Flux localAttestations = Flux.create(e -> localAttestationSink = e); + + Flux> allNewAttestations = Flux.from(addedPeersStream) + .flatMap(api -> Flux.from(api.inboundAttestationsStream()).map(attest -> new ApiData<>(api, attest))) + .mergeWith(localAttestations.map(attest -> new ApiData<>(this, attest))) + .distinct(ApiData::getData, () -> Utils.newLRUSet(DUPLICATE_DETECTION_SET_SIZE)); + + allNewAttestations.subscribe( + p -> { + activeApis + .stream() + .filter(api -> !api.equals(p.getApi())) + .forEach(api -> api.sendAttestation(p.getData())); + }); + + inboundBlocksStream = Flux.from(addedPeersStream) .flatMap(WireApiSub::inboundBlocksStream) - .distinct(); + .distinct(Function.identity(), () -> Utils.newLRUSet(DUPLICATE_DETECTION_SET_SIZE)); - attestationsStream = Flux + inboundAttestationsStream = Flux .from(addedPeersStream) .flatMap(WireApiSub::inboundAttestationsStream) - .distinct(); + .distinct(Function.identity(), () -> Utils.newLRUSet(DUPLICATE_DETECTION_SET_SIZE)); - Flux.from(addedPeersStream).subscribe(api -> activeApis.add(api)); - Flux.from(removedPeersStream).subscribe(api -> activeApis.remove(api)); } @Override public void sendProposedBlock(BeaconBlock block) { - activeApis.forEach(api -> api.sendProposedBlock(block)); + localBlocksSink.next(block); } @Override public void sendAttestation(Attestation attestation) { - activeApis.forEach(api -> api.sendAttestation(attestation)); + localAttestationSink.next(attestation); } @Override public Publisher inboundBlocksStream() { - return blocksStream; + return inboundBlocksStream; } @Override public Publisher inboundAttestationsStream() { - return attestationsStream; + return inboundAttestationsStream; + } + + static class ApiData { + private final WireApiSub api; + private final T block; + + public ApiData(WireApiSub api, T block) { + this.api = api; + this.block = block; + } + + public WireApiSub getApi() { + return api; + } + + public T getData() { + return block; + } } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java index dafb4ff82..26530b82e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/RpcChannelAdapter.java @@ -33,7 +33,10 @@ public RpcChannelAdapter(RpcChannel inChannel this.inChannel = inChannel; this.serverHandler = serverHandler; this.timeoutScheduler = timeoutScheduler; - inChannel.subscribeToOutbound(Flux.create(s -> outboundSink = s)); + inChannel.subscribeToOutbound( + Flux.>create(s -> outboundSink = s) + .publish(1) + .autoConnect()); Flux.from(inChannel.inboundMessageStream()) .subscribe(this::onInbound, err -> logger.warn("Unexpected error", err), this::onClose); } diff --git a/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java b/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java new file mode 100644 index 000000000..0ef589614 --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java @@ -0,0 +1,136 @@ +package org.ethereum.beacon.wire; + +import static org.ethereum.beacon.core.ModelsSerializeTest.createBeaconBlock; +import static tech.pegasys.artemis.util.bytes.BytesValue.fromHexString; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.ModelsSerializeTest; +import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.util.Utils; +import org.ethereum.beacon.wire.WireApiSubRouterTest.TestRouter.Connection; +import org.ethereum.beacon.wire.channel.beacon.WireApiSubAdapter; +import org.junit.Assert; +import org.junit.Test; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.FluxSink; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class WireApiSubRouterTest { + + static class TestRouter { + static class Connection { + TestRouter router1; + TestRouter router2; + WireApiSub outerApi1; + WireApiSub outerApi2; + + public Connection(TestRouter router1, + TestRouter router2, WireApiSub outerApi1, WireApiSub outerApi2) { + this.router1 = router1; + this.router2 = router2; + this.outerApi1 = outerApi1; + this.outerApi2 = outerApi2; + } + + public void disconnect() { + router1.removeSink.next(outerApi2); + router2.removeSink.next(outerApi1); + } + } + + FluxSink addSink; + FluxSink removeSink; + WireApiSubRouter router; + + List receivedBlocks = new ArrayList<>(); + List receivedAttestations = new ArrayList<>(); + + public TestRouter() { + router = new WireApiSubRouter( + Flux.create(s -> addSink = s).publish(1).autoConnect(), + Flux.create(s -> removeSink = s).publish(1).autoConnect()); + Flux.from(router.inboundBlocksStream()).subscribe(receivedBlocks::add); + Flux.from(router.inboundAttestationsStream()).subscribe(receivedAttestations::add); + } + + public Connection connect(TestRouter other) { + WireApiSubAdapter thisApi = new WireApiSubAdapter(); + WireApiSubAdapter otherApi = new WireApiSubAdapter(); + thisApi.setSubClient(otherApi); + otherApi.setSubClient(thisApi); + this.addSink.next(otherApi); + other.addSink.next(thisApi); + + return new Connection(this, other, thisApi, otherApi); + } + + void clear() { + receivedBlocks.clear(); + receivedAttestations.clear(); + } + } + + @Test + public void test1() { + TestRouter router1 = new TestRouter(); + router1.router.sendProposedBlock(createBeaconBlock()); + Assert.assertTrue(router1.receivedBlocks.isEmpty()); + + TestRouter router2 = new TestRouter(); + Connection connection1 = router1.connect(router2); + + router1.router.sendProposedBlock(createBeaconBlock(fromHexString("01"))); + Assert.assertTrue(router1.receivedBlocks.isEmpty()); + Assert.assertEquals(1, router2.receivedBlocks.size()); + + router1.router.sendProposedBlock(createBeaconBlock(fromHexString("02"))); + Assert.assertTrue(router1.receivedBlocks.isEmpty()); + Assert.assertEquals(2, router2.receivedBlocks.size()); + + router1.router.sendProposedBlock(createBeaconBlock(fromHexString("01"))); + Assert.assertTrue(router1.receivedBlocks.isEmpty()); + Assert.assertEquals(2, router2.receivedBlocks.size()); + + connection1.outerApi2.sendProposedBlock(createBeaconBlock(fromHexString("01"))); + Assert.assertEquals(2, router2.receivedBlocks.size()); + + connection1.disconnect(); + router2.clear(); + + router1.router.sendProposedBlock(createBeaconBlock(fromHexString("03"))); + Assert.assertTrue(router1.receivedBlocks.isEmpty()); + Assert.assertTrue(router2.receivedBlocks.isEmpty()); + + Connection connection2 = router1.connect(router2); + + router1.router.sendProposedBlock(createBeaconBlock(fromHexString("04"))); + Assert.assertTrue(router1.receivedBlocks.isEmpty()); + Assert.assertEquals(1, router2.receivedBlocks.size()); + + router2.clear(); + + TestRouter router3 = new TestRouter(); + Connection connection3 = router2.connect(router3); + + router1.router.sendProposedBlock(createBeaconBlock(fromHexString("05"))); + Assert.assertTrue(router1.receivedBlocks.isEmpty()); + Assert.assertEquals(1, router2.receivedBlocks.size()); + Assert.assertEquals(1, router3.receivedBlocks.size()); + } + + @Test + public void testMisc() { + BeaconBlock b1 = createBeaconBlock(fromHexString("01")); + BeaconBlock b2 = createBeaconBlock(fromHexString("01")); + Assert.assertTrue(b1.equals(b2)); + Assert.assertEquals(b1.hashCode(), b2.hashCode()); + HashSet set = new HashSet<>(); + Assert.assertTrue(set.add(b1)); + Assert.assertFalse(set.add(b2)); + } +} From 10422710fc545efc9bae7f233597dfe723823fe3 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 16 May 2019 21:49:50 +0300 Subject: [PATCH 33/96] Fix ConnectionManager, add test --- .../org/ethereum/beacon/wire/net/Client.java | 2 +- .../beacon/wire/net/ConnectionManager.java | 10 +- .../wire/net/ConnectionManagerTest.java | 93 +++++++++++++++++++ 3 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java b/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java index 55472f02a..a76c5ca47 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java @@ -6,6 +6,6 @@ public interface Client { - CompletableFuture> connect(TAddress address); + > CompletableFuture connect(TAddress address); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java index 3a96b5550..ed36128ef 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java @@ -18,6 +18,7 @@ public class ConnectionManager { private static final Logger logger = LogManager.getLogger(ConnectionManager.class); + private static final int RECONNECT_TIMEOUT_SECONDS = 1; private final Server server; private final Client client; @@ -46,19 +47,20 @@ public CompletableFuture> connect(TAddress peerAddress) { public void addActivePeer(TAddress peerAddress) { activePeers.add(peerAddress); - // TODO make sure this woodoo magic actually works + Flux.just(peerAddress) .doOnNext(addr -> logger.info("Connecting to active peer " + peerAddress)) .map(client::connect) .flatMap(Mono::fromFuture, 1, 1) - .onErrorContinue((t, o) -> logger.info("Couldn't connect to active peer " + peerAddress + ": " + t)) + .doOnError(t-> logger.info("Couldn't connect to active peer " + peerAddress + ": " + t)) .doOnNext(ch -> logger.info("Connected to active peer " + peerAddress)) .doOnNext(clientConnectionsSink::next) .map(Channel::getCloseFuture) - .flatMap(Mono::fromFuture, 1, 1) + .onErrorResume(t -> Flux.just(CompletableFuture.completedFuture(null))) + .flatMap(f -> Mono.fromFuture(f.thenApply(v -> "")), 1, 1) .doOnNext(ch -> logger.info("Disconnected from active peer " + peerAddress)) + .delayElements(Duration.ofSeconds(RECONNECT_TIMEOUT_SECONDS), rxScheduler) .repeat(() -> activePeers.contains(peerAddress)) - .delayElements(Duration.ofSeconds(1), rxScheduler) .subscribe(); } diff --git a/wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java b/wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java new file mode 100644 index 000000000..e07cd76ca --- /dev/null +++ b/wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java @@ -0,0 +1,93 @@ +package org.ethereum.beacon.wire.net; + +import io.netty.channel.ConnectTimeoutException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import org.ethereum.beacon.schedulers.ControlledSchedulers; +import org.ethereum.beacon.schedulers.Schedulers; +import org.ethereum.beacon.wire.channel.Channel; +import org.junit.Assert; +import org.junit.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.DirectProcessor; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import tech.pegasys.artemis.util.bytes.BytesValue; + +public class ConnectionManagerTest { + + class TestClient implements Client { + List>> connections = new ArrayList<>(); + + @Override + public CompletableFuture> connect(String s) { + CompletableFuture> ret = new CompletableFuture<>(); + connections.add(ret); + return ret; + } + } + + class TestChannel implements Channel { + DirectProcessor data = DirectProcessor.create(); + + @Override + public Publisher inboundMessageStream() { + return data; + } + + @Override + public void subscribeToOutbound(Publisher outboundMessageStream) { + } + + public void close() { + data.onComplete(); + } + } + + @Test + public void test1() { + List> channels = new ArrayList<>(); + + TestClient client = new TestClient(); + ControlledSchedulers schedulers = Schedulers.createControlled(); + ConnectionManager manager = new ConnectionManager<>(null, client, + schedulers.reactorEvents()); + Flux.from(manager.channelsStream()).subscribe(channels::add); + + manager.addActivePeer("1"); + Assert.assertEquals(1, client.connections.size()); + Assert.assertEquals(0, channels.size()); + schedulers.addTime(Duration.ofSeconds(10)); + Assert.assertEquals(1, client.connections.size()); + Assert.assertEquals(0, channels.size()); + Channel testChannel1 = new TestChannel(); + client.connections.get(0).complete(testChannel1); + Assert.assertEquals(1, channels.size()); + schedulers.addTime(Duration.ofSeconds(10)); + Assert.assertEquals(1, client.connections.size()); + testChannel1.close(); + Assert.assertEquals(1, client.connections.size()); + schedulers.addTime(Duration.ofMillis(500)); + Assert.assertEquals(1, client.connections.size()); + schedulers.addTime(Duration.ofSeconds(10)); + Assert.assertEquals(2, client.connections.size()); + Assert.assertEquals(1, channels.size()); + + client.connections.get(1).completeExceptionally(new ConnectTimeoutException()); + Assert.assertEquals(1, channels.size()); + Assert.assertEquals(2, client.connections.size()); + schedulers.addTime(Duration.ofSeconds(10)); + Assert.assertEquals(3, client.connections.size()); + TestChannel testChannel2 = new TestChannel(); + client.connections.get(2).complete(testChannel2); + Assert.assertEquals(2, channels.size()); + testChannel2.close(); + + Assert.assertEquals(3, client.connections.size()); + manager.removeActivePeer("1"); + schedulers.addTime(Duration.ofSeconds(10)); + Assert.assertEquals(3, client.connections.size()); + } +} From f3fda15f4bb6f016cd2954650b91d3a3c5e6a2fe Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 17 May 2019 11:17:31 +0300 Subject: [PATCH 34/96] Minor cleanup --- .../org/ethereum/beacon/wire/net/ConnectionManager.java | 8 -------- .../java/org/ethereum/beacon/wire/sync/SyncManager.java | 1 + .../org/ethereum/beacon/wire/sync/WireApiSyncRouter.java | 9 --------- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java index ed36128ef..a73617958 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java @@ -73,12 +73,4 @@ public Publisher> channelsStream() { server == null ? Flux.empty() : server.channelsStream(), client == null ? Flux.empty() : clientConnections); } - - public static void main(String[] args) throws Exception { - Flux.merge( - Flux.just(1).delayElements(Duration.ofMillis(1000)), - Flux.just(2).delayElements(Duration.ofMillis(100))) - .subscribe(i -> System.out.println(i)); - Thread.sleep(2000); - } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java index 1d6c82a22..19534e72d 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManager.java @@ -6,6 +6,7 @@ import org.reactivestreams.Publisher; import reactor.core.Disposable; +// TODO: revisit and complete this interface public interface SyncManager { Disposable subscribeToOnlineBlocks(Publisher> onlineBlocks); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java index 1d752c36f..90fc8e5b9 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java @@ -1,7 +1,5 @@ package org.ethereum.beacon.wire.sync; -import java.util.ArrayList; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; @@ -15,9 +13,7 @@ import org.ethereum.beacon.wire.message.payload.BlockHeadersResponseMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsRequestMessage; import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; -import org.javatuples.Pair; import org.reactivestreams.Publisher; -import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; import reactor.core.publisher.ReplayProcessor; @@ -27,15 +23,10 @@ public class WireApiSyncRouter implements WireApiSync { private final ReplayProcessor> tasks = ReplayProcessor.create(64); private final FluxSink> tasksSink = tasks.sink(); - enum Op { - ADDED, REMOVED - }; - public WireApiSyncRouter( Publisher addedPeersStream, Publisher removedPeersStream) { - // TODO simple unlimited first peer here, need something more smart Publisher freePeersStream = RxUtil.collect(addedPeersStream, removedPeersStream) .switchMap( From 1a06c0c97c504e61bf69e674b87ff840b930d38d Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 17 May 2019 16:13:52 +0300 Subject: [PATCH 35/96] Draft template for Node launcher --- settings.gradle | 1 + .../beacon/{ => start/common}/Launcher.java | 2 +- .../{ => start/common}/NodeLauncher.java | 2 +- .../common}/util/MDCControlledSchedulers.java | 2 +- .../common}/util/SimpleDepositContract.java | 2 +- .../start/common}/util/SimulateUtils.java | 2 +- .../emulator/config/main/Configuration.java | 12 + .../config/main/network/NettyNetwork.java | 24 ++ .../emulator/config/main/network/Network.java | 10 + start/config/src/test/resources/config.yml | 6 + start/node/build.gradle | 39 ++++ .../java/org/ethereum/beacon/node/Node.java | 37 ++++ .../beacon/node/NodeCommandLauncher.java | 209 ++++++++++++++++++ .../beacon/node/command/LogLevel.java | 25 +++ .../ethereum/beacon/node/command/RunNode.java | 35 +++ .../src/main/resources/config/node-config.yml | 64 ++++++ start/node/src/main/resources/log4j2.xml | 40 ++++ .../test/resources/config/fast-chainSpec.yml | 8 + .../beacon/simulator/BenchmarkLauncher.java | 8 +- .../beacon/simulator/SimulatorLauncher.java | 8 +- .../org/ethereum/beacon/wire/NodeTest.java | 8 +- .../org/ethereum/beacon/wire/PeersTest.java | 2 +- .../beacon/wire/WireApiSubRouterTest.java | 5 - .../ethereum/beacon/wire/sync/SyncTest.java | 2 +- 24 files changed, 529 insertions(+), 24 deletions(-) rename start/common/src/main/java/org/ethereum/beacon/{ => start/common}/Launcher.java (99%) rename start/common/src/main/java/org/ethereum/beacon/{ => start/common}/NodeLauncher.java (99%) rename start/{simulator/src/main/java/org/ethereum/beacon/simulator => common/src/main/java/org/ethereum/beacon/start/common}/util/MDCControlledSchedulers.java (97%) rename start/{simulator/src/main/java/org/ethereum/beacon/simulator => common/src/main/java/org/ethereum/beacon/start/common}/util/SimpleDepositContract.java (96%) rename start/{simulator/src/main/java/org/ethereum/beacon/simulator => common/src/main/java/org/ethereum/beacon/start/common}/util/SimulateUtils.java (98%) create mode 100644 start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java create mode 100644 start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/Network.java create mode 100644 start/node/build.gradle create mode 100644 start/node/src/main/java/org/ethereum/beacon/node/Node.java create mode 100644 start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java create mode 100644 start/node/src/main/java/org/ethereum/beacon/node/command/LogLevel.java create mode 100644 start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java create mode 100644 start/node/src/main/resources/config/node-config.yml create mode 100644 start/node/src/main/resources/log4j2.xml create mode 100644 start/node/src/test/resources/config/fast-chainSpec.yml diff --git a/settings.gradle b/settings.gradle index a2f2be58e..07ce9f40a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -23,6 +23,7 @@ include 'pow:web3j' include 'ssz' // Command line executable clients include 'start:simulator' +include 'start:node' // Clients helpers include 'start:common' // Configuration parser diff --git a/start/common/src/main/java/org/ethereum/beacon/Launcher.java b/start/common/src/main/java/org/ethereum/beacon/start/common/Launcher.java similarity index 99% rename from start/common/src/main/java/org/ethereum/beacon/Launcher.java rename to start/common/src/main/java/org/ethereum/beacon/start/common/Launcher.java index 05644214c..37b8bef4c 100644 --- a/start/common/src/main/java/org/ethereum/beacon/Launcher.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/Launcher.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon; +package org.ethereum.beacon.start.common; import java.util.List; import org.ethereum.beacon.chain.DefaultBeaconChain; diff --git a/start/common/src/main/java/org/ethereum/beacon/NodeLauncher.java b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java similarity index 99% rename from start/common/src/main/java/org/ethereum/beacon/NodeLauncher.java rename to start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java index 4967d25b5..aecc973c3 100644 --- a/start/common/src/main/java/org/ethereum/beacon/NodeLauncher.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon; +package org.ethereum.beacon.start.common; import java.net.SocketAddress; import java.util.List; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/MDCControlledSchedulers.java b/start/common/src/main/java/org/ethereum/beacon/start/common/util/MDCControlledSchedulers.java similarity index 97% rename from start/simulator/src/main/java/org/ethereum/beacon/simulator/util/MDCControlledSchedulers.java rename to start/common/src/main/java/org/ethereum/beacon/start/common/util/MDCControlledSchedulers.java index 95bb32d92..f079427a5 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/MDCControlledSchedulers.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/util/MDCControlledSchedulers.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon.simulator.util; +package org.ethereum.beacon.start.common.util; import java.text.DateFormat; import java.text.SimpleDateFormat; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimpleDepositContract.java b/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimpleDepositContract.java similarity index 96% rename from start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimpleDepositContract.java rename to start/common/src/main/java/org/ethereum/beacon/start/common/util/SimpleDepositContract.java index 4bae1b4a9..d3f4084a1 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimpleDepositContract.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimpleDepositContract.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon.simulator.util; +package org.ethereum.beacon.start.common.util; import java.util.Collections; import java.util.List; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimulateUtils.java b/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimulateUtils.java similarity index 98% rename from start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimulateUtils.java rename to start/common/src/main/java/org/ethereum/beacon/start/common/util/SimulateUtils.java index 11cb071e5..b9607d38f 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/util/SimulateUtils.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimulateUtils.java @@ -1,4 +1,4 @@ -package org.ethereum.beacon.simulator.util; +package org.ethereum.beacon.start.common.util; import java.util.ArrayList; import java.util.Collections; diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java index ef69a426b..a11951832 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java @@ -1,8 +1,12 @@ package org.ethereum.beacon.emulator.config.main; +import java.util.List; +import org.ethereum.beacon.emulator.config.main.network.Network; + /** Beacon chain configuration */ public class Configuration { private String db; + private List networks; private Validator validator; public String getDb() { @@ -13,6 +17,14 @@ public void setDb(String db) { this.db = db; } + public List getNetworks() { + return networks; + } + + public void setNetworks(List networks) { + this.networks = networks; + } + public Validator getValidator() { return validator; } diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java new file mode 100644 index 000000000..a3059f1d3 --- /dev/null +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java @@ -0,0 +1,24 @@ +package org.ethereum.beacon.emulator.config.main.network; + +import java.util.List; + +public class NettyNetwork extends Network { + private Integer listenPort; + private List activePeers; + + public Integer getListenPort() { + return listenPort; + } + + public void setListenPort(Integer listenPort) { + this.listenPort = listenPort; + } + + public List getActivePeers() { + return activePeers; + } + + public void setActivePeers(List activePeers) { + this.activePeers = activePeers; + } +} diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/Network.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/Network.java new file mode 100644 index 000000000..93d51ca20 --- /dev/null +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/Network.java @@ -0,0 +1,10 @@ +package org.ethereum.beacon.emulator.config.main.network; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = NettyNetwork.class, name = "netty"), +}) +public abstract class Network {} diff --git a/start/config/src/test/resources/config.yml b/start/config/src/test/resources/config.yml index e0603fe36..805a2c756 100644 --- a/start/config/src/test/resources/config.yml +++ b/start/config/src/test/resources/config.yml @@ -1,5 +1,11 @@ config: db: file://db + networks: + - type: netty + listenPort: 40001 + activePeers: + - tcp://localhost:40002 + - tcp://localhost:40003 validator: contract: handler: ethereumj diff --git a/start/node/build.gradle b/start/node/build.gradle new file mode 100644 index 000000000..303db7eb8 --- /dev/null +++ b/start/node/build.gradle @@ -0,0 +1,39 @@ +plugins { + id 'application' +} + +// The next two lines disable the tasks for the primary main which by default +// generates a script with a name matching the project name. +// You can leave them enabled but if so you'll need to define mainClassName +// And you'll be creating your application scripts two different ways which +// could lead to confusion +startScripts.enabled = false +run.enabled = false + +// Call this for each Main class you want to expose with an app script +createScript(project, 'org.ethereum.beacon.simulator.Simulator', 'simulator') + +dependencies { + implementation project(':types') + implementation project(':wire') + implementation project(':util') + implementation project(':start:common') + implementation project(':start:config') + implementation project(':crypto') + implementation project(':core') + implementation project(':consensus') + implementation project(':db:core') + implementation project(':chain') + implementation project(':ssz') + implementation project(':pow:core') + implementation project(':validator') + + implementation 'info.picocli:picocli' + implementation 'com.google.guava:guava' + implementation 'io.projectreactor:reactor-core' + implementation 'org.apache.logging.log4j:log4j-core' + implementation 'com.fasterxml.jackson.core:jackson-databind' + implementation 'io.netty:netty-all' + + testImplementation 'org.mockito:mockito-core' +} diff --git a/start/node/src/main/java/org/ethereum/beacon/node/Node.java b/start/node/src/main/java/org/ethereum/beacon/node/Node.java new file mode 100644 index 000000000..aef24a22c --- /dev/null +++ b/start/node/src/main/java/org/ethereum/beacon/node/Node.java @@ -0,0 +1,37 @@ +package org.ethereum.beacon.node; + +import org.ethereum.beacon.node.command.RunNode; +import picocli.CommandLine; +import picocli.CommandLine.RunLast; + +@CommandLine.Command( + description = "Beacon chain node", + name = "node", + version = "simulator " + Node.VERSION, + mixinStandardHelpOptions = true, + subcommands = {RunNode.class}) +public class Node implements Runnable { + + static final String VERSION = "0.1.0"; + + private static final int SUCCESS_EXIT_CODE = 0; + private static final int ERROR_EXIT_CODE = 1; + + public static void main(String[] args) { + try { + CommandLine commandLine = new CommandLine(new Node()); + commandLine.setCaseInsensitiveEnumValuesAllowed(true); + commandLine.parseWithHandlers( + new RunLast().andExit(SUCCESS_EXIT_CODE), + CommandLine.defaultExceptionHandler().andExit(ERROR_EXIT_CODE), + args); + } catch (Exception e) { + System.out.println(String.format((char) 27 + "[31m" + "FATAL ERROR: %s", e.getMessage())); + } + } + + @Override + public void run() { + CommandLine.usage(this, System.out); + } +} diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java new file mode 100644 index 000000000..84f8aeec2 --- /dev/null +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -0,0 +1,209 @@ +package org.ethereum.beacon.node; + +import java.io.File; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.LoggerConfig; +import org.ethereum.beacon.chain.storage.impl.MemBeaconChainStorageFactory; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.core.operations.Deposit; +import org.ethereum.beacon.core.spec.SpecConstants; +import org.ethereum.beacon.core.state.Eth1Data; +import org.ethereum.beacon.core.types.Time; +import org.ethereum.beacon.crypto.BLS381.KeyPair; +import org.ethereum.beacon.emulator.config.ConfigBuilder; +import org.ethereum.beacon.emulator.config.chainspec.SpecBuilder; +import org.ethereum.beacon.emulator.config.chainspec.SpecData; +import org.ethereum.beacon.emulator.config.main.MainConfig; +import org.ethereum.beacon.emulator.config.main.plan.SimulationPlan; +import org.ethereum.beacon.emulator.config.simulator.PeersConfig; +import org.ethereum.beacon.pow.DepositContract; +import org.ethereum.beacon.schedulers.ControlledSchedulers; +import org.ethereum.beacon.start.common.NodeLauncher; +import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; +import org.ethereum.beacon.start.common.util.SimpleDepositContract; +import org.ethereum.beacon.start.common.util.SimulateUtils; +import org.ethereum.beacon.validator.crypto.BLS381Credentials; +import org.ethereum.beacon.wire.net.ConnectionManager; +import org.ethereum.beacon.wire.net.netty.NettyServer; +import org.javatuples.Pair; +import tech.pegasys.artemis.ethereum.core.Hash32; + +public class NodeCommandLauncher implements Runnable { + private static final Logger logger = LogManager.getLogger("node"); + + private final MainConfig config; + private final SpecConstants specConstants; + private final BeaconChainSpec spec; + private final Level logLevel; + private final SpecBuilder specBuilder; + + private Random rnd; + private Time genesisTime; + private MDCControlledSchedulers controlledSchedulers; + private List keyPairs; + private Eth1Data eth1Data; + private DepositContract depositContract; + + /** + * Creates launcher with following settings + * + * @param config configuration and run plan. + * @param specBuilder chain specification builder. + * @param logLevel Log level, Apache log4j type. + */ + public NodeCommandLauncher( + MainConfig config, + SpecBuilder specBuilder, + Level logLevel) { + this.config = config; + this.specBuilder = specBuilder; + this.specConstants = specBuilder.buildSpecConstants(); + this.spec = specBuilder.buildSpec(); + this.logLevel = logLevel; + + init(); + } + + private void setupLogging() { + // set logLevel + if (logLevel != null) { + LoggerContext context = + (LoggerContext) LogManager.getContext(false); + Configuration config = context.getConfiguration(); + LoggerConfig loggerConfig = config.getLoggerConfig("node"); + loggerConfig.setLevel(logLevel); + context.updateLoggers(); + } + } + + public void init() { + setupLogging(); + +// List deposits = validatorDeposits.getValue0().stream() +// .filter(Objects::nonNull).collect(Collectors.toList()); +// keyPairs = validatorDeposits.getValue1(); +// +// genesisTime = Time.of(simulationPlan.getGenesisTime()); +// +// controlledSchedulers = new MDCControlledSchedulers(); +// controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); +// +// eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); +// +// DepositContract.ChainStart chainStart = +// new DepositContract.ChainStart(genesisTime, eth1Data, deposits); +// depositContract = new SimpleDepositContract(chainStart); + } + + public void run() { + if (config.getChainSpec().isDefined()) + logger.info("Overridden beacon chain parameters:\n{}", config.getChainSpec()); + + Random rnd = new Random(); + + ConfigBuilder specConfigBuilder = + new ConfigBuilder<>(SpecData.class) + .addYamlConfigFromResources("/config/spec-constants.yml") + .addYamlConfigFromResources("/test-spec-config.yml"); + SpecData specData = specConfigBuilder.build(); + SpecBuilder specBuilder = new SpecBuilder().withSpec(specData); + BeaconChainSpec spec = specBuilder.buildSpec(); + + Pair, List> depositPairs = + SimulateUtils.getAnyDeposits(rnd, spec, 16, false); + + Time genesisTime = Time.of(60000); + + MDCControlledSchedulers controlledSchedulers = new MDCControlledSchedulers(); + controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); + + Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); + + DepositContract.ChainStart chainStart = + new DepositContract.ChainStart(genesisTime, eth1Data, depositPairs.getValue0()); + SimpleDepositContract depositContract = new SimpleDepositContract(chainStart); + + // master node with all validators + { + ControlledSchedulers schedulers = controlledSchedulers.createNew("master"); + NettyServer nettyServer = new NettyServer(40001); + nettyServer.start(); + ConnectionManager connectionManager = new ConnectionManager<>( + nettyServer, null, schedulers.reactorEvents()); + NodeLauncher masterNode = new NodeLauncher( + specBuilder.buildSpec(), + depositContract, + depositPairs + .getValue1() + .stream() + .map(BLS381Credentials::createWithDummySigner) + .collect(Collectors.toList()), + connectionManager, + new MemBeaconChainStorageFactory(spec.getObjectHasher()), + schedulers, + false); + } + + } + + public static class Builder { + private MainConfig config; + private Level logLevel = Level.INFO; + + public Builder() {} + + public NodeCommandLauncher build() { + assert config != null; + SimulationPlan simulationPlan = (SimulationPlan) config.getPlan(); + + ConfigBuilder specConfigBuilder = + new ConfigBuilder<>(SpecData.class).addYamlConfigFromResources("/config/spec-constants.yml"); + if (config.getChainSpec().isDefined()) { + specConfigBuilder.addConfig(config.getChainSpec()); + } + + SpecData spec = specConfigBuilder.build(); + + List peers = new ArrayList<>(); + for (PeersConfig peer : simulationPlan.getPeers()) { + for (int i = 0; i < peer.getCount(); i++) { + peers.add(peer); + } + } + + SpecBuilder specBuilder = new SpecBuilder().withSpec(spec); + + return new NodeCommandLauncher( + config, + specBuilder, + logLevel); + } + + public Builder withConfigFromFile(File file) { + this.config = new ConfigBuilder<>(MainConfig.class).addYamlConfig(file).build(); + return this; + } + + public Builder withConfigFromResource(String resourceName) { + this.config = + new ConfigBuilder<>(MainConfig.class) + .addYamlConfigFromResources(resourceName) + .build(); + return this; + } + + public Builder withLogLevel(Level logLevel) { + this.logLevel = logLevel; + return this; + } + } +} diff --git a/start/node/src/main/java/org/ethereum/beacon/node/command/LogLevel.java b/start/node/src/main/java/org/ethereum/beacon/node/command/LogLevel.java new file mode 100644 index 000000000..9153ef569 --- /dev/null +++ b/start/node/src/main/java/org/ethereum/beacon/node/command/LogLevel.java @@ -0,0 +1,25 @@ +package org.ethereum.beacon.node.command; + +import org.apache.logging.log4j.Level; + +public enum LogLevel { + all, + debug, + info, + error; + + public Level toLog4j() { + switch (this) { + case all: + return Level.ALL; + case debug: + return Level.DEBUG; + case info: + return Level.INFO; + case error: + return Level.ERROR; + default: + throw new IllegalArgumentException("Unsupported log level " + this); + } + } +} diff --git a/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java new file mode 100644 index 000000000..b53ce57c1 --- /dev/null +++ b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java @@ -0,0 +1,35 @@ +package org.ethereum.beacon.node.command; + +import java.io.File; +import org.ethereum.beacon.node.NodeCommandLauncher; +import picocli.CommandLine; + +@CommandLine.Command(name = "run", description = "Runs beacon chain node", mixinStandardHelpOptions = true) +public class RunNode implements Runnable { + + @CommandLine.Parameters( + index = "0", + paramLabel = "node-config.yml", + description = + "A path to a config file containing node config in YAML format\nuse 'default' to run a node with default setup") + private String config; + + @CommandLine.Option( + names = {"--loglevel"}, + paramLabel = "level", + description = "Log verbosity level: all, debug, info, error\ninfo is set by default") + private LogLevel logLevel = LogLevel.info; + + @Override + public void run() { + NodeCommandLauncher.Builder nodeBuilder = new NodeCommandLauncher.Builder().withLogLevel(logLevel.toLog4j()); + + if ("default".equals(config)) { + nodeBuilder.withConfigFromResource("/config/default-node-config.yml"); + } else { + nodeBuilder.withConfigFromFile(new File(config)); + } + + nodeBuilder.build().run(); + } +} diff --git a/start/node/src/main/resources/config/node-config.yml b/start/node/src/main/resources/config/node-config.yml new file mode 100644 index 000000000..865cc0cdd --- /dev/null +++ b/start/node/src/main/resources/config/node-config.yml @@ -0,0 +1,64 @@ +config: + db: file://db + networks: + - type: netty + listenPort: 40001 + activePeers: + - tcp://localhost:40002 + - tcp://localhost:40003 + + validator: + contract: + handler: ethereumj + distanceFromHead: 1000 + contractAddress: 0xd47c61f2c25aaa677dcf23e65765fac04c85d6a0 + contractBlock: 8000000 + contractAbi: file://Contract.abi + signer: + implementation: + class: InsecureBLS381MessageSigner + input: + privKeys: + - 0x0000000000000000000000000000000000000000000000000000000000000000 + - 0x0000000000000000000000000000000000000000000000000000000000000000 + - 0x0000000000000000000000000000000000000000000000000000000000000000 + +plan: !general + sync: + - action: run + validator: + - action: deposit + creator: ethereumj + sender: ethereumj + gasLimit: 2000000 + eth1From: 0x0000000000000000000000000000000000000000 + eth1PrivKey: 0x0000000000000000000000000000000000000000000000000000000000000000 + withdrawalCredentials: 0x0000000000000000000000000000000000000000 + amount: 32 + + - action: run + +chainSpec: + specConstants: + initialValues: + GENESIS_SLOT: 1000000 + miscParameters: + SHARD_COUNT: 4 + TARGET_COMMITTEE_SIZE: 2 + timeParameters: + SECONDS_PER_SLOT: 10 + MIN_ATTESTATION_INCLUSION_DELAY: 1 + SLOTS_PER_EPOCH: 4 + SLOTS_PER_HISTORICAL_ROOT: 64 + + honestValidatorParameters: + ETH1_FOLLOW_DISTANCE: 1 + stateListLengths: + LATEST_RANDAO_MIXES_LENGTH: 64 + LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 64 + LATEST_SLASHED_EXIT_LENGTH: 64 + + specHelpersOptions: + blsVerify: false + blsVerifyProofOfPossession: false + blsSign: false diff --git a/start/node/src/main/resources/log4j2.xml b/start/node/src/main/resources/log4j2.xml new file mode 100644 index 000000000..d651e7c84 --- /dev/null +++ b/start/node/src/main/resources/log4j2.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + %d{HH:mm:ss.SSS} [%X{validatorTime}] %p %c{1.} [%t] %m%n + + + + + + + + + %d{HH:mm:ss.SSS} [%X{validatorTime}] %p %c{1.} [%t] #%X{validatorIndex} %m%n + + + + + + %d{HH:mm:ss.SSS} #%X{validatorIndex} %X{validatorTime} %-5level - %msg%n + + + + + + + + + + + + + + diff --git a/start/node/src/test/resources/config/fast-chainSpec.yml b/start/node/src/test/resources/config/fast-chainSpec.yml new file mode 100644 index 000000000..94ef0349f --- /dev/null +++ b/start/node/src/test/resources/config/fast-chainSpec.yml @@ -0,0 +1,8 @@ +initialValues: + GENESIS_SLOT: 1000000 +miscParameters: + TARGET_COMMITTEE_SIZE: 1 +timeParameters: + SECONDS_PER_SLOT: 10 + MIN_ATTESTATION_INCLUSION_DELAY: 1 + SLOTS_PER_EPOCH: 2 \ No newline at end of file diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java index 9e72f255c..8b851c660 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/BenchmarkLauncher.java @@ -16,7 +16,7 @@ import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; -import org.ethereum.beacon.Launcher; +import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.chain.observer.ObservableBeaconState; import org.ethereum.beacon.chain.storage.impl.MemBeaconChainStorageFactory; import org.ethereum.beacon.consensus.BeaconChainSpec; @@ -39,9 +39,9 @@ import org.ethereum.beacon.emulator.config.simulator.PeersConfig; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; -import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; -import org.ethereum.beacon.simulator.util.SimpleDepositContract; -import org.ethereum.beacon.simulator.util.SimulateUtils; +import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; +import org.ethereum.beacon.start.common.util.SimpleDepositContract; +import org.ethereum.beacon.start.common.util.SimulateUtils; import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java index bb3645efb..8e6d5eddb 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java @@ -16,7 +16,7 @@ import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; -import org.ethereum.beacon.Launcher; +import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.chain.observer.ObservableBeaconState; import org.ethereum.beacon.chain.storage.impl.MemBeaconChainStorageFactory; import org.ethereum.beacon.consensus.BeaconChainSpec; @@ -40,9 +40,9 @@ import org.ethereum.beacon.emulator.config.simulator.PeersConfig; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; -import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; -import org.ethereum.beacon.simulator.util.SimpleDepositContract; -import org.ethereum.beacon.simulator.util.SimulateUtils; +import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; +import org.ethereum.beacon.start.common.util.SimpleDepositContract; +import org.ethereum.beacon.start.common.util.SimulateUtils; import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java index a1614c9c6..7cc489c57 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; -import org.ethereum.beacon.NodeLauncher; +import org.ethereum.beacon.start.common.NodeLauncher; import org.ethereum.beacon.chain.storage.impl.MemBeaconChainStorageFactory; import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.operations.Deposit; @@ -27,9 +27,9 @@ import org.ethereum.beacon.emulator.config.chainspec.SpecData; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; -import org.ethereum.beacon.simulator.util.MDCControlledSchedulers; -import org.ethereum.beacon.simulator.util.SimpleDepositContract; -import org.ethereum.beacon.simulator.util.SimulateUtils; +import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; +import org.ethereum.beacon.start.common.util.SimpleDepositContract; +import org.ethereum.beacon.start.common.util.SimulateUtils; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.channel.Channel; import org.ethereum.beacon.wire.net.ConnectionManager; diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index 3eea57f57..06d8324d7 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -5,7 +5,7 @@ import java.time.Duration; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; -import org.ethereum.beacon.Launcher; +import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.simulator.SimulatorLauncher; import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; import org.ethereum.beacon.ssz.SSZBuilder; diff --git a/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java b/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java index 0ef589614..2cc1613e4 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java @@ -6,19 +6,14 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Set; import org.ethereum.beacon.core.BeaconBlock; -import org.ethereum.beacon.core.ModelsSerializeTest; import org.ethereum.beacon.core.operations.Attestation; -import org.ethereum.beacon.util.Utils; import org.ethereum.beacon.wire.WireApiSubRouterTest.TestRouter.Connection; import org.ethereum.beacon.wire.channel.beacon.WireApiSubAdapter; import org.junit.Assert; import org.junit.Test; -import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; -import tech.pegasys.artemis.util.bytes.BytesValue; public class WireApiSubRouterTest { diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java index bc4c1ed16..fc4792f6c 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -5,7 +5,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.ethereum.beacon.Launcher; +import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.schedulers.Scheduler; import org.ethereum.beacon.simulator.SimulatorLauncher; import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; From e401c852ae1779c12902e66fecb6fb3331846617 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 17 May 2019 16:53:20 +0300 Subject: [PATCH 36/96] Resolve merge compile conflicts --- .../ethereum/beacon/benchmaker/BenchmarkRunner.java | 6 +++--- .../org/ethereum/beacon/node/NodeCommandLauncher.java | 6 ++++-- .../ethereum/beacon/simulator/SimulatorLauncher.java | 11 +++-------- .../test/java/org/ethereum/beacon/wire/NodeTest.java | 6 ++++-- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java b/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java index 08c3f3723..be7b17cfc 100644 --- a/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java +++ b/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java @@ -13,7 +13,6 @@ import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.ethereum.beacon.Launcher; import org.ethereum.beacon.bench.BenchmarkController; import org.ethereum.beacon.bench.BenchmarkReport; import org.ethereum.beacon.bench.BenchmarkUtils; @@ -35,11 +34,12 @@ import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.schedulers.TimeController; import org.ethereum.beacon.schedulers.TimeControllerImpl; +import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.start.common.util.SimulateUtils; import org.ethereum.beacon.util.stats.MeasurementsCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; -import org.ethereum.beacon.wire.WireApi; +import org.ethereum.beacon.wire.WireApiSub; import org.javatuples.Pair; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; @@ -109,7 +109,7 @@ public void run() { logger.info("Bootstrapping validators ..."); ControlledSchedulers schedulers = controlledSchedulers.createNew("V0"); - WireApi wireApi = localWireHub.createNewPeer("0"); + WireApiSub wireApi = localWireHub.createNewPeer("0"); List blsCreds; if (spec.isBlsVerify()) { diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java index 84f8aeec2..141cca97d 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -36,6 +36,7 @@ import org.ethereum.beacon.wire.net.netty.NettyServer; import org.javatuples.Pair; import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.uint.UInt64; public class NodeCommandLauncher implements Runnable { private static final Logger logger = LogManager.getLogger("node"); @@ -118,15 +119,16 @@ public void run() { SpecBuilder specBuilder = new SpecBuilder().withSpec(specData); BeaconChainSpec spec = specBuilder.buildSpec(); + int depositCount = 16; Pair, List> depositPairs = - SimulateUtils.getAnyDeposits(rnd, spec, 16, false); + SimulateUtils.getAnyDeposits(rnd, spec, depositCount, false); Time genesisTime = Time.of(60000); MDCControlledSchedulers controlledSchedulers = new MDCControlledSchedulers(); controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); - Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); + Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), UInt64.valueOf(depositCount), Hash32.random(rnd)); DepositContract.ChainStart chainStart = new DepositContract.ChainStart(genesisTime, eth1Data, depositPairs.getValue0()); diff --git a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java index 9ada3353f..680b8f06c 100644 --- a/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java +++ b/start/simulator/src/main/java/org/ethereum/beacon/simulator/SimulatorLauncher.java @@ -16,7 +16,6 @@ import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; -import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.chain.observer.ObservableBeaconState; import org.ethereum.beacon.chain.storage.impl.MemBeaconChainStorageFactory; import org.ethereum.beacon.consensus.BeaconChainSpec; @@ -33,17 +32,17 @@ import org.ethereum.beacon.crypto.BLS381.KeyPair; import org.ethereum.beacon.crypto.BLS381.PrivateKey; import org.ethereum.beacon.emulator.config.ConfigBuilder; -import org.ethereum.beacon.emulator.config.chainspec.SpecData; import org.ethereum.beacon.emulator.config.chainspec.SpecBuilder; +import org.ethereum.beacon.emulator.config.chainspec.SpecData; import org.ethereum.beacon.emulator.config.main.MainConfig; import org.ethereum.beacon.emulator.config.main.plan.SimulationPlan; import org.ethereum.beacon.emulator.config.simulator.PeersConfig; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; +import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; import org.ethereum.beacon.start.common.util.SimpleDepositContract; import org.ethereum.beacon.start.common.util.SimulateUtils; -import org.ethereum.beacon.util.stats.TimeCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; import org.ethereum.beacon.wire.WireApiSub; @@ -73,7 +72,6 @@ public class SimulatorLauncher implements Runnable { private List keyPairs; private Eth1Data eth1Data; private DepositContract depositContract; - private TimeCollector proposeTimeCollector; private List peers; @@ -154,8 +152,6 @@ public void init() { DepositContract.ChainStart chainStart = new DepositContract.ChainStart(genesisTime, eth1Data, deposits); depositContract = new SimpleDepositContract(chainStart); - - proposeTimeCollector = new TimeCollector(); } public Launcher createPeer(String name) { @@ -181,8 +177,7 @@ public Launcher createPeer(PeersConfig config, BLS381Credentials bls, WireApiSub bls == null ? null : Collections.singletonList(bls), wireApi, new MemBeaconChainStorageFactory(spec.getObjectHasher()), - schedulers, - proposeTimeCollector); + schedulers); } public void run() { diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java index 7cc489c57..5f4f5213e 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -39,6 +39,7 @@ import org.junit.Test; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; public class NodeTest { @@ -54,15 +55,16 @@ public void test1() throws ExecutionException, InterruptedException { SpecBuilder specBuilder = new SpecBuilder().withSpec(specData); BeaconChainSpec spec = specBuilder.buildSpec(); + int depositCount = 16; Pair, List> depositPairs = - SimulateUtils.getAnyDeposits(rnd, spec, 16, false); + SimulateUtils.getAnyDeposits(rnd, spec, depositCount, false); Time genesisTime = Time.of(60000); MDCControlledSchedulers controlledSchedulers = new MDCControlledSchedulers(); controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); - Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); + Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), UInt64.valueOf(depositCount), Hash32.random(rnd)); DepositContract.ChainStart chainStart = new DepositContract.ChainStart(genesisTime, eth1Data, depositPairs.getValue0()); From 1d6823ab954599eb5816e7c47d00a5d661f723f4 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 17 May 2019 17:05:48 +0300 Subject: [PATCH 37/96] Fix build error --- wire/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/wire/build.gradle b/wire/build.gradle index d491b1c57..c2d069d94 100644 --- a/wire/build.gradle +++ b/wire/build.gradle @@ -15,6 +15,7 @@ dependencies { testImplementation 'org.mockito:mockito-core' testImplementation 'io.projectreactor:reactor-test' testImplementation project(':start:common') + testImplementation project(':start:config') testImplementation project(':start:simulator') testImplementation project(':start:simulator').sourceSets.test.output From 4e2552494d69c18c8fdbea98b801e78108ed0af2 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 10:34:06 +0300 Subject: [PATCH 38/96] Use common classes for BenchmarkRunner --- .../beacon/benchmaker/BenchmarkRunner.java | 88 +------------------ 1 file changed, 2 insertions(+), 86 deletions(-) diff --git a/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java b/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java index be7b17cfc..ba1ac2b04 100644 --- a/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java +++ b/start/benchmaker/src/main/java/org/ethereum/beacon/benchmaker/BenchmarkRunner.java @@ -1,14 +1,9 @@ package org.ethereum.beacon.benchmaker; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.time.Duration; import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.Random; import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; @@ -30,20 +25,16 @@ import org.ethereum.beacon.crypto.util.BlsKeyPairReader; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.ControlledSchedulers; -import org.ethereum.beacon.schedulers.LoggerMDCExecutor; -import org.ethereum.beacon.schedulers.Schedulers; -import org.ethereum.beacon.schedulers.TimeController; -import org.ethereum.beacon.schedulers.TimeControllerImpl; import org.ethereum.beacon.start.common.Launcher; +import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; +import org.ethereum.beacon.start.common.util.SimpleDepositContract; import org.ethereum.beacon.start.common.util.SimulateUtils; import org.ethereum.beacon.util.stats.MeasurementsCollector; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.LocalWireHub; import org.ethereum.beacon.wire.WireApiSub; import org.javatuples.Pair; -import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; @@ -231,79 +222,4 @@ static Stats createFrom(MeasurementsCollector collector) { return stats; } } - - private static class SimpleDepositContract implements DepositContract { - private final ChainStart chainStart; - - public SimpleDepositContract(ChainStart chainStart) { - this.chainStart = chainStart; - } - - @Override - public Publisher getChainStartMono() { - return Mono.just(chainStart); - } - - @Override - public Publisher getDepositStream() { - return Mono.empty(); - } - - @Override - public List peekDeposits( - int maxCount, Eth1Data fromDepositExclusive, Eth1Data tillDepositInclusive) { - return Collections.emptyList(); - } - - @Override - public boolean hasDepositRoot(Hash32 blockHash, Hash32 depositRoot) { - return true; - } - - @Override - public Optional getLatestEth1Data() { - return Optional.of(chainStart.getEth1Data()); - } - - @Override - public void setDistanceFromHead(long distanceFromHead) {} - } - - public static class MDCControlledSchedulers { - private DateFormat localTimeFormat = new SimpleDateFormat("HH:mm:ss.SSS"); - - private TimeController timeController = new TimeControllerImpl(); - - public ControlledSchedulers createNew(String validatorId) { - return createNew(validatorId, 0); - } - - public ControlledSchedulers createNew(String validatorId, long timeShift) { - ControlledSchedulers[] newSched = new ControlledSchedulers[1]; - LoggerMDCExecutor mdcExecutor = new LoggerMDCExecutor() - .add("validatorTime", () -> localTimeFormat.format(new Date(newSched[0].getCurrentTime()))) - .add("validatorIndex", () -> "" + validatorId); - newSched[0] = Schedulers.createControlled(() -> mdcExecutor); - newSched[0].getTimeController().setParent(timeController); - newSched[0].getTimeController().setTimeShift(timeShift); - - return newSched[0]; - } - - public void setCurrentTime(long time) { - timeController.setTime(time); - } - - void addTime(Duration duration) { - addTime(duration.toMillis()); - } - - void addTime(long millis) { - setCurrentTime(timeController.getTime() + millis); - } - - public long getCurrentTime() { - return timeController.getTime(); - } - } } From 89b5755ce92c585d775b45534989c69402ea79eb Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 10:44:10 +0300 Subject: [PATCH 39/96] Fix test dependencies --- wire/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wire/build.gradle b/wire/build.gradle index c2d069d94..b319c2a5a 100644 --- a/wire/build.gradle +++ b/wire/build.gradle @@ -14,6 +14,9 @@ dependencies { testImplementation 'org.mockito:mockito-core' testImplementation 'io.projectreactor:reactor-test' + testImplementation project(':crypto') + testImplementation project(':validator') + testImplementation project(':pow:core') testImplementation project(':start:common') testImplementation project(':start:config') testImplementation project(':start:simulator') From 6b547fad53dcf0ae402d9c04befb453ce41f573d Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 11:10:21 +0300 Subject: [PATCH 40/96] Filter 'future' attestations in ObservableState --- .../beacon/chain/observer/ObservableStateProcessorImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java index 4631f1954..d8c8b119e 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java @@ -255,6 +255,8 @@ private PendingOperations getPendingOperations( BeaconState state, Map> attestationMap) { List attestations = attestationMap.values().stream() .flatMap(Collection::stream) + .filter(attestation -> + attestation.getData().getTargetEpoch().lessEqual(spec.get_current_epoch(state))) .filter(attestation -> { /* attestation_slot = get_attestation_slot(state, attestation) assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH */ From 234f1da5ba97dfda94011d764d76906054e5aa13 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 11:10:53 +0300 Subject: [PATCH 41/96] Expose 'syncMode' from SyncManager --- .../beacon/wire/sync/SyncManagerImpl.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java index 1d5e62386..1ab7c31c5 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java @@ -27,6 +27,12 @@ import tech.pegasys.artemis.ethereum.core.Hash32; public class SyncManagerImpl { + + public enum SyncMode { + Long, + Short + } + private static final Logger logger = LogManager.getLogger(SyncManagerImpl.class); private final Publisher blockStatesStream; @@ -35,6 +41,9 @@ public class SyncManagerImpl { private final WireApiSync syncApi; private Publisher> newBlocks; private final SyncQueue syncQueue; + private final ModeDetector modeDetector; + private final Flux syncModeFlux; + FluxSink> requestsStreams; Flux blockRequestFlux; Scheduler delayScheduler; @@ -65,12 +74,11 @@ public SyncManagerImpl( this.maxConcurrentBlockRequests = maxConcurrentBlockRequests; this.delayScheduler = delayScheduler; - ModeDetector modeDetector = - new ModeDetector( - Flux.from(chain.getBlockStatesStream()).map(BeaconTuple::getBlock), - Flux.from(newBlocks).map(Feedback::get)); - blockRequestFlux = - Flux.from(modeDetector.getSyncModeStream()) + modeDetector = new ModeDetector( + Flux.from(chain.getBlockStatesStream()).map(BeaconTuple::getBlock), + Flux.from(newBlocks).map(Feedback::get)); + syncModeFlux = Flux.from(modeDetector.getSyncModeStream()).replay(1).autoConnect(); + blockRequestFlux = syncModeFlux .doOnNext(mode -> logger.info("Switch sync to mode " + mode)) .switchMap( mode -> { @@ -145,9 +153,8 @@ public void stop() { readyBlocksStreamSub.dispose(); } - enum SyncMode { - Long, - Short + public Publisher getSyncModeStream() { + return syncModeFlux; } class ModeDetector { From 7ea8888908c1e358d37dcd9012f5269cafcc2918 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 11:15:05 +0300 Subject: [PATCH 42/96] Add toString() --- .../beacon/ssz/incremental/ObservableCompositeHelper.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ssz/src/main/java/org/ethereum/beacon/ssz/incremental/ObservableCompositeHelper.java b/ssz/src/main/java/org/ethereum/beacon/ssz/incremental/ObservableCompositeHelper.java index 025b74350..e77fd1f1d 100644 --- a/ssz/src/main/java/org/ethereum/beacon/ssz/incremental/ObservableCompositeHelper.java +++ b/ssz/src/main/java/org/ethereum/beacon/ssz/incremental/ObservableCompositeHelper.java @@ -57,6 +57,11 @@ public UpdateListener fork() { public C get() { return value; } + + @Override + public String toString() { + return value == null ? "null" : value.toString(); + } } private Map listeners; From e6966f93d559b894c5fae17a221be1410d685ff6 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 11:16:43 +0300 Subject: [PATCH 43/96] Make NodeTest a unit test --- .../beacon/start/common/NodeLauncher.java | 4 ++++ .../org/ethereum/beacon/wire/NodeTest.java | 18 ++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java index aecc973c3..3c23f5d3b 100644 --- a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java @@ -308,4 +308,8 @@ public BeaconChainStorageFactory getStorageFactory() { public Schedulers getSchedulers() { return schedulers; } + + public SyncManagerImpl getSyncManager() { + return syncManager; + } } diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java index 5f4f5213e..27d055cbd 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -35,8 +35,13 @@ import org.ethereum.beacon.wire.net.ConnectionManager; import org.ethereum.beacon.wire.net.netty.NettyClient; import org.ethereum.beacon.wire.net.netty.NettyServer; +import org.ethereum.beacon.wire.sync.SyncManager; +import org.ethereum.beacon.wire.sync.SyncManagerImpl; +import org.ethereum.beacon.wire.sync.SyncManagerImpl.SyncMode; import org.javatuples.Pair; +import org.junit.Assert; import org.junit.Test; +import reactor.core.publisher.Flux; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.bytes.BytesValue; import tech.pegasys.artemis.util.uint.UInt64; @@ -71,13 +76,14 @@ public void test1() throws ExecutionException, InterruptedException { SimpleDepositContract depositContract = new SimpleDepositContract(chainStart); // master node with all validators + NodeLauncher masterNode; { ControlledSchedulers schedulers = controlledSchedulers.createNew("master"); NettyServer nettyServer = new NettyServer(40001); nettyServer.start(); ConnectionManager connectionManager = new ConnectionManager<>( nettyServer, null, schedulers.reactorEvents()); - NodeLauncher masterNode = new NodeLauncher( + masterNode = new NodeLauncher( specBuilder.buildSpec(), depositContract, depositPairs @@ -97,12 +103,13 @@ public void test1() throws ExecutionException, InterruptedException { // slave node ConnectionManager slaveConnectionManager; CompletableFuture> connectFut; + NodeLauncher slaveNode; { ControlledSchedulers schedulers = controlledSchedulers.createNew("slave"); NettyClient nettyClient = new NettyClient(); slaveConnectionManager = new ConnectionManager<>( null, nettyClient, schedulers.reactorEvents()); - NodeLauncher slaveNode = new NodeLauncher( + slaveNode = new NodeLauncher( specBuilder.buildSpec(), depositContract, null, @@ -142,11 +149,14 @@ public void test1() throws ExecutionException, InterruptedException { System.out.println("Slave connected"); System.out.println("Some time in 'realtime' mode..."); - for (int i = 0; i < 50000; i++) { + for (int i = 0; i < 100; i++) { controlledSchedulers.addTime(Duration.ofSeconds(1)); Thread.sleep(50); } - Thread.sleep(10000000); + + SyncMode syncMode = Flux.from(slaveNode.getSyncManager().getSyncModeStream()) + .blockLast(Duration.ofSeconds(1)); + Assert.assertEquals(SyncMode.Short, syncMode); } public static Integer getValue() { From 215c78e310d3a68929c54603f1fb4ca2263206eb Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 14:23:05 +0300 Subject: [PATCH 44/96] Fix mode switching --- .../java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java index 1ab7c31c5..eeb673a3d 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java @@ -137,7 +137,7 @@ public void start() { req.getStep())) .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher())), maxConcurrentBlockRequests) - .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o, t)); + .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o)); if (newBlocks != null) { wireBlocksStream = wireBlocksStream.mergeWith( @@ -176,7 +176,7 @@ public ModeDetector( s1.retainAll(s2); return s1.isEmpty() ? SyncMode.Long : SyncMode.Short; }) - .distinct(); + .distinctUntilChanged(); } private ArrayList listAddLimited(ArrayList list, A elem, int maxSize) { From 1701eb544661f10e191fb200e6dbcf30e7f718d3 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 14:23:48 +0300 Subject: [PATCH 45/96] Fix NodeTest: check sync mode on different stages --- .../org/ethereum/beacon/wire/NodeTest.java | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java index 27d055cbd..614f8b48d 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -42,6 +42,7 @@ import org.junit.Assert; import org.junit.Test; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.bytes.BytesValue; import tech.pegasys.artemis.util.uint.UInt64; @@ -57,7 +58,8 @@ public void test1() throws ExecutionException, InterruptedException { .addYamlConfigFromResources("/config/spec-constants.yml") .addYamlConfigFromResources("/test-spec-config.yml"); SpecData specData = specConfigBuilder.build(); - SpecBuilder specBuilder = new SpecBuilder().withSpec(specData); + SpecBuilder specBuilder = new SpecBuilder() + .withSpec(specData); BeaconChainSpec spec = specBuilder.buildSpec(); int depositCount = 16; @@ -122,10 +124,18 @@ public void test1() throws ExecutionException, InterruptedException { System.out.println("Connected! " + connectFut.get()); } + Assert.assertEquals( + SyncMode.Long, + Mono.from(slaveNode.getSyncManager().getSyncModeStream()).block(Duration.ZERO)); + // generate some new blocks System.out.println("Generating online blocks"); controlledSchedulers.addTime(Duration.ofSeconds(3 * 10)); + Flux.from(slaveNode.getSyncManager().getSyncModeStream()) + .filter(mode -> mode == SyncMode.Short) + .blockFirst(Duration.ofSeconds(30)); + // 'realtime' mode System.out.println("Some time in 'realtime' mode..."); for (int i = 0; i < 50; i++) { @@ -148,15 +158,22 @@ public void test1() throws ExecutionException, InterruptedException { connectFut1.get(); System.out.println("Slave connected"); + System.out.println("Generating online blocks"); + controlledSchedulers.addTime(Duration.ofSeconds(10 * 10)); + + Flux.from(slaveNode.getSyncManager().getSyncModeStream()) + .filter(mode -> mode == SyncMode.Long) + .blockFirst(Duration.ofSeconds(30)); + System.out.println("Some time in 'realtime' mode..."); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 50; i++) { controlledSchedulers.addTime(Duration.ofSeconds(1)); Thread.sleep(50); } - SyncMode syncMode = Flux.from(slaveNode.getSyncManager().getSyncModeStream()) - .blockLast(Duration.ofSeconds(1)); - Assert.assertEquals(SyncMode.Short, syncMode); + Flux.from(slaveNode.getSyncManager().getSyncModeStream()) + .filter(mode -> mode == SyncMode.Short) + .blockFirst(Duration.ofSeconds(30)); } public static Integer getValue() { From 13d4804f53eeb60eaefbf35c0cc797b90d62ac9e Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 14:29:03 +0300 Subject: [PATCH 46/96] Add specHelpers cache control via config --- .../beacon/emulator/config/chainspec/SpecBuilder.java | 2 +- .../emulator/config/chainspec/SpecHelpersData.java | 10 ++++++++++ wire/src/test/resources/test-spec-config.yml | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecBuilder.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecBuilder.java index 7436e338d..fc46111ac 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecBuilder.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecBuilder.java @@ -41,7 +41,7 @@ public BeaconChainSpec buildSpec( .withConstants(specConstants) .withBlsVerify(specHelpersOptions.isBlsVerify()) .withBlsVerifyProofOfPossession(specHelpersOptions.isBlsVerifyProofOfPossession()) - .enableCache() + .withCache(spec.getSpecHelpersOptions().isEnableCache()) .build(); } diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecHelpersData.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecHelpersData.java index 79c30ba89..e04267656 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecHelpersData.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/chainspec/SpecHelpersData.java @@ -9,6 +9,8 @@ public class SpecHelpersData { private boolean blsSign = true; + private boolean enableCache = true; + public boolean isBlsVerify() { return blsVerify; } @@ -32,4 +34,12 @@ public boolean isBlsSign() { public void setBlsSign(boolean blsSign) { this.blsSign = blsSign; } + + public boolean isEnableCache() { + return enableCache; + } + + public void setEnableCache(boolean enableCache) { + this.enableCache = enableCache; + } } diff --git a/wire/src/test/resources/test-spec-config.yml b/wire/src/test/resources/test-spec-config.yml index 89f9587fb..42ca4b738 100644 --- a/wire/src/test/resources/test-spec-config.yml +++ b/wire/src/test/resources/test-spec-config.yml @@ -21,3 +21,5 @@ specHelpersOptions: blsVerify: false blsVerifyProofOfPossession: false blsSign: false + enableCache: true + From 069f44896a8a6802dbbb47109bf0f536200914d0 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 15:46:52 +0300 Subject: [PATCH 47/96] Add DebugCacheFactory --- .../beacon/util/cache/DebugCacheFactory.java | 32 +++++++++++++++++++ .../ethereum/beacon/util/cache/LRUCache.java | 5 +++ 2 files changed, 37 insertions(+) create mode 100644 util/src/main/java/org/ethereum/beacon/util/cache/DebugCacheFactory.java diff --git a/util/src/main/java/org/ethereum/beacon/util/cache/DebugCacheFactory.java b/util/src/main/java/org/ethereum/beacon/util/cache/DebugCacheFactory.java new file mode 100644 index 000000000..b7025d903 --- /dev/null +++ b/util/src/main/java/org/ethereum/beacon/util/cache/DebugCacheFactory.java @@ -0,0 +1,32 @@ +package org.ethereum.beacon.util.cache; + +import java.util.Optional; +import java.util.function.Function; + +/** + * Checks validity of cache entry on access + */ +public class DebugCacheFactory implements CacheFactory { + + @Override + public Cache createLRUCache(int capacity) { + return new Cache() { + + LRUCache cache = new LRUCache<>(capacity); + + @Override + public V get(K key, Function fallback) { + Optional cacheEntry = cache.getExisting(key); + if (cacheEntry.isPresent()) { + V goldenVal = fallback.apply(key); + if (!cacheEntry.get().equals(goldenVal)) { + throw new IllegalStateException("Cache broken: key=" + key + ", cacheEntry: " + cacheEntry.get() + ", but should be: " + goldenVal); + } + return goldenVal; + } else { + return cache.get(key, fallback); + } + } + }; + } +} diff --git a/util/src/main/java/org/ethereum/beacon/util/cache/LRUCache.java b/util/src/main/java/org/ethereum/beacon/util/cache/LRUCache.java index 4d1d94ba1..f8b71190a 100644 --- a/util/src/main/java/org/ethereum/beacon/util/cache/LRUCache.java +++ b/util/src/main/java/org/ethereum/beacon/util/cache/LRUCache.java @@ -3,6 +3,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import org.ethereum.beacon.util.cache.Cache; @@ -60,6 +61,10 @@ public V get(K key, Function fallback) { return result; } + public Optional getExisting(K key) { + return Optional.ofNullable(cacheData.get(key)); + } + public long getHits() { return hits.get(); } From 9d6bc62ae7ad52ed608ef1142334c6ca40a606c6 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 15:50:12 +0300 Subject: [PATCH 48/96] Temporarily disable cache for tests (due to invalid fork cache handling) --- wire/src/test/resources/test-spec-config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wire/src/test/resources/test-spec-config.yml b/wire/src/test/resources/test-spec-config.yml index 42ca4b738..4a0f1d189 100644 --- a/wire/src/test/resources/test-spec-config.yml +++ b/wire/src/test/resources/test-spec-config.yml @@ -21,5 +21,5 @@ specHelpersOptions: blsVerify: false blsVerifyProofOfPossession: false blsSign: false - enableCache: true + enableCache: false From e4e3da1265989ead15b027b67808369b2fe655be Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 15:56:49 +0300 Subject: [PATCH 49/96] Fix the test --- .../test/java/org/ethereum/beacon/wire/PeersTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index 06d8324d7..dcdf7e5e0 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -4,6 +4,8 @@ import java.net.SocketAddress; import java.time.Duration; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.simulator.SimulatorLauncher; @@ -151,14 +153,14 @@ public void test1() throws Exception { 1, peer1.getSchedulers().reactorEvents()); - AtomicBoolean synced = new AtomicBoolean(); + CountDownLatch syncLatch = new CountDownLatch(1); Flux.from(peer1.getBeaconChain().getBlockStatesStream()) .subscribe(s -> { System.out.println(s); if (s.getFinalState().getSlot().equals( simulatorLauncher.getSpec().getConstants().getGenesisSlot().plus(slotCount))) { syncManager.stop(); - synced.set(true); + syncLatch.countDown(); } }); @@ -173,9 +175,7 @@ public void test1() throws Exception { localhost.get(); System.out.println("Peer 1: connected to peer 0"); - - Thread.sleep(10000000); - Assert.assertTrue(synced.get()); + Assert.assertTrue(syncLatch.await(1, TimeUnit.MINUTES)); System.out.println("Done"); } } From 0a327447a6e8d50fa172100e0f26e8614824f80d Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 16:24:49 +0300 Subject: [PATCH 50/96] Add hashCode() --- .../core/operations/slashing/IndexedAttestation.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/org/ethereum/beacon/core/operations/slashing/IndexedAttestation.java b/core/src/main/java/org/ethereum/beacon/core/operations/slashing/IndexedAttestation.java index def78711b..91588490f 100644 --- a/core/src/main/java/org/ethereum/beacon/core/operations/slashing/IndexedAttestation.java +++ b/core/src/main/java/org/ethereum/beacon/core/operations/slashing/IndexedAttestation.java @@ -86,6 +86,15 @@ public boolean equals(Object o) { && Objects.equal(signature, that.signature); } + @Override + public int hashCode() { + int result = custodyBit0Indices.hashCode(); + result = 31 * result + custodyBit1Indices.hashCode(); + result = 31 * result + data.hashCode(); + result = 31 * result + signature.hashCode(); + return result; + } + @Override public String toString() { return toString(null, null); From 2903a44ca2f46e47ef631623bfa7b19509d46199 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 16:25:43 +0300 Subject: [PATCH 51/96] Extract useful TestDataFactory test util from ModelSerializerTest --- .../beacon/core/ModelsSerializeTest.java | 230 ++------------- .../beacon/core/util/TestDataFactory.java | 272 ++++++++++++++++++ .../beacon/wire/WireApiSubRouterTest.java | 24 +- 3 files changed, 307 insertions(+), 219 deletions(-) create mode 100644 core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java diff --git a/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java b/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java index 42a47e2f0..82f340994 100644 --- a/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java +++ b/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java @@ -37,6 +37,7 @@ import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.core.types.ValidatorIndex; import org.ethereum.beacon.core.util.BeaconBlockTestUtil; +import org.ethereum.beacon.core.util.TestDataFactory; import org.ethereum.beacon.crypto.Hashes; import org.ethereum.beacon.ssz.SSZBuilder; import org.ethereum.beacon.ssz.SSZSerializer; @@ -52,7 +53,8 @@ public class ModelsSerializeTest { private SSZSerializer sszSerializer; - private static SpecConstants specConstants; + private SpecConstants specConstants; + private TestDataFactory dataFactory; @Before public void setup() { @@ -60,97 +62,37 @@ public void setup() { sszSerializer = new SSZBuilder() .withExternalVarResolver(new SpecConstantsResolver(specConstants)) .buildSerializer(); - } - - public static AttestationData createAttestationData() { - AttestationData expected = - new AttestationData( - Hashes.sha256(BytesValue.fromHexString("aa")), - EpochNumber.ZERO, - Hashes.sha256(BytesValue.fromHexString("bb")), - EpochNumber.of(123), - Hashes.sha256(BytesValue.fromHexString("cc")), - ShardNumber.of(345), - Hashes.sha256(BytesValue.fromHexString("dd")), - Hash32.ZERO); - - return expected; + dataFactory = new TestDataFactory(specConstants, sszSerializer); } @Test public void attestationDataTest() { - AttestationData expected = createAttestationData(); + AttestationData expected = dataFactory.createAttestationData(); BytesValue encoded = sszSerializer.encode2(expected); AttestationData reconstructed = sszSerializer.decode(encoded, AttestationData.class); assertEquals(expected, reconstructed); } - public static Attestation createAttestation() { - return createAttestation(BytesValue.fromHexString("aa")); - } - - public static Attestation createAttestation(BytesValue someValue) { - AttestationData attestationData = createAttestationData(); - Attestation attestation = - new Attestation( - Bitfield.of(someValue), - attestationData, - Bitfield.of(BytesValue.fromHexString("bb")), - BLSSignature.wrap(Bytes96.fromHexString("cc"))); - - return attestation; - } - @Test public void attestationTest() { - Attestation expected = createAttestation(); + Attestation expected = dataFactory.createAttestation(); BytesValue encoded = sszSerializer.encode2(expected); Attestation reconstructed = sszSerializer.decode(encoded, Attestation.class); assertEquals(expected, reconstructed); } - private static DepositData createDepositData() { - DepositData depositData = - new DepositData( - BLSPubkey.wrap(Bytes48.TRUE), - Hashes.sha256(BytesValue.fromHexString("aa")), - Gwei.ZERO, BLSSignature.wrap(Bytes96.ZERO)); - - return depositData; - } - @Test public void depositDataTest() { - DepositData expected = createDepositData(); + DepositData expected = dataFactory.createDepositData(); BytesValue encoded = sszSerializer.encode2(expected); DepositData reconstructed = sszSerializer.decode(encoded, DepositData.class); assertEquals(expected, reconstructed); } - public static Deposit createDeposit1() { - Deposit deposit = new Deposit( - ReadVector.wrap( - Collections.nCopies(specConstants.getDepositContractTreeDepth().getIntValue(), Hash32.ZERO), Integer::new), - UInt64.ZERO, createDepositData()); - - return deposit; - } - - public static Deposit createDeposit2() { - ArrayList hashes = new ArrayList<>(); - hashes.add(Hashes.sha256(BytesValue.fromHexString("aa"))); - hashes.add(Hashes.sha256(BytesValue.fromHexString("bb"))); - hashes.addAll(Collections.nCopies(specConstants.getDepositContractTreeDepth().getIntValue() - hashes.size(), Hash32.ZERO)); - ReadVector proof = ReadVector.wrap(hashes, Integer::new); - Deposit deposit = new Deposit(proof, UInt64.ZERO, createDepositData()); - - return deposit; - } - @Test public void depositTest() { - Deposit expected1 = createDeposit1(); - Deposit expected2 = createDeposit2(); + Deposit expected1 = dataFactory.createDeposit1(); + Deposit expected2 = dataFactory.createDeposit2(); BytesValue encoded1 = sszSerializer.encode2(expected1); BytesValue encoded2 = sszSerializer.encode2(expected2); Deposit reconstructed1 = sszSerializer.decode(encoded1, Deposit.class); @@ -159,15 +101,9 @@ public void depositTest() { assertEquals(expected2, reconstructed2); } - public static VoluntaryExit createExit() { - VoluntaryExit voluntaryExit = new VoluntaryExit(EpochNumber.of(123), ValidatorIndex.MAX, BLSSignature.wrap(Bytes96.fromHexString("aa"))); - - return voluntaryExit; - } - @Test public void exitTest() { - VoluntaryExit expected = createExit(); + VoluntaryExit expected = dataFactory.createExit(); BytesValue encoded = sszSerializer.encode2(expected); VoluntaryExit reconstructed = sszSerializer.decode(encoded, VoluntaryExit.class); assertEquals(expected, reconstructed); @@ -182,73 +118,18 @@ public void beaconBlockHeaderTest() { assertEquals(expected, reconstructed); } - public static ProposerSlashing createProposerSlashing(Random random) { - ProposerSlashing proposerSlashing = - new ProposerSlashing( - ValidatorIndex.MAX, - BeaconBlockTestUtil.createRandomHeader(random), - BeaconBlockTestUtil.createRandomHeader(random)); - - return proposerSlashing; - } - @Test public void proposerSlashingTest() { Random random = new Random(1); - ProposerSlashing expected = createProposerSlashing(random); + ProposerSlashing expected = dataFactory.createProposerSlashing(random); BytesValue encoded = sszSerializer.encode2(expected); ProposerSlashing reconstructed = sszSerializer.decode(encoded, ProposerSlashing.class); assertEquals(expected, reconstructed); } - public static BeaconBlockBody createBeaconBlockBody() { - Random random = new Random(1); - List proposerSlashings = new ArrayList<>(); - proposerSlashings.add(createProposerSlashing(random)); - List attesterSlashings = new ArrayList<>(); - attesterSlashings.add(createAttesterSlashings()); - attesterSlashings.add(createAttesterSlashings()); - List attestations = new ArrayList<>(); - attestations.add(createAttestation()); - List deposits = new ArrayList<>(); - deposits.add(createDeposit1()); - deposits.add(createDeposit2()); - List voluntaryExits = new ArrayList<>(); - voluntaryExits.add(createExit()); - List transfers = new ArrayList<>(); - BeaconBlockBody beaconBlockBody = - BeaconBlockBody.create( - BLSSignature.ZERO, - new Eth1Data(Hash32.ZERO, UInt64.ZERO, Hash32.ZERO), - Bytes32.ZERO, - proposerSlashings, - attesterSlashings, - attestations, - deposits, - voluntaryExits, - transfers - ); - - return beaconBlockBody; - } - - public static AttesterSlashing createAttesterSlashings() { - return new AttesterSlashing( - createSlashableAttestation(), - createSlashableAttestation()); - } - - private static IndexedAttestation createSlashableAttestation() { - return new IndexedAttestation( - Arrays.asList(ValidatorIndex.of(234), ValidatorIndex.of(235)), - Arrays.asList(ValidatorIndex.of(678), ValidatorIndex.of(679)), - createAttestationData(), - BLSSignature.wrap(Bytes96.fromHexString("aa"))); - } - @Test public void slashableAttestationTest() { - IndexedAttestation expected = createSlashableAttestation(); + IndexedAttestation expected = dataFactory.createSlashableAttestation(); BytesValue encoded = sszSerializer.encode2(expected); IndexedAttestation reconstructed = sszSerializer.decode(encoded, IndexedAttestation.class); assertEquals(expected, reconstructed); @@ -256,7 +137,7 @@ public void slashableAttestationTest() { @Test public void attesterSlashingTest() { - AttesterSlashing expected = createAttesterSlashings(); + AttesterSlashing expected = dataFactory.createAttesterSlashings(); BytesValue encoded = sszSerializer.encode2(expected); AttesterSlashing reconstructed = sszSerializer.decode(encoded, AttesterSlashing.class); assertEquals(expected, reconstructed); @@ -264,45 +145,23 @@ public void attesterSlashingTest() { @Test public void beaconBlockBodyTest() { - BeaconBlockBody expected = createBeaconBlockBody(); + BeaconBlockBody expected = dataFactory.createBeaconBlockBody(); BytesValue encoded = sszSerializer.encode2(expected); BeaconBlockBody reconstructed = sszSerializer.decode(encoded, BeaconBlockBody.class); assertEquals(expected, reconstructed); } - public static BeaconBlock createBeaconBlock() { - return createBeaconBlock(BytesValue.fromHexString("aa")); - } - - public static BeaconBlock createBeaconBlock(BytesValue someValue) { - BeaconBlock beaconBlock = - new BeaconBlock( - SlotNumber.castFrom(UInt64.MAX_VALUE), - Hashes.sha256(someValue), - Hashes.sha256(BytesValue.fromHexString("bb")), - createBeaconBlockBody(), - BLSSignature.wrap(Bytes96.fromHexString("aa"))); - - return beaconBlock; - } - @Test public void beaconBlockTest() { - BeaconBlock expected = createBeaconBlock(); + BeaconBlock expected = dataFactory.createBeaconBlock(); BytesValue encoded = sszSerializer.encode2(expected); BeaconBlock reconstructed = sszSerializer.decode(encoded, BeaconBlock.class); assertEquals(expected, reconstructed); } - private BeaconState createBeaconState() { - BeaconState beaconState = BeaconState.getEmpty(); - - return beaconState; - } - @Test public void beaconStateTest() { - BeaconState expected = createBeaconState(); + BeaconState expected = dataFactory.createBeaconState(); BytesValue encoded = sszSerializer.encode2(expected); BeaconState reconstructed = sszSerializer.decode(encoded, BeaconStateImpl.class); assertEquals(expected, reconstructed); @@ -310,94 +169,49 @@ public void beaconStateTest() { @Test public void beaconStateExTest() { - BeaconState expected = createBeaconState(); + BeaconState expected = dataFactory.createBeaconState(); BeaconStateEx stateEx = new BeaconStateExImpl(expected); BytesValue encoded = sszSerializer.encode2(stateEx); BeaconState reconstructed = sszSerializer.decode(encoded, BeaconStateImpl.class); assertEquals(expected, reconstructed); } - private Crosslink createCrosslink() { - Crosslink crosslink = Crosslink.EMPTY; - - return crosslink; - } - @Test public void crosslinkTest() { - Crosslink expected = createCrosslink(); + Crosslink expected = dataFactory.createCrosslink(); BytesValue encoded = sszSerializer.encode2(expected); Crosslink reconstructed = sszSerializer.decode(encoded, Crosslink.class); assertEquals(expected, reconstructed); } - private Eth1DataVote createEth1DataVote() { - Eth1DataVote eth1DataVote = new Eth1DataVote(Eth1Data.EMPTY, UInt64.MAX_VALUE); - - return eth1DataVote; - } - @Test public void eth1DataVoteTest() { - Eth1DataVote expected = createEth1DataVote(); + Eth1DataVote expected = dataFactory.createEth1DataVote(); BytesValue encoded = sszSerializer.encode2(expected); Eth1DataVote reconstructed = sszSerializer.decode(encoded, Eth1DataVote.class); assertEquals(expected, reconstructed); } - private Fork createFork() { - Fork fork = Fork.EMPTY; - - return fork; - } - @Test public void forkTest() { - Fork expected = createFork(); + Fork expected = dataFactory.createFork(); BytesValue encoded = sszSerializer.encode2(expected); Fork reconstructed = sszSerializer.decode(encoded, Fork.class); assertEquals(expected, reconstructed); } - private PendingAttestation createPendingAttestation() { - PendingAttestation pendingAttestation = - new PendingAttestation( - Bitfield.of(BytesValue.fromHexString("aa")), - createAttestationData(), - SlotNumber.ZERO, - ValidatorIndex.ZERO); - - return pendingAttestation; - } - @Test public void pendingAttestationTest() { - PendingAttestation expected = createPendingAttestation(); + PendingAttestation expected = dataFactory.createPendingAttestation(); BytesValue encoded = sszSerializer.encode2(expected); PendingAttestation reconstructed = sszSerializer.decode(encoded, PendingAttestation.class); assertEquals(expected, reconstructed); } - private ValidatorRecord createValidatorRecord() { - ValidatorRecord validatorRecord = - ValidatorRecord.Builder.fromDepositData(createDepositData()) - .withPubKey(BLSPubkey.ZERO) - .withWithdrawalCredentials(Hash32.ZERO) - .withActivationEligibilityEpoch(EpochNumber.ZERO) - .withActivationEpoch(EpochNumber.ZERO) - .withExitEpoch(EpochNumber.ZERO) - .withWithdrawableEpoch(EpochNumber.ZERO) - .withSlashed(Boolean.FALSE) - .withEffectiveBalance(Gwei.ZERO) - .build(); - - return validatorRecord; - } - @Test public void validatorRecordTest() { - ValidatorRecord expected = createValidatorRecord(); + ValidatorRecord expected = dataFactory.createValidatorRecord(); BytesValue encoded = sszSerializer.encode2(expected); ValidatorRecord reconstructed = sszSerializer.decode(encoded, ValidatorRecord.class); assertEquals(expected, reconstructed); diff --git a/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java b/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java new file mode 100644 index 000000000..a4ed34b33 --- /dev/null +++ b/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java @@ -0,0 +1,272 @@ +package org.ethereum.beacon.core.util; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.consensus.BeaconStateEx; +import org.ethereum.beacon.consensus.transition.BeaconStateExImpl; +import org.ethereum.beacon.core.BeaconBlock; +import org.ethereum.beacon.core.BeaconBlockBody; +import org.ethereum.beacon.core.BeaconBlockHeader; +import org.ethereum.beacon.core.BeaconState; +import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.core.operations.Deposit; +import org.ethereum.beacon.core.operations.ProposerSlashing; +import org.ethereum.beacon.core.operations.Transfer; +import org.ethereum.beacon.core.operations.VoluntaryExit; +import org.ethereum.beacon.core.operations.attestation.AttestationData; +import org.ethereum.beacon.core.operations.attestation.Crosslink; +import org.ethereum.beacon.core.operations.deposit.DepositData; +import org.ethereum.beacon.core.operations.slashing.AttesterSlashing; +import org.ethereum.beacon.core.operations.slashing.IndexedAttestation; +import org.ethereum.beacon.core.spec.SpecConstants; +import org.ethereum.beacon.core.spec.SpecConstantsResolver; +import org.ethereum.beacon.core.state.BeaconStateImpl; +import org.ethereum.beacon.core.state.Eth1Data; +import org.ethereum.beacon.core.state.Eth1DataVote; +import org.ethereum.beacon.core.state.Fork; +import org.ethereum.beacon.core.state.PendingAttestation; +import org.ethereum.beacon.core.state.ValidatorRecord; +import org.ethereum.beacon.core.types.BLSPubkey; +import org.ethereum.beacon.core.types.BLSSignature; +import org.ethereum.beacon.core.types.Bitfield; +import org.ethereum.beacon.core.types.EpochNumber; +import org.ethereum.beacon.core.types.Gwei; +import org.ethereum.beacon.core.types.ShardNumber; +import org.ethereum.beacon.core.types.SlotNumber; +import org.ethereum.beacon.core.types.ValidatorIndex; +import org.ethereum.beacon.crypto.Hashes; +import org.ethereum.beacon.ssz.SSZBuilder; +import org.ethereum.beacon.ssz.SSZSerializer; +import org.junit.Test; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.bytes.Bytes32; +import tech.pegasys.artemis.util.bytes.Bytes48; +import tech.pegasys.artemis.util.bytes.Bytes96; +import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.collections.ReadVector; +import tech.pegasys.artemis.util.uint.UInt64; + +public class TestDataFactory { + private SpecConstants specConstants; + private SSZSerializer sszSerializer; + + public TestDataFactory() { + this(BeaconChainSpec.DEFAULT_CONSTANTS); + } + + public TestDataFactory(SpecConstants specConstants) { + this(specConstants, new SSZBuilder() + .withExternalVarResolver(new SpecConstantsResolver(specConstants)) + .buildSerializer()); + } + + public TestDataFactory(SpecConstants specConstants, + SSZSerializer sszSerializer) { + this.specConstants = specConstants; + this.sszSerializer = sszSerializer; + } + + public AttestationData createAttestationData() { + AttestationData expected = + new AttestationData( + Hashes.sha256(BytesValue.fromHexString("aa")), + EpochNumber.ZERO, + Hashes.sha256(BytesValue.fromHexString("bb")), + EpochNumber.of(123), + Hashes.sha256(BytesValue.fromHexString("cc")), + ShardNumber.of(345), + Hashes.sha256(BytesValue.fromHexString("dd")), + Hash32.ZERO); + + return expected; + } + + public Attestation createAttestation() { + return createAttestation(BytesValue.fromHexString("aa")); + } + + public Attestation createAttestation(BytesValue someValue) { + AttestationData attestationData = createAttestationData(); + Attestation attestation = + new Attestation( + Bitfield.of(someValue), + attestationData, + Bitfield.of(BytesValue.fromHexString("bb")), + BLSSignature.wrap(Bytes96.fromHexString("cc"))); + + return attestation; + } + + public DepositData createDepositData() { + DepositData depositData = + new DepositData( + BLSPubkey.wrap(Bytes48.TRUE), + Hashes.sha256(BytesValue.fromHexString("aa")), + Gwei.ZERO, BLSSignature.wrap(Bytes96.ZERO)); + + return depositData; + } + + public Deposit createDeposit1() { + Deposit deposit = new Deposit( + ReadVector.wrap( + Collections.nCopies(specConstants.getDepositContractTreeDepth().getIntValue(), Hash32.ZERO), Integer::new), + UInt64.ZERO, createDepositData()); + + return deposit; + } + + public Deposit createDeposit2() { + ArrayList hashes = new ArrayList<>(); + hashes.add(Hashes.sha256(BytesValue.fromHexString("aa"))); + hashes.add(Hashes.sha256(BytesValue.fromHexString("bb"))); + hashes.addAll(Collections.nCopies(specConstants.getDepositContractTreeDepth().getIntValue() - hashes.size(), Hash32.ZERO)); + ReadVector proof = ReadVector.wrap(hashes, Integer::new); + Deposit deposit = new Deposit(proof, UInt64.ZERO, createDepositData()); + + return deposit; + } + + public VoluntaryExit createExit() { + VoluntaryExit voluntaryExit = new VoluntaryExit(EpochNumber.of(123), ValidatorIndex.MAX, BLSSignature.wrap(Bytes96.fromHexString("aa"))); + + return voluntaryExit; + } + + public ProposerSlashing createProposerSlashing(Random random) { + ProposerSlashing proposerSlashing = + new ProposerSlashing( + ValidatorIndex.MAX, + BeaconBlockTestUtil.createRandomHeader(random), + BeaconBlockTestUtil.createRandomHeader(random)); + + return proposerSlashing; + } + + public BeaconBlockBody createBeaconBlockBody() { + Random random = new Random(1); + List proposerSlashings = new ArrayList<>(); + proposerSlashings.add(createProposerSlashing(random)); + List attesterSlashings = new ArrayList<>(); + attesterSlashings.add(createAttesterSlashings()); + attesterSlashings.add(createAttesterSlashings()); + List attestations = new ArrayList<>(); + attestations.add(createAttestation()); + List deposits = new ArrayList<>(); + deposits.add(createDeposit1()); + deposits.add(createDeposit2()); + List voluntaryExits = new ArrayList<>(); + voluntaryExits.add(createExit()); + List transfers = new ArrayList<>(); + BeaconBlockBody beaconBlockBody = + BeaconBlockBody.create( + BLSSignature.ZERO, + new Eth1Data(Hash32.ZERO, UInt64.ZERO, Hash32.ZERO), + Bytes32.ZERO, + proposerSlashings, + attesterSlashings, + attestations, + deposits, + voluntaryExits, + transfers + ); + + return beaconBlockBody; + } + + public AttesterSlashing createAttesterSlashings() { + return new AttesterSlashing( + createSlashableAttestation(), + createSlashableAttestation()); + } + + public IndexedAttestation createSlashableAttestation() { + return new IndexedAttestation( + Arrays.asList(ValidatorIndex.of(234), ValidatorIndex.of(235)), + Arrays.asList(ValidatorIndex.of(678), ValidatorIndex.of(679)), + createAttestationData(), + BLSSignature.wrap(Bytes96.fromHexString("aa"))); + } + + public BeaconBlock createBeaconBlock() { + return createBeaconBlock(BytesValue.fromHexString("aa")); + } + + public BeaconBlock createBeaconBlock(BytesValue someValue) { + BeaconBlock beaconBlock = + new BeaconBlock( + SlotNumber.castFrom(UInt64.MAX_VALUE), + Hashes.sha256(someValue), + Hashes.sha256(BytesValue.fromHexString("bb")), + createBeaconBlockBody(), + BLSSignature.wrap(Bytes96.fromHexString("aa"))); + + return beaconBlock; + } + + public BeaconState createBeaconState() { + BeaconState beaconState = BeaconState.getEmpty(); + + return beaconState; + } + + @Test + public void beaconStateExTest() { + BeaconState expected = createBeaconState(); + BeaconStateEx stateEx = new BeaconStateExImpl(expected); + BytesValue encoded = sszSerializer.encode2(stateEx); + BeaconState reconstructed = sszSerializer.decode(encoded, BeaconStateImpl.class); + assertEquals(expected, reconstructed); + } + + public Crosslink createCrosslink() { + Crosslink crosslink = Crosslink.EMPTY; + + return crosslink; + } + + public Eth1DataVote createEth1DataVote() { + Eth1DataVote eth1DataVote = new Eth1DataVote(Eth1Data.EMPTY, UInt64.MAX_VALUE); + + return eth1DataVote; + } + + public Fork createFork() { + Fork fork = Fork.EMPTY; + + return fork; + } + + public PendingAttestation createPendingAttestation() { + PendingAttestation pendingAttestation = + new PendingAttestation( + Bitfield.of(BytesValue.fromHexString("aa")), + createAttestationData(), + SlotNumber.ZERO, + ValidatorIndex.ZERO); + + return pendingAttestation; + } + + public ValidatorRecord createValidatorRecord() { + ValidatorRecord validatorRecord = + ValidatorRecord.Builder.fromDepositData(createDepositData()) + .withPubKey(BLSPubkey.ZERO) + .withWithdrawalCredentials(Hash32.ZERO) + .withActivationEligibilityEpoch(EpochNumber.ZERO) + .withActivationEpoch(EpochNumber.ZERO) + .withExitEpoch(EpochNumber.ZERO) + .withWithdrawableEpoch(EpochNumber.ZERO) + .withSlashed(Boolean.FALSE) + .withEffectiveBalance(Gwei.ZERO) + .build(); + + return validatorRecord; + } +} diff --git a/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java b/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java index 2cc1613e4..769cc5b4c 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/WireApiSubRouterTest.java @@ -1,6 +1,5 @@ package org.ethereum.beacon.wire; -import static org.ethereum.beacon.core.ModelsSerializeTest.createBeaconBlock; import static tech.pegasys.artemis.util.bytes.BytesValue.fromHexString; import java.util.ArrayList; @@ -8,6 +7,7 @@ import java.util.List; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; +import org.ethereum.beacon.core.util.TestDataFactory; import org.ethereum.beacon.wire.WireApiSubRouterTest.TestRouter.Connection; import org.ethereum.beacon.wire.channel.beacon.WireApiSubAdapter; import org.junit.Assert; @@ -72,38 +72,39 @@ void clear() { @Test public void test1() { + TestDataFactory dataFactory = new TestDataFactory(); TestRouter router1 = new TestRouter(); - router1.router.sendProposedBlock(createBeaconBlock()); + router1.router.sendProposedBlock(dataFactory.createBeaconBlock()); Assert.assertTrue(router1.receivedBlocks.isEmpty()); TestRouter router2 = new TestRouter(); Connection connection1 = router1.connect(router2); - router1.router.sendProposedBlock(createBeaconBlock(fromHexString("01"))); + router1.router.sendProposedBlock(dataFactory.createBeaconBlock(fromHexString("01"))); Assert.assertTrue(router1.receivedBlocks.isEmpty()); Assert.assertEquals(1, router2.receivedBlocks.size()); - router1.router.sendProposedBlock(createBeaconBlock(fromHexString("02"))); + router1.router.sendProposedBlock(dataFactory.createBeaconBlock(fromHexString("02"))); Assert.assertTrue(router1.receivedBlocks.isEmpty()); Assert.assertEquals(2, router2.receivedBlocks.size()); - router1.router.sendProposedBlock(createBeaconBlock(fromHexString("01"))); + router1.router.sendProposedBlock(dataFactory.createBeaconBlock(fromHexString("01"))); Assert.assertTrue(router1.receivedBlocks.isEmpty()); Assert.assertEquals(2, router2.receivedBlocks.size()); - connection1.outerApi2.sendProposedBlock(createBeaconBlock(fromHexString("01"))); + connection1.outerApi2.sendProposedBlock(dataFactory.createBeaconBlock(fromHexString("01"))); Assert.assertEquals(2, router2.receivedBlocks.size()); connection1.disconnect(); router2.clear(); - router1.router.sendProposedBlock(createBeaconBlock(fromHexString("03"))); + router1.router.sendProposedBlock(dataFactory.createBeaconBlock(fromHexString("03"))); Assert.assertTrue(router1.receivedBlocks.isEmpty()); Assert.assertTrue(router2.receivedBlocks.isEmpty()); Connection connection2 = router1.connect(router2); - router1.router.sendProposedBlock(createBeaconBlock(fromHexString("04"))); + router1.router.sendProposedBlock(dataFactory.createBeaconBlock(fromHexString("04"))); Assert.assertTrue(router1.receivedBlocks.isEmpty()); Assert.assertEquals(1, router2.receivedBlocks.size()); @@ -112,7 +113,7 @@ public void test1() { TestRouter router3 = new TestRouter(); Connection connection3 = router2.connect(router3); - router1.router.sendProposedBlock(createBeaconBlock(fromHexString("05"))); + router1.router.sendProposedBlock(dataFactory.createBeaconBlock(fromHexString("05"))); Assert.assertTrue(router1.receivedBlocks.isEmpty()); Assert.assertEquals(1, router2.receivedBlocks.size()); Assert.assertEquals(1, router3.receivedBlocks.size()); @@ -120,8 +121,9 @@ public void test1() { @Test public void testMisc() { - BeaconBlock b1 = createBeaconBlock(fromHexString("01")); - BeaconBlock b2 = createBeaconBlock(fromHexString("01")); + TestDataFactory dataFactory = new TestDataFactory(); + BeaconBlock b1 = dataFactory.createBeaconBlock(fromHexString("01")); + BeaconBlock b2 = dataFactory.createBeaconBlock(fromHexString("01")); Assert.assertTrue(b1.equals(b2)); Assert.assertEquals(b1.hashCode(), b2.hashCode()); HashSet set = new HashSet<>(); From 9d6ac8585d1c5d2706de7915a7fa193492807bbd Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 16:26:25 +0300 Subject: [PATCH 52/96] Close server on test complete --- .../org/ethereum/beacon/wire/net/Server.java | 7 +- .../org/ethereum/beacon/wire/NodeTest.java | 185 ++++++++-------- .../org/ethereum/beacon/wire/PeersTest.java | 204 +++++++++--------- 3 files changed, 202 insertions(+), 194 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java b/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java index 71e6aa028..ec44b95f3 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java @@ -5,11 +5,16 @@ import org.reactivestreams.Publisher; import tech.pegasys.artemis.util.bytes.BytesValue; -public interface Server { +public interface Server extends AutoCloseable { Publisher> channelsStream(); ChannelFuture start(); void stop(); + + @Override + default void close() throws Exception { + stop(); + } } diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java index 614f8b48d..4f95094bf 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -50,7 +50,7 @@ public class NodeTest { @Test - public void test1() throws ExecutionException, InterruptedException { + public void test1() throws Exception { Random rnd = new Random(); ConfigBuilder specConfigBuilder = @@ -77,103 +77,104 @@ public void test1() throws ExecutionException, InterruptedException { new DepositContract.ChainStart(genesisTime, eth1Data, depositPairs.getValue0()); SimpleDepositContract depositContract = new SimpleDepositContract(chainStart); - // master node with all validators - NodeLauncher masterNode; - { - ControlledSchedulers schedulers = controlledSchedulers.createNew("master"); - NettyServer nettyServer = new NettyServer(40001); - nettyServer.start(); - ConnectionManager connectionManager = new ConnectionManager<>( - nettyServer, null, schedulers.reactorEvents()); - masterNode = new NodeLauncher( - specBuilder.buildSpec(), - depositContract, - depositPairs - .getValue1() - .stream() - .map(BLS381Credentials::createWithDummySigner) - .collect(Collectors.toList()), - connectionManager, - new MemBeaconChainStorageFactory(spec.getObjectHasher()), - schedulers, - false); - } - - // generate some blocks - controlledSchedulers.addTime(Duration.ofSeconds(64 * 10)); - - // slave node - ConnectionManager slaveConnectionManager; - CompletableFuture> connectFut; - NodeLauncher slaveNode; - { - ControlledSchedulers schedulers = controlledSchedulers.createNew("slave"); - NettyClient nettyClient = new NettyClient(); - slaveConnectionManager = new ConnectionManager<>( - null, nettyClient, schedulers.reactorEvents()); - slaveNode = new NodeLauncher( - specBuilder.buildSpec(), - depositContract, - null, - slaveConnectionManager, - new MemBeaconChainStorageFactory(spec.getObjectHasher()), - schedulers, - true); - connectFut = slaveConnectionManager + try (NettyServer nettyServer = new NettyServer(40001)) { + // master node with all validators + NodeLauncher masterNode; + { + ControlledSchedulers schedulers = controlledSchedulers.createNew("master"); + nettyServer.start(); + ConnectionManager connectionManager = new ConnectionManager<>( + nettyServer, null, schedulers.reactorEvents()); + masterNode = new NodeLauncher( + specBuilder.buildSpec(), + depositContract, + depositPairs + .getValue1() + .stream() + .map(BLS381Credentials::createWithDummySigner) + .collect(Collectors.toList()), + connectionManager, + new MemBeaconChainStorageFactory(spec.getObjectHasher()), + schedulers, + false); + } + + // generate some blocks + controlledSchedulers.addTime(Duration.ofSeconds(64 * 10)); + + // slave node + ConnectionManager slaveConnectionManager; + CompletableFuture> connectFut; + NodeLauncher slaveNode; + { + ControlledSchedulers schedulers = controlledSchedulers.createNew("slave"); + NettyClient nettyClient = new NettyClient(); + slaveConnectionManager = new ConnectionManager<>( + null, nettyClient, schedulers.reactorEvents()); + slaveNode = new NodeLauncher( + specBuilder.buildSpec(), + depositContract, + null, + slaveConnectionManager, + new MemBeaconChainStorageFactory(spec.getObjectHasher()), + schedulers, + true); + connectFut = slaveConnectionManager + .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + System.out.println("Connected! " + connectFut.get()); + } + + Assert.assertEquals( + SyncMode.Long, + Mono.from(slaveNode.getSyncManager().getSyncModeStream()).block(Duration.ZERO)); + + // generate some new blocks + System.out.println("Generating online blocks"); + controlledSchedulers.addTime(Duration.ofSeconds(3 * 10)); + + Flux.from(slaveNode.getSyncManager().getSyncModeStream()) + .filter(mode -> mode == SyncMode.Short) + .blockFirst(Duration.ofSeconds(30)); + + // 'realtime' mode + System.out.println("Some time in 'realtime' mode..."); + for (int i = 0; i < 50; i++) { + controlledSchedulers.addTime(Duration.ofSeconds(1)); + Thread.sleep(50); + } + + // disconnecting slave + System.out.println("Disconnecting slave"); + connectFut.get().close(); + + // generate new blocks on master + System.out.println("Generate new blocks on master"); + controlledSchedulers.addTime(Duration.ofSeconds(32 * 10)); + + // connect the slave again + System.out.println("Connect the slave again"); + CompletableFuture> connectFut1 = slaveConnectionManager .connect(InetSocketAddress.createUnresolved("localhost", 40001)); - System.out.println("Connected! " + connectFut.get()); - } + connectFut1.get(); + System.out.println("Slave connected"); - Assert.assertEquals( - SyncMode.Long, - Mono.from(slaveNode.getSyncManager().getSyncModeStream()).block(Duration.ZERO)); + System.out.println("Generating online blocks"); + controlledSchedulers.addTime(Duration.ofSeconds(10 * 10)); - // generate some new blocks - System.out.println("Generating online blocks"); - controlledSchedulers.addTime(Duration.ofSeconds(3 * 10)); + Flux.from(slaveNode.getSyncManager().getSyncModeStream()) + .filter(mode -> mode == SyncMode.Long) + .blockFirst(Duration.ofSeconds(30)); - Flux.from(slaveNode.getSyncManager().getSyncModeStream()) - .filter(mode -> mode == SyncMode.Short) - .blockFirst(Duration.ofSeconds(30)); + System.out.println("Some time in 'realtime' mode..."); + for (int i = 0; i < 50; i++) { + controlledSchedulers.addTime(Duration.ofSeconds(1)); + Thread.sleep(50); + } - // 'realtime' mode - System.out.println("Some time in 'realtime' mode..."); - for (int i = 0; i < 50; i++) { - controlledSchedulers.addTime(Duration.ofSeconds(1)); - Thread.sleep(50); + Flux.from(slaveNode.getSyncManager().getSyncModeStream()) + .filter(mode -> mode == SyncMode.Short) + .blockFirst(Duration.ofSeconds(30)); } - - // disconnecting slave - System.out.println("Disconnecting slave"); - connectFut.get().close(); - - // generate new blocks on master - System.out.println("Generate new blocks on master"); - controlledSchedulers.addTime(Duration.ofSeconds(32 * 10)); - - // connect the slave again - System.out.println("Connect the slave again"); - CompletableFuture> connectFut1 = slaveConnectionManager - .connect(InetSocketAddress.createUnresolved("localhost", 40001)); - connectFut1.get(); - System.out.println("Slave connected"); - - System.out.println("Generating online blocks"); - controlledSchedulers.addTime(Duration.ofSeconds(10 * 10)); - - Flux.from(slaveNode.getSyncManager().getSyncModeStream()) - .filter(mode -> mode == SyncMode.Long) - .blockFirst(Duration.ofSeconds(30)); - - System.out.println("Some time in 'realtime' mode..."); - for (int i = 0; i < 50; i++) { - controlledSchedulers.addTime(Duration.ofSeconds(1)); - Thread.sleep(50); - } - - Flux.from(slaveNode.getSyncManager().getSyncModeStream()) - .filter(mode -> mode == SyncMode.Short) - .blockFirst(Duration.ofSeconds(30)); } public static Integer getValue() { diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index dcdf7e5e0..52e0f4a86 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -76,107 +76,109 @@ public void test1() throws Exception { Launcher peer1 = simulatorLauncher.createPeer("test"); - { - // peer 0 - Server server = new NettyServer(40001); - server.start().await(); - System.out.println("Peer 0 listening on port 40001"); - ConnectionManager connectionManager = new ConnectionManager<>( - server, null, Schedulers.single()); - - SSZSerializer ssz = new SSZBuilder().buildSerializer(); - MessageSerializer messageSerializer = new SSZMessageSerializer(ssz); - WireApiSyncServer syncServer = new WireApiSyncServer(peer0.getBeaconChainStorage()); - SimplePeerManagerImpl peerManager = new SimplePeerManagerImpl( - (byte) 1, - UInt64.valueOf(1), - connectionManager.channelsStream(), - ssz, - peer0.getSpec(), - messageSerializer, - peer0.getSchedulers(), - syncServer, - peer0.getBeaconChain().getBlockStatesStream()); - - Flux.from(peerManager.connectedPeerStream()) - .subscribe( - peer -> { - System.out.println("Remote peer connected: " + peer); - Flux.from(peer.getRawChannel().inboundMessageStream()) - .doOnError(e -> System.out.println("#### Error: " + e)) - .doOnComplete(() -> System.out.println("#### Complete")) - .doOnNext(msg -> System.out.println("#### on message")) - .subscribe(); - }); - Flux.from(peerManager.activePeerStream()) - .subscribe(peer -> System.out.println("Remote peer active: " + peer)); - Flux.from(peerManager.disconnectedPeerStream()) - .subscribe(peer -> System.out.println("Remote peer disconnected: " + peer)); - System.out.println("Peer 0 is ready."); - } - - { - // peer 1 - ConnectionManager connectionManager = new ConnectionManager<>( - null, new NettyClient(), Schedulers.single()); - - SSZSerializer ssz = new SSZBuilder().buildSerializer(); - MessageSerializer messageSerializer = new SSZMessageSerializer(ssz); - SimplePeerManagerImpl peerManager = new SimplePeerManagerImpl( - (byte) 1, - UInt64.valueOf(1), - connectionManager.channelsStream(), - ssz, - peer1.getSpec(), - messageSerializer, - peer0.getSchedulers(), - null, - peer1.getBeaconChain().getBlockStatesStream()); - - Flux.from(peerManager.connectedPeerStream()) - .subscribe(peer -> System.out.println("Peer 1 connected: " + peer)); - Flux.from(peerManager.activePeerStream()) - .subscribe(peer -> System.out.println("Peer 1 active: " + peer)); - Flux.from(peerManager.disconnectedPeerStream()) - .subscribe(peer -> System.out.println("Peer 1 disconnected: " + peer)); - - BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); - SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); - - SyncManagerImpl syncManager = new SyncManagerImpl( - peer1.getBeaconChain(), - Flux.from(peerManager.getWireApiSub().inboundBlocksStream()).map(Feedback::of), - peer1.getBeaconChainStorage(), - peer1.getSpec(), - peerManager.getWireApiSync(), - syncQueue, - 1, - peer1.getSchedulers().reactorEvents()); - - CountDownLatch syncLatch = new CountDownLatch(1); - Flux.from(peer1.getBeaconChain().getBlockStatesStream()) - .subscribe(s -> { - System.out.println(s); - if (s.getFinalState().getSlot().equals( - simulatorLauncher.getSpec().getConstants().getGenesisSlot().plus(slotCount))) { - syncManager.stop(); - syncLatch.countDown(); - } - }); - - System.out.println("Peer 1: starting sync manager"); - syncManager.start(); - - // simulatorLauncher.getControlledSchedulers().addTime(3000); - - System.out.println("Peer 1: connecting to peer 0 for syncing..."); - CompletableFuture> localhost = connectionManager - .connect(InetSocketAddress.createUnresolved("localhost", 40001)); - localhost.get(); - System.out.println("Peer 1: connected to peer 0"); - - Assert.assertTrue(syncLatch.await(1, TimeUnit.MINUTES)); - System.out.println("Done"); + try (Server server = new NettyServer(40001)) { + { + // peer 0 + server.start().await(); + System.out.println("Peer 0 listening on port 40001"); + ConnectionManager connectionManager = new ConnectionManager<>( + server, null, Schedulers.single()); + + SSZSerializer ssz = new SSZBuilder().buildSerializer(); + MessageSerializer messageSerializer = new SSZMessageSerializer(ssz); + WireApiSyncServer syncServer = new WireApiSyncServer(peer0.getBeaconChainStorage()); + SimplePeerManagerImpl peerManager = new SimplePeerManagerImpl( + (byte) 1, + UInt64.valueOf(1), + connectionManager.channelsStream(), + ssz, + peer0.getSpec(), + messageSerializer, + peer0.getSchedulers(), + syncServer, + peer0.getBeaconChain().getBlockStatesStream()); + + Flux.from(peerManager.connectedPeerStream()) + .subscribe( + peer -> { + System.out.println("Remote peer connected: " + peer); + Flux.from(peer.getRawChannel().inboundMessageStream()) + .doOnError(e -> System.out.println("#### Error: " + e)) + .doOnComplete(() -> System.out.println("#### Complete")) + .doOnNext(msg -> System.out.println("#### on message")) + .subscribe(); + }); + Flux.from(peerManager.activePeerStream()) + .subscribe(peer -> System.out.println("Remote peer active: " + peer)); + Flux.from(peerManager.disconnectedPeerStream()) + .subscribe(peer -> System.out.println("Remote peer disconnected: " + peer)); + System.out.println("Peer 0 is ready."); + } + + { + // peer 1 + ConnectionManager connectionManager = new ConnectionManager<>( + null, new NettyClient(), Schedulers.single()); + + SSZSerializer ssz = new SSZBuilder().buildSerializer(); + MessageSerializer messageSerializer = new SSZMessageSerializer(ssz); + SimplePeerManagerImpl peerManager = new SimplePeerManagerImpl( + (byte) 1, + UInt64.valueOf(1), + connectionManager.channelsStream(), + ssz, + peer1.getSpec(), + messageSerializer, + peer0.getSchedulers(), + null, + peer1.getBeaconChain().getBlockStatesStream()); + + Flux.from(peerManager.connectedPeerStream()) + .subscribe(peer -> System.out.println("Peer 1 connected: " + peer)); + Flux.from(peerManager.activePeerStream()) + .subscribe(peer -> System.out.println("Peer 1 active: " + peer)); + Flux.from(peerManager.disconnectedPeerStream()) + .subscribe(peer -> System.out.println("Peer 1 disconnected: " + peer)); + + BeaconBlockTree blockTree = new BeaconBlockTree( + simulatorLauncher.getSpec().getObjectHasher()); + SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); + + SyncManagerImpl syncManager = new SyncManagerImpl( + peer1.getBeaconChain(), + Flux.from(peerManager.getWireApiSub().inboundBlocksStream()).map(Feedback::of), + peer1.getBeaconChainStorage(), + peer1.getSpec(), + peerManager.getWireApiSync(), + syncQueue, + 1, + peer1.getSchedulers().reactorEvents()); + + CountDownLatch syncLatch = new CountDownLatch(1); + Flux.from(peer1.getBeaconChain().getBlockStatesStream()) + .subscribe(s -> { + System.out.println(s); + if (s.getFinalState().getSlot().equals( + simulatorLauncher.getSpec().getConstants().getGenesisSlot().plus(slotCount))) { + syncManager.stop(); + syncLatch.countDown(); + } + }); + + System.out.println("Peer 1: starting sync manager"); + syncManager.start(); + + // simulatorLauncher.getControlledSchedulers().addTime(3000); + + System.out.println("Peer 1: connecting to peer 0 for syncing..."); + CompletableFuture> localhost = connectionManager + .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + localhost.get(); + System.out.println("Peer 1: connected to peer 0"); + + Assert.assertTrue(syncLatch.await(1, TimeUnit.MINUTES)); + System.out.println("Done"); + } } } } From 52ba7666e546976982f05020ade3e22981794e00 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 16:26:39 +0300 Subject: [PATCH 53/96] Remove obsolete test --- wire/src/test/java/org/ethereum/beacon/wire/RxTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/wire/src/test/java/org/ethereum/beacon/wire/RxTest.java b/wire/src/test/java/org/ethereum/beacon/wire/RxTest.java index b9dfdadaa..bdcdef3fe 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/RxTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/RxTest.java @@ -43,10 +43,4 @@ public void test1() { test.expectNext(new ArrayList<>(asList(1, 3))); } - - @Test - public void test2() { - Flux.fromIterable(Collections.emptyList()).repeat().subscribe(e -> System.out.println("a")); - System.out.println("Complete"); - } } From c51831f874309bf201f162bd5198bb09052185a7 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 16:27:10 +0300 Subject: [PATCH 54/96] Disable specHelpers cache for tests due to incorrect fork handling --- wire/src/test/resources/sync-simulation-config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/wire/src/test/resources/sync-simulation-config.yml b/wire/src/test/resources/sync-simulation-config.yml index 10911a81d..737fe5e13 100644 --- a/wire/src/test/resources/sync-simulation-config.yml +++ b/wire/src/test/resources/sync-simulation-config.yml @@ -34,3 +34,4 @@ chainSpec: blsVerify: false blsVerifyProofOfPossession: false blsSign: false + enableCache: false From b129310650bb9b14be3c25034aaffc3e321eea95 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 16:30:22 +0300 Subject: [PATCH 55/96] Removed @Test from the test util class --- .../ethereum/beacon/core/util/TestDataFactory.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java b/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java index a4ed34b33..b69cec4db 100644 --- a/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java +++ b/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java @@ -43,7 +43,7 @@ import org.ethereum.beacon.crypto.Hashes; import org.ethereum.beacon.ssz.SSZBuilder; import org.ethereum.beacon.ssz.SSZSerializer; -import org.junit.Test; +import org.junit.Ignore; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.bytes.Bytes32; import tech.pegasys.artemis.util.bytes.Bytes48; @@ -52,6 +52,7 @@ import tech.pegasys.artemis.util.collections.ReadVector; import tech.pegasys.artemis.util.uint.UInt64; + public class TestDataFactory { private SpecConstants specConstants; private SSZSerializer sszSerializer; @@ -216,15 +217,6 @@ public BeaconState createBeaconState() { return beaconState; } - @Test - public void beaconStateExTest() { - BeaconState expected = createBeaconState(); - BeaconStateEx stateEx = new BeaconStateExImpl(expected); - BytesValue encoded = sszSerializer.encode2(stateEx); - BeaconState reconstructed = sszSerializer.decode(encoded, BeaconStateImpl.class); - assertEquals(expected, reconstructed); - } - public Crosslink createCrosslink() { Crosslink crosslink = Crosslink.EMPTY; From feffd7651c15910a82c6baae48fbddacb6941f52 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 16:32:57 +0300 Subject: [PATCH 56/96] Remove obsolete test util field --- .../beacon/core/util/TestDataFactory.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java b/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java index b69cec4db..20675c5b2 100644 --- a/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java +++ b/core/src/test/java/org/ethereum/beacon/core/util/TestDataFactory.java @@ -1,18 +1,13 @@ package org.ethereum.beacon.core.util; -import static org.junit.Assert.assertEquals; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; import org.ethereum.beacon.consensus.BeaconChainSpec; -import org.ethereum.beacon.consensus.BeaconStateEx; -import org.ethereum.beacon.consensus.transition.BeaconStateExImpl; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.BeaconBlockBody; -import org.ethereum.beacon.core.BeaconBlockHeader; import org.ethereum.beacon.core.BeaconState; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.core.operations.Deposit; @@ -25,8 +20,6 @@ import org.ethereum.beacon.core.operations.slashing.AttesterSlashing; import org.ethereum.beacon.core.operations.slashing.IndexedAttestation; import org.ethereum.beacon.core.spec.SpecConstants; -import org.ethereum.beacon.core.spec.SpecConstantsResolver; -import org.ethereum.beacon.core.state.BeaconStateImpl; import org.ethereum.beacon.core.state.Eth1Data; import org.ethereum.beacon.core.state.Eth1DataVote; import org.ethereum.beacon.core.state.Fork; @@ -41,9 +34,6 @@ import org.ethereum.beacon.core.types.SlotNumber; import org.ethereum.beacon.core.types.ValidatorIndex; import org.ethereum.beacon.crypto.Hashes; -import org.ethereum.beacon.ssz.SSZBuilder; -import org.ethereum.beacon.ssz.SSZSerializer; -import org.junit.Ignore; import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.bytes.Bytes32; import tech.pegasys.artemis.util.bytes.Bytes48; @@ -55,22 +45,13 @@ public class TestDataFactory { private SpecConstants specConstants; - private SSZSerializer sszSerializer; public TestDataFactory() { this(BeaconChainSpec.DEFAULT_CONSTANTS); } public TestDataFactory(SpecConstants specConstants) { - this(specConstants, new SSZBuilder() - .withExternalVarResolver(new SpecConstantsResolver(specConstants)) - .buildSerializer()); - } - - public TestDataFactory(SpecConstants specConstants, - SSZSerializer sszSerializer) { this.specConstants = specConstants; - this.sszSerializer = sszSerializer; } public AttestationData createAttestationData() { From 4a748a1307853b683751f83d865e7b60bd298112 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 17:47:34 +0300 Subject: [PATCH 57/96] Refactor Validator config --- .../beacon/emulator/config/main/Signer.java | 45 +++++--------- .../emulator/config/main/Validator.java | 8 +-- .../emulator/config/main/ValidatorKeys.java | 59 +++++++++++++++++++ .../config/main/conract/Contract.java | 11 ++++ .../config/main/conract/EmulatorContract.java | 25 ++++++++ .../main/conract/EthereumJContract.java | 31 ++++++++++ .../emulator/config/ConfigBuilderTest.java | 12 +++- start/config/src/test/resources/config.yml | 32 ++++++---- 8 files changed, 173 insertions(+), 50 deletions(-) create mode 100644 start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java create mode 100644 start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/Contract.java create mode 100644 start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java create mode 100644 start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EthereumJContract.java diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Signer.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Signer.java index bb67b88d4..2a0cb93bd 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Signer.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Signer.java @@ -1,41 +1,24 @@ package org.ethereum.beacon.emulator.config.main; -import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.util.List; -import java.util.Map; +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = Signer.Insecure.class, name = "insecure"), +}) +public abstract class Signer { -/** Eth2.0 Signer settings */ -public class Signer { - private SignerImplementation implementation; + public static class Insecure extends Signer{ + List keys; - public SignerImplementation getImplementation() { - return implementation; - } - - public void setImplementation(SignerImplementation implementation) { - this.implementation = implementation; - } - - public static class SignerImplementation { - @JsonProperty("class") - private String clazz; - - private Map input; - - public String getClazz() { - return clazz; - } - - public void setClazz(String clazz) { - this.clazz = clazz; - } - - public Map getInput() { - return input; + public List getKeys() { + return keys; } - public void setInput(Map input) { - this.input = input; + public void setKeys(List keys) { + this.keys = keys; } } } diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Validator.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Validator.java index c9a94cdbe..800f1d181 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Validator.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Validator.java @@ -1,17 +1,17 @@ package org.ethereum.beacon.emulator.config.main; -import java.util.Map; +import org.ethereum.beacon.emulator.config.main.conract.Contract; /** Validator settings */ public class Validator { - private Map contract; + private Contract contract; private Signer signer; - public Map getContract() { + public Contract getContract() { return contract; } - public void setContract(Map contract) { + public void setContract(Contract contract) { this.contract = contract; } diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java new file mode 100644 index 000000000..00d262476 --- /dev/null +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java @@ -0,0 +1,59 @@ +package org.ethereum.beacon.emulator.config.main; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import java.util.List; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = ValidatorKeys.Generate.class, name = "generate"), + @JsonSubTypes.Type(value = ValidatorKeys.Private.class, name = "private"), + @JsonSubTypes.Type(value = ValidatorKeys.Public.class, name = "public"), +}) +public abstract class ValidatorKeys { + + public static class Generate extends ValidatorKeys { + private int count; + private int seed = 0; + private int startIndex = 0; + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public int getSeed() { + return seed; + } + + public void setSeed(int seed) { + this.seed = seed; + } + + public int getStartIndex() { + return startIndex; + } + + public void setStartIndex(int startIndex) { + this.startIndex = startIndex; + } + } + + public static class ExplicitKeys extends ValidatorKeys { + private List keys; + + public List getKeys() { + return keys; + } + + public void setKeys(List keys) { + this.keys = keys; + } + } + + public static class Private extends ExplicitKeys {} + public static class Public extends ExplicitKeys {} +} diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/Contract.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/Contract.java new file mode 100644 index 000000000..889516473 --- /dev/null +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/Contract.java @@ -0,0 +1,11 @@ +package org.ethereum.beacon.emulator.config.main.conract; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @JsonSubTypes.Type(value = EmulatorContract.class, name = "emulator"), + @JsonSubTypes.Type(value = EthereumJContract.class, name = "ethereumj"), +}) +public abstract class Contract {} diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java new file mode 100644 index 000000000..b979d7e2b --- /dev/null +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java @@ -0,0 +1,25 @@ +package org.ethereum.beacon.emulator.config.main.conract; + +import java.util.List; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys; + +public class EmulatorContract extends Contract { + Long genesisTime; + List keys; + + public Long getGenesisTime() { + return genesisTime; + } + + public void setGenesisTime(Long genesisTime) { + this.genesisTime = genesisTime; + } + + public List getKeys() { + return keys; + } + + public void setKeys(List keys) { + this.keys = keys; + } +} diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EthereumJContract.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EthereumJContract.java new file mode 100644 index 000000000..8b7c0c754 --- /dev/null +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EthereumJContract.java @@ -0,0 +1,31 @@ +package org.ethereum.beacon.emulator.config.main.conract; + +public class EthereumJContract extends Contract { + private String contractAddress; + private long contractBlock; + private String contractAbiPath; + + public String getContractAddress() { + return contractAddress; + } + + public void setContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + } + + public long getContractBlock() { + return contractBlock; + } + + public void setContractBlock(long contractBlock) { + this.contractBlock = contractBlock; + } + + public String getContractAbiPath() { + return contractAbiPath; + } + + public void setContractAbiPath(String contractAbiPath) { + this.contractAbiPath = contractAbiPath; + } +} diff --git a/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java b/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java index cfeaaa8f0..89f1ade74 100644 --- a/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java +++ b/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java @@ -7,7 +7,10 @@ import org.ethereum.beacon.emulator.config.chainspec.SpecBuilder; import org.ethereum.beacon.emulator.config.main.Configuration; import org.ethereum.beacon.emulator.config.main.MainConfig; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys; import org.ethereum.beacon.emulator.config.main.action.ActionRun; +import org.ethereum.beacon.emulator.config.main.conract.EmulatorContract; +import org.ethereum.beacon.emulator.config.main.conract.EthereumJContract; import org.ethereum.beacon.emulator.config.main.plan.GeneralPlan; import org.junit.Test; import tech.pegasys.artemis.util.uint.UInt64; @@ -16,6 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; public class ConfigBuilderTest { @@ -46,14 +50,16 @@ public void test1() { MainConfig merged = configBuilder.build(); assertEquals("file://second/path", merged.getConfig().getDb()); assertEquals(2, ((GeneralPlan) merged.getPlan()).getValidator().size()); - assertEquals("ethereumj", merged.getConfig().getValidator().getContract().get("handler")); + assertTrue(merged.getConfig().getValidator().getContract() instanceof EmulatorContract); + assertEquals(3, (long) ((ValidatorKeys.Private)((EmulatorContract) + merged.getConfig().getValidator().getContract()).getKeys().get(1)).getKeys().size()); configBuilder.addConfigOverride("config.db", "file://test-db"); - configBuilder.addConfigOverride("config.validator.contract.handler", "unknown"); + configBuilder.addConfigOverride("config.validator.contract.genesisTime", 888L); MainConfig overrided = configBuilder.build(); assertEquals("file://test-db", overrided.getConfig().getDb()); assertEquals(2, ((GeneralPlan) overrided.getPlan()).getValidator().size()); - assertEquals("unknown", overrided.getConfig().getValidator().getContract().get("handler")); + assertEquals(888, (long) ((EmulatorContract)overrided.getConfig().getValidator().getContract()).getGenesisTime()); } @Test diff --git a/start/config/src/test/resources/config.yml b/start/config/src/test/resources/config.yml index 805a2c756..fcc823de6 100644 --- a/start/config/src/test/resources/config.yml +++ b/start/config/src/test/resources/config.yml @@ -7,18 +7,26 @@ config: - tcp://localhost:40002 - tcp://localhost:40003 validator: - contract: - handler: ethereumj - distanceFromHead: 1000 - contractAddress: 0xd47c61f2c25aaa677dcf23e65765fac04c85d6a0 - contractBlock: 8000000 - contractAbi: file://Contract.abi - signer: - implementation: - class: InsecureBLS381MessageSigner - input: - pubKey: 1 - privKey: 1 + contract: !emulator + genesisTime: 7777 + keys: + - !generate + count: 16 + seed: 666 + - !private + keys: + - 0x3564c032070e518026e47b32b6d34bca57c192d56f62e41b33e4b952e7b04d7a + - 0x3564c032070e518026e47b32b6d34bca57c192d56f62e41b33e4b952e7b04d7b + - 0x3564c032070e518026e47b32b6d34bca57c192d56f62e41b33e4b952e7b04d7c + signer: !insecure + keys: + - !generate + startIndex: 15 + count: 1 + seed: 666 + - !private + keys: + - 0x3564c032070e518026e47b32b6d34bca57c192d56f62e41b33e4b952e7b04d7a plan: !general sync: From 99d331e8f070a07a7982e1fb7ebd0369a0b616ff Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 20 May 2019 20:02:34 +0300 Subject: [PATCH 58/96] Create NodeLauncher from config --- .../beacon/start/common/NodeLauncher.java | 4 +- .../config/main/conract/EmulatorContract.java | 10 +- .../emulator/config/ConfigBuilderTest.java | 6 +- start/config/src/test/resources/config.yml | 2 +- .../org/ethereum/beacon/node/ConfigUtils.java | 97 +++++++++++++ .../beacon/node/NodeCommandLauncher.java | 131 ++++++++---------- 6 files changed, 166 insertions(+), 84 deletions(-) create mode 100644 start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java diff --git a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java index 3c23f5d3b..18d336417 100644 --- a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java @@ -82,7 +82,7 @@ public class NodeLauncher { private WireApiSub wireApiSub; private WireApiSync wireApiSyncRemote; - private final ConnectionManager connectionManager; + private final ConnectionManager connectionManager; private SimplePeerManagerImpl peerManager; private BeaconBlockTree blockTree; private SyncQueue syncQueue; @@ -93,7 +93,7 @@ public NodeLauncher( BeaconChainSpec spec, DepositContract depositContract, List validatorCred, - ConnectionManager connectionManager, + ConnectionManager connectionManager, BeaconChainStorageFactory storageFactory, Schedulers schedulers, boolean startSyncManager) { diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java index b979d7e2b..0d36bfafe 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java @@ -1,17 +1,21 @@ package org.ethereum.beacon.emulator.config.main.conract; +import com.fasterxml.jackson.annotation.JsonFormat; +import java.util.Date; import java.util.List; import org.ethereum.beacon.emulator.config.main.ValidatorKeys; public class EmulatorContract extends Contract { - Long genesisTime; + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT") + private Date genesisTime; List keys; - public Long getGenesisTime() { + public Date getGenesisTime() { return genesisTime; } - public void setGenesisTime(Long genesisTime) { + public void setGenesisTime(Date genesisTime) { this.genesisTime = genesisTime; } diff --git a/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java b/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java index 89f1ade74..c5baa952f 100644 --- a/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java +++ b/start/config/src/test/java/org/ethereum/beacon/emulator/config/ConfigBuilderTest.java @@ -1,6 +1,7 @@ package org.ethereum.beacon.emulator.config; import java.util.Collections; +import java.util.Date; import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.spec.SpecConstants; import org.ethereum.beacon.emulator.config.chainspec.SpecData; @@ -53,13 +54,14 @@ public void test1() { assertTrue(merged.getConfig().getValidator().getContract() instanceof EmulatorContract); assertEquals(3, (long) ((ValidatorKeys.Private)((EmulatorContract) merged.getConfig().getValidator().getContract()).getKeys().get(1)).getKeys().size()); + assertEquals( + new Date(1526835300000L), + ((EmulatorContract) merged.getConfig().getValidator().getContract()).getGenesisTime()); configBuilder.addConfigOverride("config.db", "file://test-db"); - configBuilder.addConfigOverride("config.validator.contract.genesisTime", 888L); MainConfig overrided = configBuilder.build(); assertEquals("file://test-db", overrided.getConfig().getDb()); assertEquals(2, ((GeneralPlan) overrided.getPlan()).getValidator().size()); - assertEquals(888, (long) ((EmulatorContract)overrided.getConfig().getValidator().getContract()).getGenesisTime()); } @Test diff --git a/start/config/src/test/resources/config.yml b/start/config/src/test/resources/config.yml index fcc823de6..f7fe1bc01 100644 --- a/start/config/src/test/resources/config.yml +++ b/start/config/src/test/resources/config.yml @@ -8,7 +8,7 @@ config: - tcp://localhost:40003 validator: contract: !emulator - genesisTime: 7777 + genesisTime: 2018-05-20 16:55:00 keys: - !generate count: 16 diff --git a/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java b/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java new file mode 100644 index 000000000..a5af0edf5 --- /dev/null +++ b/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java @@ -0,0 +1,97 @@ +package org.ethereum.beacon.node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; +import org.ethereum.beacon.consensus.BeaconChainSpec; +import org.ethereum.beacon.core.operations.Deposit; +import org.ethereum.beacon.core.state.Eth1Data; +import org.ethereum.beacon.core.types.Time; +import org.ethereum.beacon.crypto.BLS381.KeyPair; +import org.ethereum.beacon.crypto.BLS381.PrivateKey; +import org.ethereum.beacon.emulator.config.main.Signer; +import org.ethereum.beacon.emulator.config.main.Signer.Insecure; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys.Generate; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys.Private; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys.Public; +import org.ethereum.beacon.emulator.config.main.conract.Contract; +import org.ethereum.beacon.emulator.config.main.conract.EmulatorContract; +import org.ethereum.beacon.pow.DepositContract; +import org.ethereum.beacon.start.common.util.SimpleDepositContract; +import org.ethereum.beacon.start.common.util.SimulateUtils; +import org.ethereum.beacon.validator.crypto.BLS381Credentials; +import tech.pegasys.artemis.ethereum.core.Hash32; +import tech.pegasys.artemis.util.bytes.Bytes32; +import tech.pegasys.artemis.util.bytes.BytesValue; +import tech.pegasys.artemis.util.uint.UInt64; + +public class ConfigUtils { + + public static List createCredentials(Signer config, boolean isBlsSign) { + if (config instanceof Signer.Insecure) { + return createKeyPairs(((Insecure) config).getKeys()) + .stream() + .map( + key -> + isBlsSign + ? BLS381Credentials.createWithInsecureSigner(key) + : BLS381Credentials.createWithDummySigner(key)) + .collect(Collectors.toList()); + } else { + throw new IllegalArgumentException( + "This config class is not yet supported: " + config.getClass()); + } + } + + public static List createKeyPairs(List keys) { + return keys.stream().flatMap(k -> createKeyPairs(k).stream()).collect(Collectors.toList()); + } + + public static List createKeyPairs(ValidatorKeys keys) { + if (keys instanceof Public) { + throw new IllegalArgumentException("Can't generate key pairs from public keys: " + keys); + } else if (keys instanceof Private) { + return ((Private) keys).getKeys().stream() + .map(Bytes32::fromHexString) + .map(PrivateKey::create) + .map(KeyPair::create) + .collect(Collectors.toList()); + } else if (keys instanceof Generate) { + Generate genKeys = (Generate) keys; + Random random = new Random(genKeys.getSeed()); + for (int i = 0; i < genKeys.getStartIndex(); i++) { + Bytes32.random(random); + } + List ret = new ArrayList<>(); + for (int i = 0; i < genKeys.getCount(); i++) { + ret.add(KeyPair.create(PrivateKey.create(Bytes32.random(random)))); + } + return ret; + } else { + throw new IllegalArgumentException("Unknown ValidatorKeys subclass: " + keys.getClass()); + } + } + + public static DepositContract createDepositContract(Contract config, BeaconChainSpec spec, boolean verifyProof) { + if (config instanceof EmulatorContract) { + EmulatorContract eConfig = (EmulatorContract) config; + List keyPairs = createKeyPairs(eConfig.getKeys()); + Random random = new Random(1); + List deposits = SimulateUtils + .getDepositsForKeyPairs(UInt64.ZERO, random, keyPairs, spec, verifyProof); + Eth1Data eth1Data = + new Eth1Data( + Hash32.random(random), UInt64.valueOf(deposits.size()), Hash32.random(random)); + DepositContract.ChainStart chainStart = + new DepositContract.ChainStart( + Time.of(eConfig.getGenesisTime().getTime() / 1000), eth1Data, deposits); + SimpleDepositContract depositContract = new SimpleDepositContract(chainStart); + return depositContract; + } else { + throw new IllegalArgumentException( + "This config class is not yet supported: " + config.getClass()); + } + } +} diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java index 141cca97d..7bc405da1 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -1,11 +1,12 @@ package org.ethereum.beacon.node; +import io.netty.channel.ChannelFutureListener; import java.io.File; +import java.net.InetSocketAddress; import java.net.SocketAddress; -import java.util.ArrayList; +import java.net.URI; import java.util.List; import java.util.Random; -import java.util.stream.Collectors; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -14,7 +15,6 @@ import org.apache.logging.log4j.core.config.LoggerConfig; import org.ethereum.beacon.chain.storage.impl.MemBeaconChainStorageFactory; import org.ethereum.beacon.consensus.BeaconChainSpec; -import org.ethereum.beacon.core.operations.Deposit; import org.ethereum.beacon.core.spec.SpecConstants; import org.ethereum.beacon.core.state.Eth1Data; import org.ethereum.beacon.core.types.Time; @@ -23,20 +23,16 @@ import org.ethereum.beacon.emulator.config.chainspec.SpecBuilder; import org.ethereum.beacon.emulator.config.chainspec.SpecData; import org.ethereum.beacon.emulator.config.main.MainConfig; -import org.ethereum.beacon.emulator.config.main.plan.SimulationPlan; -import org.ethereum.beacon.emulator.config.simulator.PeersConfig; +import org.ethereum.beacon.emulator.config.main.network.NettyNetwork; +import org.ethereum.beacon.emulator.config.main.network.Network; import org.ethereum.beacon.pow.DepositContract; -import org.ethereum.beacon.schedulers.ControlledSchedulers; +import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.start.common.NodeLauncher; import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; -import org.ethereum.beacon.start.common.util.SimpleDepositContract; -import org.ethereum.beacon.start.common.util.SimulateUtils; import org.ethereum.beacon.validator.crypto.BLS381Credentials; import org.ethereum.beacon.wire.net.ConnectionManager; +import org.ethereum.beacon.wire.net.netty.NettyClient; import org.ethereum.beacon.wire.net.netty.NettyServer; -import org.javatuples.Pair; -import tech.pegasys.artemis.ethereum.core.Hash32; -import tech.pegasys.artemis.util.uint.UInt64; public class NodeCommandLauncher implements Runnable { private static final Logger logger = LogManager.getLogger("node"); @@ -88,21 +84,6 @@ private void setupLogging() { public void init() { setupLogging(); - -// List deposits = validatorDeposits.getValue0().stream() -// .filter(Objects::nonNull).collect(Collectors.toList()); -// keyPairs = validatorDeposits.getValue1(); -// -// genesisTime = Time.of(simulationPlan.getGenesisTime()); -// -// controlledSchedulers = new MDCControlledSchedulers(); -// controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); -// -// eth1Data = new Eth1Data(Hash32.random(rnd), Hash32.random(rnd)); -// -// DepositContract.ChainStart chainStart = -// new DepositContract.ChainStart(genesisTime, eth1Data, deposits); -// depositContract = new SimpleDepositContract(chainStart); } public void run() { @@ -110,51 +91,57 @@ public void run() { logger.info("Overridden beacon chain parameters:\n{}", config.getChainSpec()); Random rnd = new Random(); + Schedulers schedulers = Schedulers.createDefault(); + + depositContract = ConfigUtils.createDepositContract( + config.getConfig().getValidator().getContract(), + spec, + config.getChainSpec().getSpecHelpersOptions().isBlsVerifyProofOfPossession()); - ConfigBuilder specConfigBuilder = - new ConfigBuilder<>(SpecData.class) - .addYamlConfigFromResources("/config/spec-constants.yml") - .addYamlConfigFromResources("/test-spec-config.yml"); - SpecData specData = specConfigBuilder.build(); - SpecBuilder specBuilder = new SpecBuilder().withSpec(specData); - BeaconChainSpec spec = specBuilder.buildSpec(); - - int depositCount = 16; - Pair, List> depositPairs = - SimulateUtils.getAnyDeposits(rnd, spec, depositCount, false); - - Time genesisTime = Time.of(60000); - - MDCControlledSchedulers controlledSchedulers = new MDCControlledSchedulers(); - controlledSchedulers.setCurrentTime(genesisTime.getMillis().getValue() + 1000); - - Eth1Data eth1Data = new Eth1Data(Hash32.random(rnd), UInt64.valueOf(depositCount), Hash32.random(rnd)); - - DepositContract.ChainStart chainStart = - new DepositContract.ChainStart(genesisTime, eth1Data, depositPairs.getValue0()); - SimpleDepositContract depositContract = new SimpleDepositContract(chainStart); - - // master node with all validators - { - ControlledSchedulers schedulers = controlledSchedulers.createNew("master"); - NettyServer nettyServer = new NettyServer(40001); - nettyServer.start(); - ConnectionManager connectionManager = new ConnectionManager<>( - nettyServer, null, schedulers.reactorEvents()); - NodeLauncher masterNode = new NodeLauncher( - specBuilder.buildSpec(), - depositContract, - depositPairs - .getValue1() - .stream() - .map(BLS381Credentials::createWithDummySigner) - .collect(Collectors.toList()), - connectionManager, - new MemBeaconChainStorageFactory(spec.getObjectHasher()), - schedulers, - false); + List credentials = ConfigUtils.createCredentials( + config.getConfig().getValidator().getSigner(), + config.getChainSpec().getSpecHelpersOptions().isBlsSign()); + + ConnectionManager connectionManager; + if (config.getConfig().getNetworks().size() != 1) { + throw new IllegalArgumentException("1 network should be specified in config"); + } + Network networkCfg = config.getConfig().getNetworks().get(0); + if (networkCfg instanceof NettyNetwork) { + NettyNetwork nettyConfig = (NettyNetwork) networkCfg; + NettyServer nettyServer = null; + if (nettyConfig.getListenPort() != null) { + nettyServer = new NettyServer(nettyConfig.getListenPort()); + nettyServer.start().addListener((ChannelFutureListener) channelFuture -> { + try { + channelFuture.get(); + logger.info("Listening for inbound connections on port " + nettyConfig.getListenPort()); + } catch (Exception e) { + logger.error("Unable to open inbound port " + nettyConfig.getListenPort(), e); + } + }); + } + NettyClient nettyClient = new NettyClient(); + ConnectionManager tcpConnectionManager = + new ConnectionManager<>(nettyServer, nettyClient, schedulers.reactorEvents()); + connectionManager = tcpConnectionManager; + for (String addr : nettyConfig.getActivePeers()) { + URI uri = URI.create(addr); + tcpConnectionManager.addActivePeer(InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort())); + } + } else { + throw new IllegalArgumentException( + "This type of network is not supported yet: " + networkCfg.getClass()); } + NodeLauncher node = new NodeLauncher( + specBuilder.buildSpec(), + this.depositContract, + credentials, + connectionManager, + new MemBeaconChainStorageFactory(spec.getObjectHasher()), + schedulers, + false); } public static class Builder { @@ -165,7 +152,6 @@ public Builder() {} public NodeCommandLauncher build() { assert config != null; - SimulationPlan simulationPlan = (SimulationPlan) config.getPlan(); ConfigBuilder specConfigBuilder = new ConfigBuilder<>(SpecData.class).addYamlConfigFromResources("/config/spec-constants.yml"); @@ -175,13 +161,6 @@ public NodeCommandLauncher build() { SpecData spec = specConfigBuilder.build(); - List peers = new ArrayList<>(); - for (PeersConfig peer : simulationPlan.getPeers()) { - for (int i = 0; i < peer.getCount(); i++) { - peers.add(peer); - } - } - SpecBuilder specBuilder = new SpecBuilder().withSpec(spec); return new NodeCommandLauncher( From 094b39ff71c6d4ea49e35e8a09bf7c8427985487 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 14:35:19 +0300 Subject: [PATCH 59/96] Reduce loglevel for StateCachingTransition --- .../beacon/consensus/transition/StateCachingTransition.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/consensus/src/main/java/org/ethereum/beacon/consensus/transition/StateCachingTransition.java b/consensus/src/main/java/org/ethereum/beacon/consensus/transition/StateCachingTransition.java index 0537bb153..34938f810 100644 --- a/consensus/src/main/java/org/ethereum/beacon/consensus/transition/StateCachingTransition.java +++ b/consensus/src/main/java/org/ethereum/beacon/consensus/transition/StateCachingTransition.java @@ -26,7 +26,7 @@ public StateCachingTransition(BeaconChainSpec spec) { @Override public BeaconStateEx apply(BeaconStateEx source) { - logger.debug(() -> "Applying state caching to state: (" + + logger.trace(() -> "Applying state caching to state: (" + spec.hash_tree_root(source).toStringShort() + ") " + source.toString(spec.getConstants(), spec::signing_root)); @@ -38,7 +38,7 @@ public BeaconStateEx apply(BeaconStateEx source) { BeaconStateEx ret = new BeaconStateExImpl(state.createImmutable(), TransitionType.CACHING); - logger.debug(() -> "State caching result state: (" + + logger.trace(() -> "State caching result state: (" + spec.hash_tree_root(ret).toStringShort() + ") " + ret.toString(spec.getConstants(), spec::signing_root)); From 35e3feae864dfc956c28da383511e0665e072f5d Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 14:35:54 +0300 Subject: [PATCH 60/96] Add 'node' command line options --- .../emulator/config/main/Configuration.java | 3 +- .../emulator/config/main/ValidatorKeys.java | 11 ++- .../config/main/network/NettyNetwork.java | 3 +- .../org/ethereum/beacon/node/ConfigUtils.java | 3 + .../java/org/ethereum/beacon/node/Node.java | 2 +- .../beacon/node/NodeCommandLauncher.java | 77 +++++++++++++++++++ .../ethereum/beacon/node/command/RunNode.java | 71 ++++++++++++++--- .../resources/config/default-node-config.yml | 42 ++++++++++ .../src/main/resources/config/node-config.yml | 40 +++------- start/node/src/main/resources/log4j2.xml | 9 ++- 10 files changed, 214 insertions(+), 47 deletions(-) create mode 100644 start/node/src/main/resources/config/default-node-config.yml diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java index a11951832..a0b5c83c6 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java @@ -1,12 +1,13 @@ package org.ethereum.beacon.emulator.config.main; +import java.util.ArrayList; import java.util.List; import org.ethereum.beacon.emulator.config.main.network.Network; /** Beacon chain configuration */ public class Configuration { private String db; - private List networks; + private List networks = new ArrayList<>(); private Validator validator; public String getDb() { diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java index 00d262476..ee1de7a5e 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/ValidatorKeys.java @@ -54,6 +54,15 @@ public void setKeys(List keys) { } } - public static class Private extends ExplicitKeys {} + public static class Private extends ExplicitKeys { + + public Private() { + } + + public Private(List keys) { + setKeys(keys); + } + } + public static class Public extends ExplicitKeys {} } diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java index a3059f1d3..3c3e134dd 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/network/NettyNetwork.java @@ -1,10 +1,11 @@ package org.ethereum.beacon.emulator.config.main.network; +import java.util.ArrayList; import java.util.List; public class NettyNetwork extends Network { private Integer listenPort; - private List activePeers; + private List activePeers = new ArrayList<>(); public Integer getListenPort() { return listenPort; diff --git a/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java b/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java index a5af0edf5..cc385f101 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java @@ -30,6 +30,9 @@ public class ConfigUtils { public static List createCredentials(Signer config, boolean isBlsSign) { + if (config == null) { + return null; + } if (config instanceof Signer.Insecure) { return createKeyPairs(((Insecure) config).getKeys()) .stream() diff --git a/start/node/src/main/java/org/ethereum/beacon/node/Node.java b/start/node/src/main/java/org/ethereum/beacon/node/Node.java index aef24a22c..7a59d64fa 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/Node.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/Node.java @@ -7,7 +7,7 @@ @CommandLine.Command( description = "Beacon chain node", name = "node", - version = "simulator " + Node.VERSION, + version = "node " + Node.VERSION, mixinStandardHelpOptions = true, subcommands = {RunNode.class}) public class Node implements Runnable { diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java index 7bc405da1..c31532f90 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -5,8 +5,12 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Random; +import java.util.stream.IntStream; +import java.util.stream.Stream; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -23,8 +27,13 @@ import org.ethereum.beacon.emulator.config.chainspec.SpecBuilder; import org.ethereum.beacon.emulator.config.chainspec.SpecData; import org.ethereum.beacon.emulator.config.main.MainConfig; +import org.ethereum.beacon.emulator.config.main.Signer.Insecure; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys; +import org.ethereum.beacon.emulator.config.main.ValidatorKeys.Private; +import org.ethereum.beacon.emulator.config.main.conract.EmulatorContract; import org.ethereum.beacon.emulator.config.main.network.NettyNetwork; import org.ethereum.beacon.emulator.config.main.network.Network; +import org.ethereum.beacon.node.command.RunNode; import org.ethereum.beacon.pow.DepositContract; import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.start.common.NodeLauncher; @@ -142,11 +151,20 @@ public void run() { new MemBeaconChainStorageFactory(spec.getObjectHasher()), schedulers, false); + + while (true) { + try { + Thread.sleep(1000000L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } public static class Builder { private MainConfig config; private Level logLevel = Level.INFO; + private RunNode cliOptions; public Builder() {} @@ -163,6 +181,60 @@ public NodeCommandLauncher build() { SpecBuilder specBuilder = new SpecBuilder().withSpec(spec); + if (cliOptions.getListenPort() != null || cliOptions.getActivePeers() != null) { + NettyNetwork network = (NettyNetwork) config + .getConfig() + .getNetworks() + .stream() + .filter(n -> n instanceof NettyNetwork) + .findFirst().orElse(null); + + if (network == null) { + network = new NettyNetwork(); + config.getConfig().getNetworks().add(network); + } + + if (cliOptions.getListenPort() != null) { + network.setListenPort(cliOptions.getListenPort()); + } + + if (cliOptions.getActivePeers() != null) { + network.setActivePeers(cliOptions.getActivePeers()); + } + } + + if (cliOptions.getValidators() != null) { + List validatorKeys = new ArrayList<>(); + List depositKeypairs = null; + for (String key : cliOptions.getValidators()) { + if (key.startsWith("0x")) { + validatorKeys.add(key); + } else { + if (depositKeypairs == null) { + depositKeypairs = ConfigUtils + .createKeyPairs(((EmulatorContract)config.getConfig().getValidator().getContract()).getKeys()); + } + List finalDepositKeypairs = depositKeypairs; + + IntStream indices; + if (key.contains("-")) { + int idx = key.indexOf("-"); + int start = Integer.parseInt(key.substring(0, idx)); + int end = Integer.parseInt(key.substring(idx + 1)); + indices = IntStream.range(start, end + 1); + } else { + indices = IntStream.of(Integer.parseInt(key)); + } + indices + .mapToObj(i -> finalDepositKeypairs.get(i).getPrivate().getEncodedBytes().toString()) + .forEach(validatorKeys::add); + } + } + Insecure signer = new Insecure(); + signer.setKeys(Collections.singletonList(new Private(validatorKeys))); + config.getConfig().getValidator().setSigner(signer); + } + return new NodeCommandLauncher( config, specBuilder, @@ -186,5 +258,10 @@ public Builder withLogLevel(Level logLevel) { this.logLevel = logLevel; return this; } + + public Builder withCliOptions(RunNode runNode) { + cliOptions = runNode; + return this; + } } } diff --git a/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java index b53ce57c1..5a4c3e0a9 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java @@ -1,28 +1,79 @@ package org.ethereum.beacon.node.command; import java.io.File; +import java.util.List; import org.ethereum.beacon.node.NodeCommandLauncher; import picocli.CommandLine; -@CommandLine.Command(name = "run", description = "Runs beacon chain node", mixinStandardHelpOptions = true) +@CommandLine.Command( + name = "run", + description = "Runs beacon chain node", + mixinStandardHelpOptions = true, + sortOptions = false +) public class RunNode implements Runnable { @CommandLine.Parameters( - index = "0", - paramLabel = "node-config.yml", - description = - "A path to a config file containing node config in YAML format\nuse 'default' to run a node with default setup") + index = "0", + paramLabel = "node-config.yml", + description = + "A path to a config file containing node config in YAML format\nuse 'default' to run a node with default setup" + ) private String config; @CommandLine.Option( - names = {"--loglevel"}, - paramLabel = "level", - description = "Log verbosity level: all, debug, info, error\ninfo is set by default") - private LogLevel logLevel = LogLevel.info; + names = {"--loglevel"}, + paramLabel = "level", + description = "Log verbosity level: all, debug, info, error\ninfo is set by default" + ) + private LogLevel logLevel = null; + + @CommandLine.Option( + names = {"--listen"}, + paramLabel = "port", + description = "Listen for inbound connections on TCP port" + ) + private Integer listenPort; + + @CommandLine.Option( + names = {"--connect"}, + paramLabel = "URL", + split = ",", + description = "Actively connects to remote peers. URL in form 'tcp://:'" + ) + private List activePeers; + + @CommandLine.Option( + names = {"--validators"}, + paramLabel = "key", + split = ",", + description = { + "List of signers. Entry is either hex private key (starting from '0x'), " + + "or index of keypair specified in the 'contract emulator' config deposits " + + "or a range of such indices", + "Example: --validators=1,2,5-9,0x1234567...ef" + } + ) + private List validators; + + public Integer getListenPort() { + return listenPort; + } + + public List getActivePeers() { + return activePeers; + } + + public List getValidators() { + return validators; + } @Override public void run() { - NodeCommandLauncher.Builder nodeBuilder = new NodeCommandLauncher.Builder().withLogLevel(logLevel.toLog4j()); + NodeCommandLauncher.Builder nodeBuilder = + new NodeCommandLauncher.Builder() + .withLogLevel(logLevel == null ? null : logLevel.toLog4j()) + .withCliOptions(this); if ("default".equals(config)) { nodeBuilder.withConfigFromResource("/config/default-node-config.yml"); diff --git a/start/node/src/main/resources/config/default-node-config.yml b/start/node/src/main/resources/config/default-node-config.yml new file mode 100644 index 000000000..0a294bdff --- /dev/null +++ b/start/node/src/main/resources/config/default-node-config.yml @@ -0,0 +1,42 @@ +config: + db: file://db + networks: + - type: netty + + validator: + contract: !emulator + genesisTime: 2019-05-21 11:00:00 + keys: + - !generate + count: 16 + seed: 0 +# signer: !insecure +# keys: +# - !generate +# count: 16 +# seed: 0 + +chainSpec: + specConstants: + initialValues: + GENESIS_SLOT: 1000000 + miscParameters: + SHARD_COUNT: 4 + TARGET_COMMITTEE_SIZE: 2 + timeParameters: + SECONDS_PER_SLOT: 10 + MIN_ATTESTATION_INCLUSION_DELAY: 1 + SLOTS_PER_EPOCH: 4 + SLOTS_PER_HISTORICAL_ROOT: 64 + + honestValidatorParameters: + ETH1_FOLLOW_DISTANCE: 1 + stateListLengths: + LATEST_RANDAO_MIXES_LENGTH: 64 + LATEST_ACTIVE_INDEX_ROOTS_LENGTH: 64 + LATEST_SLASHED_EXIT_LENGTH: 64 + + specHelpersOptions: + blsVerify: false + blsVerifyProofOfPossession: false + blsSign: false diff --git a/start/node/src/main/resources/config/node-config.yml b/start/node/src/main/resources/config/node-config.yml index 865cc0cdd..58b6821b7 100644 --- a/start/node/src/main/resources/config/node-config.yml +++ b/start/node/src/main/resources/config/node-config.yml @@ -8,35 +8,17 @@ config: - tcp://localhost:40003 validator: - contract: - handler: ethereumj - distanceFromHead: 1000 - contractAddress: 0xd47c61f2c25aaa677dcf23e65765fac04c85d6a0 - contractBlock: 8000000 - contractAbi: file://Contract.abi - signer: - implementation: - class: InsecureBLS381MessageSigner - input: - privKeys: - - 0x0000000000000000000000000000000000000000000000000000000000000000 - - 0x0000000000000000000000000000000000000000000000000000000000000000 - - 0x0000000000000000000000000000000000000000000000000000000000000000 - -plan: !general - sync: - - action: run - validator: - - action: deposit - creator: ethereumj - sender: ethereumj - gasLimit: 2000000 - eth1From: 0x0000000000000000000000000000000000000000 - eth1PrivKey: 0x0000000000000000000000000000000000000000000000000000000000000000 - withdrawalCredentials: 0x0000000000000000000000000000000000000000 - amount: 32 - - - action: run + contract: !emulator + genesisTime: 2018-05-20 16:55:00 + keys: + - !generate + count: 16 + seed: 0 + signer: !insecure + keys: + - !generate + count: 16 + seed: 0 chainSpec: specConstants: diff --git a/start/node/src/main/resources/log4j2.xml b/start/node/src/main/resources/log4j2.xml index d651e7c84..30bf2cda2 100644 --- a/start/node/src/main/resources/log4j2.xml +++ b/start/node/src/main/resources/log4j2.xml @@ -28,11 +28,12 @@ - - - + + + - + + From d2c370fe368397f3ec1262500b36e69e70a98e28 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 18:53:14 +0300 Subject: [PATCH 61/96] Fix connectionManager --- .../ethereum/beacon/wire/net/ConnectionManager.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java index a73617958..5abe946c6 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java @@ -13,6 +13,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; +import reactor.core.publisher.ReplayProcessor; import reactor.core.scheduler.Scheduler; import tech.pegasys.artemis.util.bytes.BytesValue; @@ -24,7 +25,7 @@ public class ConnectionManager { private final Client client; private final Scheduler rxScheduler; - private final DirectProcessor> clientConnections = DirectProcessor.create(); + private final ReplayProcessor> clientConnections = ReplayProcessor.create(); private final FluxSink> clientConnectionsSink = clientConnections.sink(); private final Set activePeers = Collections.synchronizedSet(new HashSet<>()); @@ -54,7 +55,7 @@ public void addActivePeer(TAddress peerAddress) { .flatMap(Mono::fromFuture, 1, 1) .doOnError(t-> logger.info("Couldn't connect to active peer " + peerAddress + ": " + t)) .doOnNext(ch -> logger.info("Connected to active peer " + peerAddress)) - .doOnNext(clientConnectionsSink::next) + .doOnNext(ch -> clientConnectionsSink.next(ch)) .map(Channel::getCloseFuture) .onErrorResume(t -> Flux.just(CompletableFuture.completedFuture(null))) .flatMap(f -> Mono.fromFuture(f.thenApply(v -> "")), 1, 1) @@ -70,7 +71,9 @@ public void removeActivePeer(TAddress peerAddress) { public Publisher> channelsStream() { return Flux.merge( - server == null ? Flux.empty() : server.channelsStream(), - client == null ? Flux.empty() : clientConnections); + server == null ? Flux.empty() : server.channelsStream(), + client == null ? Flux.empty() : + clientConnections) + .doOnNext(ch -> System.out.println(ch)); } } From cabbeac863bca3f569b0d1a045ccbaa8b360f369 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 18:53:46 +0300 Subject: [PATCH 62/96] Fix peerManager --- .../org/ethereum/beacon/wire/SimplePeerManagerImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java index 936451b0d..eba70798f 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java @@ -61,7 +61,9 @@ public SimplePeerManagerImpl( this.syncServer = syncServer; this.headStream = headStream; - connectedPeersStream = Flux.from(channelsStream).map(this::createPeer).publish().autoConnect(); + connectedPeersStream = Flux.from(channelsStream) + .map(this::createPeer) + .replay(1).autoConnect(); Flux.from(activePeerStream()).subscribe(this::onNewActivePeer); @@ -86,6 +88,7 @@ protected HelloMessage createLocalHello() { } protected PeerImpl createPeer(Channel channel) { + logger.info("Creating a peer from new channel: " + channel); return new PeerImpl(channel, createLocalHello(), ssz, messageSerializer, syncServer, schedulers); } @@ -107,6 +110,7 @@ public Publisher activePeerStream() { } protected void onNewActivePeer(Peer peer) { + logger.info("New active peer: " + peer); activePeers.add(peer); peer.getRawChannel().getCloseFuture().thenAccept(v -> activePeers.remove(peer)); } From de938b94e9bf0cb1af2a44ac1091abcb7472c2e8 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 18:54:15 +0300 Subject: [PATCH 63/96] Fix SyncServer to correctly manage empty slots --- .../ethereum/beacon/wire/WireApiSyncServer.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index 555acc818..599b9a9b8 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -72,18 +72,23 @@ public CompletableFuture requestBlockHeaders( SlotNumber maxSlot = storage.getBlockStorage().getMaxSlot(); SlotNumber prevSlot = SlotNumber.ZERO; for(int i = 0; i < requestMessage.getMaxHeaders().intValue(); i++) { - + if (slot.greater(maxSlot)) { + break; + } List slotBlocks = Collections.emptyList(); SlotNumber nonEmptySlot = slot; - while (slotBlocks.isEmpty() && nonEmptySlot.greater(prevSlot)) { + while (nonEmptySlot.greater(prevSlot)) { slotBlocks = storage.getBlockStorage().getSlotBlocks(nonEmptySlot); + if (!slotBlocks.isEmpty()) { + break; + } nonEmptySlot = nonEmptySlot.decrement(); } - headers.add(storage.getBlockHeaderStorage().get(slotBlocks.get(0)).get()); - slot = slot.plus(increment); - if (slot.greater(maxSlot)) { - break; + + if (nonEmptySlot.greater(prevSlot)) { + headers.add(storage.getBlockHeaderStorage().get(slotBlocks.get(0)).get()); } + slot = slot.plus(increment); prevSlot = nonEmptySlot; } ret.complete(new BlockHeadersResponseMessage(headers)); From ad208da8d3fbe9664a504056f578f77e3cc2a796 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 18:54:32 +0300 Subject: [PATCH 64/96] Enable syncManager --- .../main/java/org/ethereum/beacon/node/NodeCommandLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java index c31532f90..aa6cc4d17 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -150,7 +150,7 @@ public void run() { connectionManager, new MemBeaconChainStorageFactory(spec.getObjectHasher()), schedulers, - false); + true); while (true) { try { From a41153b78c16b0a41fdbb633b9598e73df916989 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 19:12:08 +0300 Subject: [PATCH 65/96] Use SyncQueue production params --- .../java/org/ethereum/beacon/start/common/NodeLauncher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java index 18d336417..966904dfc 100644 --- a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java @@ -175,7 +175,7 @@ void chainStarted(ChainStart chainStartEvent) { wireApiSyncRemote = peerManager.getWireApiSync(); blockTree = new BeaconBlockTree(spec.getObjectHasher()); - syncQueue = new SyncQueueImpl(blockTree, 4, 16); + syncQueue = new SyncQueueImpl(blockTree); syncManager = new SyncManagerImpl( beaconChain, From 8ebb472d34ceb2f20f4aca78f168d03d9f2ceab3 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 19:12:30 +0300 Subject: [PATCH 66/96] Synchronize the BlockTree class --- .../org/ethereum/beacon/wire/sync/AbstractBlockTree.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java index 19e89b3de..639030640 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java @@ -34,7 +34,7 @@ public void setTopBlock(TRawBlock block) { @Nonnull @Override - public List addBlock(@Nonnull TBlock block) { + public synchronized List addBlock(@Nonnull TBlock block) { if (topBlock == null) { throw new IllegalStateException("Top block should be set first"); } @@ -70,7 +70,7 @@ private void addChildrenRecursively(THash blockHash, List successors) { } @Override - public void setTopBlock(@Nonnull TBlock block) { + public synchronized void setTopBlock(@Nonnull TBlock block) { if (topBlock != null) { if (!hashMap.containsKey(block.getHash())) { throw new IllegalArgumentException( @@ -91,7 +91,7 @@ public void setTopBlock(@Nonnull TBlock block) { @Nonnull @Override - public TBlock getTopBlock() { + public synchronized TBlock getTopBlock() { return topBlock; } } From 9ae41e552919ab7d7b22719e9c81ff4199ed603f Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Tue, 21 May 2019 19:16:39 +0300 Subject: [PATCH 67/96] Fix test compile error --- .../test/java/org/ethereum/beacon/core/ModelsSerializeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java b/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java index 82f340994..ec5b70498 100644 --- a/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java +++ b/core/src/test/java/org/ethereum/beacon/core/ModelsSerializeTest.java @@ -62,7 +62,7 @@ public void setup() { sszSerializer = new SSZBuilder() .withExternalVarResolver(new SpecConstantsResolver(specConstants)) .buildSerializer(); - dataFactory = new TestDataFactory(specConstants, sszSerializer); + dataFactory = new TestDataFactory(specConstants); } @Test From 1d28c913b5e4226c7e464b6d323a460db6f3d744 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 10:57:19 +0300 Subject: [PATCH 68/96] Don't recalculate empty slot transitions from head on every slot --- .../observer/ObservableStateProcessorImpl.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java index 28b7edfbd..48b8453ff 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java @@ -226,14 +226,26 @@ private void newSlot(SlotNumber newSlot) { private void updateCurrentObservableState(BeaconTupleDetails head, SlotNumber slot) { assert slot.greaterEqual(head.getBlock().getSlot()); - PendingOperations pendingOperations = - getPendingOperations(head.getFinalState(), copyAttestationCache()); if (slot.greater(head.getBlock().getSlot())) { - BeaconStateEx stateUponASlot = emptySlotTransition.apply(head.getFinalState(), slot); + BeaconStateEx stateUponASlot; + if (latestState.getSlot().greater(spec.getConstants().getGenesisSlot()) + && spec.getObjectHasher() + .getHashTruncateLast(head.getBlock()) + .equals( + spec.get_block_root_at_slot(latestState, latestState.getSlot().decrement()))) { + + // latestState is actual with respect to current head + stateUponASlot = emptySlotTransition.apply(latestState, slot); + } else { + // recalculate all empty slots starting from the head + stateUponASlot = emptySlotTransition.apply(head.getFinalState(), slot); + } latestState = stateUponASlot; + PendingOperations pendingOperations = getPendingOperations(stateUponASlot, copyAttestationCache()); observableStateStream.onNext( new ObservableBeaconState(head.getBlock(), stateUponASlot, pendingOperations)); } else { + PendingOperations pendingOperations = getPendingOperations(head.getFinalState(), copyAttestationCache()); if (head.getPostSlotState().isPresent()) { latestState = head.getPostSlotState().get(); observableStateStream.onNext(new ObservableBeaconState( From 5a69b83980f77b7bcf459aa810d0d19927ec77b9 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 13:49:38 +0300 Subject: [PATCH 69/96] Fix typo --- .../ethereum/beacon/chain/ProposedBlockProcessorImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java index 1a81bacfa..7e6e1ce0c 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java @@ -19,10 +19,10 @@ public ProposedBlockProcessorImpl(MutableBeaconChain beaconChain, Schedulers sch } @Override - public void newBlockProposed(BeaconBlock newBlcok) { - boolean result = beaconChain.insert(newBlcok); + public void newBlockProposed(BeaconBlock newBlock) { + boolean result = beaconChain.insert(newBlock); if (result) { - blocksStream.onNext(newBlcok); + blocksStream.onNext(newBlock); } } From f585230763f2294ce9c418543dc3bfac4dad43a5 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 14:27:20 +0300 Subject: [PATCH 70/96] Fix: bodies can be the same for different blocks. Thus duplicates are allowed in toMap() --- wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java index 654f69017..435e3aec8 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -50,7 +50,7 @@ default CompletableFuture>> requestBlocks( bodiesFuture, (headers, bodies) -> { Map bodyMap = - bodies.get().stream().collect(Collectors.toMap(hasher::getHash, b -> b)); + bodies.get().stream().collect(Collectors.toMap(hasher::getHash, b -> b, (b1, b2) -> b1)); return bodies.delegate( headers.stream() .map(h -> Optional.ofNullable(bodyMap.get(h.getBlockBodyRoot())) From ba58fdee5ad3cc35d5a616abc0243e25a1f85764 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 15:23:50 +0300 Subject: [PATCH 71/96] Rely on SpecHelpers.verify_attestation when populating pending operations --- .../ObservableStateProcessorImpl.java | 11 +---------- .../consensus/spec/BlockProcessing.java | 19 ++++++++++++------- .../operation/AttestationVerifier.java | 6 ++++-- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java index 48b8453ff..90ade9ea7 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java @@ -269,16 +269,7 @@ private PendingOperations getPendingOperations( .flatMap(Collection::stream) .filter(attestation -> attestation.getData().getTargetEpoch().lessEqual(spec.get_current_epoch(state))) - .filter(attestation -> { - /* attestation_slot = get_attestation_slot(state, attestation) - assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH */ - SlotNumber attestationSlot = spec.get_attestation_slot(state, attestation.getData()); - SlotNumber lowerBoundary = - attestationSlot.plus(spec.getConstants().getMinAttestationInclusionDelay()); - SlotNumber upperBoundary = attestationSlot.plus(spec.getConstants().getSlotsPerEpoch()); - return lowerBoundary.lessEqual(state.getSlot()) - && state.getSlot().lessEqual(upperBoundary); - }) + .filter(attestation -> spec.verify_attestation(state, attestation)) .sorted(Comparator.comparing(attestation -> attestation.getData().getTargetEpoch())) .collect(Collectors.toList()); diff --git a/consensus/src/main/java/org/ethereum/beacon/consensus/spec/BlockProcessing.java b/consensus/src/main/java/org/ethereum/beacon/consensus/spec/BlockProcessing.java index cd900f5fa..a020a74f0 100644 --- a/consensus/src/main/java/org/ethereum/beacon/consensus/spec/BlockProcessing.java +++ b/consensus/src/main/java/org/ethereum/beacon/consensus/spec/BlockProcessing.java @@ -207,16 +207,17 @@ default void process_attester_slashing(MutableBeaconState state, AttesterSlashin assertTrue(slashed_any); } - default void verify_attestation(BeaconState state, Attestation attestation) { + default boolean verify_attestation(BeaconState state, Attestation attestation) { AttestationData data = attestation.getData(); /* attestation_slot = get_attestation_slot(state, attestation) assert attestation_slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot <= attestation_slot + SLOTS_PER_EPOCH */ SlotNumber attestation_slot = get_attestation_slot(state, data); - assertTrue( + if (! attestation_slot.plus(getConstants().getMinAttestationInclusionDelay()).lessEqual(state.getSlot()) - && state.getSlot().lessEqual(attestation_slot.plus(getConstants().getSlotsPerEpoch())) - ); + && state.getSlot().lessEqual(attestation_slot.plus(getConstants().getSlotsPerEpoch()))) { + return false; + } /* Check target epoch, source epoch, source root, and source crosslink data = attestation.data @@ -234,15 +235,19 @@ default void verify_attestation(BeaconState state, Attestation attestation) { && data.getSourceEpoch().equals(state.getPreviousJustifiedEpoch()) && data.getSourceRoot().equals(state.getPreviousJustifiedRoot()) && data.getPreviousCrosslinkRoot().equals(hash_tree_root(state.getPreviousCrosslinks().get(data.getShard()))); - assertTrue(current_epoch_attestation || previous_epoch_attestation); + if (!(current_epoch_attestation || previous_epoch_attestation)) { + return false; + } /* Check crosslink data root assert data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] */ - assertTrue(data.getCrosslinkDataRoot().equals(Hash32.ZERO)); + if (!(data.getCrosslinkDataRoot().equals(Hash32.ZERO))) { + return false; + } /* Check signature and bitfields assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) */ - assertTrue(verify_indexed_attestation(state, convert_to_indexed(state, attestation))); + return verify_indexed_attestation(state, convert_to_indexed(state, attestation)); } /* diff --git a/consensus/src/main/java/org/ethereum/beacon/consensus/verifier/operation/AttestationVerifier.java b/consensus/src/main/java/org/ethereum/beacon/consensus/verifier/operation/AttestationVerifier.java index d8954169f..5fbd931e9 100644 --- a/consensus/src/main/java/org/ethereum/beacon/consensus/verifier/operation/AttestationVerifier.java +++ b/consensus/src/main/java/org/ethereum/beacon/consensus/verifier/operation/AttestationVerifier.java @@ -25,8 +25,10 @@ public AttestationVerifier(BeaconChainSpec spec) { @Override public VerificationResult verify(Attestation attestation, BeaconState state) { try { - spec.verify_attestation(state, attestation); - return VerificationResult.PASSED; + return spec.verify_attestation(state, attestation) + ? VerificationResult.PASSED + : VerificationResult.failedResult( + "Failed to verify attestation %s against state %s", attestation, state); } catch (Exception e) { return VerificationResult.failedResult(e.getMessage()); } From 37d63dd7d829b707d84cd54030a41fdc4cc96396 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 15:24:57 +0300 Subject: [PATCH 72/96] Temporarily make the sync less aggressive --- .../org/ethereum/beacon/wire/sync/SyncManagerImpl.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java index eeb673a3d..23964559a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java @@ -84,10 +84,11 @@ public SyncManagerImpl( mode -> { switch (mode) { case Long: - return syncQueue.getBlockRequestsStream(); - case Short: return Flux.from(syncQueue.getBlockRequestsStream()) .delayElements(Duration.ofSeconds(1), delayScheduler); + case Short: + return Flux.from(syncQueue.getBlockRequestsStream()) + .delayElements(Duration.ofSeconds(5), delayScheduler); default: throw new IllegalStateException(); } @@ -137,7 +138,7 @@ public void start() { req.getStep())) .flatMap(req -> Mono.fromFuture(syncApi.requestBlocks(req, spec.getObjectHasher())), maxConcurrentBlockRequests) - .onErrorContinue((t, o) -> logger.info("SyncApi exception: " + t + ", " + o)); + .onErrorContinue((t, o) -> logger.warn("SyncApi exception: " + t + ", " + o)); if (newBlocks != null) { wireBlocksStream = wireBlocksStream.mergeWith( From 1dd95063d8cf891d6b3ac4dc2b76200d7dbf836f Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 15:26:10 +0300 Subject: [PATCH 73/96] Make deposit amount configurable --- .../start/common/util/SimulateUtils.java | 41 +++++++++++++++++-- .../config/main/conract/EmulatorContract.java | 12 +++++- .../org/ethereum/beacon/node/ConfigUtils.java | 7 +++- .../resources/config/default-node-config.yml | 5 ++- 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimulateUtils.java b/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimulateUtils.java index d8858a523..960e3c040 100644 --- a/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimulateUtils.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/util/SimulateUtils.java @@ -6,6 +6,7 @@ import org.ethereum.beacon.core.spec.SignatureDomains; import org.ethereum.beacon.core.types.BLSPubkey; import org.ethereum.beacon.core.types.BLSSignature; +import org.ethereum.beacon.core.types.Gwei; import org.ethereum.beacon.crypto.BLS381; import org.ethereum.beacon.crypto.BLS381.PrivateKey; import org.ethereum.beacon.crypto.MessageParameters; @@ -42,14 +43,25 @@ public static synchronized Pair, List> getAnyDepos cachedDeposits.getValue0().subList(0, count), cachedDeposits.getValue1().subList(0, count)); } - public static synchronized Deposit getDepositForKeyPair(UInt64 depositIndex, Random rnd, + public static Deposit getDepositForKeyPair(UInt64 depositIndex, Random rnd, BLS381.KeyPair keyPair, BeaconChainSpec spec, boolean isProofVerifyEnabled) { + return getDepositForKeyPair( + depositIndex, + rnd, + keyPair, + spec, + spec.getConstants().getMaxEffectiveBalance(), + isProofVerifyEnabled); + } + + public static synchronized Deposit getDepositForKeyPair(UInt64 depositIndex, Random rnd, + BLS381.KeyPair keyPair, BeaconChainSpec spec, Gwei initBalance, boolean isProofVerifyEnabled) { Hash32 withdrawalCredentials = Hash32.random(rnd); DepositData depositDataWithoutSignature = new DepositData( BLSPubkey.wrap(Bytes48.leftPad(keyPair.getPublic().getEncodedBytes())), withdrawalCredentials, - spec.getConstants().getMaxEffectiveBalance(), + initBalance, BLSSignature.wrap(Bytes96.ZERO)); BLSSignature signature = BLSSignature.ZERO; @@ -75,12 +87,33 @@ public static synchronized Deposit getDepositForKeyPair(UInt64 depositIndex, Ran } public static synchronized List getDepositsForKeyPairs( - UInt64 startIndex, Random rnd, List keyPairs, BeaconChainSpec spec, boolean isProofVerifyEnabled) { + UInt64 startIndex, + Random rnd, + List keyPairs, + BeaconChainSpec spec, + boolean isProofVerifyEnabled) { + return getDepositsForKeyPairs( + startIndex, + rnd, + keyPairs, + spec, + spec.getConstants().getMaxEffectiveBalance(), + isProofVerifyEnabled); + } + + public static synchronized List getDepositsForKeyPairs( + UInt64 startIndex, + Random rnd, + List keyPairs, + BeaconChainSpec spec, + Gwei initBalance, + boolean isProofVerifyEnabled) { + List deposits = new ArrayList<>(); UInt64 index = startIndex; for (BLS381.KeyPair keyPair : keyPairs) { - deposits.add(getDepositForKeyPair(index, rnd, keyPair, spec, isProofVerifyEnabled)); + deposits.add(getDepositForKeyPair(index, rnd, keyPair, spec, initBalance, isProofVerifyEnabled)); index = index.increment(); } diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java index 0d36bfafe..96a9350be 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/conract/EmulatorContract.java @@ -9,7 +9,9 @@ public class EmulatorContract extends Contract { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT") private Date genesisTime; - List keys; + + private Integer balance; + private List keys; public Date getGenesisTime() { return genesisTime; @@ -19,6 +21,14 @@ public void setGenesisTime(Date genesisTime) { this.genesisTime = genesisTime; } + public Integer getBalance() { + return balance; + } + + public void setBalance(Integer balance) { + this.balance = balance; + } + public List getKeys() { return keys; } diff --git a/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java b/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java index cc385f101..52ae6edae 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/ConfigUtils.java @@ -7,6 +7,7 @@ import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.operations.Deposit; import org.ethereum.beacon.core.state.Eth1Data; +import org.ethereum.beacon.core.types.Gwei; import org.ethereum.beacon.core.types.Time; import org.ethereum.beacon.crypto.BLS381.KeyPair; import org.ethereum.beacon.crypto.BLS381.PrivateKey; @@ -82,8 +83,12 @@ public static DepositContract createDepositContract(Contract config, BeaconChain EmulatorContract eConfig = (EmulatorContract) config; List keyPairs = createKeyPairs(eConfig.getKeys()); Random random = new Random(1); + Gwei amount = + eConfig.getBalance() != null + ? Gwei.ofEthers(eConfig.getBalance()) + : spec.getConstants().getMaxEffectiveBalance(); List deposits = SimulateUtils - .getDepositsForKeyPairs(UInt64.ZERO, random, keyPairs, spec, verifyProof); + .getDepositsForKeyPairs(UInt64.ZERO, random, keyPairs, spec, amount, verifyProof); Eth1Data eth1Data = new Eth1Data( Hash32.random(random), UInt64.valueOf(deposits.size()), Hash32.random(random)); diff --git a/start/node/src/main/resources/config/default-node-config.yml b/start/node/src/main/resources/config/default-node-config.yml index 0a294bdff..bb676be69 100644 --- a/start/node/src/main/resources/config/default-node-config.yml +++ b/start/node/src/main/resources/config/default-node-config.yml @@ -5,7 +5,8 @@ config: validator: contract: !emulator - genesisTime: 2019-05-21 11:00:00 + genesisTime: 2019-05-22 11:50:00 + balance: 55 keys: - !generate count: 16 @@ -40,3 +41,5 @@ chainSpec: blsVerify: false blsVerifyProofOfPossession: false blsSign: false + enableCache: false + From f59a68046c87fffc8741b9a47cdeea7942d2afae Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 17:42:46 +0300 Subject: [PATCH 74/96] Move netty to our Schedulers. Add node name for logs and thread namings. Set node-name MDC for all threads. Tune logging --- .../emulator/config/main/Configuration.java | 9 ++++ .../beacon/node/NodeCommandLauncher.java | 41 ++++++++++++++++--- .../ethereum/beacon/node/command/RunNode.java | 11 +++++ start/node/src/main/resources/log4j2.xml | 9 ++-- .../beacon/schedulers/DefaultSchedulers.java | 8 +++- .../ethereum/beacon/schedulers/Scheduler.java | 8 ++++ .../beacon/wire/net/netty/NettyClient.java | 13 +++++- .../beacon/wire/net/netty/NettyServer.java | 20 +++++---- 8 files changed, 98 insertions(+), 21 deletions(-) diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java index a0b5c83c6..1f6eb6ada 100644 --- a/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/main/Configuration.java @@ -6,10 +6,19 @@ /** Beacon chain configuration */ public class Configuration { + private String name; private String db; private List networks = new ArrayList<>(); private Validator validator; + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + public String getDb() { return db; } diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java index aa6cc4d17..5d6e99289 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -1,5 +1,6 @@ package org.ethereum.beacon.node; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.channel.ChannelFutureListener; import java.io.File; import java.net.InetSocketAddress; @@ -9,11 +10,12 @@ import java.util.Collections; import java.util.List; import java.util.Random; +import java.util.concurrent.ThreadFactory; import java.util.stream.IntStream; -import java.util.stream.Stream; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; @@ -28,13 +30,14 @@ import org.ethereum.beacon.emulator.config.chainspec.SpecData; import org.ethereum.beacon.emulator.config.main.MainConfig; import org.ethereum.beacon.emulator.config.main.Signer.Insecure; -import org.ethereum.beacon.emulator.config.main.ValidatorKeys; import org.ethereum.beacon.emulator.config.main.ValidatorKeys.Private; import org.ethereum.beacon.emulator.config.main.conract.EmulatorContract; import org.ethereum.beacon.emulator.config.main.network.NettyNetwork; import org.ethereum.beacon.emulator.config.main.network.Network; import org.ethereum.beacon.node.command.RunNode; import org.ethereum.beacon.pow.DepositContract; +import org.ethereum.beacon.schedulers.DefaultSchedulers; +import org.ethereum.beacon.schedulers.Scheduler; import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.start.common.NodeLauncher; import org.ethereum.beacon.start.common.util.MDCControlledSchedulers; @@ -96,11 +99,30 @@ public void init() { } public void run() { + String nodeName = config.getConfig().getName(); + if (nodeName != null) { + ThreadContext.put("validatorIndex", nodeName); + } + if (config.getChainSpec().isDefined()) logger.info("Overridden beacon chain parameters:\n{}", config.getChainSpec()); - Random rnd = new Random(); - Schedulers schedulers = Schedulers.createDefault(); + Schedulers schedulers = + new DefaultSchedulers() { + @Override + protected ThreadFactory createThreadFactory(String namePattern) { + ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true) + .setNameFormat((nodeName == null ? "" : nodeName + "-") + namePattern).build(); + if (nodeName == null) { + return factory; + } else { + return r -> factory.newThread(() -> { + ThreadContext.put("validatorIndex", nodeName); + r.run(); + }); + } + } + }; depositContract = ConfigUtils.createDepositContract( config.getConfig().getValidator().getContract(), @@ -120,7 +142,8 @@ public void run() { NettyNetwork nettyConfig = (NettyNetwork) networkCfg; NettyServer nettyServer = null; if (nettyConfig.getListenPort() != null) { - nettyServer = new NettyServer(nettyConfig.getListenPort()); + Scheduler serverScheduler = schedulers.newParallelDaemon("netty-server-%d", 16); + nettyServer = new NettyServer(nettyConfig.getListenPort(), serverScheduler::executeR); nettyServer.start().addListener((ChannelFutureListener) channelFuture -> { try { channelFuture.get(); @@ -130,7 +153,8 @@ public void run() { } }); } - NettyClient nettyClient = new NettyClient(); + Scheduler clientScheduler = schedulers.newParallelDaemon("netty-client-%d", 2); + NettyClient nettyClient = new NettyClient(clientScheduler::executeR); ConnectionManager tcpConnectionManager = new ConnectionManager<>(nettyServer, nettyClient, schedulers.reactorEvents()); connectionManager = tcpConnectionManager; @@ -181,6 +205,11 @@ public NodeCommandLauncher build() { SpecBuilder specBuilder = new SpecBuilder().withSpec(spec); + if (cliOptions.getName() != null) { + config.getConfig().setName(cliOptions.getName()); + } + + if (cliOptions.getListenPort() != null || cliOptions.getActivePeers() != null) { NettyNetwork network = (NettyNetwork) config .getConfig() diff --git a/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java index 5a4c3e0a9..b7d665c0f 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java @@ -56,6 +56,17 @@ public class RunNode implements Runnable { ) private List validators; + @CommandLine.Option( + names = {"--name"}, + paramLabel = "node-name", + description = "Node name for logs identification (when several nodes running)" + ) + private String name; + + public String getName() { + return name; + } + public Integer getListenPort() { return listenPort; } diff --git a/start/node/src/main/resources/log4j2.xml b/start/node/src/main/resources/log4j2.xml index 30bf2cda2..02648fc2d 100644 --- a/start/node/src/main/resources/log4j2.xml +++ b/start/node/src/main/resources/log4j2.xml @@ -6,22 +6,23 @@ - + - %d{HH:mm:ss.SSS} [%X{validatorTime}] %p %c{1.} [%t] %m%n + %d{HH:mm:ss.SSS} %p %c{1.} [%t] %m%n - + - %d{HH:mm:ss.SSS} [%X{validatorTime}] %p %c{1.} [%t] #%X{validatorIndex} %m%n + %d{HH:mm:ss.SSS} [#%X{validatorIndex}] %p %c{1.} [%t] %m%n + %d{HH:mm:ss.SSS} #%X{validatorIndex} %X{validatorTime} %-5level - %msg%n diff --git a/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java b/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java index 27d665eb6..195f38e68 100644 --- a/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java +++ b/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java @@ -3,6 +3,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; import java.util.function.Consumer; public class DefaultSchedulers extends AbstractSchedulers { @@ -29,7 +30,10 @@ protected Scheduler createExecutorScheduler(ScheduledExecutorService executorSer @Override protected ScheduledExecutorService createExecutor(String namePattern, int threads) { started = true; - return Executors.newScheduledThreadPool( - threads, new ThreadFactoryBuilder().setDaemon(true).setNameFormat(namePattern).build()); + return Executors.newScheduledThreadPool(threads, createThreadFactory(namePattern)); + } + + protected ThreadFactory createThreadFactory(String namePattern) { + return new ThreadFactoryBuilder().setDaemon(true).setNameFormat(namePattern).build(); } } diff --git a/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java b/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java index 4e6519647..b4d74da61 100644 --- a/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java +++ b/util/src/main/java/org/ethereum/beacon/schedulers/Scheduler.java @@ -16,10 +16,18 @@ public interface Scheduler { CompletableFuture executeAtFixedRate(Duration initialDelay, Duration period, RunnableEx task); + default CompletableFuture executeR(Runnable task) { + return execute(task::run); + } + default CompletableFuture execute(RunnableEx task) { return execute(() -> {task.run(); return null;}); } + default CompletableFuture executeWithDelayR(Duration delay, Runnable task) { + return executeWithDelay(delay, task::run); + } + default CompletableFuture executeWithDelay(Duration delay, RunnableEx task) { return executeWithDelay(delay, () -> {task.run(); return null;}); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyClient.java b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyClient.java index dd1a5590f..154b5555b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyClient.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyClient.java @@ -9,14 +9,23 @@ import io.netty.channel.socket.nio.NioSocketChannel; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; import org.ethereum.beacon.wire.net.Client; public class NettyClient implements Client { private final NioEventLoopGroup workerGroup; + public NettyClient(NioEventLoopGroup workerGroup) { + this.workerGroup = workerGroup; + } + + public NettyClient(Executor executor) { + this(new NioEventLoopGroup(2, executor)); + } + public NettyClient() { - workerGroup = new NioEventLoopGroup(2, - new ThreadFactoryBuilder().setNameFormat("netty-client-worker-%d").build()); + this(new NioEventLoopGroup(2, + new ThreadFactoryBuilder().setNameFormat("netty-client-worker-%d").build())); } @Override diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyServer.java b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyServer.java index d7e45bba4..3bdace966 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/netty/NettyServer.java @@ -9,6 +9,7 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LoggingHandler; +import java.util.concurrent.Executor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ethereum.beacon.wire.net.Server; @@ -23,10 +24,20 @@ public class NettyServer implements Server { private FluxSink channelsSink = channels.sink(); private final int port; private ChannelFuture channelFuture; + private final NioEventLoopGroup workerGroup; + public NettyServer(int port, NioEventLoopGroup workerGroup) { + this.port = port; + this.workerGroup = workerGroup; + } + + public NettyServer(int port, Executor executor) { + this(port, new NioEventLoopGroup(16, executor)); + } public NettyServer(int port) { - this.port = port; + this(port, new NioEventLoopGroup(16, + new ThreadFactoryBuilder().setNameFormat("netty-service-worker-%d").build())); } @Override @@ -40,15 +51,11 @@ private void onChannelActive(NettyChannel channel) { @Override public ChannelFuture start() { - NioEventLoopGroup bossGroup = new NioEventLoopGroup(1, - new ThreadFactoryBuilder().setNameFormat("netty-service-boss-%d").build()); - NioEventLoopGroup workerGroup = new NioEventLoopGroup(16, - new ThreadFactoryBuilder().setNameFormat("netty-service-worker-%d").build()); try { ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup); + b.group(workerGroup, workerGroup); b.channel(NioServerSocketChannel.class); b.option(ChannelOption.MESSAGE_SIZE_ESTIMATOR, DefaultMessageSizeEstimator.DEFAULT); @@ -73,7 +80,6 @@ public ChannelFuture start() { channelFuture.channel().closeFuture().addListener(aa -> { logger.debug("Incoming port is closed: " + port); workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); channelsSink.complete(); }); From 95d76ba950b83b7286aa0cece8616e3deba043ae Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 18:10:26 +0300 Subject: [PATCH 75/96] Return meaningful result from BeaconChain.insert() --- .../beacon/chain/DefaultBeaconChain.java | 14 +++++++------- .../beacon/chain/MutableBeaconChain.java | 12 +++++++++++- .../chain/ProposedBlockProcessorImpl.java | 5 +++-- .../beacon/chain/DefaultBeaconChainTest.java | 3 ++- .../beacon/wire/sync/SyncManagerImpl.java | 18 +++++++++++++++++- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/DefaultBeaconChain.java b/chain/src/main/java/org/ethereum/beacon/chain/DefaultBeaconChain.java index aa3fca282..3ada9d6ec 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/DefaultBeaconChain.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/DefaultBeaconChain.java @@ -98,17 +98,17 @@ private void initializeStorage() { } @Override - public synchronized boolean insert(BeaconBlock block) { + public synchronized ImportResult insert(BeaconBlock block) { if (rejectedByTime(block)) { - return false; + return ImportResult.ExpiredBlock; } if (exist(block)) { - return false; + return ImportResult.ExistingBlock; } if (!hasParent(block)) { - return false; + return ImportResult.NoParent; } long s = System.nanoTime(); @@ -121,7 +121,7 @@ public synchronized boolean insert(BeaconBlock block) { if (!blockVerification.isPassed()) { logger.warn("Block verification failed: " + blockVerification + ": " + block.toString(spec.getConstants(), parentState.getGenesisTime(), spec::signing_root)); - return false; + return ImportResult.InvalidBlock; } BeaconStateEx postBlockState = blockTransition.apply(preBlockState, block); @@ -130,7 +130,7 @@ public synchronized boolean insert(BeaconBlock block) { stateVerifier.verify(postBlockState, block); if (!stateVerification.isPassed()) { logger.warn("State verification failed: " + stateVerification); - return false; + return ImportResult.StateMismatch; } BeaconTuple newTuple = BeaconTuple.of(block, postBlockState); @@ -154,7 +154,7 @@ public synchronized boolean insert(BeaconBlock block) { spec::signing_root), String.format("%.3f", ((double) total) / 1_000_000_000d)); - return true; + return ImportResult.OK; } @Override diff --git a/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java b/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java index ebd575f89..a2c42c64f 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java @@ -4,11 +4,21 @@ public interface MutableBeaconChain extends BeaconChain { + enum ImportResult { + OK, + ExistingBlock, + NoParent, + ExpiredBlock, + InvalidBlock, + StateMismatch, + OtherError + } + /** * Inserts new block into a chain. * * @param block a block. * @return whether a block was inserted or not. */ - boolean insert(BeaconBlock block); + ImportResult insert(BeaconBlock block); } diff --git a/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java index 7e6e1ce0c..0ff909478 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/ProposedBlockProcessorImpl.java @@ -1,5 +1,6 @@ package org.ethereum.beacon.chain; +import org.ethereum.beacon.chain.MutableBeaconChain.ImportResult; import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.schedulers.Schedulers; import org.ethereum.beacon.stream.SimpleProcessor; @@ -20,8 +21,8 @@ public ProposedBlockProcessorImpl(MutableBeaconChain beaconChain, Schedulers sch @Override public void newBlockProposed(BeaconBlock newBlock) { - boolean result = beaconChain.insert(newBlock); - if (result) { + ImportResult result = beaconChain.insert(newBlock); + if (result == ImportResult.OK) { blocksStream.onNext(newBlock); } } diff --git a/chain/src/test/java/org/ethereum/beacon/chain/DefaultBeaconChainTest.java b/chain/src/test/java/org/ethereum/beacon/chain/DefaultBeaconChainTest.java index ab16df449..e044aa91d 100644 --- a/chain/src/test/java/org/ethereum/beacon/chain/DefaultBeaconChainTest.java +++ b/chain/src/test/java/org/ethereum/beacon/chain/DefaultBeaconChainTest.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.stream.IntStream; +import org.ethereum.beacon.chain.MutableBeaconChain.ImportResult; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.chain.storage.impl.SSZBeaconChainStorageFactory; import org.ethereum.beacon.chain.storage.impl.SerializerFactory; @@ -54,7 +55,7 @@ public void insertAChain() { BeaconTuple recentlyProcessed = beaconChain.getRecentlyProcessed(); BeaconBlock aBlock = createBlock(recentlyProcessed, spec, schedulers.getCurrentTime(), perSlotTransition); - Assert.assertTrue(beaconChain.insert(aBlock)); + Assert.assertEquals(ImportResult.OK, beaconChain.insert(aBlock)); Assert.assertEquals(aBlock, beaconChain.getRecentlyProcessed().getBlock()); System.out.println("Inserted block: " + (idx + 1)); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java index 23964559a..4d4a44106 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java @@ -1,5 +1,12 @@ package org.ethereum.beacon.wire.sync; +import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.ExistingBlock; +import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.ExpiredBlock; +import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.InvalidBlock; +import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.NoParent; +import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.OK; +import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.StateMismatch; + import java.time.Duration; import java.util.ArrayList; import java.util.Collections; @@ -10,6 +17,7 @@ import org.ethereum.beacon.chain.BeaconTuple; import org.ethereum.beacon.chain.BeaconTupleDetails; import org.ethereum.beacon.chain.MutableBeaconChain; +import org.ethereum.beacon.chain.MutableBeaconChain.ImportResult; import org.ethereum.beacon.chain.storage.BeaconChainStorage; import org.ethereum.beacon.consensus.BeaconChainSpec; import org.ethereum.beacon.core.BeaconBlock; @@ -112,12 +120,20 @@ public SyncManagerImpl( Flux.from(syncQueue.getBlocksStream()) .subscribe( block -> { - if (!chain.insert(block.get())) { + ImportResult result = chain.insert(block.get()); + if (result == InvalidBlock || result == StateMismatch || result == ExpiredBlock) { block.feedbackError( new WireInvalidConsensusDataException( "Couldn't insert block: " + block.get())); } else { block.feedbackSuccess(); + if (result == NoParent) { + logger.warn("No parent for block: " + block.get()); + } else if (result == ExistingBlock) { + logger.info("Trying to import existing block: " + block.get()); + } else if (result != OK) { + logger.info("Other error importing block: " + block.get()); + } } }); } From f317c9b18b88382c825c6a083632e8b07de8ff9b Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Wed, 22 May 2019 19:03:05 +0300 Subject: [PATCH 76/96] Submit to SyncManager not only inbound new blocks but own created and verified blocks as well --- .../beacon/start/common/NodeLauncher.java | 33 +++++++++++-------- .../beacon/wire/sync/AbstractBlockTree.java | 28 +++++++++++----- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java index 966904dfc..e82a05716 100644 --- a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java @@ -21,6 +21,7 @@ import org.ethereum.beacon.consensus.transition.StateCachingTransition; import org.ethereum.beacon.consensus.verifier.BeaconBlockVerifier; import org.ethereum.beacon.consensus.verifier.BeaconStateVerifier; +import org.ethereum.beacon.core.BeaconBlock; import org.ethereum.beacon.core.operations.Attestation; import org.ethereum.beacon.db.InMemoryDatabase; import org.ethereum.beacon.pow.DepositContract; @@ -177,20 +178,7 @@ void chainStarted(ChainStart chainStartEvent) { blockTree = new BeaconBlockTree(spec.getObjectHasher()); syncQueue = new SyncQueueImpl(blockTree); - syncManager = new SyncManagerImpl( - beaconChain, - Flux.from(wireApiSub.inboundBlocksStream()).map(Feedback::of), - beaconChainStorage, - spec, - wireApiSyncRemote, - syncQueue, - 1, - schedulers.reactorEvents()); - - if (startSyncManager) { - syncManager.start(); - } - + Flux ownBlocks = Flux.empty(); if (validatorCred != null) { beaconChainProposer = new BeaconChainProposerImpl(spec, perBlockTransition, depositContract); beaconChainAttester = new BeaconChainAttesterImpl(spec); @@ -213,12 +201,29 @@ void chainStarted(ChainStart chainStartEvent) { Flux.from(beaconChainValidator.getAttestationsStream()).subscribe(wireApiSub::sendAttestation); Flux.from(beaconChainValidator.getAttestationsStream()).subscribe(allAttestations); + + ownBlocks = Flux.from(proposedBlocksProcessor.processedBlocksStream()); } Flux.from(wireApiSub.inboundAttestationsStream()) .publishOn(schedulers.reactorEvents()) .subscribe(allAttestations); + Flux allNewBlocks = Flux.merge(ownBlocks, wireApiSub.inboundBlocksStream()); + syncManager = new SyncManagerImpl( + beaconChain, + allNewBlocks.map(Feedback::of), + beaconChainStorage, + spec, + wireApiSyncRemote, + syncQueue, + 1, + schedulers.reactorEvents()); + + if (startSyncManager) { + syncManager.start(); + } + // Flux.from(wireApiSub.inboundBlocksStream()) // .publishOn(schedulers.reactorEvents()) // .subscribe(beaconChain::insert); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java index 639030640..cfcf05815 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/AbstractBlockTree.java @@ -9,6 +9,8 @@ import java.util.Map.Entry; import java.util.stream.Collectors; import javax.annotation.Nonnull; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ethereum.beacon.wire.sync.AbstractBlockTree.BlockWrap; public abstract class AbstractBlockTree, TRawBlock> @@ -18,6 +20,8 @@ interface BlockWrap extends Block { TRawBlock get(); } + private static final Logger logger = LogManager.getLogger(AbstractBlockTree.class); + private TBlock topBlock; private final Map hashMap = new HashMap<>(); private final Map> childrenMap = new HashMap<>(); @@ -38,17 +42,23 @@ public synchronized List addBlock(@Nonnull TBlock block) { if (topBlock == null) { throw new IllegalStateException("Top block should be set first"); } - if (hashMap.containsKey(block.getHash())) return Collections.emptyList(); - if (topBlock.getHeight() >= block.getHeight()) return Collections.emptyList(); - hashMap.put(block.getHash(), block); - childrenMap.computeIfAbsent(block.getParentHash(), r -> new ArrayList<>()).add(block.getHash()); + try { + if (hashMap.containsKey(block.getHash())) return Collections.emptyList(); + if (topBlock.getHeight() >= block.getHeight()) return Collections.emptyList(); + hashMap.put(block.getHash(), block); + childrenMap.computeIfAbsent(block.getParentHash(), r -> new ArrayList<>()).add(block.getHash()); - List ret = new ArrayList<>(); - if (isRootSuccessor(block)) { - ret.add(block); - addChildrenRecursively(block.getHash(), ret); + List ret = new ArrayList<>(); + if (isRootSuccessor(block)) { + ret.add(block); + addChildrenRecursively(block.getHash(), ret); + } + logger.debug("Returning " + ret.size() + " ready blocks on added block " + block + " ~~> " + ret); + return ret; + } catch (Exception e) { + logger.error("Exception adding block: " + block, e); + throw new RuntimeException(e); } - return ret; } private boolean isRootSuccessor(TBlock block) { From 941b61a9d000be583f896fff7425121d5adf554b Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 23 May 2019 09:53:51 +0300 Subject: [PATCH 77/96] Fix concurrent modification, make stream elements (arrays) immutable --- .../ethereum/beacon/wire/sync/SyncManagerImpl.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java index 4d4a44106..0775291b4 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java @@ -1,5 +1,6 @@ package org.ethereum.beacon.wire.sync; +import static java.lang.Math.max; import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.ExistingBlock; import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.ExpiredBlock; import static org.ethereum.beacon.chain.MutableBeaconChain.ImportResult.InvalidBlock; @@ -193,15 +194,14 @@ public ModeDetector( s1.retainAll(s2); return s1.isEmpty() ? SyncMode.Long : SyncMode.Short; }) - .distinctUntilChanged(); + .distinctUntilChanged() + .onErrorContinue((t, o) -> logger.error("Unexpected error: ", t)); } private ArrayList listAddLimited(ArrayList list, A elem, int maxSize) { - list.add(elem); - if (list.size() > maxSize) { - list.remove(0); - } - return list; + ArrayList ret = new ArrayList<>(list.subList(max(0, list.size() + 1 - maxSize), list.size())); + ret.add(elem); + return ret; } public Publisher getSyncModeStream() { From 14905c96687b5fa49f7c6385597b950510e7e741 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 23 May 2019 10:31:24 +0300 Subject: [PATCH 78/96] Write unhandled exceptions to log by default --- .../ethereum/beacon/schedulers/DefaultSchedulers.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java b/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java index 195f38e68..ed3174c2a 100644 --- a/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java +++ b/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java @@ -5,14 +5,14 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.function.Consumer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class DefaultSchedulers extends AbstractSchedulers { - private Consumer errorHandler = - t -> { - System.err.println("ErrorHandlingScheduler (default error handler):"); - t.printStackTrace(); - }; + private static final Logger logger = LogManager.getLogger(DefaultSchedulers.class); + + private Consumer errorHandler = t -> logger.error("Unhandled exception:", t); private volatile boolean started; public void setErrorHandler(Consumer errorHandler) { From 4aaf9178490683dfefcefd1f4039b3d5dceebe21 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 23 May 2019 11:12:23 +0300 Subject: [PATCH 79/96] Fix Log4j warnings at startup --- start/node/src/main/resources/log4j2.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/start/node/src/main/resources/log4j2.xml b/start/node/src/main/resources/log4j2.xml index 02648fc2d..00f89c297 100644 --- a/start/node/src/main/resources/log4j2.xml +++ b/start/node/src/main/resources/log4j2.xml @@ -15,7 +15,8 @@ - + %d{HH:mm:ss.SSS} [#%X{validatorIndex}] %p %c{1.} [%t] %m%n From f7702f1cbbac902af654eab6557e9ac31388fc5e Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 23 May 2019 11:14:42 +0300 Subject: [PATCH 80/96] Set test genesis slot to 0 --- start/node/src/main/resources/config/default-node-config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/start/node/src/main/resources/config/default-node-config.yml b/start/node/src/main/resources/config/default-node-config.yml index bb676be69..828c31f4d 100644 --- a/start/node/src/main/resources/config/default-node-config.yml +++ b/start/node/src/main/resources/config/default-node-config.yml @@ -5,7 +5,7 @@ config: validator: contract: !emulator - genesisTime: 2019-05-22 11:50:00 + genesisTime: 2019-05-23 08:00:00 balance: 55 keys: - !generate @@ -20,7 +20,7 @@ config: chainSpec: specConstants: initialValues: - GENESIS_SLOT: 1000000 + GENESIS_SLOT: 0 miscParameters: SHARD_COUNT: 4 TARGET_COMMITTEE_SIZE: 2 From 56b12db429c5bc825cb1247e888a052c94aa0ee7 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 23 May 2019 16:49:06 +0300 Subject: [PATCH 81/96] Wire javadoc added --- .../org/ethereum/beacon/wire/Feedback.java | 28 ++++++++++++ .../beacon/wire/MessageSerializer.java | 3 ++ .../java/org/ethereum/beacon/wire/Peer.java | 12 +++++ .../org/ethereum/beacon/wire/PeerManager.java | 39 +++++++++++++--- .../beacon/wire/SimplePeerManagerImpl.java | 14 ++---- .../org/ethereum/beacon/wire/WireApiPeer.java | 3 ++ .../org/ethereum/beacon/wire/WireApiSub.java | 17 +++++++ .../beacon/wire/WireApiSubRouter.java | 4 ++ .../org/ethereum/beacon/wire/WireApiSync.java | 31 +++++++++---- .../beacon/wire/WireApiSyncServer.java | 3 ++ .../ethereum/beacon/wire/channel/Channel.java | 27 ++++++++++++ .../beacon/wire/channel/ChannelOp.java | 8 ++++ .../WireIllegalArgumentsException.java | 3 ++ .../WireInvalidConsensusDataException.java | 3 ++ .../WireInvalidResponseException.java | 3 ++ .../wire/exceptions/WireRemoteRpcError.java | 5 ++- .../exceptions/WireRpcClosedException.java | 3 ++ .../wire/exceptions/WireRpcException.java | 3 ++ .../exceptions/WireRpcTimeoutException.java | 3 ++ .../org/ethereum/beacon/wire/net/Client.java | 7 +++ .../org/ethereum/beacon/wire/net/Server.java | 16 +++++++ .../ethereum/beacon/wire/sync/BlockTree.java | 44 ++++++++++++++----- .../ethereum/beacon/wire/sync/SyncQueue.java | 30 +++++++++++++ .../beacon/wire/sync/WireApiSyncRouter.java | 5 +++ .../org/ethereum/beacon/wire/PeersTest.java | 5 +-- 25 files changed, 282 insertions(+), 37 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java b/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java index 387eb4a1c..62bbe1faa 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/Feedback.java @@ -3,22 +3,50 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Function; +/** + * Wrapper for some asynchronous result for which the result consumer may leave a feedback. + * + * E.g. blocks downloaded from a remote peer are then later process and verified. The PeerManager + * may want to know if a peer sends invalid blocks and thus ban it. + * + * @param + */ public interface Feedback { static Feedback of(T result) { return new Impl<>(result); } + /** + * Return the wrapped value + */ TResult get(); + /** + * Report the value is OK + */ void feedbackSuccess(); + /** + * Report the value is erroneous + */ void feedbackError(Throwable e); + /** + * Creates a CompletableFuture which is done when feedback left + */ CompletableFuture getFeedback(); + /** + * Creates another Feedback with other value which forwards feedback to this instance + * @see #map(Function) + */ Feedback delegate(TOtherResult otherResult); + /** + * Convenient shortcut for {@link #delegate(Object)} method. + * Converts wrapped value to another Feedback wrapped value + */ default Feedback map(Function mapper) { return delegate(mapper.apply(get())); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java b/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java index d4b33394e..30383a830 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/MessageSerializer.java @@ -3,6 +3,9 @@ import org.ethereum.beacon.wire.message.Message; import tech.pegasys.artemis.util.bytes.BytesValue; +/** + * Serialize/deserialize RPC messages envelop + */ public interface MessageSerializer { BytesValue serialize(Message message); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java index 189c57b18..f021da60d 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/Peer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/Peer.java @@ -3,12 +3,24 @@ import org.ethereum.beacon.wire.channel.Channel; import tech.pegasys.artemis.util.bytes.BytesValue; +/** + * Represent connected peer + */ public interface Peer { + /** + * Returns raw bytes {@link Channel} of this peer + */ Channel getRawChannel(); + /** + * Returns {@link WireApiSync} instance linked to this peer + */ WireApiSync getSyncApi(); + /** + * Returns {@link WireApiSub} instance linked to this peer + */ WireApiSub getSubApi(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java b/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java index c24f4a309..3f71033ae 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/PeerManager.java @@ -1,19 +1,48 @@ package org.ethereum.beacon.wire; -import java.util.Collection; +import java.util.List; +import org.ethereum.beacon.stream.RxUtil; import org.reactivestreams.Publisher; +/** + * Manages connected peers and aggregates their `high-level` APIs + */ public interface PeerManager { + /** + * Stream of new peer connections + */ Publisher connectedPeerStream(); + /** + * Stream of peer disconnects + * Peer must occur in this stream strictly after occurring in the {@link #connectedPeerStream()} + */ Publisher disconnectedPeerStream(); - Publisher activePeerStream(); - - Collection getActivePeers(); - + /** + * Steam of new active peers which are connected and handshake was done. + * A peer appearing in this stream is available for 'high-level' APIs calls + */ + Publisher activatedPeerStream(); + + /** + * Stream of currently active peers list + */ + default Publisher> activePeersStream() { + return RxUtil.collect(activatedPeerStream(), disconnectedPeerStream()); + } + + /** + * Returns WireApiSync instance which is the aggregation of all connected peer APIs + * When currently no active peers the API enqueues invocations and execute them upon any + * active peer connected + */ WireApiSync getWireApiSync(); + /** + * Returns WireApiSub instance which is the aggregation of all connected peer APIs. + * When currently no active peers the instance just ignores outbound notifications. + */ WireApiSub getWireApiSub(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java index eba70798f..0fe23f267 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/SimplePeerManagerImpl.java @@ -2,7 +2,6 @@ import java.time.Duration; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import org.apache.logging.log4j.LogManager; @@ -65,14 +64,14 @@ public SimplePeerManagerImpl( .map(this::createPeer) .replay(1).autoConnect(); - Flux.from(activePeerStream()).subscribe(this::onNewActivePeer); + Flux.from(activatedPeerStream()).subscribe(this::onNewActivePeer); wireApiSyncRouter = new WireApiSyncRouter( - Flux.from(activePeerStream()).map(Peer::getSyncApi), + Flux.from(activatedPeerStream()).map(Peer::getSyncApi), Flux.from(disconnectedPeerStream()).map(Peer::getSyncApi)); wireApiSubRouter = new WireApiSubRouter( - Flux.from(activePeerStream()).map(Peer::getSubApi), + Flux.from(activatedPeerStream()).map(Peer::getSubApi), Flux.from(disconnectedPeerStream()).map(Peer::getSubApi)); } @@ -104,7 +103,7 @@ public Publisher disconnectedPeerStream() { } @Override - public Publisher activePeerStream() { + public Publisher activatedPeerStream() { return connectedPeersStream.flatMap( peer -> Mono.fromFuture(peer.getPeerActiveFuture().thenApply(v -> peer))); } @@ -115,11 +114,6 @@ protected void onNewActivePeer(Peer peer) { peer.getRawChannel().getCloseFuture().thenAccept(v -> activePeers.remove(peer)); } - @Override - public Collection getActivePeers() { - return activePeers; - } - @Override public WireApiSync getWireApiSync() { return wireApiSyncRouter; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java index 4e5fe70dc..37c4507b1 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiPeer.java @@ -3,6 +3,9 @@ import org.ethereum.beacon.wire.message.payload.GoodbyeMessage; import org.ethereum.beacon.wire.message.payload.HelloMessage; +/** + * Represents synchronous interface for peers 'low level' interaction + */ public interface WireApiPeer { void hello(HelloMessage message); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java index 5ac2d7fd8..aaa6fa787 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSub.java @@ -4,13 +4,30 @@ import org.ethereum.beacon.core.operations.Attestation; import org.reactivestreams.Publisher; +/** + * Represents asynchronous wire interface for subscription-like messages + */ public interface WireApiSub { + /** + * Sends a new block to remote peer(s) + */ void sendProposedBlock(BeaconBlock block); + /** + * Sends a new attestation to remote peer(s) + */ void sendAttestation(Attestation attestation); + /** + * Stream of new blocks from remote peer(s) + * This stream must be distinct, i.e. doesn't contain duplicate blocks + */ Publisher inboundBlocksStream(); + /** + * Stream of new attestations from remote peer(s) + * This stream must be distinct, i.e. doesn't contain duplicate attestations + */ Publisher inboundAttestationsStream(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java index 1a2f70f3a..f3ed98232 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSubRouter.java @@ -13,6 +13,10 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; +/** + * Tracks and aggregates {@link WireApiSub} instances from separate peers + * The class realizes flood pub strategy for notifications propagation + */ public class WireApiSubRouter implements WireApiSub { private static final int DUPLICATE_DETECTION_SET_SIZE = 64; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java index 435e3aec8..e61690b57 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSync.java @@ -19,32 +19,47 @@ import org.ethereum.beacon.wire.message.payload.BlockRootsResponseMessage; import tech.pegasys.artemis.ethereum.core.Hash32; +/** + * Asynchronous wire interface for downloading blockchain sync data from remote peer(s) + */ public interface WireApiSync { int MAX_BLOCK_ROOTS_COUNT = 32768; + /** + * Requests block roots from remote peer(s) + */ CompletableFuture requestBlockRoots( BlockRootsRequestMessage requestMessage); + /** + * Requests block headers from remote peer(s) + */ CompletableFuture requestBlockHeaders( BlockHeadersRequestMessage requestMessage); + /** + * Requests block bodies from remote peer(s) + */ CompletableFuture> requestBlockBodies( BlockBodiesRequestMessage requestMessage); + /** + * Handy shortcut to download headers+bodies + */ default CompletableFuture>> requestBlocks( BlockHeadersRequestMessage requestMessage, ObjectHasher hasher) { CompletableFuture> headersFuture = requestBlockHeaders( requestMessage).thenApply(BlockHeadersResponseMessage::getHeaders); - CompletableFuture>> bodiesFuture = headersFuture - .thenCompose(headers -> { - List blockHashes = headers.stream() - .map(hasher::getHashTruncateLast) - .collect(Collectors.toList()); - return requestBlockBodies(new BlockBodiesRequestMessage(blockHashes)) - .thenApply(bb -> bb.delegate(bb.get().getBlockBodies())); - }); + CompletableFuture>> bodiesFuture = + headersFuture.thenCompose( + headers -> { + List blockHashes = + headers.stream().map(hasher::getHashTruncateLast).collect(Collectors.toList()); + return requestBlockBodies(new BlockBodiesRequestMessage(blockHashes)) + .thenApply(bb -> bb.map(BlockBodiesResponseMessage::getBlockBodies)); + }); return headersFuture.thenCombine( bodiesFuture, diff --git a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java index 599b9a9b8..1b19e0c2c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/WireApiSyncServer.java @@ -24,6 +24,9 @@ import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; +/** + * Serves {@link WireApiSync} requests supplying local blockchain information to remote party + */ public class WireApiSyncServer implements WireApiSync { private final BeaconChainStorage storage; diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java index 665cb2808..71a91d4a0 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/Channel.java @@ -4,16 +4,43 @@ import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; +/** + * Duplex channel handling one (inbound) or two (inbound/outbound) streams of abstract messages + * The channel is assumed closed when {@link #inboundMessageStream()} is in Complete + * state. + */ public interface Channel { + /** + * Returns the steam of inbound messages. When the stream completes the channel is assumed + * closed. + * The publisher returned must cache all messages and flush them upon the first subscription. + * It may handle several subscribers and it's upon implementation what past messages + * to replay to later subscribers + */ Publisher inboundMessageStream(); + /** + * This method is called if the client wants to send any messages to this channel. + * Normally the outboundMessageStream is immediately subscribed to during the call + * When this method called more than than once the behaviour is not specified + * but advanced implementations may merge messages from different publishers + */ void subscribeToOutbound(Publisher outboundMessageStream); + /** + * Returns the future which completes when this channel is closed. + * This default implementation just subscribes to {@link #inboundMessageStream()} and + * waits for it to complete. Implementing classes may override it for more effective implementation. + */ default CompletableFuture getCloseFuture() { return Mono.ignoreElements(inboundMessageStream()).toFuture().thenApply(ignore -> null); } + /** + * Closes this channel. {@link #inboundMessageStream()} will complete + * synchronously/asynchronously during/after this call. + */ default void close() { } } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java index 9953f16dc..dd9037552 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/ChannelOp.java @@ -1,6 +1,14 @@ package org.ethereum.beacon.wire.channel; +/** + * Represents {@link Channel} operation which has inbound {@link Channel} with messages of + * type TInMessage, maps these messages to TOutMessage type + * and serves them as a {@link Channel} itself + */ public interface ChannelOp extends Channel { + /** + * Returns the source {@link Channel} + */ Channel getInChannel(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java index 8c3dd87b4..4aae7e508 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireIllegalArgumentsException.java @@ -1,5 +1,8 @@ package org.ethereum.beacon.wire.exceptions; +/** + * Is thrown on malformed request from remote side + */ public class WireIllegalArgumentsException extends WireException { public WireIllegalArgumentsException(String message) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java index b80e17d3a..503d0425e 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidConsensusDataException.java @@ -1,5 +1,8 @@ package org.ethereum.beacon.wire.exceptions; +/** + * Is thrown when the data from a remote party violates consensus rules + */ public class WireInvalidConsensusDataException extends WireException { public WireInvalidConsensusDataException(String message) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java index a0aba3b1f..2836efd78 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireInvalidResponseException.java @@ -1,5 +1,8 @@ package org.ethereum.beacon.wire.exceptions; +/** + * When a remote party replied with invalid or inconsistent data to our request + */ public class WireInvalidResponseException extends WireException { public WireInvalidResponseException(String message) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java index e10d47bef..6188a0b1a 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRemoteRpcError.java @@ -1,6 +1,9 @@ package org.ethereum.beacon.wire.exceptions; -public class WireRemoteRpcError extends WireException { +/** + * This exception is a 'deserialized version' of error answer from remote RPC party + */ +public class WireRemoteRpcError extends WireRpcException { public WireRemoteRpcError(String message) { super(message); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java index fc07ee95e..ba6064fc7 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcClosedException.java @@ -1,5 +1,8 @@ package org.ethereum.beacon.wire.exceptions; +/** + * Thrown when calling RPC method on closed channel + */ public class WireRpcClosedException extends WireRpcException { public WireRpcClosedException(String message) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java index 38e7c6cde..31cd811d0 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcException.java @@ -1,5 +1,8 @@ package org.ethereum.beacon.wire.exceptions; +/** + * Any RPC interaction exception + */ public class WireRpcException extends WireException { public WireRpcException(String message) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java index 13ca72ed6..c0a6a3c60 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/exceptions/WireRpcTimeoutException.java @@ -1,5 +1,8 @@ package org.ethereum.beacon.wire.exceptions; +/** + * Thrown when no reply to RPC request for some time + */ public class WireRpcTimeoutException extends WireRpcException { public WireRpcTimeoutException(String message) { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java b/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java index a76c5ca47..db0274d49 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/Client.java @@ -4,8 +4,15 @@ import org.ethereum.beacon.wire.channel.Channel; import tech.pegasys.artemis.util.bytes.BytesValue; +/** + * An abstract client which can connect to remote party by supplying its abstract TAddress + */ public interface Client { + /** + * Connects to remote party and returns the bytes {@link Channel} upon connection + * If connecting fails the {@link CompletableFuture} return will be completed with exception + */ > CompletableFuture connect(TAddress address); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java b/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java index ec44b95f3..e7104c3c9 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/Server.java @@ -5,12 +5,28 @@ import org.reactivestreams.Publisher; import tech.pegasys.artemis.util.bytes.BytesValue; +/** + * Abstract server which accepts inbound connections making bytes {@link Channel}'s from them. + */ public interface Server extends AutoCloseable { + /** + * Stream of connected channels. + * This publisher should queue and connections made before anyone subscribed and replay them + * to the first subscriber. The same rule applies to the Channel's inbound bytes + * (see {@link Channel#inboundMessageStream()}) + */ Publisher> channelsStream(); + /** + * Start listening for inbound connections + * @return Future which indicates when ready to accept connections or error + */ ChannelFuture start(); + /** + * Stops listening and release any system resources allocated + */ void stop(); @Override diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java index 907c59c98..3cbf7788c 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java @@ -1,13 +1,17 @@ package org.ethereum.beacon.wire.sync; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import javax.annotation.Nonnull; import org.ethereum.beacon.wire.sync.BlockTree.Block; +/** + * Builds a tree of added blocks returning block chains linked to the top block + */ public interface BlockTree> { + /** + * Abstract blockchain Block which has a Hash, a Parent and a Height + */ interface Block { THash getHash(); @@ -17,17 +21,37 @@ interface Block { long getHeight(); } + /** + * Adds a new block to the block tree and returns a list of blocks + * (in order of their inheritance - first parents then children) + * which became linked to the {@link #getTopBlock()} due to adding this + * block (including the block itself). + * All blocks returned across all calls to this method ar unique, i.e. no + * block returned twice. + * Any block returned from this method is connected to initial TopBlock + * with blocks already returned from this method before. + * E.g. + * - if the supplied block has no parents in the current tree, then block is stored + * but empty list is returned + * - if the supplied block has a parent but no existing children then only this block is + * returned + * - if the supplied block has a parent and a number of descendants then + * this block + all its descendants returned + * + * Blocks with height less than {@link #getTopBlock()} are dropped + * Blocks with height bigger than some threshold above {@link #getTopBlock()} are dropped + * Duplicate blocks are ignored + */ @Nonnull List addBlock(@Nonnull TBlock block); - default List addChainedBlocks(Collection blocks) { - List ret = new ArrayList<>(); - for (TBlock block : blocks) { - ret.addAll(addBlock(block)); - } - return ret; - } - + /** + * Sets the top final root block + * All blocks with height less than top block height are removed from the tree + */ void setTopBlock(@Nonnull TBlock block); + /** + * Returns current Top Block + */ @Nonnull TBlock getTopBlock(); } diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java index a61c58965..7e84445b0 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncQueue.java @@ -11,14 +11,44 @@ import tech.pegasys.artemis.ethereum.core.Hash32; import tech.pegasys.artemis.util.uint.UInt64; +/** + * The class which declares what blocks are wanted to be downloaded, consumes + * downloaded blocks, builds chains of blocks linked to the finalized block and + * streams them for importing. + */ public interface SyncQueue { + /** + * Potentially unbounded stream of blocks wanted to be downloaded. + * The stream may be unbounded because the queue tries to retrieve any + * new blocks above the final block again and again with the hope that + * something new can be discovered. Thus the consumer should have a mechanism + * of limiting these requests to prevent traffic overhead. + */ Publisher getBlockRequestsStream(); + /** + * The stream of blocks ready to be imported to the blockchain. + * Any issued block must be a child of some block issued before. + * Blocks are wrapped to a {@link Feedback} instance so + * block verification and importing result should be reported via this {@link Feedback} + */ Publisher> getBlocksStream(); + /** + * finalBlockRootStream notifies the {@link SyncQueue} on finalized blocks + * so the queue may stick to those blocks and perform necessary cleanup + * of outdated blocks + */ Disposable subscribeToFinalBlocks(Flux finalBlockRootStream); + /** + * All new blocks are streamed via blocksStream. + * Those blocks may include: + * - downloaded per {@link SyncQueue} requests blocks + * - new fresh blocks broadcasted from remote parties + * - new blocks proposed by local validators + */ Disposable subscribeToNewBlocks(Publisher>> blocksStream); class BlockRequest { diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java index 90fc8e5b9..b96eb63e3 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java @@ -18,6 +18,11 @@ import reactor.core.publisher.FluxSink; import reactor.core.publisher.ReplayProcessor; +/** + * Tracks and aggregates {@link WireApiSync} instances from separate peers + * This is a pretty simple implementation which just delegates API calls in a round robin fashion + * When no single delegate api available all calls are queued until any api arises + */ public class WireApiSyncRouter implements WireApiSync { private final ReplayProcessor> tasks = ReplayProcessor.create(64); diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index 52e0f4a86..d316723e5 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -6,7 +6,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import org.ethereum.beacon.start.common.Launcher; import org.ethereum.beacon.simulator.SimulatorLauncher; import org.ethereum.beacon.simulator.SimulatorLauncher.Builder; @@ -108,7 +107,7 @@ public void test1() throws Exception { .doOnNext(msg -> System.out.println("#### on message")) .subscribe(); }); - Flux.from(peerManager.activePeerStream()) + Flux.from(peerManager.activatedPeerStream()) .subscribe(peer -> System.out.println("Remote peer active: " + peer)); Flux.from(peerManager.disconnectedPeerStream()) .subscribe(peer -> System.out.println("Remote peer disconnected: " + peer)); @@ -135,7 +134,7 @@ public void test1() throws Exception { Flux.from(peerManager.connectedPeerStream()) .subscribe(peer -> System.out.println("Peer 1 connected: " + peer)); - Flux.from(peerManager.activePeerStream()) + Flux.from(peerManager.activatedPeerStream()) .subscribe(peer -> System.out.println("Peer 1 active: " + peer)); Flux.from(peerManager.disconnectedPeerStream()) .subscribe(peer -> System.out.println("Peer 1 disconnected: " + peer)); From 1da43bee847a320a52edceb7065bd942524e7a69 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 23 May 2019 18:51:35 +0300 Subject: [PATCH 82/96] Make syncManager sync mode delays configurable (should be 0 delay for tests) --- .../beacon/start/common/NodeLauncher.java | 3 ++- .../beacon/wire/sync/SyncManagerImpl.java | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java index e82a05716..31e3a6475 100644 --- a/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java +++ b/start/common/src/main/java/org/ethereum/beacon/start/common/NodeLauncher.java @@ -1,6 +1,6 @@ package org.ethereum.beacon.start.common; -import java.net.SocketAddress; +import java.time.Duration; import java.util.List; import org.ethereum.beacon.chain.DefaultBeaconChain; import org.ethereum.beacon.chain.MutableBeaconChain; @@ -219,6 +219,7 @@ void chainStarted(ChainStart chainStartEvent) { syncQueue, 1, schedulers.reactorEvents()); + syncManager.setRequestsDelay(Duration.ofSeconds(1), Duration.ofSeconds(5)); if (startSyncManager) { syncManager.start(); diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java index 0775291b4..68523ee77 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/SyncManagerImpl.java @@ -62,6 +62,9 @@ public enum SyncMode { private Disposable finalizedBlockStreamSub; private Disposable readyBlocksStreamSub; + private Duration requestsDelayLongMode = Duration.ZERO; + private Duration requestsDelayShortMode = Duration.ofSeconds(1); + // TODO: make this parameter dynamic depending on active peers number int maxConcurrentBlockRequests = 2; @@ -93,11 +96,12 @@ public SyncManagerImpl( mode -> { switch (mode) { case Long: - return Flux.from(syncQueue.getBlockRequestsStream()) - .delayElements(Duration.ofSeconds(1), delayScheduler); + Flux blockRequestFlux = Flux.from(syncQueue.getBlockRequestsStream()); + return requestsDelayLongMode.toMillis() == 0 ? blockRequestFlux + : blockRequestFlux.delayElements(requestsDelayLongMode, delayScheduler); case Short: return Flux.from(syncQueue.getBlockRequestsStream()) - .delayElements(Duration.ofSeconds(5), delayScheduler); + .delayElements(requestsDelayShortMode, delayScheduler); default: throw new IllegalStateException(); } @@ -143,6 +147,11 @@ public Publisher> getBlocksReadyToImport() { return syncQueue.getBlocksStream(); } + public void setRequestsDelay(Duration longMode, Duration shortMode) { + this.requestsDelayLongMode = longMode; + this.requestsDelayShortMode = shortMode; + } + public void start() { finalizedBlockStreamSub = syncQueue.subscribeToFinalBlocks(finalizedBlockStream); From 322f7be7142d1be31b32b2df5f26b714c1800755 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Thu, 23 May 2019 18:51:57 +0300 Subject: [PATCH 83/96] Fix tests --- .../beacon/wire/channel/beacon/BeaconPipeline.java | 14 ++++++++++---- .../java/org/ethereum/beacon/wire/NodeTest.java | 11 +++++++---- .../java/org/ethereum/beacon/wire/PeersTest.java | 4 ++-- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java index 27ab029bc..2d4c3c1e4 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/channel/beacon/BeaconPipeline.java @@ -152,14 +152,18 @@ public WireApiSync getSyncClient() { private WireApiSync createWireApiSync(WireApiSync syncServer) { RpcChannelAdapter blockRootsAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockRootsRequestMessage.class), - syncServer != null ? syncServer::requestBlockRoots : null, schedulers.events()); + syncServer != null ? syncServer::requestBlockRoots : null, schedulers.events()) + .withRpcCallTimeout(rpsTimeout); RpcChannelAdapter blockHeadersAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockHeadersRequestMessage.class), - syncServer != null ? syncServer::requestBlockHeaders : null, schedulers.events()); + syncServer != null ? syncServer::requestBlockHeaders : null, schedulers.events()) + .withRpcCallTimeout(rpsTimeout); RpcChannelAdapter blockBodiesAsync = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, BlockBodiesRequestMessage.class), syncServer != null ? req -> syncServer.requestBlockBodies(req).thenApply(Feedback::get) : null, schedulers.events()); + blockBodiesAsync.withRpcCallTimeout(rpsTimeout); + return new WireApiSync() { @Override @@ -188,14 +192,16 @@ private WireApiSubRpc createWireApiSub(WireApiSubRpc subServer) { subServer == null ? null : newBlock -> { subServer.newBlock(newBlock.getBlock()); return null; - }, schedulers.events()); + }, schedulers.events()) + .withRpcCallTimeout(rpsTimeout); RpcChannelAdapter attestations = new RpcChannelAdapter<>(new RpcChannelClassFilter<>(rpcHub, NotifyNewAttestationMessage.class), subServer == null ? null : newAttest -> { subServer.newAttestation(newAttest.getAttestation()); return null; - }, schedulers.events()); + }, schedulers.events()) + .withRpcCallTimeout(rpsTimeout); return new WireApiSubRpc() { @Override diff --git a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java index 4f95094bf..81b9d8136 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/NodeTest.java @@ -77,7 +77,7 @@ public void test1() throws Exception { new DepositContract.ChainStart(genesisTime, eth1Data, depositPairs.getValue0()); SimpleDepositContract depositContract = new SimpleDepositContract(chainStart); - try (NettyServer nettyServer = new NettyServer(40001)) { + try (NettyServer nettyServer = new NettyServer(41001)) { // master node with all validators NodeLauncher masterNode; { @@ -120,7 +120,7 @@ public void test1() throws Exception { schedulers, true); connectFut = slaveConnectionManager - .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + .connect(InetSocketAddress.createUnresolved("localhost", 41001)); System.out.println("Connected! " + connectFut.get()); } @@ -130,7 +130,10 @@ public void test1() throws Exception { // generate some new blocks System.out.println("Generating online blocks"); - controlledSchedulers.addTime(Duration.ofSeconds(3 * 10)); + for (int i = 0; i < 10; i++) { + controlledSchedulers.addTime(Duration.ofSeconds(1)); + Thread.sleep(100); + } Flux.from(slaveNode.getSyncManager().getSyncModeStream()) .filter(mode -> mode == SyncMode.Short) @@ -154,7 +157,7 @@ public void test1() throws Exception { // connect the slave again System.out.println("Connect the slave again"); CompletableFuture> connectFut1 = slaveConnectionManager - .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + .connect(InetSocketAddress.createUnresolved("localhost", 41001)); connectFut1.get(); System.out.println("Slave connected"); diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index d316723e5..87c23426f 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -75,7 +75,7 @@ public void test1() throws Exception { Launcher peer1 = simulatorLauncher.createPeer("test"); - try (Server server = new NettyServer(40001)) { + try (Server server = new NettyServer(41001)) { { // peer 0 server.start().await(); @@ -171,7 +171,7 @@ public void test1() throws Exception { System.out.println("Peer 1: connecting to peer 0 for syncing..."); CompletableFuture> localhost = connectionManager - .connect(InetSocketAddress.createUnresolved("localhost", 40001)); + .connect(InetSocketAddress.createUnresolved("localhost", 41001)); localhost.get(); System.out.println("Peer 1: connected to peer 0"); From 485b05d96fa5107540127b4b71cccb3aea4c3041 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 10:26:21 +0300 Subject: [PATCH 84/96] Add --genesis-time cli option. Add default genesis time generation --- .../emulator/config/ConfigException.java | 16 +++++ .../beacon/node/NodeCommandLauncher.java | 61 ++++++++++++++++++- .../ethereum/beacon/node/command/RunNode.java | 12 ++++ .../resources/config/default-node-config.yml | 1 - 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 start/config/src/main/java/org/ethereum/beacon/emulator/config/ConfigException.java diff --git a/start/config/src/main/java/org/ethereum/beacon/emulator/config/ConfigException.java b/start/config/src/main/java/org/ethereum/beacon/emulator/config/ConfigException.java new file mode 100644 index 000000000..96e31f82b --- /dev/null +++ b/start/config/src/main/java/org/ethereum/beacon/emulator/config/ConfigException.java @@ -0,0 +1,16 @@ +package org.ethereum.beacon.emulator.config; + +public class ConfigException extends RuntimeException { + + public ConfigException(String message) { + super(message); + } + + public ConfigException(String message, Throwable cause) { + super(message, cause); + } + + public ConfigException(Throwable cause) { + super(cause); + } +} diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java index 5d6e99289..ab0a86fb7 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -6,10 +6,14 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Random; +import java.util.TimeZone; import java.util.concurrent.ThreadFactory; import java.util.stream.IntStream; import org.apache.logging.log4j.Level; @@ -26,6 +30,7 @@ import org.ethereum.beacon.core.types.Time; import org.ethereum.beacon.crypto.BLS381.KeyPair; import org.ethereum.beacon.emulator.config.ConfigBuilder; +import org.ethereum.beacon.emulator.config.ConfigException; import org.ethereum.beacon.emulator.config.chainspec.SpecBuilder; import org.ethereum.beacon.emulator.config.chainspec.SpecData; import org.ethereum.beacon.emulator.config.main.MainConfig; @@ -264,7 +269,61 @@ public NodeCommandLauncher build() { config.getConfig().getValidator().setSigner(signer); } - return new NodeCommandLauncher( + if (cliOptions.getGenesisTime() != null) { + SimpleDateFormat[] supportedFormats = new SimpleDateFormat[] { + new SimpleDateFormat("yyyy-MM-dd HH:mm"), + new SimpleDateFormat("HH:mm")}; + + Date time = null; + for (SimpleDateFormat format : supportedFormats) { + format.setTimeZone(TimeZone.getTimeZone("GMT")); + try { + time = format.parse(cliOptions.getGenesisTime()); + break; + } catch (ParseException e) { + continue; + } + } + if (time == null) { + throw new ConfigException( + "Couldn't parse --genesisTime option value: '" + cliOptions.getGenesisTime() + "'"); + } + if (time.getYear() + 1900 == 1970) { + Date now = new Date(); + time.setYear(now.getYear()); + time.setMonth(now.getMonth()); + time.setDate(now.getDate()); + } + + if (!(config.getConfig().getValidator().getContract() instanceof EmulatorContract)) { + throw new ConfigException("Genesis time can only be set for 'emulator' contract type"); + } + EmulatorContract contract = (EmulatorContract) config.getConfig().getValidator().getContract(); + contract.setGenesisTime(time); + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + logger.info("Genesis time from cli option: " + + format.format(time) + " GMT"); + } + + if (config.getConfig().getValidator().getContract() instanceof EmulatorContract) { + EmulatorContract contract = (EmulatorContract) config.getConfig().getValidator().getContract(); + if (contract.getGenesisTime() == null) { + Date defaultTime = new Date(); + defaultTime.setMinutes(0); + defaultTime.setSeconds(0); + defaultTime = new Date(defaultTime.getTime() / 1000 * 1000); + contract.setGenesisTime(defaultTime); + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + logger.warn("Genesis time not specified. Default genesisTime was generated: " + + format.format(defaultTime) + " GMT"); + } + } + + return new NodeCommandLauncher( config, specBuilder, logLevel); diff --git a/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java index b7d665c0f..d4a371a26 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/command/RunNode.java @@ -63,6 +63,14 @@ public class RunNode implements Runnable { ) private String name; + @CommandLine.Option( + names = {"--genesis-time"}, + paramLabel = "time", + description = "Genesis time in GMT+0 timezone. In either form: '2019-05-24 11:23', or just" + + " '11:23' (current day is taken). Default value is start of the current hour." + ) + private String genesisTime; + public String getName() { return name; } @@ -79,6 +87,10 @@ public List getValidators() { return validators; } + public String getGenesisTime() { + return genesisTime; + } + @Override public void run() { NodeCommandLauncher.Builder nodeBuilder = diff --git a/start/node/src/main/resources/config/default-node-config.yml b/start/node/src/main/resources/config/default-node-config.yml index 828c31f4d..e1eba1d80 100644 --- a/start/node/src/main/resources/config/default-node-config.yml +++ b/start/node/src/main/resources/config/default-node-config.yml @@ -5,7 +5,6 @@ config: validator: contract: !emulator - genesisTime: 2019-05-23 08:00:00 balance: 55 keys: - !generate From b227b275bbe969cec5e302271654e703530b5e87 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 11:23:02 +0300 Subject: [PATCH 85/96] Add uncaught exceptions handler for DefaultScheduler Threads to report unhandled Flux errors --- .../ethereum/beacon/node/NodeCommandLauncher.java | 15 ++++++++------- .../beacon/schedulers/DefaultSchedulers.java | 9 ++++++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java index ab0a86fb7..9b618297c 100644 --- a/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java +++ b/start/node/src/main/java/org/ethereum/beacon/node/NodeCommandLauncher.java @@ -1,6 +1,5 @@ package org.ethereum.beacon.node; -import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.channel.ChannelFutureListener; import java.io.File; import java.net.InetSocketAddress; @@ -116,15 +115,17 @@ public void run() { new DefaultSchedulers() { @Override protected ThreadFactory createThreadFactory(String namePattern) { - ThreadFactory factory = new ThreadFactoryBuilder().setDaemon(true) - .setNameFormat((nodeName == null ? "" : nodeName + "-") + namePattern).build(); + ThreadFactory factory = + createThreadFactoryBuilder((nodeName == null ? "" : nodeName + "-") + namePattern).build(); if (nodeName == null) { return factory; } else { - return r -> factory.newThread(() -> { - ThreadContext.put("validatorIndex", nodeName); - r.run(); - }); + return r -> + factory.newThread( + () -> { + ThreadContext.put("validatorIndex", nodeName); + r.run(); + }); } } }; diff --git a/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java b/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java index ed3174c2a..8a7b701e5 100644 --- a/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java +++ b/util/src/main/java/org/ethereum/beacon/schedulers/DefaultSchedulers.java @@ -34,6 +34,13 @@ protected ScheduledExecutorService createExecutor(String namePattern, int thread } protected ThreadFactory createThreadFactory(String namePattern) { - return new ThreadFactoryBuilder().setDaemon(true).setNameFormat(namePattern).build(); + return createThreadFactoryBuilder(namePattern).build(); + } + + protected ThreadFactoryBuilder createThreadFactoryBuilder(String namePattern) { + return new ThreadFactoryBuilder() + .setDaemon(true) + .setNameFormat(namePattern) + .setUncaughtExceptionHandler((thread, thr) -> errorHandler.accept(thr)); } } From 56af85d9213fe258ae1a318eff64a3502490f053 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 14:37:47 +0300 Subject: [PATCH 86/96] Don't stop Validator service subscription on internal validator error --- .../org/ethereum/beacon/validator/MultiValidatorService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/validator/src/main/java/org/ethereum/beacon/validator/MultiValidatorService.java b/validator/src/main/java/org/ethereum/beacon/validator/MultiValidatorService.java index c7d532441..2341b1c8d 100644 --- a/validator/src/main/java/org/ethereum/beacon/validator/MultiValidatorService.java +++ b/validator/src/main/java/org/ethereum/beacon/validator/MultiValidatorService.java @@ -361,7 +361,10 @@ private void propagateAttestation(Attestation attestation) { } private void subscribeToStateUpdates(Consumer payload) { - Flux.from(stateStream).subscribe(payload); + Flux.from(stateStream) + .doOnNext(payload) + .onErrorContinue((t,o) -> logger.warn("Validator error: ", t)) + .subscribe(); } @VisibleForTesting From f8b7dcdc9b8c060ff8befa112178916d9e44da26 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 15:05:43 +0300 Subject: [PATCH 87/96] Don't generate ObservableState if the current slot is far above the latest known head --- .../observer/ObservableStateProcessorImpl.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java index 90ade9ea7..d7d7b56c5 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/observer/ObservableStateProcessorImpl.java @@ -11,6 +11,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ethereum.beacon.chain.BeaconChainHead; import org.ethereum.beacon.chain.BeaconTuple; import org.ethereum.beacon.chain.BeaconTupleDetails; @@ -39,7 +41,12 @@ import reactor.core.publisher.Flux; public class ObservableStateProcessorImpl implements ObservableStateProcessor { + private static final Logger logger = LogManager.getLogger(ObservableStateProcessorImpl.class); + private static final int MAX_TUPLE_CACHE_SIZE = 32; + + private final int maxEmptySlotTransitions = 256; + private final BeaconTupleStorage tupleStorage; private final HeadFunction headFunction; @@ -218,8 +225,14 @@ private void newHead(BeaconTupleDetails head) { private void newSlot(SlotNumber newSlot) { if (head.getBlock().getSlot().greater(newSlot)) { + logger.info("Ignore new slot " + newSlot + " below head block: " + head.getBlock()); return; } + if (newSlot.greater(head.getBlock().getSlot().plus(maxEmptySlotTransitions))) { + logger.debug("Ignore new slot " + newSlot + " far above head block: " + head.getBlock()); + return; + } + updateCurrentObservableState(head, newSlot); } From c4d232d5753b40bb97b6223902faba1b423951c6 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Fri, 24 May 2019 18:57:13 +0600 Subject: [PATCH 88/96] Fix build script for node module --- start/node/build.gradle | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/start/node/build.gradle b/start/node/build.gradle index 303db7eb8..d6eb5edca 100644 --- a/start/node/build.gradle +++ b/start/node/build.gradle @@ -2,16 +2,10 @@ plugins { id 'application' } -// The next two lines disable the tasks for the primary main which by default -// generates a script with a name matching the project name. -// You can leave them enabled but if so you'll need to define mainClassName -// And you'll be creating your application scripts two different ways which -// could lead to confusion -startScripts.enabled = false -run.enabled = false - -// Call this for each Main class you want to expose with an app script -createScript(project, 'org.ethereum.beacon.simulator.Simulator', 'simulator') +application { + mainClassName = 'org.ethereum.beacon.node.Node' + applicationDefaultJvmArgs = ['-Xmx2g'] +} dependencies { implementation project(':types') From 59a0b2328981e6fb44f629becbd5608ec8dea244 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 16:23:48 +0300 Subject: [PATCH 89/96] Modify zip() operator prefetch parameter to 1. By default it is 32 and this cause tasks submitted to already disconnected api --- .../beacon/wire/sync/WireApiSyncRouter.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java index b96eb63e3..35f595d5b 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/WireApiSyncRouter.java @@ -1,8 +1,11 @@ package org.ethereum.beacon.wire.sync; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.ethereum.beacon.stream.RxUtil; import org.ethereum.beacon.util.Utils; import org.ethereum.beacon.wire.Feedback; @@ -24,28 +27,34 @@ * When no single delegate api available all calls are queued until any api arises */ public class WireApiSyncRouter implements WireApiSync { + private static final Logger logger = LogManager.getLogger(WireApiSyncRouter.class); private final ReplayProcessor> tasks = ReplayProcessor.create(64); private final FluxSink> tasksSink = tasks.sink(); + private final AtomicInteger pendingTasks = new AtomicInteger(); public WireApiSyncRouter( Publisher addedPeersStream, Publisher removedPeersStream) { - Publisher freePeersStream = + Flux freePeersStream = RxUtil.collect(addedPeersStream, removedPeersStream) + .doOnNext(activePeers -> logger.info("Active APIs count: " + activePeers.size())) .switchMap( activePeers -> activePeers.isEmpty() ? Flux.never() : Flux.fromIterable(activePeers).repeat(), 1); - Flux.zip(freePeersStream, tasks) - .subscribe(p -> p.getT2().accept(p.getT1())); + freePeersStream.zipWith(tasks, 1) + .doOnNext(p -> pendingTasks.decrementAndGet()) + .subscribe(p -> p.getT2().accept(p.getT1())); } private CompletableFuture submitAsyncTask(Function> task) { CompletableFuture ret = new CompletableFuture<>(); tasksSink.next(api -> Utils.futureForward(task.apply(api), ret)); + int cnt = pendingTasks.incrementAndGet(); + logger.debug("New task submitted. Pending tasks: " + cnt); return ret; } From df8b760ce3ec1972535bbd12e8fba15b069ae532 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 16:24:28 +0300 Subject: [PATCH 90/96] Add ability to disconnect when removing activePeer --- .../beacon/wire/net/ConnectionManager.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java index 5abe946c6..d7cfc2425 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/net/ConnectionManager.java @@ -3,13 +3,14 @@ import java.time.Duration; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ethereum.beacon.wire.channel.Channel; import org.reactivestreams.Publisher; -import reactor.core.publisher.DirectProcessor; import reactor.core.publisher.Flux; import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; @@ -28,6 +29,7 @@ public class ConnectionManager { private final ReplayProcessor> clientConnections = ReplayProcessor.create(); private final FluxSink> clientConnectionsSink = clientConnections.sink(); private final Set activePeers = Collections.synchronizedSet(new HashSet<>()); + private final Map> activePeerConnections = new ConcurrentHashMap<>(); public ConnectionManager(Server server, Client client, Scheduler rxScheduler) { @@ -47,6 +49,9 @@ public CompletableFuture> connect(TAddress peerAddress) { } public void addActivePeer(TAddress peerAddress) { + if (activePeers.contains(peerAddress)) { + throw new RuntimeException("Already have active peer address: " + peerAddress); + } activePeers.add(peerAddress); Flux.just(peerAddress) @@ -55,18 +60,30 @@ public void addActivePeer(TAddress peerAddress) { .flatMap(Mono::fromFuture, 1, 1) .doOnError(t-> logger.info("Couldn't connect to active peer " + peerAddress + ": " + t)) .doOnNext(ch -> logger.info("Connected to active peer " + peerAddress)) - .doOnNext(ch -> clientConnectionsSink.next(ch)) + .doOnNext(ch -> { + activePeerConnections.put(peerAddress, ch); + clientConnectionsSink.next(ch); + }) .map(Channel::getCloseFuture) .onErrorResume(t -> Flux.just(CompletableFuture.completedFuture(null))) .flatMap(f -> Mono.fromFuture(f.thenApply(v -> "")), 1, 1) - .doOnNext(ch -> logger.info("Disconnected from active peer " + peerAddress)) + .doOnNext(ch -> { + activePeerConnections.remove(peerAddress); + logger.info("Disconnected from active peer " + peerAddress); + }) .delayElements(Duration.ofSeconds(RECONNECT_TIMEOUT_SECONDS), rxScheduler) .repeat(() -> activePeers.contains(peerAddress)) .subscribe(); } - public void removeActivePeer(TAddress peerAddress) { + public void removeActivePeer(TAddress peerAddress, boolean disconnect) { activePeers.remove(peerAddress); + if (disconnect) { + Channel channel = activePeerConnections.remove(peerAddress); + if (channel != null) { + channel.close(); + } + } } public Publisher> channelsStream() { From 124780fd4a9dfd6e56c530ad4f717b9bd072cfac Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 16:31:01 +0300 Subject: [PATCH 91/96] New line at the end --- start/node/src/test/resources/config/fast-chainSpec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/start/node/src/test/resources/config/fast-chainSpec.yml b/start/node/src/test/resources/config/fast-chainSpec.yml index 94ef0349f..41be4da49 100644 --- a/start/node/src/test/resources/config/fast-chainSpec.yml +++ b/start/node/src/test/resources/config/fast-chainSpec.yml @@ -5,4 +5,4 @@ miscParameters: timeParameters: SECONDS_PER_SLOT: 10 MIN_ATTESTATION_INCLUSION_DELAY: 1 - SLOTS_PER_EPOCH: 2 \ No newline at end of file + SLOTS_PER_EPOCH: 2 From 481a17060fd37b375434478477395dc4eee8f913 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 16:32:53 +0300 Subject: [PATCH 92/96] Fix the test --- .../org/ethereum/beacon/wire/net/ConnectionManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java b/wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java index e07cd76ca..8cb61be64 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/net/ConnectionManagerTest.java @@ -86,7 +86,7 @@ public void test1() { testChannel2.close(); Assert.assertEquals(3, client.connections.size()); - manager.removeActivePeer("1"); + manager.removeActivePeer("1", true); schedulers.addTime(Duration.ofSeconds(10)); Assert.assertEquals(3, client.connections.size()); } From cea0aaddf302196f3b3484704bbd1df7f80aa9c4 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 16:34:57 +0300 Subject: [PATCH 93/96] Reword comment --- wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java b/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java index 3cbf7788c..3c3436624 100644 --- a/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java +++ b/wire/src/main/java/org/ethereum/beacon/wire/sync/BlockTree.java @@ -39,7 +39,7 @@ interface Block { * this block + all its descendants returned * * Blocks with height less than {@link #getTopBlock()} are dropped - * Blocks with height bigger than some threshold above {@link #getTopBlock()} are dropped + * Blocks with height bigger than {@link #getTopBlock()} + threshold are dropped * Duplicate blocks are ignored */ @Nonnull List addBlock(@Nonnull TBlock block); From 2d77a30a0288ac2a579016d13268d9acbcd1e389 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 16:36:05 +0300 Subject: [PATCH 94/96] Rename OtherError to UnexpectedError --- .../main/java/org/ethereum/beacon/chain/MutableBeaconChain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java b/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java index a2c42c64f..23d4f2a04 100644 --- a/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java +++ b/chain/src/main/java/org/ethereum/beacon/chain/MutableBeaconChain.java @@ -11,7 +11,7 @@ enum ImportResult { ExpiredBlock, InvalidBlock, StateMismatch, - OtherError + UnexpectedError } /** From 8013b77754c6f28d2321742931f07e30bd5f2ba7 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Fri, 24 May 2019 18:05:49 +0300 Subject: [PATCH 95/96] Add some test out --- .../src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java index fc4792f6c..c56384d34 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -96,9 +96,11 @@ public void test1() throws Exception { } }); + System.out.println("Starting sync manager..."); syncManager.start(); - simulatorLauncher.getControlledSchedulers().addTime(3000); + System.out.println("Adding 3 seconds..."); + simulatorLauncher.getControlledSchedulers().addTime(5000); Assert.assertTrue(synced.get()); System.out.println("Done"); From be9c3aafc06d6ca4da3e9c340b15da913ba14c48 Mon Sep 17 00:00:00 2001 From: Anton Nashatyrev Date: Mon, 27 May 2019 10:05:26 +0300 Subject: [PATCH 96/96] Fix tests: due to finalization changes we need a bit longer chain to be downloaded for sync to proceed --- wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java | 2 +- wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java index 87c23426f..a48995437 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/PeersTest.java @@ -141,7 +141,7 @@ public void test1() throws Exception { BeaconBlockTree blockTree = new BeaconBlockTree( simulatorLauncher.getSpec().getObjectHasher()); - SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); + SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 20); SyncManagerImpl syncManager = new SyncManagerImpl( peer1.getBeaconChain(), diff --git a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java index c56384d34..22128f373 100644 --- a/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java +++ b/wire/src/test/java/org/ethereum/beacon/wire/sync/SyncTest.java @@ -45,6 +45,7 @@ public CompletableFuture requestBlockRoots( @Override public CompletableFuture requestBlockHeaders( BlockHeadersRequestMessage requestMessage) { + System.out.println("Headers requested: " + requestMessage); return scheduler.executeWithDelay(delay, () -> delegate.requestBlockHeaders(requestMessage).get()); } @@ -73,7 +74,7 @@ public void test1() throws Exception { testPeer.getSchedulers().blocking(), Duration.ofMillis(10)); BeaconBlockTree blockTree = new BeaconBlockTree(simulatorLauncher.getSpec().getObjectHasher()); - SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 16); + SyncQueue syncQueue = new SyncQueueImpl(blockTree, 4, 20); SyncManagerImpl syncManager = new SyncManagerImpl( testPeer.getBeaconChain(),