• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacks-network / stacks-core / 26250451051-1

21 May 2026 08:11PM UTC coverage: 85.585% (-0.1%) from 85.712%
26250451051-1

Pull #7215

github

ec9d4c
web-flow
Merge 9487bf852 into af1280aac
Pull Request #7215: Chore: fix flake in non_blocking_minority_configured_to_favour_...

188844 of 220651 relevant lines covered (85.58%)

18975267.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.01
/stackslib/src/net/chat.rs
1
// Copyright (C) 2013-2020 Blockstack PBC, a public benefit corporation
2
// Copyright (C) 2020-2026 Stacks Open Internet Foundation
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
use std::collections::{HashMap, HashSet, VecDeque};
18
use std::io::{Read, Write};
19
use std::net::SocketAddr;
20
use std::{cmp, mem};
21

22
use clarity::vm::types::QualifiedContractIdentifier;
23
use rand::{self, thread_rng, Rng};
24
use stacks_common::types::net::PeerAddress;
25
use stacks_common::types::StacksPublicKeyBuffer;
26
use stacks_common::util::hash::to_hex;
27
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
28
use stacks_common::util::{get_epoch_time_ms, get_epoch_time_secs};
29

30
use crate::burnchains::{Burnchain, BurnchainView};
31
use crate::chainstate::burn::db::sortdb::SortitionDB;
32
use crate::chainstate::burn::BlockSnapshot;
33
use crate::chainstate::stacks::db::StacksChainState;
34
use crate::chainstate::stacks::StacksPublicKey;
35
use crate::core::{
36
    EpochList, StacksEpoch, PEER_VERSION_EPOCH_2_2, PEER_VERSION_EPOCH_2_3, PEER_VERSION_EPOCH_3_2,
37
    PEER_VERSION_EPOCH_3_3,
38
};
39
use crate::monitoring;
40
use crate::net::connection::{ConnectionOptions, ConnectionP2P, ReplyHandleP2P};
41
use crate::net::db::{PeerDB, *};
42
use crate::net::neighbors::MAX_NEIGHBOR_BLOCK_DELAY;
43
use crate::net::p2p::PeerNetwork;
44
use crate::net::{
45
    Error as net_error, GetBlocksInv, GetPoxInv, Neighbor, NeighborKey, StacksMessage, StacksP2P,
46
    GETPOXINV_MAX_BITLEN, *,
47
};
48
use crate::util_lib::db::{DBConn, Error as db_error};
49

50
// did we or did we not successfully send a message?
51
#[derive(Debug, Clone, Default)]
52
pub struct NeighborHealthPoint {
53
    pub success: bool,
54
    pub time: u64,
55
}
56

57
pub const NUM_HEALTH_POINTS: usize = 32;
58
pub const HEALTH_POINT_LIFETIME: u64 = 12 * 3600; // 12 hours
59

60
/// The max number of data points to gather for block/microblock/transaction/stackerdb push messages from a neighbor
61
pub const NUM_BANDWIDTH_POINTS: usize = 32;
62
/// The number of seconds a block data point is valid for the purpose of computing stats
63
pub const BANDWIDTH_POINT_LIFETIME: u64 = 600;
64

65
pub const MAX_PEER_HEARTBEAT_INTERVAL: usize = 3600 * 6; // 6 hours
66

67
/// Statistics on relayer hints in Stacks messages.  Used to deduce network choke points.
68
#[derive(Debug, Clone)]
69
pub struct RelayStats {
70
    pub num_messages: u64, // how many messages a relayer has pushed to this neighbor
71
    pub num_bytes: u64,    // how many bytes a relayer has pushed to this neighbor
72
    pub last_seen: u64,    // the last time (in seconds) since we've seen this relayer
73
}
74

75
impl RelayStats {
76
    pub fn new() -> RelayStats {
×
77
        RelayStats {
×
78
            num_messages: 0,
×
79
            num_bytes: 0,
×
80
            last_seen: 0,
×
81
        }
×
82
    }
×
83

84
    /// Combine two relayers' stats
85
    pub fn merge(&mut self, other: RelayStats) {
293,972✔
86
        if other.last_seen > self.last_seen {
293,972✔
87
            self.num_messages += other.num_messages;
98,811✔
88
            self.num_bytes += other.num_bytes;
98,811✔
89
            self.last_seen = get_epoch_time_secs();
98,811✔
90
        }
204,311✔
91
    }
293,972✔
92
}
93

94
#[derive(Debug, Clone)]
95
pub struct NeighborStats {
96
    pub outbound: bool,
97
    pub first_contact_time: u64,
98
    pub last_contact_time: u64,
99
    pub last_send_time: u64,
100
    pub last_recv_time: u64,
101
    pub last_handshake_time: u64,
102
    pub bytes_tx: u64,
103
    pub bytes_rx: u64,
104
    pub msgs_tx: u64,
105
    pub msgs_rx: u64,
106
    pub msgs_rx_unsolicited: u64,
107
    pub msgs_err: u64,
108
    pub healthpoints: VecDeque<NeighborHealthPoint>,
109
    pub msg_rx_counts: HashMap<StacksMessageID, u64>,
110
    /// (timestamp, num bytes)
111
    pub block_push_rx_counts: VecDeque<(u64, u64)>,
112
    /// (timestamp, num bytes)
113
    pub microblocks_push_rx_counts: VecDeque<(u64, u64)>,
114
    /// (timestamp, num bytes)
115
    pub transaction_push_rx_counts: VecDeque<(u64, u64)>,
116
    /// (timestamp, num bytes)
117
    pub stackerdb_push_rx_counts: VecDeque<(u64, u64)>,
118
    /// (timestamp, num bytes)
119
    pub nakamoto_block_push_rx_counts: VecDeque<(u64, u64)>,
120
    pub relayed_messages: HashMap<NeighborAddress, RelayStats>,
121
}
122

123
impl NeighborStats {
124
    pub fn new(outbound: bool) -> NeighborStats {
23,796✔
125
        NeighborStats {
23,796✔
126
            outbound,
23,796✔
127
            first_contact_time: 0,
23,796✔
128
            last_contact_time: 0,
23,796✔
129
            last_send_time: 0,
23,796✔
130
            last_recv_time: 0,
23,796✔
131
            last_handshake_time: 0,
23,796✔
132
            bytes_tx: 0,
23,796✔
133
            bytes_rx: 0,
23,796✔
134
            msgs_tx: 0,
23,796✔
135
            msgs_rx: 0,
23,796✔
136
            msgs_rx_unsolicited: 0,
23,796✔
137
            msgs_err: 0,
23,796✔
138
            healthpoints: VecDeque::new(),
23,796✔
139
            msg_rx_counts: HashMap::new(),
23,796✔
140
            block_push_rx_counts: VecDeque::new(),
23,796✔
141
            microblocks_push_rx_counts: VecDeque::new(),
23,796✔
142
            transaction_push_rx_counts: VecDeque::new(),
23,796✔
143
            stackerdb_push_rx_counts: VecDeque::new(),
23,796✔
144
            nakamoto_block_push_rx_counts: VecDeque::new(),
23,796✔
145
            relayed_messages: HashMap::new(),
23,796✔
146
        }
23,796✔
147
    }
23,796✔
148

149
    /// Add a neighbor health point for this peer.
150
    /// This updates the recent list of instances where this peer either successfully replied to a
151
    /// message, or failed to do so (indicated by `success`).
152
    pub fn add_healthpoint(&mut self, success: bool) {
12,658,320✔
153
        let hp = NeighborHealthPoint {
12,658,320✔
154
            success,
12,658,320✔
155
            time: get_epoch_time_secs(),
12,658,320✔
156
        };
12,658,320✔
157
        self.healthpoints.push_back(hp);
12,658,320✔
158
        while self.healthpoints.len() > NUM_HEALTH_POINTS {
25,158,747✔
159
            self.healthpoints.pop_front();
12,500,427✔
160
        }
12,500,427✔
161
    }
12,658,320✔
162

163
    /// Record that we recently received a block of the given size.
164
    /// Keeps track of the last `NUM_BANDWIDTH_POINTS` such events, so we can estimate the current
165
    /// bandwidth consumed by block pushes.
166
    pub fn add_block_push(&mut self, message_size: u64) {
17,397✔
167
        self.block_push_rx_counts
17,397✔
168
            .push_back((get_epoch_time_secs(), message_size));
17,397✔
169
        while self.block_push_rx_counts.len() > NUM_BANDWIDTH_POINTS {
20,457✔
170
            self.block_push_rx_counts.pop_front();
3,060✔
171
        }
3,060✔
172
    }
17,397✔
173

174
    /// Record that we recently received a microblock of the given size.
175
    /// Keeps track of the last `NUM_BANDWIDTH_POINTS` such events, so we can estimate the current
176
    /// bandwidth consumed by microblock pushes.
177
    pub fn add_microblocks_push(&mut self, message_size: u64) {
45✔
178
        self.microblocks_push_rx_counts
45✔
179
            .push_back((get_epoch_time_secs(), message_size));
45✔
180
        while self.microblocks_push_rx_counts.len() > NUM_BANDWIDTH_POINTS {
45✔
181
            self.microblocks_push_rx_counts.pop_front();
×
182
        }
×
183
    }
45✔
184

185
    /// Record that we recently received a transaction of the given size.
186
    /// Keeps track of the last `NUM_BANDWIDTH_POINTS` such events, so we can estimate the current
187
    /// bandwidth consumed by transaction pushes.
188
    pub fn add_transaction_push(&mut self, message_size: u64) {
4,554✔
189
        self.transaction_push_rx_counts
4,554✔
190
            .push_back((get_epoch_time_secs(), message_size));
4,554✔
191
        while self.transaction_push_rx_counts.len() > NUM_BANDWIDTH_POINTS {
5,184✔
192
            self.transaction_push_rx_counts.pop_front();
630✔
193
        }
630✔
194
    }
4,554✔
195

196
    /// Record that we recently received a stackerdb chunk push of the given size.
197
    /// Keeps track of the last `NUM_BANDWIDTH_POINTS` such events, so we can estimate the current
198
    /// bandwidth consumed by stackerdb chunk pushes.
199
    pub fn add_stackerdb_push(&mut self, message_size: u64) {
488,337✔
200
        self.stackerdb_push_rx_counts
488,337✔
201
            .push_back((get_epoch_time_secs(), message_size));
488,337✔
202
        while self.stackerdb_push_rx_counts.len() > NUM_BANDWIDTH_POINTS {
919,554✔
203
            self.stackerdb_push_rx_counts.pop_front();
431,217✔
204
        }
431,217✔
205
    }
488,337✔
206

207
    /// Record that we recently received a Nakamoto blcok push of the given size.
208
    /// Keeps track of the last `NUM_BANDWIDTH_POINTS` such events, so we can estimate the current
209
    /// bandwidth consumed by Nakamoto block pushes
210
    pub fn add_nakamoto_block_push(&mut self, message_size: u64) {
7,614✔
211
        self.nakamoto_block_push_rx_counts
7,614✔
212
            .push_back((get_epoch_time_secs(), message_size));
7,614✔
213
        while self.nakamoto_block_push_rx_counts.len() > NUM_BANDWIDTH_POINTS {
8,550✔
214
            self.nakamoto_block_push_rx_counts.pop_front();
936✔
215
        }
936✔
216
    }
7,614✔
217

218
    pub fn add_relayer(&mut self, addr: &NeighborAddress, num_bytes: u64) {
398,371✔
219
        if let Some(stats) = self.relayed_messages.get_mut(addr) {
398,371✔
220
            stats.num_messages += 1;
103,441✔
221
            stats.num_bytes += num_bytes;
103,441✔
222
            stats.last_seen = get_epoch_time_secs();
103,441✔
223
        } else {
294,965✔
224
            let info = RelayStats {
294,930✔
225
                num_messages: 1,
294,930✔
226
                num_bytes,
294,930✔
227
                last_seen: get_epoch_time_secs(),
294,930✔
228
            };
294,930✔
229
            self.relayed_messages.insert(addr.clone(), info);
294,930✔
230
        }
294,930✔
231
    }
398,371✔
232

233
    pub fn take_relayers(&mut self) -> HashMap<NeighborAddress, RelayStats> {
10,363,211✔
234
        let ret = mem::replace(&mut self.relayed_messages, HashMap::new());
10,363,211✔
235
        ret
10,363,211✔
236
    }
10,363,211✔
237

238
    /// Get a peer's perceived health -- the last $NUM_HEALTH_POINTS successful messages divided by
239
    /// the total.
240
    pub fn get_health_score(&self) -> f64 {
11,099✔
241
        // if we don't have enough data, assume 50%
242
        if self.healthpoints.len() < NUM_HEALTH_POINTS {
11,099✔
243
            return 0.5;
11,096✔
244
        }
3✔
245

246
        let mut successful = 0;
3✔
247
        let mut total = 0;
3✔
248
        let now = get_epoch_time_secs();
3✔
249
        for hp in self.healthpoints.iter() {
96✔
250
            // penalize stale data points -- only look at recent data
251
            if hp.success && now < hp.time + HEALTH_POINT_LIFETIME {
96✔
252
                successful += 1;
48✔
253
            }
48✔
254
            total += 1;
96✔
255
        }
256
        (successful as f64) / (total as f64)
3✔
257
    }
11,099✔
258

259
    fn get_bandwidth(rx_counts: &VecDeque<(u64, u64)>, lifetime: u64) -> f64 {
28✔
260
        if rx_counts.len() < 2 {
28✔
261
            return 0.0;
14✔
262
        }
14✔
263

264
        let elapsed_time_start = rx_counts.front().unwrap().0;
14✔
265
        let elapsed_time_end = rx_counts.back().unwrap().0;
14✔
266
        let now = get_epoch_time_secs();
14✔
267

268
        let mut total_bytes = 0;
14✔
269
        for (time, size) in rx_counts.iter() {
223✔
270
            if now < time + lifetime {
223✔
271
                total_bytes += size;
223✔
272
            }
223✔
273
        }
274

275
        if elapsed_time_start == elapsed_time_end {
14✔
276
            total_bytes as f64
9✔
277
        } else {
278
            (total_bytes as f64) / ((elapsed_time_end - elapsed_time_start) as f64)
5✔
279
        }
280
    }
28✔
281

282
    /// Get a peer's total block-push bandwidth usage.
283
    pub fn get_block_push_bandwidth(&self) -> f64 {
6✔
284
        NeighborStats::get_bandwidth(&self.block_push_rx_counts, BANDWIDTH_POINT_LIFETIME)
6✔
285
    }
6✔
286

287
    /// Get a peer's total microblock-push bandwidth usage.
288
    pub fn get_microblocks_push_bandwidth(&self) -> f64 {
6✔
289
        NeighborStats::get_bandwidth(&self.microblocks_push_rx_counts, BANDWIDTH_POINT_LIFETIME)
6✔
290
    }
6✔
291

292
    /// Get a peer's total transaction-push bandwidth usage
293
    pub fn get_transaction_push_bandwidth(&self) -> f64 {
6✔
294
        NeighborStats::get_bandwidth(&self.transaction_push_rx_counts, BANDWIDTH_POINT_LIFETIME)
6✔
295
    }
6✔
296

297
    /// Get a peer's total stackerdb-push bandwidth usage
298
    pub fn get_stackerdb_push_bandwidth(&self) -> f64 {
6✔
299
        NeighborStats::get_bandwidth(&self.stackerdb_push_rx_counts, BANDWIDTH_POINT_LIFETIME)
6✔
300
    }
6✔
301

302
    /// Get a peer's total nakamoto block bandwidth usage
303
    pub fn get_nakamoto_block_push_bandwidth(&self) -> f64 {
4✔
304
        NeighborStats::get_bandwidth(
4✔
305
            &self.nakamoto_block_push_rx_counts,
4✔
306
            BANDWIDTH_POINT_LIFETIME,
307
        )
308
    }
4✔
309

310
    /// Determine how many of a particular message this peer has received
311
    pub fn get_message_recv_count(&self, msg_id: StacksMessageID) -> u64 {
×
312
        *(self.msg_rx_counts.get(&msg_id).unwrap_or(&0))
×
313
    }
×
314
}
315

316
/// P2P ongoing conversation with another Stacks peer
317
pub struct ConversationP2P {
318
    /// Instantiation timestamp in seconds since the epoch
319
    pub instantiated: u64,
320

321
    /// network ID of the local end of this conversation
322
    pub network_id: u32,
323
    /// peer version of the local end of this conversation
324
    pub version: u32,
325
    /// Underlying inbox/outbox for protocol communication
326
    pub connection: ConnectionP2P,
327
    /// opaque ID of the socket
328
    pub conn_id: usize,
329

330
    /// copy of our burnchain config
331
    pub burnchain: Burnchain,
332
    /// how often do we send heartbeats?
333
    pub heartbeat: u32,
334

335
    /// network ID of the remote end of this conversation
336
    pub peer_network_id: u32,
337
    /// peer version of the remote end of this conversation
338
    pub peer_version: u32,
339
    /// reported services of the remote end of this conversation
340
    pub peer_services: u16,
341
    /// from socketaddr
342
    pub peer_addrbytes: PeerAddress,
343
    /// from socketaddr
344
    pub peer_port: u16,
345
    /// from handshake
346
    pub handshake_addrbytes: PeerAddress,
347
    /// from handshake
348
    pub handshake_port: u16,
349
    /// how often do we need to ping the remote peer?
350
    pub peer_heartbeat: u32,
351
    /// when does the peer's key expire?
352
    pub peer_expire_block_height: u64,
353

354
    /// where does this peer's data live?  Set to a 0-length string if not known.
355
    pub data_url: UrlString,
356
    /// Resolved IP address of the data URL
357
    pub data_ip: Option<SocketAddr>,
358
    /// Time to try DNS reesolution again
359
    pub dns_deadline: u128,
360
    /// Ongoing request to DNS resolver
361
    pub dns_request: Option<DNSRequest>,
362

363
    /// what this peer believes is the height of the burnchain
364
    pub burnchain_tip_height: u64,
365
    /// what this peer believes is the hash of the burnchain tip
366
    pub burnchain_tip_burn_header_hash: BurnchainHeaderHash,
367
    /// what this peer believes is the stable height of the burnchain (i.e. after a chain-specific
368
    /// number of confirmations)
369
    pub burnchain_stable_tip_height: u64,
370
    /// the hash of the burnchain block at height `burnchain_stable_tip_height`
371
    pub burnchain_stable_tip_burn_header_hash: BurnchainHeaderHash,
372

373
    /// Statistics about this peer from this conversation
374
    pub stats: NeighborStats,
375

376
    /// which stacker DBs this peer replicates
377
    pub db_smart_contracts: Vec<QualifiedContractIdentifier>,
378

379
    /// outbound replies
380
    pub reply_handles: VecDeque<ReplyHandleP2P>,
381

382
    /// system epochs
383
    epochs: EpochList,
384
}
385

386
impl fmt::Display for ConversationP2P {
387
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
388
        write!(
×
389
            f,
×
390
            "convo:id={},outbound={},peer={:?}",
391
            self.conn_id,
392
            self.stats.outbound,
393
            &self.to_neighbor_key()
×
394
        )
395
    }
×
396
}
397

398
impl fmt::Debug for ConversationP2P {
399
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
8,377✔
400
        write!(
8,377✔
401
            f,
8,377✔
402
            "convo:id={},outbound={},peer={:?}",
403
            self.conn_id,
404
            self.stats.outbound,
405
            &self.to_neighbor_key()
8,377✔
406
        )
407
    }
8,377✔
408
}
409

410
impl NeighborKey {
411
    pub fn from_handshake(
319,842✔
412
        peer_version: u32,
319,842✔
413
        network_id: u32,
319,842✔
414
        handshake_data: &HandshakeData,
319,842✔
415
    ) -> NeighborKey {
319,842✔
416
        NeighborKey {
319,842✔
417
            peer_version,
319,842✔
418
            network_id,
319,842✔
419
            addrbytes: handshake_data.addrbytes.clone(),
319,842✔
420
            port: handshake_data.port,
319,842✔
421
        }
319,842✔
422
    }
319,842✔
423

424
    pub fn from_socketaddr(peer_version: u32, network_id: u32, addr: &SocketAddr) -> NeighborKey {
23,724✔
425
        NeighborKey {
23,724✔
426
            peer_version,
23,724✔
427
            network_id,
23,724✔
428
            addrbytes: PeerAddress::from_socketaddr(addr),
23,724✔
429
            port: addr.port(),
23,724✔
430
        }
23,724✔
431
    }
23,724✔
432
}
433

434
impl Neighbor {
435
    /// Update fields in this neighbor from a given handshake.
436
    /// Also, re-calculate the peer's ASN and organization ID
437
    pub fn handshake_update(
616,967✔
438
        &mut self,
616,967✔
439
        conn: &DBConn,
616,967✔
440
        handshake_data: &HandshakeData,
616,967✔
441
    ) -> Result<(), net_error> {
616,967✔
442
        let pubk = handshake_data
616,967✔
443
            .node_public_key
616,967✔
444
            .to_public_key()
616,967✔
445
            .map_err(|e| net_error::DeserializeError(e.into()))?;
616,967✔
446
        let asn_opt =
616,967✔
447
            PeerDB::asn_lookup(conn, &handshake_data.addrbytes).map_err(net_error::DBError)?;
616,967✔
448

449
        let asn = match asn_opt {
616,967✔
450
            Some(a) => a,
×
451
            None => 0,
616,967✔
452
        };
453

454
        self.public_key = pubk;
616,967✔
455
        self.expire_block = handshake_data.expire_block_height;
616,967✔
456
        self.last_contact_time = get_epoch_time_secs();
616,967✔
457

458
        if asn != 0 {
616,967✔
459
            self.asn = asn;
×
460
            self.org = asn; // TODO; AS number is a place-holder for an organization ID (an organization can own multiple ASs)
×
461
        }
616,967✔
462

463
        Ok(())
616,967✔
464
    }
616,967✔
465

466
    /// Instantiate a Neighbor from HandshakeData, merging the information we have on-disk in the
467
    /// PeerDB with information in the handshake.
468
    /// * If we already know about this neighbor, then all previously-calculated state and local
469
    /// configuration state will be loaded as well.  This includes things like the calculated
470
    /// in/out-degree and last-contact time, as well as the allow/deny time limits.
471
    /// * If we do not know about this neighbor, then the above state will not be loaded.
472
    /// Returns (the neighbor, whether or not the neighbor was known)
473
    pub fn load_and_update(
319,842✔
474
        conn: &DBConn,
319,842✔
475
        peer_version: u32,
319,842✔
476
        network_id: u32,
319,842✔
477
        handshake_data: &HandshakeData,
319,842✔
478
    ) -> Result<(Neighbor, bool), net_error> {
319,842✔
479
        let addr = NeighborKey::from_handshake(peer_version, network_id, handshake_data);
319,842✔
480
        let pubk = handshake_data
319,842✔
481
            .node_public_key
319,842✔
482
            .to_public_key()
319,842✔
483
            .map_err(|e| net_error::DeserializeError(e.into()))?;
319,842✔
484

485
        let peer_opt = PeerDB::get_peer(conn, network_id, &addr.addrbytes, addr.port)
319,842✔
486
            .map_err(net_error::DBError)?;
319,842✔
487

488
        let (mut neighbor, present) = match peer_opt {
319,842✔
489
            Some(neighbor) => {
313,413✔
490
                let mut ret = neighbor;
313,413✔
491
                ret.addr = addr.clone();
313,413✔
492
                (ret, true)
313,413✔
493
            }
494
            None => {
495
                let ret = Neighbor::empty(&addr, &pubk, handshake_data.expire_block_height);
6,429✔
496
                (ret, false)
6,429✔
497
            }
498
        };
499

500
        #[cfg(test)]
501
        {
502
            // setting BLOCKSTACK_NEIGHBOR_TEST_${PORTNUMBER} will let us select an organization
503
            // for this peer
504
            use std::env;
505
            if let Ok(asn_str) = env::var(format!("BLOCKSTACK_NEIGHBOR_TEST_{}", addr.port)) {
22,923✔
506
                neighbor.asn = asn_str.parse().unwrap();
236✔
507
                neighbor.org = neighbor.asn;
236✔
508
                test_debug!("Override {:?} to ASN/org {}", &neighbor.addr, neighbor.asn);
236✔
509
            };
22,687✔
510
        }
511

512
        neighbor.handshake_update(conn, handshake_data)?;
319,842✔
513
        Ok((neighbor, present))
319,842✔
514
    }
319,842✔
515

516
    pub fn from_conversation(
×
517
        conn: &DBConn,
×
518
        convo: &ConversationP2P,
×
519
    ) -> Result<Option<Neighbor>, net_error> {
×
520
        let addr = convo.to_neighbor_key();
×
521
        let peer_opt = PeerDB::get_peer(conn, addr.network_id, &addr.addrbytes, addr.port)
×
522
            .map_err(net_error::DBError)?;
×
523

524
        match peer_opt {
×
525
            None => Ok(None),
×
526
            Some(mut peer) => {
×
527
                if peer.asn == 0 {
×
528
                    let asn_opt =
×
529
                        PeerDB::asn_lookup(conn, &addr.addrbytes).map_err(net_error::DBError)?;
×
530

531
                    if let Some(a) = asn_opt {
×
532
                        if a != 0 {
×
533
                            peer.asn = a;
×
534
                        }
×
535
                    };
×
536
                }
×
537
                Ok(Some(peer))
×
538
            }
539
        }
540
    }
×
541
}
542

543
impl ConversationP2P {
544
    /// Create an unconnected conversation
545
    pub fn new(
23,785✔
546
        network_id: u32,
23,785✔
547
        version: u32,
23,785✔
548
        burnchain: &Burnchain,
23,785✔
549
        peer_addr: &SocketAddr,
23,785✔
550
        conn_opts: &ConnectionOptions,
23,785✔
551
        outbound: bool,
23,785✔
552
        conn_id: usize,
23,785✔
553
        epochs: EpochList,
23,785✔
554
    ) -> ConversationP2P {
23,785✔
555
        ConversationP2P {
23,785✔
556
            instantiated: get_epoch_time_secs(),
23,785✔
557
            network_id,
23,785✔
558
            version,
23,785✔
559
            connection: ConnectionP2P::new(StacksP2P::new(), conn_opts, None),
23,785✔
560
            conn_id,
23,785✔
561
            heartbeat: conn_opts.heartbeat,
23,785✔
562
            burnchain: burnchain.clone(),
23,785✔
563

23,785✔
564
            peer_network_id: 0,
23,785✔
565
            peer_version: 0,
23,785✔
566
            peer_addrbytes: PeerAddress::from_socketaddr(peer_addr),
23,785✔
567
            peer_port: peer_addr.port(),
23,785✔
568
            handshake_addrbytes: PeerAddress([0u8; 16]),
23,785✔
569
            handshake_port: 0,
23,785✔
570
            peer_heartbeat: 0,
23,785✔
571
            peer_services: 0,
23,785✔
572
            peer_expire_block_height: 0,
23,785✔
573

23,785✔
574
            data_url: UrlString::try_from("".to_string()).unwrap(),
23,785✔
575
            data_ip: None,
23,785✔
576
            dns_deadline: 0,
23,785✔
577
            dns_request: None,
23,785✔
578

23,785✔
579
            burnchain_tip_height: 0,
23,785✔
580
            burnchain_tip_burn_header_hash: BurnchainHeaderHash::zero(),
23,785✔
581
            burnchain_stable_tip_height: 0,
23,785✔
582
            burnchain_stable_tip_burn_header_hash: BurnchainHeaderHash::zero(),
23,785✔
583

23,785✔
584
            stats: NeighborStats::new(outbound),
23,785✔
585
            reply_handles: VecDeque::new(),
23,785✔
586

23,785✔
587
            db_smart_contracts: vec![],
23,785✔
588

23,785✔
589
            epochs,
23,785✔
590
        }
23,785✔
591
    }
23,785✔
592

593
    pub fn age(&self) -> u64 {
36✔
594
        get_epoch_time_secs().saturating_sub(self.instantiated)
36✔
595
    }
36✔
596

597
    pub fn set_public_key(&mut self, pubkey_opt: Option<Secp256k1PublicKey>) {
23,724✔
598
        self.connection.set_public_key(pubkey_opt);
23,724✔
599
    }
23,724✔
600

601
    pub fn to_neighbor_key(&self) -> NeighborKey {
3,011,979✔
602
        NeighborKey {
3,011,979✔
603
            peer_version: self.peer_version,
3,011,979✔
604
            network_id: self.peer_network_id,
3,011,979✔
605
            addrbytes: self.peer_addrbytes.clone(),
3,011,979✔
606
            port: self.peer_port,
3,011,979✔
607
        }
3,011,979✔
608
    }
3,011,979✔
609

610
    pub fn to_handshake_neighbor_key(&self) -> NeighborKey {
11,488✔
611
        NeighborKey {
11,488✔
612
            peer_version: self.peer_version,
11,488✔
613
            network_id: self.peer_network_id,
11,488✔
614
            addrbytes: self.handshake_addrbytes.clone(),
11,488✔
615
            port: self.handshake_port,
11,488✔
616
        }
11,488✔
617
    }
11,488✔
618

619
    pub fn to_neighbor_address(&self) -> NeighborAddress {
8,098,732✔
620
        let pubkh = if let Some(pubk) = self.ref_public_key() {
8,098,732✔
621
            Hash160::from_node_public_key(pubk)
8,098,732✔
622
        } else {
623
            Hash160([0u8; 20])
×
624
        };
625

626
        NeighborAddress {
8,098,732✔
627
            addrbytes: self.peer_addrbytes.clone(),
8,098,732✔
628
            port: self.peer_port,
8,098,732✔
629
            public_key_hash: pubkh,
8,098,732✔
630
        }
8,098,732✔
631
    }
8,098,732✔
632

633
    pub fn to_handshake_neighbor_address(&self) -> NeighborAddress {
11,488✔
634
        let pubkh = if let Some(pubk) = self.ref_public_key() {
11,488✔
635
            Hash160::from_node_public_key(pubk)
11,488✔
636
        } else {
637
            Hash160([0u8; 20])
×
638
        };
639

640
        NeighborAddress {
11,488✔
641
            addrbytes: self.handshake_addrbytes.clone(),
11,488✔
642
            port: self.handshake_port,
11,488✔
643
            public_key_hash: pubkh,
11,488✔
644
        }
11,488✔
645
    }
11,488✔
646

647
    pub fn is_outbound(&self) -> bool {
40,320,821✔
648
        self.stats.outbound
40,320,821✔
649
    }
40,320,821✔
650

651
    pub fn is_authenticated(&self) -> bool {
35,945,637✔
652
        self.connection.has_public_key()
35,945,637✔
653
    }
35,945,637✔
654

655
    pub fn get_public_key(&self) -> Option<StacksPublicKey> {
452,468✔
656
        self.connection.get_public_key()
452,468✔
657
    }
452,468✔
658

659
    pub fn get_public_key_hash(&self) -> Option<Hash160> {
2,510,298✔
660
        self.ref_public_key().map(Hash160::from_node_public_key)
2,510,298✔
661
    }
2,510,298✔
662

663
    pub fn ref_public_key(&self) -> Option<&StacksPublicKey> {
12,057,215✔
664
        self.connection.ref_public_key()
12,057,215✔
665
    }
12,057,215✔
666

667
    pub fn get_burnchain_tip_height(&self) -> u64 {
809,552✔
668
        self.burnchain_tip_height
809,552✔
669
    }
809,552✔
670

671
    pub fn get_stable_burnchain_tip_height(&self) -> u64 {
618,630✔
672
        self.burnchain_stable_tip_height
618,630✔
673
    }
618,630✔
674

675
    pub fn get_burnchain_tip_burn_header_hash(&self) -> BurnchainHeaderHash {
809,552✔
676
        self.burnchain_tip_burn_header_hash.clone()
809,552✔
677
    }
809,552✔
678

679
    pub fn get_stable_burnchain_tip_burn_header_hash(&self) -> BurnchainHeaderHash {
618,630✔
680
        self.burnchain_stable_tip_burn_header_hash.clone()
618,630✔
681
    }
618,630✔
682

683
    /// Does the given services bitfield mempool query interface?  It will if it has both
684
    /// RELAY and RPC bits set.
685
    #[cfg_attr(test, mutants::skip)]
686
    pub fn supports_mempool_query(peer_services: u16) -> bool {
6,049✔
687
        let expected_bits = (ServiceFlags::RELAY as u16) | (ServiceFlags::RPC as u16);
6,049✔
688
        (peer_services & expected_bits) == expected_bits
6,049✔
689
    }
6,049✔
690

691
    /// Does the given services bitfield support stacker DBs?  It will if it has the STACKERDB bit set
692
    pub fn supports_stackerdb(peer_services: u16) -> bool {
722,042✔
693
        (peer_services & (ServiceFlags::STACKERDB as u16)) != 0
722,042✔
694
    }
722,042✔
695

696
    /// Does this remote neighbor support a particular StackerDB?
697
    pub fn replicates_stackerdb(&self, db: &QualifiedContractIdentifier) -> bool {
7,103,967✔
698
        for cid in self.db_smart_contracts.iter() {
64,444,272✔
699
            if cid == db {
64,437,882✔
700
                return true;
4,603,110✔
701
            }
59,834,772✔
702
        }
703
        false
2,500,857✔
704
    }
7,103,967✔
705

706
    /// Determine whether or not a given (height, burn_header_hash) pair _disagrees_ with our
707
    /// burnchain view.  If it does, return true.  If it doesn't (including if the given pair is
708
    /// simply absent from the chain_view), then return False.
709
    fn check_burn_header_hash_disagreement(
12,287,852✔
710
        block_height: u64,
12,287,852✔
711
        their_burn_header_hash: &BurnchainHeaderHash,
12,287,852✔
712
        chain_view: &BurnchainView,
12,287,852✔
713
    ) -> bool {
12,287,852✔
714
        let bhh = match chain_view.last_burn_block_hashes.get(&block_height) {
12,287,852✔
715
            Some(bhh) => bhh,
11,408,525✔
716
            None => {
717
                // not present; can't prove disagreement (assume the remote peer is just stale)
718
                return false;
879,327✔
719
            }
720
        };
721
        if bhh != their_burn_header_hash {
11,408,525✔
722
            debug!(
10✔
723
                "Burn header hash mismatch in preamble: {} != {}",
724
                bhh, their_burn_header_hash
725
            );
726
            return true;
10✔
727
        }
11,408,515✔
728
        false
11,408,515✔
729
    }
12,287,852✔
730

731
    /// Get the current epoch
732
    fn get_current_epoch(&self, cur_burn_height: u64) -> StacksEpoch {
302,538✔
733
        self.epochs
302,538✔
734
            .epoch_at_height(cur_burn_height)
302,538✔
735
            .unwrap_or_else(|| panic!("BUG: block {} is not in a known epoch", cur_burn_height))
302,538✔
736
    }
302,538✔
737

738
    /// Determine whether or not a remote node has the proper epoch marker in its peer version
739
    /// * If the local and remote nodes are in the same system epoch, then yes
740
    /// * If they're in different epochs, but the epoch shift hasn't happened yet, then yes
741
    /// * Otherwise, no
742
    fn has_acceptable_epoch(&self, cur_burn_height: u64, remote_peer_version: u32) -> bool {
12,287,893✔
743
        // which epochs do I support, and which epochs does the remote peer support?
744
        let my_epoch = (self.version & 0x000000ff) as u8;
12,287,893✔
745
        let remote_epoch = (remote_peer_version & 0x000000ff) as u8;
12,287,893✔
746

747
        if my_epoch <= remote_epoch {
12,287,893✔
748
            // remote node supports same epochs we do
749
            debug!(
12,287,851✔
750
                "Remote peer has epoch {}, which is at least as new as our epoch {}",
751
                remote_epoch, my_epoch
752
            );
753
            return true;
12,287,851✔
754
        }
42✔
755

756
        debug!(
42✔
757
            "Remote peer has old network version {} (epoch {})",
758
            remote_peer_version, remote_epoch
759
        );
760

761
        // what epoch are we in?
762
        // note that it might not be my_epoch -- for example, my_epoch can be 0x05 for a 2.05 node,
763
        // which can run in epoch 2.0 as well (in which case cur_epoch would be 0x00).
764
        let epoch = self.get_current_epoch(cur_burn_height);
42✔
765
        let cur_epoch = epoch.network_epoch;
42✔
766

767
        if cur_epoch <= remote_epoch {
42✔
768
            // epoch shift hasn't happened yet, and this peer supports the current epoch
769
            debug!(
1✔
770
                "Remote peer has epoch {} and current epoch is {}, so still valid",
771
                remote_epoch, cur_epoch
772
            );
773
            return true;
1✔
774
        }
41✔
775

776
        // be a little more permissive with epochs 2.3 and 2.2, because 2.3.0.0.0 shipped with
777
        //  PEER_VERSION_MAINNET = 0x18000007 and PEER_VERSION_TESTNET = 0xfacade07
778
        if cur_epoch == PEER_VERSION_EPOCH_2_3 && remote_epoch == PEER_VERSION_EPOCH_2_2 {
41✔
779
            debug!(
×
780
                "Remote peer has epoch {} and current epoch is {}, but we're permissive about 2.2/2.3 boundary",
781
                remote_epoch,
782
                cur_epoch
783
            );
784
            return true;
×
785
        }
41✔
786

787
        // be permissive with epochs 3.3 and 3.2. a prior 3.3 release accidentally reported
788
        //  PEER_VERSION_EPOCH_3_2.
789
        if cur_epoch == PEER_VERSION_EPOCH_3_3 && remote_epoch == PEER_VERSION_EPOCH_3_2 {
41✔
790
            debug!(
1✔
791
                "Remote peer has epoch {} and current epoch is {}, but we're permissive about 3.2/3.3 boundary",
792
                remote_epoch,
793
                cur_epoch
794
            );
795
            return true;
1✔
796
        }
40✔
797

798
        return false;
40✔
799
    }
12,287,893✔
800

801
    /// Validate an inbound message's preamble against our knowledge of the burn chain.
802
    /// Return Ok(true) if we can proceed
803
    /// Return Ok(false) if we can't proceed, but the remote peer is not in violation of the protocol
804
    /// Return Err(net_error::InvalidMessage) if the remote peer returns an invalid message in
805
    ///     violation of the protocol
806
    pub fn is_preamble_valid(
12,287,900✔
807
        &self,
12,287,900✔
808
        msg: &StacksMessage,
12,287,900✔
809
        chain_view: &BurnchainView,
12,287,900✔
810
    ) -> Result<bool, net_error> {
12,287,900✔
811
        if msg.preamble.network_id != self.network_id {
12,287,900✔
812
            // not on our network
813
            debug!(
7✔
814
                "{:?}: Preamble invalid: wrong network ID: {:x} != {:x}",
815
                &self, msg.preamble.network_id, self.network_id
×
816
            );
817
            return Err(net_error::InvalidMessage);
7✔
818
        }
12,287,893✔
819
        if (msg.preamble.peer_version & 0xff000000) != (self.version & 0xff000000) {
12,287,893✔
820
            // major version mismatch
821
            debug!(
×
822
                "{:?}: Preamble invalid: wrong peer version: {:x} != {:x}",
823
                &self, msg.preamble.peer_version, self.version
×
824
            );
825
            return Err(net_error::InvalidMessage);
×
826
        }
12,287,893✔
827
        if !self.has_acceptable_epoch(chain_view.burn_block_height, msg.preamble.peer_version) {
12,287,893✔
828
            debug!(
40✔
829
                "{:?}: Preamble invalid: remote peer has stale max-epoch {} (ours is {})",
830
                &self, msg.preamble.peer_version, self.version
×
831
            );
832
            return Err(net_error::InvalidMessage);
40✔
833
        }
12,287,853✔
834
        if msg
12,287,853✔
835
            .preamble
12,287,853✔
836
            .burn_stable_block_height
12,287,853✔
837
            .checked_add(self.burnchain.stable_confirmations as u64)
12,287,853✔
838
            != Some(msg.preamble.burn_block_height)
12,287,853✔
839
        {
840
            // invalid message
841
            debug!(
1✔
842
                "{:?}: Preamble invalid: wrong stable block height: {:?} != {}",
843
                &self,
×
844
                msg.preamble
×
845
                    .burn_stable_block_height
×
846
                    .checked_add(self.burnchain.stable_confirmations as u64),
×
847
                msg.preamble.burn_block_height
848
            );
849
            return Err(net_error::InvalidMessage);
1✔
850
        }
12,287,852✔
851

852
        if msg.preamble.burn_stable_block_height
12,287,852✔
853
            > chain_view.burn_block_height + MAX_NEIGHBOR_BLOCK_DELAY
12,287,852✔
854
        {
855
            // this node is too far ahead of us for neighbor walks, but otherwise still potentially valid
856
            debug!(
1,745✔
857
                "{:?}: remote peer is far ahead of us: {} > {}",
858
                &self, msg.preamble.burn_stable_block_height, chain_view.burn_block_height
×
859
            );
860
        }
12,286,107✔
861

862
        // must agree on stable burn header hash
863
        let rules_disagree = ConversationP2P::check_burn_header_hash_disagreement(
12,287,852✔
864
            msg.preamble.burn_stable_block_height,
12,287,852✔
865
            &msg.preamble.burn_stable_block_hash,
12,287,852✔
866
            chain_view,
12,287,852✔
867
        );
868
        if rules_disagree {
12,287,852✔
869
            // remote peer disagrees on stable burn header hash -- follows different rules than us
870
            return Err(net_error::InvalidMessage);
10✔
871
        }
12,287,842✔
872

873
        Ok(true)
12,287,842✔
874
    }
12,287,900✔
875

876
    /// Get next message sequence number
877
    fn next_seq(&mut self) -> u32 {
6,186,326✔
878
        let mut rng = thread_rng();
6,186,326✔
879
        rng.gen::<u32>()
6,186,326✔
880
    }
6,186,326✔
881

882
    /// Generate a signed message for this conversation
883
    /// with a particular sequence number.  Used for generating replies.
884
    pub fn sign_message_seq(
6,259,125✔
885
        &mut self,
6,259,125✔
886
        chain_view: &BurnchainView,
6,259,125✔
887
        private_key: &Secp256k1PrivateKey,
6,259,125✔
888
        seq: u32,
6,259,125✔
889
        payload: StacksMessageType,
6,259,125✔
890
    ) -> Result<StacksMessage, net_error> {
6,259,125✔
891
        let mut msg =
6,259,125✔
892
            StacksMessage::from_chain_view(self.version, self.network_id, chain_view, payload);
6,259,125✔
893
        msg.sign(seq, private_key)?;
6,259,125✔
894
        Ok(msg)
6,259,125✔
895
    }
6,259,125✔
896

897
    /// Generate a signed message for this converation
898
    pub fn sign_message(
5,787,412✔
899
        &mut self,
5,787,412✔
900
        chain_view: &BurnchainView,
5,787,412✔
901
        private_key: &Secp256k1PrivateKey,
5,787,412✔
902
        payload: StacksMessageType,
5,787,412✔
903
    ) -> Result<StacksMessage, net_error> {
5,787,412✔
904
        let seq = self.next_seq();
5,787,412✔
905
        self.sign_message_seq(chain_view, private_key, seq, payload)
5,787,412✔
906
    }
5,787,412✔
907

908
    /// Generate a signed forwarded message for this conversation.
909
    /// Include ourselves as the latest relayer.
910
    pub fn sign_relay_message(
398,914✔
911
        &mut self,
398,914✔
912
        local_peer: &LocalPeer,
398,914✔
913
        chain_view: &BurnchainView,
398,914✔
914
        mut relay_hints: Vec<RelayData>,
398,914✔
915
        payload: StacksMessageType,
398,914✔
916
    ) -> Result<StacksMessage, net_error> {
398,914✔
917
        let mut msg =
398,914✔
918
            StacksMessage::from_chain_view(self.version, self.network_id, chain_view, payload);
398,914✔
919
        msg.relayers.append(&mut relay_hints);
398,914✔
920
        msg.sign_relay(
398,914✔
921
            &local_peer.private_key,
398,914✔
922
            self.next_seq(),
398,914✔
923
            &local_peer.to_neighbor_addr(),
398,914✔
924
        )?;
2✔
925
        Ok(msg)
398,912✔
926
    }
398,914✔
927

928
    /// Generate a signed reply for this conversation
929
    pub fn sign_reply(
5,281,658✔
930
        &mut self,
5,281,658✔
931
        chain_view: &BurnchainView,
5,281,658✔
932
        private_key: &Secp256k1PrivateKey,
5,281,658✔
933
        payload: StacksMessageType,
5,281,658✔
934
        seq: u32,
5,281,658✔
935
    ) -> Result<StacksMessage, net_error> {
5,281,658✔
936
        let mut msg =
5,281,658✔
937
            StacksMessage::from_chain_view(self.version, self.network_id, chain_view, payload);
5,281,658✔
938
        msg.sign(seq, private_key)?;
5,281,658✔
939
        Ok(msg)
5,281,658✔
940
    }
5,281,658✔
941

942
    /// sign and reply a message
943
    fn sign_and_reply(
4,979,161✔
944
        &mut self,
4,979,161✔
945
        local_peer: &LocalPeer,
4,979,161✔
946
        burnchain_view: &BurnchainView,
4,979,161✔
947
        request_preamble: &Preamble,
4,979,161✔
948
        reply_message: StacksMessageType,
4,979,161✔
949
    ) -> Result<ReplyHandleP2P, net_error> {
4,979,161✔
950
        let _msgtype = reply_message.get_message_name().to_owned();
4,979,161✔
951
        let reply = self.sign_reply(
4,979,161✔
952
            burnchain_view,
4,979,161✔
953
            &local_peer.private_key,
4,979,161✔
954
            reply_message,
4,979,161✔
955
            request_preamble.seq,
4,979,161✔
956
        )?;
×
957
        let reply_handle = self
4,979,161✔
958
            .relay_signed_message(reply)
4,979,161✔
959
            .inspect_err(|e| debug!("Unable to reply a {_msgtype}: {e:?}"))?;
4,979,161✔
960

961
        Ok(reply_handle)
4,979,161✔
962
    }
4,979,161✔
963

964
    /// Sign and forward a message
965
    pub fn sign_and_forward(
398,904✔
966
        &mut self,
398,904✔
967
        local_peer: &LocalPeer,
398,904✔
968
        burnchain_view: &BurnchainView,
398,904✔
969
        relay_hints: Vec<RelayData>,
398,904✔
970
        forward_message: StacksMessageType,
398,904✔
971
    ) -> Result<ReplyHandleP2P, net_error> {
398,904✔
972
        let _msgtype = forward_message.get_message_name().to_owned();
398,904✔
973
        let fwd =
398,903✔
974
            self.sign_relay_message(local_peer, burnchain_view, relay_hints, forward_message)?;
398,904✔
975
        let fwd_handle = self
398,903✔
976
            .relay_signed_message(fwd)
398,903✔
977
            .inspect_err(|e| debug!("Unable to forward a {_msgtype}: {e:?}"))?;
398,903✔
978

979
        Ok(fwd_handle)
398,903✔
980
    }
398,904✔
981

982
    /// Reply a NACK
983
    fn reply_nack(
4✔
984
        &mut self,
4✔
985
        local_peer: &LocalPeer,
4✔
986
        burnchain_view: &BurnchainView,
4✔
987
        preamble: &Preamble,
4✔
988
        nack_code: u32,
4✔
989
    ) -> Result<ReplyHandleP2P, net_error> {
4✔
990
        let nack_payload = StacksMessageType::Nack(NackData::new(nack_code));
4✔
991
        self.sign_and_reply(local_peer, burnchain_view, preamble, nack_payload)
4✔
992
    }
4✔
993

994
    /// Queue up this message to this peer, and update our stats.
995
    /// This is a non-blocking operation. The caller needs to call .try_flush() or .flush() on the
996
    /// returned Write to finish sending.
997
    pub fn relay_signed_message(
6,064,585✔
998
        &mut self,
6,064,585✔
999
        msg: StacksMessage,
6,064,585✔
1000
    ) -> Result<ReplyHandleP2P, net_error> {
6,064,585✔
1001
        let _name = msg.payload.get_message_description();
6,064,585✔
1002
        let _seq = msg.request_id();
6,064,585✔
1003

1004
        let mut handle = self.connection.make_relay_handle(self.conn_id)?;
6,064,585✔
1005
        let buf = msg.serialize_to_vec();
6,064,585✔
1006
        handle.write_all(&buf).map_err(net_error::WriteError)?;
6,064,585✔
1007

1008
        self.stats.msgs_tx += 1;
6,064,585✔
1009

1010
        debug!(
6,064,585✔
1011
            "{:?}: relay-send({}) {} seq {}",
1012
            &self, self.stats.msgs_tx, _name, _seq
×
1013
        );
1014
        Ok(handle)
6,064,585✔
1015
    }
6,064,585✔
1016

1017
    /// Queue up this message to this peer, and update our stats.  Expect a reply.
1018
    /// This is a non-blocking operation.  The caller needs to call .try_flush() or .flush() on the
1019
    /// returned handle to finish sending.
1020
    pub fn send_signed_request(
6,241,805✔
1021
        &mut self,
6,241,805✔
1022
        msg: StacksMessage,
6,241,805✔
1023
        ttl: u64,
6,241,805✔
1024
    ) -> Result<ReplyHandleP2P, net_error> {
6,241,805✔
1025
        let _name = msg.get_message_name();
6,241,805✔
1026
        let _seq = msg.request_id();
6,241,805✔
1027

1028
        let mut handle =
6,241,805✔
1029
            self.connection
6,241,805✔
1030
                .make_request_handle(msg.request_id(), ttl, self.conn_id)?;
6,241,805✔
1031
        let buf = msg.serialize_to_vec();
6,241,805✔
1032
        handle.write_all(&buf).map_err(net_error::WriteError)?;
6,241,805✔
1033

1034
        self.stats.msgs_tx += 1;
6,241,805✔
1035

1036
        debug!(
6,241,805✔
1037
            "{:?}: request-send({}) {} seq {}",
1038
            &self, self.stats.msgs_tx, _name, _seq
×
1039
        );
1040
        Ok(handle)
6,241,805✔
1041
    }
6,241,805✔
1042

1043
    /// Validate a handshake request.
1044
    /// Return Err(...) if the handshake request was invalid.
1045
    fn validate_handshake(
366,188✔
1046
        &mut self,
366,188✔
1047
        local_peer: &LocalPeer,
366,188✔
1048
        chain_view: &BurnchainView,
366,188✔
1049
        message: &mut StacksMessage,
366,188✔
1050
    ) -> Result<(), net_error> {
366,188✔
1051
        let handshake_data = match message.payload {
366,188✔
1052
            StacksMessageType::Handshake(ref mut data) => data.clone(),
366,188✔
1053
            _ => panic!("Message is not a handshake"),
×
1054
        };
1055

1056
        match self.connection.get_public_key() {
366,188✔
1057
            None => {
1058
                // if we don't yet have a public key for this node, verify the message.
1059
                // if it's improperly signed, it's probably a poorly-timed re-key request (but either way the message should be rejected)
1060
                message
12,282✔
1061
                    .verify_secp256k1(&handshake_data.node_public_key)
12,282✔
1062
                    .map_err(|_e| {
12,282✔
1063
                        debug!(
1✔
1064
                            "{:?}: invalid handshake: not signed with given public key",
1065
                            &self
×
1066
                        );
1067
                        net_error::InvalidMessage
1✔
1068
                    })?;
1✔
1069
            }
1070
            Some(_) => {
1071
                // for outbound connections, the self-reported address must match socket address if we already have a public key.
1072
                // (not the case for inbound connections, since the peer socket address we see may
1073
                // not be the same as the address the remote peer thinks it has).
1074
                // The only exception to this is if the remote peer does not yet know its own
1075
                // public IP address, in which case, its handshake addrbytes will be the
1076
                // any-network bind address (0.0.0.0 or ::)
1077
                if self.stats.outbound
353,906✔
1078
                    && (!handshake_data.addrbytes.is_anynet()
3,423✔
1079
                        && (self.peer_addrbytes != handshake_data.addrbytes
3,423✔
1080
                            || self.peer_port != handshake_data.port))
3,423✔
1081
                {
1082
                    // wrong peer address
1083
                    debug!(
19✔
1084
                        "{:?}: invalid handshake -- wrong addr/port ({:?}:{:?})",
1085
                        &self, &handshake_data.addrbytes, handshake_data.port
×
1086
                    );
1087
                    return Err(net_error::InvalidHandshake);
19✔
1088
                }
353,887✔
1089
            }
1090
        };
1091

1092
        let their_public_key_res = handshake_data.node_public_key.to_public_key();
366,168✔
1093
        match their_public_key_res {
366,168✔
1094
            Ok(_) => {}
366,168✔
1095
            Err(_e) => {
×
1096
                // bad public key
1097
                debug!("{:?}: invalid handshake -- invalid public key", &self);
×
1098
                return Err(net_error::InvalidMessage);
×
1099
            }
1100
        };
1101

1102
        if handshake_data.expire_block_height <= chain_view.burn_block_height {
366,168✔
1103
            // already stale
1104
            debug!(
2✔
1105
                "{:?}: invalid handshake -- stale public key (expired at {})",
1106
                &self, handshake_data.expire_block_height
×
1107
            );
1108
            return Err(net_error::InvalidHandshake);
2✔
1109
        }
366,166✔
1110

1111
        // the handshake cannot come from us
1112
        if handshake_data.node_public_key
366,166✔
1113
            == StacksPublicKeyBuffer::from_public_key(&Secp256k1PublicKey::from_private(
366,166✔
1114
                &local_peer.private_key,
366,166✔
1115
            ))
366,166✔
1116
        {
1117
            debug!(
567✔
1118
                "{:?}: invalid handshake -- got a handshake from myself",
1119
                &self
×
1120
            );
1121
            return Err(net_error::InvalidHandshake);
567✔
1122
        }
365,599✔
1123

1124
        Ok(())
365,599✔
1125
    }
366,188✔
1126

1127
    /// Update connection state from handshake data.
1128
    /// Returns true if we learned a new public key; false if not
1129
    pub fn update_from_handshake_data(
730,782✔
1130
        &mut self,
730,782✔
1131
        preamble: &Preamble,
730,782✔
1132
        handshake_data: &HandshakeData,
730,782✔
1133
    ) -> Result<bool, net_error> {
730,782✔
1134
        let pubk = handshake_data
730,782✔
1135
            .node_public_key
730,782✔
1136
            .to_public_key()
730,782✔
1137
            .map_err(|e| net_error::DeserializeError(e.into()))?;
730,782✔
1138

1139
        self.peer_version = preamble.peer_version;
730,782✔
1140
        self.peer_network_id = preamble.network_id;
730,782✔
1141
        self.peer_services = handshake_data.services;
730,782✔
1142
        self.peer_expire_block_height = handshake_data.expire_block_height;
730,782✔
1143
        self.handshake_addrbytes = handshake_data.addrbytes.clone();
730,782✔
1144
        self.handshake_port = handshake_data.port;
730,782✔
1145
        self.data_url = handshake_data.data_url.clone();
730,782✔
1146

1147
        let mut updated = false;
730,782✔
1148
        let cur_pubk_opt = self.connection.get_public_key();
730,782✔
1149
        if let Some(cur_pubk) = cur_pubk_opt {
730,782✔
1150
            if pubk != cur_pubk {
707,437✔
1151
                debug!(
7✔
1152
                    "{:?}: Upgrade key {:?} to {:?} expires {:?}",
1153
                    &self,
×
1154
                    &to_hex(&cur_pubk.to_bytes_compressed()),
×
1155
                    &to_hex(&pubk.to_bytes_compressed()),
×
1156
                    self.peer_expire_block_height
1157
                );
1158
                updated = true;
7✔
1159
            }
707,430✔
1160
        }
23,345✔
1161

1162
        self.connection.set_public_key(Some(pubk.clone()));
730,782✔
1163

1164
        Ok(updated)
730,782✔
1165
    }
730,782✔
1166

1167
    /// Update connection state from stacker DB handshake data.
1168
    /// Just synchronizes the announced smart contracts for which this node replicates data.
1169
    pub fn update_from_stacker_db_handshake_data(
195,078✔
1170
        &mut self,
195,078✔
1171
        stacker_db_data: &StackerDBHandshakeData,
195,078✔
1172
    ) {
195,078✔
1173
        self.db_smart_contracts
195,078✔
1174
            .clone_from(&stacker_db_data.smart_contracts);
195,078✔
1175
    }
195,078✔
1176

1177
    /// Forget about this peer's stacker DB replication state
1178
    pub fn clear_stacker_db_handshake_data(&mut self) {
170,105✔
1179
        self.db_smart_contracts.clear();
170,105✔
1180
    }
170,105✔
1181

1182
    /// Getter for stacker DB contracts
1183
    pub fn get_stackerdb_contract_ids(&self) -> &[QualifiedContractIdentifier] {
36✔
1184
        &self.db_smart_contracts
36✔
1185
    }
36✔
1186

1187
    /// Handle an inbound NAT-punch request -- just tell the peer what we think their IP/port are.
1188
    /// No authentication from the peer is necessary.
1189
    fn handle_natpunch_request(&self, chain_view: &BurnchainView, nonce: u32) -> StacksMessage {
666✔
1190
        monitoring::increment_msg_counter("p2p_nat_punch_request".to_string());
666✔
1191

1192
        let natpunch_data = NatPunchData {
666✔
1193
            addrbytes: self.peer_addrbytes.clone(),
666✔
1194
            port: self.peer_port,
666✔
1195
            nonce,
666✔
1196
        };
666✔
1197
        let msg = StacksMessage::from_chain_view(
666✔
1198
            self.version,
666✔
1199
            self.network_id,
666✔
1200
            chain_view,
666✔
1201
            StacksMessageType::NatPunchReply(natpunch_data),
666✔
1202
        );
1203
        msg
666✔
1204
    }
666✔
1205

1206
    /// Handle an inbound handshake request, and generate either a HandshakeAccept or a HandshakeReject
1207
    /// payload to send back.
1208
    /// A handshake will only be accepted if we do not yet know the public key of this remote peer,
1209
    /// or if it is signed by the current public key.
1210
    /// Returns a reply (either an accept or reject) if appropriate
1211
    /// Panics if this message is not a handshake (caller should check)
1212
    fn handle_handshake(
366,188✔
1213
        &mut self,
366,188✔
1214
        network: &mut PeerNetwork,
366,188✔
1215
        message: &mut StacksMessage,
366,188✔
1216
        authenticated: bool,
366,188✔
1217
        ibd: bool,
366,188✔
1218
    ) -> Result<(Option<StacksMessage>, bool), net_error> {
366,188✔
1219
        if !authenticated && self.connection.options.disable_inbound_handshakes {
366,188✔
1220
            debug!("{:?}: blocking inbound unauthenticated handshake", &self);
×
1221
            return Ok((None, true));
×
1222
        }
366,188✔
1223

1224
        let res =
366,188✔
1225
            self.validate_handshake(network.get_local_peer(), network.get_chain_view(), message);
366,188✔
1226
        match res {
589✔
1227
            Ok(_) => {}
365,599✔
1228
            Err(net_error::InvalidHandshake) => {
1229
                let reject = StacksMessage::from_chain_view(
588✔
1230
                    self.version,
588✔
1231
                    self.network_id,
588✔
1232
                    network.get_chain_view(),
588✔
1233
                    StacksMessageType::HandshakeReject,
588✔
1234
                );
1235
                debug!("{:?}: invalid handshake", &self);
588✔
1236
                return Ok((Some(reject), true));
588✔
1237
            }
1238
            Err(e) => {
1✔
1239
                return Err(e);
1✔
1240
            }
1241
        };
1242

1243
        let handshake_data = match message.payload {
365,599✔
1244
            StacksMessageType::Handshake(ref mut data) => data.clone(),
365,599✔
1245
            _ => panic!("Message is not a handshake"),
×
1246
        };
1247

1248
        let old_pubkey_opt = self.connection.get_public_key();
365,599✔
1249
        let updated = self.update_from_handshake_data(&message.preamble, &handshake_data)?;
365,599✔
1250
        let _authentic_msg = if !updated {
365,599✔
1251
            "same"
365,592✔
1252
        } else if old_pubkey_opt.is_none() {
7✔
1253
            "new"
×
1254
        } else {
1255
            "upgraded"
7✔
1256
        };
1257

1258
        debug!("Handling handshake";
365,599✔
1259
             "neighbor" => ?self,
1260
             "authentic_msg" => &_authentic_msg,
×
1261
             "public_key" => &to_hex(
×
1262
                &handshake_data
×
1263
                    .node_public_key
×
1264
                    .to_public_key()
×
1265
                    .unwrap()
×
1266
                    .to_bytes_compressed()
×
1267
             ),
×
1268
             "services" => &to_hex(&handshake_data.services.to_be_bytes()),
×
1269
             "expires_block_height" => handshake_data.expire_block_height,
×
1270
             "supports_mempool_query" => Self::supports_mempool_query(handshake_data.services),
×
1271
        );
1272

1273
        if updated && self.stats.outbound {
365,599✔
1274
            // save the new key
1275
            let tx = network.peerdb_tx_begin().map_err(net_error::DBError)?;
4✔
1276
            let (mut neighbor, _) = Neighbor::load_and_update(
4✔
1277
                &tx,
4✔
1278
                message.preamble.peer_version,
4✔
1279
                message.preamble.network_id,
4✔
1280
                &handshake_data,
4✔
1281
            )?;
×
1282
            neighbor.save_update(&tx, None)?;
4✔
1283
            tx.commit()
4✔
1284
                .map_err(|e| net_error::DBError(db_error::SqliteError(e)))?;
4✔
1285

1286
            debug!(
4✔
1287
                "{:?}: Re-key {:?} to {:?} expires {}",
1288
                network.get_local_peer(),
×
1289
                &neighbor.addr,
×
1290
                &to_hex(&neighbor.public_key.to_bytes_compressed()),
×
1291
                neighbor.expire_block
1292
            );
1293
        }
365,595✔
1294

1295
        let accept_data = HandshakeAcceptData::new(network.get_local_peer(), self.heartbeat);
365,599✔
1296
        let stacks_message =
365,599✔
1297
            if ConversationP2P::supports_stackerdb(network.get_local_peer().services)
365,599✔
1298
                && ConversationP2P::supports_stackerdb(self.peer_services)
356,443✔
1299
            {
1300
                // participate in stackerdb protocol, but only announce stackerdbs if we're no
1301
                // longer in the initial block download.
1302
                StacksMessageType::StackerDBHandshakeAccept(
1303
                    accept_data,
351,152✔
1304
                    StackerDBHandshakeData {
1305
                        rc_consensus_hash: network.get_chain_view().rc_consensus_hash.clone(),
351,152✔
1306
                        smart_contracts: if ibd {
351,152✔
1307
                            vec![]
89,575✔
1308
                        } else {
1309
                            network.get_local_peer().stacker_dbs.clone()
261,577✔
1310
                        },
1311
                    },
1312
                )
1313
            } else {
1314
                StacksMessageType::HandshakeAccept(accept_data)
14,447✔
1315
            };
1316

1317
        let accept = StacksMessage::from_chain_view(
365,599✔
1318
            self.version,
365,599✔
1319
            self.network_id,
365,599✔
1320
            network.get_chain_view(),
365,599✔
1321
            stacks_message,
365,599✔
1322
        );
1323

1324
        // update stats
1325
        self.stats.last_contact_time = get_epoch_time_secs();
365,599✔
1326
        self.peer_heartbeat = self.heartbeat; // use our own heartbeat to determine how often we expect this peer to ping us, since that's what we've told the peer
365,599✔
1327

1328
        // always pass back handshakes, even though we "handled" them (since other processes --
1329
        // in particular, the neighbor-walk logic -- need to receive them)
1330
        Ok((Some(accept), false))
365,599✔
1331
    }
366,188✔
1332

1333
    /// Handle an inbound handshake-accept
1334
    /// Update conversation state based on a HandshakeAccept
1335
    /// Called from the p2p network thread.
1336
    fn handle_handshake_accept(
365,183✔
1337
        &mut self,
365,183✔
1338
        burnchain_view: &BurnchainView,
365,183✔
1339
        preamble: &Preamble,
365,183✔
1340
        handshake_accept: &HandshakeAcceptData,
365,183✔
1341
        stackerdb_accept: Option<&StackerDBHandshakeData>,
365,183✔
1342
    ) -> Result<(), net_error> {
365,183✔
1343
        self.update_from_handshake_data(preamble, &handshake_accept.handshake)?;
365,183✔
1344
        self.peer_heartbeat =
1345
            if handshake_accept.heartbeat_interval > (MAX_PEER_HEARTBEAT_INTERVAL as u32) {
365,183✔
1346
                debug!(
×
1347
                    "{:?}: heartbeat interval is too long; forcing default maximum",
1348
                    self
1349
                );
1350
                MAX_PEER_HEARTBEAT_INTERVAL as u32
×
1351
            } else {
1352
                handshake_accept.heartbeat_interval
365,183✔
1353
            };
1354

1355
        if let Some(stackerdb_accept) = stackerdb_accept {
365,183✔
1356
            if stackerdb_accept.rc_consensus_hash == burnchain_view.rc_consensus_hash {
350,884✔
1357
                // remote peer is in the same reward cycle as us.
195,078✔
1358
                self.update_from_stacker_db_handshake_data(stackerdb_accept);
195,078✔
1359
            } else {
291,663✔
1360
                // remote peer's burnchain view has diverged, so assume no longer replicating (we
155,806✔
1361
                // can't talk to it anyway).  This can happen once per burnchain block for a few
155,806✔
1362
                // seconds as nodes begin processing the next Stacks blocks, but it's harmless -- at worst, it
155,806✔
1363
                // just means that no stacker DB replication happens between this peer and
155,806✔
1364
                // localhost during this time.
155,806✔
1365
                self.clear_stacker_db_handshake_data();
155,806✔
1366
            }
155,806✔
1367
        } else {
14,299✔
1368
            // no longer replicating
14,299✔
1369
            self.clear_stacker_db_handshake_data();
14,299✔
1370
        }
14,299✔
1371

1372
        self.stats.last_handshake_time = get_epoch_time_secs();
365,183✔
1373

1374
        debug!(
365,183✔
1375
            "HandshakeAccept from {:?}: set public key to {:?} expiring at {:?} heartbeat {}s",
1376
            &self,
×
1377
            &to_hex(
×
1378
                &handshake_accept
×
1379
                    .handshake
×
1380
                    .node_public_key
×
1381
                    .to_public_key()
×
1382
                    .unwrap()
×
1383
                    .to_bytes_compressed()
×
1384
            ),
×
1385
            handshake_accept.handshake.expire_block_height,
1386
            self.peer_heartbeat
1387
        );
1388
        Ok(())
365,183✔
1389
    }
365,183✔
1390

1391
    /// Reply to a ping with a pong.
1392
    /// Called from the p2p network thread.
1393
    fn handle_ping(
6✔
1394
        &mut self,
6✔
1395
        chain_view: &BurnchainView,
6✔
1396
        message: &mut StacksMessage,
6✔
1397
    ) -> StacksMessage {
6✔
1398
        monitoring::increment_msg_counter("p2p_ping".to_string());
6✔
1399

1400
        let ping_data = match message.payload {
6✔
1401
            StacksMessageType::Ping(ref data) => data,
6✔
1402
            _ => panic!("Message is not a ping"),
×
1403
        };
1404
        let pong_data = PongData::from_ping(ping_data);
6✔
1405
        StacksMessage::from_chain_view(
6✔
1406
            self.version,
6✔
1407
            self.network_id,
6✔
1408
            chain_view,
6✔
1409
            StacksMessageType::Pong(pong_data),
6✔
1410
        )
1411
    }
6✔
1412

1413
    /// Handle an inbound GetNeighbors request.
1414
    fn handle_getneighbors(
302,496✔
1415
        &mut self,
302,496✔
1416
        network: &PeerNetwork,
302,496✔
1417
        preamble: &Preamble,
302,496✔
1418
    ) -> Result<ReplyHandleP2P, net_error> {
302,496✔
1419
        monitoring::increment_msg_counter("p2p_get_neighbors".to_string());
302,496✔
1420

1421
        let peer_dbconn = network.peerdb_conn();
302,496✔
1422
        let chain_view = network.get_chain_view();
302,496✔
1423
        let local_peer = network.get_local_peer();
302,496✔
1424

1425
        let epoch = self.get_current_epoch(chain_view.burn_block_height);
302,496✔
1426

1427
        // get neighbors at random as long as they're fresh, and as long as they're compatible with
1428
        // the current system epoch.
1429
        // Alternate at random between serving public-only and public/private-mixed IPs, since for
1430
        // the time being, the remote peer has no way of asking for a particular subset.
1431
        let mut neighbors = PeerDB::get_fresh_random_neighbors(
302,496✔
1432
            peer_dbconn,
302,496✔
1433
            self.network_id,
302,496✔
1434
            epoch.network_epoch,
302,496✔
1435
            self.peer_version,
302,496✔
1436
            get_epoch_time_secs().saturating_sub(self.connection.options.max_neighbor_age),
302,496✔
1437
            MAX_NEIGHBORS_DATA_LEN,
1438
            chain_view.burn_block_height,
302,496✔
1439
            false,
1440
            thread_rng().gen(),
302,496✔
1441
        )
1442
        .map_err(net_error::DBError)?;
302,496✔
1443

1444
        if cfg!(test) && self.connection.options.disable_chat_neighbors {
302,496✔
1445
            // never report neighbors if this is disabled by a test
1446
            debug!(
×
1447
                "{:?}: Neighbor crawl is disabled; reporting 0 neighbors",
1448
                &local_peer
×
1449
            );
1450
            neighbors.clear();
×
1451
        }
302,496✔
1452

1453
        let neighbor_addrs: Vec<NeighborAddress> = neighbors
302,496✔
1454
            .iter()
302,496✔
1455
            .map(NeighborAddress::from_neighbor)
302,496✔
1456
            .collect();
302,496✔
1457

1458
        debug!(
302,496✔
1459
            "{:?}: handle GetNeighbors from {:?}. Reply with {} neighbors",
1460
            &local_peer,
×
1461
            &self,
×
1462
            neighbor_addrs.len()
×
1463
        );
1464

1465
        let payload = StacksMessageType::Neighbors(NeighborsData {
302,496✔
1466
            neighbors: neighbor_addrs,
302,496✔
1467
        });
302,496✔
1468
        let reply = self.sign_reply(chain_view, &local_peer.private_key, payload, preamble.seq)?;
302,496✔
1469
        let reply_handle = self
302,496✔
1470
            .relay_signed_message(reply)
302,496✔
1471
            .inspect_err(|_e| debug!("Outbox to {self:?} is full; cannot reply to GetNeighbors"))?;
302,496✔
1472

1473
        Ok(reply_handle)
302,496✔
1474
    }
302,496✔
1475

1476
    /// Verify that a given consensus hash corresponds to a valid PoX sortition and is aligned to
1477
    /// the start of a reward cycle boundary.  Used to validate both GetBlocksInv and
1478
    /// GetNakamotoInv messages.
1479
    /// Returns Ok(Ok(snapshot-for-consensus-hash)) if valid
1480
    /// Returns Ok(Err(message)) if invalid, in which case, `message` should be replied
1481
    /// Returns Err(..) on DB errors
1482
    fn validate_consensus_hash_reward_cycle_start(
848,896✔
1483
        _local_peer: &LocalPeer,
848,896✔
1484
        sortdb: &SortitionDB,
848,896✔
1485
        consensus_hash: &ConsensusHash,
848,896✔
1486
    ) -> Result<Result<BlockSnapshot, StacksMessageType>, net_error> {
848,896✔
1487
        // request must correspond to valid PoX fork and must be aligned to reward cycle
1488
        let Some(base_snapshot) =
844,470✔
1489
            SortitionDB::get_block_snapshot_consensus(sortdb.conn(), consensus_hash)?
848,896✔
1490
        else {
1491
            debug!(
4,426✔
1492
                "{:?}: No such block snapshot for {}",
1493
                _local_peer, consensus_hash
1494
            );
1495
            return Ok(Err(StacksMessageType::Nack(NackData::new(
4,426✔
1496
                NackErrorCodes::NoSuchBurnchainBlock,
4,426✔
1497
            ))));
4,426✔
1498
        };
1499

1500
        // must be on the main PoX fork
1501
        if !base_snapshot.pox_valid {
844,470✔
1502
            debug!(
×
1503
                "{:?}: Snapshot for {:?} is not on the valid PoX fork",
1504
                _local_peer, base_snapshot.consensus_hash
1505
            );
1506
            return Ok(Err(StacksMessageType::Nack(NackData::new(
×
1507
                NackErrorCodes::InvalidPoxFork,
×
1508
            ))));
×
1509
        }
844,470✔
1510

1511
        // must be aligned to the start of a reward cycle
1512
        // (note that the first reward cycle bit doesn't count)
1513
        if base_snapshot.block_height > sortdb.first_block_height + 1
844,470✔
1514
            && !sortdb
795,917✔
1515
                .pox_constants
795,917✔
1516
                .is_reward_cycle_start(sortdb.first_block_height, base_snapshot.block_height)
795,917✔
1517
        {
1518
            warn!(
1✔
1519
                "{:?}: Snapshot for {:?} is at height {}, which is not aligned to a reward cycle",
1520
                _local_peer, base_snapshot.consensus_hash, base_snapshot.block_height
1521
            );
1522
            return Ok(Err(StacksMessageType::Nack(NackData::new(
1✔
1523
                NackErrorCodes::InvalidPoxFork,
1✔
1524
            ))));
1✔
1525
        }
844,469✔
1526

1527
        Ok(Ok(base_snapshot))
844,469✔
1528
    }
848,896✔
1529

1530
    /// Handle an inbound GetBlocksInv request.
1531
    /// Returns a reply handle to the generated message (possibly a nack)
1532
    /// Only returns up to $reward_cycle_length bits
1533
    pub fn make_getblocksinv_response(
619,903✔
1534
        network: &mut PeerNetwork,
619,903✔
1535
        sortdb: &SortitionDB,
619,903✔
1536
        chainstate: &StacksChainState,
619,903✔
1537
        get_blocks_inv: &GetBlocksInv,
619,903✔
1538
    ) -> Result<StacksMessageType, net_error> {
619,903✔
1539
        let _local_peer = network.get_local_peer();
619,903✔
1540

1541
        // must not ask for more than a reasonable number of blocks
1542
        if get_blocks_inv.num_blocks == 0
619,903✔
1543
            || get_blocks_inv.num_blocks as u32
619,903✔
1544
                > network.get_burnchain().pox_constants.reward_cycle_length
619,903✔
1545
        {
1546
            return Ok(StacksMessageType::Nack(NackData::new(
×
1547
                NackErrorCodes::InvalidMessage,
×
1548
            )));
×
1549
        }
619,903✔
1550

1551
        let base_snapshot_or_nack = Self::validate_consensus_hash_reward_cycle_start(
619,903✔
1552
            _local_peer,
619,903✔
1553
            sortdb,
619,903✔
1554
            &get_blocks_inv.consensus_hash,
619,903✔
1555
        )?;
×
1556
        let base_snapshot = match base_snapshot_or_nack {
619,903✔
1557
            Ok(sn) => sn,
619,900✔
1558
            Err(msg) => {
3✔
1559
                return Ok(msg);
3✔
1560
            }
1561
        };
1562

1563
        // find the tail end of this range on the canonical fork.
1564
        let tip_snapshot = {
610,990✔
1565
            let tip_sort_id = SortitionDB::get_canonical_sortition_tip(sortdb.conn())?;
619,900✔
1566
            let ic = sortdb.index_conn();
619,900✔
1567
            // NOTE: need the '- 1' here because get_stacks_header_hashes includes
1568
            // tip_snapshot.consensus_hash at the end.
1569
            match SortitionDB::get_ancestor_snapshot(
619,900✔
1570
                &ic,
619,900✔
1571
                base_snapshot.block_height + (get_blocks_inv.num_blocks as u64) - 1,
619,900✔
1572
                &tip_sort_id,
619,900✔
1573
            )? {
×
1574
                Some(sn) => sn,
610,990✔
1575
                None => {
1576
                    debug!(
8,910✔
1577
                        "{:?}: No block known for base {} + num_blocks {} = {} block height",
1578
                        _local_peer,
1579
                        base_snapshot.block_height,
1580
                        get_blocks_inv.num_blocks,
1581
                        base_snapshot.block_height + (get_blocks_inv.num_blocks as u64)
×
1582
                    );
1583
                    return Ok(StacksMessageType::Nack(NackData::new(
8,910✔
1584
                        NackErrorCodes::NoSuchBurnchainBlock,
8,910✔
1585
                    )));
8,910✔
1586
                }
1587
            }
1588
        };
1589

1590
        let block_hashes = {
610,990✔
1591
            let num_headers = cmp::min(
610,990✔
1592
                network.get_burnchain().pox_constants.reward_cycle_length as u64,
610,990✔
1593
                get_blocks_inv.num_blocks as u64,
610,990✔
1594
            );
1595

1596
            let ic = sortdb.index_conn();
610,990✔
1597
            let res = ic.get_stacks_header_hashes(
610,990✔
1598
                num_headers,
610,990✔
1599
                &tip_snapshot.consensus_hash,
610,990✔
1600
                network.get_header_cache(),
610,990✔
1601
            );
1602
            match res {
×
1603
                Ok(hashes) => Ok(hashes),
610,990✔
1604
                Err(db_error::NotFoundError) | Err(db_error::InvalidPoxSortition) => {
1605
                    debug!(
×
1606
                        "{:?}: Failed to load ancestor hashes from {}",
1607
                        _local_peer, &tip_snapshot.consensus_hash
×
1608
                    );
1609

1610
                    // make this into a NACK
1611
                    return Ok(StacksMessageType::Nack(NackData::new(
×
1612
                        NackErrorCodes::NoSuchBurnchainBlock,
×
1613
                    )));
×
1614
                }
1615
                Err(e) => Err(net_error::DBError(e)),
×
1616
            }
1617
        }?;
×
1618

1619
        // update cache
1620
        SortitionDB::merge_block_header_cache(network.get_header_cache_mut(), &block_hashes);
610,990✔
1621

1622
        let reward_cycle = network
610,990✔
1623
            .get_burnchain()
610,990✔
1624
            .block_height_to_reward_cycle(base_snapshot.block_height)
610,990✔
1625
            .expect("FATAL: no reward cycle for a valid BlockSnapshot");
610,990✔
1626
        let blocks_inv_data = chainstate
610,990✔
1627
            .get_blocks_inventory_for_reward_cycle(
610,990✔
1628
                network.get_burnchain(),
610,990✔
1629
                reward_cycle,
610,990✔
1630
                &block_hashes,
610,990✔
1631
            )
1632
            .map_err(net_error::from)?;
610,990✔
1633

1634
        if cfg!(test) {
610,990✔
1635
            // make *sure* the behavior stays the same in epoch 2
1636
            let original_blocks_inv_data: BlocksInvData =
340✔
1637
                chainstate.get_blocks_inventory(&block_hashes)?;
340✔
1638

1639
            if original_blocks_inv_data != blocks_inv_data {
340✔
1640
                warn!(
×
1641
                    "For reward cycle {}: {:?} != {:?}",
1642
                    reward_cycle, &original_blocks_inv_data, &blocks_inv_data
×
1643
                );
1644
            }
340✔
1645
        }
610,650✔
1646

1647
        Ok(StacksMessageType::BlocksInv(blocks_inv_data))
610,990✔
1648
    }
619,903✔
1649

1650
    /// Handle an inbound GetBlocksInv request.
1651
    /// Returns a reply handle to the generated message (possibly a nack)
1652
    fn handle_getblocksinv(
617,414✔
1653
        &mut self,
617,414✔
1654
        network: &mut PeerNetwork,
617,414✔
1655
        sortdb: &SortitionDB,
617,414✔
1656
        chainstate: &mut StacksChainState,
617,414✔
1657
        preamble: &Preamble,
617,414✔
1658
        get_blocks_inv: &GetBlocksInv,
617,414✔
1659
    ) -> Result<ReplyHandleP2P, net_error> {
617,414✔
1660
        monitoring::increment_msg_counter("p2p_get_blocks_inv".to_string());
617,414✔
1661

1662
        let mut response = ConversationP2P::make_getblocksinv_response(
617,414✔
1663
            network,
617,414✔
1664
            sortdb,
617,414✔
1665
            chainstate,
617,414✔
1666
            get_blocks_inv,
617,414✔
1667
        )?;
×
1668

1669
        if let StacksMessageType::BlocksInv(ref mut blocks_inv_data) = &mut response {
617,414✔
1670
            debug!(
608,503✔
1671
                "{:?}: Handled GetBlocksInv. Reply {:?} to request {:?}",
1672
                &network.get_local_peer(),
×
1673
                &blocks_inv_data,
×
1674
                get_blocks_inv
1675
            );
1676

1677
            if self.connection.options.disable_inv_chat {
608,503✔
1678
                // never reply that we have blocks
1679
                debug!(
×
1680
                    "{:?}: Disable inv chat -- pretend like we have nothing",
1681
                    network.get_local_peer()
×
1682
                );
1683
                for entry in blocks_inv_data.block_bitvec.iter_mut() {
×
1684
                    *entry = 0;
×
1685
                }
×
1686
                for entry in blocks_inv_data.microblocks_bitvec.iter_mut() {
×
1687
                    *entry = 0;
×
1688
                }
×
1689
            }
608,503✔
1690
        }
8,911✔
1691

1692
        self.sign_and_reply(
617,414✔
1693
            network.get_local_peer(),
617,414✔
1694
            network.get_chain_view(),
617,414✔
1695
            preamble,
617,414✔
1696
            response,
617,414✔
1697
        )
1698
    }
617,414✔
1699

1700
    /// Handle an inbound GetNakamotoInv request.
1701
    /// Returns a reply handle to the generated message (possibly a nack)
1702
    /// Only returns up to $reward_cycle_length bits
1703
    pub fn make_getnakamotoinv_response(
228,993✔
1704
        network: &mut PeerNetwork,
228,993✔
1705
        sortdb: &SortitionDB,
228,993✔
1706
        chainstate: &StacksChainState,
228,993✔
1707
        get_nakamoto_inv: &GetNakamotoInvData,
228,993✔
1708
    ) -> Result<StacksMessageType, net_error> {
228,993✔
1709
        let _local_peer = network.get_local_peer();
228,993✔
1710

1711
        let base_snapshot_or_nack = Self::validate_consensus_hash_reward_cycle_start(
228,993✔
1712
            _local_peer,
228,993✔
1713
            sortdb,
228,993✔
1714
            &get_nakamoto_inv.consensus_hash,
228,993✔
1715
        )?;
×
1716
        let base_snapshot = match base_snapshot_or_nack {
228,993✔
1717
            Ok(sn) => sn,
224,569✔
1718
            Err(msg) => {
4,424✔
1719
                return Ok(msg);
4,424✔
1720
            }
1721
        };
1722

1723
        let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())?;
224,569✔
1724
        let reward_cycle = sortdb
224,569✔
1725
            .pox_constants
224,569✔
1726
            .block_height_to_reward_cycle(sortdb.first_block_height, base_snapshot.block_height)
224,569✔
1727
            .ok_or(net_error::InvalidMessage)?;
224,569✔
1728

1729
        let bitvec_bools = network.nakamoto_inv_generator.make_tenure_bitvector(
224,569✔
1730
            &tip,
224,569✔
1731
            sortdb,
224,569✔
1732
            chainstate,
224,569✔
1733
            &network.stacks_tip.consensus_hash,
224,569✔
1734
            &network.stacks_tip.block_hash,
224,569✔
1735
            reward_cycle,
224,569✔
1736
        )?;
×
1737
        let nakamoto_inv = NakamotoInvData::try_from(&bitvec_bools).inspect_err(|e| {
224,569✔
1738
            warn!("Failed to create a NakamotoInv response to {get_nakamoto_inv:?}: {e:?}")
×
1739
        })?;
×
1740

1741
        debug!(
224,569✔
1742
            "Reply NakamotoInv for {} (rc {}): {:?}",
1743
            &get_nakamoto_inv.consensus_hash, reward_cycle, &nakamoto_inv
×
1744
        );
1745

1746
        Ok(StacksMessageType::NakamotoInv(nakamoto_inv))
224,569✔
1747
    }
228,993✔
1748

1749
    /// Handle an inbound GetNakamotoInv request.
1750
    /// Returns a reply handle to the generated message (possibly a nack)
1751
    fn handle_getnakamotoinv(
228,993✔
1752
        &mut self,
228,993✔
1753
        network: &mut PeerNetwork,
228,993✔
1754
        sortdb: &SortitionDB,
228,993✔
1755
        chainstate: &mut StacksChainState,
228,993✔
1756
        preamble: &Preamble,
228,993✔
1757
        get_nakamoto_inv: &GetNakamotoInvData,
228,993✔
1758
    ) -> Result<ReplyHandleP2P, net_error> {
228,993✔
1759
        monitoring::increment_msg_counter("p2p_get_nakamoto_inv".to_string());
228,993✔
1760

1761
        let mut response = ConversationP2P::make_getnakamotoinv_response(
228,993✔
1762
            network,
228,993✔
1763
            sortdb,
228,993✔
1764
            chainstate,
228,993✔
1765
            get_nakamoto_inv,
228,993✔
1766
        )?;
×
1767

1768
        if let StacksMessageType::NakamotoInv(ref mut tenure_inv_data) = &mut response {
228,993✔
1769
            debug!(
224,569✔
1770
                "{:?}: Handled GetNakamotoInv. Reply {:?} to request {:?}",
1771
                &network.get_local_peer(),
×
1772
                &tenure_inv_data,
×
1773
                get_nakamoto_inv
1774
            );
1775

1776
            if self.connection.options.disable_inv_chat {
224,569✔
1777
                // never reply that we have blocks
1778
                debug!(
×
1779
                    "{:?}: Disable inv chat -- pretend like we have nothing",
1780
                    network.get_local_peer()
×
1781
                );
1782
                tenure_inv_data.tenures.clear();
×
1783
            }
224,569✔
1784
        }
4,424✔
1785

1786
        self.sign_and_reply(
228,993✔
1787
            network.get_local_peer(),
228,993✔
1788
            network.get_chain_view(),
228,993✔
1789
            preamble,
228,993✔
1790
            response,
228,993✔
1791
        )
1792
    }
228,993✔
1793

1794
    /// Create a response an inbound GetPoxInv request, but unsigned.
1795
    /// Returns a reply handle to the generated message (possibly a nack)
1796
    pub fn make_getpoxinv_response(
189,151✔
1797
        network: &PeerNetwork,
189,151✔
1798
        sortdb: &SortitionDB,
189,151✔
1799
        getpoxinv: &GetPoxInv,
189,151✔
1800
    ) -> Result<StacksMessageType, net_error> {
189,151✔
1801
        let local_peer = network.get_local_peer();
189,151✔
1802
        let burnchain = network.get_burnchain();
189,151✔
1803
        let pox_id = network.get_pox_id();
189,151✔
1804

1805
        if pox_id.len() <= 1 {
189,151✔
1806
            // not initialized yet
1807
            debug!("{:?}: PoX not initialized yet", local_peer);
×
1808
            return Ok(StacksMessageType::Nack(NackData::new(
×
1809
                NackErrorCodes::InvalidPoxFork,
×
1810
            )));
×
1811
        }
189,151✔
1812
        // consensus hash in getpoxinv must exist on the canonical chain tip
1813
        match SortitionDB::get_block_snapshot_consensus(sortdb.conn(), &getpoxinv.consensus_hash) {
189,151✔
1814
            Ok(Some(sn)) => {
189,150✔
1815
                if !sn.pox_valid {
189,150✔
1816
                    // invalid consensus hash
1817
                    debug!(
×
1818
                        "{:?}: Snapshot {:?} is not on a valid PoX fork",
1819
                        local_peer, sn.burn_header_hash
1820
                    );
1821
                    return Ok(StacksMessageType::Nack(NackData::new(
×
1822
                        NackErrorCodes::InvalidPoxFork,
×
1823
                    )));
×
1824
                }
189,150✔
1825

1826
                // must align to reward cycle, or this is an invalid fork
1827
                if (sn.block_height - burnchain.first_block_height)
189,150✔
1828
                    % (burnchain.pox_constants.reward_cycle_length as u64)
189,150✔
1829
                    != 1
189,150✔
1830
                {
1831
                    debug!(
×
1832
                        "{:?}: block height ({} - {}) % {} != 1",
1833
                        local_peer,
1834
                        sn.block_height,
1835
                        burnchain.first_block_height,
1836
                        burnchain.pox_constants.reward_cycle_length
1837
                    );
1838
                    return Ok(StacksMessageType::Nack(NackData::new(
×
1839
                        NackErrorCodes::InvalidPoxFork,
×
1840
                    )));
×
1841
                }
189,150✔
1842

1843
                match burnchain.block_height_to_reward_cycle(sn.block_height) {
189,150✔
1844
                    Some(reward_cycle) => {
189,150✔
1845
                        // take a slice of the PoxId
1846
                        let (bitvec, bitlen) =
189,150✔
1847
                            pox_id.bit_slice(reward_cycle as usize, getpoxinv.num_cycles as usize);
189,150✔
1848
                        assert!(bitlen <= GETPOXINV_MAX_BITLEN);
189,150✔
1849

1850
                        let poxinvdata = PoxInvData {
189,150✔
1851
                            pox_bitvec: bitvec,
189,150✔
1852
                            bitlen: bitlen as u16,
189,150✔
1853
                        };
189,150✔
1854
                        debug!(
189,150✔
1855
                            "{:?}: Handle GetPoxInv at reward cycle {}; Reply {:?} to request {:?}",
1856
                            &local_peer, reward_cycle, &poxinvdata, getpoxinv
×
1857
                        );
1858
                        Ok(StacksMessageType::PoxInv(poxinvdata))
189,150✔
1859
                    }
1860
                    None => {
1861
                        // if we can't turn the block height into a reward cycle, then it's before
1862
                        // the first-ever reward cycle and this consensus hash does not correspond
1863
                        // to a real reward cycle.  NACK it.
1864
                        debug!(
×
1865
                            "{:?}: Consensus hash {:?} does not correspond to a real reward cycle",
1866
                            &local_peer, &getpoxinv.consensus_hash
×
1867
                        );
1868
                        Ok(StacksMessageType::Nack(NackData::new(
×
1869
                            NackErrorCodes::InvalidPoxFork,
×
1870
                        )))
×
1871
                    }
1872
                }
1873
            }
1874
            Ok(None) | Err(db_error::NotFoundError) => {
1875
                debug!(
1✔
1876
                    "{:?}: snapshot for consensus hash {} not found",
1877
                    local_peer, getpoxinv.consensus_hash
1878
                );
1879
                Ok(StacksMessageType::Nack(NackData::new(
1✔
1880
                    NackErrorCodes::InvalidPoxFork,
1✔
1881
                )))
1✔
1882
            }
1883
            Err(e) => Err(net_error::DBError(e)),
×
1884
        }
1885
    }
189,151✔
1886

1887
    /// Handle an inbound GetPoxInv request.
1888
    /// Returns a reply handle to the generated message (possibly a nack)
1889
    fn handle_getpoxinv(
189,148✔
1890
        &mut self,
189,148✔
1891
        network: &PeerNetwork,
189,148✔
1892
        sortdb: &SortitionDB,
189,148✔
1893
        preamble: &Preamble,
189,148✔
1894
        getpoxinv: &GetPoxInv,
189,148✔
1895
    ) -> Result<ReplyHandleP2P, net_error> {
189,148✔
1896
        let response = ConversationP2P::make_getpoxinv_response(network, sortdb, getpoxinv)?;
189,148✔
1897
        self.sign_and_reply(
189,148✔
1898
            network.get_local_peer(),
189,148✔
1899
            network.get_chain_view(),
189,148✔
1900
            preamble,
189,148✔
1901
            response,
189,148✔
1902
        )
1903
    }
189,148✔
1904

1905
    /// Handle an inbound StackerDBGetChunkInv request.
1906
    /// Generates a StackerDBChunkInv response from the target database table, if we have it.
1907
    /// Generates a Nack if we don't have this DB, or if the request's consensus hash is invalid.
1908
    fn make_stacker_db_getchunkinv_response(
3,865,787✔
1909
        network: &PeerNetwork,
3,865,787✔
1910
        naddr: NeighborAddress,
3,865,787✔
1911
        chainstate: &mut StacksChainState,
3,865,787✔
1912
        getchunkinv: &StackerDBGetChunkInvData,
3,865,787✔
1913
    ) -> StacksMessageType {
3,865,787✔
1914
        network.make_StackerDBChunksInv_or_Nack(
3,865,787✔
1915
            naddr,
3,865,787✔
1916
            chainstate,
3,865,787✔
1917
            &getchunkinv.contract_id,
3,865,787✔
1918
            &getchunkinv.rc_consensus_hash,
3,865,787✔
1919
        )
1920
    }
3,865,787✔
1921

1922
    /// Handle an inbound StackerDBGetChunkInv request.
1923
    /// Retrns a reply handle to the generated message (possibly a nack)
1924
    fn handle_stacker_db_getchunkinv(
3,865,787✔
1925
        &mut self,
3,865,787✔
1926
        network: &PeerNetwork,
3,865,787✔
1927
        chainstate: &mut StacksChainState,
3,865,787✔
1928
        preamble: &Preamble,
3,865,787✔
1929
        getchunkinv: &StackerDBGetChunkInvData,
3,865,787✔
1930
    ) -> Result<ReplyHandleP2P, net_error> {
3,865,787✔
1931
        let response = ConversationP2P::make_stacker_db_getchunkinv_response(
3,865,787✔
1932
            network,
3,865,787✔
1933
            self.to_neighbor_address(),
3,865,787✔
1934
            chainstate,
3,865,787✔
1935
            getchunkinv,
3,865,787✔
1936
        );
1937
        self.sign_and_reply(
3,865,787✔
1938
            network.get_local_peer(),
3,865,787✔
1939
            network.get_chain_view(),
3,865,787✔
1940
            preamble,
3,865,787✔
1941
            response,
3,865,787✔
1942
        )
1943
    }
3,865,787✔
1944

1945
    /// Handle an inbound StackerDBGetChunk request.
1946
    /// Generates a StackerDBChunk response from the target database table, if we have it.
1947
    /// Generates a NACK if we don't have this DB, or if the request's consensus hash or version
1948
    /// are stale or invalid.
1949
    fn make_stacker_db_getchunk_response(
77,815✔
1950
        network: &PeerNetwork,
77,815✔
1951
        getchunk: &StackerDBGetChunkData,
77,815✔
1952
    ) -> StacksMessageType {
77,815✔
1953
        let local_peer = network.get_local_peer();
77,815✔
1954
        let burnchain_view = network.get_chain_view();
77,815✔
1955
        let stacker_dbs = network.get_stackerdbs();
77,815✔
1956

1957
        if burnchain_view.rc_consensus_hash != getchunk.rc_consensus_hash {
77,815✔
1958
            debug!(
360✔
1959
                "{local_peer:?}: NACK StackerDBGetChunk; {} != {}",
1960
                &burnchain_view.rc_consensus_hash, &getchunk.rc_consensus_hash
×
1961
            );
1962
            return StacksMessageType::Nack(NackData::new(NackErrorCodes::StaleView));
360✔
1963
        }
77,455✔
1964

1965
        let chunk = match stacker_dbs.get_chunk(
77,455✔
1966
            &getchunk.contract_id,
77,455✔
1967
            getchunk.slot_id,
77,455✔
1968
            getchunk.slot_version,
77,455✔
1969
        ) {
1970
            Ok(Some(chunk)) => chunk,
77,320✔
1971
            Ok(None) => {
1972
                // TODO: this is racey
1973
                if let Ok(Some(actual_version)) =
135✔
1974
                    stacker_dbs.get_slot_version(&getchunk.contract_id, getchunk.slot_id)
135✔
1975
                {
1976
                    // request for a stale chunk
1977
                    debug!("{local_peer:?}: NACK StackerDBGetChunk; version mismatch for requested slot {}.{} for {}. Expected {actual_version}", getchunk.slot_id, getchunk.slot_version, &getchunk.contract_id);
135✔
1978
                    if actual_version > getchunk.slot_version {
135✔
1979
                        return StacksMessageType::Nack(NackData::new(
99✔
1980
                            NackErrorCodes::StaleVersion,
99✔
1981
                        ));
99✔
1982
                    } else {
1983
                        return StacksMessageType::Nack(NackData::new(
36✔
1984
                            NackErrorCodes::FutureVersion,
36✔
1985
                        ));
36✔
1986
                    }
1987
                }
×
1988
                // if we hit a DB error, just treat it as if the DB doesn't exist
1989
                debug!(
×
1990
                    "{local_peer:?}: NACK StackerDBGetChunk; unloadable slot {}.{} for {}",
1991
                    getchunk.slot_id, getchunk.slot_version, &getchunk.contract_id
×
1992
                );
1993
                return StacksMessageType::Nack(NackData::new(NackErrorCodes::NoSuchDB));
×
1994
            }
1995
            Err(e) => {
×
1996
                debug!(
×
1997
                    "{local_peer:?}: failed to get chunk for slot {}.{} for {}: {e:?}",
1998
                    getchunk.slot_id, getchunk.slot_version, &getchunk.contract_id
×
1999
                );
2000

2001
                // most likely indicates that this DB doesn't exist
2002
                return StacksMessageType::Nack(NackData::new(NackErrorCodes::NoSuchDB));
×
2003
            }
2004
        };
2005

2006
        StacksMessageType::StackerDBChunk(chunk)
77,320✔
2007
    }
77,815✔
2008

2009
    /// Handle an inbound StackerDBGetChunk request
2010
    /// Returns a reply handle to the generated message (possibly a nack)
2011
    fn handle_stacker_db_getchunk(
77,815✔
2012
        &mut self,
77,815✔
2013
        network: &PeerNetwork,
77,815✔
2014
        preamble: &Preamble,
77,815✔
2015
        getchunk: &StackerDBGetChunkData,
77,815✔
2016
    ) -> Result<ReplyHandleP2P, net_error> {
77,815✔
2017
        let response = ConversationP2P::make_stacker_db_getchunk_response(network, getchunk);
77,815✔
2018
        self.sign_and_reply(
77,815✔
2019
            network.get_local_peer(),
77,815✔
2020
            network.get_chain_view(),
77,815✔
2021
            preamble,
77,815✔
2022
            response,
77,815✔
2023
        )
2024
    }
77,815✔
2025

2026
    /// Verify that there are no cycles in our relayers list.
2027
    /// Identify relayers by public key hash
2028
    fn check_relayer_cycles(relayers: &[RelayData]) -> bool {
517,739✔
2029
        let mut addrs = HashSet::new();
517,739✔
2030
        for r in relayers.iter() {
517,744✔
2031
            if addrs.contains(&r.peer.public_key_hash) {
398,382✔
2032
                return false;
5✔
2033
            }
398,377✔
2034
            addrs.insert(r.peer.public_key_hash.clone());
398,377✔
2035
        }
2036
        true
517,734✔
2037
    }
517,739✔
2038

2039
    /// Verify that we aren't in this relayers list
2040
    fn check_relayers_remote(local_peer: &LocalPeer, relayers: &[RelayData]) -> bool {
517,734✔
2041
        let addr = local_peer.to_neighbor_addr();
517,734✔
2042
        for r in relayers.iter() {
517,734✔
2043
            if r.peer.public_key_hash == addr.public_key_hash {
398,372✔
2044
                return false;
1✔
2045
            }
398,371✔
2046
        }
2047
        return true;
517,733✔
2048
    }
517,734✔
2049

2050
    /// Check that a message was properly relayed.
2051
    /// * there are no relay cycles
2052
    /// * we didn't send this
2053
    /// Update relayer statistics for this conversation
2054
    fn process_relayers(
517,739✔
2055
        &mut self,
517,739✔
2056
        local_peer: &LocalPeer,
517,739✔
2057
        preamble: &Preamble,
517,739✔
2058
        relayers: &[RelayData],
517,739✔
2059
    ) -> bool {
517,739✔
2060
        if !ConversationP2P::check_relayer_cycles(relayers) {
517,739✔
2061
            warn!(
5✔
2062
                "Invalid relayers -- message from {:?} contains a cycle",
2063
                self.to_neighbor_key()
5✔
2064
            );
2065
            return false;
5✔
2066
        }
517,734✔
2067

2068
        if !ConversationP2P::check_relayers_remote(local_peer, relayers) {
517,734✔
2069
            warn!(
1✔
2070
                "Invalid relayers -- message originates from us ({})",
2071
                local_peer.to_neighbor_addr()
1✔
2072
            );
2073
            return false;
1✔
2074
        }
517,733✔
2075

2076
        for relayer in relayers.iter() {
517,733✔
2077
            self.stats
398,371✔
2078
                .add_relayer(&relayer.peer, (preamble.payload_len - 1) as u64);
398,371✔
2079
        }
398,371✔
2080

2081
        return true;
517,733✔
2082
    }
517,739✔
2083

2084
    /// Validate pushed blocks.
2085
    /// Make sure the peer doesn't send us too much at once, though.
2086
    fn validate_blocks_push(
17,355✔
2087
        &mut self,
17,355✔
2088
        network: &PeerNetwork,
17,355✔
2089
        preamble: &Preamble,
17,355✔
2090
        relayers: Vec<RelayData>,
17,355✔
2091
    ) -> Result<Option<ReplyHandleP2P>, net_error> {
17,355✔
2092
        assert!(preamble.payload_len > 5); // don't count 1-byte type prefix + 4 byte vector length
17,355✔
2093

2094
        let local_peer = network.get_local_peer();
17,355✔
2095
        let chain_view = network.get_chain_view();
17,355✔
2096

2097
        if !self.process_relayers(local_peer, preamble, &relayers) {
17,355✔
2098
            warn!("Drop pushed blocks -- invalid relayers {:?}", &relayers);
1✔
2099
            self.stats.msgs_err += 1;
1✔
2100
            return Err(net_error::InvalidMessage);
1✔
2101
        }
17,354✔
2102

2103
        self.stats.add_block_push((preamble.payload_len as u64) - 5);
17,354✔
2104

2105
        if self.connection.options.max_block_push_bandwidth > 0
17,354✔
2106
            && self.stats.get_block_push_bandwidth()
2✔
2107
                > (self.connection.options.max_block_push_bandwidth as f64)
2✔
2108
        {
2109
            debug!(
1✔
2110
                "{:?}: Neighbor {:?} exceeded max block-push bandwidth of {} bytes/sec (currently at {})",
2111
                &self,
×
2112
                &self.to_neighbor_key(),
×
2113
                self.connection.options.max_block_push_bandwidth,
2114
                self.stats.get_block_push_bandwidth()
×
2115
            );
2116
            return self
1✔
2117
                .reply_nack(local_peer, chain_view, preamble, NackErrorCodes::Throttled)
1✔
2118
                .map(Some);
1✔
2119
        }
17,353✔
2120
        Ok(None)
17,353✔
2121
    }
17,355✔
2122

2123
    /// Validate pushed microblocks.
2124
    /// Not much we can do to see if they're semantically correct, but we can at least throttle a
2125
    /// peer that sends us too many at once.
2126
    fn validate_microblocks_push(
3✔
2127
        &mut self,
3✔
2128
        network: &PeerNetwork,
3✔
2129
        preamble: &Preamble,
3✔
2130
        relayers: Vec<RelayData>,
3✔
2131
    ) -> Result<Option<ReplyHandleP2P>, net_error> {
3✔
2132
        assert!(preamble.payload_len > 5); // don't count 1-byte type prefix + 4 byte vector length
3✔
2133

2134
        let local_peer = network.get_local_peer();
3✔
2135
        let chain_view = network.get_chain_view();
3✔
2136

2137
        if !self.process_relayers(local_peer, preamble, &relayers) {
3✔
2138
            warn!(
1✔
2139
                "Drop pushed microblocks -- invalid relayers {:?}",
2140
                &relayers
1✔
2141
            );
2142
            self.stats.msgs_err += 1;
1✔
2143
            return Err(net_error::InvalidMessage);
1✔
2144
        }
2✔
2145

2146
        self.stats
2✔
2147
            .add_microblocks_push((preamble.payload_len as u64) - 5);
2✔
2148

2149
        if self.connection.options.max_microblocks_push_bandwidth > 0
2✔
2150
            && self.stats.get_microblocks_push_bandwidth()
2✔
2151
                > (self.connection.options.max_microblocks_push_bandwidth as f64)
2✔
2152
        {
2153
            debug!("{:?}: Neighbor {:?} exceeded max microblocks-push bandwidth of {} bytes/sec (currently at {})", self, &self.to_neighbor_key(), self.connection.options.max_microblocks_push_bandwidth, self.stats.get_microblocks_push_bandwidth());
1✔
2154
            return self
1✔
2155
                .reply_nack(local_peer, chain_view, preamble, NackErrorCodes::Throttled)
1✔
2156
                .map(Some);
1✔
2157
        }
1✔
2158
        Ok(None)
1✔
2159
    }
3✔
2160

2161
    /// Validate a pushed transaction.
2162
    /// Update bandwidth accounting, but forward the transaction along.
2163
    fn validate_transaction_push(
4,512✔
2164
        &mut self,
4,512✔
2165
        network: &PeerNetwork,
4,512✔
2166
        preamble: &Preamble,
4,512✔
2167
        relayers: Vec<RelayData>,
4,512✔
2168
    ) -> Result<Option<ReplyHandleP2P>, net_error> {
4,512✔
2169
        assert!(preamble.payload_len > 1); // don't count 1-byte type prefix
4,512✔
2170

2171
        let local_peer = network.get_local_peer();
4,512✔
2172
        let chain_view = network.get_chain_view();
4,512✔
2173

2174
        if !self.process_relayers(local_peer, preamble, &relayers) {
4,512✔
2175
            warn!(
1✔
2176
                "Drop pushed transaction -- invalid relayers {:?}",
2177
                &relayers
1✔
2178
            );
2179
            self.stats.msgs_err += 1;
1✔
2180
            return Err(net_error::InvalidMessage);
1✔
2181
        }
4,511✔
2182

2183
        self.stats
4,511✔
2184
            .add_transaction_push((preamble.payload_len as u64) - 1);
4,511✔
2185

2186
        if self.connection.options.max_transaction_push_bandwidth > 0
4,511✔
2187
            && self.stats.get_transaction_push_bandwidth()
2✔
2188
                > (self.connection.options.max_transaction_push_bandwidth as f64)
2✔
2189
        {
2190
            debug!("{:?}: Neighbor {:?} exceeded max transaction-push bandwidth of {} bytes/sec (currently at {})", self, &self.to_neighbor_key(), self.connection.options.max_transaction_push_bandwidth, self.stats.get_transaction_push_bandwidth());
1✔
2191
            return self
1✔
2192
                .reply_nack(local_peer, chain_view, preamble, NackErrorCodes::Throttled)
1✔
2193
                .map(Some);
1✔
2194
        }
4,510✔
2195
        Ok(None)
4,510✔
2196
    }
4,512✔
2197

2198
    /// Validate a pushed stackerdb chunk.
2199
    /// Update bandwidth accounting, but forward the stackerdb chunk along if we can accept it.
2200
    /// Possibly return a reply handle for a NACK if we throttle the remote sender
2201
    fn validate_stackerdb_push(
488,295✔
2202
        &mut self,
488,295✔
2203
        network: &PeerNetwork,
488,295✔
2204
        preamble: &Preamble,
488,295✔
2205
        relayers: Vec<RelayData>,
488,295✔
2206
    ) -> Result<Option<ReplyHandleP2P>, net_error> {
488,295✔
2207
        assert!(preamble.payload_len > 1); // don't count 1-byte type prefix
488,295✔
2208

2209
        let local_peer = network.get_local_peer();
488,295✔
2210
        let chain_view = network.get_chain_view();
488,295✔
2211

2212
        if !self.process_relayers(local_peer, preamble, &relayers) {
488,295✔
2213
            warn!(
1✔
2214
                "Drop pushed stackerdb chunk -- invalid relayers {:?}",
2215
                &relayers
1✔
2216
            );
2217
            self.stats.msgs_err += 1;
1✔
2218
            return Err(net_error::InvalidMessage);
1✔
2219
        }
488,294✔
2220

2221
        self.stats
488,294✔
2222
            .add_stackerdb_push((preamble.payload_len as u64) - 1);
488,294✔
2223

2224
        if self.connection.options.max_stackerdb_push_bandwidth > 0
488,294✔
2225
            && self.stats.get_stackerdb_push_bandwidth()
2✔
2226
                > (self.connection.options.max_stackerdb_push_bandwidth as f64)
2✔
2227
        {
2228
            debug!("{:?}: Neighbor {:?} exceeded max stackerdb-push bandwidth of {} bytes/sec (currently at {})", self, &self.to_neighbor_key(), self.connection.options.max_stackerdb_push_bandwidth, self.stats.get_stackerdb_push_bandwidth());
1✔
2229
            return self
1✔
2230
                .reply_nack(local_peer, chain_view, preamble, NackErrorCodes::Throttled)
1✔
2231
                .map(Some);
1✔
2232
        }
488,293✔
2233

2234
        Ok(None)
488,293✔
2235
    }
488,295✔
2236

2237
    /// Validate a pushed Nakamoto block list.
2238
    /// Update bandwidth accounting, but forward the blocks along if we can accept them.
2239
    /// Possibly return a reply handle for a NACK if we throttle the remote sender
2240
    fn validate_nakamoto_block_push(
7,571✔
2241
        &mut self,
7,571✔
2242
        network: &PeerNetwork,
7,571✔
2243
        preamble: &Preamble,
7,571✔
2244
        relayers: Vec<RelayData>,
7,571✔
2245
    ) -> Result<Option<ReplyHandleP2P>, net_error> {
7,571✔
2246
        assert!(preamble.payload_len > 1); // don't count 1-byte type prefix
7,571✔
2247

2248
        let local_peer = network.get_local_peer();
7,571✔
2249
        let chain_view = network.get_chain_view();
7,571✔
2250

2251
        if !self.process_relayers(local_peer, preamble, &relayers) {
7,571✔
2252
            warn!(
×
2253
                "Drop pushed Nakamoto blocks -- invalid relayers {:?}",
2254
                &relayers
×
2255
            );
2256
            self.stats.msgs_err += 1;
×
2257
            return Err(net_error::InvalidMessage);
×
2258
        }
7,571✔
2259

2260
        self.stats
7,571✔
2261
            .add_nakamoto_block_push((preamble.payload_len as u64) - 1);
7,571✔
2262

2263
        if self.connection.options.max_nakamoto_block_push_bandwidth > 0
7,571✔
2264
            && self.stats.get_nakamoto_block_push_bandwidth()
×
2265
                > (self.connection.options.max_nakamoto_block_push_bandwidth as f64)
×
2266
        {
2267
            debug!("{:?}: Neighbor {:?} exceeded max Nakamoto block push bandwidth of {} bytes/sec (currently at {})", self, &self.to_neighbor_key(), self.connection.options.max_nakamoto_block_push_bandwidth, self.stats.get_nakamoto_block_push_bandwidth());
×
2268
            return self
×
2269
                .reply_nack(local_peer, chain_view, preamble, NackErrorCodes::Throttled)
×
2270
                .map(Some);
×
2271
        }
7,571✔
2272

2273
        Ok(None)
7,571✔
2274
    }
7,571✔
2275

2276
    /// Handle an inbound authenticated p2p data-plane message.
2277
    /// Return the message if not handled
2278
    fn handle_data_message(
6,534,522✔
2279
        &mut self,
6,534,522✔
2280
        network: &mut PeerNetwork,
6,534,522✔
2281
        sortdb: &SortitionDB,
6,534,522✔
2282
        chainstate: &mut StacksChainState,
6,534,522✔
2283
        msg: StacksMessage,
6,534,522✔
2284
    ) -> Result<Option<StacksMessage>, net_error> {
6,534,522✔
2285
        let res = match msg.payload {
6,534,522✔
2286
            StacksMessageType::GetNeighbors => self.handle_getneighbors(network, &msg.preamble),
302,496✔
2287
            StacksMessageType::GetPoxInv(ref getpoxinv) => {
189,148✔
2288
                self.handle_getpoxinv(network, sortdb, &msg.preamble, getpoxinv)
189,148✔
2289
            }
2290
            StacksMessageType::GetBlocksInv(ref get_blocks_inv) => {
617,414✔
2291
                self.handle_getblocksinv(network, sortdb, chainstate, &msg.preamble, get_blocks_inv)
617,414✔
2292
            }
2293
            StacksMessageType::GetNakamotoInv(ref get_nakamoto_inv) => self.handle_getnakamotoinv(
228,993✔
2294
                network,
228,993✔
2295
                sortdb,
228,993✔
2296
                chainstate,
228,993✔
2297
                &msg.preamble,
228,993✔
2298
                get_nakamoto_inv,
228,993✔
2299
            ),
2300
            StacksMessageType::Blocks(_) => {
2301
                monitoring::increment_stx_blocks_received_counter();
17,352✔
2302

2303
                // not handled here, but do some accounting -- we can't receive blocks too often,
2304
                // so close this conversation if we do.
2305
                match self.validate_blocks_push(network, &msg.preamble, msg.relayers.clone())? {
17,352✔
2306
                    Some(handle) => Ok(handle),
×
2307
                    None => {
2308
                        // will forward upstream
2309
                        return Ok(Some(msg));
17,352✔
2310
                    }
2311
                }
2312
            }
2313
            StacksMessageType::Microblocks(_) => {
2314
                monitoring::increment_stx_micro_blocks_received_counter();
×
2315

2316
                // not handled here, but do some accounting -- we can't receive too many
2317
                // unconfirmed microblocks per second
2318
                match self.validate_microblocks_push(
×
2319
                    network,
×
2320
                    &msg.preamble,
×
2321
                    msg.relayers.clone(),
×
2322
                )? {
×
2323
                    Some(handle) => Ok(handle),
×
2324
                    None => {
2325
                        // will forward upstream
2326
                        return Ok(Some(msg));
×
2327
                    }
2328
                }
2329
            }
2330
            StacksMessageType::Transaction(_) => {
2331
                monitoring::increment_txs_received_counter();
4,509✔
2332

2333
                // not handled here, but do some accounting -- we can't receive too many
2334
                // unconfirmed transactions per second
2335
                match self.validate_transaction_push(
4,509✔
2336
                    network,
4,509✔
2337
                    &msg.preamble,
4,509✔
2338
                    msg.relayers.clone(),
4,509✔
2339
                )? {
×
2340
                    Some(handle) => Ok(handle),
×
2341
                    None => {
2342
                        // will forward upstream
2343
                        return Ok(Some(msg));
4,509✔
2344
                    }
2345
                }
2346
            }
2347
            StacksMessageType::StackerDBGetChunkInv(ref getchunkinv) => {
3,865,787✔
2348
                self.handle_stacker_db_getchunkinv(network, chainstate, &msg.preamble, getchunkinv)
3,865,787✔
2349
            }
2350
            StacksMessageType::StackerDBGetChunk(ref getchunk) => {
77,815✔
2351
                self.handle_stacker_db_getchunk(network, &msg.preamble, getchunk)
77,815✔
2352
            }
2353
            StacksMessageType::StackerDBChunk(_) | StacksMessageType::StackerDBPushChunk(_) => {
2354
                // not handled here, but do some accounting -- we can't receive too many
2355
                // stackerdb chunks per second
2356
                match self.validate_stackerdb_push(network, &msg.preamble, msg.relayers.clone())? {
488,292✔
2357
                    Some(handle) => Ok(handle),
×
2358
                    None => {
2359
                        // will forward upstream
2360
                        return Ok(Some(msg));
488,292✔
2361
                    }
2362
                }
2363
            }
2364
            StacksMessageType::NakamotoBlocks(_) => {
2365
                // not handled here, but do some accounting -- we can't receive too many
2366
                // Nakamoto blocks per second
2367
                match self.validate_nakamoto_block_push(
7,571✔
2368
                    network,
7,571✔
2369
                    &msg.preamble,
7,571✔
2370
                    msg.relayers.clone(),
7,571✔
2371
                )? {
×
2372
                    Some(handle) => Ok(handle),
×
2373
                    None => {
2374
                        // will forward upstream
2375
                        return Ok(Some(msg));
7,571✔
2376
                    }
2377
                }
2378
            }
2379
            _ => {
2380
                // all else will forward upstream
2381
                return Ok(Some(msg));
735,145✔
2382
            }
2383
        };
2384

2385
        match res {
5,281,653✔
2386
            Ok(handle) => {
5,281,653✔
2387
                self.reply_handles.push_back(handle);
5,281,653✔
2388
                Ok(None)
5,281,653✔
2389
            }
2390
            Err(e) => {
×
2391
                debug!("Failed to handle messsage: {:?}", &e);
×
2392
                Ok(Some(msg))
×
2393
            }
2394
        }
2395
    }
6,534,522✔
2396

2397
    /// Load data into our connection
2398
    pub fn recv<R: Read>(&mut self, r: &mut R) -> Result<usize, net_error> {
4,262,671✔
2399
        let mut total_recved = 0;
4,262,671✔
2400
        loop {
2401
            let res = self.connection.recv_data(r);
8,147,729✔
2402
            match res {
10,291✔
2403
                Ok(num_recved) => {
8,137,438✔
2404
                    total_recved += num_recved;
8,137,438✔
2405
                    if num_recved > 0 {
8,137,438✔
2406
                        debug!("{:?}: received {} bytes", self, num_recved);
3,885,058✔
2407
                        self.stats.last_recv_time = get_epoch_time_secs();
3,885,058✔
2408
                        self.stats.bytes_rx += num_recved as u64;
3,885,058✔
2409
                    } else {
2410
                        debug!("{:?}: received {} bytes, stopping", self, num_recved);
4,252,380✔
2411
                        break;
4,252,380✔
2412
                    }
2413
                }
2414
                Err(net_error::PermanentlyDrained) => {
2415
                    debug!(
10,291✔
2416
                        "{:?}: failed to recv on P2P conversation: PermanentlyDrained",
2417
                        self
2418
                    );
2419
                    return Err(net_error::PermanentlyDrained);
10,291✔
2420
                }
2421
                Err(e) => {
×
2422
                    info!("{:?}: failed to recv on P2P conversation: {:?}", self, &e);
×
2423
                    return Err(e);
×
2424
                }
2425
            }
2426
        }
2427
        debug!("{:?}: received {} bytes", self, total_recved);
4,252,380✔
2428
        Ok(total_recved)
4,252,380✔
2429
    }
4,262,671✔
2430

2431
    /// Write data out of our conversation
2432
    pub fn send<W: Write>(&mut self, w: &mut W) -> Result<usize, net_error> {
40,494,419✔
2433
        let mut total_sent = 0;
40,494,419✔
2434
        loop {
2435
            // queue next byte slice
2436
            self.try_flush()?;
52,797,569✔
2437

2438
            let res = self.connection.send_data(w);
52,797,569✔
2439
            match res {
52,797,569✔
2440
                Ok(num_sent) => {
52,797,047✔
2441
                    total_sent += num_sent;
52,797,047✔
2442
                    if num_sent > 0 {
52,797,047✔
2443
                        self.stats.last_send_time = get_epoch_time_secs();
12,303,150✔
2444
                        self.stats.bytes_tx += num_sent as u64;
12,303,150✔
2445
                    } else {
12,303,150✔
2446
                        break;
40,493,897✔
2447
                    }
2448
                }
2449
                Err(e) => {
522✔
2450
                    info!("{:?}: failed to send on P2P conversation: {:?}", self, &e);
522✔
2451
                    return Err(e);
522✔
2452
                }
2453
            }
2454
        }
2455
        debug!("{:?}: sent {} bytes", self, total_sent);
40,493,897✔
2456
        Ok(total_sent)
40,493,897✔
2457
    }
40,494,419✔
2458

2459
    /// Make progress on in-flight messages.
2460
    pub fn try_flush(&mut self) -> Result<(), net_error> {
52,797,795✔
2461
        // send out responses in the order they were requested
2462
        let mut drained = false;
52,797,795✔
2463
        let mut broken = false;
52,797,795✔
2464
        if let Some(ref mut reply) = self.reply_handles.front_mut() {
52,797,795✔
2465
            // try moving some data to the connection
2466
            match reply.try_flush() {
5,646,323✔
2467
                Ok(res) => {
5,646,323✔
2468
                    drained = res;
5,646,323✔
2469
                }
5,646,323✔
2470
                Err(e) => {
×
2471
                    // dead
2472
                    warn!("Broken P2P connection: {:?}", &e);
×
2473
                    broken = true;
×
2474
                }
2475
            }
2476
        }
47,151,472✔
2477

2478
        if broken || drained {
52,797,795✔
2479
            // done with this stream
5,646,323✔
2480
            self.reply_handles.pop_front();
5,646,323✔
2481
        }
47,151,472✔
2482
        Ok(())
52,797,795✔
2483
    }
52,797,795✔
2484

2485
    /// How many pending outgoing messages are there
2486
    pub fn num_pending_outbound(&self) -> usize {
226✔
2487
        self.reply_handles.len()
226✔
2488
    }
226✔
2489

2490
    /// Validate an inbound p2p message
2491
    /// Return Ok(true) if valid, Ok(false) if invalid, and Err if we should disconnect.
2492
    fn validate_inbound_message(
12,287,890✔
2493
        &mut self,
12,287,890✔
2494
        msg: &StacksMessage,
12,287,890✔
2495
        burnchain_view: &BurnchainView,
12,287,890✔
2496
    ) -> Result<bool, net_error> {
12,287,890✔
2497
        // validate message preamble
2498
        if let Err(e) = self.is_preamble_valid(msg, burnchain_view) {
12,287,890✔
2499
            match e {
53✔
2500
                net_error::InvalidMessage => {
2501
                    // Disconnect from this peer.  If it thinks nothing's wrong, it'll
2502
                    // reconnect on its own.
2503
                    // However, only count this message as error.  Drop all other queued
2504
                    // messages.
2505
                    info!(
53✔
2506
                        "{:?}: Received invalid preamble; dropping connection",
2507
                        &self
53✔
2508
                    );
2509
                    self.stats.msgs_err += 1;
53✔
2510
                    self.stats.add_healthpoint(false);
53✔
2511
                    return Err(e);
53✔
2512
                }
2513
                _ => {
2514
                    // skip this message
2515
                    info!("{:?}: Failed to process message: {:?}", &self, &e);
×
2516
                    self.stats.msgs_err += 1;
×
2517
                    self.stats.add_healthpoint(false);
×
2518
                    return Ok(false);
×
2519
                }
2520
            }
2521
        }
12,287,837✔
2522
        return Ok(true);
12,287,837✔
2523
    }
12,287,890✔
2524

2525
    /// Handle an inbound authenticated p2p control-plane message
2526
    /// Return true if we should consume it (i.e. it's not something to forward along), as well as the message we'll send as a reply (if any)
2527
    fn handle_authenticated_control_message(
12,263,349✔
2528
        &mut self,
12,263,349✔
2529
        network: &mut PeerNetwork,
12,263,349✔
2530
        msg: &mut StacksMessage,
12,263,349✔
2531
        ibd: bool,
12,263,349✔
2532
    ) -> Result<(Option<StacksMessage>, bool), net_error> {
12,263,349✔
2533
        let mut consume = false;
12,263,349✔
2534

2535
        // already have public key; match payload
2536
        let reply_opt = match msg.payload {
12,263,349✔
2537
            StacksMessageType::Handshake(_) => {
2538
                monitoring::increment_msg_counter("p2p_authenticated_handshake".to_string());
353,906✔
2539

2540
                debug!("{self:?}: Got Handshake");
353,906✔
2541
                let (handshake_opt, handled) = self.handle_handshake(network, msg, true, ibd)?;
353,906✔
2542
                consume = handled;
353,906✔
2543
                Ok(handshake_opt)
353,906✔
2544
            }
2545
            StacksMessageType::HandshakeAccept(ref data) => {
7,317✔
2546
                debug!("{self:?}: Got HandshakeAccept");
7,317✔
2547
                self.handle_handshake_accept(network.get_chain_view(), &msg.preamble, data, None)
7,317✔
2548
                    .map(|_| None)
7,317✔
2549
            }
2550
            StacksMessageType::StackerDBHandshakeAccept(ref data, ref db_data) => {
346,234✔
2551
                debug!("{self:?}: Got StackerDBHandshakeAccept");
346,234✔
2552
                self.handle_handshake_accept(
346,234✔
2553
                    network.get_chain_view(),
346,234✔
2554
                    &msg.preamble,
346,234✔
2555
                    data,
346,234✔
2556
                    Some(db_data),
346,234✔
2557
                )
2558
                .map(|_| None)
346,234✔
2559
            }
2560
            StacksMessageType::Ping(_) => {
2561
                debug!("{self:?}: Got Ping");
6✔
2562

2563
                // consume here if unsolicited
2564
                consume = true;
6✔
2565
                Ok(Some(self.handle_ping(network.get_chain_view(), msg)))
6✔
2566
            }
2567
            StacksMessageType::Pong(_) => {
2568
                debug!("{self:?}: Got Pong");
6✔
2569
                Ok(None)
6✔
2570
            }
2571
            StacksMessageType::NatPunchRequest(ref nonce) => {
666✔
2572
                if cfg!(test) && self.connection.options.disable_natpunch {
666✔
2573
                    return Err(net_error::InvalidMessage);
1✔
2574
                }
665✔
2575
                debug!("{self:?}: Got NatPunchRequest({nonce})");
665✔
2576

2577
                consume = true;
665✔
2578
                let msg = self.handle_natpunch_request(network.get_chain_view(), *nonce);
665✔
2579
                Ok(Some(msg))
665✔
2580
            }
2581
            StacksMessageType::NatPunchReply(ref _m) => {
665✔
2582
                if cfg!(test) && self.connection.options.disable_natpunch {
665✔
2583
                    return Err(net_error::InvalidMessage);
×
2584
                }
665✔
2585
                debug!("{self:?}: Got NatPunchReply({})", _m.nonce);
665✔
2586
                Ok(None)
665✔
2587
            }
2588
            _ => {
2589
                debug!(
11,554,549✔
2590
                    "{self:?}: Got a data-plane message (type {})",
2591
                    msg.payload.get_message_name()
×
2592
                );
2593
                Ok(None) // nothing to reply to at this time
11,554,549✔
2594
            }
2595
        }?;
×
2596
        Ok((reply_opt, consume))
12,263,348✔
2597
    }
12,263,349✔
2598

2599
    /// Handle an inbound unauthenticated p2p control-plane message.
2600
    /// Return true if the message was also solicited, as well as the reply we generate to
2601
    /// deal with it (if we do deal with it)
2602
    fn handle_unauthenticated_control_message(
24,488✔
2603
        &mut self,
24,488✔
2604
        network: &mut PeerNetwork,
24,488✔
2605
        msg: &mut StacksMessage,
24,488✔
2606
        ibd: bool,
24,488✔
2607
    ) -> Result<(Option<StacksMessage>, bool), net_error> {
24,488✔
2608
        // only thing we'll take right now is a handshake, as well as handshake
2609
        // accept/rejects, nacks, and NAT holepunches
2610
        //
2611
        // Anything else will be nack'ed -- the peer will first need to handshake.
2612
        let mut consume = false;
24,488✔
2613
        let solicited = self.connection.is_solicited(msg);
24,488✔
2614
        let reply_opt = match msg.payload {
24,488✔
2615
            StacksMessageType::Handshake(_) => {
2616
                monitoring::increment_msg_counter("p2p_unauthenticated_handshake".to_string());
12,282✔
2617
                debug!("{:?}: Got unauthenticated Handshake", &self);
12,282✔
2618
                let (reply_opt, handled) = self.handle_handshake(network, msg, false, ibd)?;
12,282✔
2619
                consume = handled;
12,281✔
2620
                Ok(reply_opt)
12,281✔
2621
            }
2622
            StacksMessageType::HandshakeAccept(ref data) => {
6,983✔
2623
                if solicited {
6,983✔
2624
                    debug!("{:?}: Got unauthenticated HandshakeAccept", &self);
6,982✔
2625
                    self.handle_handshake_accept(
6,982✔
2626
                        network.get_chain_view(),
6,982✔
2627
                        &msg.preamble,
6,982✔
2628
                        data,
6,982✔
2629
                        None,
6,982✔
2630
                    )
2631
                    .map(|_| None)
6,982✔
2632
                } else {
2633
                    debug!("{:?}: Unsolicited unauthenticated HandshakeAccept", &self);
1✔
2634

2635
                    // don't update stats or state, and don't pass back
2636
                    consume = true;
1✔
2637
                    Ok(None)
1✔
2638
                }
2639
            }
2640
            StacksMessageType::StackerDBHandshakeAccept(ref data, ref db_data) => {
4,651✔
2641
                if solicited {
4,651✔
2642
                    debug!("{:?}: Got unauthenticated StackerDBHandshakeAccept", &self);
4,650✔
2643
                    self.handle_handshake_accept(
4,650✔
2644
                        network.get_chain_view(),
4,650✔
2645
                        &msg.preamble,
4,650✔
2646
                        data,
4,650✔
2647
                        Some(db_data),
4,650✔
2648
                    )
2649
                    .map(|_| None)
4,650✔
2650
                } else {
2651
                    debug!(
1✔
2652
                        "{:?}: Unsolicited unauthenticated StackerDBHandshakeAccept",
2653
                        &self
×
2654
                    );
2655

2656
                    // don't update stats or state, and don't pass back
2657
                    consume = true;
1✔
2658
                    Ok(None)
1✔
2659
                }
2660
            }
2661
            StacksMessageType::HandshakeReject => {
2662
                debug!("{:?}: Got unauthenticated HandshakeReject", &self);
568✔
2663

2664
                // don't NACK this back just because we were rejected.
2665
                // But, it's okay to forward this back (i.e. don't consume).
2666
                Ok(None)
568✔
2667
            }
2668
            StacksMessageType::Nack(_) => {
2669
                debug!("{:?}: Got unauthenticated Nack", &self);
1✔
2670

2671
                // don't NACK back.
2672
                // But, it's okay to forward this back (i.e. don't consume).
2673
                Ok(None)
1✔
2674
            }
2675
            StacksMessageType::NatPunchRequest(ref nonce) => {
1✔
2676
                if cfg!(test) && self.connection.options.disable_natpunch {
1✔
2677
                    return Err(net_error::InvalidMessage);
×
2678
                }
1✔
2679
                debug!(
1✔
2680
                    "{:?}: Got unauthenticated NatPunchRequest({})",
2681
                    &self, *nonce
×
2682
                );
2683
                consume = true;
1✔
2684
                let msg = self.handle_natpunch_request(network.get_chain_view(), *nonce);
1✔
2685
                Ok(Some(msg))
1✔
2686
            }
2687
            StacksMessageType::NatPunchReply(ref _m) => {
1✔
2688
                if cfg!(test) && self.connection.options.disable_natpunch {
1✔
2689
                    return Err(net_error::InvalidMessage);
×
2690
                }
1✔
2691
                debug!(
1✔
2692
                    "{:?}: Got unauthenticated NatPunchReply({})",
2693
                    &self, _m.nonce
×
2694
                );
2695

2696
                // it's okay to forward this back (i.e. don't consume)
2697
                Ok(None)
1✔
2698
            }
2699
            _ => {
2700
                debug!(
1✔
2701
                    "{:?}: Got unauthenticated message (type {}), will NACK",
2702
                    &self,
×
2703
                    msg.payload.get_message_name()
×
2704
                );
2705
                let nack_payload =
1✔
2706
                    StacksMessageType::Nack(NackData::new(NackErrorCodes::HandshakeRequired));
1✔
2707
                let nack = StacksMessage::from_chain_view(
1✔
2708
                    self.version,
1✔
2709
                    self.network_id,
1✔
2710
                    network.get_chain_view(),
1✔
2711
                    nack_payload,
1✔
2712
                );
2713

2714
                monitoring::increment_msg_counter("p2p_nack_sent".to_string());
1✔
2715

2716
                // unauthenticated, so don't forward it (but do consume it, and do nack it)
2717
                consume = true;
1✔
2718
                Ok(Some(nack))
1✔
2719
            }
2720
        }?;
×
2721
        Ok((reply_opt, consume))
24,487✔
2722
    }
24,488✔
2723

2724
    /// Update chat statistics, depending on whether or not the message was solicited
2725
    fn update_stats(&mut self, msg: &StacksMessage, solicited: bool) {
12,287,835✔
2726
        if solicited {
12,287,835✔
2727
            let now = get_epoch_time_secs();
12,275,550✔
2728

2729
            // successfully got a message we asked for -- update stats
2730
            if self.stats.first_contact_time == 0 {
12,275,550✔
2731
                self.stats.first_contact_time = now;
18,766✔
2732
            }
12,256,784✔
2733

2734
            let msg_id = msg.payload.get_message_id();
12,275,550✔
2735
            if let Some(count) = self.stats.msg_rx_counts.get_mut(&msg_id) {
12,275,550✔
2736
                *count += 1;
12,219,437✔
2737
            } else {
12,226,140✔
2738
                self.stats.msg_rx_counts.insert(msg_id, 1);
56,113✔
2739
            }
56,113✔
2740

2741
            self.stats.msgs_rx += 1;
12,275,550✔
2742
            self.stats.last_recv_time = now;
12,275,550✔
2743
            self.stats.last_contact_time = get_epoch_time_secs();
12,275,550✔
2744
            self.stats.add_healthpoint(true);
12,275,550✔
2745

2746
            // update chain view from preamble
2747
            if msg.preamble.burn_block_height > self.burnchain_tip_height {
12,275,550✔
2748
                self.burnchain_tip_height = msg.preamble.burn_block_height;
86,773✔
2749
                self.burnchain_tip_burn_header_hash = msg.preamble.burn_block_hash.clone();
86,773✔
2750
            }
12,188,777✔
2751

2752
            if msg.preamble.burn_stable_block_height > self.burnchain_stable_tip_height {
12,275,550✔
2753
                self.burnchain_stable_tip_height = msg.preamble.burn_stable_block_height;
86,314✔
2754
                self.burnchain_stable_tip_burn_header_hash =
86,314✔
2755
                    msg.preamble.burn_stable_block_hash.clone();
86,314✔
2756
            }
12,189,236✔
2757

2758
            debug!(
12,275,550✔
2759
                "{:?}: remote chain view is ({},{})-({},{})",
2760
                self,
2761
                self.burnchain_stable_tip_height,
2762
                &self.burnchain_stable_tip_burn_header_hash,
×
2763
                self.burnchain_tip_height,
2764
                &self.burnchain_tip_burn_header_hash
×
2765
            );
2766
        } else {
12,285✔
2767
            // got an unhandled message we didn't ask for
12,285✔
2768
            self.stats.msgs_rx_unsolicited += 1;
12,285✔
2769
        }
12,285✔
2770
    }
12,287,835✔
2771

2772
    /// Are we trying to resolve DNS?
2773
    pub fn waiting_for_dns(&self) -> bool {
×
2774
        self.dns_deadline < u128::MAX
×
2775
    }
×
2776

2777
    /// Try to get the IPv4 or IPv6 address out of a data URL.
2778
    fn try_decode_data_url_ipaddr(data_url: &UrlString) -> Option<SocketAddr> {
23,363✔
2779
        // need to begin resolution
2780
        // NOTE: should always succeed, since a UrlString shouldn't decode unless it's a valid URL or the empty string
2781
        let url = data_url.parse_to_block_url().ok()?;
23,363✔
2782
        let port = url.port_or_known_default()?;
23,363✔
2783
        let ip_addr_opt = match url.host() {
23,363✔
2784
            Some(url::Host::Ipv4(addr)) => {
23,327✔
2785
                // have IPv4 address already
2786
                Some(SocketAddr::new(IpAddr::V4(addr), port))
23,327✔
2787
            }
2788
            Some(url::Host::Ipv6(addr)) => {
×
2789
                // have IPv6 address already
2790
                Some(SocketAddr::new(IpAddr::V6(addr), port))
×
2791
            }
2792
            _ => None,
36✔
2793
        };
2794
        ip_addr_opt
23,363✔
2795
    }
23,363✔
2796

2797
    /// Attempt to resolve the hostname of a conversation's data URL to its IP address.
2798
    fn try_resolve_data_url_host(
4,262,555✔
2799
        &mut self,
4,262,555✔
2800
        dns_client_opt: &mut Option<&mut DNSClient>,
4,262,555✔
2801
        dns_timeout: u128,
4,262,555✔
2802
    ) {
4,262,555✔
2803
        if self.data_ip.is_some() {
4,262,555✔
2804
            return;
4,223,046✔
2805
        }
39,509✔
2806
        if self.data_url.is_empty() {
39,509✔
2807
            return;
16,146✔
2808
        }
23,363✔
2809
        if let Some(ipaddr) = Self::try_decode_data_url_ipaddr(&self.data_url) {
23,363✔
2810
            // don't need to resolve!
2811
            debug!(
23,327✔
2812
                "{}: Resolved data URL {} to {}",
2813
                &self, &self.data_url, &ipaddr
×
2814
            );
2815
            self.data_ip = Some(ipaddr);
23,327✔
2816
            return;
23,327✔
2817
        }
36✔
2818

2819
        let Some(dns_client) = dns_client_opt else {
36✔
2820
            return;
36✔
2821
        };
2822
        if get_epoch_time_ms() < self.dns_deadline {
×
2823
            return;
×
2824
        }
×
2825
        if let Some(dns_request) = self.dns_request.take() {
×
2826
            // perhaps resolution completed?
2827
            match dns_client.poll_lookup(&dns_request.host, dns_request.port) {
×
2828
                Ok(query_result_opt) => {
×
2829
                    // just take one of the addresses, if there are any
2830
                    self.data_ip =
2831
                        query_result_opt.and_then(|query_result| match query_result.result {
×
2832
                            Ok(mut ips) => ips.pop(),
×
2833
                            Err(e) => {
×
2834
                                warn!(
×
2835
                                    "{}: Failed to resolve data URL {}: {:?}",
2836
                                    self, &self.data_url, &e
×
2837
                                );
2838

2839
                                // don't try again
2840
                                self.dns_deadline = u128::MAX;
×
2841
                                None
×
2842
                            }
2843
                        });
×
2844
                    if let Some(ip) = self.data_ip.as_ref() {
×
2845
                        debug!("{}: Resolved data URL {} to {}", &self, &self.data_url, &ip);
×
2846
                    } else {
2847
                        info!(
×
2848
                            "{}: Failed to resolve URL {}: no IP addresses found",
2849
                            &self, &self.data_url
×
2850
                        );
2851
                    }
2852
                    // don't try again
2853
                    self.dns_deadline = u128::MAX;
×
2854
                }
2855
                Err(e) => {
×
2856
                    warn!("DNS lookup failed on {}: {:?}", &self.data_url, &e);
×
2857

2858
                    // don't try again
2859
                    self.dns_deadline = u128::MAX;
×
2860
                }
2861
            }
2862
        }
×
2863

2864
        // need to begin resolution
2865
        // NOTE: should always succeed, since a UrlString shouldn't decode unless it's a valid URL or the empty string
2866
        let Ok(url) = self.data_url.parse_to_block_url() else {
×
2867
            return;
×
2868
        };
2869
        let port = match url.port_or_known_default() {
×
2870
            Some(p) => p,
×
2871
            None => {
2872
                warn!("Unsupported URL {:?}: unknown port", &url);
×
2873

2874
                // don't try again
2875
                self.dns_deadline = u128::MAX;
×
2876
                return;
×
2877
            }
2878
        };
2879
        let ip_addr_opt = match url.host() {
×
2880
            Some(url::Host::Domain(domain)) => {
×
2881
                // need to resolve a DNS name
2882
                let deadline = get_epoch_time_ms().saturating_add(dns_timeout);
×
2883
                if let Err(e) = dns_client.queue_lookup(domain, port, deadline) {
×
2884
                    debug!("Failed to queue DNS resolution of {}: {:?}", &url, &e);
×
2885
                    return;
×
2886
                }
×
2887
                self.dns_request = Some(DNSRequest::new(domain.to_string(), port, 0));
×
2888
                self.dns_deadline = deadline;
×
2889
                None
×
2890
            }
2891
            Some(url::Host::Ipv4(addr)) => {
×
2892
                // have IPv4 address already
2893
                Some(SocketAddr::new(IpAddr::V4(addr), port))
×
2894
            }
2895
            Some(url::Host::Ipv6(addr)) => {
×
2896
                // have IPv6 address already
2897
                Some(SocketAddr::new(IpAddr::V6(addr), port))
×
2898
            }
2899
            None => {
2900
                warn!("Unsupported URL {:?}", &url);
×
2901

2902
                // don't try again
2903
                self.dns_deadline = u128::MAX;
×
2904
                return;
×
2905
            }
2906
        };
2907
        self.data_ip = ip_addr_opt;
×
2908
        if let Some(ip) = self.data_ip.as_ref() {
×
2909
            debug!("{}: Resolved data URL {} to {}", &self, &self.data_url, &ip);
×
2910
        }
×
2911
    }
4,262,555✔
2912

2913
    /// Carry on a conversation with the remote peer.
2914
    /// Called from the p2p network thread, so no need for a network handle.
2915
    /// Attempts to fulfill requests in other threads as a result of processing a message.
2916
    /// Returns the list of unfulfilled Stacks messages we received -- messages not destined for
2917
    /// any other thread in this program (i.e. "unsolicited messages").
2918
    pub fn chat(
4,262,610✔
2919
        &mut self,
4,262,610✔
2920
        network: &mut PeerNetwork,
4,262,610✔
2921
        sortdb: &SortitionDB,
4,262,610✔
2922
        chainstate: &mut StacksChainState,
4,262,610✔
2923
        dns_client_opt: &mut Option<&mut DNSClient>,
4,262,610✔
2924
        ibd: bool,
4,262,610✔
2925
    ) -> Result<Vec<StacksMessage>, net_error> {
4,262,610✔
2926
        let num_inbound = self.connection.inbox_len();
4,262,610✔
2927
        debug!("{:?}: {} messages pending", &self, num_inbound);
4,262,610✔
2928

2929
        let mut unsolicited = vec![];
4,262,610✔
2930
        for _ in 0..num_inbound {
4,262,610✔
2931
            let update_stats; // whether or not this message can count towards this peer's liveness stats
2932
            let mut msg = match self.connection.next_inbox_message() {
12,287,890✔
2933
                None => {
2934
                    continue;
×
2935
                }
2936
                Some(m) => m,
12,287,890✔
2937
            };
2938

2939
            if !self.validate_inbound_message(&msg, network.get_chain_view())? {
12,287,890✔
2940
                continue;
×
2941
            }
12,287,837✔
2942

2943
            let (mut reply_opt, consumed) = if self.connection.has_public_key() {
12,287,837✔
2944
                // we already have this remote peer's public key, so the message signature will
2945
                // have been verified by the underlying ConnectionP2P.
2946
                update_stats = true;
12,263,349✔
2947
                self.handle_authenticated_control_message(network, &mut msg, ibd)?
12,263,349✔
2948
            } else {
2949
                // the underlying ConnectionP2P does not yet have a public key installed (i.e.
2950
                // we don't know it yet), so treat this message with a little bit more
2951
                // suspicion.
2952
                // Update stats only if we were asking for this message.
2953
                update_stats = self.connection.is_solicited(&msg);
24,488✔
2954
                self.handle_unauthenticated_control_message(network, &mut msg, ibd)?
24,488✔
2955
            };
2956

2957
            if let Some(mut reply) = reply_opt.take() {
12,287,835✔
2958
                // handler generated a reply.
2959
                // send back this message to the remote peer.
2960
                debug!(
366,860✔
2961
                    "{:?}: Send control-plane reply type {}",
2962
                    &self,
×
2963
                    reply.payload.get_message_name()
×
2964
                );
2965
                reply.sign(msg.preamble.seq, &network.get_local_peer().private_key)?;
366,860✔
2966
                let reply_handle = self.relay_signed_message(reply)?;
366,860✔
2967
                self.reply_handles.push_back(reply_handle);
366,860✔
2968
            }
11,920,975✔
2969

2970
            self.update_stats(&msg, update_stats);
12,287,835✔
2971

2972
            let _msgtype = msg.payload.get_message_description().to_owned();
12,287,835✔
2973
            let _relayers = format!("{:?}", &msg.relayers);
12,287,835✔
2974
            let _seq = msg.request_id();
12,287,835✔
2975

2976
            debug!(
12,287,835✔
2977
                "{:?}: Received message {}, relayed by {}",
2978
                &self, &_msgtype, &_relayers
×
2979
            );
2980

2981
            // Is there someone else waiting for this message?  If so, pass it along.
2982
            if let Some(msg) = self.connection.fulfill_request(msg) {
12,287,835✔
2983
                if consumed {
6,535,785✔
2984
                    // already handled
2985
                    debug!(
1,263✔
2986
                        "{:?}: Consumed message (type {} seq {})",
2987
                        &self, _msgtype, _seq
×
2988
                    );
2989
                } else {
2990
                    debug!(
6,534,522✔
2991
                        "{:?}: Try handling message (type {} seq {})",
2992
                        &self, _msgtype, _seq
×
2993
                    );
2994
                    if let Some(msg) = self.handle_data_message(network, sortdb, chainstate, msg)? {
6,534,522✔
2995
                        // this message was unsolicited
2996
                        debug!(
1,252,869✔
2997
                            "{:?}: Did not handle message (type {} seq {}); passing upstream",
2998
                            &self, _msgtype, _seq
×
2999
                        );
3000
                        unsolicited.push(msg);
1,252,869✔
3001
                    } else {
3002
                        // expected and handled the message
3003
                        debug!("{:?}: Handled message {} seq {}", &self, _msgtype, _seq);
5,281,653✔
3004
                    }
3005
                }
3006
            } else {
3007
                // message was passed to the relevant message handle
3008
                debug!(
5,752,050✔
3009
                    "{:?}: Fulfilled pending message request (type {} seq {})",
3010
                    &self, _msgtype, _seq
×
3011
                );
3012
            }
3013
        }
3014

3015
        // while we're at it, update our IP address if we have a pending DNS resolution (or start
3016
        // the process if we need it)
3017
        self.try_resolve_data_url_host(dns_client_opt, network.get_connection_opts().dns_timeout);
4,262,555✔
3018
        Ok(unsolicited)
4,262,555✔
3019
    }
4,262,610✔
3020

3021
    /// Remove all timed-out messages, and ding the remote peer as unhealthy
3022
    pub fn clear_timeouts(&mut self) {
10,363,229✔
3023
        let num_drained = self.connection.drain_timeouts();
10,363,229✔
3024
        for _ in 0..num_drained {
10,363,229✔
3025
            self.stats.add_healthpoint(false);
382,653✔
3026
        }
382,653✔
3027
    }
10,363,229✔
3028

3029
    /// Get a ref to the conversation stats
3030
    pub fn get_stats(&self) -> &NeighborStats {
×
3031
        &self.stats
×
3032
    }
×
3033

3034
    /// Get a mut ref to the conversation stats
3035
    pub fn get_stats_mut(&mut self) -> &mut NeighborStats {
10,363,210✔
3036
        &mut self.stats
10,363,210✔
3037
    }
10,363,210✔
3038
}
3039

3040
#[cfg(test)]
3041
mod test {
3042
    use std::fs;
3043
    use std::net::{Ipv4Addr, SocketAddr};
3044

3045
    use stacks_common::types::chainstate::{BlockHeaderHash, BurnchainHeaderHash, SortitionId};
3046
    use stacks_common::util::pipe::*;
3047
    use stacks_common::util::secp256k1::*;
3048
    use stacks_common::util::uint::*;
3049
    use stacks_common::util::*;
3050

3051
    use super::*;
3052
    use crate::burnchains::db::BurnchainDB;
3053
    use crate::burnchains::*;
3054
    use crate::chainstate::burn::db::sortdb::*;
3055
    use crate::chainstate::burn::*;
3056
    use crate::chainstate::stacks::db::ChainStateBootData;
3057
    use crate::core::*;
3058
    use crate::net::asn::ASEntry4;
3059
    use crate::net::atlas::{AtlasConfig, AtlasDB};
3060
    use crate::net::connection::*;
3061
    use crate::net::db::*;
3062
    use crate::net::stackerdb::StackerDBs;
3063
    use crate::net::*;
3064
    use crate::util_lib::test::*;
3065

3066
    const DEFAULT_SERVICES: u16 = (ServiceFlags::RELAY as u16) | (ServiceFlags::RPC as u16);
3067
    const STACKERDB_SERVICES: u16 = (ServiceFlags::RELAY as u16)
3068
        | (ServiceFlags::RPC as u16)
3069
        | (ServiceFlags::STACKERDB as u16);
3070

3071
    fn make_test_chain_dbs(
39✔
3072
        testname: &str,
39✔
3073
        burnchain: &Burnchain,
39✔
3074
        network_id: u32,
39✔
3075
        key_expires: u64,
39✔
3076
        data_url: UrlString,
39✔
3077
        asn4_entries: &[ASEntry4],
39✔
3078
        initial_neighbors: &[Neighbor],
39✔
3079
        services: u16,
39✔
3080
    ) -> (PeerDB, SortitionDB, StackerDBs, PoxId, StacksChainState) {
39✔
3081
        let test_path = format!("/tmp/stacks-test-databases-{}", testname);
39✔
3082
        if fs::metadata(&test_path).is_ok() {
39✔
3083
            fs::remove_dir_all(&test_path).unwrap();
3✔
3084
        };
36✔
3085

3086
        fs::create_dir_all(&test_path).unwrap();
39✔
3087

3088
        let sortdb_path = format!("{}/burn", &test_path);
39✔
3089
        let peerdb_path = format!("{}/peers.sqlite", &test_path);
39✔
3090
        let stackerdb_path = format!("{}/stackerdb.sqlite", &test_path);
39✔
3091
        let chainstate_path = format!("{}/chainstate", &test_path);
39✔
3092
        let burnchain_db =
39✔
3093
            BurnchainDB::connect(&burnchain.get_burnchaindb_path(), burnchain, true).unwrap();
39✔
3094

3095
        let mut peerdb = PeerDB::connect(
39✔
3096
            &peerdb_path,
39✔
3097
            true,
3098
            network_id,
39✔
3099
            burnchain.network_id,
39✔
3100
            None,
39✔
3101
            key_expires,
39✔
3102
            PeerAddress::from_ipv4(127, 0, 0, 1),
39✔
3103
            NETWORK_P2P_PORT,
3104
            data_url,
39✔
3105
            asn4_entries,
39✔
3106
            Some(initial_neighbors),
39✔
3107
            &[QualifiedContractIdentifier::parse("SP000000000000000000002Q6VF78.sbtc").unwrap()],
39✔
3108
        )
3109
        .unwrap();
39✔
3110
        let sortdb = SortitionDB::connect(
39✔
3111
            &sortdb_path,
39✔
3112
            burnchain.first_block_height,
39✔
3113
            &burnchain.first_block_hash,
39✔
3114
            get_epoch_time_secs(),
39✔
3115
            &StacksEpoch::unit_test_pre_2_05(burnchain.first_block_height),
39✔
3116
            burnchain.pox_constants.clone(),
39✔
3117
            None,
39✔
3118
            true,
3119
            None,
39✔
3120
        )
3121
        .unwrap();
39✔
3122

3123
        let tx = peerdb.tx_begin().unwrap();
39✔
3124
        PeerDB::set_local_services(&tx, services).unwrap();
39✔
3125
        tx.commit().unwrap();
39✔
3126

3127
        let stackerdb = StackerDBs::connect(&stackerdb_path, true).unwrap();
39✔
3128

3129
        let first_burnchain_block_height = burnchain.first_block_height;
39✔
3130

3131
        let mut boot_data = ChainStateBootData::new(burnchain, vec![], None);
39✔
3132

3133
        let (chainstate, _) = StacksChainState::open_and_exec(
39✔
3134
            false,
39✔
3135
            network_id,
39✔
3136
            &chainstate_path,
39✔
3137
            Some(&mut boot_data),
39✔
3138
            None,
39✔
3139
        )
39✔
3140
        .unwrap();
39✔
3141

3142
        let pox_id = {
39✔
3143
            let ic = sortdb.index_conn();
39✔
3144
            let tip_sort_id = SortitionDB::get_canonical_sortition_tip(sortdb.conn()).unwrap();
39✔
3145
            let sortdb_reader = SortitionHandleConn::open_reader(&ic, &tip_sort_id).unwrap();
39✔
3146
            sortdb_reader.get_pox_id().unwrap()
39✔
3147
        };
3148

3149
        (peerdb, sortdb, stackerdb, pox_id, chainstate)
39✔
3150
    }
39✔
3151

3152
    fn convo_send_recv(
52✔
3153
        sender: &mut ConversationP2P,
52✔
3154
        mut sender_handles: Vec<&mut ReplyHandleP2P>,
52✔
3155
        receiver: &mut ConversationP2P,
52✔
3156
    ) {
52✔
3157
        let (mut pipe_read, mut pipe_write) = Pipe::new();
52✔
3158
        pipe_read.set_nonblocking(true);
52✔
3159

3160
        loop {
3161
            let mut res = true;
113✔
3162
            for i in 0..sender_handles.len() {
149✔
3163
                let r = sender_handles[i].try_flush().unwrap();
149✔
3164
                res = r && res;
149✔
3165
            }
3166

3167
            sender.try_flush().unwrap();
113✔
3168
            receiver.try_flush().unwrap();
113✔
3169

3170
            pipe_write.try_flush().unwrap();
113✔
3171

3172
            let all_relays_flushed =
113✔
3173
                receiver.num_pending_outbound() == 0 && sender.num_pending_outbound() == 0;
113✔
3174

3175
            let nw = sender.send(&mut pipe_write).unwrap();
113✔
3176
            let nr = receiver.recv(&mut pipe_read).unwrap();
113✔
3177

3178
            test_debug!(
113✔
3179
                "res = {}, all_relays_flushed = {}, nr = {}, nw = {}",
3180
                res,
3181
                all_relays_flushed,
3182
                nr,
3183
                nw
3184
            );
3185
            if res && all_relays_flushed && nr == 0 && nw == 0 {
113✔
3186
                break;
52✔
3187
            }
61✔
3188
        }
3189

3190
        eprintln!("pipe_read = {:?}", pipe_read);
52✔
3191
        eprintln!("pipe_write = {:?}", pipe_write);
52✔
3192
    }
52✔
3193

3194
    fn db_setup(
39✔
3195
        test_name: &str,
39✔
3196
        burnchain: &Burnchain,
39✔
3197
        peer_version: u32,
39✔
3198
        peerdb: &mut PeerDB,
39✔
3199
        sortdb: &mut SortitionDB,
39✔
3200
        socketaddr: &SocketAddr,
39✔
3201
        chain_view: &BurnchainView,
39✔
3202
    ) -> PeerNetwork {
39✔
3203
        let test_path = format!("/tmp/stacks-test-databases-{}", test_name);
39✔
3204
        {
39✔
3205
            let tx = peerdb.tx_begin().unwrap();
39✔
3206
            PeerDB::set_local_ipaddr(
39✔
3207
                &tx,
39✔
3208
                &PeerAddress::from_socketaddr(socketaddr),
39✔
3209
                socketaddr.port(),
39✔
3210
            )
39✔
3211
            .unwrap();
39✔
3212
            tx.commit().unwrap();
39✔
3213
        }
39✔
3214
        let mut prev_snapshot = SortitionDB::get_first_block_snapshot(sortdb.conn()).unwrap();
39✔
3215
        for i in prev_snapshot.block_height..chain_view.burn_block_height + 1 {
1,843✔
3216
            let mut next_snapshot = prev_snapshot.clone();
1,843✔
3217

3218
            let big_i = Uint256::from_u64(i);
1,843✔
3219
            let mut big_i_bytes_32 = [0u8; 32];
1,843✔
3220
            let mut big_i_bytes_20 = [0u8; 20];
1,843✔
3221
            big_i_bytes_32.copy_from_slice(&big_i.to_u8_slice());
1,843✔
3222
            big_i_bytes_20.copy_from_slice(&big_i.to_u8_slice()[0..20]);
1,843✔
3223

3224
            next_snapshot.block_height += 1;
1,843✔
3225
            next_snapshot.parent_burn_header_hash = next_snapshot.burn_header_hash.clone();
1,843✔
3226
            if i == chain_view.burn_block_height {
1,843✔
3227
                next_snapshot.burn_header_hash = chain_view.burn_block_hash.clone();
39✔
3228
            } else if i == chain_view.burn_stable_block_height {
1,804✔
3229
                next_snapshot.burn_header_hash = chain_view.burn_stable_block_hash.clone();
39✔
3230
            } else {
1,765✔
3231
                next_snapshot.burn_header_hash = BurnchainHeaderHash(big_i_bytes_32);
1,765✔
3232
            }
1,765✔
3233

3234
            next_snapshot.consensus_hash = ConsensusHash(big_i_bytes_20);
1,843✔
3235
            next_snapshot.sortition_id = SortitionId(big_i_bytes_32);
1,843✔
3236
            next_snapshot.parent_sortition_id = prev_snapshot.sortition_id.clone();
1,843✔
3237
            next_snapshot.ops_hash = OpsHash::from_bytes(&big_i_bytes_32).unwrap();
1,843✔
3238
            next_snapshot.winning_stacks_block_hash = BlockHeaderHash(big_i_bytes_32);
1,843✔
3239
            next_snapshot.winning_block_txid = Txid(big_i_bytes_32);
1,843✔
3240
            next_snapshot.total_burn += 1;
1,843✔
3241
            next_snapshot.sortition = true;
1,843✔
3242
            next_snapshot.sortition_hash = next_snapshot
1,843✔
3243
                .sortition_hash
1,843✔
3244
                .mix_burn_header(&BurnchainHeaderHash(big_i_bytes_32));
1,843✔
3245
            next_snapshot.num_sortitions += 1;
1,843✔
3246

3247
            let mut tx = SortitionHandleTx::begin(sortdb, &prev_snapshot.sortition_id).unwrap();
1,843✔
3248

3249
            let next_index_root = tx
1,843✔
3250
                .append_chain_tip_snapshot(
1,843✔
3251
                    &prev_snapshot,
1,843✔
3252
                    &next_snapshot,
1,843✔
3253
                    &[],
1,843✔
3254
                    &[],
1,843✔
3255
                    None,
1,843✔
3256
                    None,
1,843✔
3257
                    None,
1,843✔
3258
                )
3259
                .unwrap();
1,843✔
3260
            next_snapshot.index_root = next_index_root;
1,843✔
3261

3262
            test_debug!(
1,843✔
3263
                "i = {}, chain_view.burn_block_height = {}, ch = {}",
3264
                i,
3265
                chain_view.burn_block_height,
3266
                next_snapshot.consensus_hash
3267
            );
3268

3269
            prev_snapshot = next_snapshot;
1,843✔
3270

3271
            tx.commit().unwrap();
1,843✔
3272
        }
3273

3274
        let atlasdb_path = format!("{}/atlas.sqlite", &test_path);
39✔
3275
        let peerdb_path = format!("{}/peers.sqlite", &test_path);
39✔
3276
        let stackerdb_path = format!("{}/stackerdb.sqlite", &test_path);
39✔
3277

3278
        let atlas_config = AtlasConfig::new(false);
39✔
3279

3280
        let atlasdb = AtlasDB::connect(atlas_config, &atlasdb_path, true).unwrap();
39✔
3281
        let stackerdbs = StackerDBs::connect(&stackerdb_path, true).unwrap();
39✔
3282
        let peerdb = PeerDB::open(&peerdb_path, true).unwrap();
39✔
3283
        let burnchain_db = burnchain.open_burnchain_db(false).unwrap();
39✔
3284

3285
        let local_peer = PeerDB::get_local_peer(peerdb.conn()).unwrap();
39✔
3286
        let network = PeerNetwork::new(
39✔
3287
            peerdb,
39✔
3288
            atlasdb,
39✔
3289
            stackerdbs,
39✔
3290
            burnchain_db,
39✔
3291
            local_peer,
39✔
3292
            peer_version,
39✔
3293
            burnchain.clone(),
39✔
3294
            chain_view.clone(),
39✔
3295
            ConnectionOptions::default(),
39✔
3296
            HashMap::new(),
39✔
3297
            StacksEpoch::unit_test_pre_2_05(0),
39✔
3298
        );
3299
        network
39✔
3300
    }
39✔
3301

3302
    fn testing_burnchain_config(test_name: &str) -> Burnchain {
40✔
3303
        let first_burn_hash = BurnchainHeaderHash::from_hex(
40✔
3304
            "0000000000000000000000000000000000000000000000000000000000000000",
40✔
3305
        )
3306
        .unwrap();
40✔
3307

3308
        Burnchain {
40✔
3309
            peer_version: PEER_VERSION_TESTNET,
40✔
3310
            network_id: 0,
40✔
3311
            chain_name: "bitcoin".to_string(),
40✔
3312
            network_name: "testnet".to_string(),
40✔
3313
            working_dir: format!("/tmp/stacks-test-databases-{test_name}"),
40✔
3314
            consensus_hash_lifetime: 24,
40✔
3315
            stable_confirmations: 7,
40✔
3316
            first_block_height: 12300,
40✔
3317
            initial_reward_start_block: 12300,
40✔
3318
            first_block_hash: first_burn_hash.clone(),
40✔
3319
            first_block_timestamp: 0,
40✔
3320
            pox_constants: PoxConstants::test_default(),
40✔
3321
            marf_opts: None,
40✔
3322
        }
40✔
3323
    }
40✔
3324

3325
    /// Inner function for testing various kinds of stackerdb/non-stackerdb peer interactions
3326
    fn inner_convo_handshake_accept_stackerdb(
3✔
3327
        peer_1_services: u16,
3✔
3328
        peer_1_rc_consensus_hash: ConsensusHash,
3✔
3329
        peer_2_services: u16,
3✔
3330
        peer_2_rc_consensus_hash: ConsensusHash,
3✔
3331
    ) {
3✔
3332
        with_timeout(100, move || {
3✔
3333
            let conn_opts = ConnectionOptions::default();
3✔
3334

3335
            let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
3✔
3336
            let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
3✔
3337

3338
            let mut chain_view_1 = BurnchainView {
3✔
3339
                burn_block_height: 12348,
3✔
3340
                burn_block_hash: BurnchainHeaderHash([0x11; 32]),
3✔
3341
                burn_stable_block_height: 12341,
3✔
3342
                burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
3✔
3343
                last_burn_block_hashes: HashMap::new(),
3✔
3344
                rc_consensus_hash: peer_1_rc_consensus_hash.clone(),
3✔
3345
            };
3✔
3346
            chain_view_1.make_test_data();
3✔
3347

3348
            let mut chain_view_2 = chain_view_1.clone();
3✔
3349
            chain_view_2.rc_consensus_hash = peer_2_rc_consensus_hash.clone();
3✔
3350

3351
            let test_name_1 = format!(
3✔
3352
                "convo_handshake_accept_1-{peer_1_services}-{peer_2_services}-{peer_1_rc_consensus_hash}-{peer_2_rc_consensus_hash}"
3353
            );
3354

3355
            let test_name_2 = format!(
3✔
3356
                "convo_handshake_accept_1-{peer_1_services}-{peer_2_services}-{peer_1_rc_consensus_hash}-{peer_2_rc_consensus_hash}"
3357
            );
3358

3359
            let burnchain_1 = testing_burnchain_config(&test_name_1);
3✔
3360
            let burnchain_2 = testing_burnchain_config(&test_name_2);
3✔
3361

3362
            let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
3✔
3363
                make_test_chain_dbs(
3✔
3364
                    &test_name_1,
3✔
3365
                    &burnchain_1,
3✔
3366
                    0x9abcdef0,
3✔
3367
                    12350,
3✔
3368
                    UrlString::from_literal("http://peer1.com"),
3✔
3369
                    &[],
3✔
3370
                    &[],
3✔
3371
                    peer_1_services,
3✔
3372
                );
3✔
3373
            let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
3✔
3374
                make_test_chain_dbs(
3✔
3375
                    &test_name_2,
3✔
3376
                    &burnchain_2,
3✔
3377
                    0x9abcdef0,
3✔
3378
                    12351,
3✔
3379
                    UrlString::from_literal("http://peer2.com"),
3✔
3380
                    &[],
3✔
3381
                    &[],
3✔
3382
                    peer_2_services,
3✔
3383
                );
3✔
3384

3385
            let mut net_1 = db_setup(
3✔
3386
                &test_name_1,
3✔
3387
                &burnchain_1,
3✔
3388
                0x9abcdef0,
3389
                &mut peerdb_1,
3✔
3390
                &mut sortdb_1,
3✔
3391
                &socketaddr_1,
3✔
3392
                &chain_view_1,
3✔
3393
            );
3394
            let mut net_2 = db_setup(
3✔
3395
                &test_name_2,
3✔
3396
                &burnchain_2,
3✔
3397
                0x9abcdef0,
3398
                &mut peerdb_2,
3✔
3399
                &mut sortdb_2,
3✔
3400
                &socketaddr_2,
3✔
3401
                &chain_view_2,
3✔
3402
            );
3403

3404
            let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
3✔
3405
            let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
3✔
3406

3407
            peerdb_1
3✔
3408
                .update_local_peer(
3✔
3409
                    0x9abcdef0,
3410
                    burnchain_1.network_id,
3✔
3411
                    local_peer_1.data_url,
3✔
3412
                    local_peer_1.port,
3✔
3413
                    &[
3✔
3414
                        QualifiedContractIdentifier::parse("SP000000000000000000002Q6VF78.sbtc")
3✔
3415
                            .unwrap(),
3✔
3416
                    ],
3✔
3417
                )
3418
                .unwrap();
3✔
3419

3420
            peerdb_2
3✔
3421
                .update_local_peer(
3✔
3422
                    0x9abcdef0,
3423
                    burnchain_2.network_id,
3✔
3424
                    local_peer_2.data_url,
3✔
3425
                    local_peer_2.port,
3✔
3426
                    &[
3✔
3427
                        QualifiedContractIdentifier::parse("SP000000000000000000002Q6VF78.sbtc")
3✔
3428
                            .unwrap(),
3✔
3429
                    ],
3✔
3430
                )
3431
                .unwrap();
3✔
3432

3433
            let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
3✔
3434
            let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
3✔
3435

3436
            assert_eq!(
3✔
3437
                local_peer_1.stacker_dbs,
3438
                vec![
3✔
3439
                    QualifiedContractIdentifier::parse("SP000000000000000000002Q6VF78.sbtc")
3✔
3440
                        .unwrap()
3✔
3441
                ]
3442
            );
3443

3444
            assert_eq!(
3✔
3445
                local_peer_2.stacker_dbs,
3446
                vec![
3✔
3447
                    QualifiedContractIdentifier::parse("SP000000000000000000002Q6VF78.sbtc")
3✔
3448
                        .unwrap()
3✔
3449
                ]
3450
            );
3451

3452
            let mut convo_1 = ConversationP2P::new(
3✔
3453
                123,
3454
                456,
3455
                &burnchain_1,
3✔
3456
                &socketaddr_2,
3✔
3457
                &conn_opts,
3✔
3458
                true,
3459
                0,
3460
                StacksEpoch::unit_test_pre_2_05(0),
3✔
3461
            );
3462
            let mut convo_2 = ConversationP2P::new(
3✔
3463
                123,
3464
                456,
3465
                &burnchain_2,
3✔
3466
                &socketaddr_1,
3✔
3467
                &conn_opts,
3✔
3468
                true,
3469
                0,
3470
                StacksEpoch::unit_test_pre_2_05(0),
3✔
3471
            );
3472

3473
            // no peer public keys known yet
3474
            assert!(convo_1.connection.get_public_key().is_none());
3✔
3475
            assert!(convo_2.connection.get_public_key().is_none());
3✔
3476

3477
            // convo_1 sends a handshake to convo_2
3478
            let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
3✔
3479
            let handshake_1 = convo_1
3✔
3480
                .sign_message(
3✔
3481
                    &chain_view_1,
3✔
3482
                    &local_peer_1.private_key,
3✔
3483
                    StacksMessageType::Handshake(handshake_data_1.clone()),
3✔
3484
                )
3485
                .unwrap();
3✔
3486
            let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
3✔
3487

3488
            // convo_2 receives it and processes it, and since no one is waiting for it, will forward
3489
            // it along to the chat caller (us)
3490
            test_debug!("send handshake");
3✔
3491
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
3✔
3492
            let unhandled_2 = convo_2
3✔
3493
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
3✔
3494
                .unwrap();
3✔
3495

3496
            // convo_1 has a handshakeaccept
3497
            test_debug!("send handshake-accept");
3✔
3498
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
3✔
3499
            let unhandled_1 = convo_1
3✔
3500
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
3✔
3501
                .unwrap();
3✔
3502

3503
            let reply_1 = rh_1.recv(0).unwrap();
3✔
3504

3505
            assert!(unhandled_1.is_empty());
3✔
3506
            assert_eq!(unhandled_2.len(), 1);
3✔
3507

3508
            // convo 2 returns the handshake from convo 1
3509
            if let StacksMessageType::Handshake(data) = &unhandled_2[0].payload {
3✔
3510
                assert_eq!(handshake_data_1, *data);
3✔
3511
            } else {
3512
                panic!("Unexpected payload message type");
×
3513
            }
3514

3515
            if (peer_1_services & (ServiceFlags::STACKERDB as u16) != 0)
3✔
3516
                && (peer_2_services & (ServiceFlags::STACKERDB as u16) != 0)
3✔
3517
            {
3518
                // received a valid StackerDBHandshakeAccept from peer 2?
3519
                let StacksMessageType::StackerDBHandshakeAccept(ref data, ref db_data) =
2✔
3520
                    reply_1.payload
2✔
3521
                else {
3522
                    panic!("Unexpected payload message type");
×
3523
                };
3524
                assert_eq!(data.handshake.addrbytes, local_peer_2.addrbytes);
2✔
3525
                assert_eq!(data.handshake.port, local_peer_2.port);
2✔
3526
                assert_eq!(data.handshake.services, local_peer_2.services);
2✔
3527
                assert_eq!(
2✔
3528
                    data.handshake.node_public_key,
3529
                    StacksPublicKeyBuffer::from_public_key(&Secp256k1PublicKey::from_private(
2✔
3530
                        &local_peer_2.private_key
2✔
3531
                    ))
2✔
3532
                );
3533
                assert_eq!(
2✔
3534
                    data.handshake.expire_block_height,
3535
                    local_peer_2.private_key_expire
3536
                );
3537
                assert_eq!(
2✔
3538
                    data.handshake.data_url,
3539
                    UrlString::from_literal("http://peer2.com")
2✔
3540
                );
3541
                assert_eq!(data.heartbeat_interval, conn_opts.heartbeat);
2✔
3542

3543
                if peer_1_rc_consensus_hash == peer_2_rc_consensus_hash {
2✔
3544
                    assert_eq!(db_data.rc_consensus_hash, chain_view_1.rc_consensus_hash);
1✔
3545

3546
                    // remote peer always replies with its supported smart contracts
3547
                    assert_eq!(
1✔
3548
                        db_data.smart_contracts,
3549
                        vec![QualifiedContractIdentifier::parse(
1✔
3550
                            "SP000000000000000000002Q6VF78.sbtc"
1✔
3551
                        )
3552
                        .unwrap()]
1✔
3553
                    );
3554

3555
                    // peers learn each others' smart contract DBs
3556
                    eprintln!(
1✔
3557
                        "{:?}, {:?}",
3558
                        &convo_1.db_smart_contracts, &convo_2.db_smart_contracts
1✔
3559
                    );
3560
                    assert_eq!(convo_1.db_smart_contracts.len(), 1);
1✔
3561
                    assert!(convo_1.replicates_stackerdb(
1✔
3562
                        &QualifiedContractIdentifier::parse("SP000000000000000000002Q6VF78.sbtc")
1✔
3563
                            .unwrap()
1✔
3564
                    ));
3565
                } else {
3566
                    assert_eq!(db_data.rc_consensus_hash, chain_view_2.rc_consensus_hash);
1✔
3567

3568
                    // peers ignore each others' smart contract DBs
3569
                    eprintln!(
1✔
3570
                        "{:?}, {:?}",
3571
                        &convo_1.db_smart_contracts, &convo_2.db_smart_contracts
1✔
3572
                    );
3573
                    assert!(convo_1.db_smart_contracts.is_empty());
1✔
3574
                    assert!(!convo_1.replicates_stackerdb(
1✔
3575
                        &QualifiedContractIdentifier::parse("SP000000000000000000002Q6VF78.sbtc")
1✔
3576
                            .unwrap()
1✔
3577
                    ));
1✔
3578
                }
3579
            } else {
3580
                let StacksMessageType::HandshakeAccept(data) = &reply_1.payload else {
1✔
3581
                    panic!("Unexpected payload message type");
×
3582
                };
3583
                // received a valid HandshakeAccept from peer 2
3584
                assert_eq!(data.handshake.addrbytes, local_peer_2.addrbytes);
1✔
3585
                assert_eq!(data.handshake.port, local_peer_2.port);
1✔
3586
                assert_eq!(data.handshake.services, local_peer_2.services);
1✔
3587
                assert_eq!(
1✔
3588
                    data.handshake.node_public_key,
3589
                    StacksPublicKeyBuffer::from_public_key(&Secp256k1PublicKey::from_private(
1✔
3590
                        &local_peer_2.private_key
1✔
3591
                    ))
1✔
3592
                );
3593
                assert_eq!(
1✔
3594
                    data.handshake.expire_block_height,
3595
                    local_peer_2.private_key_expire
3596
                );
3597
                assert_eq!(
1✔
3598
                    data.handshake.data_url,
3599
                    UrlString::from_literal("http://peer2.com")
1✔
3600
                );
3601
                assert_eq!(data.heartbeat_interval, conn_opts.heartbeat);
1✔
3602
            }
3603

3604
            // convo_2 got updated with convo_1's peer info, but no heartbeat info
3605
            assert_eq!(convo_2.peer_heartbeat, 3600);
3✔
3606
            assert_eq!(
3✔
3607
                convo_2.connection.get_public_key().unwrap(),
3✔
3608
                Secp256k1PublicKey::from_private(&local_peer_1.private_key)
3✔
3609
            );
3610
            assert_eq!(
3✔
3611
                convo_2.data_url,
3612
                UrlString::from_literal("http://peer1.com")
3✔
3613
            );
3614

3615
            // convo_1 got updated with convo_2's peer info, as well as heartbeat
3616
            assert_eq!(convo_1.peer_heartbeat, conn_opts.heartbeat);
3✔
3617
            assert_eq!(
3✔
3618
                convo_1.connection.get_public_key().unwrap(),
3✔
3619
                Secp256k1PublicKey::from_private(&local_peer_2.private_key)
3✔
3620
            );
3621
            assert_eq!(
3✔
3622
                convo_1.data_url,
3623
                UrlString::from_literal("http://peer2.com")
3✔
3624
            );
3625

3626
            assert_eq!(convo_1.peer_services, peer_2_services);
3✔
3627
            assert_eq!(convo_2.peer_services, peer_1_services);
3✔
3628
        })
3✔
3629
    }
3✔
3630

3631
    #[test]
3632
    /// Two stackerdb peers handshake
3633
    fn convo_handshake_accept_stackerdb() {
1✔
3634
        inner_convo_handshake_accept_stackerdb(
1✔
3635
            STACKERDB_SERVICES,
3636
            ConsensusHash([0x33; 20]),
1✔
3637
            STACKERDB_SERVICES,
3638
            ConsensusHash([0x33; 20]),
1✔
3639
        );
3640
    }
1✔
3641

3642
    #[test]
3643
    /// A stackerdb peer handshakes with a legacy peer
3644
    fn convo_handshake_accept_stackerdb_legacy() {
1✔
3645
        inner_convo_handshake_accept_stackerdb(
1✔
3646
            STACKERDB_SERVICES,
3647
            ConsensusHash([0x44; 20]),
1✔
3648
            DEFAULT_SERVICES,
3649
            ConsensusHash([0x44; 20]),
1✔
3650
        );
3651
    }
1✔
3652

3653
    #[test]
3654
    /// Two stackerdb peers handshake, but with different reward cycle consensus hashes
3655
    fn convo_handshake_accept_stackerdb_bad_consensus_hash() {
1✔
3656
        inner_convo_handshake_accept_stackerdb(
1✔
3657
            STACKERDB_SERVICES,
3658
            ConsensusHash([0x33; 20]),
1✔
3659
            STACKERDB_SERVICES,
3660
            ConsensusHash([0x44; 20]),
1✔
3661
        );
3662
    }
1✔
3663

3664
    #[test]
3665
    fn convo_handshake_accept() {
1✔
3666
        with_timeout(100, || {
1✔
3667
            let conn_opts = ConnectionOptions::default();
1✔
3668

3669
            let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
3670
            let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
3671

3672
            let mut chain_view = BurnchainView {
1✔
3673
                burn_block_height: 12348,
1✔
3674
                burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
3675
                burn_stable_block_height: 12341,
1✔
3676
                burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
3677
                last_burn_block_hashes: HashMap::new(),
1✔
3678
                rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
3679
            };
1✔
3680
            chain_view.make_test_data();
1✔
3681

3682
            let test_name_1 = "convo_handshake_accept_1";
1✔
3683
            let test_name_2 = "convo_handshake_accept_2";
1✔
3684

3685
            let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
3686
            let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
3687

3688
            let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
3689
                make_test_chain_dbs(
1✔
3690
                    test_name_1,
1✔
3691
                    &burnchain_1,
1✔
3692
                    0x9abcdef0,
1✔
3693
                    12350,
1✔
3694
                    UrlString::from_literal("http://peer1.com"),
1✔
3695
                    &[],
1✔
3696
                    &[],
1✔
3697
                    DEFAULT_SERVICES,
1✔
3698
                );
1✔
3699
            let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
3700
                make_test_chain_dbs(
1✔
3701
                    test_name_2,
1✔
3702
                    &burnchain_2,
1✔
3703
                    0x9abcdef0,
1✔
3704
                    12351,
1✔
3705
                    UrlString::from_literal("http://peer2.com"),
1✔
3706
                    &[],
1✔
3707
                    &[],
1✔
3708
                    DEFAULT_SERVICES,
1✔
3709
                );
1✔
3710

3711
            let mut net_1 = db_setup(
1✔
3712
                test_name_1,
1✔
3713
                &burnchain_1,
1✔
3714
                0x9abcdef0,
3715
                &mut peerdb_1,
1✔
3716
                &mut sortdb_1,
1✔
3717
                &socketaddr_1,
1✔
3718
                &chain_view,
1✔
3719
            );
3720
            let mut net_2 = db_setup(
1✔
3721
                test_name_2,
1✔
3722
                &burnchain_2,
1✔
3723
                0x9abcdef0,
3724
                &mut peerdb_2,
1✔
3725
                &mut sortdb_2,
1✔
3726
                &socketaddr_2,
1✔
3727
                &chain_view,
1✔
3728
            );
3729

3730
            let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
3731
            let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
3732

3733
            let mut convo_1 = ConversationP2P::new(
1✔
3734
                123,
3735
                456,
3736
                &burnchain_1,
1✔
3737
                &socketaddr_2,
1✔
3738
                &conn_opts,
1✔
3739
                true,
3740
                0,
3741
                StacksEpoch::unit_test_pre_2_05(0),
1✔
3742
            );
3743
            let mut convo_2 = ConversationP2P::new(
1✔
3744
                123,
3745
                456,
3746
                &burnchain_2,
1✔
3747
                &socketaddr_1,
1✔
3748
                &conn_opts,
1✔
3749
                true,
3750
                0,
3751
                StacksEpoch::unit_test_pre_2_05(0),
1✔
3752
            );
3753

3754
            // no peer public keys known yet
3755
            assert!(convo_1.connection.get_public_key().is_none());
1✔
3756
            assert!(convo_2.connection.get_public_key().is_none());
1✔
3757

3758
            // convo_1 sends a handshake to convo_2
3759
            let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
3760
            let handshake_1 = convo_1
1✔
3761
                .sign_message(
1✔
3762
                    &chain_view,
1✔
3763
                    &local_peer_1.private_key,
1✔
3764
                    StacksMessageType::Handshake(handshake_data_1.clone()),
1✔
3765
                )
3766
                .unwrap();
1✔
3767
            let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
3768

3769
            // convo_2 receives it and processes it, and since no one is waiting for it, will forward
3770
            // it along to the chat caller (us)
3771
            test_debug!("send handshake");
1✔
3772
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
3773
            let unhandled_2 = convo_2
1✔
3774
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
3775
                .unwrap();
1✔
3776

3777
            // convo_1 has a handshakeaccept
3778
            test_debug!("send handshake-accept");
1✔
3779
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
3780
            let unhandled_1 = convo_1
1✔
3781
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
3782
                .unwrap();
1✔
3783

3784
            let reply_1 = rh_1.recv(0).unwrap();
1✔
3785

3786
            assert!(unhandled_1.is_empty());
1✔
3787
            assert_eq!(unhandled_2.len(), 1);
1✔
3788

3789
            // convo 2 returns the handshake from convo 1
3790
            if let StacksMessageType::Handshake(data) = &unhandled_2[0].payload {
1✔
3791
                assert_eq!(handshake_data_1, *data);
1✔
3792
            } else {
3793
                panic!("Unexpected payload message type");
×
3794
            }
3795

3796
            // received a valid HandshakeAccept from peer 2
3797
            if let StacksMessageType::HandshakeAccept(ref data) = reply_1.payload {
1✔
3798
                assert_eq!(data.handshake.addrbytes, local_peer_2.addrbytes);
1✔
3799
                assert_eq!(data.handshake.port, local_peer_2.port);
1✔
3800
                assert_eq!(data.handshake.services, local_peer_2.services);
1✔
3801
                assert_eq!(
1✔
3802
                    data.handshake.node_public_key,
3803
                    StacksPublicKeyBuffer::from_public_key(&Secp256k1PublicKey::from_private(
1✔
3804
                        &local_peer_2.private_key
1✔
3805
                    ))
1✔
3806
                );
3807
                assert_eq!(
1✔
3808
                    data.handshake.expire_block_height,
3809
                    local_peer_2.private_key_expire
3810
                );
3811
                assert_eq!(
1✔
3812
                    data.handshake.data_url,
3813
                    UrlString::from_literal("http://peer2.com")
1✔
3814
                );
3815
                assert_eq!(data.heartbeat_interval, conn_opts.heartbeat);
1✔
3816
            } else {
3817
                panic!("Unexpected payload message type");
×
3818
            }
3819

3820
            // convo_2 got updated with convo_1's peer info, but no heartbeat info
3821
            assert_eq!(convo_2.peer_heartbeat, 3600);
1✔
3822
            assert_eq!(
1✔
3823
                convo_2.connection.get_public_key().unwrap(),
1✔
3824
                Secp256k1PublicKey::from_private(&local_peer_1.private_key)
1✔
3825
            );
3826
            assert_eq!(
1✔
3827
                convo_2.data_url,
3828
                UrlString::from_literal("http://peer1.com")
1✔
3829
            );
3830

3831
            // convo_1 got updated with convo_2's peer info, as well as heartbeat
3832
            assert_eq!(convo_1.peer_heartbeat, conn_opts.heartbeat);
1✔
3833
            assert_eq!(
1✔
3834
                convo_1.connection.get_public_key().unwrap(),
1✔
3835
                Secp256k1PublicKey::from_private(&local_peer_2.private_key)
1✔
3836
            );
3837
            assert_eq!(
1✔
3838
                convo_1.data_url,
3839
                UrlString::from_literal("http://peer2.com")
1✔
3840
            );
3841
        })
1✔
3842
    }
1✔
3843

3844
    #[test]
3845
    fn convo_handshake_reject() {
1✔
3846
        let conn_opts = ConnectionOptions::default();
1✔
3847
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
3848
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
3849

3850
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
3851
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
3852
        )
3853
        .unwrap();
1✔
3854

3855
        let mut chain_view = BurnchainView {
1✔
3856
            burn_block_height: 12348,
1✔
3857
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
3858
            burn_stable_block_height: 12341,
1✔
3859
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
3860
            last_burn_block_hashes: HashMap::new(),
1✔
3861
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
3862
        };
1✔
3863
        chain_view.make_test_data();
1✔
3864

3865
        let test_name_1 = "convo_handshake_reject_1";
1✔
3866
        let test_name_2 = "convo_handshake_reject_2";
1✔
3867

3868
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
3869
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
3870

3871
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
3872
            make_test_chain_dbs(
1✔
3873
                test_name_1,
1✔
3874
                &burnchain_1,
1✔
3875
                0x9abcdef0,
1✔
3876
                12350,
1✔
3877
                UrlString::from_literal("http://peer1.com"),
1✔
3878
                &[],
1✔
3879
                &[],
1✔
3880
                DEFAULT_SERVICES,
1✔
3881
            );
1✔
3882
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
3883
            make_test_chain_dbs(
1✔
3884
                test_name_2,
1✔
3885
                &burnchain_2,
1✔
3886
                0x9abcdef0,
1✔
3887
                12351,
1✔
3888
                UrlString::from_literal("http://peer2.com"),
1✔
3889
                &[],
1✔
3890
                &[],
1✔
3891
                DEFAULT_SERVICES,
1✔
3892
            );
1✔
3893

3894
        let mut net_1 = db_setup(
1✔
3895
            test_name_1,
1✔
3896
            &burnchain_1,
1✔
3897
            0x9abcdef0,
3898
            &mut peerdb_1,
1✔
3899
            &mut sortdb_1,
1✔
3900
            &socketaddr_1,
1✔
3901
            &chain_view,
1✔
3902
        );
3903
        let mut net_2 = db_setup(
1✔
3904
            test_name_2,
1✔
3905
            &burnchain_2,
1✔
3906
            0x9abcdef0,
3907
            &mut peerdb_2,
1✔
3908
            &mut sortdb_2,
1✔
3909
            &socketaddr_2,
1✔
3910
            &chain_view,
1✔
3911
        );
3912

3913
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
3914
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
3915

3916
        let mut convo_1 = ConversationP2P::new(
1✔
3917
            123,
3918
            456,
3919
            &burnchain_1,
1✔
3920
            &socketaddr_2,
1✔
3921
            &conn_opts,
1✔
3922
            true,
3923
            0,
3924
            StacksEpoch::unit_test_pre_2_05(0),
1✔
3925
        );
3926
        let mut convo_2 = ConversationP2P::new(
1✔
3927
            123,
3928
            456,
3929
            &burnchain_2,
1✔
3930
            &socketaddr_1,
1✔
3931
            &conn_opts,
1✔
3932
            true,
3933
            0,
3934
            StacksEpoch::unit_test_pre_2_05(0),
1✔
3935
        );
3936

3937
        // no peer public keys known yet
3938
        assert!(convo_1.connection.get_public_key().is_none());
1✔
3939
        assert!(convo_2.connection.get_public_key().is_none());
1✔
3940

3941
        // convo_1 sends a _stale_ handshake to convo_2 (wrong public key)
3942
        let mut handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
3943
        handshake_data_1.expire_block_height = 12340;
1✔
3944
        let handshake_1 = convo_1
1✔
3945
            .sign_message(
1✔
3946
                &chain_view,
1✔
3947
                &local_peer_1.private_key,
1✔
3948
                StacksMessageType::Handshake(handshake_data_1),
1✔
3949
            )
3950
            .unwrap();
1✔
3951

3952
        let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
3953

3954
        // convo_2 receives it and automatically rejects it.
3955
        convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
3956
        let unhandled_2 = convo_2
1✔
3957
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
3958
            .unwrap();
1✔
3959

3960
        // convo_1 has a handshakreject
3961
        convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
3962
        let unhandled_1 = convo_1
1✔
3963
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
3964
            .unwrap();
1✔
3965

3966
        let reply_1 = rh_1.recv(0).unwrap();
1✔
3967

3968
        assert!(unhandled_1.is_empty());
1✔
3969
        assert!(unhandled_2.is_empty());
1✔
3970

3971
        // received a valid HandshakeReject from peer 2
3972
        assert!(matches!(
1✔
3973
            reply_1.payload,
1✔
3974
            StacksMessageType::HandshakeReject
3975
        ));
3976

3977
        // neither peer updated their info on one another
3978
        assert!(convo_1.connection.get_public_key().is_none());
1✔
3979
        assert!(convo_2.connection.get_public_key().is_none());
1✔
3980
    }
1✔
3981

3982
    #[test]
3983
    fn convo_handshake_badsignature() {
1✔
3984
        let conn_opts = ConnectionOptions::default();
1✔
3985
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
3986
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
3987

3988
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
3989
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
3990
        )
3991
        .unwrap();
1✔
3992

3993
        let mut chain_view = BurnchainView {
1✔
3994
            burn_block_height: 12348,
1✔
3995
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
3996
            burn_stable_block_height: 12341,
1✔
3997
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
3998
            last_burn_block_hashes: HashMap::new(),
1✔
3999
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
4000
        };
1✔
4001
        chain_view.make_test_data();
1✔
4002

4003
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4004
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4005
        )
4006
        .unwrap();
1✔
4007

4008
        let test_name_1 = "convo_handshake_badsignature_1";
1✔
4009
        let test_name_2 = "convo_handshake_badsignature_2";
1✔
4010

4011
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
4012
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
4013

4014
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
4015
            make_test_chain_dbs(
1✔
4016
                test_name_1,
1✔
4017
                &burnchain_1,
1✔
4018
                0x9abcdef0,
1✔
4019
                12350,
1✔
4020
                UrlString::from_literal("http://peer1.com"),
1✔
4021
                &[],
1✔
4022
                &[],
1✔
4023
                DEFAULT_SERVICES,
1✔
4024
            );
1✔
4025
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
4026
            make_test_chain_dbs(
1✔
4027
                test_name_2,
1✔
4028
                &burnchain_2,
1✔
4029
                0x9abcdef0,
1✔
4030
                12351,
1✔
4031
                UrlString::from_literal("http://peer2.com"),
1✔
4032
                &[],
1✔
4033
                &[],
1✔
4034
                DEFAULT_SERVICES,
1✔
4035
            );
1✔
4036

4037
        let mut net_1 = db_setup(
1✔
4038
            test_name_1,
1✔
4039
            &burnchain_1,
1✔
4040
            0x9abcdef0,
4041
            &mut peerdb_1,
1✔
4042
            &mut sortdb_1,
1✔
4043
            &socketaddr_1,
1✔
4044
            &chain_view,
1✔
4045
        );
4046
        let mut net_2 = db_setup(
1✔
4047
            test_name_2,
1✔
4048
            &burnchain_2,
1✔
4049
            0x9abcdef0,
4050
            &mut peerdb_2,
1✔
4051
            &mut sortdb_2,
1✔
4052
            &socketaddr_2,
1✔
4053
            &chain_view,
1✔
4054
        );
4055

4056
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
4057
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
4058

4059
        let mut convo_1 = ConversationP2P::new(
1✔
4060
            123,
4061
            456,
4062
            &burnchain_1,
1✔
4063
            &socketaddr_2,
1✔
4064
            &conn_opts,
1✔
4065
            true,
4066
            0,
4067
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4068
        );
4069
        let mut convo_2 = ConversationP2P::new(
1✔
4070
            123,
4071
            456,
4072
            &burnchain_2,
1✔
4073
            &socketaddr_1,
1✔
4074
            &conn_opts,
1✔
4075
            true,
4076
            0,
4077
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4078
        );
4079

4080
        // no peer public keys known yet
4081
        assert!(convo_1.connection.get_public_key().is_none());
1✔
4082
        assert!(convo_2.connection.get_public_key().is_none());
1✔
4083

4084
        // convo_1 sends an _invalid_ handshake to convo_2 (bad signature)
4085
        let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
4086
        let mut handshake_1 = convo_1
1✔
4087
            .sign_message(
1✔
4088
                &chain_view,
1✔
4089
                &local_peer_1.private_key,
1✔
4090
                StacksMessageType::Handshake(handshake_data_1),
1✔
4091
            )
4092
            .unwrap();
1✔
4093
        if let StacksMessageType::Handshake(ref mut data) = handshake_1.payload {
1✔
4094
            data.expire_block_height += 1;
1✔
4095
        } else {
1✔
4096
            panic!("Unexpected payload message type");
×
4097
        }
4098

4099
        let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
4100

4101
        // convo_2 receives it and processes it, and barfs
4102
        convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
4103
        let unhandled_2_err =
1✔
4104
            convo_2.chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false);
1✔
4105

4106
        // convo_1 gets a nack and consumes it
4107
        convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
4108
        let unhandled_1 = convo_1
1✔
4109
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
4110
            .unwrap();
1✔
4111

4112
        // the waiting reply aborts on disconnect
4113
        let reply_1_err = rh_1.recv(0);
1✔
4114

4115
        assert_eq!(unhandled_2_err.unwrap_err(), net_error::InvalidMessage);
1✔
4116
        assert_eq!(reply_1_err, Err(net_error::ConnectionBroken));
1✔
4117

4118
        assert!(unhandled_1.is_empty());
1✔
4119

4120
        // neither peer updated their info on one another
4121
        assert!(convo_1.connection.get_public_key().is_none());
1✔
4122
        assert!(convo_2.connection.get_public_key().is_none());
1✔
4123
    }
1✔
4124

4125
    #[test]
4126
    fn convo_handshake_badpeeraddress() {
1✔
4127
        let conn_opts = ConnectionOptions::default();
1✔
4128
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
4129
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
4130

4131
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4132
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4133
        )
4134
        .unwrap();
1✔
4135

4136
        let mut chain_view = BurnchainView {
1✔
4137
            burn_block_height: 12348,
1✔
4138
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
4139
            burn_stable_block_height: 12341,
1✔
4140
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
4141
            last_burn_block_hashes: HashMap::new(),
1✔
4142
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
4143
        };
1✔
4144
        chain_view.make_test_data();
1✔
4145

4146
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4147
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4148
        )
4149
        .unwrap();
1✔
4150

4151
        let test_name_1 = "convo_handshake_badpeeraddress_1";
1✔
4152
        let test_name_2 = "convo_handshake_badpeeraddress_2";
1✔
4153

4154
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
4155
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
4156

4157
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
4158
            make_test_chain_dbs(
1✔
4159
                test_name_1,
1✔
4160
                &burnchain_1,
1✔
4161
                0x9abcdef0,
1✔
4162
                12350,
1✔
4163
                UrlString::from_literal("http://peer1.com"),
1✔
4164
                &[],
1✔
4165
                &[],
1✔
4166
                DEFAULT_SERVICES,
1✔
4167
            );
1✔
4168
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
4169
            make_test_chain_dbs(
1✔
4170
                test_name_2,
1✔
4171
                &burnchain_2,
1✔
4172
                0x9abcdef0,
1✔
4173
                12351,
1✔
4174
                UrlString::from_literal("http://peer2.com"),
1✔
4175
                &[],
1✔
4176
                &[],
1✔
4177
                DEFAULT_SERVICES,
1✔
4178
            );
1✔
4179

4180
        let mut net_1 = db_setup(
1✔
4181
            test_name_1,
1✔
4182
            &burnchain_1,
1✔
4183
            0x9abcdef0,
4184
            &mut peerdb_1,
1✔
4185
            &mut sortdb_1,
1✔
4186
            &socketaddr_1,
1✔
4187
            &chain_view,
1✔
4188
        );
4189
        let mut net_2 = db_setup(
1✔
4190
            test_name_2,
1✔
4191
            &burnchain_2,
1✔
4192
            0x9abcdef0,
4193
            &mut peerdb_2,
1✔
4194
            &mut sortdb_2,
1✔
4195
            &socketaddr_2,
1✔
4196
            &chain_view,
1✔
4197
        );
4198

4199
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
4200
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
4201

4202
        let mut convo_1 = ConversationP2P::new(
1✔
4203
            123,
4204
            456,
4205
            &burnchain_1,
1✔
4206
            &socketaddr_2,
1✔
4207
            &conn_opts,
1✔
4208
            true,
4209
            0,
4210
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4211
        );
4212
        let mut convo_2 = ConversationP2P::new(
1✔
4213
            123,
4214
            456,
4215
            &burnchain_2,
1✔
4216
            &socketaddr_1,
1✔
4217
            &conn_opts,
1✔
4218
            true,
4219
            0,
4220
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4221
        );
4222

4223
        // no peer public keys known yet
4224
        assert!(convo_1.connection.get_public_key().is_none());
1✔
4225
        assert!(convo_2.connection.get_public_key().is_none());
1✔
4226

4227
        // teach each convo about each other's public keys
4228
        convo_1
1✔
4229
            .connection
1✔
4230
            .set_public_key(Some(Secp256k1PublicKey::from_private(
1✔
4231
                &local_peer_2.private_key,
1✔
4232
            )));
1✔
4233
        convo_2
1✔
4234
            .connection
1✔
4235
            .set_public_key(Some(Secp256k1PublicKey::from_private(
1✔
4236
                &local_peer_1.private_key,
1✔
4237
            )));
1✔
4238

4239
        assert!(convo_1.connection.get_public_key().is_some());
1✔
4240
        assert!(convo_2.connection.get_public_key().is_some());
1✔
4241

4242
        convo_1.stats.outbound = false;
1✔
4243
        convo_2.stats.outbound = true;
1✔
4244

4245
        convo_2.peer_port = 8080;
1✔
4246

4247
        // convo_1 sends a handshake from a different address than reported
4248
        let mut handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
4249
        handshake_data_1.port = 8082;
1✔
4250
        let handshake_1 = convo_1
1✔
4251
            .sign_message(
1✔
4252
                &chain_view,
1✔
4253
                &local_peer_1.private_key,
1✔
4254
                StacksMessageType::Handshake(handshake_data_1),
1✔
4255
            )
4256
            .unwrap();
1✔
4257

4258
        let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
4259

4260
        // convo_2 receives it and processes it, and rejects it
4261
        convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
4262
        let unhandled_2 = convo_2
1✔
4263
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
4264
            .unwrap();
1✔
4265

4266
        // convo_1 gets a handshake-reject and consumes it
4267
        convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
4268
        let unhandled_1 = convo_1
1✔
4269
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
4270
            .unwrap();
1✔
4271

4272
        // the waiting reply aborts on disconnect
4273
        let reply_1 = rh_1.recv(0).unwrap();
1✔
4274

4275
        // received a valid HandshakeReject from peer 2
4276
        assert!(matches!(
1✔
4277
            reply_1.payload,
1✔
4278
            StacksMessageType::HandshakeReject
4279
        ));
4280

4281
        assert!(unhandled_1.is_empty());
1✔
4282
        assert!(unhandled_2.is_empty());
1✔
4283
    }
1✔
4284

4285
    #[test]
4286
    fn convo_handshake_update_key() {
1✔
4287
        let conn_opts = ConnectionOptions::default();
1✔
4288
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
4289
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
4290

4291
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4292
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4293
        )
4294
        .unwrap();
1✔
4295

4296
        let mut chain_view = BurnchainView {
1✔
4297
            burn_block_height: 12348,
1✔
4298
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
4299
            burn_stable_block_height: 12341,
1✔
4300
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
4301
            last_burn_block_hashes: HashMap::new(),
1✔
4302
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
4303
        };
1✔
4304
        chain_view.make_test_data();
1✔
4305

4306
        let test_name_1 = "convo_handshake_update_key_1";
1✔
4307
        let test_name_2 = "convo_handshake_update_key_2";
1✔
4308

4309
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
4310
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
4311

4312
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
4313
            make_test_chain_dbs(
1✔
4314
                test_name_1,
1✔
4315
                &burnchain_1,
1✔
4316
                0x9abcdef0,
1✔
4317
                12350,
1✔
4318
                UrlString::from_literal("http://peer1.com"),
1✔
4319
                &[],
1✔
4320
                &[],
1✔
4321
                DEFAULT_SERVICES,
1✔
4322
            );
1✔
4323
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
4324
            make_test_chain_dbs(
1✔
4325
                test_name_2,
1✔
4326
                &burnchain_2,
1✔
4327
                0x9abcdef0,
1✔
4328
                12351,
1✔
4329
                UrlString::from_literal("http://peer2.com"),
1✔
4330
                &[],
1✔
4331
                &[],
1✔
4332
                DEFAULT_SERVICES,
1✔
4333
            );
1✔
4334

4335
        let mut net_1 = db_setup(
1✔
4336
            test_name_1,
1✔
4337
            &burnchain_1,
1✔
4338
            0x9abcdef0,
4339
            &mut peerdb_1,
1✔
4340
            &mut sortdb_1,
1✔
4341
            &socketaddr_1,
1✔
4342
            &chain_view,
1✔
4343
        );
4344
        let mut net_2 = db_setup(
1✔
4345
            test_name_2,
1✔
4346
            &burnchain_2,
1✔
4347
            0x9abcdef0,
4348
            &mut peerdb_2,
1✔
4349
            &mut sortdb_2,
1✔
4350
            &socketaddr_2,
1✔
4351
            &chain_view,
1✔
4352
        );
4353

4354
        let mut local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
4355
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
4356

4357
        let mut convo_1 = ConversationP2P::new(
1✔
4358
            123,
4359
            456,
4360
            &burnchain_1,
1✔
4361
            &socketaddr_2,
1✔
4362
            &conn_opts,
1✔
4363
            true,
4364
            0,
4365
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4366
        );
4367
        let mut convo_2 = ConversationP2P::new(
1✔
4368
            123,
4369
            456,
4370
            &burnchain_2,
1✔
4371
            &socketaddr_1,
1✔
4372
            &conn_opts,
1✔
4373
            true,
4374
            0,
4375
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4376
        );
4377

4378
        // no peer public keys known yet
4379
        assert!(convo_1.connection.get_public_key().is_none());
1✔
4380
        assert!(convo_2.connection.get_public_key().is_none());
1✔
4381

4382
        // convo_1 sends a handshake to convo_2
4383
        let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
4384
        let handshake_1 = convo_1
1✔
4385
            .sign_message(
1✔
4386
                &chain_view,
1✔
4387
                &local_peer_1.private_key,
1✔
4388
                StacksMessageType::Handshake(handshake_data_1),
1✔
4389
            )
4390
            .unwrap();
1✔
4391

4392
        let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
4393

4394
        // convo_2 receives it
4395
        convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
4396
        let unhandled_2 = convo_2
1✔
4397
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
4398
            .unwrap();
1✔
4399

4400
        // convo_1 has a handshakaccept
4401
        convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
4402
        let unhandled_1 = convo_1
1✔
4403
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
4404
            .unwrap();
1✔
4405

4406
        let reply_1 = rh_1.recv(0).unwrap();
1✔
4407

4408
        assert!(unhandled_1.is_empty());
1✔
4409
        assert_eq!(unhandled_2.len(), 1);
1✔
4410

4411
        // received a valid HandshakeAccept from peer 2
4412
        assert!(matches!(
1✔
4413
            reply_1.payload,
1✔
4414
            StacksMessageType::HandshakeAccept(..)
4415
        ));
4416

4417
        // peers learned each other's keys
4418
        assert_eq!(
1✔
4419
            convo_1.connection.get_public_key().as_ref().unwrap(),
1✔
4420
            &Secp256k1PublicKey::from_private(&local_peer_2.private_key)
1✔
4421
        );
4422
        assert_eq!(
1✔
4423
            convo_2.connection.get_public_key().as_ref().unwrap(),
1✔
4424
            &Secp256k1PublicKey::from_private(&local_peer_1.private_key)
1✔
4425
        );
4426

4427
        let old_peer_1_privkey = local_peer_1.private_key.clone();
1✔
4428
        let old_peer_1_pubkey = Secp256k1PublicKey::from_private(&old_peer_1_privkey);
1✔
4429

4430
        // peer 1 updates their private key
4431
        local_peer_1.private_key = Secp256k1PrivateKey::random();
1✔
4432

4433
        // peer 1 re-handshakes
4434
        // convo_1 sends a handshake to convo_2
4435
        let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
4436
        let handshake_1 = convo_1
1✔
4437
            .sign_message(
1✔
4438
                &chain_view,
1✔
4439
                &old_peer_1_privkey,
1✔
4440
                StacksMessageType::Handshake(handshake_data_1),
1✔
4441
            )
4442
            .unwrap();
1✔
4443

4444
        let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
4445

4446
        // convo_2 receives it
4447
        convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
4448
        let unhandled_2 = convo_2
1✔
4449
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
4450
            .unwrap();
1✔
4451

4452
        // convo_1 has a handshakaccept
4453
        convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
4454
        let unhandled_1 = convo_1
1✔
4455
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
4456
            .unwrap();
1✔
4457

4458
        let reply_1 = rh_1.recv(0).unwrap();
1✔
4459

4460
        assert!(unhandled_1.is_empty());
1✔
4461
        assert_eq!(unhandled_2.len(), 1);
1✔
4462

4463
        // new keys were learned
4464
        assert_eq!(
1✔
4465
            convo_1.connection.get_public_key().as_ref().unwrap(),
1✔
4466
            &Secp256k1PublicKey::from_private(&local_peer_2.private_key)
1✔
4467
        );
4468
        assert_eq!(
1✔
4469
            convo_2.connection.get_public_key().as_ref().unwrap(),
1✔
4470
            &Secp256k1PublicKey::from_private(&local_peer_1.private_key)
1✔
4471
        );
4472

4473
        assert!(convo_1.connection.get_public_key().as_ref().unwrap() != &old_peer_1_pubkey);
1✔
4474
        assert!(convo_2.connection.get_public_key().as_ref().unwrap() != &old_peer_1_pubkey);
1✔
4475
    }
1✔
4476

4477
    #[test]
4478
    fn convo_handshake_self() {
1✔
4479
        let conn_opts = ConnectionOptions::default();
1✔
4480
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
4481
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
4482

4483
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4484
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4485
        )
4486
        .unwrap();
1✔
4487

4488
        let mut chain_view = BurnchainView {
1✔
4489
            burn_block_height: 12348,
1✔
4490
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
4491
            burn_stable_block_height: 12341,
1✔
4492
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
4493
            last_burn_block_hashes: HashMap::new(),
1✔
4494
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
4495
        };
1✔
4496
        chain_view.make_test_data();
1✔
4497

4498
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4499
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4500
        )
4501
        .unwrap();
1✔
4502

4503
        let test_name_1 = "convo_handshake_self_1";
1✔
4504
        let test_name_2 = "convo_handshake_self_2";
1✔
4505

4506
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
4507
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
4508

4509
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
4510
            make_test_chain_dbs(
1✔
4511
                test_name_1,
1✔
4512
                &burnchain_1,
1✔
4513
                0x9abcdef0,
1✔
4514
                12350,
1✔
4515
                UrlString::from_literal("http://peer1.com"),
1✔
4516
                &[],
1✔
4517
                &[],
1✔
4518
                DEFAULT_SERVICES,
1✔
4519
            );
1✔
4520
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
4521
            make_test_chain_dbs(
1✔
4522
                test_name_2,
1✔
4523
                &burnchain_2,
1✔
4524
                0x9abcdef0,
1✔
4525
                12351,
1✔
4526
                UrlString::from_literal("http://peer2.com"),
1✔
4527
                &[],
1✔
4528
                &[],
1✔
4529
                DEFAULT_SERVICES,
1✔
4530
            );
1✔
4531

4532
        let mut net_1 = db_setup(
1✔
4533
            test_name_1,
1✔
4534
            &burnchain_1,
1✔
4535
            0x9abcdef0,
4536
            &mut peerdb_1,
1✔
4537
            &mut sortdb_1,
1✔
4538
            &socketaddr_1,
1✔
4539
            &chain_view,
1✔
4540
        );
4541
        let mut net_2 = db_setup(
1✔
4542
            test_name_2,
1✔
4543
            &burnchain_2,
1✔
4544
            0x9abcdef0,
4545
            &mut peerdb_2,
1✔
4546
            &mut sortdb_2,
1✔
4547
            &socketaddr_2,
1✔
4548
            &chain_view,
1✔
4549
        );
4550

4551
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
4552
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
4553

4554
        let mut convo_1 = ConversationP2P::new(
1✔
4555
            123,
4556
            456,
4557
            &burnchain_1,
1✔
4558
            &socketaddr_2,
1✔
4559
            &conn_opts,
1✔
4560
            true,
4561
            0,
4562
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4563
        );
4564
        let mut convo_2 = ConversationP2P::new(
1✔
4565
            123,
4566
            456,
4567
            &burnchain_2,
1✔
4568
            &socketaddr_1,
1✔
4569
            &conn_opts,
1✔
4570
            true,
4571
            0,
4572
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4573
        );
4574

4575
        // no peer public keys known yet
4576
        assert!(convo_1.connection.get_public_key().is_none());
1✔
4577
        assert!(convo_2.connection.get_public_key().is_none());
1✔
4578

4579
        // convo_1 sends a handshake to itself (not allowed)
4580
        let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
4581
        let handshake_1 = convo_1
1✔
4582
            .sign_message(
1✔
4583
                &chain_view,
1✔
4584
                &local_peer_1.private_key,
1✔
4585
                StacksMessageType::Handshake(handshake_data_1),
1✔
4586
            )
4587
            .unwrap();
1✔
4588
        let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
4589

4590
        // convo_2 receives it and processes it automatically (consuming it), and give back a handshake reject
4591
        convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
4592
        let unhandled_2 = convo_2
1✔
4593
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
4594
            .unwrap();
1✔
4595

4596
        // convo_1 gets a handshake reject and consumes it
4597
        convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
4598
        let unhandled_1 = convo_1
1✔
4599
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
4600
            .unwrap();
1✔
4601

4602
        // get back handshake reject
4603
        let reply_1 = rh_1.recv(0).unwrap();
1✔
4604

4605
        // received a valid HandshakeReject from peer 2
4606
        assert!(matches!(
1✔
4607
            reply_1.payload,
1✔
4608
            StacksMessageType::HandshakeReject
4609
        ));
4610

4611
        assert!(unhandled_1.is_empty());
1✔
4612
        assert!(unhandled_2.is_empty());
1✔
4613

4614
        // neither peer updated their info on one another
4615
        assert!(convo_1.connection.get_public_key().is_none());
1✔
4616
        assert!(convo_2.connection.get_public_key().is_none());
1✔
4617
    }
1✔
4618

4619
    #[test]
4620
    fn convo_ping() {
1✔
4621
        let conn_opts = ConnectionOptions::default();
1✔
4622
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
4623
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
4624

4625
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4626
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4627
        )
4628
        .unwrap();
1✔
4629

4630
        let mut chain_view = BurnchainView {
1✔
4631
            burn_block_height: 12348,
1✔
4632
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
4633
            burn_stable_block_height: 12341,
1✔
4634
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
4635
            last_burn_block_hashes: HashMap::new(),
1✔
4636
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
4637
        };
1✔
4638
        chain_view.make_test_data();
1✔
4639

4640
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4641
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4642
        )
4643
        .unwrap();
1✔
4644

4645
        let test_name_1 = "convo_ping_1";
1✔
4646
        let test_name_2 = "convo_ping_2";
1✔
4647

4648
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
4649
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
4650

4651
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
4652
            make_test_chain_dbs(
1✔
4653
                test_name_1,
1✔
4654
                &burnchain_1,
1✔
4655
                0x9abcdef0,
1✔
4656
                12350,
1✔
4657
                UrlString::from_literal("http://peer1.com"),
1✔
4658
                &[],
1✔
4659
                &[],
1✔
4660
                DEFAULT_SERVICES,
1✔
4661
            );
1✔
4662
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
4663
            make_test_chain_dbs(
1✔
4664
                test_name_2,
1✔
4665
                &burnchain_2,
1✔
4666
                0x9abcdef0,
1✔
4667
                12351,
1✔
4668
                UrlString::from_literal("http://peer2.com"),
1✔
4669
                &[],
1✔
4670
                &[],
1✔
4671
                DEFAULT_SERVICES,
1✔
4672
            );
1✔
4673

4674
        let mut net_1 = db_setup(
1✔
4675
            test_name_1,
1✔
4676
            &burnchain_1,
1✔
4677
            0x9abcdef0,
4678
            &mut peerdb_1,
1✔
4679
            &mut sortdb_1,
1✔
4680
            &socketaddr_1,
1✔
4681
            &chain_view,
1✔
4682
        );
4683
        let mut net_2 = db_setup(
1✔
4684
            test_name_2,
1✔
4685
            &burnchain_2,
1✔
4686
            0x9abcdef0,
4687
            &mut peerdb_2,
1✔
4688
            &mut sortdb_2,
1✔
4689
            &socketaddr_2,
1✔
4690
            &chain_view,
1✔
4691
        );
4692

4693
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
4694
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
4695

4696
        let mut convo_1 = ConversationP2P::new(
1✔
4697
            123,
4698
            456,
4699
            &burnchain_1,
1✔
4700
            &socketaddr_2,
1✔
4701
            &conn_opts,
1✔
4702
            true,
4703
            0,
4704
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4705
        );
4706
        let mut convo_2 = ConversationP2P::new(
1✔
4707
            123,
4708
            456,
4709
            &burnchain_2,
1✔
4710
            &socketaddr_1,
1✔
4711
            &conn_opts,
1✔
4712
            true,
4713
            0,
4714
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4715
        );
4716

4717
        // convo_1 sends a handshake to convo_2
4718
        let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
4719
        let handshake_1 = convo_1
1✔
4720
            .sign_message(
1✔
4721
                &chain_view,
1✔
4722
                &local_peer_1.private_key,
1✔
4723
                StacksMessageType::Handshake(handshake_data_1.clone()),
1✔
4724
            )
4725
            .unwrap();
1✔
4726
        let mut rh_handshake_1 = convo_1
1✔
4727
            .send_signed_request(handshake_1.clone(), 1000000)
1✔
4728
            .unwrap();
1✔
4729

4730
        // convo_1 sends a ping to convo_2
4731
        let ping_data_1 = PingData::new();
1✔
4732
        let ping_1 = convo_1
1✔
4733
            .sign_message(
1✔
4734
                &chain_view,
1✔
4735
                &local_peer_1.private_key,
1✔
4736
                StacksMessageType::Ping(ping_data_1.clone()),
1✔
4737
            )
4738
            .unwrap();
1✔
4739
        let mut rh_ping_1 = convo_1
1✔
4740
            .send_signed_request(ping_1.clone(), 1000000)
1✔
4741
            .unwrap();
1✔
4742

4743
        // convo_2 receives the handshake and ping and processes both, and since no one is waiting for the handshake, will forward
4744
        // it along to the chat caller (us)
4745
        test_debug!("send handshake {handshake_1:?}");
1✔
4746
        test_debug!("send ping {ping_1:?}");
1✔
4747
        convo_send_recv(
1✔
4748
            &mut convo_1,
1✔
4749
            vec![&mut rh_handshake_1, &mut rh_ping_1],
1✔
4750
            &mut convo_2,
1✔
4751
        );
4752
        let unhandled_2 = convo_2
1✔
4753
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
4754
            .unwrap();
1✔
4755

4756
        // convo_1 has a handshakeaccept
4757
        test_debug!("reply handshake-accept");
1✔
4758
        test_debug!("send pong");
1✔
4759
        convo_send_recv(
1✔
4760
            &mut convo_2,
1✔
4761
            vec![&mut rh_handshake_1, &mut rh_ping_1],
1✔
4762
            &mut convo_1,
1✔
4763
        );
4764
        let unhandled_1 = convo_1
1✔
4765
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
4766
            .unwrap();
1✔
4767

4768
        let reply_handshake_1 = rh_handshake_1.recv(0).unwrap();
1✔
4769
        let reply_ping_1 = rh_ping_1.recv(0).unwrap();
1✔
4770

4771
        assert!(unhandled_1.is_empty());
1✔
4772
        assert_eq!(unhandled_2.len(), 1); // only the handshake is given back.  the ping is consumed
1✔
4773

4774
        // convo 2 returns the handshake from convo 1
4775
        if let StacksMessageType::Handshake(ref data) = unhandled_2[0].payload {
1✔
4776
            assert_eq!(handshake_data_1, *data);
1✔
4777
        } else {
4778
            panic!("Unexpected payload message type");
×
4779
        }
4780

4781
        // convo 2 replied to convo 1 with a matching pong
4782
        if let StacksMessageType::Pong(ref data) = reply_ping_1.payload {
1✔
4783
            assert_eq!(data.nonce, ping_data_1.nonce);
1✔
4784
        } else {
4785
            panic!("Unexpected payload message type");
×
4786
        }
4787
    }
1✔
4788

4789
    #[test]
4790
    fn convo_handshake_ping_loop() {
1✔
4791
        let conn_opts = ConnectionOptions::default();
1✔
4792
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
4793
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
4794

4795
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4796
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4797
        )
4798
        .unwrap();
1✔
4799

4800
        let mut chain_view = BurnchainView {
1✔
4801
            burn_block_height: 12348,
1✔
4802
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
4803
            burn_stable_block_height: 12341,
1✔
4804
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
4805
            last_burn_block_hashes: HashMap::new(),
1✔
4806
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
4807
        };
1✔
4808
        chain_view.make_test_data();
1✔
4809

4810
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
4811
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
4812
        )
4813
        .unwrap();
1✔
4814

4815
        let test_name_1 = "convo_handshake_ping_loop_1";
1✔
4816
        let test_name_2 = "convo_handshake_ping_loop_2";
1✔
4817

4818
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
4819
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
4820

4821
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
4822
            make_test_chain_dbs(
1✔
4823
                test_name_1,
1✔
4824
                &burnchain_1,
1✔
4825
                0x9abcdef0,
1✔
4826
                12350,
1✔
4827
                UrlString::from_literal("http://peer1.com"),
1✔
4828
                &[],
1✔
4829
                &[],
1✔
4830
                DEFAULT_SERVICES,
1✔
4831
            );
1✔
4832
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
4833
            make_test_chain_dbs(
1✔
4834
                test_name_2,
1✔
4835
                &burnchain_2,
1✔
4836
                0x9abcdef0,
1✔
4837
                12351,
1✔
4838
                UrlString::from_literal("http://peer2.com"),
1✔
4839
                &[],
1✔
4840
                &[],
1✔
4841
                DEFAULT_SERVICES,
1✔
4842
            );
1✔
4843

4844
        let mut net_1 = db_setup(
1✔
4845
            test_name_1,
1✔
4846
            &burnchain_1,
1✔
4847
            0x9abcdef0,
4848
            &mut peerdb_1,
1✔
4849
            &mut sortdb_1,
1✔
4850
            &socketaddr_1,
1✔
4851
            &chain_view,
1✔
4852
        );
4853
        let mut net_2 = db_setup(
1✔
4854
            test_name_2,
1✔
4855
            &burnchain_2,
1✔
4856
            0x9abcdef0,
4857
            &mut peerdb_2,
1✔
4858
            &mut sortdb_2,
1✔
4859
            &socketaddr_2,
1✔
4860
            &chain_view,
1✔
4861
        );
4862

4863
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
4864
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
4865

4866
        let mut convo_1 = ConversationP2P::new(
1✔
4867
            123,
4868
            456,
4869
            &burnchain_1,
1✔
4870
            &socketaddr_2,
1✔
4871
            &conn_opts,
1✔
4872
            true,
4873
            0,
4874
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4875
        );
4876
        let mut convo_2 = ConversationP2P::new(
1✔
4877
            123,
4878
            456,
4879
            &burnchain_2,
1✔
4880
            &socketaddr_1,
1✔
4881
            &conn_opts,
1✔
4882
            true,
4883
            1,
4884
            StacksEpoch::unit_test_pre_2_05(0),
1✔
4885
        );
4886

4887
        for i in 0..5 {
5✔
4888
            // do handshake/ping over and over, with different keys.
4889
            // tests re-keying.
4890

4891
            // convo_1 sends a handshake to convo_2
4892
            let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
5✔
4893
            let handshake_1 = convo_1
5✔
4894
                .sign_message(
5✔
4895
                    &chain_view,
5✔
4896
                    &local_peer_1.private_key,
5✔
4897
                    StacksMessageType::Handshake(handshake_data_1.clone()),
5✔
4898
                )
4899
                .unwrap();
5✔
4900
            let mut rh_handshake_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
5✔
4901

4902
            // convo_1 sends a ping to convo_2
4903
            let ping_data_1 = PingData::new();
5✔
4904
            let ping_1 = convo_1
5✔
4905
                .sign_message(
5✔
4906
                    &chain_view,
5✔
4907
                    &local_peer_1.private_key,
5✔
4908
                    StacksMessageType::Ping(ping_data_1.clone()),
5✔
4909
                )
4910
                .unwrap();
5✔
4911
            let mut rh_ping_1 = convo_1.send_signed_request(ping_1, 1000000).unwrap();
5✔
4912

4913
            // convo_2 receives the handshake and ping and processes both, and since no one is waiting for the handshake, will forward
4914
            // it along to the chat caller (us)
4915
            convo_send_recv(
5✔
4916
                &mut convo_1,
5✔
4917
                vec![&mut rh_handshake_1, &mut rh_ping_1],
5✔
4918
                &mut convo_2,
5✔
4919
            );
4920
            let unhandled_2 = convo_2
5✔
4921
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
5✔
4922
                .unwrap();
5✔
4923

4924
            // convo_1 has a handshakeaccept
4925
            convo_send_recv(
5✔
4926
                &mut convo_2,
5✔
4927
                vec![&mut rh_handshake_1, &mut rh_ping_1],
5✔
4928
                &mut convo_1,
5✔
4929
            );
4930
            let unhandled_1 = convo_1
5✔
4931
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
5✔
4932
                .unwrap();
5✔
4933

4934
            let reply_handshake_1 = rh_handshake_1.recv(0).unwrap();
5✔
4935
            let reply_ping_1 = rh_ping_1.recv(0).unwrap();
5✔
4936

4937
            assert!(unhandled_1.is_empty());
5✔
4938
            assert_eq!(unhandled_2.len(), 1); // only the handshake is given back.  the ping is consumed
5✔
4939

4940
            // convo 2 returns the handshake from convo 1
4941
            if let StacksMessageType::Handshake(ref data) = unhandled_2[0].payload {
5✔
4942
                assert_eq!(handshake_data_1, *data);
5✔
4943
            } else {
4944
                panic!("Unexpected payload message type");
×
4945
            }
4946

4947
            // convo 2 replied to convo 1 with a matching pong
4948
            if let StacksMessageType::Pong(ref data) = reply_ping_1.payload {
5✔
4949
                assert_eq!(data.nonce, ping_data_1.nonce);
5✔
4950
            } else {
4951
                panic!("Unexpected payload message type");
×
4952
            }
4953

4954
            // received a valid HandshakeAccept from peer 2
4955
            match reply_handshake_1.payload {
5✔
4956
                StacksMessageType::HandshakeAccept(ref data)
5✔
4957
                | StacksMessageType::StackerDBHandshakeAccept(ref data, ..) => {
×
4958
                    assert_eq!(data.handshake.addrbytes, local_peer_2.addrbytes);
5✔
4959
                    assert_eq!(data.handshake.port, local_peer_2.port);
5✔
4960
                    assert_eq!(data.handshake.services, local_peer_2.services);
5✔
4961
                    assert_eq!(
5✔
4962
                        data.handshake.node_public_key,
4963
                        StacksPublicKeyBuffer::from_public_key(&Secp256k1PublicKey::from_private(
5✔
4964
                            &local_peer_2.private_key
5✔
4965
                        ))
5✔
4966
                    );
4967
                    assert_eq!(
5✔
4968
                        data.handshake.expire_block_height,
4969
                        local_peer_2.private_key_expire
4970
                    );
4971
                    assert_eq!(data.heartbeat_interval, conn_opts.heartbeat);
5✔
4972
                }
4973
                _ => {
4974
                    panic!("Unexpected payload message type");
×
4975
                }
4976
            };
4977

4978
            // convo_2 got updated with convo_1's peer info, and default heartbeat filled in
4979
            assert_eq!(convo_2.peer_heartbeat, 3600);
5✔
4980
            assert_eq!(
5✔
4981
                convo_2
5✔
4982
                    .connection
5✔
4983
                    .get_public_key()
5✔
4984
                    .unwrap()
5✔
4985
                    .to_bytes_compressed(),
5✔
4986
                Secp256k1PublicKey::from_private(&local_peer_1.private_key).to_bytes_compressed()
5✔
4987
            );
4988

4989
            // convo_1 got updated with convo_2's peer info, as well as heartbeat
4990
            assert_eq!(convo_1.peer_heartbeat, conn_opts.heartbeat);
5✔
4991
            assert_eq!(
5✔
4992
                convo_1
5✔
4993
                    .connection
5✔
4994
                    .get_public_key()
5✔
4995
                    .unwrap()
5✔
4996
                    .to_bytes_compressed(),
5✔
4997
                Secp256k1PublicKey::from_private(&local_peer_2.private_key).to_bytes_compressed()
5✔
4998
            );
4999

5000
            // regenerate keys and expiries in peer 1
5001
            let new_privkey = Secp256k1PrivateKey::random();
5✔
5002
            {
5✔
5003
                let tx = peerdb_1.tx_begin().unwrap();
5✔
5004
                PeerDB::set_local_private_key(&tx, &new_privkey, (12350 + i) as u64).unwrap();
5✔
5005
                tx.commit().unwrap();
5✔
5006
            }
5✔
5007
        }
5008
    }
1✔
5009

5010
    #[test]
5011
    fn convo_nack_unsolicited() {
1✔
5012
        let conn_opts = ConnectionOptions::default();
1✔
5013
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
5014
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
5015

5016
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5017
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5018
        )
5019
        .unwrap();
1✔
5020

5021
        let mut chain_view = BurnchainView {
1✔
5022
            burn_block_height: 12348,
1✔
5023
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
5024
            burn_stable_block_height: 12341,
1✔
5025
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
5026
            last_burn_block_hashes: HashMap::new(),
1✔
5027
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
5028
        };
1✔
5029
        chain_view.make_test_data();
1✔
5030

5031
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5032
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5033
        )
5034
        .unwrap();
1✔
5035

5036
        let test_name_1 = "convo_nack_unsolicited_1";
1✔
5037
        let test_name_2 = "convo_nack_unsolicited_2";
1✔
5038

5039
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
5040
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
5041

5042
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
5043
            make_test_chain_dbs(
1✔
5044
                test_name_1,
1✔
5045
                &burnchain_1,
1✔
5046
                0x9abcdef0,
1✔
5047
                12350,
1✔
5048
                UrlString::from_literal("http://peer1.com"),
1✔
5049
                &[],
1✔
5050
                &[],
1✔
5051
                DEFAULT_SERVICES,
1✔
5052
            );
1✔
5053
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
5054
            make_test_chain_dbs(
1✔
5055
                test_name_2,
1✔
5056
                &burnchain_2,
1✔
5057
                0x9abcdef0,
1✔
5058
                12351,
1✔
5059
                UrlString::from_literal("http://peer2.com"),
1✔
5060
                &[],
1✔
5061
                &[],
1✔
5062
                DEFAULT_SERVICES,
1✔
5063
            );
1✔
5064

5065
        let mut net_1 = db_setup(
1✔
5066
            test_name_1,
1✔
5067
            &burnchain_1,
1✔
5068
            0x9abcdef0,
5069
            &mut peerdb_1,
1✔
5070
            &mut sortdb_1,
1✔
5071
            &socketaddr_1,
1✔
5072
            &chain_view,
1✔
5073
        );
5074
        let mut net_2 = db_setup(
1✔
5075
            test_name_2,
1✔
5076
            &burnchain_2,
1✔
5077
            0x9abcdef0,
5078
            &mut peerdb_2,
1✔
5079
            &mut sortdb_2,
1✔
5080
            &socketaddr_2,
1✔
5081
            &chain_view,
1✔
5082
        );
5083

5084
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
5085
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
5086

5087
        let mut convo_1 = ConversationP2P::new(
1✔
5088
            123,
5089
            456,
5090
            &burnchain_1,
1✔
5091
            &socketaddr_2,
1✔
5092
            &conn_opts,
1✔
5093
            true,
5094
            0,
5095
            StacksEpoch::unit_test_pre_2_05(0),
1✔
5096
        );
5097
        let mut convo_2 = ConversationP2P::new(
1✔
5098
            123,
5099
            456,
5100
            &burnchain_2,
1✔
5101
            &socketaddr_1,
1✔
5102
            &conn_opts,
1✔
5103
            true,
5104
            0,
5105
            StacksEpoch::unit_test_pre_2_05(0),
1✔
5106
        );
5107

5108
        // no peer public keys known yet
5109
        assert!(convo_1.connection.get_public_key().is_none());
1✔
5110
        assert!(convo_2.connection.get_public_key().is_none());
1✔
5111

5112
        // convo_1 sends a ping to convo_2
5113
        let ping_data_1 = PingData::new();
1✔
5114
        let ping_1 = convo_1
1✔
5115
            .sign_message(
1✔
5116
                &chain_view,
1✔
5117
                &local_peer_1.private_key,
1✔
5118
                StacksMessageType::Ping(ping_data_1),
1✔
5119
            )
5120
            .unwrap();
1✔
5121
        let mut rh_ping_1 = convo_1.send_signed_request(ping_1, 1000000).unwrap();
1✔
5122

5123
        // convo_2 will reply with a nack since peer_1 hasn't authenticated yet
5124
        convo_send_recv(&mut convo_1, vec![&mut rh_ping_1], &mut convo_2);
1✔
5125
        let unhandled_2 = convo_2
1✔
5126
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5127
            .unwrap();
1✔
5128

5129
        // convo_1 has a nack
5130
        convo_send_recv(&mut convo_2, vec![&mut rh_ping_1], &mut convo_1);
1✔
5131
        let unhandled_1 = convo_1
1✔
5132
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5133
            .unwrap();
1✔
5134

5135
        let reply_1 = rh_ping_1.recv(0).unwrap();
1✔
5136

5137
        // convo_2 gives back nothing
5138
        assert!(unhandled_1.is_empty());
1✔
5139
        assert!(unhandled_2.is_empty());
1✔
5140

5141
        // convo_1 got a NACK
5142
        if let StacksMessageType::Nack(ref data) = reply_1.payload {
1✔
5143
            assert_eq!(data.error_code, NackErrorCodes::HandshakeRequired);
1✔
5144
        } else {
5145
            panic!("Unexpected payload message type");
×
5146
        }
5147

5148
        // convo_2 did NOT get updated with convo_1's peer info
5149
        assert_eq!(convo_2.peer_heartbeat, 0);
1✔
5150
        assert!(convo_2.connection.get_public_key().is_none());
1✔
5151

5152
        // convo_1 did NOT get updated
5153
        assert_eq!(convo_1.peer_heartbeat, 0);
1✔
5154
        assert!(convo_2.connection.get_public_key().is_none());
1✔
5155
    }
1✔
5156

5157
    #[test]
5158
    fn convo_ignore_unsolicited_handshake() {
1✔
5159
        let conn_opts = ConnectionOptions::default();
1✔
5160
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
5161
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
5162

5163
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5164
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5165
        )
5166
        .unwrap();
1✔
5167

5168
        let mut chain_view = BurnchainView {
1✔
5169
            burn_block_height: 12348,
1✔
5170
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
5171
            burn_stable_block_height: 12341,
1✔
5172
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
5173
            last_burn_block_hashes: HashMap::new(),
1✔
5174
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
5175
        };
1✔
5176
        chain_view.make_test_data();
1✔
5177

5178
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5179
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5180
        )
5181
        .unwrap();
1✔
5182

5183
        let test_name_1 = "convo_ignore_unsolicited_handshake_1";
1✔
5184
        let test_name_2 = "convo_ignore_unsolicited_handshake_2";
1✔
5185

5186
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
5187
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
5188

5189
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
5190
            make_test_chain_dbs(
1✔
5191
                test_name_1,
1✔
5192
                &burnchain_1,
1✔
5193
                0x9abcdef0,
1✔
5194
                12350,
1✔
5195
                UrlString::from_literal("http://peer1.com"),
1✔
5196
                &[],
1✔
5197
                &[],
1✔
5198
                DEFAULT_SERVICES,
1✔
5199
            );
1✔
5200
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
5201
            make_test_chain_dbs(
1✔
5202
                test_name_2,
1✔
5203
                &burnchain_2,
1✔
5204
                0x9abcdef0,
1✔
5205
                12351,
1✔
5206
                UrlString::from_literal("http://peer2.com"),
1✔
5207
                &[],
1✔
5208
                &[],
1✔
5209
                DEFAULT_SERVICES,
1✔
5210
            );
1✔
5211

5212
        let mut net_1 = db_setup(
1✔
5213
            test_name_1,
1✔
5214
            &burnchain_1,
1✔
5215
            0x9abcdef0,
5216
            &mut peerdb_1,
1✔
5217
            &mut sortdb_1,
1✔
5218
            &socketaddr_1,
1✔
5219
            &chain_view,
1✔
5220
        );
5221
        let mut net_2 = db_setup(
1✔
5222
            test_name_2,
1✔
5223
            &burnchain_2,
1✔
5224
            0x9abcdef0,
5225
            &mut peerdb_2,
1✔
5226
            &mut sortdb_2,
1✔
5227
            &socketaddr_2,
1✔
5228
            &chain_view,
1✔
5229
        );
5230

5231
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
5232
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
5233

5234
        let mut convo_1 = ConversationP2P::new(
1✔
5235
            123,
5236
            456,
5237
            &burnchain_1,
1✔
5238
            &socketaddr_2,
1✔
5239
            &conn_opts,
1✔
5240
            true,
5241
            0,
5242
            StacksEpoch::unit_test_pre_2_05(0),
1✔
5243
        );
5244
        let mut convo_2 = ConversationP2P::new(
1✔
5245
            123,
5246
            456,
5247
            &burnchain_2,
1✔
5248
            &socketaddr_1,
1✔
5249
            &conn_opts,
1✔
5250
            true,
5251
            0,
5252
            StacksEpoch::unit_test_pre_2_05(0),
1✔
5253
        );
5254

5255
        // no peer public keys known yet
5256
        assert!(convo_1.connection.get_public_key().is_none());
1✔
5257
        assert!(convo_2.connection.get_public_key().is_none());
1✔
5258

5259
        // convo_1 sends unauthenticated control-plane messages to convo_2
5260
        let unauthed_messages = {
1✔
5261
            let accept_data_1 = HandshakeAcceptData::new(&local_peer_1, 1000000000);
1✔
5262
            let accept_1 = convo_1
1✔
5263
                .sign_message(
1✔
5264
                    &chain_view,
1✔
5265
                    &local_peer_1.private_key,
1✔
5266
                    StacksMessageType::HandshakeAccept(accept_data_1.clone()),
1✔
5267
                )
5268
                .unwrap();
1✔
5269

5270
            let stackerdb_accept_data_1 = StacksMessageType::StackerDBHandshakeAccept(
1✔
5271
                accept_data_1,
1✔
5272
                StackerDBHandshakeData {
1✔
5273
                    rc_consensus_hash: chain_view.rc_consensus_hash.clone(),
1✔
5274
                    // placeholder sbtc address for now
1✔
5275
                    smart_contracts: vec![QualifiedContractIdentifier::parse(
1✔
5276
                        "SP000000000000000000002Q6VF78.sbtc",
1✔
5277
                    )
1✔
5278
                    .unwrap()],
1✔
5279
                },
1✔
5280
            );
1✔
5281

5282
            let stackerdb_accept_1 = convo_1
1✔
5283
                .sign_message(
1✔
5284
                    &chain_view,
1✔
5285
                    &local_peer_1.private_key,
1✔
5286
                    stackerdb_accept_data_1,
1✔
5287
                )
5288
                .unwrap();
1✔
5289

5290
            vec![accept_1, stackerdb_accept_1]
1✔
5291
        };
5292

5293
        for unauthed_msg in unauthed_messages.into_iter() {
2✔
5294
            let mut rh_1 = convo_1.send_signed_request(unauthed_msg, 1000000).unwrap();
2✔
5295

5296
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
2✔
5297
            let unhandled_2 = convo_2
2✔
5298
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
2✔
5299
                .unwrap();
2✔
5300

5301
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
2✔
5302
            let unhandled_1 = convo_1
2✔
5303
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
2✔
5304
                .unwrap();
2✔
5305

5306
            // connection should break off since nodes ignore unsolicited messages
5307
            match rh_1.recv(1).unwrap_err() {
2✔
5308
                net_error::ConnectionBroken => {}
2✔
5309
                e => {
×
5310
                    panic!(
×
5311
                        "Unexpected error from consuming unsolicited message: {:?}",
5312
                        &e
×
5313
                    );
5314
                }
5315
            }
5316

5317
            // convo_2 gives back nothing
5318
            assert!(unhandled_1.is_empty());
2✔
5319
            assert!(unhandled_2.is_empty());
2✔
5320

5321
            // convo_2 did NOT get updated with convo_1's peer info
5322
            assert_eq!(convo_2.peer_heartbeat, 0);
2✔
5323
            assert!(convo_2.connection.get_public_key().is_none());
2✔
5324

5325
            // convo_1 did NOT get updated
5326
            assert_eq!(convo_1.peer_heartbeat, 0);
2✔
5327
            assert!(convo_2.connection.get_public_key().is_none());
2✔
5328
        }
5329
    }
1✔
5330

5331
    #[test]
5332
    fn convo_handshake_getblocksinv() {
1✔
5333
        with_timeout(100, || {
1✔
5334
            let conn_opts = ConnectionOptions::default();
1✔
5335

5336
            let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
5337
            let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
5338

5339
            let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5340
                "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5341
            )
5342
            .unwrap();
1✔
5343

5344
            let mut chain_view = BurnchainView {
1✔
5345
                burn_block_height: 12331,
1✔
5346
                burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
5347
                burn_stable_block_height: 12331 - 7,
1✔
5348
                burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
5349
                last_burn_block_hashes: HashMap::new(),
1✔
5350
                rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
5351
            };
1✔
5352
            chain_view.make_test_data();
1✔
5353

5354
            let test_name_1 = "convo_handshake_getblocksinv_1";
1✔
5355
            let test_name_2 = "convo_handshake_getblocksinv_2";
1✔
5356

5357
            let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
5358
            let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
5359

5360
            let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
5361
                make_test_chain_dbs(
1✔
5362
                    test_name_1,
1✔
5363
                    &burnchain_1,
1✔
5364
                    0x9abcdef0,
1✔
5365
                    12350,
1✔
5366
                    UrlString::from_literal("http://peer1.com"),
1✔
5367
                    &[],
1✔
5368
                    &[],
1✔
5369
                    DEFAULT_SERVICES,
1✔
5370
                );
1✔
5371
            let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
5372
                make_test_chain_dbs(
1✔
5373
                    test_name_2,
1✔
5374
                    &burnchain_2,
1✔
5375
                    0x9abcdef0,
1✔
5376
                    12351,
1✔
5377
                    UrlString::from_literal("http://peer2.com"),
1✔
5378
                    &[],
1✔
5379
                    &[],
1✔
5380
                    DEFAULT_SERVICES,
1✔
5381
                );
1✔
5382

5383
            let mut net_1 = db_setup(
1✔
5384
                test_name_1,
1✔
5385
                &burnchain_1,
1✔
5386
                0x9abcdef0,
5387
                &mut peerdb_1,
1✔
5388
                &mut sortdb_1,
1✔
5389
                &socketaddr_1,
1✔
5390
                &chain_view,
1✔
5391
            );
5392
            let mut net_2 = db_setup(
1✔
5393
                test_name_2,
1✔
5394
                &burnchain_2,
1✔
5395
                0x9abcdef0,
5396
                &mut peerdb_2,
1✔
5397
                &mut sortdb_2,
1✔
5398
                &socketaddr_2,
1✔
5399
                &chain_view,
1✔
5400
            );
5401

5402
            let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
5403
            let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
5404

5405
            let mut convo_1 = ConversationP2P::new(
1✔
5406
                123,
5407
                456,
5408
                &burnchain_1,
1✔
5409
                &socketaddr_2,
1✔
5410
                &conn_opts,
1✔
5411
                true,
5412
                0,
5413
                StacksEpoch::unit_test_pre_2_05(0),
1✔
5414
            );
5415
            let mut convo_2 = ConversationP2P::new(
1✔
5416
                123,
5417
                456,
5418
                &burnchain_2,
1✔
5419
                &socketaddr_1,
1✔
5420
                &conn_opts,
1✔
5421
                true,
5422
                0,
5423
                StacksEpoch::unit_test_pre_2_05(0),
1✔
5424
            );
5425

5426
            // no peer public keys known yet
5427
            assert!(convo_1.connection.get_public_key().is_none());
1✔
5428
            assert!(convo_2.connection.get_public_key().is_none());
1✔
5429

5430
            // convo_1 sends a handshake to convo_2
5431
            let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
5432
            let handshake_1 = convo_1
1✔
5433
                .sign_message(
1✔
5434
                    &chain_view,
1✔
5435
                    &local_peer_1.private_key,
1✔
5436
                    StacksMessageType::Handshake(handshake_data_1.clone()),
1✔
5437
                )
5438
                .unwrap();
1✔
5439
            let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
5440

5441
            // convo_2 receives it and processes it, and since no one is waiting for it, will forward
5442
            // it along to the chat caller (us)
5443
            test_debug!("send handshake");
1✔
5444
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
5445
            let unhandled_2 = convo_2
1✔
5446
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5447
                .unwrap();
1✔
5448

5449
            // convo_1 has a handshakeaccept
5450
            test_debug!("send handshake-accept");
1✔
5451
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
5452
            let unhandled_1 = convo_1
1✔
5453
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5454
                .unwrap();
1✔
5455

5456
            let reply_1 = rh_1.recv(0).unwrap();
1✔
5457

5458
            assert!(unhandled_1.is_empty());
1✔
5459
            assert_eq!(unhandled_2.len(), 1);
1✔
5460

5461
            // convo 2 returns the handshake from convo 1
5462
            if let StacksMessageType::Handshake(ref data) = unhandled_2[0].payload {
1✔
5463
                assert_eq!(handshake_data_1, *data);
1✔
5464
            } else {
5465
                panic!("Unexpected payload message type");
×
5466
            }
5467

5468
            // received a valid HandshakeAccept from peer 2
5469
            match reply_1.payload {
1✔
5470
                StacksMessageType::HandshakeAccept(ref data)
1✔
5471
                | StacksMessageType::StackerDBHandshakeAccept(ref data, ..) => {
×
5472
                    assert_eq!(data.handshake.addrbytes, local_peer_2.addrbytes);
1✔
5473
                    assert_eq!(data.handshake.port, local_peer_2.port);
1✔
5474
                    assert_eq!(data.handshake.services, local_peer_2.services);
1✔
5475
                    assert_eq!(
1✔
5476
                        data.handshake.node_public_key,
5477
                        StacksPublicKeyBuffer::from_public_key(&Secp256k1PublicKey::from_private(
1✔
5478
                            &local_peer_2.private_key
1✔
5479
                        ))
1✔
5480
                    );
5481
                    assert_eq!(
1✔
5482
                        data.handshake.expire_block_height,
5483
                        local_peer_2.private_key_expire
5484
                    );
5485
                    assert_eq!(
1✔
5486
                        data.handshake.data_url,
5487
                        UrlString::from_literal("http://peer2.com")
1✔
5488
                    );
5489
                    assert_eq!(data.heartbeat_interval, conn_opts.heartbeat);
1✔
5490
                }
5491
                _ => {
5492
                    panic!("Unexpected payload message type");
×
5493
                }
5494
            };
5495

5496
            // convo_1 sends a getblocksinv to convo_2 for all the blocks in the last reward cycle
5497
            let convo_1_chaintip =
1✔
5498
                SortitionDB::get_canonical_burn_chain_tip(sortdb_1.conn()).unwrap();
1✔
5499
            let convo_1_ancestor = {
1✔
5500
                let ic = sortdb_1.index_conn();
1✔
5501
                SortitionDB::get_ancestor_snapshot(
1✔
5502
                    &ic,
1✔
5503
                    convo_1_chaintip.block_height - 10 - 1,
1✔
5504
                    &convo_1_chaintip.sortition_id,
1✔
5505
                )
5506
                .unwrap()
1✔
5507
                .unwrap()
1✔
5508
            };
5509

5510
            let getblocksdata_1 = GetBlocksInv {
1✔
5511
                consensus_hash: convo_1_ancestor.consensus_hash,
1✔
5512
                num_blocks: 10,
1✔
5513
            };
1✔
5514
            let getblocksdata_1_msg = convo_1
1✔
5515
                .sign_message(
1✔
5516
                    &chain_view,
1✔
5517
                    &local_peer_1.private_key,
1✔
5518
                    StacksMessageType::GetBlocksInv(getblocksdata_1),
1✔
5519
                )
5520
                .unwrap();
1✔
5521
            let mut rh_1 = convo_1
1✔
5522
                .send_signed_request(getblocksdata_1_msg, 10000000)
1✔
5523
                .unwrap();
1✔
5524

5525
            // convo_2 receives it, and handles it
5526
            test_debug!("send getblocksinv");
1✔
5527
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
5528
            let unhandled_2 = convo_2
1✔
5529
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5530
                .unwrap();
1✔
5531

5532
            // convo_1 gets back a blocksinv message
5533
            test_debug!("send blocksinv");
1✔
5534
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
5535
            let unhandled_1 = convo_1
1✔
5536
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5537
                .unwrap();
1✔
5538

5539
            let reply_1 = rh_1.recv(0).unwrap();
1✔
5540

5541
            // no unhandled messages forwarded
5542
            assert!(unhandled_1.is_empty());
1✔
5543
            assert!(unhandled_2.is_empty());
1✔
5544

5545
            // convo 2 returned a block-inv for all blocks
5546
            match reply_1.payload {
1✔
5547
                StacksMessageType::BlocksInv(ref data) => {
1✔
5548
                    assert_eq!(data.bitlen, 10);
1✔
5549
                    test_debug!("data: {:?}", data);
1✔
5550

5551
                    // all burn blocks had sortitions, but we have no Stacks blocks :(
5552
                    for i in 0..data.bitlen {
10✔
5553
                        assert!(!data.has_ith_block(i));
10✔
5554
                    }
5555
                }
5556
                x => {
×
5557
                    panic!("received invalid payload: {x:?}");
×
5558
                }
5559
            }
5560

5561
            // request for a non-existent consensus hash
5562
            let getblocksdata_diverged_1 = GetBlocksInv {
1✔
5563
                consensus_hash: ConsensusHash([0xff; 20]),
1✔
5564
                num_blocks: GETPOXINV_MAX_BITLEN as u16,
1✔
5565
            };
1✔
5566
            let getblocksdata_diverged_1_msg = convo_1
1✔
5567
                .sign_message(
1✔
5568
                    &chain_view,
1✔
5569
                    &local_peer_1.private_key,
1✔
5570
                    StacksMessageType::GetBlocksInv(getblocksdata_diverged_1),
1✔
5571
                )
5572
                .unwrap();
1✔
5573
            let mut rh_1 = convo_1
1✔
5574
                .send_signed_request(getblocksdata_diverged_1_msg, 10000000)
1✔
5575
                .unwrap();
1✔
5576

5577
            // convo_2 receives it, and handles it
5578
            test_debug!("send getblocksinv (diverged)");
1✔
5579
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
5580
            let unhandled_2 = convo_2
1✔
5581
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5582
                .unwrap();
1✔
5583

5584
            // convo_1 gets back a nack message
5585
            test_debug!("send nack (diverged)");
1✔
5586
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
5587
            let unhandled_1 = convo_1
1✔
5588
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5589
                .unwrap();
1✔
5590

5591
            let reply_1 = rh_1.recv(0).unwrap();
1✔
5592

5593
            // no unhandled messages forwarded
5594
            assert!(unhandled_1.is_empty());
1✔
5595
            assert!(unhandled_2.is_empty());
1✔
5596

5597
            // convo 2 returned a nack with the appropriate error message
5598
            if let StacksMessageType::Nack(ref data) = reply_1.payload {
1✔
5599
                assert_eq!(data.error_code, NackErrorCodes::NoSuchBurnchainBlock);
1✔
5600
            } else {
5601
                panic!("Unexpected payload message type");
×
5602
            }
5603
        })
1✔
5604
    }
1✔
5605

5606
    #[test]
5607
    fn convo_handshake_getnakamotoinv() {
1✔
5608
        with_timeout(100, || {
1✔
5609
            let conn_opts = ConnectionOptions::default();
1✔
5610

5611
            let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
5612
            let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
5613

5614
            let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5615
                "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5616
            )
5617
            .unwrap();
1✔
5618

5619
            let mut chain_view = BurnchainView {
1✔
5620
                burn_block_height: 12331,
1✔
5621
                burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
5622
                burn_stable_block_height: 12331 - 7,
1✔
5623
                burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
5624
                last_burn_block_hashes: HashMap::new(),
1✔
5625
                rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
5626
            };
1✔
5627
            chain_view.make_test_data();
1✔
5628

5629
            let test_name_1 = "convo_handshake_getnakamotoinv_1";
1✔
5630
            let test_name_2 = "convo_handshake_getnakamotoinv_2";
1✔
5631

5632
            let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
5633
            let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
5634

5635
            let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
5636
                make_test_chain_dbs(
1✔
5637
                    test_name_1,
1✔
5638
                    &burnchain_1,
1✔
5639
                    0x9abcdef0,
1✔
5640
                    12350,
1✔
5641
                    UrlString::from_literal("http://peer1.com"),
1✔
5642
                    &[],
1✔
5643
                    &[],
1✔
5644
                    DEFAULT_SERVICES,
1✔
5645
                );
1✔
5646
            let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
5647
                make_test_chain_dbs(
1✔
5648
                    test_name_2,
1✔
5649
                    &burnchain_2,
1✔
5650
                    0x9abcdef0,
1✔
5651
                    12351,
1✔
5652
                    UrlString::from_literal("http://peer2.com"),
1✔
5653
                    &[],
1✔
5654
                    &[],
1✔
5655
                    DEFAULT_SERVICES,
1✔
5656
                );
1✔
5657

5658
            let mut net_1 = db_setup(
1✔
5659
                test_name_1,
1✔
5660
                &burnchain_1,
1✔
5661
                0x9abcdef0,
5662
                &mut peerdb_1,
1✔
5663
                &mut sortdb_1,
1✔
5664
                &socketaddr_1,
1✔
5665
                &chain_view,
1✔
5666
            );
5667
            let mut net_2 = db_setup(
1✔
5668
                test_name_2,
1✔
5669
                &burnchain_2,
1✔
5670
                0x9abcdef0,
5671
                &mut peerdb_2,
1✔
5672
                &mut sortdb_2,
1✔
5673
                &socketaddr_2,
1✔
5674
                &chain_view,
1✔
5675
            );
5676

5677
            let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
5678
            let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
5679

5680
            let mut convo_1 = ConversationP2P::new(
1✔
5681
                123,
5682
                456,
5683
                &burnchain_1,
1✔
5684
                &socketaddr_2,
1✔
5685
                &conn_opts,
1✔
5686
                true,
5687
                0,
5688
                StacksEpoch::unit_test_pre_2_05(0),
1✔
5689
            );
5690
            let mut convo_2 = ConversationP2P::new(
1✔
5691
                123,
5692
                456,
5693
                &burnchain_2,
1✔
5694
                &socketaddr_1,
1✔
5695
                &conn_opts,
1✔
5696
                true,
5697
                0,
5698
                StacksEpoch::unit_test_pre_2_05(0),
1✔
5699
            );
5700

5701
            // no peer public keys known yet
5702
            assert!(convo_1.connection.get_public_key().is_none());
1✔
5703
            assert!(convo_2.connection.get_public_key().is_none());
1✔
5704

5705
            // convo_1 sends a handshake to convo_2
5706
            let handshake_data_1 = HandshakeData::from_local_peer(&local_peer_1);
1✔
5707
            let handshake_1 = convo_1
1✔
5708
                .sign_message(
1✔
5709
                    &chain_view,
1✔
5710
                    &local_peer_1.private_key,
1✔
5711
                    StacksMessageType::Handshake(handshake_data_1.clone()),
1✔
5712
                )
5713
                .unwrap();
1✔
5714
            let mut rh_1 = convo_1.send_signed_request(handshake_1, 1000000).unwrap();
1✔
5715

5716
            // convo_2 receives it and processes it, and since no one is waiting for it, will forward
5717
            // it along to the chat caller (us)
5718
            test_debug!("send handshake");
1✔
5719
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
5720
            let unhandled_2 = convo_2
1✔
5721
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5722
                .unwrap();
1✔
5723

5724
            // convo_1 has a handshakeaccept
5725
            test_debug!("send handshake-accept");
1✔
5726
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
5727
            let unhandled_1 = convo_1
1✔
5728
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5729
                .unwrap();
1✔
5730

5731
            let reply_1 = rh_1.recv(0).unwrap();
1✔
5732

5733
            assert!(unhandled_1.is_empty());
1✔
5734
            assert_eq!(unhandled_2.len(), 1);
1✔
5735

5736
            // convo 2 returns the handshake from convo 1
5737
            if let StacksMessageType::Handshake(ref data) = unhandled_2[0].payload {
1✔
5738
                assert_eq!(handshake_data_1, *data);
1✔
5739
            } else {
5740
                panic!("Unexpected payload message type");
×
5741
            }
5742

5743
            // received a valid HandshakeAccept from peer 2
5744
            match reply_1.payload {
1✔
5745
                StacksMessageType::HandshakeAccept(ref data)
1✔
5746
                | StacksMessageType::StackerDBHandshakeAccept(ref data, ..) => {
×
5747
                    assert_eq!(data.handshake.addrbytes, local_peer_2.addrbytes);
1✔
5748
                    assert_eq!(data.handshake.port, local_peer_2.port);
1✔
5749
                    assert_eq!(data.handshake.services, local_peer_2.services);
1✔
5750
                    assert_eq!(
1✔
5751
                        data.handshake.node_public_key,
5752
                        StacksPublicKeyBuffer::from_public_key(&Secp256k1PublicKey::from_private(
1✔
5753
                            &local_peer_2.private_key
1✔
5754
                        ))
1✔
5755
                    );
5756
                    assert_eq!(
1✔
5757
                        data.handshake.expire_block_height,
5758
                        local_peer_2.private_key_expire
5759
                    );
5760
                    assert_eq!(
1✔
5761
                        data.handshake.data_url,
5762
                        UrlString::from_literal("http://peer2.com")
1✔
5763
                    );
5764
                    assert_eq!(data.heartbeat_interval, conn_opts.heartbeat);
1✔
5765
                }
5766
                _ => {
5767
                    panic!("Unexpected payload message type");
×
5768
                }
5769
            };
5770

5771
            // convo_1 sends a getnakamotoinv to convo_2 for all the tenures in the last reward cycle
5772
            let convo_1_chaintip =
1✔
5773
                SortitionDB::get_canonical_burn_chain_tip(sortdb_1.conn()).unwrap();
1✔
5774
            let convo_1_ancestor = {
1✔
5775
                let ic = sortdb_1.index_conn();
1✔
5776
                SortitionDB::get_ancestor_snapshot(
1✔
5777
                    &ic,
1✔
5778
                    convo_1_chaintip.block_height - 10 - 1,
1✔
5779
                    &convo_1_chaintip.sortition_id,
1✔
5780
                )
5781
                .unwrap()
1✔
5782
                .unwrap()
1✔
5783
            };
5784

5785
            let getnakamotodata_1 = GetNakamotoInvData {
1✔
5786
                consensus_hash: convo_1_ancestor.consensus_hash,
1✔
5787
            };
1✔
5788
            let getnakamotodata_1_msg = convo_1
1✔
5789
                .sign_message(
1✔
5790
                    &chain_view,
1✔
5791
                    &local_peer_1.private_key,
1✔
5792
                    StacksMessageType::GetNakamotoInv(getnakamotodata_1),
1✔
5793
                )
5794
                .unwrap();
1✔
5795
            let mut rh_1 = convo_1
1✔
5796
                .send_signed_request(getnakamotodata_1_msg, 10000000)
1✔
5797
                .unwrap();
1✔
5798

5799
            // convo_2 receives it, and handles it
5800
            test_debug!("send getnakamotoinv");
1✔
5801
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
5802
            let unhandled_2 = convo_2
1✔
5803
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5804
                .unwrap();
1✔
5805

5806
            // convo_1 gets back a nakamotoinv message
5807
            test_debug!("send nakamotoinv");
1✔
5808
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
5809
            let unhandled_1 = convo_1
1✔
5810
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5811
                .unwrap();
1✔
5812

5813
            let reply_1 = rh_1.recv(0).unwrap();
1✔
5814

5815
            // no unhandled messages forwarded
5816
            assert!(unhandled_1.is_empty());
1✔
5817
            assert!(unhandled_2.is_empty());
1✔
5818

5819
            // convo 2 returned a tenure-inv for all tenures
5820
            if let StacksMessageType::NakamotoInv(ref data) = reply_1.payload {
1✔
5821
                assert_eq!(data.tenures.len(), 10);
1✔
5822
                test_debug!("data: {data:?}");
1✔
5823

5824
                // all burn blocks had sortitions, but we have no tenures :(
5825
                for i in 0..10 {
10✔
5826
                    assert!(!data.tenures.get(i).unwrap());
10✔
5827
                }
5828
            } else {
5829
                panic!("Unexpected payload message type");
×
5830
            }
5831

5832
            // request for a non-existent consensus hash
5833
            let getnakamotodata_diverged_1 = GetNakamotoInvData {
1✔
5834
                consensus_hash: ConsensusHash([0xff; 20]),
1✔
5835
            };
1✔
5836
            let getnakamotodata_diverged_1_msg = convo_1
1✔
5837
                .sign_message(
1✔
5838
                    &chain_view,
1✔
5839
                    &local_peer_1.private_key,
1✔
5840
                    StacksMessageType::GetNakamotoInv(getnakamotodata_diverged_1),
1✔
5841
                )
5842
                .unwrap();
1✔
5843
            let mut rh_1 = convo_1
1✔
5844
                .send_signed_request(getnakamotodata_diverged_1_msg, 10000000)
1✔
5845
                .unwrap();
1✔
5846

5847
            // convo_2 receives it, and handles it
5848
            test_debug!("send getnakamotoinv (diverged)");
1✔
5849
            convo_send_recv(&mut convo_1, vec![&mut rh_1], &mut convo_2);
1✔
5850
            let unhandled_2 = convo_2
1✔
5851
                .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5852
                .unwrap();
1✔
5853

5854
            // convo_1 gets back a nack message
5855
            test_debug!("send nack (diverged)");
1✔
5856
            convo_send_recv(&mut convo_2, vec![&mut rh_1], &mut convo_1);
1✔
5857
            let unhandled_1 = convo_1
1✔
5858
                .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5859
                .unwrap();
1✔
5860

5861
            let reply_1 = rh_1.recv(0).unwrap();
1✔
5862

5863
            // no unhandled messages forwarded
5864
            assert!(unhandled_1.is_empty());
1✔
5865
            assert!(unhandled_2.is_empty());
1✔
5866

5867
            // convo 2 returned a nack with the appropriate error message
5868
            if let StacksMessageType::Nack(ref data) = reply_1.payload {
1✔
5869
                assert_eq!(data.error_code, NackErrorCodes::NoSuchBurnchainBlock);
1✔
5870
            } else {
5871
                panic!("Unexpected payload message type");
×
5872
            }
5873
        })
1✔
5874
    }
1✔
5875

5876
    #[test]
5877
    fn convo_natpunch() {
1✔
5878
        let conn_opts = ConnectionOptions::default();
1✔
5879
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
5880
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
5881

5882
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5883
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5884
        )
5885
        .unwrap();
1✔
5886

5887
        let mut chain_view = BurnchainView {
1✔
5888
            burn_block_height: 12348,
1✔
5889
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
5890
            burn_stable_block_height: 12341,
1✔
5891
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
5892
            last_burn_block_hashes: HashMap::new(),
1✔
5893
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
5894
        };
1✔
5895
        chain_view.make_test_data();
1✔
5896

5897
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
5898
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
5899
        )
5900
        .unwrap();
1✔
5901

5902
        let test_name_1 = "convo_natpunch_1";
1✔
5903
        let test_name_2 = "convo_natpunch_2";
1✔
5904

5905
        let burnchain_1 = testing_burnchain_config(test_name_1);
1✔
5906
        let burnchain_2 = testing_burnchain_config(test_name_2);
1✔
5907

5908
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, mut chainstate_1) =
1✔
5909
            make_test_chain_dbs(
1✔
5910
                test_name_1,
1✔
5911
                &burnchain_1,
1✔
5912
                0x9abcdef0,
1✔
5913
                12352,
1✔
5914
                UrlString::from_literal("http://peer1.com"),
1✔
5915
                &[],
1✔
5916
                &[],
1✔
5917
                DEFAULT_SERVICES,
1✔
5918
            );
1✔
5919
        let (mut peerdb_2, mut sortdb_2, stackerdbs_2, pox_id_2, mut chainstate_2) =
1✔
5920
            make_test_chain_dbs(
1✔
5921
                test_name_2,
1✔
5922
                &burnchain_2,
1✔
5923
                0x9abcdef0,
1✔
5924
                12353,
1✔
5925
                UrlString::from_literal("http://peer2.com"),
1✔
5926
                &[],
1✔
5927
                &[],
1✔
5928
                DEFAULT_SERVICES,
1✔
5929
            );
1✔
5930

5931
        let mut net_1 = db_setup(
1✔
5932
            test_name_1,
1✔
5933
            &burnchain_1,
1✔
5934
            0x9abcdef0,
5935
            &mut peerdb_1,
1✔
5936
            &mut sortdb_1,
1✔
5937
            &socketaddr_1,
1✔
5938
            &chain_view,
1✔
5939
        );
5940
        let mut net_2 = db_setup(
1✔
5941
            test_name_2,
1✔
5942
            &burnchain_2,
1✔
5943
            0x9abcdef0,
5944
            &mut peerdb_2,
1✔
5945
            &mut sortdb_2,
1✔
5946
            &socketaddr_2,
1✔
5947
            &chain_view,
1✔
5948
        );
5949

5950
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
5951
        let local_peer_2 = PeerDB::get_local_peer(peerdb_2.conn()).unwrap();
1✔
5952

5953
        let mut convo_1 = ConversationP2P::new(
1✔
5954
            123,
5955
            456,
5956
            &burnchain_1,
1✔
5957
            &socketaddr_2,
1✔
5958
            &conn_opts,
1✔
5959
            true,
5960
            0,
5961
            StacksEpoch::unit_test_pre_2_05(0),
1✔
5962
        );
5963
        let mut convo_2 = ConversationP2P::new(
1✔
5964
            123,
5965
            456,
5966
            &burnchain_2,
1✔
5967
            &socketaddr_1,
1✔
5968
            &conn_opts,
1✔
5969
            true,
5970
            0,
5971
            StacksEpoch::unit_test_pre_2_05(0),
1✔
5972
        );
5973

5974
        // convo_1 sends natpunch request to convo_2
5975
        let natpunch_1 = convo_1
1✔
5976
            .sign_message(
1✔
5977
                &chain_view,
1✔
5978
                &local_peer_1.private_key,
1✔
5979
                StacksMessageType::NatPunchRequest(0x12345678),
1✔
5980
            )
5981
            .unwrap();
1✔
5982
        let mut rh_natpunch_1 = convo_1
1✔
5983
            .send_signed_request(natpunch_1.clone(), 1000000)
1✔
5984
            .unwrap();
1✔
5985

5986
        // convo_2 receives the natpunch request and processes it
5987
        test_debug!("send natpunch {:?}", &natpunch_1);
1✔
5988
        convo_send_recv(&mut convo_1, vec![&mut rh_natpunch_1], &mut convo_2);
1✔
5989
        let unhandled_2 = convo_2
1✔
5990
            .chat(&mut net_2, &sortdb_2, &mut chainstate_2, &mut None, false)
1✔
5991
            .unwrap();
1✔
5992

5993
        // convo_1 gets back a natpunch reply
5994
        test_debug!("reply natpunch-reply");
1✔
5995
        convo_send_recv(&mut convo_2, vec![&mut rh_natpunch_1], &mut convo_1);
1✔
5996
        let unhandled_1 = convo_1
1✔
5997
            .chat(&mut net_1, &sortdb_1, &mut chainstate_1, &mut None, false)
1✔
5998
            .unwrap();
1✔
5999

6000
        let natpunch_reply_1 = rh_natpunch_1.recv(0).unwrap();
1✔
6001

6002
        // handled and consumed
6003
        assert!(unhandled_1.is_empty());
1✔
6004
        assert!(unhandled_2.is_empty());
1✔
6005

6006
        // convo_2 replies the natpunch data for convo_1 -- i.e. what convo_2 thinks convo_1's IP
6007
        // address is
6008
        if let StacksMessageType::NatPunchReply(ref data) = natpunch_reply_1.payload {
1✔
6009
            assert_eq!(data.addrbytes, PeerAddress::from_socketaddr(&socketaddr_1));
1✔
6010
            assert_eq!(data.nonce, 0x12345678);
1✔
6011
        } else {
6012
            panic!("Unexpected payload message type");
×
6013
        }
6014
    }
1✔
6015

6016
    #[test]
6017
    fn convo_is_preamble_valid() {
1✔
6018
        let conn_opts = ConnectionOptions::default();
1✔
6019
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
1✔
6020
        let socketaddr_2 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
6021

6022
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
6023
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
6024
        )
6025
        .unwrap();
1✔
6026

6027
        let mut chain_view = BurnchainView {
1✔
6028
            burn_block_height: 12348,
1✔
6029
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
6030
            burn_stable_block_height: 12341,
1✔
6031
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
6032
            last_burn_block_hashes: HashMap::new(),
1✔
6033
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
6034
        };
1✔
6035
        chain_view.make_test_data();
1✔
6036

6037
        let test_name_1 = "convo_is_preamble_valid";
1✔
6038
        let burnchain = testing_burnchain_config(test_name_1);
1✔
6039

6040
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, chainstate_1) =
1✔
6041
            make_test_chain_dbs(
1✔
6042
                test_name_1,
1✔
6043
                &burnchain,
1✔
6044
                0x9abcdef0,
1✔
6045
                12352,
1✔
6046
                UrlString::from_literal("http://peer1.com"),
1✔
6047
                &[],
1✔
6048
                &[],
1✔
6049
                DEFAULT_SERVICES,
1✔
6050
            );
1✔
6051

6052
        let net_1 = db_setup(
1✔
6053
            test_name_1,
1✔
6054
            &burnchain,
1✔
6055
            0x9abcdef0,
6056
            &mut peerdb_1,
1✔
6057
            &mut sortdb_1,
1✔
6058
            &socketaddr_1,
1✔
6059
            &chain_view,
1✔
6060
        );
6061

6062
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
6063

6064
        // network ID check
6065
        {
6066
            let mut convo_bad = ConversationP2P::new(
1✔
6067
                123,
6068
                456,
6069
                &burnchain,
1✔
6070
                &socketaddr_2,
1✔
6071
                &conn_opts,
1✔
6072
                true,
6073
                0,
6074
                StacksEpoch::unit_test_pre_2_05(0),
1✔
6075
            );
6076

6077
            let ping_data = PingData::new();
1✔
6078
            convo_bad.network_id += 1;
1✔
6079
            let ping_bad = convo_bad
1✔
6080
                .sign_message(
1✔
6081
                    &chain_view,
1✔
6082
                    &local_peer_1.private_key,
1✔
6083
                    StacksMessageType::Ping(ping_data),
1✔
6084
                )
6085
                .unwrap();
1✔
6086
            convo_bad.network_id -= 1;
1✔
6087

6088
            assert_eq!(
1✔
6089
                convo_bad.is_preamble_valid(&ping_bad, &chain_view),
1✔
6090
                Err(net_error::InvalidMessage)
6091
            );
6092
        }
6093

6094
        // stable block height check
6095
        {
6096
            let mut convo_bad = ConversationP2P::new(
1✔
6097
                123,
6098
                456,
6099
                &burnchain,
1✔
6100
                &socketaddr_2,
1✔
6101
                &conn_opts,
1✔
6102
                true,
6103
                0,
6104
                StacksEpoch::unit_test_pre_2_05(0),
1✔
6105
            );
6106

6107
            let ping_data = PingData::new();
1✔
6108

6109
            let mut chain_view_bad = chain_view.clone();
1✔
6110
            chain_view_bad.burn_stable_block_height -= 1;
1✔
6111

6112
            let ping_bad = convo_bad
1✔
6113
                .sign_message(
1✔
6114
                    &chain_view_bad,
1✔
6115
                    &local_peer_1.private_key,
1✔
6116
                    StacksMessageType::Ping(ping_data),
1✔
6117
                )
6118
                .unwrap();
1✔
6119

6120
            assert_eq!(
1✔
6121
                convo_bad.is_preamble_valid(&ping_bad, &chain_view),
1✔
6122
                Err(net_error::InvalidMessage)
6123
            );
6124
        }
6125

6126
        // unstable burn header hash mismatch
6127
        {
6128
            let mut convo_bad = ConversationP2P::new(
1✔
6129
                123,
6130
                456,
6131
                &burnchain,
1✔
6132
                &socketaddr_2,
1✔
6133
                &conn_opts,
1✔
6134
                true,
6135
                0,
6136
                StacksEpoch::unit_test_pre_2_05(0),
1✔
6137
            );
6138

6139
            let ping_data = PingData::new();
1✔
6140

6141
            let mut chain_view_bad = chain_view.clone();
1✔
6142
            let old = chain_view_bad.burn_block_hash.clone();
1✔
6143
            chain_view_bad.burn_block_hash = BurnchainHeaderHash([0x33; 32]);
1✔
6144
            chain_view_bad.last_burn_block_hashes.insert(
1✔
6145
                chain_view_bad.burn_block_height,
1✔
6146
                chain_view_bad.burn_block_hash.clone(),
1✔
6147
            );
6148

6149
            let ping_bad = convo_bad
1✔
6150
                .sign_message(
1✔
6151
                    &chain_view_bad,
1✔
6152
                    &local_peer_1.private_key,
1✔
6153
                    StacksMessageType::Ping(ping_data),
1✔
6154
                )
6155
                .unwrap();
1✔
6156

6157
            // considered valid as long as the stable burn header hash is valid
6158
            assert_eq!(
1✔
6159
                convo_bad.is_preamble_valid(&ping_bad, &chain_view),
1✔
6160
                Ok(true)
6161
            );
6162
        }
6163

6164
        // stable burn header hash mismatch
6165
        {
6166
            let mut convo_bad = ConversationP2P::new(
1✔
6167
                123,
6168
                456,
6169
                &burnchain,
1✔
6170
                &socketaddr_2,
1✔
6171
                &conn_opts,
1✔
6172
                true,
6173
                0,
6174
                StacksEpoch::unit_test_pre_2_05(0),
1✔
6175
            );
6176

6177
            let ping_data = PingData::new();
1✔
6178

6179
            let mut chain_view_bad = chain_view.clone();
1✔
6180
            let old = chain_view_bad.burn_stable_block_hash.clone();
1✔
6181
            chain_view_bad.burn_stable_block_hash = BurnchainHeaderHash([0x11; 32]);
1✔
6182
            chain_view_bad.last_burn_block_hashes.insert(
1✔
6183
                chain_view_bad.burn_stable_block_height,
1✔
6184
                chain_view_bad.burn_stable_block_hash.clone(),
1✔
6185
            );
6186

6187
            let ping_bad = convo_bad
1✔
6188
                .sign_message(
1✔
6189
                    &chain_view_bad,
1✔
6190
                    &local_peer_1.private_key,
1✔
6191
                    StacksMessageType::Ping(ping_data),
1✔
6192
                )
6193
                .unwrap();
1✔
6194

6195
            assert_eq!(
1✔
6196
                convo_bad.is_preamble_valid(&ping_bad, &chain_view),
1✔
6197
                Err(net_error::InvalidMessage)
6198
            );
6199
        }
6200

6201
        // stale peer version max-epoch
6202
        {
6203
            // convo thinks its epoch 2.05
6204
            let epochs = StacksEpoch::unit_test_2_05(chain_view.burn_block_height - 4);
1✔
6205
            let cur_epoch = epochs
1✔
6206
                .epoch_at_height(chain_view.burn_block_height)
1✔
6207
                .unwrap();
1✔
6208
            assert_eq!(cur_epoch.epoch_id, StacksEpochId::Epoch2_05);
1✔
6209

6210
            eprintln!(
1✔
6211
                "cur_epoch = {:?}, burn height = {}",
6212
                &cur_epoch, chain_view.burn_block_height
1✔
6213
            );
6214

6215
            let mut convo_bad = ConversationP2P::new(
1✔
6216
                123,
6217
                0x18000005,
6218
                &burnchain,
1✔
6219
                &socketaddr_2,
1✔
6220
                &conn_opts,
1✔
6221
                true,
6222
                0,
6223
                epochs,
1✔
6224
            );
6225

6226
            let ping_data = PingData::new();
1✔
6227

6228
            // give ping a pre-2.05 epoch marker in its peer version
6229
            convo_bad.version = 0x18000000;
1✔
6230
            let ping_bad = convo_bad
1✔
6231
                .sign_message(
1✔
6232
                    &chain_view,
1✔
6233
                    &local_peer_1.private_key,
1✔
6234
                    StacksMessageType::Ping(ping_data.clone()),
1✔
6235
                )
6236
                .unwrap();
1✔
6237
            convo_bad.version = 0x18000005;
1✔
6238

6239
            assert_eq!(
1✔
6240
                convo_bad.is_preamble_valid(&ping_bad, &chain_view),
1✔
6241
                Err(net_error::InvalidMessage)
6242
            );
6243

6244
            // give ping the same peer version as the convo
6245
            let ping_good = convo_bad
1✔
6246
                .sign_message(
1✔
6247
                    &chain_view,
1✔
6248
                    &local_peer_1.private_key,
1✔
6249
                    StacksMessageType::Ping(ping_data.clone()),
1✔
6250
                )
6251
                .unwrap();
1✔
6252
            assert_eq!(
1✔
6253
                convo_bad.is_preamble_valid(&ping_good, &chain_view),
1✔
6254
                Ok(true)
6255
            );
6256

6257
            // give ping a newer epoch than we support
6258
            convo_bad.version = 0x18000006;
1✔
6259
            let ping_good = convo_bad
1✔
6260
                .sign_message(
1✔
6261
                    &chain_view,
1✔
6262
                    &local_peer_1.private_key,
1✔
6263
                    StacksMessageType::Ping(ping_data.clone()),
1✔
6264
                )
6265
                .unwrap();
1✔
6266
            convo_bad.version = 0x18000005;
1✔
6267
            assert_eq!(
1✔
6268
                convo_bad.is_preamble_valid(&ping_good, &chain_view),
1✔
6269
                Ok(true)
6270
            );
6271

6272
            // give ping an older version, but test with a block in which the ping's version is
6273
            // valid
6274
            convo_bad.version = 0x18000000;
1✔
6275
            let ping_old = convo_bad
1✔
6276
                .sign_message(
1✔
6277
                    &chain_view,
1✔
6278
                    &local_peer_1.private_key,
1✔
6279
                    StacksMessageType::Ping(ping_data),
1✔
6280
                )
6281
                .unwrap();
1✔
6282
            convo_bad.version = 0x18000005;
1✔
6283

6284
            let mut old_chain_view = chain_view.clone();
1✔
6285
            old_chain_view.burn_block_height -= 1;
1✔
6286
            old_chain_view.burn_stable_block_height -= 1;
1✔
6287
            old_chain_view.last_burn_block_hashes.insert(
1✔
6288
                old_chain_view.burn_stable_block_height,
1✔
6289
                BurnchainHeaderHash([0xff; 32]),
1✔
6290
            );
6291
            assert_eq!(
1✔
6292
                convo_bad.is_preamble_valid(&ping_old, &old_chain_view),
1✔
6293
                Ok(true)
6294
            );
6295
        }
6296

6297
        // 3.3/3.2 compatibility: allow peers that still report 3.2 in epoch 3.3.
6298
        {
6299
            let epochs = StacksEpoch::unit_test_3_3(chain_view.burn_block_height - 40);
1✔
6300
            let cur_epoch = epochs
1✔
6301
                .epoch_at_height(chain_view.burn_block_height)
1✔
6302
                .unwrap();
1✔
6303
            assert_eq!(cur_epoch.epoch_id, StacksEpochId::Epoch33);
1✔
6304

6305
            let local_version = 0x18000000 | PEER_VERSION_EPOCH_3_3 as u32;
1✔
6306
            let mut convo_compat = ConversationP2P::new(
1✔
6307
                123,
6308
                local_version,
1✔
6309
                &burnchain,
1✔
6310
                &socketaddr_2,
1✔
6311
                &conn_opts,
1✔
6312
                true,
6313
                0,
6314
                epochs,
1✔
6315
            );
6316
            let ping_data = PingData::new();
1✔
6317

6318
            // Remote peer reports 3.2 in epoch 3.3: allowed.
6319
            convo_compat.version = 0x18000000 | PEER_VERSION_EPOCH_3_2 as u32;
1✔
6320
            let ping_32 = convo_compat
1✔
6321
                .sign_message(
1✔
6322
                    &chain_view,
1✔
6323
                    &local_peer_1.private_key,
1✔
6324
                    StacksMessageType::Ping(ping_data.clone()),
1✔
6325
                )
6326
                .unwrap();
1✔
6327
            convo_compat.version = local_version;
1✔
6328
            assert_eq!(
1✔
6329
                convo_compat.is_preamble_valid(&ping_32, &chain_view),
1✔
6330
                Ok(true)
6331
            );
6332

6333
            // Remote peer reports 3.1 in epoch 3.3: still too old.
6334
            convo_compat.version = 0x18000000 | PEER_VERSION_EPOCH_3_1 as u32;
1✔
6335
            let ping_31 = convo_compat
1✔
6336
                .sign_message(
1✔
6337
                    &chain_view,
1✔
6338
                    &local_peer_1.private_key,
1✔
6339
                    StacksMessageType::Ping(ping_data),
1✔
6340
                )
6341
                .unwrap();
1✔
6342
            convo_compat.version = local_version;
1✔
6343
            assert_eq!(
1✔
6344
                convo_compat.is_preamble_valid(&ping_31, &chain_view),
1✔
6345
                Err(net_error::InvalidMessage)
6346
            );
6347
        }
6348
    }
1✔
6349

6350
    // TODO: test for has_acceptable_epoch()
6351

6352
    #[test]
6353
    fn convo_process_relayers() {
1✔
6354
        let conn_opts = ConnectionOptions::default();
1✔
6355
        let socketaddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8090);
1✔
6356

6357
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
6358
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
6359
        )
6360
        .unwrap();
1✔
6361

6362
        let burnchain = testing_burnchain_config("unused");
1✔
6363

6364
        let mut chain_view = BurnchainView {
1✔
6365
            burn_block_height: 12348,
1✔
6366
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
6367
            burn_stable_block_height: 12341,
1✔
6368
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
6369
            last_burn_block_hashes: HashMap::new(),
1✔
6370
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
6371
        };
1✔
6372
        chain_view.make_test_data();
1✔
6373

6374
        let local_peer = LocalPeer::new(
1✔
6375
            123,
6376
            burnchain.network_id,
1✔
6377
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
6378
            NETWORK_P2P_PORT,
6379
            None,
1✔
6380
            get_epoch_time_secs() + 123456,
1✔
6381
            UrlString::try_from("http://foo.com").unwrap(),
1✔
6382
            vec![],
1✔
6383
        );
6384
        let mut convo = ConversationP2P::new(
1✔
6385
            123,
6386
            456,
6387
            &burnchain,
1✔
6388
            &socketaddr,
1✔
6389
            &conn_opts,
1✔
6390
            true,
6391
            0,
6392
            StacksEpoch::unit_test_pre_2_05(0),
1✔
6393
        );
6394

6395
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
6396
        let msg = convo
1✔
6397
            .sign_reply(&chain_view, &local_peer.private_key, payload, 123)
1✔
6398
            .unwrap();
1✔
6399

6400
        // cycles
6401
        let relay_cycles = vec![
1✔
6402
            RelayData {
1✔
6403
                peer: NeighborAddress {
1✔
6404
                    addrbytes: PeerAddress([0u8; 16]),
1✔
6405
                    port: 123,
1✔
6406
                    public_key_hash: Hash160([0u8; 20]),
1✔
6407
                },
1✔
6408
                seq: 123,
1✔
6409
            },
1✔
6410
            RelayData {
1✔
6411
                peer: NeighborAddress {
1✔
6412
                    addrbytes: PeerAddress([1u8; 16]),
1✔
6413
                    port: 456,
1✔
6414
                    public_key_hash: Hash160([0u8; 20]),
1✔
6415
                },
1✔
6416
                seq: 456,
1✔
6417
            },
1✔
6418
        ];
6419

6420
        // contains localpeer
6421
        let self_sent = vec![RelayData {
1✔
6422
            peer: NeighborAddress {
1✔
6423
                addrbytes: local_peer.addrbytes.clone(),
1✔
6424
                port: local_peer.port,
1✔
6425
                public_key_hash: Hash160::from_node_public_key(&StacksPublicKey::from_private(
1✔
6426
                    &local_peer.private_key,
1✔
6427
                )),
1✔
6428
            },
1✔
6429
            seq: 789,
1✔
6430
        }];
1✔
6431

6432
        // allowed
6433
        let relayers = vec![
1✔
6434
            RelayData {
1✔
6435
                peer: NeighborAddress {
1✔
6436
                    addrbytes: PeerAddress([0u8; 16]),
1✔
6437
                    port: 123,
1✔
6438
                    public_key_hash: Hash160([0u8; 20]),
1✔
6439
                },
1✔
6440
                seq: 123,
1✔
6441
            },
1✔
6442
            RelayData {
1✔
6443
                peer: NeighborAddress {
1✔
6444
                    addrbytes: PeerAddress([1u8; 16]),
1✔
6445
                    port: 456,
1✔
6446
                    public_key_hash: Hash160([1u8; 20]),
1✔
6447
                },
1✔
6448
                seq: 456,
1✔
6449
            },
1✔
6450
        ];
6451

6452
        assert!(!convo.process_relayers(&local_peer, &msg.preamble, &relay_cycles));
1✔
6453
        assert!(!convo.process_relayers(&local_peer, &msg.preamble, &self_sent));
1✔
6454

6455
        assert!(convo.process_relayers(&local_peer, &msg.preamble, &relayers));
1✔
6456

6457
        // stats updated
6458
        assert_eq!(convo.stats.relayed_messages.len(), 2);
1✔
6459
        let relayer_map = convo.stats.take_relayers();
1✔
6460
        assert!(convo.stats.relayed_messages.is_empty());
1✔
6461

6462
        for r in relayers.into_iter() {
2✔
6463
            assert!(relayer_map.contains_key(&r.peer));
2✔
6464

6465
            let stats = relayer_map.get(&r.peer).unwrap();
2✔
6466
            assert_eq!(stats.num_messages, 1);
2✔
6467
            assert_eq!(stats.num_bytes, (msg.preamble.payload_len - 1) as u64);
2✔
6468
        }
6469
    }
1✔
6470

6471
    #[test]
6472
    fn test_neighbor_stats_healthpoint() {
1✔
6473
        let mut stats = NeighborStats::new(false);
1✔
6474

6475
        assert_eq!(stats.get_health_score(), 0.5);
1✔
6476

6477
        for _ in 0..NUM_HEALTH_POINTS - 1 {
1✔
6478
            stats.add_healthpoint(true);
31✔
6479
            assert_eq!(stats.get_health_score(), 0.5);
31✔
6480
        }
6481

6482
        stats.add_healthpoint(true);
1✔
6483
        assert_eq!(stats.get_health_score(), 1.0);
1✔
6484

6485
        for _ in 0..(NUM_HEALTH_POINTS / 2) {
16✔
6486
            stats.add_healthpoint(false);
16✔
6487
        }
16✔
6488

6489
        assert_eq!(stats.get_health_score(), 0.5);
1✔
6490

6491
        for _ in 0..(NUM_HEALTH_POINTS / 2) {
16✔
6492
            stats.add_healthpoint(false);
16✔
6493
        }
16✔
6494

6495
        assert_eq!(stats.get_health_score(), 0.0);
1✔
6496
    }
1✔
6497

6498
    #[test]
6499
    fn test_neighbor_stats_block_push_bandwidth() {
1✔
6500
        let mut stats = NeighborStats::new(false);
1✔
6501

6502
        assert_eq!(stats.get_block_push_bandwidth(), 0.0);
1✔
6503

6504
        stats.add_block_push(100);
1✔
6505
        assert_eq!(stats.get_block_push_bandwidth(), 0.0);
1✔
6506

6507
        // this should all happen in one second
6508
        let bw_stats = loop {
1✔
6509
            let mut bw_stats = stats.clone();
1✔
6510
            let start = get_epoch_time_secs();
1✔
6511

6512
            for _ in 0..(NUM_BANDWIDTH_POINTS - 1) {
31✔
6513
                bw_stats.add_block_push(100);
31✔
6514
            }
31✔
6515

6516
            let end = get_epoch_time_secs();
1✔
6517
            if end == start {
1✔
6518
                break bw_stats;
1✔
6519
            }
×
6520
        };
6521

6522
        assert_eq!(
1✔
6523
            bw_stats.get_block_push_bandwidth(),
1✔
6524
            (NUM_BANDWIDTH_POINTS as f64) * 100.0
6525
        );
6526

6527
        // space some out; make sure it takes 11 seconds
6528
        let bw_stats = loop {
1✔
6529
            let mut bw_stats = NeighborStats::new(false);
1✔
6530
            let start = get_epoch_time_secs();
1✔
6531
            for _ in 0..11 {
11✔
6532
                bw_stats.add_block_push(100);
11✔
6533
                sleep_ms(1001);
11✔
6534
            }
11✔
6535

6536
            let end = get_epoch_time_secs();
1✔
6537
            if end == start + 11 {
1✔
6538
                break bw_stats;
1✔
6539
            }
×
6540
        };
6541

6542
        // 100 bytes/sec
6543
        assert_eq!(bw_stats.get_block_push_bandwidth(), 110.0);
1✔
6544
    }
1✔
6545

6546
    #[test]
6547
    fn test_neighbor_stats_transaction_push_bandwidth() {
1✔
6548
        let mut stats = NeighborStats::new(false);
1✔
6549

6550
        assert_eq!(stats.get_transaction_push_bandwidth(), 0.0);
1✔
6551

6552
        stats.add_transaction_push(100);
1✔
6553
        assert_eq!(stats.get_transaction_push_bandwidth(), 0.0);
1✔
6554

6555
        // this should all happen in one second
6556
        let bw_stats = loop {
1✔
6557
            let mut bw_stats = stats.clone();
1✔
6558
            let start = get_epoch_time_secs();
1✔
6559

6560
            for _ in 0..(NUM_BANDWIDTH_POINTS - 1) {
31✔
6561
                bw_stats.add_transaction_push(100);
31✔
6562
            }
31✔
6563

6564
            let end = get_epoch_time_secs();
1✔
6565
            if end == start {
1✔
6566
                break bw_stats;
1✔
6567
            }
×
6568
        };
6569

6570
        assert_eq!(
1✔
6571
            bw_stats.get_transaction_push_bandwidth(),
1✔
6572
            (NUM_BANDWIDTH_POINTS as f64) * 100.0
6573
        );
6574

6575
        // space some out; make sure it takes 11 seconds
6576
        let bw_stats = loop {
1✔
6577
            let mut bw_stats = NeighborStats::new(false);
1✔
6578
            let start = get_epoch_time_secs();
1✔
6579
            for _ in 0..11 {
11✔
6580
                bw_stats.add_transaction_push(100);
11✔
6581
                sleep_ms(1001);
11✔
6582
            }
11✔
6583

6584
            let end = get_epoch_time_secs();
1✔
6585
            if end == start + 11 {
1✔
6586
                break bw_stats;
1✔
6587
            }
×
6588
        };
6589

6590
        // 100 bytes/sec
6591
        assert_eq!(bw_stats.get_transaction_push_bandwidth(), 110.0);
1✔
6592
    }
1✔
6593

6594
    #[test]
6595
    fn test_neighbor_stats_microblocks_push_bandwidth() {
1✔
6596
        let mut stats = NeighborStats::new(false);
1✔
6597

6598
        assert_eq!(stats.get_microblocks_push_bandwidth(), 0.0);
1✔
6599

6600
        stats.add_microblocks_push(100);
1✔
6601
        assert_eq!(stats.get_microblocks_push_bandwidth(), 0.0);
1✔
6602

6603
        // this should all happen in one second
6604
        let bw_stats = loop {
1✔
6605
            let mut bw_stats = stats.clone();
1✔
6606
            let start = get_epoch_time_secs();
1✔
6607

6608
            for _ in 0..(NUM_BANDWIDTH_POINTS - 1) {
31✔
6609
                bw_stats.add_microblocks_push(100);
31✔
6610
            }
31✔
6611

6612
            let end = get_epoch_time_secs();
1✔
6613
            if end == start {
1✔
6614
                break bw_stats;
1✔
6615
            }
×
6616
        };
6617

6618
        assert_eq!(
1✔
6619
            bw_stats.get_microblocks_push_bandwidth(),
1✔
6620
            (NUM_BANDWIDTH_POINTS as f64) * 100.0
6621
        );
6622

6623
        // space some out; make sure it takes 11 seconds
6624
        let bw_stats = loop {
1✔
6625
            let mut bw_stats = NeighborStats::new(false);
1✔
6626
            let start = get_epoch_time_secs();
1✔
6627
            for _ in 0..11 {
11✔
6628
                bw_stats.add_microblocks_push(100);
11✔
6629
                sleep_ms(1001);
11✔
6630
            }
11✔
6631

6632
            let end = get_epoch_time_secs();
1✔
6633
            if end == start + 11 {
1✔
6634
                break bw_stats;
1✔
6635
            }
×
6636
        };
6637

6638
        // 100 bytes/sec
6639
        assert_eq!(bw_stats.get_microblocks_push_bandwidth(), 110.0);
1✔
6640
    }
1✔
6641

6642
    #[test]
6643
    fn test_neighbor_stats_stackerdb_push_bandwidth() {
1✔
6644
        let mut stats = NeighborStats::new(false);
1✔
6645

6646
        assert_eq!(stats.get_stackerdb_push_bandwidth(), 0.0);
1✔
6647

6648
        stats.add_stackerdb_push(100);
1✔
6649
        assert_eq!(stats.get_stackerdb_push_bandwidth(), 0.0);
1✔
6650

6651
        // this should all happen in one second
6652
        let bw_stats = loop {
1✔
6653
            let mut bw_stats = stats.clone();
1✔
6654
            let start = get_epoch_time_secs();
1✔
6655

6656
            for _ in 0..(NUM_BANDWIDTH_POINTS - 1) {
31✔
6657
                bw_stats.add_stackerdb_push(100);
31✔
6658
            }
31✔
6659

6660
            let end = get_epoch_time_secs();
1✔
6661
            if end == start {
1✔
6662
                break bw_stats;
1✔
6663
            }
×
6664
        };
6665

6666
        assert_eq!(
1✔
6667
            bw_stats.get_stackerdb_push_bandwidth(),
1✔
6668
            (NUM_BANDWIDTH_POINTS as f64) * 100.0
6669
        );
6670

6671
        // space some out; make sure it takes 11 seconds
6672
        let bw_stats = loop {
1✔
6673
            let mut bw_stats = NeighborStats::new(false);
1✔
6674
            let start = get_epoch_time_secs();
1✔
6675
            for _ in 0..11 {
11✔
6676
                bw_stats.add_stackerdb_push(100);
11✔
6677
                sleep_ms(1001);
11✔
6678
            }
11✔
6679

6680
            let end = get_epoch_time_secs();
1✔
6681
            if end == start + 11 {
1✔
6682
                break bw_stats;
1✔
6683
            }
×
6684
        };
6685

6686
        // 100 bytes/sec
6687
        assert_eq!(bw_stats.get_stackerdb_push_bandwidth(), 110.0);
1✔
6688
    }
1✔
6689

6690
    #[test]
6691
    fn test_neighbor_stats_nakamoto_block_push_bandwidth() {
1✔
6692
        let mut stats = NeighborStats::new(false);
1✔
6693

6694
        assert_eq!(stats.get_nakamoto_block_push_bandwidth(), 0.0);
1✔
6695

6696
        stats.add_nakamoto_block_push(100);
1✔
6697
        assert_eq!(stats.get_nakamoto_block_push_bandwidth(), 0.0);
1✔
6698

6699
        // this should all happen in one second
6700
        let bw_stats = loop {
1✔
6701
            let mut bw_stats = stats.clone();
1✔
6702
            let start = get_epoch_time_secs();
1✔
6703

6704
            for _ in 0..(NUM_BANDWIDTH_POINTS - 1) {
31✔
6705
                bw_stats.add_nakamoto_block_push(100);
31✔
6706
            }
31✔
6707

6708
            let end = get_epoch_time_secs();
1✔
6709
            if end == start {
1✔
6710
                break bw_stats;
1✔
6711
            }
×
6712
        };
6713

6714
        assert_eq!(
1✔
6715
            bw_stats.get_nakamoto_block_push_bandwidth(),
1✔
6716
            (NUM_BANDWIDTH_POINTS as f64) * 100.0
6717
        );
6718

6719
        // space some out; make sure it takes 11 seconds
6720
        let bw_stats = loop {
1✔
6721
            let mut bw_stats = NeighborStats::new(false);
1✔
6722
            let start = get_epoch_time_secs();
1✔
6723
            for _ in 0..11 {
11✔
6724
                bw_stats.add_nakamoto_block_push(100);
11✔
6725
                sleep_ms(1001);
11✔
6726
            }
11✔
6727

6728
            let end = get_epoch_time_secs();
1✔
6729
            if end == start + 11 {
1✔
6730
                break bw_stats;
1✔
6731
            }
×
6732
        };
6733

6734
        // 100 bytes/sec
6735
        assert_eq!(bw_stats.get_nakamoto_block_push_bandwidth(), 110.0);
1✔
6736
    }
1✔
6737

6738
    #[test]
6739
    fn test_sign_relay_forward_message() {
1✔
6740
        let conn_opts = ConnectionOptions::default();
1✔
6741
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
6742

6743
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
6744
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
6745
        )
6746
        .unwrap();
1✔
6747

6748
        let mut chain_view = BurnchainView {
1✔
6749
            burn_block_height: 12348,
1✔
6750
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
6751
            burn_stable_block_height: 12341,
1✔
6752
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
6753
            last_burn_block_hashes: HashMap::new(),
1✔
6754
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
6755
        };
1✔
6756
        chain_view.make_test_data();
1✔
6757

6758
        let test_name_1 = "sign_relay_forward_message_1";
1✔
6759
        let burnchain = testing_burnchain_config(test_name_1);
1✔
6760

6761
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, _) = make_test_chain_dbs(
1✔
6762
            test_name_1,
1✔
6763
            &burnchain,
1✔
6764
            0x9abcdef0,
1✔
6765
            12352,
1✔
6766
            UrlString::from_literal("http://peer1.com"),
1✔
6767
            &[],
1✔
6768
            &[],
1✔
6769
            DEFAULT_SERVICES,
1✔
6770
        );
1✔
6771

6772
        let net_1 = db_setup(
1✔
6773
            test_name_1,
1✔
6774
            &burnchain,
1✔
6775
            0x9abcdef0,
6776
            &mut peerdb_1,
1✔
6777
            &mut sortdb_1,
1✔
6778
            &socketaddr_1,
1✔
6779
            &chain_view,
1✔
6780
        );
6781

6782
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
6783

6784
        let mut convo_1 = ConversationP2P::new(
1✔
6785
            123,
6786
            456,
6787
            &burnchain,
1✔
6788
            &socketaddr_1,
1✔
6789
            &conn_opts,
1✔
6790
            true,
6791
            0,
6792
            StacksEpoch::unit_test_pre_2_05(0),
1✔
6793
        );
6794

6795
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
6796
        let relayers = vec![RelayData {
1✔
6797
            peer: NeighborAddress {
1✔
6798
                addrbytes: PeerAddress([0u8; 16]),
1✔
6799
                port: 123,
1✔
6800
                public_key_hash: Hash160([0u8; 20]),
1✔
6801
            },
1✔
6802
            seq: 123,
1✔
6803
        }];
1✔
6804
        let msg = convo_1
1✔
6805
            .sign_relay_message(
1✔
6806
                &local_peer_1,
1✔
6807
                &chain_view,
1✔
6808
                relayers.clone(),
1✔
6809
                payload.clone(),
1✔
6810
            )
6811
            .unwrap();
1✔
6812

6813
        let mut expected_relayers = relayers;
1✔
6814
        expected_relayers.push(RelayData {
1✔
6815
            peer: local_peer_1.to_neighbor_addr(),
1✔
6816
            seq: 0,
1✔
6817
        });
1✔
6818

6819
        assert_eq!(msg.relayers, expected_relayers);
1✔
6820

6821
        // can't insert a loop
6822
        let fail = convo_1
1✔
6823
            .sign_relay_message(
1✔
6824
                &local_peer_1,
1✔
6825
                &chain_view,
1✔
6826
                expected_relayers.clone(),
1✔
6827
                payload.clone(),
1✔
6828
            )
6829
            .unwrap_err();
1✔
6830

6831
        assert!(
1✔
6832
            matches!(fail, net_error::InvalidMessage),
1✔
6833
            "FATAL: unexpected error {fail:?}"
6834
        );
6835

6836
        // can't forward with a loop either
6837
        let fail = convo_1
1✔
6838
            .sign_and_forward(
1✔
6839
                &local_peer_1,
1✔
6840
                &chain_view,
1✔
6841
                expected_relayers.clone(),
1✔
6842
                payload,
1✔
6843
            )
6844
            .unwrap_err();
1✔
6845

6846
        assert!(
1✔
6847
            matches!(fail, net_error::InvalidMessage),
1✔
6848
            "FATAL: unexpected error {fail:?}"
6849
        );
6850
    }
1✔
6851

6852
    #[test]
6853
    fn test_sign_and_forward() {
1✔
6854
        let conn_opts = ConnectionOptions::default();
1✔
6855
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
6856

6857
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
6858
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
6859
        )
6860
        .unwrap();
1✔
6861

6862
        let mut chain_view = BurnchainView {
1✔
6863
            burn_block_height: 12348,
1✔
6864
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
6865
            burn_stable_block_height: 12341,
1✔
6866
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
6867
            last_burn_block_hashes: HashMap::new(),
1✔
6868
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
6869
        };
1✔
6870
        chain_view.make_test_data();
1✔
6871

6872
        let test_name_1 = "sign_and_forward_1";
1✔
6873
        let burnchain = testing_burnchain_config(test_name_1);
1✔
6874

6875
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, _) = make_test_chain_dbs(
1✔
6876
            test_name_1,
1✔
6877
            &burnchain,
1✔
6878
            0x9abcdef0,
1✔
6879
            12352,
1✔
6880
            UrlString::from_literal("http://peer1.com"),
1✔
6881
            &[],
1✔
6882
            &[],
1✔
6883
            DEFAULT_SERVICES,
1✔
6884
        );
1✔
6885

6886
        let net_1 = db_setup(
1✔
6887
            test_name_1,
1✔
6888
            &burnchain,
1✔
6889
            0x9abcdef0,
6890
            &mut peerdb_1,
1✔
6891
            &mut sortdb_1,
1✔
6892
            &socketaddr_1,
1✔
6893
            &chain_view,
1✔
6894
        );
6895

6896
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
6897

6898
        let mut convo_1 = ConversationP2P::new(
1✔
6899
            123,
6900
            456,
6901
            &burnchain,
1✔
6902
            &socketaddr_1,
1✔
6903
            &conn_opts,
1✔
6904
            true,
6905
            0,
6906
            StacksEpoch::unit_test_pre_2_05(0),
1✔
6907
        );
6908

6909
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
6910

6911
        // should succeed
6912
        convo_1
1✔
6913
            .sign_and_forward(&local_peer_1, &chain_view, vec![], payload)
1✔
6914
            .unwrap();
1✔
6915
    }
1✔
6916

6917
    #[test]
6918
    fn test_validate_block_push() {
1✔
6919
        let mut conn_opts = ConnectionOptions::default();
1✔
6920
        conn_opts.max_block_push_bandwidth = 100;
1✔
6921

6922
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
6923

6924
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
6925
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
6926
        )
6927
        .unwrap();
1✔
6928

6929
        let mut chain_view = BurnchainView {
1✔
6930
            burn_block_height: 12348,
1✔
6931
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
6932
            burn_stable_block_height: 12341,
1✔
6933
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
6934
            last_burn_block_hashes: HashMap::new(),
1✔
6935
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
6936
        };
1✔
6937
        chain_view.make_test_data();
1✔
6938

6939
        let test_name_1 = "validate_block_push_1";
1✔
6940
        let burnchain = testing_burnchain_config(test_name_1);
1✔
6941

6942
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, _) = make_test_chain_dbs(
1✔
6943
            test_name_1,
1✔
6944
            &burnchain,
1✔
6945
            0x9abcdef0,
1✔
6946
            12352,
1✔
6947
            UrlString::from_literal("http://peer1.com"),
1✔
6948
            &[],
1✔
6949
            &[],
1✔
6950
            DEFAULT_SERVICES,
1✔
6951
        );
1✔
6952

6953
        let net_1 = db_setup(
1✔
6954
            test_name_1,
1✔
6955
            &burnchain,
1✔
6956
            0x9abcdef0,
6957
            &mut peerdb_1,
1✔
6958
            &mut sortdb_1,
1✔
6959
            &socketaddr_1,
1✔
6960
            &chain_view,
1✔
6961
        );
6962

6963
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
6964

6965
        let mut convo_1 = ConversationP2P::new(
1✔
6966
            123,
6967
            456,
6968
            &burnchain,
1✔
6969
            &socketaddr_1,
1✔
6970
            &conn_opts,
1✔
6971
            true,
6972
            0,
6973
            StacksEpoch::unit_test_pre_2_05(0),
1✔
6974
        );
6975

6976
        // NOTE: payload can be anything since we only look at premable length here
6977
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
6978

6979
        // bad message -- got bad relayers (cycle)
6980
        let bad_relayers = vec![
1✔
6981
            RelayData {
1✔
6982
                peer: NeighborAddress {
1✔
6983
                    addrbytes: PeerAddress([0u8; 16]),
1✔
6984
                    port: 123,
1✔
6985
                    public_key_hash: Hash160([0u8; 20]),
1✔
6986
                },
1✔
6987
                seq: 123,
1✔
6988
            },
1✔
6989
            RelayData {
1✔
6990
                peer: NeighborAddress {
1✔
6991
                    addrbytes: PeerAddress([1u8; 16]),
1✔
6992
                    port: 456,
1✔
6993
                    public_key_hash: Hash160([0u8; 20]),
1✔
6994
                },
1✔
6995
                seq: 456,
1✔
6996
            },
1✔
6997
        ];
6998

6999
        let mut bad_msg = convo_1
1✔
7000
            .sign_relay_message(&local_peer_1, &chain_view, bad_relayers, payload)
1✔
7001
            .unwrap();
1✔
7002

7003
        bad_msg.preamble.payload_len = 10;
1✔
7004

7005
        let err_before = convo_1.stats.msgs_err;
1✔
7006
        let fail = convo_1
1✔
7007
            .validate_blocks_push(&net_1, &bad_msg.preamble, bad_msg.relayers.clone())
1✔
7008
            .unwrap_err();
1✔
7009
        assert!(
1✔
7010
            matches!(fail, net_error::InvalidMessage),
1✔
7011
            "FATAL: unexpected error {fail:?}"
7012
        );
7013
        assert_eq!(convo_1.stats.msgs_err, err_before + 1);
1✔
7014

7015
        // mock a second local peer with a different private key
7016
        let mut local_peer_2 = local_peer_1.clone();
1✔
7017
        local_peer_2.private_key = Secp256k1PrivateKey::random();
1✔
7018

7019
        // NOTE: payload can be anything since we only look at premable length here
7020
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
7021
        let mut msg = convo_1
1✔
7022
            .sign_relay_message(&local_peer_2, &chain_view, vec![], payload)
1✔
7023
            .unwrap();
1✔
7024

7025
        let err_before = convo_1.stats.msgs_err;
1✔
7026

7027
        // succeeds because it's the first sample
7028
        msg.preamble.payload_len = 106;
1✔
7029
        assert!(convo_1
1✔
7030
            .validate_blocks_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7031
            .unwrap()
1✔
7032
            .is_none());
1✔
7033
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7034

7035
        // fails because the second sample says we're over bandwidth
7036
        msg.preamble.payload_len = 106;
1✔
7037
        assert!(convo_1
1✔
7038
            .validate_blocks_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7039
            .unwrap()
1✔
7040
            .is_some());
1✔
7041
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7042
    }
1✔
7043

7044
    #[test]
7045
    fn test_validate_transaction_push() {
1✔
7046
        let mut conn_opts = ConnectionOptions::default();
1✔
7047
        conn_opts.max_transaction_push_bandwidth = 100;
1✔
7048

7049
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
7050

7051
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
7052
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
7053
        )
7054
        .unwrap();
1✔
7055

7056
        let mut chain_view = BurnchainView {
1✔
7057
            burn_block_height: 12348,
1✔
7058
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
7059
            burn_stable_block_height: 12341,
1✔
7060
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
7061
            last_burn_block_hashes: HashMap::new(),
1✔
7062
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
7063
        };
1✔
7064
        chain_view.make_test_data();
1✔
7065

7066
        let test_name_1 = "validate_transaction_push_1";
1✔
7067
        let burnchain = testing_burnchain_config(test_name_1);
1✔
7068

7069
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, _) = make_test_chain_dbs(
1✔
7070
            test_name_1,
1✔
7071
            &burnchain,
1✔
7072
            0x9abcdef0,
1✔
7073
            12352,
1✔
7074
            UrlString::from_literal("http://peer1.com"),
1✔
7075
            &[],
1✔
7076
            &[],
1✔
7077
            DEFAULT_SERVICES,
1✔
7078
        );
1✔
7079

7080
        let net_1 = db_setup(
1✔
7081
            test_name_1,
1✔
7082
            &burnchain,
1✔
7083
            0x9abcdef0,
7084
            &mut peerdb_1,
1✔
7085
            &mut sortdb_1,
1✔
7086
            &socketaddr_1,
1✔
7087
            &chain_view,
1✔
7088
        );
7089

7090
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
7091

7092
        let mut convo_1 = ConversationP2P::new(
1✔
7093
            123,
7094
            456,
7095
            &burnchain,
1✔
7096
            &socketaddr_1,
1✔
7097
            &conn_opts,
1✔
7098
            true,
7099
            0,
7100
            StacksEpoch::unit_test_pre_2_05(0),
1✔
7101
        );
7102

7103
        // NOTE: payload can be anything since we only look at premable length here
7104
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
7105

7106
        // bad message -- got bad relayers (cycle)
7107
        let bad_relayers = vec![
1✔
7108
            RelayData {
1✔
7109
                peer: NeighborAddress {
1✔
7110
                    addrbytes: PeerAddress([0u8; 16]),
1✔
7111
                    port: 123,
1✔
7112
                    public_key_hash: Hash160([0u8; 20]),
1✔
7113
                },
1✔
7114
                seq: 123,
1✔
7115
            },
1✔
7116
            RelayData {
1✔
7117
                peer: NeighborAddress {
1✔
7118
                    addrbytes: PeerAddress([1u8; 16]),
1✔
7119
                    port: 456,
1✔
7120
                    public_key_hash: Hash160([0u8; 20]),
1✔
7121
                },
1✔
7122
                seq: 456,
1✔
7123
            },
1✔
7124
        ];
7125

7126
        let mut bad_msg = convo_1
1✔
7127
            .sign_relay_message(&local_peer_1, &chain_view, bad_relayers, payload)
1✔
7128
            .unwrap();
1✔
7129

7130
        bad_msg.preamble.payload_len = 10;
1✔
7131

7132
        let err_before = convo_1.stats.msgs_err;
1✔
7133
        let fail = convo_1
1✔
7134
            .validate_transaction_push(&net_1, &bad_msg.preamble, bad_msg.relayers.clone())
1✔
7135
            .unwrap_err();
1✔
7136
        assert!(
1✔
7137
            matches!(fail, net_error::InvalidMessage),
1✔
7138
            "Wrong error {fail:?}"
7139
        );
7140
        assert_eq!(convo_1.stats.msgs_err, err_before + 1);
1✔
7141

7142
        // mock a second local peer with a different private key
7143
        let mut local_peer_2 = local_peer_1.clone();
1✔
7144
        local_peer_2.private_key = Secp256k1PrivateKey::random();
1✔
7145

7146
        // NOTE: payload can be anything since we only look at premable length here
7147
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
7148
        let mut msg = convo_1
1✔
7149
            .sign_relay_message(&local_peer_2, &chain_view, vec![], payload)
1✔
7150
            .unwrap();
1✔
7151

7152
        let err_before = convo_1.stats.msgs_err;
1✔
7153

7154
        // succeeds because it's the first sample
7155
        msg.preamble.payload_len = 106;
1✔
7156
        assert!(convo_1
1✔
7157
            .validate_transaction_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7158
            .unwrap()
1✔
7159
            .is_none());
1✔
7160
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7161

7162
        // fails because the second sample says we're over bandwidth
7163
        msg.preamble.payload_len = 106;
1✔
7164
        assert!(convo_1
1✔
7165
            .validate_transaction_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7166
            .unwrap()
1✔
7167
            .is_some());
1✔
7168
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7169
    }
1✔
7170

7171
    #[test]
7172
    fn test_validate_microblocks_push() {
1✔
7173
        let mut conn_opts = ConnectionOptions::default();
1✔
7174
        conn_opts.max_microblocks_push_bandwidth = 100;
1✔
7175

7176
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
7177

7178
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
7179
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
7180
        )
7181
        .unwrap();
1✔
7182

7183
        let mut chain_view = BurnchainView {
1✔
7184
            burn_block_height: 12348,
1✔
7185
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
7186
            burn_stable_block_height: 12341,
1✔
7187
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
7188
            last_burn_block_hashes: HashMap::new(),
1✔
7189
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
7190
        };
1✔
7191
        chain_view.make_test_data();
1✔
7192

7193
        let test_name_1 = "validate_microblocks_push_1";
1✔
7194
        let burnchain = testing_burnchain_config(test_name_1);
1✔
7195

7196
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, _) = make_test_chain_dbs(
1✔
7197
            test_name_1,
1✔
7198
            &burnchain,
1✔
7199
            0x9abcdef0,
1✔
7200
            12352,
1✔
7201
            UrlString::from_literal("http://peer1.com"),
1✔
7202
            &[],
1✔
7203
            &[],
1✔
7204
            DEFAULT_SERVICES,
1✔
7205
        );
1✔
7206

7207
        let net_1 = db_setup(
1✔
7208
            test_name_1,
1✔
7209
            &burnchain,
1✔
7210
            0x9abcdef0,
7211
            &mut peerdb_1,
1✔
7212
            &mut sortdb_1,
1✔
7213
            &socketaddr_1,
1✔
7214
            &chain_view,
1✔
7215
        );
7216

7217
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
7218

7219
        let mut convo_1 = ConversationP2P::new(
1✔
7220
            123,
7221
            456,
7222
            &burnchain,
1✔
7223
            &socketaddr_1,
1✔
7224
            &conn_opts,
1✔
7225
            true,
7226
            0,
7227
            StacksEpoch::unit_test_pre_2_05(0),
1✔
7228
        );
7229

7230
        // NOTE: payload can be anything since we only look at premable length here
7231
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
7232

7233
        // bad message -- got bad relayers (cycle)
7234
        let bad_relayers = vec![
1✔
7235
            RelayData {
1✔
7236
                peer: NeighborAddress {
1✔
7237
                    addrbytes: PeerAddress([0u8; 16]),
1✔
7238
                    port: 123,
1✔
7239
                    public_key_hash: Hash160([0u8; 20]),
1✔
7240
                },
1✔
7241
                seq: 123,
1✔
7242
            },
1✔
7243
            RelayData {
1✔
7244
                peer: NeighborAddress {
1✔
7245
                    addrbytes: PeerAddress([1u8; 16]),
1✔
7246
                    port: 456,
1✔
7247
                    public_key_hash: Hash160([0u8; 20]),
1✔
7248
                },
1✔
7249
                seq: 456,
1✔
7250
            },
1✔
7251
        ];
7252

7253
        let mut bad_msg = convo_1
1✔
7254
            .sign_relay_message(&local_peer_1, &chain_view, bad_relayers, payload)
1✔
7255
            .unwrap();
1✔
7256

7257
        bad_msg.preamble.payload_len = 10;
1✔
7258

7259
        let err_before = convo_1.stats.msgs_err;
1✔
7260
        let fail = convo_1
1✔
7261
            .validate_microblocks_push(&net_1, &bad_msg.preamble, bad_msg.relayers.clone())
1✔
7262
            .unwrap_err();
1✔
7263
        assert!(
1✔
7264
            matches!(fail, net_error::InvalidMessage),
1✔
7265
            "Wrong error {fail:?}"
7266
        );
7267
        assert_eq!(convo_1.stats.msgs_err, err_before + 1);
1✔
7268

7269
        // mock a second local peer with a different private key
7270
        let mut local_peer_2 = local_peer_1.clone();
1✔
7271
        local_peer_2.private_key = Secp256k1PrivateKey::random();
1✔
7272

7273
        // NOTE: payload can be anything since we only look at premable length here
7274
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
7275
        let mut msg = convo_1
1✔
7276
            .sign_relay_message(&local_peer_2, &chain_view, vec![], payload)
1✔
7277
            .unwrap();
1✔
7278

7279
        let err_before = convo_1.stats.msgs_err;
1✔
7280

7281
        // succeeds because it's the first sample
7282
        msg.preamble.payload_len = 106;
1✔
7283
        assert!(convo_1
1✔
7284
            .validate_microblocks_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7285
            .unwrap()
1✔
7286
            .is_none());
1✔
7287
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7288

7289
        // fails because the second sample says we're over bandwidth
7290
        msg.preamble.payload_len = 106;
1✔
7291
        assert!(convo_1
1✔
7292
            .validate_microblocks_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7293
            .unwrap()
1✔
7294
            .is_some());
1✔
7295
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7296
    }
1✔
7297

7298
    #[test]
7299
    fn test_validate_stackerdb_push() {
1✔
7300
        let mut conn_opts = ConnectionOptions::default();
1✔
7301
        conn_opts.max_stackerdb_push_bandwidth = 100;
1✔
7302

7303
        let socketaddr_1 = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 8081);
1✔
7304

7305
        let first_burn_hash = BurnchainHeaderHash::from_hex(
1✔
7306
            "0000000000000000000000000000000000000000000000000000000000000000",
1✔
7307
        )
7308
        .unwrap();
1✔
7309

7310
        let mut chain_view = BurnchainView {
1✔
7311
            burn_block_height: 12348,
1✔
7312
            burn_block_hash: BurnchainHeaderHash([0x11; 32]),
1✔
7313
            burn_stable_block_height: 12341,
1✔
7314
            burn_stable_block_hash: BurnchainHeaderHash([0x22; 32]),
1✔
7315
            last_burn_block_hashes: HashMap::new(),
1✔
7316
            rc_consensus_hash: ConsensusHash([0x33; 20]),
1✔
7317
        };
1✔
7318
        chain_view.make_test_data();
1✔
7319

7320
        let test_name_1 = "validate_stackerdb_push_1";
1✔
7321
        let burnchain = testing_burnchain_config(test_name_1);
1✔
7322

7323
        let (mut peerdb_1, mut sortdb_1, stackerdbs_1, pox_id_1, _) = make_test_chain_dbs(
1✔
7324
            test_name_1,
1✔
7325
            &burnchain,
1✔
7326
            0x9abcdef0,
1✔
7327
            12352,
1✔
7328
            UrlString::from_literal("http://peer1.com"),
1✔
7329
            &[],
1✔
7330
            &[],
1✔
7331
            DEFAULT_SERVICES,
1✔
7332
        );
1✔
7333

7334
        let net_1 = db_setup(
1✔
7335
            test_name_1,
1✔
7336
            &burnchain,
1✔
7337
            0x9abcdef0,
7338
            &mut peerdb_1,
1✔
7339
            &mut sortdb_1,
1✔
7340
            &socketaddr_1,
1✔
7341
            &chain_view,
1✔
7342
        );
7343

7344
        let local_peer_1 = PeerDB::get_local_peer(peerdb_1.conn()).unwrap();
1✔
7345

7346
        let mut convo_1 = ConversationP2P::new(
1✔
7347
            123,
7348
            456,
7349
            &burnchain,
1✔
7350
            &socketaddr_1,
1✔
7351
            &conn_opts,
1✔
7352
            true,
7353
            0,
7354
            StacksEpoch::unit_test_pre_2_05(0),
1✔
7355
        );
7356

7357
        // NOTE: payload can be anything since we only look at premable length here
7358
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
7359

7360
        // bad message -- got bad relayers (cycle)
7361
        let bad_relayers = vec![
1✔
7362
            RelayData {
1✔
7363
                peer: NeighborAddress {
1✔
7364
                    addrbytes: PeerAddress([0u8; 16]),
1✔
7365
                    port: 123,
1✔
7366
                    public_key_hash: Hash160([0u8; 20]),
1✔
7367
                },
1✔
7368
                seq: 123,
1✔
7369
            },
1✔
7370
            RelayData {
1✔
7371
                peer: NeighborAddress {
1✔
7372
                    addrbytes: PeerAddress([1u8; 16]),
1✔
7373
                    port: 456,
1✔
7374
                    public_key_hash: Hash160([0u8; 20]),
1✔
7375
                },
1✔
7376
                seq: 456,
1✔
7377
            },
1✔
7378
        ];
7379

7380
        let mut bad_msg = convo_1
1✔
7381
            .sign_relay_message(&local_peer_1, &chain_view, bad_relayers, payload)
1✔
7382
            .unwrap();
1✔
7383

7384
        bad_msg.preamble.payload_len = 10;
1✔
7385

7386
        let err_before = convo_1.stats.msgs_err;
1✔
7387
        let fail = convo_1
1✔
7388
            .validate_stackerdb_push(&net_1, &bad_msg.preamble, bad_msg.relayers.clone())
1✔
7389
            .unwrap_err();
1✔
7390
        assert!(
1✔
7391
            matches!(fail, net_error::InvalidMessage),
1✔
7392
            "Wrong error {fail:?}"
7393
        );
7394
        assert_eq!(convo_1.stats.msgs_err, err_before + 1);
1✔
7395

7396
        // mock a second local peer with a different private key
7397
        let mut local_peer_2 = local_peer_1.clone();
1✔
7398
        local_peer_2.private_key = Secp256k1PrivateKey::random();
1✔
7399

7400
        // NOTE: payload can be anything since we only look at premable length here
7401
        let payload = StacksMessageType::Nack(NackData { error_code: 123 });
1✔
7402
        let mut msg = convo_1
1✔
7403
            .sign_relay_message(&local_peer_2, &chain_view, vec![], payload)
1✔
7404
            .unwrap();
1✔
7405

7406
        let err_before = convo_1.stats.msgs_err;
1✔
7407

7408
        // succeeds because it's the first sample
7409
        msg.preamble.payload_len = 106;
1✔
7410
        assert!(convo_1
1✔
7411
            .validate_stackerdb_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7412
            .unwrap()
1✔
7413
            .is_none());
1✔
7414
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7415

7416
        // fails because the second sample says we're over bandwidth
7417
        msg.preamble.payload_len = 106;
1✔
7418
        assert!(convo_1
1✔
7419
            .validate_stackerdb_push(&net_1, &msg.preamble, msg.relayers.clone())
1✔
7420
            .unwrap()
1✔
7421
            .is_some());
1✔
7422
        assert_eq!(convo_1.stats.msgs_err, err_before);
1✔
7423
    }
1✔
7424
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc