Skip to content

Commit

Permalink
Merge pull request #45 from eqlabs/conf_010
Browse files Browse the repository at this point in the history
conformance test 010 and 009
  • Loading branch information
niklaslong committed May 17, 2021
2 parents 42210a6 + 1e85a04 commit 5957afa
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 12 deletions.
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -59,8 +59,8 @@ Quick overview of the current status, providing implementation progress and test
| [![conf_006](https://img.shields.io/badge/006-██████████-green) ](SPEC.md#ZG-CONFORMANCE-006)| :warning: Need to confirm expected behaviour with zcash.
| [![conf_007](https://img.shields.io/badge/007-██████████-red) ](SPEC.md#ZG-CONFORMANCE-007)| :warning: Need to confirm expected behaviour with zcash.
| [![conf_008](https://img.shields.io/badge/008-████░░░░░░-red) ](SPEC.md#ZG-CONFORMANCE-008)| :warning: Need to confirm expected behaviour with zcash.
| [![conf_009](https://img.shields.io/badge/009-██████░░░░-green) ](SPEC.md#ZG-CONFORMANCE-009)|
| [![conf_010](https://img.shields.io/badge/010-░░░░░░░░░░-inactive)](SPEC.md#ZG-CONFORMANCE-010)|
| [![conf_009](https://img.shields.io/badge/009-██████████-green) ](SPEC.md#ZG-CONFORMANCE-009)|
| [![conf_010](https://img.shields.io/badge/010-██████████-red) ](SPEC.md#ZG-CONFORMANCE-010)|
| [![conf_011](https://img.shields.io/badge/011-░░░░░░░░░░-inactive)](SPEC.md#ZG-CONFORMANCE-011)|
| [![conf_012](https://img.shields.io/badge/012-██████████-red) ](SPEC.md#ZG-CONFORMANCE-012)| :warning: Zcashd node config requires investigation.
| [![conf_013](https://img.shields.io/badge/013-██████████-red) ](SPEC.md#ZG-CONFORMANCE-013)| :warning: Need to confirm expected behaviour with zcash.
Expand All @@ -80,5 +80,5 @@ Quick overview of the current status, providing implementation progress and test
| [![resis_002](https://img.shields.io/badge/002-████████░░-red)](SPEC.md#ZG-RESISTANCE-002)| :warning: Need to confirm expected behaviour with zcash. Message specific fuzzing isn't implemented for all messages yet.
| [![resis_003](https://img.shields.io/badge/003-░░░░░░░░░░-inactive)](SPEC.md#ZG-RESISTANCE-003)|
| [![resis_004](https://img.shields.io/badge/004-░░░░░░░░░░-inactive)](SPEC.md#ZG-RESISTANCE-004)|
| [![resis_005](https://img.shields.io/badge/005-████████░░-red)](SPEC.md#ZG-RESISTANCE-005)| :warning: Need to confirm expected behaviour with zcash. Message specific fuzzing isn't implemented for all messages yet.
| [![resis_005](https://img.shields.io/badge/005-████████░░-red)](SPEC.md#ZG-RESISTANCE-005)| :warning: Need to confirm expected behaviour with zcash. Message specific fuzzing isn't implemented for all messages yet.
| [![resis_006](https://img.shields.io/badge/006-░░░░░░░░░░-inactive)](SPEC.md#ZG-RESISTANCE-006)|
7 changes: 7 additions & 0 deletions src/protocol/payload/tx.rs
Expand Up @@ -7,6 +7,8 @@ use std::{
io::{self, Cursor, Read, Write},
};

use crate::protocol::payload::inv::{InvHash, ObjectKind};

/// A Zcash transaction ([spec](https://zips.z.cash/protocol/canopy.pdf#txnencodingandconsensus)).
///
/// Supports V1-V4, V5 isn't yet stable.
Expand All @@ -33,6 +35,11 @@ impl Tx {

Ok(hash)
}

/// Convenience function which creates the [InvHash] for this [Tx]
pub fn inv_hash(&self) -> InvHash {
InvHash::new(ObjectKind::Tx, self.double_sha256().unwrap())
}
}

impl Codec for Tx {
Expand Down
151 changes: 142 additions & 9 deletions src/tests/conformance/messages.rs
@@ -1,4 +1,5 @@
use crate::{
assert_matches,
helpers::{initiate_handshake, respond_to_handshake},
protocol::{
message::{Message, MessageFilter},
Expand Down Expand Up @@ -157,17 +158,16 @@ async fn ignores_unsolicited_responses() {
.await
.unwrap();

// TODO: rest of the message types
let test_messages = vec![
Message::Pong(Nonce::default()),
Message::Headers(Headers::empty()),
Message::Addr(Addr::empty()),
// Block(Block),
// NotFound(Inv),
// Tx(Tx),
Message::Block(Box::new(Block::testnet_genesis())),
Message::NotFound(Inv::new(vec![Block::testnet_1().txs[0].inv_hash()])),
Message::Tx(Block::testnet_2().txs[0].clone()),
];

let filter = MessageFilter::with_all_auto_reply().enable_logging();
let filter = MessageFilter::with_all_auto_reply();

for message in test_messages {
message.write_to_stream(&mut stream).await.unwrap();
Expand All @@ -178,15 +178,148 @@ async fn ignores_unsolicited_responses() {
.await
.unwrap();

match filter.read_from_stream(&mut stream).await.unwrap() {
Message::Pong(returned_nonce) => assert_eq!(nonce, returned_nonce),
msg => panic!("Expected pong: {:?}", msg),
}
let pong = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(pong, Message::Pong(..));
}

node.stop().await;
}

#[tokio::test]
async fn basic_query_response() {
// ZG-CONFORMANCE-010, node is seeded with data
//
// The node responds with the correct messages. Message correctness is naively verified through successful encoding/decoding.
//
// `Ping` expects `Pong`.
// `GetAddr` expects `Addr`.
// `Mempool` expects `Inv`.
// `Getblocks` expects `Inv`.
// `GetData(tx_hash)` expects `Tx`.
// `GetData(block_hash)` expects `Blocks`.
// `GetHeaders` expects `Headers`.
//
// The test currently fails for zcashd and zebra.
//
// Current behaviour:
//
// zcashd: Ignores the following messages
// - GetAddr
// - MemPool
// - GetBlocks
//
// GetData(tx) returns NotFound (which is correct),
// because we currently can't seed a mempool.
//
// zebra: DDoS spam due to auto-response

let mut node: Node = Default::default();
node.initial_action(Action::SeedWithTestnetBlocks {
socket_addr: new_local_addr(),
block_count: 3,
})
.start()
.await;

let mut stream = initiate_handshake(node.addr()).await.unwrap();
let filter = MessageFilter::with_all_auto_reply();
let genesis_block = Block::testnet_genesis();

Message::Ping(Nonce::default())
.write_to_stream(&mut stream)
.await
.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::Pong(..));

Message::GetAddr.write_to_stream(&mut stream).await.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::Addr(..));

Message::MemPool.write_to_stream(&mut stream).await.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::Inv(..));

Message::GetBlocks(LocatorHashes::new(
vec![genesis_block.double_sha256().unwrap()],
Hash::zeroed(),
))
.write_to_stream(&mut stream)
.await
.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::Inv(..));

Message::GetData(Inv::new(vec![genesis_block.txs[0].inv_hash()]))
.write_to_stream(&mut stream)
.await
.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::Tx(..));

Message::GetData(Inv::new(vec![Block::testnet_2().inv_hash()]))
.write_to_stream(&mut stream)
.await
.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::Block(..));

Message::GetHeaders(LocatorHashes::new(
vec![genesis_block.double_sha256().unwrap()],
Hash::zeroed(),
))
.write_to_stream(&mut stream)
.await
.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::Headers(..));

node.stop().await;
}

#[tokio::test]
async fn basic_query_response_unseeded() {
// ZG-CONFORMANCE-010, node is *not* seeded with data
//
// The node responds with the correct messages. Message correctness is naively verified through successful encoding/decoding.
//
// `GetData(tx_hash)` expects `NotFound`.
// `GetData(block_hash)` expects `NotFound`.
//
// The test currently fails for zcashd and zebra
//
// Current behaviour:
//
// zcashd: Ignores `GetData(block_hash)`
//
// zebra: DDoS spam due to auto-response

let mut node: Node = Default::default();
node.initial_action(Action::WaitForConnection(new_local_addr()))
.start()
.await;

let mut stream = initiate_handshake(node.addr()).await.unwrap();
let filter = MessageFilter::with_all_auto_reply();
let genesis_block = Block::testnet_genesis();

Message::GetData(Inv::new(vec![genesis_block.txs[0].inv_hash()]))
.write_to_stream(&mut stream)
.await
.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::NotFound(..));

Message::GetData(Inv::new(vec![Block::testnet_2().inv_hash()]))
.write_to_stream(&mut stream)
.await
.unwrap();
let reply = filter.read_from_stream(&mut stream).await.unwrap();
assert_matches!(reply, Message::NotFound(..));

node.stop().await;
}

#[tokio::test]
async fn eagerly_crawls_network_for_peers() {
// ZG-CONFORMANCE-012
Expand Down
23 changes: 23 additions & 0 deletions src/tests/mod.rs
@@ -1,2 +1,25 @@
mod conformance;
mod resistance;

/// Shorthand for `assert!(matches!(val, pattern), args..)`
///
/// Useful for asserting a enum matches a specific variant.
///
/// Usage:
/// `assert_matches!(value, MyEnum::Variant(..));`
///
/// or with additional context:
/// `assert_matches!(value, MyEnum::Variant(..), "Additional {}", "context");`
#[macro_export]
macro_rules! assert_matches {
($value:expr, $pattern:pat $(,)?) => {
assert_matches!($value, $pattern, "");
};
($value:expr, $pattern:pat, $($arg:tt)*) => {{
assert!(matches!($value, $pattern),
r#"assert_matches!({}, {})
left: {:?}
right: {} : {}"#,
stringify!($value), stringify!($pattern), $value, stringify!($pattern), $($arg)*);
}}
}

0 comments on commit 5957afa

Please sign in to comment.