private EthMessage mockMessage(final EthPeer peer) { return new EthMessage(peer, new RawMessage(1, BytesValue.EMPTY)); }
@Test public void doesntSendToValidatorsWhichAreNotDirectlyConnected() throws PeerNotConnected { validators.add(Util.publicKeyToAddress(publicKeys.get(0))); final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidators()).thenReturn(validators); final ValidatorPeers peers = new ValidatorPeers(validatorProvider); // only add peer connections 1, 2 & 3, none of which should be invoked. newArrayList(1, 2, 3).forEach(i -> peers.add(peerConnections.get(i))); final MessageData messageToSend = new RawMessage(1, BytesValue.EMPTY); peers.send(messageToSend); verify(peerConnections.get(0), never()).sendForProtocol(any(), any()); verify(peerConnections.get(1), never()).sendForProtocol(any(), any()); verify(peerConnections.get(2), never()).sendForProtocol(any(), any()); verify(peerConnections.get(3), never()).sendForProtocol(any(), any()); }
@Test public void oldMessagesAreEvictedWhenFullAndCanThenBeRetransmitted() { final List<MessageData> messagesSent = Lists.newArrayList(); for (int i = 0; i < 6; i++) { final RawMessage msg = new RawMessage(i, BytesValue.wrap(new byte[i])); messagesSent.add(msg); messageTracker.send(msg); verify(multicaster, times(1)).send(msg, emptyList()); } reset(multicaster); messageTracker.send(messagesSent.get(5)); verifyZeroInteractions(multicaster); messageTracker.send(messagesSent.get(0)); verify(multicaster, times(1)).send(messagesSent.get(0), emptyList()); }
@Test public void onlyValidatorsAreSentAMessage() throws PeerNotConnected { // Only add the first Peer's address to the validators. validators.add(Util.publicKeyToAddress(publicKeys.get(0))); final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidators()).thenReturn(validators); final ValidatorPeers peers = new ValidatorPeers(validatorProvider); for (final PeerConnection peer : peerConnections) { peers.add(peer); } final MessageData messageToSend = new RawMessage(1, BytesValue.EMPTY); peers.send(messageToSend); verify(peerConnections.get(0), times(1)).sendForProtocol("IBF", messageToSend); verify(peerConnections.get(1), never()).sendForProtocol(any(), any()); verify(peerConnections.get(2), never()).sendForProtocol(any(), any()); verify(peerConnections.get(3), never()).sendForProtocol(any(), any()); }
@Test public void onlyValidatorsAreSentAMessageNotInExcludes() throws PeerNotConnected { // Only add the first Peer's address to the validators. final Address validatorAddress = Util.publicKeyToAddress(publicKeys.get(0)); validators.add(validatorAddress); validators.add(Util.publicKeyToAddress(publicKeys.get(1))); final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); when(validatorProvider.getValidators()).thenReturn(validators); final ValidatorPeers peers = new ValidatorPeers(validatorProvider); for (final PeerConnection peer : peerConnections) { peers.add(peer); } final MessageData messageToSend = new RawMessage(1, BytesValue.EMPTY); peers.send(messageToSend, newArrayList(validatorAddress)); verify(peerConnections.get(0), never()).sendForProtocol(any(), any()); verify(peerConnections.get(1), times(1)).sendForProtocol(any(), any()); verify(peerConnections.get(2), never()).sendForProtocol(any(), any()); verify(peerConnections.get(3), never()).sendForProtocol(any(), any()); } }
@Test public void readFromWithReason() { MessageData messageData = new RawMessage(WireMessageCodes.DISCONNECT, BytesValue.fromHexString("0xC103")); DisconnectMessage disconnectMessage = DisconnectMessage.readFrom(messageData); DisconnectReason reason = disconnectMessage.getReason(); assertThat(reason).isEqualTo(DisconnectReason.USELESS_PEER); }
@Test public void readFromWithNoReason() { MessageData messageData = new RawMessage(WireMessageCodes.DISCONNECT, BytesValue.fromHexString("0xC180")); DisconnectMessage disconnectMessage = DisconnectMessage.readFrom(messageData); DisconnectReason reason = disconnectMessage.getReason(); assertThat(reason).isEqualTo(DisconnectReason.UNKNOWN); }
@Test public void roundTripTest() { // Generate some data final BlockDataGenerator gen = new BlockDataGenerator(1); final List<BytesValue> nodeData = new ArrayList<>(); final int nodeCount = 20; for (int i = 0; i < nodeCount; ++i) { nodeData.add(gen.bytesValue()); } // Perform round-trip transformation // Create specific message, copy it to a generic message, then read back into a specific format final MessageData initialMessage = NodeDataMessage.create(nodeData); final MessageData raw = new RawMessage(EthPV63.NODE_DATA, initialMessage.getData()); final NodeDataMessage message = NodeDataMessage.readFrom(raw); // Read data back out after round trip and check they match originals. final Iterator<BytesValue> readData = message.nodeData().iterator(); for (int i = 0; i < nodeCount; ++i) { Assertions.assertThat(readData.next()).isEqualTo(nodeData.get(i)); } Assertions.assertThat(readData.hasNext()).isFalse(); } }
@Test public void roundTripTest() { // Generate some hashes final BlockDataGenerator gen = new BlockDataGenerator(1); final List<Hash> hashes = new ArrayList<>(); final int hashCount = 20; for (int i = 0; i < hashCount; ++i) { hashes.add(gen.hash()); } // Perform round-trip transformation // Create GetNodeData, copy it to a generic message, then read back into a GetNodeData message final MessageData initialMessage = GetNodeDataMessage.create(hashes); final MessageData raw = new RawMessage(EthPV63.GET_NODE_DATA, initialMessage.getData()); final GetNodeDataMessage message = GetNodeDataMessage.readFrom(raw); // Read hashes back out after round trip and check they match originals. final Iterator<Hash> readData = message.hashes().iterator(); for (int i = 0; i < hashCount; ++i) { Assertions.assertThat(readData.next()).isEqualTo(hashes.get(i)); } Assertions.assertThat(readData.hasNext()).isFalse(); } }
@Test public void roundTripTest() { // Generate some hashes final BlockDataGenerator gen = new BlockDataGenerator(1); final List<Hash> hashes = new ArrayList<>(); final int hashCount = 20; for (int i = 0; i < hashCount; ++i) { hashes.add(gen.hash()); } // Perform round-trip transformation // Create GetReceipts message, copy it to a generic message, then read back into a GetReceipts // message final MessageData initialMessage = GetReceiptsMessage.create(hashes); final MessageData raw = new RawMessage(EthPV63.GET_RECEIPTS, initialMessage.getData()); final GetReceiptsMessage message = GetReceiptsMessage.readFrom(raw); // Read hashes back out after round trip and check they match originals. final Iterator<Hash> readData = message.hashes().iterator(); for (int i = 0; i < hashCount; ++i) { Assertions.assertThat(readData.next()).isEqualTo(hashes.get(i)); } Assertions.assertThat(readData.hasNext()).isFalse(); } }
@Test public void roundTripBlockNum() { for (final boolean reverse : Arrays.asList(true, false)) { final long blockNum = 1000L; final int skip = 10; final int maxHeaders = 128; final GetBlockHeadersMessage initialMessage = GetBlockHeadersMessage.create(blockNum, maxHeaders, skip, reverse); final MessageData raw = new RawMessage(EthPV62.GET_BLOCK_HEADERS, initialMessage.getData()); final GetBlockHeadersMessage message = GetBlockHeadersMessage.readFrom(raw); Assertions.assertThat(message.blockNumber().getAsLong()).isEqualTo(blockNum); Assertions.assertThat(message.hash()).isEmpty(); Assertions.assertThat(message.reverse()).isEqualTo(reverse); Assertions.assertThat(message.skip()).isEqualTo(skip); Assertions.assertThat(message.maxHeaders()).isEqualTo(maxHeaders); } } }
@Test public void transactionRoundTrip() { // Setup list of transactions final int txCount = 20; final BlockDataGenerator gen = new BlockDataGenerator(1); final List<Transaction> transactions = new ArrayList<>(); for (int i = 0; i < txCount; ++i) { transactions.add(gen.transaction()); } // Create TransactionsMessage final MessageData initialMessage = TransactionsMessage.create(transactions); // Read message into a generic RawMessage final MessageData raw = new RawMessage(EthPV62.TRANSACTIONS, initialMessage.getData()); // Transform back to a TransactionsMessage from RawMessage final TransactionsMessage message = TransactionsMessage.readFrom(raw); // Check that transactions match original inputs after transformations final Iterator<Transaction> readTransactions = message.transactions(Transaction::readFrom); for (int i = 0; i < txCount; ++i) { Assertions.assertThat(readTransactions.next()).isEqualTo(transactions.get(i)); } Assertions.assertThat(readTransactions.hasNext()).isFalse(); } }
@Test public void readFromWithInvalidReason() { String[] invalidReasons = { "0xC10C", "0xC155", // List containing a byte > 128 (negative valued) "0xC281FF", // List containing a multi-byte reason "0xC3820101" }; for (String invalidReason : invalidReasons) { MessageData messageData = new RawMessage(WireMessageCodes.DISCONNECT, BytesValue.fromHexString(invalidReason)); DisconnectMessage disconnectMessage = DisconnectMessage.readFrom(messageData); DisconnectReason reason = disconnectMessage.getReason(); assertThat(reason).isEqualTo(DisconnectReason.UNKNOWN); } }
@Test public void roundTripTest() { // Generate some data final BlockDataGenerator gen = new BlockDataGenerator(1); final List<List<TransactionReceipt>> receipts = new ArrayList<>(); final int dataCount = 20; final int receiptsPerSet = 3; for (int i = 0; i < dataCount; ++i) { final List<TransactionReceipt> receiptSet = new ArrayList<>(); for (int j = 0; j < receiptsPerSet; j++) { receiptSet.add(gen.receipt()); } receipts.add(receiptSet); } // Perform round-trip transformation // Create specific message, copy it to a generic message, then read back into a specific format final MessageData initialMessage = ReceiptsMessage.create(receipts); final MessageData raw = new RawMessage(EthPV63.RECEIPTS, initialMessage.getData()); final ReceiptsMessage message = ReceiptsMessage.readFrom(raw); // Read data back out after round trip and check they match originals. final Iterator<List<TransactionReceipt>> readData = message.receipts().iterator(); for (int i = 0; i < dataCount; ++i) { Assertions.assertThat(readData.next()).isEqualTo(receipts.get(i)); } Assertions.assertThat(readData.hasNext()).isFalse(); } }
@Test public void roundTripWithHash() { for (final boolean reverse : Arrays.asList(true, false)) { final Hash hash = Hash.hash(BytesValue.wrap(new byte[10])); final int skip = 10; final int maxHeaders = 128; final GetBlockHeadersMessage initialMessage = GetBlockHeadersMessage.create(hash, maxHeaders, skip, reverse); final MessageData raw = new RawMessage(EthPV62.GET_BLOCK_HEADERS, initialMessage.getData()); final GetBlockHeadersMessage message = GetBlockHeadersMessage.readFrom(raw); Assertions.assertThat(message.blockNumber()).isEmpty(); Assertions.assertThat(message.hash().get()).isEqualTo(hash); Assertions.assertThat(message.reverse()).isEqualTo(reverse); Assertions.assertThat(message.skip()).isEqualTo(skip); Assertions.assertThat(message.maxHeaders()).isEqualTo(maxHeaders); } }
@Test public void readFromWithWrongMessageType() { MessageData messageData = new RawMessage(WireMessageCodes.PING, BytesValue.fromHexString("0xC103")); assertThatThrownBy(() -> DisconnectMessage.readFrom(messageData)) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("Message has code 2 and thus is not a DisconnectMessage"); }
@Test public void getBlockBodiesRoundTrip() throws IOException { final List<Hash> hashes = new ArrayList<>(); final ByteBuffer buffer = ByteBuffer.wrap(Resources.toByteArray(Resources.getResource("50.blocks"))); for (int i = 0; i < 50; ++i) { final int blockSize = RLP.calculateSize(BytesValue.wrapBuffer(buffer)); final byte[] block = new byte[blockSize]; buffer.get(block); buffer.compact().position(0); final RLPInput oneBlock = new BytesValueRLPInput(BytesValue.wrap(block), false); oneBlock.enterList(); hashes.add(BlockHeader.readFrom(oneBlock, MainnetBlockHashFunction::createHash).getHash()); // We don't care about the bodies, just the headers oneBlock.skipNext(); oneBlock.skipNext(); } final MessageData initialMessage = GetBlockBodiesMessage.create(hashes); final MessageData raw = new RawMessage(EthPV62.GET_BLOCK_BODIES, initialMessage.getData()); final GetBlockBodiesMessage message = GetBlockBodiesMessage.readFrom(raw); final Iterator<Hash> readHeaders = message.hashes().iterator(); for (int i = 0; i < 50; ++i) { Assertions.assertThat(readHeaders.next()).isEqualTo(hashes.get(i)); } } }
@Test public void shouldThrowExceptionWhenFramingMessageTooLong() { final byte[] aes = { 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2 }; final byte[] mac = { 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2, 0xa, 0x2 }; final byte[] byteArray = new byte[0xFFFFFF]; new Random().nextBytes(byteArray); final MessageData ethMessage = new RawMessage(0x00, BytesValue.wrap(byteArray)); final HandshakeSecrets secrets = new HandshakeSecrets(aes, mac, mac); final Framer framer = new Framer(secrets); assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> framer.frame(ethMessage, Unpooled.buffer())) .withMessageContaining("Message size in excess of maximum length."); }
@Test public void readFromMessageWithWrongCodeThrows() { final RawMessage rawMsg = new RawMessage(EthPV62.BLOCK_HEADERS, BytesValue.of(0)); assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> NewBlockMessage.readFrom(rawMsg)); } }
@Test public void rawMessageUpCastsToANewBlockMessage() { final UInt256 totalDifficulty = UInt256.of(12345); final BlockDataGenerator blockGenerator = new BlockDataGenerator(); final Block blockForInsertion = blockGenerator.block(); final BytesValueRLPOutput tmp = new BytesValueRLPOutput(); tmp.startList(); blockForInsertion.writeTo(tmp); tmp.writeUInt256Scalar(totalDifficulty); tmp.endList(); final RawMessage rawMsg = new RawMessage(EthPV62.NEW_BLOCK, tmp.encoded()); final NewBlockMessage newBlockMsg = NewBlockMessage.readFrom(rawMsg); assertThat(newBlockMsg.getCode()).isEqualTo(EthPV62.NEW_BLOCK); assertThat(newBlockMsg.totalDifficulty(protocolSchedule)).isEqualTo(totalDifficulty); final Block extractedBlock = newBlockMsg.block(protocolSchedule); assertThat(extractedBlock).isEqualTo(blockForInsertion); }