-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathpeering.rs
184 lines (160 loc) · 6.34 KB
/
peering.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use std::net::SocketAddr;
use assert_matches::assert_matches;
use crate::{
protocol::{
message::Message,
payload::{addr::NetworkAddr, Addr},
},
setup::node::{Action, Node},
tools::{
message_filter::{Filter, MessageFilter},
synthetic_node::SyntheticNode,
LONG_TIMEOUT,
},
wait_until,
};
#[tokio::test]
async fn c013_eagerly_crawls_network_for_peers() {
// ZG-CONFORMANCE-013
//
// The node crawls the network for new peers and eagerly connects.
//
// Test procedure:
//
// 1. Create a set of peer nodes, listening concurrently
// 2. Connect to node with another main peer node
// 3. Wait for `GetAddr`
// 4. Send set of peer listener node addresses
// 5. Expect the node to connect to each peer in the set
//
// zcashd: Has different behaviour depending on connection direction.
// If we initiate the main connection it sends Ping, GetHeaders,
// but never GetAddr.
// If the node initiates then it does send GetAddr, but it never connects
// to the peers.
//
// zebra: Fails, unless we keep responding on the main connection.
// If we do not keep responding then the peer connections take really long to establish,
// failing the test completely.
//
// Nu5: fails, caches the addresses but doesn't open new connections, peer protocol
// tbc.
//
// Related issues: https://github.com/ZcashFoundation/zebra/pull/2154
// https://github.com/ZcashFoundation/zebra/issues/2163
// Spin up a node instance.
let mut node = Node::new().unwrap();
node.initial_action(Action::WaitForConnection)
.start()
.await
.unwrap();
// Create 5 synthetic nodes.
const N: usize = 5;
let (synthetic_nodes, addrs) = SyntheticNode::builder()
.with_full_handshake()
.with_all_auto_reply()
.build_n(N)
.await
.unwrap();
let addrs = addrs
.iter()
.map(|&addr| NetworkAddr::new(addr))
.collect::<Vec<_>>();
// Adjust the config so it lets through GetAddr message and start a "main" synthetic node which
// will provide the peer list.
let synthetic_node = SyntheticNode::builder()
.with_full_handshake()
.with_message_filter(
MessageFilter::with_all_auto_reply().with_getaddr_filter(Filter::Disabled),
)
.build()
.await
.unwrap();
// Connect and handshake.
synthetic_node.connect(node.addr()).await.unwrap();
// Expect GetAddr, this used to be necessary, as of Nu5, it may not be anymore.
// let (_, getaddr) = synthetic_node.recv_message_timeout(TIMEOUT).await.unwrap();
// assert_matches!(getaddr, Message::GetAddr);
// Respond with peer list.
synthetic_node
.unicast(node.addr(), Message::Addr(Addr::new(addrs)))
.unwrap();
// Expect the synthetic nodes to get a connection request from the node.
for node in synthetic_nodes {
wait_until!(LONG_TIMEOUT, node.num_connected() == 1);
node.shut_down().await;
}
// Gracefully shut down the node.
node.stop().unwrap();
}
#[tokio::test]
async fn c014_correctly_lists_peers() {
// ZG-CONFORMANCE-014
//
// The node responds to a `GetAddr` with a list of peers it’s connected to. This command
// should only be sent once, and by the node initiating the connection.
//
// In addition, this test case exercises the known zebra bug: https://github.com/ZcashFoundation/zebra/pull/2120
//
// Test procedure
// 1. Establish N peer listeners
// 2. Start node which connects to these N peers
// 3. Create i..M new connections which,
// a) Connect to the node
// b) Query GetAddr
// c) Receive Addr == N peer addresses
//
// This test currently fails for both zcashd and zebra.
//
// Current behaviour:
//
// zcashd: Never responds. Logs indicate `Unknown command "getaddr" from peer=1` if we initiate
// the connection. If the node initiates the connection then the command is recoginized,
// but likely ignored (because only the initiating node is supposed to send it).
//
// zebra: Never responds: "zebrad::components::inbound: ignoring `Peers` request from remote peer during network setup"
//
// Nu5: never responds, not sure why, adding a timeout after the node start removes the setup error.
//
// Can be coaxed into responding by sending a non-empty Addr in
// response to node's GetAddr. This still fails as it includes previous inbound
// connections in its address book (as in the bug listed above).
// Create 5 synthetic nodes.
const N: usize = 5;
let node_builder = SyntheticNode::builder()
.with_full_handshake()
.with_all_auto_reply();
let (synthetic_nodes, expected_addrs) = node_builder.build_n(N).await.unwrap();
// Start node with the synthetic nodes as initial peers.
let mut node = Node::new().unwrap();
node.initial_action(Action::WaitForConnection)
.initial_peers(expected_addrs.clone())
.start()
.await
.unwrap();
// This fixes the "setup incomplete" issue.
// tokio::time::sleep(std::time::Duration::from_secs(10)).await;
// Connect to node and request GetAddr. We perform multiple iterations to exercise the #2120
// zebra bug.
for _ in 0..N {
let mut synthetic_node = node_builder.build().await.unwrap();
synthetic_node.connect(node.addr()).await.unwrap();
synthetic_node
.unicast(node.addr(), Message::GetAddr)
.unwrap();
let (_, addr) = synthetic_node
.recv_message_timeout(LONG_TIMEOUT)
.await
.unwrap();
let addrs = assert_matches!(addr, Message::Addr(addrs) => addrs);
// Check that ephemeral connections were not gossiped.
let addrs: Vec<SocketAddr> = addrs.iter().map(|network_addr| network_addr.addr).collect();
assert_eq!(addrs, expected_addrs);
synthetic_node.shut_down().await;
}
// Gracefully shut down nodes.
for synthetic_node in synthetic_nodes {
synthetic_node.shut_down().await;
}
node.stop().unwrap();
}