• 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

77.98
/stackslib/src/net/mod.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
use std::collections::{HashMap, HashSet};
17
use std::hash::{Hash, Hasher};
18
use std::io::{Read, Write};
19
use std::net::{IpAddr, SocketAddr};
20
use std::{error, fmt, io};
21

22
use clarity::vm::errors::{ClarityTypeError, VmExecutionError};
23
use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier};
24
use libstackerdb::{Error as libstackerdb_error, StackerDBChunkData};
25
use p2p::{DropReason, DropSource};
26
use serde::{Deserialize, Serialize};
27
use stacks_common::bitvec::BitVec;
28
use stacks_common::codec::{Error as codec_error, StacksMessageCodec};
29
use stacks_common::types::chainstate::{
30
    BlockHeaderHash, BurnchainHeaderHash, PoxId, StacksAddress, StacksBlockId,
31
};
32
use stacks_common::types::net::{Error as AddrError, PeerAddress, PeerHost};
33
use stacks_common::types::StacksPublicKeyBuffer;
34
use stacks_common::util::get_epoch_time_secs;
35
use stacks_common::util::hash::{Hash160, Sha256Sum};
36
use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PublicKey};
37
use url;
38

39
use self::dns::*;
40
use crate::burnchains::{Error as burnchain_error, Txid};
41
use crate::chainstate::burn::db::sortdb::SortitionDB;
42
use crate::chainstate::burn::ConsensusHash;
43
use crate::chainstate::coordinator::comm::CoordinatorChannels;
44
use crate::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState};
45
use crate::chainstate::stacks::db::StacksChainState;
46
use crate::chainstate::stacks::index::Error as marf_error;
47
use crate::chainstate::stacks::{
48
    Error as chainstate_error, Error as chain_error, StacksBlock, StacksMicroblock,
49
    StacksPublicKey, StacksTransaction,
50
};
51
use crate::clarity_vm::clarity::ClarityError;
52
use crate::core::mempool::*;
53
use crate::cost_estimates::metrics::CostMetric;
54
use crate::cost_estimates::{CostEstimator, FeeEstimator};
55
use crate::net::atlas::{Attachment, AttachmentInstance};
56
use crate::net::http::error::{HttpNotFound, HttpServerError};
57
use crate::net::http::{Error as HttpErr, HttpRequestContents, HttpRequestPreamble};
58
use crate::net::httpcore::{
59
    HttpRequestContentsExtensions as _, StacksHttp, StacksHttpRequest, StacksHttpResponse,
60
    TipRequest,
61
};
62
use crate::net::p2p::{PeerNetwork, PendingMessages};
63
use crate::util_lib::db::{DBConn, Error as db_error};
64
use crate::util_lib::strings::UrlString;
65

66
/// Implements RPC API
67
pub mod api;
68
/// Implements `ASEntry4` object, which is used in db.rs to store the AS number of an IP address.
69
pub mod asn;
70
/// Implements the Atlas network. This network uses the infrastructure created in `src/net` to
71
/// discover peers, query attachment inventories, and download attachments.
72
pub mod atlas;
73
/// Implements the `ConversationP2P` object, a host-to-host session abstraction which allows
74
/// the node to recieve `StacksMessage` instances. The downstream consumer of this API is `PeerNetwork`.
75
/// To use OSI terminology, this module implements the session & presentation layers of the P2P network.
76
/// Other functionality includes (but is not limited to):
77
///     * set up & tear down of sessions
78
///     * dealing with and responding to invalid messages
79
///     * rate limiting messages
80
pub mod chat;
81
/// Implements serialization and deserialization for `StacksMessage` types.
82
/// Also has functionality to sign, verify, and ensure well-formedness of messages.
83
pub mod codec;
84
pub mod connection;
85
pub mod db;
86
/// Implements `DNSResolver`, a simple DNS resolver state machine. Also implements `DNSClient`,
87
/// which serves as an API for `DNSResolver`.
88
pub mod dns;
89
pub mod download;
90
pub mod http;
91
/// Links http crate to Stacks
92
pub mod httpcore;
93
pub mod inv;
94
pub mod mempool;
95
pub mod neighbors;
96
pub mod p2p;
97
/// Implements wrapper around `mio` crate, which itself is a wrapper around Linux's `epoll(2)` syscall.
98
/// Creates a pollable interface for sockets, and provides an API for registering and deregistering
99
/// sockets. This is used to control how many sockets are allocated for the two network servers: the
100
/// p2p server and the http server.
101
pub mod poll;
102
pub mod prune;
103
pub mod relay;
104
pub mod rpc;
105
pub mod server;
106
pub mod stackerdb;
107
pub mod unsolicited;
108

109
pub use crate::net::neighbors::{NeighborComms, PeerNetworkComms};
110
use crate::net::stackerdb::{StackerDBConfig, StackerDBSyncResult};
111

112
#[cfg(test)]
113
pub mod tests;
114

115
#[derive(Debug)]
116
pub enum Error {
117
    /// Failed to encode
118
    SerializeError(String),
119
    /// Failed to read
120
    ReadError(io::Error),
121
    /// Failed to decode
122
    DeserializeError(String),
123
    /// Failed to write
124
    WriteError(io::Error),
125
    /// Underflow -- not enough bytes to form the message
126
    UnderflowError(String),
127
    /// Overflow -- message too big
128
    OverflowError(String),
129
    /// Wrong protocol family
130
    WrongProtocolFamily,
131
    /// Array is too big
132
    ArrayTooLong,
133
    /// Receive timed out
134
    RecvTimeout,
135
    /// Error signing a message
136
    SigningError(String),
137
    /// Error verifying a message
138
    VerifyingError(String),
139
    /// Read stream is drained.  Try again
140
    TemporarilyDrained,
141
    /// Read stream has reached EOF (socket closed, end-of-file reached, etc.)
142
    PermanentlyDrained,
143
    /// Failed to read from the FS
144
    FilesystemError,
145
    /// Database error
146
    DBError(db_error),
147
    /// Socket mutex was poisoned
148
    SocketMutexPoisoned,
149
    /// Socket not instantiated
150
    SocketNotConnectedToPeer,
151
    /// Not connected to peer
152
    ConnectionBroken,
153
    /// Connection could not be (re-)established
154
    ConnectionError,
155
    /// Too many outgoing messages
156
    OutboxOverflow,
157
    /// Too many incoming messages
158
    InboxOverflow,
159
    /// Send error
160
    SendError(String),
161
    /// Recv error
162
    RecvError(String),
163
    /// Invalid message
164
    InvalidMessage,
165
    /// Invalid network handle
166
    InvalidHandle,
167
    /// Network handle is full
168
    FullHandle,
169
    /// Invalid handshake
170
    InvalidHandshake,
171
    /// Stale neighbor
172
    StaleNeighbor,
173
    /// No such neighbor
174
    NoSuchNeighbor,
175
    /// Failed to bind
176
    BindError,
177
    /// Failed to poll
178
    PollError,
179
    /// Failed to accept
180
    AcceptError,
181
    /// Failed to register socket with poller
182
    RegisterError,
183
    /// Failed to query socket metadata
184
    SocketError,
185
    /// server is not bound to a socket
186
    NotConnected,
187
    /// Remote peer is not connected
188
    PeerNotConnected(String),
189
    /// Too many peers
190
    TooManyPeers,
191
    /// Peer already connected
192
    AlreadyConnected(usize, NeighborKey),
193
    /// Message already in progress
194
    InProgress,
195
    /// Peer is denied
196
    Denied,
197
    /// Data URL is not known
198
    NoDataUrl,
199
    /// Peer is transmitting too fast
200
    PeerThrottled,
201
    /// Error resolving a DNS name
202
    LookupError(String),
203
    /// MARF error, percolated up from chainstate
204
    MARFError(marf_error),
205
    /// Clarity VM error, percolated up from chainstate
206
    ClarityError(ClarityError),
207
    /// Clarity type manipulation error that occurred outside of VM execution
208
    ClarityTypeError(ClarityTypeError),
209
    /// Catch-all for chainstate errors that don't map cleanly into network errors
210
    ChainstateError(String),
211
    /// Coordinator hung up
212
    CoordinatorClosed,
213
    /// view of state is stale (e.g. from the sortition db)
214
    StaleView,
215
    /// Tried to connect to myself
216
    ConnectionCycle,
217
    /// Requested data not found
218
    NotFoundError,
219
    /// Transient error (akin to EAGAIN)
220
    Transient(String),
221
    /// Expected end-of-stream, but had more data
222
    ExpectedEndOfStream,
223
    /// burnchain error
224
    BurnchainError(burnchain_error),
225
    /// chunk is stale
226
    StaleChunk {
227
        supplied_version: u32,
228
        latest_version: u32,
229
    },
230
    /// no such slot
231
    NoSuchSlot(QualifiedContractIdentifier, u32),
232
    /// no such DB
233
    NoSuchStackerDB(QualifiedContractIdentifier),
234
    /// stacker DB exists
235
    StackerDBExists(QualifiedContractIdentifier),
236
    /// slot signer is wrong
237
    BadSlotSigner(StacksAddress, u32),
238
    /// too many writes to a slot
239
    TooManySlotWrites {
240
        supplied_version: u32,
241
        max_writes: u32,
242
    },
243
    /// too frequent writes to a slot
244
    TooFrequentSlotWrites(u64),
245
    /// Invalid control smart contract for a Stacker DB
246
    InvalidStackerDBContract(QualifiedContractIdentifier, String),
247
    /// state machine step took too long
248
    StepTimeout,
249
    /// stacker DB chunk is too big
250
    StackerDBChunkTooBig(usize),
251
    /// HTTP error
252
    Http(HttpErr),
253
    /// Invalid state machine state reached
254
    InvalidState,
255
    /// Waiting for DNS resolution
256
    WaitingForDNS,
257
    /// No reward set for given reward cycle
258
    NoPoXRewardSet(u64),
259
}
260

261
impl From<libstackerdb_error> for Error {
262
    fn from(e: libstackerdb_error) -> Self {
×
263
        match e {
×
264
            libstackerdb_error::SigningError(s) => Error::SigningError(s),
×
265
            libstackerdb_error::VerifyingError(s) => Error::VerifyingError(s),
×
266
        }
267
    }
×
268
}
269

270
impl From<stacks_codec::transaction::AuthError> for Error {
271
    fn from(e: stacks_codec::transaction::AuthError) -> Self {
10✔
272
        use stacks_codec::transaction::AuthError;
273
        match e {
10✔
274
            AuthError::SigningError(s) => Error::SigningError(s),
×
275
            AuthError::VerifyingError(s) => Error::VerifyingError(s),
10✔
276
            AuthError::IncompatibleSpendingConditionError => Error::SerializeError(
×
277
                "Spending condition is incompatible with this operation".to_string(),
×
278
            ),
×
279
        }
280
    }
10✔
281
}
282

283
impl From<codec_error> for Error {
284
    fn from(e: codec_error) -> Self {
11,890,680✔
285
        match e {
11,890,680✔
286
            codec_error::SerializeError(s) => Error::SerializeError(s),
×
287
            codec_error::ReadError(e) => Error::ReadError(e),
×
288
            codec_error::DeserializeError(s) => Error::DeserializeError(s),
1✔
289
            codec_error::WriteError(e) => Error::WriteError(e),
×
290
            codec_error::UnderflowError(s) => Error::UnderflowError(s),
11,890,679✔
291
            codec_error::OverflowError(s) => Error::OverflowError(s),
×
292
            codec_error::ArrayTooLong => Error::ArrayTooLong,
×
293
            codec_error::SigningError(s) => Error::SigningError(s),
×
294
            codec_error::GenericError(_) => Error::InvalidMessage,
×
295
        }
296
    }
11,890,680✔
297
}
298

299
impl From<HttpErr> for Error {
300
    fn from(e: HttpErr) -> Error {
55✔
301
        Error::Http(e)
55✔
302
    }
55✔
303
}
304

305
impl From<AddrError> for Error {
306
    fn from(e: AddrError) -> Error {
×
307
        match e {
×
308
            AddrError::DecodeError(s) => Error::DeserializeError(s),
×
309
        }
310
    }
×
311
}
312

313
impl fmt::Display for Error {
314
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77,845✔
315
        match self {
77,845✔
316
            Error::SerializeError(ref s) => fmt::Display::fmt(s, f),
×
317
            Error::DeserializeError(ref s) => fmt::Display::fmt(s, f),
1✔
318
            Error::ReadError(ref io) => fmt::Display::fmt(io, f),
1✔
319
            Error::WriteError(ref io) => fmt::Display::fmt(io, f),
×
320
            Error::UnderflowError(ref s) => fmt::Display::fmt(s, f),
×
321
            Error::OverflowError(ref s) => fmt::Display::fmt(s, f),
×
322
            Error::WrongProtocolFamily => write!(f, "Improper use of protocol family"),
×
323
            Error::ArrayTooLong => write!(f, "Array too long"),
×
324
            Error::RecvTimeout => write!(f, "Packet receive timeout"),
18✔
325
            Error::SigningError(ref s) => fmt::Display::fmt(s, f),
×
326
            Error::VerifyingError(ref s) => fmt::Display::fmt(s, f),
×
327
            Error::TemporarilyDrained => {
328
                write!(f, "Temporarily out of bytes to read; try again later")
×
329
            }
330
            Error::PermanentlyDrained => write!(f, "Out of bytes to read"),
×
331
            Error::FilesystemError => write!(f, "Disk I/O error"),
×
332
            Error::DBError(ref e) => fmt::Display::fmt(e, f),
×
333
            Error::SocketMutexPoisoned => write!(f, "socket mutex was poisoned"),
×
334
            Error::SocketNotConnectedToPeer => write!(f, "not connected to peer"),
×
335
            Error::ConnectionBroken => write!(f, "connection to peer node is broken"),
×
336
            Error::ConnectionError => write!(f, "connection to peer could not be (re-)established"),
×
337
            Error::OutboxOverflow => write!(f, "too many outgoing messages queued"),
×
338
            Error::InboxOverflow => write!(f, "too many messages pending"),
×
339
            Error::SendError(ref s) => fmt::Display::fmt(s, f),
63,145✔
340
            Error::RecvError(ref s) => fmt::Display::fmt(s, f),
×
341
            Error::InvalidMessage => write!(f, "invalid message (malformed or bad signature)"),
9✔
342
            Error::InvalidHandle => write!(f, "invalid network handle"),
×
343
            Error::FullHandle => write!(f, "network handle is full and needs to be drained"),
×
344
            Error::InvalidHandshake => write!(f, "invalid handshake from remote peer"),
×
345
            Error::StaleNeighbor => write!(f, "neighbor is too far behind the chain tip"),
×
346
            Error::NoSuchNeighbor => write!(f, "no such neighbor"),
×
347
            Error::BindError => write!(f, "Failed to bind to the given address"),
×
348
            Error::PollError => write!(f, "Failed to poll"),
×
349
            Error::AcceptError => write!(f, "Failed to accept connection"),
×
350
            Error::RegisterError => write!(f, "Failed to register socket with poller"),
×
351
            Error::SocketError => write!(f, "Socket error"),
×
352
            Error::NotConnected => write!(f, "Not connected to peer network"),
×
353
            Error::PeerNotConnected(ref msg) => {
13,742✔
354
                write!(f, "Remote peer is not connected to us: {}", msg)
13,742✔
355
            }
356
            Error::TooManyPeers => write!(f, "Too many peer connections open"),
×
357
            Error::AlreadyConnected(ref _id, ref _nk) => write!(f, "Peer already connected"),
×
358
            Error::InProgress => write!(f, "Message already in progress"),
3✔
359
            Error::Denied => write!(f, "Peer is denied"),
×
360
            Error::NoDataUrl => write!(f, "No data URL available"),
×
361
            Error::PeerThrottled => write!(f, "Peer is transmitting too fast"),
×
362
            Error::LookupError(ref s) => fmt::Display::fmt(s, f),
×
363
            Error::ChainstateError(ref s) => fmt::Display::fmt(s, f),
×
364
            Error::ClarityError(ref e) => fmt::Display::fmt(e, f),
21✔
365
            Error::ClarityTypeError(ref e) => fmt::Display::fmt(e, f),
×
366
            Error::MARFError(ref e) => fmt::Display::fmt(e, f),
×
367
            Error::CoordinatorClosed => write!(f, "Coordinator hung up"),
9✔
368
            Error::StaleView => write!(f, "State view is stale"),
×
369
            Error::ConnectionCycle => write!(f, "Tried to connect to myself"),
×
370
            Error::NotFoundError => write!(f, "Requested data not found"),
×
371
            Error::Transient(ref s) => write!(f, "Transient network error: {}", s),
×
372
            Error::ExpectedEndOfStream => write!(f, "Expected end-of-stream"),
×
373
            Error::BurnchainError(ref e) => fmt::Display::fmt(e, f),
×
374
            Error::StaleChunk {
375
                supplied_version,
×
376
                latest_version,
×
377
            } => {
378
                write!(
×
379
                    f,
×
380
                    "Stale DB chunk (supplied={},latest={})",
381
                    supplied_version, latest_version
382
                )
383
            }
384
            Error::NoSuchSlot(ref addr, ref slot_id) => {
864✔
385
                write!(f, "No such DB slot ({},{})", addr, slot_id)
864✔
386
            }
387
            Error::NoSuchStackerDB(ref addr) => {
×
388
                write!(f, "No such StackerDB {}", addr)
×
389
            }
390
            Error::StackerDBExists(ref addr) => {
×
391
                write!(f, "StackerDB already exists: {}", addr)
×
392
            }
393
            Error::BadSlotSigner(ref addr, ref slot_id) => {
27✔
394
                write!(f, "Bad DB slot signer ({},{})", addr, slot_id)
27✔
395
            }
396
            Error::TooManySlotWrites {
397
                supplied_version,
×
398
                max_writes,
×
399
            } => {
400
                write!(
×
401
                    f,
×
402
                    "Too many slot writes (max={},given={})",
403
                    max_writes, supplied_version
404
                )
405
            }
406
            Error::TooFrequentSlotWrites(ref deadline) => {
×
407
                write!(f, "Too frequent slot writes (deadline={})", deadline)
×
408
            }
409
            Error::InvalidStackerDBContract(ref contract_id, ref reason) => {
×
410
                write!(
×
411
                    f,
×
412
                    "Invalid StackerDB control smart contract {}: {}",
413
                    contract_id, reason
414
                )
415
            }
416
            Error::StepTimeout => write!(f, "State-machine step took too long"),
×
417
            Error::StackerDBChunkTooBig(ref sz) => {
×
418
                write!(f, "StackerDB chunk size is too big ({})", sz)
×
419
            }
420
            Error::Http(e) => fmt::Display::fmt(&e, f),
5✔
421
            Error::InvalidState => write!(f, "Invalid state-machine state reached"),
×
422
            Error::WaitingForDNS => write!(f, "Waiting for DNS resolution"),
×
423
            Error::NoPoXRewardSet(rc) => write!(f, "No PoX reward set for cycle {}", rc),
×
424
        }
425
    }
77,845✔
426
}
427

428
impl error::Error for Error {
429
    fn cause(&self) -> Option<&dyn error::Error> {
×
430
        match *self {
×
431
            Error::SerializeError(ref _s) => None,
×
432
            Error::ReadError(ref io) => Some(io),
×
433
            Error::DeserializeError(ref _s) => None,
×
434
            Error::WriteError(ref io) => Some(io),
×
435
            Error::UnderflowError(ref _s) => None,
×
436
            Error::OverflowError(ref _s) => None,
×
437
            Error::WrongProtocolFamily => None,
×
438
            Error::ArrayTooLong => None,
×
439
            Error::RecvTimeout => None,
×
440
            Error::SigningError(ref _s) => None,
×
441
            Error::VerifyingError(ref _s) => None,
×
442
            Error::TemporarilyDrained => None,
×
443
            Error::PermanentlyDrained => None,
×
444
            Error::FilesystemError => None,
×
445
            Error::DBError(ref e) => Some(e),
×
446
            Error::SocketMutexPoisoned => None,
×
447
            Error::SocketNotConnectedToPeer => None,
×
448
            Error::ConnectionBroken => None,
×
449
            Error::ConnectionError => None,
×
450
            Error::OutboxOverflow => None,
×
451
            Error::InboxOverflow => None,
×
452
            Error::SendError(ref _s) => None,
×
453
            Error::RecvError(ref _s) => None,
×
454
            Error::InvalidMessage => None,
×
455
            Error::InvalidHandle => None,
×
456
            Error::FullHandle => None,
×
457
            Error::InvalidHandshake => None,
×
458
            Error::StaleNeighbor => None,
×
459
            Error::NoSuchNeighbor => None,
×
460
            Error::BindError => None,
×
461
            Error::PollError => None,
×
462
            Error::AcceptError => None,
×
463
            Error::RegisterError => None,
×
464
            Error::SocketError => None,
×
465
            Error::NotConnected => None,
×
466
            Error::PeerNotConnected(..) => None,
×
467
            Error::TooManyPeers => None,
×
468
            Error::AlreadyConnected(ref _id, ref _nk) => None,
×
469
            Error::InProgress => None,
×
470
            Error::Denied => None,
×
471
            Error::NoDataUrl => None,
×
472
            Error::PeerThrottled => None,
×
473
            Error::LookupError(ref _s) => None,
×
474
            Error::ChainstateError(ref _s) => None,
×
475
            Error::ClarityError(ref e) => Some(e),
×
476
            Error::ClarityTypeError(ref e) => Some(e),
×
477
            Error::MARFError(ref e) => Some(e),
×
478
            Error::CoordinatorClosed => None,
×
479
            Error::StaleView => None,
×
480
            Error::ConnectionCycle => None,
×
481
            Error::NotFoundError => None,
×
482
            Error::Transient(ref _s) => None,
×
483
            Error::ExpectedEndOfStream => None,
×
484
            Error::BurnchainError(ref e) => Some(e),
×
485
            Error::StaleChunk { .. } => None,
×
486
            Error::NoSuchSlot(..) => None,
×
487
            Error::NoSuchStackerDB(..) => None,
×
488
            Error::StackerDBExists(..) => None,
×
489
            Error::BadSlotSigner(..) => None,
×
490
            Error::TooManySlotWrites { .. } => None,
×
491
            Error::TooFrequentSlotWrites(..) => None,
×
492
            Error::InvalidStackerDBContract(..) => None,
×
493
            Error::StepTimeout => None,
×
494
            Error::StackerDBChunkTooBig(..) => None,
×
495
            Error::Http(ref e) => Some(e),
×
496
            Error::InvalidState => None,
×
497
            Error::WaitingForDNS => None,
×
498
            Error::NoPoXRewardSet(..) => None,
×
499
        }
500
    }
×
501
}
502

503
impl From<chain_error> for Error {
504
    fn from(e: chain_error) -> Error {
×
505
        match e {
×
506
            chain_error::InvalidStacksBlock(s) => {
×
507
                Error::ChainstateError(format!("Invalid stacks block: {}", s))
×
508
            }
509
            chain_error::InvalidStacksMicroblock(msg, hash) => {
×
510
                Error::ChainstateError(format!("Invalid stacks microblock {:?}: {}", hash, msg))
×
511
            }
512
            chain_error::InvalidStacksTransaction(s, _) => {
×
513
                Error::ChainstateError(format!("Invalid stacks transaction: {}", s))
×
514
            }
515
            chain_error::PostConditionFailed(s) => {
×
516
                Error::ChainstateError(format!("Postcondition failed: {}", s))
×
517
            }
518
            chain_error::ClarityError(e) => Error::ClarityError(e),
×
519
            chain_error::DBError(e) => Error::DBError(e),
×
520
            chain_error::NetError(e) => e,
×
521
            chain_error::MARFError(e) => Error::MARFError(e),
×
522
            chain_error::ReadError(e) => Error::ReadError(e),
×
523
            chain_error::WriteError(e) => Error::WriteError(e),
×
524
            _ => Error::ChainstateError(format!("Stacks chainstate error: {:?}", &e)),
×
525
        }
526
    }
×
527
}
528

529
impl From<db_error> for Error {
530
    fn from(e: db_error) -> Error {
3✔
531
        Error::DBError(e)
3✔
532
    }
3✔
533
}
534

535
impl From<rusqlite::Error> for Error {
536
    fn from(e: rusqlite::Error) -> Error {
×
537
        Error::DBError(db_error::SqliteError(e))
×
538
    }
×
539
}
540

541
impl From<burnchain_error> for Error {
542
    fn from(e: burnchain_error) -> Self {
×
543
        Error::BurnchainError(e)
×
544
    }
×
545
}
546

547
impl From<ClarityError> for Error {
548
    fn from(e: ClarityError) -> Self {
×
549
        Error::ClarityError(e)
×
550
    }
×
551
}
552

553
impl From<ClarityTypeError> for Error {
554
    fn from(e: ClarityTypeError) -> Self {
×
555
        Error::ClarityTypeError(e)
×
556
    }
×
557
}
558

559
impl From<VmExecutionError> for Error {
560
    fn from(e: VmExecutionError) -> Self {
×
561
        Error::ClarityError(e.into())
×
562
    }
×
563
}
564

565
#[cfg(test)]
566
impl PartialEq for Error {
567
    /// (make I/O errors comparable for testing purposes)
568
    fn eq(&self, other: &Self) -> bool {
53✔
569
        let s1 = format!("{:?}", self);
53✔
570
        let s2 = format!("{:?}", other);
53✔
571
        s1 == s2
53✔
572
    }
53✔
573
}
574

575
/// Extension trait for PeerHost to decode it from a UrlString
576
pub trait PeerHostExtensions {
577
    fn try_from_url(url_str: &UrlString) -> Option<PeerHost>;
578
}
579

580
impl PeerHostExtensions for PeerHost {
581
    fn try_from_url(url_str: &UrlString) -> Option<PeerHost> {
27,782✔
582
        let url = match url_str.parse_to_block_url() {
27,782✔
583
            Ok(url) => url,
27,782✔
584
            Err(_e) => {
×
585
                return None;
×
586
            }
587
        };
588

589
        let port = match url.port_or_known_default() {
27,782✔
590
            Some(port) => port,
27,782✔
591
            None => {
592
                return None;
×
593
            }
594
        };
595

596
        match url.host() {
27,782✔
597
            Some(url::Host::Domain(name)) => Some(PeerHost::DNS(name.to_string(), port)),
×
598
            Some(url::Host::Ipv4(addr)) => Some(PeerHost::from_socketaddr(&SocketAddr::new(
27,782✔
599
                IpAddr::V4(addr),
27,782✔
600
                port,
27,782✔
601
            ))),
27,782✔
602
            Some(url::Host::Ipv6(addr)) => Some(PeerHost::from_socketaddr(&SocketAddr::new(
×
603
                IpAddr::V6(addr),
×
604
                port,
×
605
            ))),
×
606
            None => None,
×
607
        }
608
    }
27,782✔
609
}
610

611
/// Runtime arguments to an RPC handler
612
#[derive(Default, Clone)]
613
pub struct RPCHandlerArgs<'a> {
614
    /// What height at which this node will terminate (testnet only)
615
    pub exit_at_block_height: Option<u64>,
616
    /// What's the hash of the genesis chainstate?
617
    pub genesis_chainstate_hash: Sha256Sum,
618
    /// event observer for the mempool
619
    pub event_observer: Option<&'a dyn MemPoolEventDispatcher>,
620
    /// tx runtime cost estimator
621
    pub cost_estimator: Option<&'a dyn CostEstimator>,
622
    /// tx fee estimator
623
    pub fee_estimator: Option<&'a dyn FeeEstimator>,
624
    /// tx runtime cost metric
625
    pub cost_metric: Option<&'a dyn CostMetric>,
626
    /// coordinator channels
627
    pub coord_comms: Option<&'a CoordinatorChannels>,
628
}
629

630
impl RPCHandlerArgs<'_> {
631
    pub fn get_estimators_ref(
400✔
632
        &self,
400✔
633
    ) -> Option<(&dyn CostEstimator, &dyn FeeEstimator, &dyn CostMetric)> {
400✔
634
        match (self.cost_estimator, self.fee_estimator, self.cost_metric) {
400✔
635
            (Some(a), Some(b), Some(c)) => Some((a, b, c)),
398✔
636
            _ => None,
2✔
637
        }
638
    }
400✔
639
}
640

641
/// Wrapper around Stacks chainstate data that an HTTP request handler might need
642
pub struct StacksNodeState<'a> {
643
    inner_network: Option<&'a mut PeerNetwork>,
644
    inner_sortdb: Option<&'a SortitionDB>,
645
    inner_chainstate: Option<&'a mut StacksChainState>,
646
    inner_mempool: Option<&'a mut MemPoolDB>,
647
    inner_rpc_args: Option<&'a RPCHandlerArgs<'a>>,
648
    relay_message: Option<StacksMessageType>,
649
    /// Are we in Initial Block Download (IBD) phase?
650
    ibd: bool,
651
    /// Are we indexing transactions?
652
    txindex: bool,
653
}
654

655
impl<'a> StacksNodeState<'a> {
656
    pub fn new(
1,209,184✔
657
        inner_network: &'a mut PeerNetwork,
1,209,184✔
658
        inner_sortdb: &'a SortitionDB,
1,209,184✔
659
        inner_chainstate: &'a mut StacksChainState,
1,209,184✔
660
        inner_mempool: &'a mut MemPoolDB,
1,209,184✔
661
        inner_rpc_args: &'a RPCHandlerArgs<'a>,
1,209,184✔
662
        ibd: bool,
1,209,184✔
663
        txindex: bool,
1,209,184✔
664
    ) -> StacksNodeState<'a> {
1,209,184✔
665
        StacksNodeState {
1,209,184✔
666
            inner_network: Some(inner_network),
1,209,184✔
667
            inner_sortdb: Some(inner_sortdb),
1,209,184✔
668
            inner_chainstate: Some(inner_chainstate),
1,209,184✔
669
            inner_mempool: Some(inner_mempool),
1,209,184✔
670
            inner_rpc_args: Some(inner_rpc_args),
1,209,184✔
671
            relay_message: None,
1,209,184✔
672
            ibd,
1,209,184✔
673
            txindex,
1,209,184✔
674
        }
1,209,184✔
675
    }
1,209,184✔
676

677
    /// Run func() with the inner state
678
    pub fn with_node_state<F, R>(&mut self, func: F) -> R
15,455,829✔
679
    where
15,455,829✔
680
        F: FnOnce(
15,455,829✔
681
            &mut PeerNetwork,
15,455,829✔
682
            &SortitionDB,
15,455,829✔
683
            &mut StacksChainState,
15,455,829✔
684
            &mut MemPoolDB,
15,455,829✔
685
            &RPCHandlerArgs<'a>,
15,455,829✔
686
        ) -> R,
15,455,829✔
687
    {
688
        let network = self
15,455,829✔
689
            .inner_network
15,455,829✔
690
            .take()
15,455,829✔
691
            .expect("FATAL: network not restored");
15,455,829✔
692
        let sortdb = self
15,455,829✔
693
            .inner_sortdb
15,455,829✔
694
            .take()
15,455,829✔
695
            .expect("FATAL: sortdb not restored");
15,455,829✔
696
        let chainstate = self
15,455,829✔
697
            .inner_chainstate
15,455,829✔
698
            .take()
15,455,829✔
699
            .expect("FATAL: chainstate not restored");
15,455,829✔
700
        let mempool = self
15,455,829✔
701
            .inner_mempool
15,455,829✔
702
            .take()
15,455,829✔
703
            .expect("FATAL: mempool not restored");
15,455,829✔
704
        let rpc_args = self
15,455,829✔
705
            .inner_rpc_args
15,455,829✔
706
            .take()
15,455,829✔
707
            .expect("FATAL: rpc args not restored");
15,455,829✔
708

709
        let res = func(network, sortdb, chainstate, mempool, rpc_args);
15,455,829✔
710

711
        self.inner_network = Some(network);
15,455,829✔
712
        self.inner_sortdb = Some(sortdb);
15,455,829✔
713
        self.inner_chainstate = Some(chainstate);
15,455,829✔
714
        self.inner_mempool = Some(mempool);
15,455,829✔
715
        self.inner_rpc_args = Some(rpc_args);
15,455,829✔
716

717
        res
15,455,829✔
718
    }
15,455,829✔
719

720
    pub fn canonical_stacks_tip_height(&mut self) -> u64 {
4,744,545✔
721
        self.with_node_state(|network, _, _, _, _| network.stacks_tip.height)
4,744,545✔
722
    }
4,744,545✔
723

724
    pub fn set_relay_message(&mut self, msg: StacksMessageType) {
701,018✔
725
        self.relay_message = Some(msg);
701,018✔
726
    }
701,018✔
727

728
    pub fn take_relay_message(&mut self) -> Option<StacksMessageType> {
6,597,584✔
729
        self.relay_message.take()
6,597,584✔
730
    }
6,597,584✔
731

732
    /// Load up the canonical Stacks chain tip.  Note that this is subject to both burn chain block
733
    /// Stacks block availability -- different nodes with different partial replicas of the Stacks chain state
734
    /// will return different values here.
735
    ///
736
    /// # Warn
737
    /// - There is a potential race condition. If this function is loading the latest unconfirmed
738
    /// tip, that tip may get invalidated by the time it is used in `maybe_read_only_clarity_tx`,
739
    /// which is used to load clarity state at a particular tip (which would lead to a 404 error).
740
    /// If this race condition occurs frequently, we can modify `maybe_read_only_clarity_tx` to
741
    /// re-load the unconfirmed chain tip. Refer to issue #2997.
742
    ///
743
    /// # Inputs
744
    /// - `tip_req` is given by the HTTP request as the optional query parameter for the chain tip
745
    /// hash.  It will be UseLatestAnchoredTip if there was no parameter given. If it is set to
746
    /// `latest`, the parameter will be set to UseLatestUnconfirmedTip.
747
    ///
748
    /// Returns the requested chain tip on success.
749
    /// If the chain tip could not be found, then it returns Err(HttpNotFound)
750
    /// If there was an error querying the DB, then it returns Err(HttpServerError)
751
    pub fn load_stacks_chain_tip(
1,890,142✔
752
        &mut self,
1,890,142✔
753
        preamble: &HttpRequestPreamble,
1,890,142✔
754
        contents: &HttpRequestContents,
1,890,142✔
755
    ) -> Result<StacksBlockId, StacksHttpResponse> {
1,890,142✔
756
        self.with_node_state(|_network, sortdb, chainstate, _mempool, _rpc_args| {
1,890,142✔
757
            let tip_req = contents.tip_request();
1,890,142✔
758
            match tip_req {
1,890,142✔
759
                TipRequest::UseLatestUnconfirmedTip => {
760
                    let unconfirmed_chain_tip_opt = match &mut chainstate.unconfirmed_state {
44✔
761
                        Some(unconfirmed_state) => {
34✔
762
                            match unconfirmed_state.get_unconfirmed_state_if_exists() {
34✔
763
                                Ok(res) => res,
34✔
764
                                Err(msg) => {
×
765
                                    return Err(StacksHttpResponse::new_error(
×
766
                                        preamble,
×
767
                                        &HttpNotFound::new(format!("No unconfirmed tip: {}", &msg)),
×
768
                                    ));
×
769
                                }
770
                            }
771
                        }
772
                        None => None,
10✔
773
                    };
774

775
                    if let Some(unconfirmed_chain_tip) = unconfirmed_chain_tip_opt {
44✔
776
                        Ok(unconfirmed_chain_tip)
25✔
777
                    } else {
778
                        match NakamotoChainState::get_canonical_block_header(
19✔
779
                            chainstate.db(),
19✔
780
                            sortdb,
19✔
781
                        ) {
782
                            Ok(Some(tip)) => Ok(StacksBlockId::new(
19✔
783
                                &tip.consensus_hash,
19✔
784
                                &tip.anchored_header.block_hash(),
19✔
785
                            )),
19✔
786
                            Ok(None) => {
787
                                return Err(StacksHttpResponse::new_error(
×
788
                                    preamble,
×
789
                                    &HttpNotFound::new("No such confirmed tip".to_string()),
×
790
                                ));
×
791
                            }
792
                            Err(e) => {
×
793
                                return Err(StacksHttpResponse::new_error(
×
794
                                    preamble,
×
795
                                    &HttpServerError::new(format!(
×
796
                                        "Failed to load chain tip: {:?}",
×
797
                                        &e
×
798
                                    )),
×
799
                                ));
×
800
                            }
801
                        }
802
                    }
803
                }
804
                TipRequest::SpecificTip(tip) => Ok(tip.clone()),
17✔
805
                TipRequest::UseLatestAnchoredTip => {
806
                    match NakamotoChainState::get_canonical_block_header(chainstate.db(), sortdb) {
1,890,081✔
807
                        Ok(Some(tip)) => Ok(StacksBlockId::new(
1,890,081✔
808
                            &tip.consensus_hash,
1,890,081✔
809
                            &tip.anchored_header.block_hash(),
1,890,081✔
810
                        )),
1,890,081✔
811
                        Ok(None) => {
812
                            return Err(StacksHttpResponse::new_error(
×
813
                                preamble,
×
814
                                &HttpNotFound::new(
×
815
                                    "No stacks chain tip exists at this point in time.".to_string(),
×
816
                                ),
×
817
                            ));
×
818
                        }
819
                        Err(e) => {
×
820
                            return Err(StacksHttpResponse::new_error(
×
821
                                preamble,
×
822
                                &HttpServerError::new(format!(
×
823
                                    "Failed to load chain tip: {:?}",
×
824
                                    &e
×
825
                                )),
×
826
                            ));
×
827
                        }
828
                    }
829
                }
830
            }
831
        })
1,890,142✔
832
    }
1,890,142✔
833

834
    pub fn update_highest_stacks_neighbor(
28,080✔
835
        &mut self,
28,080✔
836
        new_address: &SocketAddr,
28,080✔
837
        new_height: Option<u64>,
28,080✔
838
    ) {
28,080✔
839
        self.with_node_state(|network, _, _, _, _| {
28,080✔
840
            if let Some(new_height) = new_height {
28,080✔
841
                let current_height = network
28,025✔
842
                    .highest_stacks_neighbor
28,025✔
843
                    .as_ref()
28,025✔
844
                    .map(|(_addr, height)| *height)
28,025✔
845
                    .unwrap_or(0);
28,025✔
846

847
                if new_height > current_height {
28,025✔
848
                    network.highest_stacks_neighbor = Some((*new_address, new_height));
12,679✔
849
                }
19,304✔
850
            }
55✔
851
        });
28,080✔
852
    }
28,080✔
853
}
854

855
pub const STACKS_PUBLIC_KEY_ENCODED_SIZE: u32 = 33;
856

857
/// P2P message preamble -- included in all p2p network messages
858
#[derive(Debug, Clone, PartialEq)]
859
pub struct Preamble {
860
    pub peer_version: u32,                           // software version
861
    pub network_id: u32,                             // mainnet, testnet, etc.
862
    pub seq: u32, // message sequence number -- pairs this message to a request
863
    pub burn_block_height: u64, // last-seen block height (at chain tip)
864
    pub burn_block_hash: BurnchainHeaderHash, // hash of the last-seen burn block
865
    pub burn_stable_block_height: u64, // latest stable block height (e.g. chain tip minus 7)
866
    pub burn_stable_block_hash: BurnchainHeaderHash, // latest stable burnchain header hash.
867
    pub additional_data: u32, // RESERVED; pointer to additional data (should be all 0's if not used)
868
    pub signature: MessageSignature, // signature from the peer that sent this
869
    pub payload_len: u32,     // length of the following payload, including relayers vector
870
}
871

872
/// Request for a block inventory or a list of blocks.
873
/// Aligned to a PoX reward cycle.
874
/// This struct is used only in Stacks 2.x for Stacks 2.x inventories
875
#[derive(Debug, Clone, PartialEq)]
876
pub struct GetBlocksInv {
877
    /// Consensus hash at thestart of the reward cycle
878
    pub consensus_hash: ConsensusHash,
879
    /// Number of sortitions to ask for. Can be up to the reward cycle length.
880
    pub num_blocks: u16,
881
}
882

883
/// A bit vector that describes which block and microblock data node has data for in a given burn
884
/// chain block range.  Sent in reply to a GetBlocksInv for Stacks 2.x block data.
885
#[derive(Debug, Clone, PartialEq)]
886
pub struct BlocksInvData {
887
    /// Number of bits in the block bit vector (not to exceed the reward cycle length)
888
    pub bitlen: u16,
889
    /// The block bitvector. block_bitvec[i] & (1 << j) != 0 means that this peer has the block for
890
    /// sortition 8*i + j.
891
    pub block_bitvec: Vec<u8>,
892
    /// The microblock bitvector. microblocks_bitvec[i] & (1 << j) != 0 means that this peer has
893
    /// the microblocks for sortition 8*i + j
894
    pub microblocks_bitvec: Vec<u8>,
895
}
896

897
/// Request for a tenure inventroy.
898
/// Aligned to a PoX reward cycle.
899
/// This struct is used only in Nakamoto, for Nakamoto inventories
900
#[derive(Debug, Clone, PartialEq)]
901
pub struct GetNakamotoInvData {
902
    /// Consensus hash at the start of the reward cycle
903
    pub consensus_hash: ConsensusHash,
904
}
905

906
/// A bit vector that describes Nakamoto tenure availability.  Sent in reply for GetBlocksInv for
907
/// Nakamoto block data.  The ith bit in `tenures` will be set if (1) there is a sortition in the
908
/// ith burnchain block in the requested reward cycle (note that 0 <= i < 2100 in production), and
909
/// (2) the remote node not only has the tenure blocks, but has processed them.
910
#[derive(Debug, Clone, PartialEq)]
911
pub struct NakamotoInvData {
912
    /// The tenure bitvector.  tenures[i] & (1 << j) != 0 means that this peer has all the blocks
913
    /// for the tenure which began in sortition 8*i + j.  There will never be more than 1 reward
914
    /// cycle's worth of bits here, and since the largest supported reward cycle is 2100 blocks
915
    /// long (i.e. mainnet),
916
    pub tenures: BitVec<2100>,
917
}
918

919
/// Request for a PoX bitvector range.
920
/// Requests bits for [start_reward_cycle, start_reward_cycle + num_anchor_blocks)
921
#[derive(Debug, Clone, PartialEq)]
922
pub struct GetPoxInv {
923
    pub consensus_hash: ConsensusHash,
924
    pub num_cycles: u16, // how many bits to expect
925
}
926

927
/// Response to a GetPoxInv request
928
#[derive(Debug, Clone, PartialEq)]
929
pub struct PoxInvData {
930
    pub bitlen: u16,         // number of bits represented
931
    pub pox_bitvec: Vec<u8>, // a bit will be '1' if the node knows for sure the status of its reward cycle's anchor block; 0 if not.
932
}
933

934
/// Stacks epoch 2.x pushed block
935
#[derive(Debug, Clone, PartialEq)]
936
pub struct BlocksDatum(pub ConsensusHash, pub StacksBlock);
937

938
/// Stacks epoch 2.x blocks pushed
939
#[derive(Debug, Clone, PartialEq)]
940
pub struct BlocksData {
941
    pub blocks: Vec<BlocksDatum>,
942
}
943

944
/// Nakamoto epoch 3.x blocks pushed.
945
/// No need for a separate NakamotoBlocksDatum struct, because the consensus hashes that place this
946
/// block into the block stream are already embedded within the header
947
#[derive(Debug, Clone, PartialEq)]
948
pub struct NakamotoBlocksData {
949
    pub blocks: Vec<NakamotoBlock>,
950
}
951

952
/// Microblocks pushed
953
#[derive(Debug, Clone, PartialEq)]
954
pub struct MicroblocksData {
955
    pub index_anchor_block: StacksBlockId,
956
    pub microblocks: Vec<StacksMicroblock>,
957
}
958

959
/// Block available hint
960
#[derive(Debug, Clone, PartialEq)]
961
pub struct BlocksAvailableData {
962
    pub available: Vec<(ConsensusHash, BurnchainHeaderHash)>,
963
}
964

965
/// A descriptor of a peer
966
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
967
pub struct NeighborAddress {
968
    #[serde(rename = "ip")]
969
    pub addrbytes: PeerAddress,
970
    pub port: u16,
971
    pub public_key_hash: Hash160, // used as a hint; useful for when a node trusts another node to be honest about this
972
}
973

974
impl fmt::Display for NeighborAddress {
975
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5,421✔
976
        write!(
5,421✔
977
            f,
5,421✔
978
            "{:?}://{:?}",
979
            &self.public_key_hash,
5,421✔
980
            &self.addrbytes.to_socketaddr(self.port)
5,421✔
981
        )
982
    }
5,421✔
983
}
984

985
impl fmt::Debug for NeighborAddress {
986
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
695,920✔
987
        write!(
695,920✔
988
            f,
695,920✔
989
            "{:?}://{:?}",
990
            &self.public_key_hash,
695,920✔
991
            &self.addrbytes.to_socketaddr(self.port)
695,920✔
992
        )
993
    }
695,920✔
994
}
995

996
impl NeighborAddress {
997
    pub fn clear_public_key(&mut self) {
298,916✔
998
        self.public_key_hash = Hash160([0u8; 20]);
298,916✔
999
    }
298,916✔
1000

1001
    pub fn from_neighbor_key(nk: NeighborKey, pkh: Hash160) -> NeighborAddress {
297,524✔
1002
        NeighborAddress {
297,524✔
1003
            addrbytes: nk.addrbytes,
297,524✔
1004
            port: nk.port,
297,524✔
1005
            public_key_hash: pkh,
297,524✔
1006
        }
297,524✔
1007
    }
297,524✔
1008

1009
    pub fn to_socketaddr(&self) -> SocketAddr {
×
1010
        self.addrbytes.to_socketaddr(self.port)
×
1011
    }
×
1012
}
1013

1014
/// A descriptor of a list of known peers
1015
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1016
pub struct NeighborsData {
1017
    pub neighbors: Vec<NeighborAddress>,
1018
}
1019

1020
/// Handshake request -- this is the first message sent to a peer.
1021
/// The remote peer will reply a HandshakeAccept with just a preamble
1022
/// if the peer accepts.  Otherwise it will get a HandshakeReject with just
1023
/// a preamble.
1024
///
1025
/// To keep peer knowledge fresh, nodes will send handshakes to each other
1026
/// as heartbeat messages.
1027
#[derive(Debug, Clone, PartialEq)]
1028
pub struct HandshakeData {
1029
    pub addrbytes: PeerAddress,
1030
    pub port: u16,
1031
    pub services: u16, // bit field representing services this node offers
1032
    pub node_public_key: StacksPublicKeyBuffer,
1033
    pub expire_block_height: u64, // burn block height after which this node's key will be revoked,
1034
    pub data_url: UrlString,
1035
}
1036

1037
#[repr(u8)]
1038
pub enum ServiceFlags {
1039
    RELAY = 0x01,
1040
    RPC = 0x02,
1041
    STACKERDB = 0x04,
1042
}
1043

1044
#[derive(Debug, Clone, PartialEq)]
1045
pub struct HandshakeAcceptData {
1046
    pub handshake: HandshakeData, // this peer's handshake information
1047
    pub heartbeat_interval: u32,  // hint as to how long this peer will remember you
1048
}
1049

1050
#[derive(Debug, Clone, PartialEq)]
1051
pub struct NackData {
1052
    pub error_code: u32,
1053
}
1054
pub mod NackErrorCodes {
1055
    /// A handshake has not yet been completed with the requester
1056
    /// and it is required before the protocol can proceed
1057
    pub const HandshakeRequired: u32 = 1;
1058
    /// The request depends on a burnchain block that this peer does not recognize
1059
    pub const NoSuchBurnchainBlock: u32 = 2;
1060
    /// The remote peer has exceeded local per-peer bandwidth limits
1061
    pub const Throttled: u32 = 3;
1062
    /// The request depends on a PoX fork that this peer does not recognize as canonical
1063
    pub const InvalidPoxFork: u32 = 4;
1064
    /// The message received is not appropriate for the ongoing step in the protocol being executed
1065
    pub const InvalidMessage: u32 = 5;
1066
    /// The StackerDB requested is not known or configured on this node
1067
    pub const NoSuchDB: u32 = 6;
1068
    /// The StackerDB chunk request referred to an older copy of the chunk than this node has
1069
    pub const StaleVersion: u32 = 7;
1070
    /// The remote peer's view of the burnchain is too out-of-date for the protocol to continue
1071
    pub const StaleView: u32 = 8;
1072
    /// The StackerDB chunk request referred to a newer copy of the chunk that this node has
1073
    pub const FutureVersion: u32 = 9;
1074
    /// The referenced StackerDB state view is stale locally relative to the requested version
1075
    pub const FutureView: u32 = 10;
1076
}
1077

1078
#[derive(Debug, Clone, PartialEq)]
1079
pub struct PingData {
1080
    pub nonce: u32,
1081
}
1082

1083
#[derive(Debug, Clone, PartialEq)]
1084
pub struct PongData {
1085
    pub nonce: u32,
1086
}
1087

1088
#[derive(Debug, Clone, PartialEq)]
1089
pub struct NatPunchData {
1090
    pub addrbytes: PeerAddress,
1091
    pub port: u16,
1092
    pub nonce: u32,
1093
}
1094

1095
/// Inform the remote peer of (a page of) the list of stacker DB contracts this node supports
1096
#[derive(Debug, Clone, PartialEq)]
1097
pub struct StackerDBHandshakeData {
1098
    /// current reward cycle consensus hash (i.e. the consensus hash of the Stacks tip in the
1099
    /// current reward cycle, which commits to both the Stacks block tip and the underlying PoX
1100
    /// history).
1101
    pub rc_consensus_hash: ConsensusHash,
1102
    /// list of smart contracts that we index.
1103
    /// there can be as many as 256 entries.
1104
    pub smart_contracts: Vec<QualifiedContractIdentifier>,
1105
}
1106

1107
/// Request for a chunk inventory
1108
#[derive(Debug, Clone, PartialEq)]
1109
pub struct StackerDBGetChunkInvData {
1110
    /// smart contract being used to determine chunk quantity and order
1111
    pub contract_id: QualifiedContractIdentifier,
1112
    /// consensus hash of the Stacks chain tip in this reward cycle
1113
    pub rc_consensus_hash: ConsensusHash,
1114
}
1115

1116
/// Inventory bitvector for chunks this node contains
1117
#[derive(Debug, Clone, PartialEq)]
1118
pub struct StackerDBChunkInvData {
1119
    /// version vector of chunks available.
1120
    /// The max-length is a protocol constant.
1121
    pub slot_versions: Vec<u32>,
1122
    /// number of outbound replicas the sender is connected to
1123
    pub num_outbound_replicas: u32,
1124
}
1125

1126
/// Request for a stacker DB chunk.
1127
#[derive(Debug, Clone, PartialEq)]
1128
pub struct StackerDBGetChunkData {
1129
    /// smart contract being used to determine slot quantity and order
1130
    pub contract_id: QualifiedContractIdentifier,
1131
    /// consensus hash of the Stacks chain tip in this reward cycle
1132
    pub rc_consensus_hash: ConsensusHash,
1133
    /// slot ID
1134
    pub slot_id: u32,
1135
    /// last-seen slot version
1136
    pub slot_version: u32,
1137
}
1138

1139
/// Stacker DB chunk push
1140
#[derive(Debug, Clone, PartialEq)]
1141
pub struct StackerDBPushChunkData {
1142
    /// smart contract being used to determine chunk quantity and order
1143
    pub contract_id: QualifiedContractIdentifier,
1144
    /// consensus hash of the Stacks chain tip in this reward cycle
1145
    pub rc_consensus_hash: ConsensusHash,
1146
    /// the pushed chunk
1147
    pub chunk_data: StackerDBChunkData,
1148
}
1149

1150
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1151
pub struct RelayData {
1152
    pub peer: NeighborAddress,
1153
    pub seq: u32,
1154
}
1155

1156
/// All P2P message types
1157
#[derive(Debug, Clone, PartialEq)]
1158
pub enum StacksMessageType {
1159
    Handshake(HandshakeData),
1160
    HandshakeAccept(HandshakeAcceptData),
1161
    HandshakeReject,
1162
    GetNeighbors,
1163
    Neighbors(NeighborsData),
1164
    GetBlocksInv(GetBlocksInv),
1165
    BlocksInv(BlocksInvData),
1166
    GetPoxInv(GetPoxInv),
1167
    PoxInv(PoxInvData),
1168
    BlocksAvailable(BlocksAvailableData),
1169
    MicroblocksAvailable(BlocksAvailableData),
1170
    Blocks(BlocksData),
1171
    Microblocks(MicroblocksData),
1172
    Transaction(StacksTransaction),
1173
    Nack(NackData),
1174
    Ping(PingData),
1175
    Pong(PongData),
1176
    NatPunchRequest(u32),
1177
    NatPunchReply(NatPunchData),
1178
    // stacker DB
1179
    StackerDBHandshakeAccept(HandshakeAcceptData, StackerDBHandshakeData),
1180
    StackerDBGetChunkInv(StackerDBGetChunkInvData),
1181
    StackerDBChunkInv(StackerDBChunkInvData),
1182
    StackerDBGetChunk(StackerDBGetChunkData),
1183
    StackerDBChunk(StackerDBChunkData),
1184
    StackerDBPushChunk(StackerDBPushChunkData),
1185
    // Nakamoto-specific
1186
    GetNakamotoInv(GetNakamotoInvData),
1187
    NakamotoInv(NakamotoInvData),
1188
    NakamotoBlocks(NakamotoBlocksData),
1189
}
1190

1191
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1192
#[repr(u8)]
1193
pub enum StacksMessageID {
1194
    Handshake = 0,
1195
    HandshakeAccept = 1,
1196
    HandshakeReject = 2,
1197
    GetNeighbors = 3,
1198
    Neighbors = 4,
1199
    GetBlocksInv = 5,
1200
    BlocksInv = 6,
1201
    GetPoxInv = 7,
1202
    PoxInv = 8,
1203
    BlocksAvailable = 9,
1204
    MicroblocksAvailable = 10,
1205
    Blocks = 11,
1206
    Microblocks = 12,
1207
    Transaction = 13,
1208
    Nack = 14,
1209
    Ping = 15,
1210
    Pong = 16,
1211
    NatPunchRequest = 17,
1212
    NatPunchReply = 18,
1213
    // stackerdb
1214
    StackerDBHandshakeAccept = 19,
1215
    StackerDBGetChunkInv = 21,
1216
    StackerDBChunkInv = 22,
1217
    StackerDBGetChunk = 23,
1218
    StackerDBChunk = 24,
1219
    StackerDBPushChunk = 25,
1220
    // nakamoto
1221
    GetNakamotoInv = 26,
1222
    NakamotoInv = 27,
1223
    NakamotoBlocks = 28,
1224
    // reserved
1225
    Reserved = 255,
1226
}
1227

1228
/// Message type for all P2P Stacks network messages
1229
#[derive(Debug, Clone, PartialEq)]
1230
pub struct StacksMessage {
1231
    pub preamble: Preamble,
1232
    pub relayers: Vec<RelayData>,
1233
    pub payload: StacksMessageType,
1234
}
1235

1236
/// Network messages implement this to have multiple messages in flight.
1237
pub trait MessageSequence {
1238
    fn request_id(&self) -> u32;
1239
    fn get_message_name(&self) -> &'static str;
1240
}
1241

1242
pub trait ProtocolFamily {
1243
    type Preamble: StacksMessageCodec + Send + Sync + Clone + PartialEq + std::fmt::Debug;
1244
    type Message: MessageSequence + Send + Sync + Clone + PartialEq + std::fmt::Debug;
1245

1246
    /// Return the maximum possible length of the serialized Preamble type
1247
    fn preamble_size_hint(&mut self) -> usize;
1248

1249
    /// Determine how long the message payload will be, given the Preamble (may return None if the
1250
    /// payload length cannot be determined solely by the Preamble).
1251
    fn payload_len(&mut self, preamble: &Self::Preamble) -> Option<usize>;
1252

1253
    /// Given a byte buffer of a length at last that of the value returned by preamble_size_hint,
1254
    /// parse a Preamble and return both the Preamble and the number of bytes actually consumed by it.
1255
    fn read_preamble(&mut self, buf: &[u8]) -> Result<(Self::Preamble, usize), Error>;
1256

1257
    /// Given a preamble and a byte buffer, parse out a message and return both the message and the
1258
    /// number of bytes actually consumed by it.  Only used if the message is _not_ streamed.  The
1259
    /// buf slice is guaranteed to have at least `payload_len()` bytes if `payload_len()` returns
1260
    /// Some(...).
1261
    fn read_payload(
1262
        &mut self,
1263
        preamble: &Self::Preamble,
1264
        buf: &[u8],
1265
    ) -> Result<(Self::Message, usize), Error>;
1266

1267
    /// Given a preamble and a Read, attempt to stream a message.  This will be called if
1268
    /// `payload_len()` returns None.  This method will be repeatedly called with new data until a
1269
    /// message can be obtained; therefore, the ProtocolFamily implementation will need to do its
1270
    /// own bufferring and state-tracking.
1271
    fn stream_payload<R: Read>(
1272
        &mut self,
1273
        preamble: &Self::Preamble,
1274
        fd: &mut R,
1275
    ) -> Result<(Option<(Self::Message, usize)>, usize), Error>;
1276

1277
    /// Given a public key, a preamble, and the yet-to-be-parsed message bytes, verify the message
1278
    /// authenticity.  Not all protocols need to do this.
1279
    fn verify_payload_bytes(
1280
        &mut self,
1281
        key: &StacksPublicKey,
1282
        preamble: &Self::Preamble,
1283
        bytes: &[u8],
1284
    ) -> Result<(), Error>;
1285

1286
    /// Given a Write and a Message, write it out.  This method is also responsible for generating
1287
    /// and writing out a Preamble for its Message.
1288
    fn write_message<W: Write>(&mut self, fd: &mut W, message: &Self::Message)
1289
        -> Result<(), Error>;
1290
}
1291

1292
// these implement the ProtocolFamily trait
1293
#[derive(Debug, Clone, PartialEq)]
1294
pub struct StacksP2P {}
1295

1296
// an array in our protocol can't exceed this many items
1297
pub const ARRAY_MAX_LEN: u32 = u32::MAX;
1298

1299
// maximum number of neighbors in a NeighborsData
1300
pub const MAX_NEIGHBORS_DATA_LEN: u32 = 128;
1301

1302
// number of peers to relay to, depending on outbound or inbound
1303
pub const MAX_BROADCAST_OUTBOUND_RECEIVERS: usize = 8;
1304
pub const MAX_BROADCAST_INBOUND_RECEIVERS: usize = 16;
1305

1306
// maximum number of blocks that can be announced as available
1307
pub const BLOCKS_AVAILABLE_MAX_LEN: u32 = 32;
1308

1309
// maximum number of PoX reward cycles we can ask about
1310
#[cfg(not(test))]
1311
pub const GETPOXINV_MAX_BITLEN: u64 = 4096;
1312
#[cfg(test)]
1313
pub const GETPOXINV_MAX_BITLEN: u64 = 8;
1314

1315
// maximum number of Stacks epoch2.x blocks that can be pushed at once (even if the entire message is undersized).
1316
// This bound is needed since it bounds the amount of I/O a peer can be asked to do to validate the
1317
// message.
1318
pub const BLOCKS_PUSHED_MAX: u32 = 32;
1319

1320
// maximum number of Nakamoto blocks that can be pushed at once (even if the entire message is undersized).
1321
// This bound is needed since it bounds the amount of I/O a peer can be asked to do to validate the
1322
// message.
1323
pub const NAKAMOTO_BLOCKS_PUSHED_MAX: u32 = 32;
1324

1325
/// Neighbor to drop
1326
#[derive(Clone, Eq, PartialOrd, Ord, Debug)]
1327
pub struct DropNeighbor {
1328
    /// the neighbor identifier
1329
    pub key: NeighborKey,
1330
    /// the reason for dropping the neighbor
1331
    pub reason: DropReason,
1332
    /// the reason the neighbor should be dropped
1333
    pub source: DropSource,
1334
}
1335

1336
impl Hash for DropNeighbor {
1337
    fn hash<H: Hasher>(&self, state: &mut H) {
16,115✔
1338
        // ignores reason and source, we only care about the neighbor key
1339
        self.key.hash(state);
16,115✔
1340
    }
16,115✔
1341
}
1342

1343
impl PartialEq for DropNeighbor {
1344
    fn eq(&self, other: &DropNeighbor) -> bool {
1,134✔
1345
        self.key == other.key
1,134✔
1346
    }
1,134✔
1347
}
1348

1349
/// neighbor identifier
1350
#[derive(Clone, Eq, PartialOrd, Ord)]
1351
pub struct NeighborKey {
1352
    pub peer_version: u32,
1353
    pub network_id: u32,
1354
    pub addrbytes: PeerAddress,
1355
    pub port: u16,
1356
}
1357

1358
impl Hash for NeighborKey {
1359
    fn hash<H: Hasher>(&self, state: &mut H) {
34,515,536✔
1360
        // ignores peer version and network ID -- we don't accept or deal with messages that have
1361
        // incompatible versions or network IDs in the first place
1362
        let peer_major_version = self.peer_version & 0xff000000;
34,515,536✔
1363
        peer_major_version.hash(state);
34,515,536✔
1364
        self.addrbytes.hash(state);
34,515,536✔
1365
        self.port.hash(state);
34,515,536✔
1366
    }
34,515,536✔
1367
}
1368

1369
impl PartialEq for NeighborKey {
1370
    fn eq(&self, other: &NeighborKey) -> bool {
26,407,332✔
1371
        // only check major version byte in peer_version
1372
        self.network_id == other.network_id
26,407,332✔
1373
            && (self.peer_version & 0xff000000) == (other.peer_version & 0xff000000)
26,407,331✔
1374
            && self.addrbytes == other.addrbytes
26,407,331✔
1375
            && self.port == other.port
26,405,807✔
1376
    }
26,407,332✔
1377
}
1378

1379
impl fmt::Display for NeighborKey {
1380
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170,487✔
1381
        let peer_version_str = if self.peer_version > 0 {
170,487✔
1382
            format!("{:08x}", self.peer_version)
169,606✔
1383
        } else {
1384
            "UNKNOWN".to_string()
881✔
1385
        };
1386
        let network_id_str = if self.network_id > 0 {
170,487✔
1387
            format!("{:08x}", self.network_id)
169,606✔
1388
        } else {
1389
            "UNKNOWN".to_string()
881✔
1390
        };
1391
        write!(
170,487✔
1392
            f,
170,487✔
1393
            "{}+{}://{:?}",
1394
            peer_version_str,
1395
            network_id_str,
1396
            &self.addrbytes.to_socketaddr(self.port)
170,487✔
1397
        )
1398
    }
170,487✔
1399
}
1400

1401
impl fmt::Debug for NeighborKey {
1402
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134,940✔
1403
        fmt::Display::fmt(self, f)
134,940✔
1404
    }
134,940✔
1405
}
1406

1407
impl NeighborKey {
1408
    pub fn empty() -> NeighborKey {
17,491✔
1409
        NeighborKey {
17,491✔
1410
            peer_version: 0,
17,491✔
1411
            network_id: 0,
17,491✔
1412
            addrbytes: PeerAddress([0u8; 16]),
17,491✔
1413
            port: 0,
17,491✔
1414
        }
17,491✔
1415
    }
17,491✔
1416

1417
    pub fn from_neighbor_address(
21,225,743✔
1418
        peer_version: u32,
21,225,743✔
1419
        network_id: u32,
21,225,743✔
1420
        na: &NeighborAddress,
21,225,743✔
1421
    ) -> NeighborKey {
21,225,743✔
1422
        NeighborKey {
21,225,743✔
1423
            peer_version,
21,225,743✔
1424
            network_id,
21,225,743✔
1425
            addrbytes: na.addrbytes.clone(),
21,225,743✔
1426
            port: na.port,
21,225,743✔
1427
        }
21,225,743✔
1428
    }
21,225,743✔
1429

1430
    pub fn to_socketaddr(&self) -> SocketAddr {
×
1431
        self.addrbytes.to_socketaddr(self.port)
×
1432
    }
×
1433
}
1434

1435
/// Entry in the neighbor set
1436
#[derive(Debug, Clone)]
1437
pub struct Neighbor {
1438
    pub addr: NeighborKey,
1439

1440
    // fields below this can change at runtime
1441
    pub public_key: Secp256k1PublicKey,
1442
    pub expire_block: u64,
1443
    pub last_contact_time: u64, // time when we last authenticated with this peer via a Handshake
1444

1445
    pub allowed: i64, // allow deadline (negative == "forever")
1446
    pub denied: i64,  // deny deadline (negative == "forever")
1447

1448
    pub asn: u32, // AS number
1449
    pub org: u32, // organization identifier
1450

1451
    pub in_degree: u32,  // number of peers who list this peer as a neighbor
1452
    pub out_degree: u32, // number of neighbors this peer has
1453
}
1454

1455
impl PartialEq for Neighbor {
1456
    /// Neighbor equality is based on having the same address and public key.
1457
    /// Everything else can change at runtime
1458
    fn eq(&self, other: &Neighbor) -> bool {
297,596✔
1459
        self.addr == other.addr && self.public_key == other.public_key
297,596✔
1460
    }
297,596✔
1461
}
1462

1463
impl Neighbor {
1464
    pub fn is_allowed(&self) -> bool {
×
1465
        let now = get_epoch_time_secs();
×
1466
        (self.allowed < 0 || (self.allowed as u64) > now)
×
1467
            && !(self.denied < 0 || (self.denied as u64) > now)
×
1468
    }
×
1469

1470
    pub fn is_always_allowed(&self) -> bool {
×
1471
        self.allowed < 0
×
1472
    }
×
1473

1474
    pub fn is_denied(&self) -> bool {
394,707✔
1475
        let now = get_epoch_time_secs();
394,707✔
1476
        !(self.allowed < 0 || (self.allowed as u64) > now)
394,707✔
1477
            && (self.denied < 0 || (self.denied as u64) > now)
154,818✔
1478
    }
394,707✔
1479
}
1480

1481
impl fmt::Display for Neighbor {
1482
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
468✔
1483
        write!(f, "{}@{}", self.public_key.to_hex(), self.addr)
468✔
1484
    }
468✔
1485
}
1486

1487
pub const NUM_NEIGHBORS: usize = 32;
1488

1489
// maximum number of unconfirmed microblocks can get streamed to us
1490
pub const MAX_MICROBLOCKS_UNCONFIRMED: usize = 1024;
1491

1492
// maximum number of block headers we'll get streamed to us
1493
pub const MAX_HEADERS: usize = 2100;
1494

1495
// how long a peer will be denied for if it misbehaves
1496
#[cfg(test)]
1497
pub const DENY_BAN_DURATION: u64 = 30; // seconds
1498
#[cfg(not(test))]
1499
pub const DENY_BAN_DURATION: u64 = 86400; // seconds (1 day)
1500

1501
pub const DENY_MIN_BAN_DURATION: u64 = 2;
1502

1503
/// Result of doing network work
1504
#[derive(Clone, PartialEq, Debug)]
1505
pub struct NetworkResult {
1506
    /// Stacks chain tip when we began this pass
1507
    pub stacks_tip: StacksBlockId,
1508
    /// PoX ID as it was when we begin downloading blocks (set if we have downloaded new blocks)
1509
    pub download_pox_id: Option<PoxId>,
1510
    /// Network messages we received but did not handle
1511
    pub unhandled_messages: HashMap<NeighborKey, Vec<StacksMessage>>,
1512
    /// Stacks 2.x blocks we downloaded, and time taken
1513
    pub blocks: Vec<(ConsensusHash, StacksBlock, u64)>,
1514
    /// Stacks 2.x confiremd microblocks we downloaded, and time taken
1515
    pub confirmed_microblocks: Vec<(ConsensusHash, Vec<StacksMicroblock>, u64)>,
1516
    /// Nakamoto blocks we downloaded
1517
    pub nakamoto_blocks: HashMap<StacksBlockId, NakamotoBlock>,
1518
    /// all transactions pushed to us and their message relay hints
1519
    pub pushed_transactions: HashMap<NeighborKey, Vec<(Vec<RelayData>, StacksTransaction)>>,
1520
    /// all Stacks 2.x blocks pushed to us
1521
    pub pushed_blocks: HashMap<NeighborKey, Vec<BlocksData>>,
1522
    /// all Stacks 2.x microblocks pushed to us, and the relay hints from the message
1523
    pub pushed_microblocks: HashMap<NeighborKey, Vec<(Vec<RelayData>, MicroblocksData)>>,
1524
    /// all Stacks 3.x blocks pushed to us
1525
    pub pushed_nakamoto_blocks: HashMap<NeighborKey, Vec<(Vec<RelayData>, NakamotoBlocksData)>>,
1526
    /// transactions sent to us by the http server
1527
    pub uploaded_transactions: Vec<StacksTransaction>,
1528
    /// blocks sent to us via the http server
1529
    pub uploaded_blocks: Vec<BlocksData>,
1530
    /// blocks sent to us via the http server
1531
    pub uploaded_nakamoto_blocks: Vec<NakamotoBlock>,
1532
    /// microblocks sent to us by the http server
1533
    pub uploaded_microblocks: Vec<MicroblocksData>,
1534
    /// chunks we received from the HTTP server
1535
    pub uploaded_stackerdb_chunks: Vec<StackerDBPushChunkData>,
1536
    /// chunks we received from p2p push
1537
    pub pushed_stackerdb_chunks: Vec<StackerDBPushChunkData>,
1538
    /// Atlas attachments we obtained
1539
    pub attachments: Vec<(AttachmentInstance, Attachment)>,
1540
    /// transactions we downloaded via a mempool sync
1541
    pub synced_transactions: Vec<StacksTransaction>,
1542
    /// chunks for stacker DBs we downloaded
1543
    pub stacker_db_sync_results: Vec<StackerDBSyncResult>,
1544
    /// Number of times the network state machine has completed one pass
1545
    pub num_state_machine_passes: u64,
1546
    /// Number of times the Stacks 2.x inventory synchronization has completed one pass
1547
    pub num_inv_sync_passes: u64,
1548
    /// Number of times the Stacks 2.x block downloader has completed one pass
1549
    pub num_download_passes: u64,
1550
    /// Number of connected peers
1551
    pub num_connected_peers: usize,
1552
    /// The observed burnchain height
1553
    pub burn_height: u64,
1554
    /// The observed stacks coinbase height
1555
    pub coinbase_height: u64,
1556
    /// The observed stacks tip height (different in Nakamoto from coinbase height)
1557
    pub stacks_tip_height: u64,
1558
    /// The consensus hash of the stacks tip (prefixed `rc_` for historical reasons)
1559
    pub rc_consensus_hash: ConsensusHash,
1560
    /// The current StackerDB configs
1561
    pub stacker_db_configs: HashMap<QualifiedContractIdentifier, StackerDBConfig>,
1562
}
1563

1564
impl NetworkResult {
1565
    pub fn new(
10,385,434✔
1566
        stacks_tip: StacksBlockId,
10,385,434✔
1567
        num_state_machine_passes: u64,
10,385,434✔
1568
        num_inv_sync_passes: u64,
10,385,434✔
1569
        num_download_passes: u64,
10,385,434✔
1570
        num_connected_peers: usize,
10,385,434✔
1571
        burn_height: u64,
10,385,434✔
1572
        coinbase_height: u64,
10,385,434✔
1573
        stacks_tip_height: u64,
10,385,434✔
1574
        rc_consensus_hash: ConsensusHash,
10,385,434✔
1575
        stacker_db_configs: HashMap<QualifiedContractIdentifier, StackerDBConfig>,
10,385,434✔
1576
    ) -> NetworkResult {
10,385,434✔
1577
        NetworkResult {
10,385,434✔
1578
            stacks_tip,
10,385,434✔
1579
            unhandled_messages: HashMap::new(),
10,385,434✔
1580
            download_pox_id: None,
10,385,434✔
1581
            blocks: vec![],
10,385,434✔
1582
            confirmed_microblocks: vec![],
10,385,434✔
1583
            nakamoto_blocks: HashMap::new(),
10,385,434✔
1584
            pushed_transactions: HashMap::new(),
10,385,434✔
1585
            pushed_blocks: HashMap::new(),
10,385,434✔
1586
            pushed_microblocks: HashMap::new(),
10,385,434✔
1587
            pushed_nakamoto_blocks: HashMap::new(),
10,385,434✔
1588
            uploaded_transactions: vec![],
10,385,434✔
1589
            uploaded_nakamoto_blocks: vec![],
10,385,434✔
1590
            uploaded_blocks: vec![],
10,385,434✔
1591
            uploaded_microblocks: vec![],
10,385,434✔
1592
            uploaded_stackerdb_chunks: vec![],
10,385,434✔
1593
            pushed_stackerdb_chunks: vec![],
10,385,434✔
1594
            attachments: vec![],
10,385,434✔
1595
            synced_transactions: vec![],
10,385,434✔
1596
            stacker_db_sync_results: vec![],
10,385,434✔
1597
            num_state_machine_passes,
10,385,434✔
1598
            num_inv_sync_passes,
10,385,434✔
1599
            num_download_passes,
10,385,434✔
1600
            num_connected_peers,
10,385,434✔
1601
            burn_height,
10,385,434✔
1602
            coinbase_height,
10,385,434✔
1603
            stacks_tip_height,
10,385,434✔
1604
            rc_consensus_hash,
10,385,434✔
1605
            stacker_db_configs,
10,385,434✔
1606
        }
10,385,434✔
1607
    }
10,385,434✔
1608

1609
    /// Get the set of all StacksBlocks represented
1610
    fn all_block_ids(&self) -> HashSet<StacksBlockId> {
84,915✔
1611
        let mut blocks: HashSet<_> = self
84,915✔
1612
            .blocks
84,915✔
1613
            .iter()
84,915✔
1614
            .map(|(ch, blk, _)| StacksBlockId::new(ch, &blk.block_hash()))
84,915✔
1615
            .collect();
84,915✔
1616

1617
        let pushed_blocks: HashSet<_> = self
84,915✔
1618
            .pushed_blocks
84,915✔
1619
            .values()
84,915✔
1620
            .flat_map(|block_list| {
84,915✔
1621
                block_list.iter().flat_map(|block_data| {
6✔
1622
                    block_data
6✔
1623
                        .blocks
6✔
1624
                        .iter()
6✔
1625
                        .map(|block_datum| {
6✔
1626
                            StacksBlockId::new(&block_datum.0, &block_datum.1.block_hash())
6✔
1627
                        })
6✔
1628
                        .collect::<HashSet<_>>()
6✔
1629
                })
6✔
1630
            })
6✔
1631
            .collect();
84,915✔
1632

1633
        let uploaded_blocks: HashSet<_> = self
84,915✔
1634
            .uploaded_blocks
84,915✔
1635
            .iter()
84,915✔
1636
            .flat_map(|blk_data| {
84,915✔
1637
                blk_data
6✔
1638
                    .blocks
6✔
1639
                    .iter()
6✔
1640
                    .map(|blk| StacksBlockId::new(&blk.0, &blk.1.block_hash()))
6✔
1641
            })
6✔
1642
            .collect();
84,915✔
1643

1644
        blocks.extend(pushed_blocks);
84,915✔
1645
        blocks.extend(uploaded_blocks);
84,915✔
1646
        blocks
84,915✔
1647
    }
84,915✔
1648

1649
    /// Get the set of all microblocks represented
1650
    fn all_microblock_hashes(&self) -> HashSet<BlockHeaderHash> {
84,915✔
1651
        let mut mblocks: HashSet<_> = self
84,915✔
1652
            .confirmed_microblocks
84,915✔
1653
            .iter()
84,915✔
1654
            .flat_map(|(_, mblocks, _)| mblocks.iter().map(|mblk| mblk.block_hash()))
84,915✔
1655
            .collect();
84,915✔
1656

1657
        let pushed_microblocks: HashSet<_> = self
84,915✔
1658
            .pushed_microblocks
84,915✔
1659
            .values()
84,915✔
1660
            .flat_map(|mblock_list| {
84,915✔
1661
                mblock_list.iter().flat_map(|(_, mblock_data)| {
6✔
1662
                    mblock_data
6✔
1663
                        .microblocks
6✔
1664
                        .iter()
6✔
1665
                        .map(|mblock| mblock.block_hash())
6✔
1666
                })
6✔
1667
            })
6✔
1668
            .collect();
84,915✔
1669

1670
        let uploaded_microblocks: HashSet<_> = self
84,915✔
1671
            .uploaded_microblocks
84,915✔
1672
            .iter()
84,915✔
1673
            .flat_map(|mblk_data| mblk_data.microblocks.iter().map(|mblk| mblk.block_hash()))
84,915✔
1674
            .collect();
84,915✔
1675

1676
        mblocks.extend(pushed_microblocks);
84,915✔
1677
        mblocks.extend(uploaded_microblocks);
84,915✔
1678
        mblocks
84,915✔
1679
    }
84,915✔
1680

1681
    /// Get the set of all nakamoto blocks represented
1682
    fn all_nakamoto_block_ids(&self) -> HashSet<StacksBlockId> {
84,915✔
1683
        let mut naka_block_ids: HashSet<_> = self
84,915✔
1684
            .nakamoto_blocks
84,915✔
1685
            .values()
84,915✔
1686
            .map(|nblk| nblk.block_id())
84,915✔
1687
            .collect();
84,915✔
1688

1689
        let pushed_nakamoto_blocks: HashSet<_> = self
84,915✔
1690
            .pushed_nakamoto_blocks
84,915✔
1691
            .values()
84,915✔
1692
            .map(|naka_blocks_list| {
84,915✔
1693
                naka_blocks_list
619✔
1694
                    .iter()
619✔
1695
                    .map(|(_, naka_blocks)| {
619✔
1696
                        naka_blocks
619✔
1697
                            .blocks
619✔
1698
                            .iter()
619✔
1699
                            .map(|nblk| nblk.block_id())
619✔
1700
                            .collect::<HashSet<_>>()
619✔
1701
                    })
619✔
1702
                    .collect::<Vec<HashSet<_>>>()
619✔
1703
            })
619✔
1704
            .collect::<Vec<Vec<HashSet<_>>>>()
84,915✔
1705
            .into_iter()
84,915✔
1706
            .flatten()
84,915✔
1707
            .fold(HashSet::new(), |mut acc, next| {
84,915✔
1708
                acc.extend(next);
619✔
1709
                acc
619✔
1710
            });
619✔
1711

1712
        let uploaded_nakamoto_blocks: HashSet<_> = self
84,915✔
1713
            .uploaded_nakamoto_blocks
84,915✔
1714
            .iter()
84,915✔
1715
            .map(|nblk| nblk.block_id())
84,915✔
1716
            .collect();
84,915✔
1717

1718
        naka_block_ids.extend(pushed_nakamoto_blocks);
84,915✔
1719
        naka_block_ids.extend(uploaded_nakamoto_blocks);
84,915✔
1720
        naka_block_ids
84,915✔
1721
    }
84,915✔
1722

1723
    /// Get the set of all txids represented
1724
    fn all_txids(&self) -> HashSet<Txid> {
84,915✔
1725
        let mut txids: HashSet<_> = self
84,915✔
1726
            .uploaded_transactions
84,915✔
1727
            .iter()
84,915✔
1728
            .map(|tx| tx.txid())
84,915✔
1729
            .collect();
84,915✔
1730
        let pushed_txids: HashSet<_> = self
84,915✔
1731
            .pushed_transactions
84,915✔
1732
            .values()
84,915✔
1733
            .map(|tx_list| {
84,915✔
1734
                tx_list
60✔
1735
                    .iter()
60✔
1736
                    .map(|(_, tx)| tx.txid())
60✔
1737
                    .collect::<HashSet<_>>()
60✔
1738
            })
60✔
1739
            .collect::<Vec<HashSet<_>>>()
84,915✔
1740
            .into_iter()
84,915✔
1741
            .fold(HashSet::new(), |mut acc, next| {
84,915✔
1742
                acc.extend(next);
60✔
1743
                acc
60✔
1744
            });
60✔
1745

1746
        let synced_txids: HashSet<_> = self
84,915✔
1747
            .synced_transactions
84,915✔
1748
            .iter()
84,915✔
1749
            .map(|tx| tx.txid())
84,915✔
1750
            .collect();
84,915✔
1751

1752
        txids.extend(pushed_txids);
84,915✔
1753
        txids.extend(synced_txids);
84,915✔
1754
        txids
84,915✔
1755
    }
84,915✔
1756

1757
    /// Get all unhandled message signatures.
1758
    /// This is unique per message.
1759
    fn all_msg_sigs(&self) -> HashSet<MessageSignature> {
84,915✔
1760
        self.unhandled_messages
84,915✔
1761
            .values()
84,915✔
1762
            .map(|msgs| {
84,915✔
1763
                msgs.iter()
15,774✔
1764
                    .map(|msg| msg.preamble.signature.clone())
20,850✔
1765
                    .collect::<HashSet<_>>()
15,774✔
1766
            })
15,774✔
1767
            .fold(HashSet::new(), |mut acc, next| {
84,915✔
1768
                acc.extend(next);
15,774✔
1769
                acc
15,774✔
1770
            })
15,774✔
1771
    }
84,915✔
1772

1773
    /// Merge self into `newer`, and return `newer`.
1774
    /// deduplicate messages when possible.
1775
    pub fn update(mut self, mut newer: NetworkResult) -> Self {
84,915✔
1776
        // merge unhandled messaegs, but deduplicate
1777
        let newer_msgs = newer.all_msg_sigs();
84,915✔
1778
        for (nk, mut msgs) in self.unhandled_messages.drain() {
84,915✔
1779
            msgs.retain(|msg| {
41,035✔
1780
                let retain = !newer_msgs.contains(&msg.preamble.signature);
41,035✔
1781
                if !retain {
41,035✔
1782
                    debug!(
3✔
1783
                        "Drop duplicate p2p message {} seq {}",
1784
                        &msg.get_message_name(),
×
1785
                        &msg.preamble.seq
×
1786
                    );
1787
                }
41,032✔
1788
                retain
41,035✔
1789
            });
41,035✔
1790
            if let Some(newer_msgs) = newer.unhandled_messages.get_mut(&nk) {
23,773✔
1791
                newer_msgs.append(&mut msgs);
5,664✔
1792
            } else {
18,111✔
1793
                newer.unhandled_messages.insert(nk, msgs);
18,109✔
1794
            }
18,109✔
1795
        }
1796

1797
        let newer_blocks = newer.all_block_ids();
84,915✔
1798
        let newer_microblocks = newer.all_microblock_hashes();
84,915✔
1799
        let newer_naka_blocks = newer.all_nakamoto_block_ids();
84,915✔
1800
        let newer_txids = newer.all_txids();
84,915✔
1801

1802
        // only retain blocks not found in `newer`
1803
        self.blocks.retain(|(ch, blk, _)| {
84,915✔
1804
            let block_id = StacksBlockId::new(ch, &blk.block_hash());
544✔
1805
            let retain = !newer_blocks.contains(&block_id);
544✔
1806
            if !retain {
544✔
1807
                debug!("Drop duplicate downloaded block {}", &block_id);
3✔
1808
            }
541✔
1809
            retain
544✔
1810
        });
544✔
1811
        newer.blocks.append(&mut self.blocks);
84,915✔
1812

1813
        // merge microblocks, but deduplicate
1814
        self.confirmed_microblocks
84,915✔
1815
            .retain_mut(|(_, ref mut mblocks, _)| {
84,915✔
1816
                mblocks.retain(|mblk| {
4✔
1817
                    let retain = !newer_microblocks.contains(&mblk.block_hash());
4✔
1818
                    if !retain {
4✔
1819
                        debug!(
3✔
1820
                            "Drop duplicate downloaded microblock {}",
1821
                            &mblk.block_hash()
×
1822
                        );
1823
                    }
1✔
1824
                    retain
4✔
1825
                });
4✔
1826
                !mblocks.is_empty()
4✔
1827
            });
4✔
1828
        newer
84,915✔
1829
            .confirmed_microblocks
84,915✔
1830
            .append(&mut self.confirmed_microblocks);
84,915✔
1831

1832
        // merge nakamoto blocks, but deduplicate
1833
        self.nakamoto_blocks.retain(|_, nblk| {
84,915✔
1834
            let retain = !newer_naka_blocks.contains(&nblk.block_id());
79✔
1835
            if !retain {
79✔
1836
                debug!(
6✔
1837
                    "Drop duplicate downloaded nakamoto block {}",
1838
                    &nblk.block_id()
×
1839
                );
1840
            }
73✔
1841
            retain
79✔
1842
        });
79✔
1843
        newer.nakamoto_blocks.extend(self.nakamoto_blocks.drain());
84,915✔
1844

1845
        // merge pushed transactions, but deduplicate
1846
        for (nk, mut tx_data) in self.pushed_transactions.drain() {
84,915✔
1847
            tx_data.retain(|(_, tx)| {
193✔
1848
                let retain = !newer_txids.contains(&tx.txid());
193✔
1849
                if !retain {
193✔
1850
                    debug!("Drop duplicate pushed transaction {}", &tx.txid());
3✔
1851
                }
190✔
1852
                retain
193✔
1853
            });
193✔
1854
            if tx_data.is_empty() {
193✔
1855
                continue;
3✔
1856
            }
190✔
1857

1858
            if let Some(newer_tx_data) = newer.pushed_transactions.get_mut(&nk) {
190✔
1859
                newer_tx_data.append(&mut tx_data);
×
1860
            } else {
190✔
1861
                newer.pushed_transactions.insert(nk, tx_data);
190✔
1862
            }
190✔
1863
        }
1864

1865
        // merge pushed blocks, but deduplicate
1866
        for (nk, mut block_list) in self.pushed_blocks.drain() {
84,915✔
1867
            block_list.retain_mut(|ref mut block_data| {
4✔
1868
                block_data.blocks.retain(|blk_datum| {
4✔
1869
                    let block_id = StacksBlockId::new(&blk_datum.0, &blk_datum.1.block_hash());
4✔
1870
                    let retain = !newer_blocks.contains(&block_id);
4✔
1871
                    if !retain {
4✔
1872
                        debug!("Drop duplicate pushed block {}", &block_id);
3✔
1873
                    }
1✔
1874
                    retain
4✔
1875
                });
4✔
1876
                !block_data.blocks.is_empty()
4✔
1877
            });
4✔
1878
            if block_list.is_empty() {
4✔
1879
                continue;
3✔
1880
            }
1✔
1881

1882
            if let Some(newer_block_data) = newer.pushed_blocks.get_mut(&nk) {
1✔
1883
                newer_block_data.append(&mut block_list);
×
1884
            } else {
1✔
1885
                newer.pushed_blocks.insert(nk, block_list);
1✔
1886
            }
1✔
1887
        }
1888

1889
        // merge pushed microblocks, but deduplicate
1890
        for (nk, mut microblock_data) in self.pushed_microblocks.drain() {
84,915✔
1891
            microblock_data.retain_mut(|(_, ref mut mblock_data)| {
4✔
1892
                mblock_data.microblocks.retain(|mblk| {
4✔
1893
                    let retain = !newer_microblocks.contains(&mblk.block_hash());
4✔
1894
                    if !retain {
4✔
1895
                        debug!("Drop duplicate pushed microblock {}", &mblk.block_hash());
3✔
1896
                    }
1✔
1897
                    retain
4✔
1898
                });
4✔
1899
                !mblock_data.microblocks.is_empty()
4✔
1900
            });
4✔
1901
            if microblock_data.is_empty() {
4✔
1902
                continue;
3✔
1903
            }
1✔
1904

1905
            if let Some(newer_microblock_data) = newer.pushed_microblocks.get_mut(&nk) {
1✔
1906
                newer_microblock_data.append(&mut microblock_data);
×
1907
            } else {
1✔
1908
                newer.pushed_microblocks.insert(nk, microblock_data);
1✔
1909
            }
1✔
1910
        }
1911

1912
        // merge pushed nakamoto blocks, but deduplicate
1913
        for (nk, mut nakamoto_block_data) in self.pushed_nakamoto_blocks.drain() {
84,915✔
1914
            nakamoto_block_data.retain_mut(|(_, ref mut naka_blocks)| {
664✔
1915
                naka_blocks.blocks.retain(|nblk| {
664✔
1916
                    let retain = !newer_naka_blocks.contains(&nblk.block_id());
664✔
1917
                    if !retain {
664✔
1918
                        debug!("Drop duplicate pushed nakamoto block {}", &nblk.block_id());
6✔
1919
                    }
658✔
1920
                    retain
664✔
1921
                });
664✔
1922
                !naka_blocks.blocks.is_empty()
664✔
1923
            });
664✔
1924
            if nakamoto_block_data.is_empty() {
664✔
1925
                continue;
6✔
1926
            }
658✔
1927

1928
            if let Some(newer_nakamoto_data) = newer.pushed_nakamoto_blocks.get_mut(&nk) {
658✔
1929
                newer_nakamoto_data.append(&mut nakamoto_block_data);
×
1930
            } else {
658✔
1931
                newer.pushed_nakamoto_blocks.insert(nk, nakamoto_block_data);
658✔
1932
            }
658✔
1933
        }
1934

1935
        // merge uploaded data, but deduplicate
1936
        self.uploaded_transactions.retain(|tx| {
84,915✔
1937
            let retain = !newer_txids.contains(&tx.txid());
4✔
1938
            if !retain {
4✔
1939
                debug!("Drop duplicate uploaded transaction {}", &tx.txid());
3✔
1940
            }
1✔
1941
            retain
4✔
1942
        });
4✔
1943
        self.uploaded_blocks.retain_mut(|ref mut blk_data| {
84,915✔
1944
            blk_data.blocks.retain(|blk| {
4✔
1945
                let block_id = StacksBlockId::new(&blk.0, &blk.1.block_hash());
4✔
1946
                let retain = !newer_blocks.contains(&block_id);
4✔
1947
                if !retain {
4✔
1948
                    debug!("Drop duplicate uploaded block {}", &block_id);
3✔
1949
                }
1✔
1950
                retain
4✔
1951
            });
4✔
1952

1953
            !blk_data.blocks.is_empty()
4✔
1954
        });
4✔
1955
        self.uploaded_microblocks.retain_mut(|ref mut mblock_data| {
84,915✔
1956
            mblock_data.microblocks.retain(|mblk| {
4✔
1957
                let retain = !newer_microblocks.contains(&mblk.block_hash());
4✔
1958
                if !retain {
4✔
1959
                    debug!("Drop duplicate uploaded microblock {}", &mblk.block_hash());
3✔
1960
                }
1✔
1961
                retain
4✔
1962
            });
4✔
1963

1964
            !mblock_data.microblocks.is_empty()
4✔
1965
        });
4✔
1966
        self.uploaded_nakamoto_blocks.retain(|nblk| {
84,924✔
1967
            let retain = !newer_naka_blocks.contains(&nblk.block_id());
2,356✔
1968
            if !retain {
2,356✔
1969
                debug!(
1,374✔
1970
                    "Drop duplicate uploaded nakamoto block {}",
1971
                    &nblk.block_id()
×
1972
                );
1973
            }
982✔
1974
            retain
2,356✔
1975
        });
2,356✔
1976

1977
        newer
84,915✔
1978
            .uploaded_transactions
84,915✔
1979
            .append(&mut self.uploaded_transactions);
84,915✔
1980
        newer.uploaded_blocks.append(&mut self.uploaded_blocks);
84,915✔
1981
        newer
84,915✔
1982
            .uploaded_microblocks
84,915✔
1983
            .append(&mut self.uploaded_microblocks);
84,915✔
1984
        newer
84,915✔
1985
            .uploaded_nakamoto_blocks
84,915✔
1986
            .append(&mut self.uploaded_nakamoto_blocks);
84,915✔
1987

1988
        // merge uploaded/pushed stackerdb, but drop stale versions
1989
        let newer_stackerdb_chunk_versions: HashMap<_, _> = newer
84,915✔
1990
            .uploaded_stackerdb_chunks
84,915✔
1991
            .iter()
84,915✔
1992
            .chain(newer.pushed_stackerdb_chunks.iter())
84,915✔
1993
            .map(|chunk| {
84,918✔
1994
                (
17,868✔
1995
                    (
17,868✔
1996
                        chunk.contract_id.clone(),
17,868✔
1997
                        chunk.rc_consensus_hash.clone(),
17,868✔
1998
                        chunk.chunk_data.slot_id,
17,868✔
1999
                    ),
17,868✔
2000
                    chunk.chunk_data.slot_version,
17,868✔
2001
                )
17,868✔
2002
            })
17,868✔
2003
            .collect();
84,915✔
2004

2005
        self.uploaded_stackerdb_chunks.retain(|push_chunk| {
84,915✔
2006
            if push_chunk.rc_consensus_hash != newer.rc_consensus_hash {
2,453✔
2007
                debug!(
4✔
2008
                    "Drop pushed StackerDB chunk for {} due to stale view ({} != {}): {:?}",
2009
                    &push_chunk.contract_id,
×
2010
                    &push_chunk.rc_consensus_hash,
×
2011
                    &newer.rc_consensus_hash,
×
2012
                    &push_chunk.chunk_data
×
2013
                );
2014
                return false;
4✔
2015
            }
2,449✔
2016
            if let Some(version) = newer_stackerdb_chunk_versions.get(&(
2,449✔
2017
                push_chunk.contract_id.clone(),
2,449✔
2018
                push_chunk.rc_consensus_hash.clone(),
2,449✔
2019
                push_chunk.chunk_data.slot_id,
2,449✔
2020
            )) {
2,449✔
2021
                let retain = push_chunk.chunk_data.slot_version > *version;
19✔
2022
                if !retain {
19✔
2023
                    debug!(
19✔
2024
                        "Drop pushed StackerDB chunk for {} due to stale version: {:?}",
2025
                        &push_chunk.contract_id, &push_chunk.chunk_data
×
2026
                    );
2027
                }
×
2028
                retain
19✔
2029
            } else {
2030
                true
2,430✔
2031
            }
2032
        });
2,453✔
2033

2034
        self.pushed_stackerdb_chunks.retain(|push_chunk| {
84,915✔
2035
            if push_chunk.rc_consensus_hash != newer.rc_consensus_hash {
36,734✔
2036
                debug!(
245✔
2037
                    "Drop uploaded StackerDB chunk for {} due to stale view ({} != {}): {:?}",
2038
                    &push_chunk.contract_id,
×
2039
                    &push_chunk.rc_consensus_hash,
×
2040
                    &newer.rc_consensus_hash,
×
2041
                    &push_chunk.chunk_data
×
2042
                );
2043
                return false;
245✔
2044
            }
36,489✔
2045
            if let Some(version) = newer_stackerdb_chunk_versions.get(&(
36,489✔
2046
                push_chunk.contract_id.clone(),
36,489✔
2047
                push_chunk.rc_consensus_hash.clone(),
36,489✔
2048
                push_chunk.chunk_data.slot_id,
36,489✔
2049
            )) {
36,489✔
2050
                let retain = push_chunk.chunk_data.slot_version > *version;
669✔
2051
                if !retain {
669✔
2052
                    debug!(
669✔
2053
                        "Drop uploaded StackerDB chunk for {} due to stale version: {:?}",
2054
                        &push_chunk.contract_id, &push_chunk.chunk_data
×
2055
                    );
2056
                }
×
2057
                retain
669✔
2058
            } else {
2059
                true
35,820✔
2060
            }
2061
        });
36,734✔
2062

2063
        newer
84,915✔
2064
            .uploaded_stackerdb_chunks
84,915✔
2065
            .append(&mut self.uploaded_stackerdb_chunks);
84,915✔
2066
        newer
84,915✔
2067
            .pushed_stackerdb_chunks
84,915✔
2068
            .append(&mut self.pushed_stackerdb_chunks);
84,915✔
2069

2070
        // dedup sync'ed transactions
2071
        self.synced_transactions.retain(|tx| {
84,915✔
2072
            let retain = !newer_txids.contains(&tx.txid());
49✔
2073
            if !retain {
49✔
2074
                debug!("Drop duplicate sync'ed transaction {}", &tx.txid());
3✔
2075
            }
46✔
2076
            retain
49✔
2077
        });
49✔
2078

2079
        newer
84,915✔
2080
            .synced_transactions
84,915✔
2081
            .append(&mut self.synced_transactions);
84,915✔
2082

2083
        // no dedup here, but do merge
2084
        newer
84,915✔
2085
            .stacker_db_sync_results
84,915✔
2086
            .append(&mut self.stacker_db_sync_results);
84,915✔
2087
        newer.attachments.append(&mut self.attachments);
84,915✔
2088

2089
        newer
84,915✔
2090
    }
84,915✔
2091

2092
    pub fn has_blocks(&self) -> bool {
8,081,804✔
2093
        !self.blocks.is_empty() || !self.pushed_blocks.is_empty()
8,081,804✔
2094
    }
8,081,804✔
2095

2096
    pub fn has_microblocks(&self) -> bool {
16,645,426✔
2097
        !self.confirmed_microblocks.is_empty()
16,645,426✔
2098
            || !self.pushed_microblocks.is_empty()
16,645,426✔
2099
            || !self.uploaded_microblocks.is_empty()
16,645,424✔
2100
    }
16,645,426✔
2101

2102
    pub fn has_nakamoto_blocks(&self) -> bool {
8,064,405✔
2103
        !self.nakamoto_blocks.is_empty()
8,064,405✔
2104
            || !self.pushed_nakamoto_blocks.is_empty()
8,064,369✔
2105
            || !self.uploaded_nakamoto_blocks.is_empty()
8,063,649✔
2106
    }
8,064,405✔
2107

2108
    pub fn has_transactions(&self) -> bool {
7,981,751✔
2109
        !self.pushed_transactions.is_empty()
7,981,751✔
2110
            || !self.uploaded_transactions.is_empty()
7,980,147✔
2111
            || !self.synced_transactions.is_empty()
7,969,950✔
2112
    }
7,981,751✔
2113

2114
    pub fn has_attachments(&self) -> bool {
16,542,324✔
2115
        !self.attachments.is_empty()
16,542,324✔
2116
    }
16,542,324✔
2117

2118
    pub fn has_stackerdb_chunks(&self) -> bool {
7,967,790✔
2119
        self.stacker_db_sync_results
7,967,790✔
2120
            .iter()
7,967,790✔
2121
            .any(|x| !x.chunks_to_store.is_empty())
9,131,058✔
2122
            || !self.uploaded_stackerdb_chunks.is_empty()
7,932,942✔
2123
            || !self.pushed_stackerdb_chunks.is_empty()
7,687,467✔
2124
    }
7,967,790✔
2125

2126
    pub fn transactions(&self) -> Vec<StacksTransaction> {
9✔
2127
        self.pushed_transactions
9✔
2128
            .values()
9✔
2129
            .flat_map(|pushed_txs| pushed_txs.iter().map(|(_, tx)| tx.clone()))
9✔
2130
            .chain(self.uploaded_transactions.iter().cloned())
9✔
2131
            .chain(self.synced_transactions.iter().cloned())
9✔
2132
            .collect()
9✔
2133
    }
9✔
2134

2135
    pub fn has_data_to_store(&self) -> bool {
7,996,887✔
2136
        self.has_blocks()
7,996,887✔
2137
            || self.has_microblocks()
7,979,661✔
2138
            || self.has_nakamoto_blocks()
7,979,652✔
2139
            || self.has_transactions()
7,979,589✔
2140
            || self.has_attachments()
7,967,790✔
2141
            || self.has_stackerdb_chunks()
7,967,790✔
2142
    }
7,996,887✔
2143

2144
    pub fn has_block_data_to_store(&self) -> bool {
84,915✔
2145
        self.has_blocks() || self.has_microblocks() || self.has_nakamoto_blocks()
84,915✔
2146
    }
84,915✔
2147

2148
    pub fn consume_unsolicited(&mut self, unhandled_messages: PendingMessages) {
20,770,821✔
2149
        for ((_event_id, neighbor_key), messages) in unhandled_messages.into_iter() {
20,770,821✔
2150
            for message in messages.into_iter() {
1,252,376✔
2151
                match message.payload {
1,252,376✔
2152
                    StacksMessageType::Blocks(block_data) => {
17,344✔
2153
                        if let Some(blocks_msgs) = self.pushed_blocks.get_mut(&neighbor_key) {
17,344✔
2154
                            blocks_msgs.push(block_data);
2,718✔
2155
                        } else {
14,626✔
2156
                            self.pushed_blocks
14,626✔
2157
                                .insert(neighbor_key.clone(), vec![block_data]);
14,626✔
2158
                        }
14,626✔
2159
                    }
2160
                    StacksMessageType::Microblocks(mblock_data) => {
1✔
2161
                        if let Some(mblocks_msgs) = self.pushed_microblocks.get_mut(&neighbor_key) {
1✔
2162
                            mblocks_msgs.push((message.relayers, mblock_data));
×
2163
                        } else {
1✔
2164
                            self.pushed_microblocks.insert(
1✔
2165
                                neighbor_key.clone(),
1✔
2166
                                vec![(message.relayers, mblock_data)],
1✔
2167
                            );
1✔
2168
                        }
1✔
2169
                    }
2170
                    StacksMessageType::Transaction(tx_data) => {
4,510✔
2171
                        if let Some(tx_msgs) = self.pushed_transactions.get_mut(&neighbor_key) {
4,510✔
2172
                            tx_msgs.push((message.relayers, tx_data));
18✔
2173
                        } else {
4,492✔
2174
                            self.pushed_transactions
4,492✔
2175
                                .insert(neighbor_key.clone(), vec![(message.relayers, tx_data)]);
4,492✔
2176
                        }
4,492✔
2177
                    }
2178
                    StacksMessageType::NakamotoBlocks(block_data) => {
7,631✔
2179
                        if let Some(nakamoto_blocks_msgs) =
1✔
2180
                            self.pushed_nakamoto_blocks.get_mut(&neighbor_key)
7,631✔
2181
                        {
1✔
2182
                            nakamoto_blocks_msgs.push((message.relayers, block_data));
1✔
2183
                        } else {
7,630✔
2184
                            self.pushed_nakamoto_blocks
7,630✔
2185
                                .insert(neighbor_key.clone(), vec![(message.relayers, block_data)]);
7,630✔
2186
                        }
7,630✔
2187
                    }
2188
                    StacksMessageType::StackerDBPushChunk(chunk_data) => {
487,931✔
2189
                        self.pushed_stackerdb_chunks.push(chunk_data)
487,931✔
2190
                    }
2191
                    _ => {
2192
                        // forward along
2193
                        if let Some(messages) = self.unhandled_messages.get_mut(&neighbor_key) {
734,959✔
2194
                            messages.push(message);
127,423✔
2195
                        } else {
619,128✔
2196
                            self.unhandled_messages
607,536✔
2197
                                .insert(neighbor_key.clone(), vec![message]);
607,536✔
2198
                        }
607,536✔
2199
                    }
2200
                }
2201
            }
2202
        }
2203
    }
20,770,821✔
2204

2205
    pub fn consume_http_uploads(&mut self, msgs: Vec<StacksMessageType>) {
10,385,392✔
2206
        for msg in msgs.into_iter() {
10,385,392✔
2207
            match msg {
701,013✔
2208
                StacksMessageType::Transaction(tx_data) => {
32,041✔
2209
                    self.uploaded_transactions.push(tx_data);
32,041✔
2210
                }
32,041✔
2211
                StacksMessageType::Blocks(block_data) => {
1✔
2212
                    self.uploaded_blocks.push(block_data);
1✔
2213
                }
1✔
2214
                StacksMessageType::Microblocks(mblock_data) => {
10✔
2215
                    self.uploaded_microblocks.push(mblock_data);
10✔
2216
                }
10✔
2217
                StacksMessageType::StackerDBPushChunk(chunk_data) => {
590,112✔
2218
                    self.uploaded_stackerdb_chunks.push(chunk_data);
590,112✔
2219
                }
590,112✔
2220
                StacksMessageType::NakamotoBlocks(data) => {
78,849✔
2221
                    self.uploaded_nakamoto_blocks.extend(data.blocks);
78,849✔
2222
                }
78,849✔
2223
                _ => {
2224
                    // drop
2225
                    warn!("Dropping unknown HTTP message");
×
2226
                }
2227
            }
2228
        }
2229
    }
10,385,392✔
2230

2231
    pub fn consume_stacker_db_sync_results(&mut self, mut msgs: Vec<StackerDBSyncResult>) {
8,384,073✔
2232
        self.stacker_db_sync_results.append(&mut msgs);
8,384,073✔
2233
    }
8,384,073✔
2234

2235
    pub fn consume_nakamoto_blocks(&mut self, blocks: HashMap<ConsensusHash, Vec<NakamotoBlock>>) {
2,253,336✔
2236
        for (_ch, blocks) in blocks.into_iter() {
2,253,336✔
2237
            for block in blocks.into_iter() {
3,678✔
2238
                let block_id = block.block_id();
3,678✔
2239
                if self.nakamoto_blocks.contains_key(&block_id) {
3,678✔
2240
                    continue;
×
2241
                }
3,678✔
2242
                self.nakamoto_blocks.insert(block_id, block);
3,678✔
2243
            }
2244
        }
2245
    }
2,253,336✔
2246
}
2247

2248
pub trait Requestable: std::fmt::Display {
2249
    fn get_url(&self) -> &UrlString;
2250

2251
    fn make_request_type(&self, peer_host: PeerHost) -> StacksHttpRequest;
2252
}
2253

2254
#[cfg(test)]
2255
pub mod test {
2256
    use std::collections::HashMap;
2257
    use std::io::{Cursor, ErrorKind, Read, Write};
2258
    use std::net::*;
2259
    use std::ops::{Deref, DerefMut};
2260
    use std::sync::Mutex;
2261
    use std::{io, thread};
2262

2263
    use clarity::types::sqlite::NO_PARAMS;
2264
    use clarity::vm::costs::ExecutionCost;
2265
    use clarity::vm::types::*;
2266
    use mio;
2267
    use rand::{self, RngCore};
2268
    use stacks_common::codec::StacksMessageCodec;
2269
    use stacks_common::deps_common::bitcoin::network::serialize::BitcoinHash;
2270
    use stacks_common::types::StacksEpochId;
2271
    use stacks_common::util::hash::*;
2272
    use stacks_common::util::secp256k1::*;
2273
    use stacks_common::util::vrf::*;
2274

2275
    use super::*;
2276
    use crate::burnchains::bitcoin::indexer::BitcoinIndexer;
2277
    use crate::burnchains::db::{BurnchainDB, BurnchainHeaderReader};
2278
    use crate::burnchains::tests::*;
2279
    use crate::burnchains::*;
2280
    use crate::chainstate::burn::db::sortdb::*;
2281
    use crate::chainstate::burn::operations::*;
2282
    use crate::chainstate::burn::*;
2283
    use crate::chainstate::coordinator::{Error as coordinator_error, *};
2284
    use crate::chainstate::stacks::address::PoxAddress;
2285
    use crate::chainstate::stacks::boot::test::get_parent_tip;
2286
    use crate::chainstate::stacks::boot::*;
2287
    use crate::chainstate::stacks::db::accounts::MinerReward;
2288
    use crate::chainstate::stacks::db::{StacksChainState, *};
2289
    use crate::chainstate::stacks::events::{StacksBlockEventData, StacksTransactionReceipt};
2290
    use crate::chainstate::stacks::tests::chain_histories::mine_smart_contract_block_contract_call_microblock;
2291
    use crate::chainstate::stacks::tests::*;
2292
    use crate::chainstate::stacks::{StacksMicroblockHeader, *};
2293
    use crate::chainstate::tests::{TestChainstate, TestChainstateConfig};
2294
    use crate::core::{StacksEpoch, StacksEpochExtension};
2295
    use crate::cost_estimates::metrics::UnitMetric;
2296
    use crate::cost_estimates::tests::fee_rate_fuzzer::ConstantFeeEstimator;
2297
    use crate::cost_estimates::UnitEstimator;
2298
    use crate::net::asn::*;
2299
    use crate::net::atlas::*;
2300
    use crate::net::chat::*;
2301
    use crate::net::connection::*;
2302
    use crate::net::db::*;
2303
    use crate::net::relay::*;
2304
    use crate::net::stackerdb::{StackerDBSync, StackerDBs};
2305
    use crate::net::Error as net_error;
2306
    use crate::util_lib::strings::*;
2307

2308
    impl StacksMessageCodec for BlockstackOperationType {
2309
        fn consensus_serialize<W: Write>(&self, fd: &mut W) -> Result<(), codec_error> {
×
2310
            match self {
×
2311
                BlockstackOperationType::LeaderKeyRegister(ref op) => op.consensus_serialize(fd),
×
2312
                BlockstackOperationType::LeaderBlockCommit(ref op) => op.consensus_serialize(fd),
×
2313
                BlockstackOperationType::TransferStx(_)
2314
                | BlockstackOperationType::DelegateStx(_)
2315
                | BlockstackOperationType::PreStx(_)
2316
                | BlockstackOperationType::VoteForAggregateKey(_)
2317
                | BlockstackOperationType::StackStx(_) => Ok(()),
×
2318
            }
2319
        }
×
2320

2321
        fn consensus_deserialize<R: Read>(
×
2322
            fd: &mut R,
×
2323
        ) -> Result<BlockstackOperationType, codec_error> {
×
2324
            panic!("not used");
×
2325
        }
2326
    }
2327

2328
    // emulate a socket
2329
    pub struct NetCursor<T> {
2330
        c: Cursor<T>,
2331
        closed: bool,
2332
        block: bool,
2333
        read_error: Option<io::ErrorKind>,
2334
        write_error: Option<io::ErrorKind>,
2335
    }
2336

2337
    impl<T> NetCursor<T> {
2338
        pub fn new(inner: T) -> NetCursor<T> {
8✔
2339
            NetCursor {
8✔
2340
                c: Cursor::new(inner),
8✔
2341
                closed: false,
8✔
2342
                block: false,
8✔
2343
                read_error: None,
8✔
2344
                write_error: None,
8✔
2345
            }
8✔
2346
        }
8✔
2347

2348
        pub fn close(&mut self) {
×
2349
            self.closed = true;
×
2350
        }
×
2351

2352
        pub fn block(&mut self) {
×
2353
            self.block = true;
×
2354
        }
×
2355

2356
        pub fn unblock(&mut self) {
×
2357
            self.block = false;
×
2358
        }
×
2359

2360
        pub fn set_read_error(&mut self, e: Option<io::ErrorKind>) {
×
2361
            self.read_error = e;
×
2362
        }
×
2363

2364
        pub fn set_write_error(&mut self, e: Option<io::ErrorKind>) {
×
2365
            self.write_error = e;
×
2366
        }
×
2367
    }
2368

2369
    impl<T> Deref for NetCursor<T> {
2370
        type Target = Cursor<T>;
2371
        fn deref(&self) -> &Cursor<T> {
1✔
2372
            &self.c
1✔
2373
        }
1✔
2374
    }
2375

2376
    impl<T> DerefMut for NetCursor<T> {
2377
        fn deref_mut(&mut self) -> &mut Cursor<T> {
1✔
2378
            &mut self.c
1✔
2379
        }
1✔
2380
    }
2381

2382
    impl<T> Read for NetCursor<T>
2383
    where
2384
        T: AsRef<[u8]>,
2385
    {
2386
        fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
4✔
2387
            if self.block {
4✔
2388
                return Err(io::Error::from(ErrorKind::WouldBlock));
×
2389
            }
4✔
2390
            if self.closed {
4✔
2391
                return Ok(0);
×
2392
            }
4✔
2393
            if let Some(ref e) = self.read_error {
4✔
2394
                return Err(io::Error::from(*e));
×
2395
            }
4✔
2396

2397
            let sz = self.c.read(buf)?;
4✔
2398
            if sz == 0 {
4✔
2399
                // when reading from a non-blocking socket, a return value of 0 indicates the
2400
                // remote end was closed.  For this reason, when we're out of bytes to read on our
2401
                // inner cursor, but still have bytes, we need to re-interpret this as EWOULDBLOCK.
2402
                return Err(io::Error::from(ErrorKind::WouldBlock));
2✔
2403
            } else {
2404
                return Ok(sz);
2✔
2405
            }
2406
        }
4✔
2407
    }
2408

2409
    impl Write for NetCursor<&mut [u8]> {
2410
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
32✔
2411
            if self.block {
32✔
2412
                return Err(io::Error::from(ErrorKind::WouldBlock));
×
2413
            }
32✔
2414
            if self.closed {
32✔
2415
                return Err(io::Error::from(ErrorKind::Other)); // EBADF
×
2416
            }
32✔
2417
            if let Some(ref e) = self.write_error {
32✔
2418
                return Err(io::Error::from(*e));
×
2419
            }
32✔
2420
            self.c.write(buf)
32✔
2421
        }
32✔
2422
        fn flush(&mut self) -> io::Result<()> {
×
2423
            self.c.flush()
×
2424
        }
×
2425
    }
2426

2427
    impl Write for NetCursor<&mut Vec<u8>> {
2428
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
×
2429
            self.c.write(buf)
×
2430
        }
×
2431
        fn flush(&mut self) -> io::Result<()> {
×
2432
            self.c.flush()
×
2433
        }
×
2434
    }
2435

2436
    impl Write for NetCursor<Vec<u8>> {
2437
        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
×
2438
            self.c.write(buf)
×
2439
        }
×
2440
        fn flush(&mut self) -> io::Result<()> {
×
2441
            self.c.flush()
×
2442
        }
×
2443
    }
2444

2445
    /// make a TCP server and a pair of TCP client sockets
2446
    pub fn make_tcp_sockets() -> (
2✔
2447
        mio::tcp::TcpListener,
2✔
2448
        mio::tcp::TcpStream,
2✔
2449
        mio::tcp::TcpStream,
2✔
2450
    ) {
2✔
2451
        let mut rng = rand::thread_rng();
2✔
2452
        let (std_listener, port) = {
2✔
2453
            let std_listener;
2454
            let mut next_port;
2455
            loop {
2456
                next_port = 1024 + (rng.next_u32() % (65535 - 1024));
2✔
2457
                let hostport = format!("127.0.0.1:{}", next_port);
2✔
2458
                std_listener = match std::net::TcpListener::bind(
2✔
2459
                    &hostport.parse::<std::net::SocketAddr>().unwrap(),
2✔
2460
                ) {
2✔
2461
                    Ok(sock) => sock,
2✔
2462
                    Err(e) => {
×
2463
                        if let io::ErrorKind::AddrInUse = e.kind() {
×
2464
                            continue;
×
2465
                        }
×
2466
                        panic!("TcpListener::bind({hostport}): {e:?}");
×
2467
                    }
2468
                };
2469
                break;
2✔
2470
            }
2471
            (std_listener, next_port)
2✔
2472
        };
2473

2474
        let std_sock_1 = std::net::TcpStream::connect(
2✔
2475
            &format!("127.0.0.1:{port}")
2✔
2476
                .parse::<std::net::SocketAddr>()
2✔
2477
                .unwrap(),
2✔
2478
        )
2479
        .unwrap();
2✔
2480
        let sock_1 = mio::tcp::TcpStream::from_stream(std_sock_1).unwrap();
2✔
2481
        let (std_sock_2, _) = std_listener.accept().unwrap();
2✔
2482
        let sock_2 = mio::tcp::TcpStream::from_stream(std_sock_2).unwrap();
2✔
2483

2484
        sock_1.set_nodelay(true).unwrap();
2✔
2485
        sock_2.set_nodelay(true).unwrap();
2✔
2486

2487
        let listener = mio::tcp::TcpListener::from_std(std_listener).unwrap();
2✔
2488

2489
        (listener, sock_1, sock_2)
2✔
2490
    }
2✔
2491

2492
    #[derive(Clone)]
2493
    pub struct TestEventObserverBlock {
2494
        pub block: StacksBlockEventData,
2495
        pub metadata: StacksHeaderInfo,
2496
        pub receipts: Vec<StacksTransactionReceipt>,
2497
        pub parent: StacksBlockId,
2498
        pub winner_txid: Txid,
2499
        pub matured_rewards: Vec<MinerReward>,
2500
        pub matured_rewards_info: Option<MinerRewardInfo>,
2501
        pub reward_set_data: Option<RewardSetData>,
2502
    }
2503

2504
    pub struct TestEventObserver {
2505
        blocks: Mutex<Vec<TestEventObserverBlock>>,
2506
    }
2507

2508
    impl TestEventObserver {
2509
        pub fn get_blocks(&self) -> Vec<TestEventObserverBlock> {
576✔
2510
            self.blocks.lock().unwrap().deref().to_vec()
576✔
2511
        }
576✔
2512

2513
        pub fn new() -> TestEventObserver {
461✔
2514
            TestEventObserver {
461✔
2515
                blocks: Mutex::new(vec![]),
461✔
2516
            }
461✔
2517
        }
461✔
2518
    }
2519

2520
    impl BlockEventDispatcher for TestEventObserver {
2521
        fn announce_block(
23,206✔
2522
            &self,
23,206✔
2523
            block: &StacksBlockEventData,
23,206✔
2524
            metadata: &StacksHeaderInfo,
23,206✔
2525
            receipts: &[events::StacksTransactionReceipt],
23,206✔
2526
            parent: &StacksBlockId,
23,206✔
2527
            winner_txid: &Txid,
23,206✔
2528
            matured_rewards: &[accounts::MinerReward],
23,206✔
2529
            matured_rewards_info: Option<&MinerRewardInfo>,
23,206✔
2530
            parent_burn_block_hash: &BurnchainHeaderHash,
23,206✔
2531
            parent_burn_block_height: u32,
23,206✔
2532
            parent_burn_block_timestamp: u64,
23,206✔
2533
            _anchor_block_cost: &ExecutionCost,
23,206✔
2534
            _confirmed_mblock_cost: &ExecutionCost,
23,206✔
2535
            pox_constants: &PoxConstants,
23,206✔
2536
            reward_set_data: &Option<RewardSetData>,
23,206✔
2537
            _signer_bitvec: &Option<BitVec<4000>>,
23,206✔
2538
            _block_timestamp: Option<u64>,
23,206✔
2539
            _coinbase_height: u64,
23,206✔
2540
        ) {
23,206✔
2541
            self.blocks.lock().unwrap().push(TestEventObserverBlock {
23,206✔
2542
                block: block.clone(),
23,206✔
2543
                metadata: metadata.clone(),
23,206✔
2544
                receipts: receipts.to_owned(),
23,206✔
2545
                parent: parent.clone(),
23,206✔
2546
                winner_txid: winner_txid.clone(),
23,206✔
2547
                matured_rewards: matured_rewards.to_owned(),
23,206✔
2548
                matured_rewards_info: matured_rewards_info.cloned(),
23,206✔
2549
                reward_set_data: reward_set_data.clone(),
23,206✔
2550
            })
23,206✔
2551
        }
23,206✔
2552

2553
        fn announce_burn_block(
10,138✔
2554
            &self,
10,138✔
2555
            _burn_block: &BurnchainHeaderHash,
10,138✔
2556
            _burn_block_height: u64,
10,138✔
2557
            _rewards: Vec<(PoxAddress, u64)>,
10,138✔
2558
            _burns: u64,
10,138✔
2559
            _pox_transactions: Vec<crate::chainstate::coordinator::PoxTransactionReward>,
10,138✔
2560
            _reward_recipients: Vec<PoxAddress>,
10,138✔
2561
            _consensus_hash: &ConsensusHash,
10,138✔
2562
            _parent_burn_block_hash: &BurnchainHeaderHash,
10,138✔
2563
        ) {
10,138✔
2564
            // pass
2565
        }
10,138✔
2566
    }
2567

2568
    const DEFAULT_RPC_HANDLER_ARGS: RPCHandlerArgs<'static> = RPCHandlerArgs {
2569
        exit_at_block_height: None,
2570
        genesis_chainstate_hash: Sha256Sum([0x00; 32]),
2571
        event_observer: None,
2572
        cost_estimator: None,
2573
        fee_estimator: None,
2574
        cost_metric: None,
2575
        coord_comms: None,
2576
    };
2577

2578
    const NULL_COST_ESTIMATOR: () = ();
2579
    const NULL_FEE_ESTIMATOR: () = ();
2580
    const NULL_COST_METRIC: UnitMetric = UnitMetric {};
2581
    const NULL_RPC_HANDLER_ARGS: RPCHandlerArgs<'static> = RPCHandlerArgs {
2582
        exit_at_block_height: None,
2583
        genesis_chainstate_hash: Sha256Sum([0x00; 32]),
2584
        event_observer: None,
2585
        cost_estimator: Some(&NULL_COST_ESTIMATOR),
2586
        fee_estimator: Some(&NULL_FEE_ESTIMATOR),
2587
        cost_metric: Some(&NULL_COST_METRIC),
2588
        coord_comms: None,
2589
    };
2590

2591
    const UNIT_COST_ESTIMATOR: UnitEstimator = UnitEstimator {};
2592
    const CONSTANT_FEE_ESTIMATOR: ConstantFeeEstimator = ConstantFeeEstimator {};
2593
    const UNIT_COST_METRIC: UnitMetric = UnitMetric {};
2594
    const UNIT_RPC_HANDLER_ARGS: RPCHandlerArgs<'static> = RPCHandlerArgs {
2595
        exit_at_block_height: None,
2596
        genesis_chainstate_hash: Sha256Sum([0x00; 32]),
2597
        event_observer: None,
2598
        cost_estimator: Some(&UNIT_COST_ESTIMATOR),
2599
        fee_estimator: Some(&CONSTANT_FEE_ESTIMATOR),
2600
        cost_metric: Some(&UNIT_COST_METRIC),
2601
        coord_comms: None,
2602
    };
2603

2604
    /// Templates for RPC Handler Args (which must be owned by the TestPeer, and cannot be a bare
2605
    /// RPCHandlerArgs since references to the inner members cannot be made thread-safe).
2606
    #[derive(Clone, Debug, PartialEq)]
2607
    pub enum RPCHandlerArgsType {
2608
        Default,
2609
        Null,
2610
        Unit,
2611
    }
2612

2613
    impl RPCHandlerArgsType {
2614
        pub fn instantiate(&self) -> RPCHandlerArgs<'static> {
6✔
2615
            match self {
6✔
2616
                Self::Default => {
2617
                    debug!("Default RPC Handler Args");
×
2618
                    DEFAULT_RPC_HANDLER_ARGS.clone()
×
2619
                }
2620
                Self::Null => {
2621
                    debug!("Null RPC Handler Args");
3✔
2622
                    NULL_RPC_HANDLER_ARGS.clone()
3✔
2623
                }
2624
                Self::Unit => {
2625
                    debug!("Unit RPC Handler Args");
3✔
2626
                    UNIT_RPC_HANDLER_ARGS.clone()
3✔
2627
                }
2628
            }
2629
        }
6✔
2630

2631
        pub fn make_default() -> RPCHandlerArgs<'static> {
62,088✔
2632
            debug!("Default RPC Handler Args");
62,088✔
2633
            DEFAULT_RPC_HANDLER_ARGS.clone()
62,088✔
2634
        }
62,088✔
2635
    }
2636

2637
    // describes a peer's initial configuration
2638
    #[derive(Debug, Clone)]
2639
    pub struct TestPeerConfig {
2640
        pub chain_config: TestChainstateConfig,
2641
        pub peer_version: u32,
2642
        pub private_key: Secp256k1PrivateKey,
2643
        pub private_key_expire: u64,
2644
        pub initial_neighbors: Vec<Neighbor>,
2645
        pub asn4_entries: Vec<ASEntry4>,
2646
        pub connection_opts: ConnectionOptions,
2647
        pub server_port: u16,
2648
        pub http_port: u16,
2649
        pub asn: u32,
2650
        pub org: u32,
2651
        pub allowed: i64,
2652
        pub denied: i64,
2653
        pub data_url: UrlString,
2654
        pub setup_code: String,
2655
        /// If some(), TestPeer should check the PoX-2 invariants
2656
        /// on cycle numbers bounded (inclusive) by the supplied u64s
2657
        pub check_pox_invariants: Option<(u64, u64)>,
2658
        /// Which stacker DBs will this peer replicate?
2659
        pub stacker_dbs: Vec<QualifiedContractIdentifier>,
2660
        /// Stacker DB configurations for each stacker_dbs entry above, if different from
2661
        /// StackerDBConfig::noop()
2662
        pub stacker_db_configs: Vec<Option<StackerDBConfig>>,
2663
        /// What services should this peer support?
2664
        pub services: u16,
2665
    }
2666

2667
    impl Default for TestPeerConfig {
2668
        fn default() -> Self {
632✔
2669
            let conn_opts = ConnectionOptions::default();
632✔
2670
            Self {
632✔
2671
                chain_config: TestChainstateConfig::default(),
632✔
2672
                peer_version: 0x01020304,
632✔
2673
                private_key: Secp256k1PrivateKey::random(),
632✔
2674
                private_key_expire: conn_opts.private_key_lifetime,
632✔
2675
                initial_neighbors: vec![],
632✔
2676
                asn4_entries: vec![],
632✔
2677
                connection_opts: conn_opts,
632✔
2678
                server_port: 32000,
632✔
2679
                http_port: 32001,
632✔
2680
                asn: 0,
632✔
2681
                org: 0,
632✔
2682
                allowed: 0,
632✔
2683
                denied: 0,
632✔
2684
                data_url: UrlString::from_literal(""),
632✔
2685
                setup_code: "".into(),
632✔
2686
                check_pox_invariants: None,
632✔
2687
                stacker_db_configs: vec![],
632✔
2688
                stacker_dbs: vec![],
632✔
2689
                services: (ServiceFlags::RELAY as u16)
632✔
2690
                    | (ServiceFlags::RPC as u16)
632✔
2691
                    | (ServiceFlags::STACKERDB as u16),
632✔
2692
            }
632✔
2693
        }
632✔
2694
    }
2695

2696
    impl TestPeerConfig {
2697
        pub fn from_port(p: u16) -> TestPeerConfig {
205✔
2698
            let mut config = TestPeerConfig {
205✔
2699
                server_port: p,
205✔
2700
                http_port: p + 1,
205✔
2701
                ..TestPeerConfig::default()
205✔
2702
            };
205✔
2703
            config.data_url =
205✔
2704
                UrlString::try_from(format!("http://127.0.0.1:{}", config.http_port).as_str())
205✔
2705
                    .unwrap();
205✔
2706
            config
205✔
2707
        }
205✔
2708

2709
        pub fn new(test_name: &str, p2p_port: u16, rpc_port: u16) -> TestPeerConfig {
421✔
2710
            let mut config = TestPeerConfig {
421✔
2711
                chain_config: TestChainstateConfig::new(test_name),
421✔
2712
                server_port: p2p_port,
421✔
2713
                http_port: rpc_port,
421✔
2714
                ..TestPeerConfig::default()
421✔
2715
            };
421✔
2716
            config.data_url =
421✔
2717
                UrlString::try_from(format!("http://127.0.0.1:{}", config.http_port).as_str())
421✔
2718
                    .unwrap();
421✔
2719
            config
421✔
2720
        }
421✔
2721

2722
        pub fn add_neighbor(&mut self, n: &Neighbor) {
451✔
2723
            self.initial_neighbors.push(n.clone());
451✔
2724
        }
451✔
2725

2726
        pub fn to_neighbor(&self) -> Neighbor {
1,356,759✔
2727
            Neighbor {
1,356,759✔
2728
                addr: NeighborKey {
1,356,759✔
2729
                    peer_version: self.peer_version,
1,356,759✔
2730
                    network_id: self.chain_config.network_id,
1,356,759✔
2731
                    addrbytes: PeerAddress([
1,356,759✔
2732
                        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1,
1,356,759✔
2733
                    ]),
1,356,759✔
2734
                    port: self.server_port,
1,356,759✔
2735
                },
1,356,759✔
2736
                public_key: Secp256k1PublicKey::from_private(&self.private_key),
1,356,759✔
2737
                expire_block: self.private_key_expire,
1,356,759✔
2738

1,356,759✔
2739
                // not known yet
1,356,759✔
2740
                last_contact_time: 0,
1,356,759✔
2741
                allowed: self.allowed,
1,356,759✔
2742
                denied: self.denied,
1,356,759✔
2743
                asn: self.asn,
1,356,759✔
2744
                org: self.org,
1,356,759✔
2745
                in_degree: 0,
1,356,759✔
2746
                out_degree: 0,
1,356,759✔
2747
            }
1,356,759✔
2748
        }
1,356,759✔
2749

2750
        pub fn to_peer_host(&self) -> PeerHost {
147✔
2751
            PeerHost::IP(
147✔
2752
                PeerAddress([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1]),
147✔
2753
                self.http_port,
147✔
2754
            )
147✔
2755
        }
147✔
2756

2757
        pub fn get_stacker_db_configs(
681✔
2758
            &self,
681✔
2759
        ) -> HashMap<QualifiedContractIdentifier, StackerDBConfig> {
681✔
2760
            let mut ret = HashMap::new();
681✔
2761
            for (contract_id, config_opt) in
104✔
2762
                self.stacker_dbs.iter().zip(self.stacker_db_configs.iter())
681✔
2763
            {
2764
                if let Some(config) = config_opt {
104✔
2765
                    ret.insert(contract_id.clone(), config.clone());
104✔
2766
                } else {
104✔
2767
                    ret.insert(contract_id.clone(), StackerDBConfig::noop());
×
2768
                }
×
2769
            }
2770
            ret
681✔
2771
        }
681✔
2772

2773
        pub fn add_stacker_db(
84✔
2774
            &mut self,
84✔
2775
            contract_id: QualifiedContractIdentifier,
84✔
2776
            config: StackerDBConfig,
84✔
2777
        ) {
84✔
2778
            self.stacker_dbs.push(contract_id);
84✔
2779
            self.stacker_db_configs.push(Some(config));
84✔
2780
        }
84✔
2781
    }
2782

2783
    pub fn dns_thread_start(max_inflight: u64) -> (DNSClient, thread::JoinHandle<()>) {
15✔
2784
        let (mut resolver, client) = DNSResolver::new(max_inflight);
15✔
2785
        let jh = thread::spawn(move || {
15✔
2786
            resolver.thread_main();
15✔
2787
        });
15✔
2788
        (client, jh)
15✔
2789
    }
15✔
2790

2791
    pub fn dns_thread_shutdown(dns_client: DNSClient, thread_handle: thread::JoinHandle<()>) {
5✔
2792
        drop(dns_client);
5✔
2793
        thread_handle.join().unwrap();
5✔
2794
    }
5✔
2795

2796
    pub struct TestPeer<'a> {
2797
        pub config: TestPeerConfig,
2798
        pub network: PeerNetwork,
2799
        pub relayer: Relayer,
2800
        pub mempool: Option<MemPoolDB>,
2801
        pub chain: TestChainstate<'a>,
2802
        /// RPC handler args to use
2803
        pub rpc_handler_args: Option<RPCHandlerArgsType>,
2804
    }
2805

2806
    impl<'a> TestPeer<'a> {
2807
        pub fn new(config: TestPeerConfig) -> TestPeer<'a> {
425✔
2808
            TestPeer::new_with_observer(config, None)
425✔
2809
        }
425✔
2810

2811
        fn init_stackerdb_syncs(
671✔
2812
            root_path: &str,
671✔
2813
            peerdb: &PeerDB,
671✔
2814
            stacker_dbs: &mut HashMap<QualifiedContractIdentifier, StackerDBConfig>,
671✔
2815
        ) -> HashMap<QualifiedContractIdentifier, (StackerDBConfig, StackerDBSync<PeerNetworkComms>)>
671✔
2816
        {
2817
            let stackerdb_path = format!("{root_path}/stacker_db.sqlite");
671✔
2818
            let mut stacker_db_syncs = HashMap::new();
671✔
2819
            let local_peer = PeerDB::get_local_peer(peerdb.conn()).unwrap();
671✔
2820
            for (i, (contract_id, db_config)) in stacker_dbs.iter_mut().enumerate() {
1,308✔
2821
                let initial_peers = PeerDB::find_stacker_db_replicas(
1,093✔
2822
                    peerdb.conn(),
1,093✔
2823
                    local_peer.network_id,
1,093✔
2824
                    contract_id,
1,093✔
2825
                    0,
2826
                    10000000,
2827
                )
2828
                .unwrap()
1,093✔
2829
                .into_iter()
1,093✔
2830
                .map(|neighbor| NeighborAddress::from_neighbor(&neighbor))
1,093✔
2831
                .collect();
1,093✔
2832

2833
                db_config.hint_replicas = initial_peers;
1,093✔
2834
                let stacker_dbs = StackerDBs::connect(&stackerdb_path, true).unwrap();
1,093✔
2835
                let stacker_db_sync = StackerDBSync::new(
1,093✔
2836
                    contract_id.clone(),
1,093✔
2837
                    db_config,
1,093✔
2838
                    PeerNetworkComms::new(),
1,093✔
2839
                    stacker_dbs,
1,093✔
2840
                );
2841

2842
                stacker_db_syncs.insert(contract_id.clone(), (db_config.clone(), stacker_db_sync));
1,093✔
2843
            }
2844
            stacker_db_syncs
671✔
2845
        }
671✔
2846

2847
        pub fn neighbor_with_observer(
6✔
2848
            &self,
6✔
2849
            privkey: StacksPrivateKey,
6✔
2850
            observer: Option<&'a TestEventObserver>,
6✔
2851
        ) -> TestPeer<'a> {
6✔
2852
            let mut config = self.config.clone();
6✔
2853
            config.private_key = privkey;
6✔
2854
            config.chain_config.test_name = format!(
6✔
2855
                "{}.neighbor-{}",
2856
                &self.config.chain_config.test_name,
6✔
2857
                Hash160::from_node_public_key(&StacksPublicKey::from_private(
6✔
2858
                    &self.config.private_key
6✔
2859
                ))
6✔
2860
            );
2861
            config.server_port = 0;
6✔
2862
            config.http_port = 0;
6✔
2863
            config.chain_config.test_stackers = self.config.chain_config.test_stackers.clone();
6✔
2864
            config.initial_neighbors = vec![self.to_neighbor()];
6✔
2865

2866
            let peer = TestPeer::new_with_observer(config, observer);
6✔
2867
            peer
6✔
2868
        }
6✔
2869

2870
        pub fn new_with_observer(
671✔
2871
            mut config: TestPeerConfig,
671✔
2872
            observer: Option<&'a TestEventObserver>,
671✔
2873
        ) -> TestPeer<'a> {
671✔
2874
            let mut chain =
671✔
2875
                TestChainstate::new_with_observer(config.chain_config.clone(), observer);
671✔
2876
            // Write back the chain config as TestChainstate::new may have made modifications.
2877
            config.chain_config = chain.config.clone();
671✔
2878
            let test_path = chain.test_path.clone();
671✔
2879

2880
            let peerdb_path = format!("{test_path}/peers.sqlite");
671✔
2881

2882
            let mut peerdb = PeerDB::connect(
671✔
2883
                &peerdb_path,
671✔
2884
                true,
2885
                config.chain_config.network_id,
671✔
2886
                config.chain_config.burnchain.network_id,
671✔
2887
                None,
671✔
2888
                config.private_key_expire,
671✔
2889
                PeerAddress::from_ipv4(127, 0, 0, 1),
671✔
2890
                config.server_port,
671✔
2891
                config.data_url.clone(),
671✔
2892
                &config.asn4_entries,
671✔
2893
                Some(&config.initial_neighbors),
671✔
2894
                &config.stacker_dbs,
671✔
2895
            )
2896
            .unwrap();
671✔
2897
            {
2898
                // bootstrap nodes *always* allowed
2899
                let tx = peerdb.tx_begin().unwrap();
671✔
2900
                for initial_neighbor in config.initial_neighbors.iter() {
794✔
2901
                    PeerDB::set_allow_peer(
457✔
2902
                        &tx,
457✔
2903
                        initial_neighbor.addr.network_id,
457✔
2904
                        &initial_neighbor.addr.addrbytes,
457✔
2905
                        initial_neighbor.addr.port,
457✔
2906
                        -1,
457✔
2907
                    )
457✔
2908
                    .unwrap();
457✔
2909
                }
457✔
2910
                PeerDB::set_local_services(&tx, config.services).unwrap();
671✔
2911
                tx.commit().unwrap();
671✔
2912
            }
2913

2914
            let atlasdb_path = format!("{test_path}/atlas.sqlite");
671✔
2915
            let atlasdb = AtlasDB::connect(AtlasConfig::new(false), &atlasdb_path, true).unwrap();
671✔
2916

2917
            let local_addr =
671✔
2918
                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), config.server_port);
671✔
2919
            let http_local_addr =
671✔
2920
                SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), config.http_port);
671✔
2921

2922
            {
671✔
2923
                let tx = peerdb.tx_begin().unwrap();
671✔
2924
                PeerDB::set_local_ipaddr(
671✔
2925
                    &tx,
671✔
2926
                    &PeerAddress::from_socketaddr(&SocketAddr::new(
671✔
2927
                        IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
671✔
2928
                        config.server_port,
671✔
2929
                    )),
671✔
2930
                    config.server_port,
671✔
2931
                )
671✔
2932
                .unwrap();
671✔
2933
                PeerDB::set_local_private_key(&tx, &config.private_key, config.private_key_expire)
671✔
2934
                    .unwrap();
671✔
2935

671✔
2936
                tx.commit().unwrap();
671✔
2937
            }
671✔
2938

2939
            let local_peer = PeerDB::get_local_peer(peerdb.conn()).unwrap();
671✔
2940
            let burnchain_view = {
671✔
2941
                let chaintip =
671✔
2942
                    SortitionDB::get_canonical_burn_chain_tip(chain.sortdb().conn()).unwrap();
671✔
2943
                SortitionDB::get_burnchain_view(
671✔
2944
                    &chain.sortdb().index_conn(),
671✔
2945
                    &config.chain_config.burnchain,
671✔
2946
                    &chaintip,
671✔
2947
                )
2948
                .unwrap()
671✔
2949
            };
2950
            let stackerdb_path = format!("{test_path}/stacker_db.sqlite");
671✔
2951
            let mut stacker_dbs_conn = StackerDBs::connect(&stackerdb_path, true).unwrap();
671✔
2952
            let relayer_stacker_dbs = StackerDBs::connect(&stackerdb_path, true).unwrap();
671✔
2953
            let p2p_stacker_dbs = StackerDBs::connect(&stackerdb_path, true).unwrap();
671✔
2954

2955
            let mut old_stackerdb_configs = HashMap::new();
671✔
2956
            for (i, contract) in config.stacker_dbs.iter().enumerate() {
1,308✔
2957
                old_stackerdb_configs.insert(
1,093✔
2958
                    contract.clone(),
1,093✔
2959
                    config
1,093✔
2960
                        .stacker_db_configs
1,093✔
2961
                        .get(i)
1,093✔
2962
                        .map(|config| config.clone().unwrap_or(StackerDBConfig::noop()))
1,093✔
2963
                        .unwrap_or(StackerDBConfig::noop()),
1,093✔
2964
                );
2965
            }
2966
            let mut stacks_node = chain.stacks_node.take().unwrap();
671✔
2967
            let mut stackerdb_configs = stacker_dbs_conn
671✔
2968
                .create_or_reconfigure_stackerdbs(
671✔
2969
                    &mut stacks_node.chainstate,
671✔
2970
                    chain.sortdb_ref(),
671✔
2971
                    old_stackerdb_configs,
671✔
2972
                    &config.connection_opts,
671✔
2973
                )
2974
                .expect("Failed to refresh stackerdb configs");
671✔
2975

2976
            let stacker_db_syncs =
671✔
2977
                Self::init_stackerdb_syncs(&test_path, &peerdb, &mut stackerdb_configs);
671✔
2978

2979
            let stackerdb_contracts: Vec<_> = stacker_db_syncs.keys().cloned().collect();
671✔
2980

2981
            let burnchain_db = config
671✔
2982
                .chain_config
671✔
2983
                .burnchain
671✔
2984
                .open_burnchain_db(false)
671✔
2985
                .unwrap();
671✔
2986

2987
            let epochs = config.chain_config.epochs.clone().unwrap_or_else(|| {
671✔
2988
                StacksEpoch::unit_test_pre_2_05(config.chain_config.burnchain.first_block_height)
404✔
2989
            });
404✔
2990

2991
            let mut peer_network = PeerNetwork::new(
671✔
2992
                peerdb,
671✔
2993
                atlasdb,
671✔
2994
                p2p_stacker_dbs,
671✔
2995
                burnchain_db,
671✔
2996
                local_peer,
671✔
2997
                config.peer_version,
671✔
2998
                config.chain_config.burnchain.clone(),
671✔
2999
                burnchain_view,
671✔
3000
                config.connection_opts.clone(),
671✔
3001
                stacker_db_syncs,
671✔
3002
                epochs,
671✔
3003
            );
3004
            peer_network.set_stacker_db_configs(config.get_stacker_db_configs());
671✔
3005

3006
            peer_network.bind(&local_addr, &http_local_addr).unwrap();
671✔
3007
            let relayer = Relayer::from_p2p(&mut peer_network, relayer_stacker_dbs);
671✔
3008
            let mempool = MemPoolDB::open_test(
671✔
3009
                false,
3010
                config.chain_config.network_id,
671✔
3011
                &chain.chainstate_path,
671✔
3012
            )
3013
            .unwrap();
671✔
3014

3015
            // extract bound ports (which may be different from what's in the config file, if e.g.
3016
            // they were 0)
3017
            let p2p_port = peer_network.bound_neighbor_key().port;
671✔
3018
            let http_port = peer_network.http.as_ref().unwrap().http_server_addr.port();
671✔
3019

3020
            debug!("Bound to (p2p={p2p_port}, http={http_port})");
671✔
3021
            config.server_port = p2p_port;
671✔
3022
            config.http_port = http_port;
671✔
3023

3024
            config.data_url =
671✔
3025
                UrlString::try_from(format!("http://127.0.0.1:{http_port}").as_str()).unwrap();
671✔
3026

3027
            peer_network
671✔
3028
                .peerdb
671✔
3029
                .update_local_peer(
671✔
3030
                    config.chain_config.network_id,
671✔
3031
                    config.chain_config.burnchain.network_id,
671✔
3032
                    config.data_url.clone(),
671✔
3033
                    p2p_port,
671✔
3034
                    &stackerdb_contracts,
671✔
3035
                )
3036
                .unwrap();
671✔
3037

3038
            let local_peer = PeerDB::get_local_peer(peer_network.peerdb.conn()).unwrap();
671✔
3039
            debug!(
671✔
3040
                "{local_peer:?}: initial neighbors: {:?}",
3041
                &config.initial_neighbors
×
3042
            );
3043
            peer_network.local_peer = local_peer;
671✔
3044
            chain.stacks_node = Some(stacks_node);
671✔
3045
            TestPeer {
671✔
3046
                config,
671✔
3047
                chain,
671✔
3048
                network: peer_network,
671✔
3049
                relayer,
671✔
3050
                mempool: Some(mempool),
671✔
3051
                rpc_handler_args: None,
671✔
3052
            }
671✔
3053
        }
671✔
3054

3055
        pub fn connect_initial(&mut self) -> Result<(), net_error> {
195✔
3056
            let local_peer = PeerDB::get_local_peer(self.network.peerdb.conn()).unwrap();
195✔
3057
            let chain_view = match self.chain.sortdb {
195✔
3058
                Some(ref mut sortdb) => {
195✔
3059
                    let chaintip =
195✔
3060
                        SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
195✔
3061
                    SortitionDB::get_burnchain_view(
195✔
3062
                        &sortdb.index_conn(),
195✔
3063
                        &self.config.chain_config.burnchain,
195✔
3064
                        &chaintip,
195✔
3065
                    )
3066
                    .unwrap()
195✔
3067
                }
3068
                None => panic!("Misconfigured peer: no sortdb"),
×
3069
            };
3070

3071
            self.network.local_peer = local_peer;
195✔
3072
            self.network.chain_view = chain_view;
195✔
3073

3074
            for n in self.config.initial_neighbors.iter() {
318✔
3075
                self.network.connect_peer(&n.addr).map(|_| ())?;
315✔
3076
            }
3077
            Ok(())
195✔
3078
        }
195✔
3079

3080
        pub fn local_peer(&self) -> &LocalPeer {
×
3081
            &self.network.local_peer
×
3082
        }
×
3083

3084
        pub fn add_neighbor(
50✔
3085
            &mut self,
50✔
3086
            n: &mut Neighbor,
50✔
3087
            stacker_dbs: Option<&[QualifiedContractIdentifier]>,
50✔
3088
            bootstrap: bool,
50✔
3089
        ) {
50✔
3090
            let tx = self.network.peerdb.tx_begin().unwrap();
50✔
3091
            n.save(&tx, stacker_dbs).unwrap();
50✔
3092
            if bootstrap {
50✔
3093
                PeerDB::set_initial_peer(
40✔
3094
                    &tx,
40✔
3095
                    self.config.chain_config.network_id,
40✔
3096
                    &n.addr.addrbytes,
40✔
3097
                    n.addr.port,
40✔
3098
                )
40✔
3099
                .unwrap();
40✔
3100
            }
47✔
3101
            tx.commit().unwrap();
50✔
3102
        }
50✔
3103

3104
        // TODO: DRY up from PoxSyncWatchdog
3105
        pub fn infer_initial_burnchain_block_download(
2,017✔
3106
            burnchain: &Burnchain,
2,017✔
3107
            last_processed_height: u64,
2,017✔
3108
            burnchain_height: u64,
2,017✔
3109
        ) -> bool {
2,017✔
3110
            let ibd =
2,017✔
3111
                last_processed_height + (burnchain.stable_confirmations as u64) < burnchain_height;
2,017✔
3112
            if ibd {
2,017✔
3113
                debug!(
2,017✔
3114
                    "PoX watchdog: {} + {} < {}, so initial block download",
3115
                    last_processed_height, burnchain.stable_confirmations, burnchain_height
3116
                );
3117
            } else {
3118
                debug!(
×
3119
                    "PoX watchdog: {} + {} >= {}, so steady-state",
3120
                    last_processed_height, burnchain.stable_confirmations, burnchain_height
3121
                );
3122
            }
3123
            ibd
2,017✔
3124
        }
2,017✔
3125

3126
        pub fn step(&mut self) -> Result<NetworkResult, net_error> {
2,017✔
3127
            let sortdb = self.chain.sortdb.take().unwrap();
2,017✔
3128
            let stacks_node = self.chain.stacks_node.take().unwrap();
2,017✔
3129
            let burn_tip_height = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
2,017✔
3130
                .unwrap()
2,017✔
3131
                .block_height;
2,017✔
3132
            let stacks_tip_height = NakamotoChainState::get_canonical_block_header(
2,017✔
3133
                stacks_node.chainstate.db(),
2,017✔
3134
                &sortdb,
2,017✔
3135
            )
3136
            .unwrap()
2,017✔
3137
            .map(|hdr| hdr.anchored_header.height())
2,017✔
3138
            .unwrap_or(0);
2,017✔
3139
            let ibd = TestPeer::infer_initial_burnchain_block_download(
2,017✔
3140
                &self.config.chain_config.burnchain,
2,017✔
3141
                stacks_tip_height,
2,017✔
3142
                burn_tip_height,
2,017✔
3143
            );
3144
            self.chain.sortdb = Some(sortdb);
2,017✔
3145
            self.chain.stacks_node = Some(stacks_node);
2,017✔
3146

3147
            self.step_with_ibd(ibd)
2,017✔
3148
        }
2,017✔
3149

3150
        pub fn step_with_ibd(&mut self, ibd: bool) -> Result<NetworkResult, net_error> {
56,237✔
3151
            self.step_with_ibd_and_dns(ibd, None)
56,237✔
3152
        }
56,237✔
3153

3154
        pub fn step_with_ibd_and_dns(
61,570✔
3155
            &mut self,
61,570✔
3156
            ibd: bool,
61,570✔
3157
            dns_client: Option<&mut DNSClient>,
61,570✔
3158
        ) -> Result<NetworkResult, net_error> {
61,570✔
3159
            let sortdb = self.chain.sortdb.take().unwrap();
61,570✔
3160
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
61,570✔
3161
            let mut mempool = self.mempool.take().unwrap();
61,570✔
3162
            let indexer = self.chain.indexer.take().unwrap();
61,570✔
3163
            let rpc_handler_args = self
61,570✔
3164
                .rpc_handler_args
61,570✔
3165
                .as_ref()
61,570✔
3166
                .map(|args_type| args_type.instantiate())
61,570✔
3167
                .unwrap_or(RPCHandlerArgsType::make_default());
61,570✔
3168

3169
            let old_tip = self.network.stacks_tip.clone();
61,570✔
3170

3171
            // make sure the right state machines run
3172
            let epoch2_passes = self.network.epoch2_state_machine_passes;
61,570✔
3173
            let nakamoto_passes = self.network.nakamoto_state_machine_passes;
61,570✔
3174

3175
            let ret = self.network.run(
61,570✔
3176
                &indexer,
61,570✔
3177
                &sortdb,
61,570✔
3178
                &mut stacks_node.chainstate,
61,570✔
3179
                &mut mempool,
61,570✔
3180
                dns_client,
61,570✔
3181
                false,
3182
                ibd,
61,570✔
3183
                100,
3184
                &rpc_handler_args,
61,570✔
3185
                self.config.chain_config.txindex,
61,570✔
3186
            );
3187

3188
            if self.network.get_current_epoch().epoch_id >= StacksEpochId::Epoch30 {
61,570✔
3189
                assert_eq!(
14,710✔
3190
                    self.network.nakamoto_state_machine_passes,
3191
                    nakamoto_passes + 1
14,710✔
3192
                );
3193
                let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto
14,710✔
3194
                    && !self.network.connection_opts.force_nakamoto_epoch_transition
14,591✔
3195
                {
3196
                    epoch2_passes
14,547✔
3197
                } else {
3198
                    epoch2_passes + 1
163✔
3199
                };
3200
                assert_eq!(
14,710✔
3201
                    self.network.epoch2_state_machine_passes,
3202
                    epoch2_expected_passes
3203
                );
3204
            }
46,860✔
3205
            if self
61,570✔
3206
                .network
61,570✔
3207
                .need_epoch2_state_machines(self.network.get_current_epoch().epoch_id)
61,570✔
3208
            {
3209
                assert_eq!(self.network.epoch2_state_machine_passes, epoch2_passes + 1);
46,979✔
3210
            }
14,591✔
3211

3212
            self.chain.sortdb = Some(sortdb);
61,570✔
3213
            self.chain.stacks_node = Some(stacks_node);
61,570✔
3214
            self.mempool = Some(mempool);
61,570✔
3215
            self.chain.indexer = Some(indexer);
61,570✔
3216
            ret
61,570✔
3217
        }
61,570✔
3218

3219
        pub fn run_with_ibd(
5,145✔
3220
            &mut self,
5,145✔
3221
            ibd: bool,
5,145✔
3222
            dns_client: Option<&mut DNSClient>,
5,145✔
3223
        ) -> Result<(NetworkResult, ProcessedNetReceipts), net_error> {
5,145✔
3224
            let net_result = self.step_with_ibd_and_dns(ibd, dns_client)?;
5,145✔
3225
            let mut sortdb = self.chain.sortdb.take().unwrap();
5,145✔
3226
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
5,145✔
3227
            let mut mempool = self.mempool.take().unwrap();
5,145✔
3228
            let indexer = self.chain.indexer.take().unwrap();
5,145✔
3229

3230
            let receipts_res = self.relayer.process_network_result(
5,145✔
3231
                self.network.get_local_peer(),
5,145✔
3232
                &mut net_result.clone(),
5,145✔
3233
                &self.network.burnchain,
5,145✔
3234
                &mut sortdb,
5,145✔
3235
                &mut stacks_node.chainstate,
5,145✔
3236
                &mut mempool,
5,145✔
3237
                ibd,
5,145✔
3238
                None,
5,145✔
3239
                None,
5,145✔
3240
            );
3241

3242
            self.chain.sortdb = Some(sortdb);
5,145✔
3243
            self.chain.stacks_node = Some(stacks_node);
5,145✔
3244
            self.mempool = Some(mempool);
5,145✔
3245
            self.chain.indexer = Some(indexer);
5,145✔
3246

3247
            self.chain.coord.handle_new_burnchain_block().unwrap();
5,145✔
3248
            self.chain.coord.handle_new_stacks_block().unwrap();
5,145✔
3249
            self.chain.coord.handle_new_nakamoto_stacks_block().unwrap();
5,145✔
3250

3251
            receipts_res.map(|receipts| (net_result, receipts))
5,145✔
3252
        }
5,145✔
3253

3254
        pub fn step_dns(&mut self, dns_client: &mut DNSClient) -> Result<NetworkResult, net_error> {
×
3255
            let sortdb = self.chain.sortdb.take().unwrap();
×
3256
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
×
3257
            let mut mempool = self.mempool.take().unwrap();
×
3258
            let indexer =
×
3259
                BitcoinIndexer::new_unit_test(&self.config.chain_config.burnchain.working_dir);
×
3260

3261
            let burn_tip_height = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())
×
3262
                .unwrap()
×
3263
                .block_height;
×
3264
            let stacks_tip_height = NakamotoChainState::get_canonical_block_header(
×
3265
                stacks_node.chainstate.db(),
×
3266
                &sortdb,
×
3267
            )
3268
            .unwrap()
×
3269
            .map(|hdr| hdr.anchored_header.height())
×
3270
            .unwrap_or(0);
×
3271
            let ibd = TestPeer::infer_initial_burnchain_block_download(
×
3272
                &self.config.chain_config.burnchain,
×
3273
                stacks_tip_height,
×
3274
                burn_tip_height,
×
3275
            );
3276
            let indexer =
×
3277
                BitcoinIndexer::new_unit_test(&self.config.chain_config.burnchain.working_dir);
×
3278
            let rpc_handler_args = self
×
3279
                .rpc_handler_args
×
3280
                .as_ref()
×
3281
                .map(|args_type| args_type.instantiate())
×
3282
                .unwrap_or(RPCHandlerArgsType::make_default());
×
3283
            let old_tip = self.network.stacks_tip.clone();
×
3284

3285
            // make sure the right state machines run
3286
            let epoch2_passes = self.network.epoch2_state_machine_passes;
×
3287
            let nakamoto_passes = self.network.nakamoto_state_machine_passes;
×
3288

3289
            let ret = self.network.run(
×
3290
                &indexer,
×
3291
                &sortdb,
×
3292
                &mut stacks_node.chainstate,
×
3293
                &mut mempool,
×
3294
                Some(dns_client),
×
3295
                false,
3296
                ibd,
×
3297
                100,
3298
                &rpc_handler_args,
×
3299
                self.config.chain_config.txindex,
×
3300
            );
3301

3302
            if self.network.get_current_epoch().epoch_id >= StacksEpochId::Epoch30 {
×
3303
                assert_eq!(
×
3304
                    self.network.nakamoto_state_machine_passes,
3305
                    nakamoto_passes + 1
×
3306
                );
3307
                let epoch2_expected_passes = if self.network.stacks_tip.is_nakamoto
×
3308
                    && !self.network.connection_opts.force_nakamoto_epoch_transition
×
3309
                {
3310
                    epoch2_passes
×
3311
                } else {
3312
                    epoch2_passes + 1
×
3313
                };
3314
                assert_eq!(
×
3315
                    self.network.epoch2_state_machine_passes,
3316
                    epoch2_expected_passes
3317
                );
3318
            }
×
3319
            if self
×
3320
                .network
×
3321
                .need_epoch2_state_machines(self.network.get_current_epoch().epoch_id)
×
3322
            {
3323
                assert_eq!(self.network.epoch2_state_machine_passes, epoch2_passes + 1);
×
3324
            }
×
3325

3326
            self.chain.sortdb = Some(sortdb);
×
3327
            self.chain.stacks_node = Some(stacks_node);
×
3328
            self.mempool = Some(mempool);
×
3329

3330
            ret
×
3331
        }
×
3332

3333
        pub fn refresh_burnchain_view(&mut self) {
6,573✔
3334
            let sortdb = self.chain.sortdb.take().unwrap();
6,573✔
3335
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
6,573✔
3336
            let indexer =
6,573✔
3337
                BitcoinIndexer::new_unit_test(&self.config.chain_config.burnchain.working_dir);
6,573✔
3338

3339
            self.network
6,573✔
3340
                .refresh_burnchain_view(&sortdb, &mut stacks_node.chainstate, false)
6,573✔
3341
                .unwrap();
6,573✔
3342

3343
            self.chain.sortdb = Some(sortdb);
6,573✔
3344
            self.chain.stacks_node = Some(stacks_node);
6,573✔
3345
        }
6,573✔
3346

3347
        pub fn refresh_reward_cycles(&mut self) {
×
3348
            let sortdb = self.chain.sortdb.take().unwrap();
×
3349
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
×
3350

3351
            let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
×
3352
            let tip_block_id = self.network.stacks_tip.block_id();
×
3353
            let tip_height = self.network.stacks_tip.height;
×
3354

3355
            self.network
×
3356
                .refresh_reward_cycles(
×
3357
                    &sortdb,
×
3358
                    &mut stacks_node.chainstate,
×
3359
                    &tip,
×
3360
                    &tip_block_id,
×
3361
                    tip_height,
×
3362
                )
3363
                .unwrap();
×
3364

3365
            self.chain.sortdb = Some(sortdb);
×
3366
            self.chain.stacks_node = Some(stacks_node);
×
3367
        }
×
3368

3369
        pub fn for_each_convo_p2p<F, R>(&mut self, mut f: F) -> Vec<Result<R, net_error>>
×
3370
        where
×
3371
            F: FnMut(usize, &mut ConversationP2P) -> Result<R, net_error>,
×
3372
        {
3373
            let mut ret = vec![];
×
3374
            for (event_id, convo) in self.network.peers.iter_mut() {
×
3375
                let res = f(*event_id, convo);
×
3376
                ret.push(res);
×
3377
            }
×
3378
            ret
×
3379
        }
×
3380

3381
        pub fn get_burnchain_db(&self, readwrite: bool) -> BurnchainDB {
×
3382
            BurnchainDB::open(
×
3383
                &self.config.chain_config.burnchain.get_burnchaindb_path(),
×
3384
                readwrite,
×
3385
            )
3386
            .unwrap()
×
3387
        }
×
3388

3389
        pub fn get_sortition_at_height(&self, height: u64) -> Option<BlockSnapshot> {
×
3390
            let sortdb = self.chain.sortdb.as_ref().unwrap();
×
3391
            let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
×
3392
            let sort_handle = sortdb.index_handle(&tip.sortition_id);
×
3393
            sort_handle.get_block_snapshot_by_height(height).unwrap()
×
3394
        }
×
3395

3396
        pub fn get_burnchain_block_ops(
269✔
3397
            &self,
269✔
3398
            burn_block_hash: &BurnchainHeaderHash,
269✔
3399
        ) -> Vec<BlockstackOperationType> {
269✔
3400
            let burnchain_db = BurnchainDB::open(
269✔
3401
                &self.config.chain_config.burnchain.get_burnchaindb_path(),
269✔
3402
                false,
3403
            )
3404
            .unwrap();
269✔
3405
            burnchain_db
269✔
3406
                .get_burnchain_block_ops(burn_block_hash)
269✔
3407
                .unwrap()
269✔
3408
        }
269✔
3409

3410
        pub fn get_burnchain_block_ops_at_height(
269✔
3411
            &self,
269✔
3412
            height: u64,
269✔
3413
        ) -> Option<Vec<BlockstackOperationType>> {
269✔
3414
            let sortdb = self.chain.sortdb.as_ref().unwrap();
269✔
3415
            let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
269✔
3416
            let sort_handle = sortdb.index_handle(&tip.sortition_id);
269✔
3417
            let Some(sn) = sort_handle.get_block_snapshot_by_height(height).unwrap() else {
269✔
3418
                return None;
×
3419
            };
3420
            Some(self.get_burnchain_block_ops(&sn.burn_header_hash))
269✔
3421
        }
269✔
3422

3423
        pub fn next_burnchain_block(
7,732✔
3424
            &mut self,
7,732✔
3425
            blockstack_ops: Vec<BlockstackOperationType>,
7,732✔
3426
        ) -> (u64, BurnchainHeaderHash, ConsensusHash) {
7,732✔
3427
            let x = self.inner_next_burnchain_block(blockstack_ops, true, true, true, false);
7,732✔
3428
            (x.0, x.1, x.2)
7,732✔
3429
        }
7,732✔
3430

3431
        pub fn next_burnchain_block_diverge(
×
3432
            &mut self,
×
3433
            blockstack_ops: Vec<BlockstackOperationType>,
×
3434
        ) -> (u64, BurnchainHeaderHash, ConsensusHash) {
×
3435
            let x = self.inner_next_burnchain_block(blockstack_ops, true, true, true, true);
×
3436
            (x.0, x.1, x.2)
×
3437
        }
×
3438

3439
        pub fn next_burnchain_block_and_missing_pox_anchor(
×
3440
            &mut self,
×
3441
            blockstack_ops: Vec<BlockstackOperationType>,
×
3442
        ) -> (
×
3443
            u64,
×
3444
            BurnchainHeaderHash,
×
3445
            ConsensusHash,
×
3446
            Option<BlockHeaderHash>,
×
3447
        ) {
×
3448
            self.inner_next_burnchain_block(blockstack_ops, true, true, true, false)
×
3449
        }
×
3450

3451
        pub fn next_burnchain_block_raw(
6✔
3452
            &mut self,
6✔
3453
            blockstack_ops: Vec<BlockstackOperationType>,
6✔
3454
        ) -> (u64, BurnchainHeaderHash, ConsensusHash) {
6✔
3455
            let x = self.inner_next_burnchain_block(blockstack_ops, false, false, true, false);
6✔
3456
            (x.0, x.1, x.2)
6✔
3457
        }
6✔
3458

3459
        pub fn next_burnchain_block_raw_sortition_only(
×
3460
            &mut self,
×
3461
            blockstack_ops: Vec<BlockstackOperationType>,
×
3462
        ) -> (u64, BurnchainHeaderHash, ConsensusHash) {
×
3463
            let x = self.inner_next_burnchain_block(blockstack_ops, false, false, false, false);
×
3464
            (x.0, x.1, x.2)
×
3465
        }
×
3466

3467
        pub fn next_burnchain_block_raw_and_missing_pox_anchor(
×
3468
            &mut self,
×
3469
            blockstack_ops: Vec<BlockstackOperationType>,
×
3470
        ) -> (
×
3471
            u64,
×
3472
            BurnchainHeaderHash,
×
3473
            ConsensusHash,
×
3474
            Option<BlockHeaderHash>,
×
3475
        ) {
×
3476
            self.inner_next_burnchain_block(blockstack_ops, false, false, true, false)
×
3477
        }
×
3478

3479
        pub fn set_ops_consensus_hash(
7,732✔
3480
            blockstack_ops: &mut Vec<BlockstackOperationType>,
7,732✔
3481
            ch: &ConsensusHash,
7,732✔
3482
        ) {
7,732✔
3483
            for op in blockstack_ops.iter_mut() {
12,930✔
3484
                if let BlockstackOperationType::LeaderKeyRegister(ref mut data) = op {
12,930✔
3485
                    data.consensus_hash = (*ch).clone();
5,405✔
3486
                }
7,525✔
3487
            }
3488
        }
7,732✔
3489

3490
        pub fn set_ops_burn_header_hash(
7,742✔
3491
            blockstack_ops: &mut Vec<BlockstackOperationType>,
7,742✔
3492
            bhh: &BurnchainHeaderHash,
7,742✔
3493
        ) {
7,742✔
3494
            for op in blockstack_ops.iter_mut() {
12,950✔
3495
                op.set_burn_header_hash(bhh.clone());
12,950✔
3496
            }
12,950✔
3497
        }
7,742✔
3498

3499
        pub fn make_next_burnchain_block(
8,007✔
3500
            burnchain: &Burnchain,
8,007✔
3501
            tip_block_height: u64,
8,007✔
3502
            tip_block_hash: &BurnchainHeaderHash,
8,007✔
3503
            num_ops: u64,
8,007✔
3504
            ops_determine_block_header: bool,
8,007✔
3505
        ) -> BurnchainBlockHeader {
8,007✔
3506
            test_debug!(
8,007✔
3507
                "make_next_burnchain_block: tip_block_height={} tip_block_hash={} num_ops={}",
3508
                tip_block_height,
3509
                tip_block_hash,
3510
                num_ops
3511
            );
3512
            let indexer = BitcoinIndexer::new_unit_test(&burnchain.working_dir);
8,007✔
3513
            let parent_hdr = indexer
8,007✔
3514
                .read_burnchain_header(tip_block_height)
8,007✔
3515
                .unwrap()
8,007✔
3516
                .unwrap();
8,007✔
3517

3518
            test_debug!("parent hdr ({}): {:?}", &tip_block_height, &parent_hdr);
8,007✔
3519
            assert_eq!(&parent_hdr.block_hash, tip_block_hash);
8,007✔
3520

3521
            let now = BURNCHAIN_TEST_BLOCK_TIME;
8,007✔
3522
            let block_header_hash = BurnchainHeaderHash::from_bitcoin_hash(
8,007✔
3523
                &BitcoinIndexer::mock_bitcoin_header(
8,007✔
3524
                    &parent_hdr.block_hash,
8,007✔
3525
                    (now as u32)
8,007✔
3526
                        + if ops_determine_block_header {
8,007✔
3527
                            num_ops as u32
×
3528
                        } else {
3529
                            0
8,007✔
3530
                        },
3531
                )
3532
                .bitcoin_hash(),
8,007✔
3533
            );
3534
            test_debug!(
8,007✔
3535
                "Block header hash at {} is {}",
3536
                tip_block_height + 1,
×
3537
                &block_header_hash
×
3538
            );
3539

3540
            let block_header = BurnchainBlockHeader {
8,007✔
3541
                block_height: tip_block_height + 1,
8,007✔
3542
                block_hash: block_header_hash.clone(),
8,007✔
3543
                parent_block_hash: parent_hdr.block_hash.clone(),
8,007✔
3544
                num_txs: num_ops,
8,007✔
3545
                timestamp: now,
8,007✔
3546
            };
8,007✔
3547

3548
            block_header
8,007✔
3549
        }
8,007✔
3550

3551
        pub fn add_burnchain_block(
8,007✔
3552
            burnchain: &Burnchain,
8,007✔
3553
            block_header: &BurnchainBlockHeader,
8,007✔
3554
            blockstack_ops: Vec<BlockstackOperationType>,
8,007✔
3555
        ) {
8,007✔
3556
            let mut burnchain_db =
8,007✔
3557
                BurnchainDB::open(&burnchain.get_burnchaindb_path(), true).unwrap();
8,007✔
3558

3559
            let mut indexer = BitcoinIndexer::new_unit_test(&burnchain.working_dir);
8,007✔
3560

3561
            test_debug!(
8,007✔
3562
                "Store header and block ops for {}-{} ({})",
3563
                &block_header.block_hash,
×
3564
                &block_header.parent_block_hash,
×
3565
                block_header.block_height
3566
            );
3567
            indexer.raw_store_header(block_header.clone()).unwrap();
8,007✔
3568
            burnchain_db
8,007✔
3569
                .raw_store_burnchain_block(
8,007✔
3570
                    burnchain,
8,007✔
3571
                    &indexer,
8,007✔
3572
                    block_header.clone(),
8,007✔
3573
                    blockstack_ops,
8,007✔
3574
                )
3575
                .unwrap();
8,007✔
3576
        }
8,007✔
3577

3578
        /// Generate and commit the next burnchain block with the given block operations.
3579
        /// * if `set_consensus_hash` is true, then each op's consensus_hash field will be set to
3580
        /// that of the resulting block snapshot.
3581
        /// * if `set_burn_hash` is true, then each op's burnchain header hash field will be set to
3582
        /// that of the resulting block snapshot.
3583
        ///
3584
        /// Returns (
3585
        ///     burnchain tip block height,
3586
        ///     burnchain tip block hash,
3587
        ///     burnchain tip consensus hash,
3588
        ///     Option<missing PoX anchor block hash>
3589
        /// )
3590
        fn inner_next_burnchain_block(
7,738✔
3591
            &mut self,
7,738✔
3592
            mut blockstack_ops: Vec<BlockstackOperationType>,
7,738✔
3593
            set_consensus_hash: bool,
7,738✔
3594
            set_burn_hash: bool,
7,738✔
3595
            update_burnchain: bool,
7,738✔
3596
            ops_determine_block_header: bool,
7,738✔
3597
        ) -> (
7,738✔
3598
            u64,
7,738✔
3599
            BurnchainHeaderHash,
7,738✔
3600
            ConsensusHash,
7,738✔
3601
            Option<BlockHeaderHash>,
7,738✔
3602
        ) {
7,738✔
3603
            let sortdb = self.chain.sortdb.take().unwrap();
7,738✔
3604
            let (block_height, block_hash, epoch_id) = {
7,738✔
3605
                let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
7,738✔
3606
                let epoch_id = SortitionDB::get_stacks_epoch(sortdb.conn(), tip.block_height + 1)
7,738✔
3607
                    .unwrap()
7,738✔
3608
                    .unwrap()
7,738✔
3609
                    .epoch_id;
7,738✔
3610

3611
                if set_consensus_hash {
7,738✔
3612
                    TestPeer::set_ops_consensus_hash(&mut blockstack_ops, &tip.consensus_hash);
7,732✔
3613
                }
7,732✔
3614

3615
                let block_header = Self::make_next_burnchain_block(
7,738✔
3616
                    &self.config.chain_config.burnchain,
7,738✔
3617
                    tip.block_height,
7,738✔
3618
                    &tip.burn_header_hash,
7,738✔
3619
                    blockstack_ops.len() as u64,
7,738✔
3620
                    ops_determine_block_header,
7,738✔
3621
                );
3622

3623
                if set_burn_hash {
7,738✔
3624
                    TestPeer::set_ops_burn_header_hash(
7,732✔
3625
                        &mut blockstack_ops,
7,732✔
3626
                        &block_header.block_hash,
7,732✔
3627
                    );
7,732✔
3628
                }
7,732✔
3629

3630
                if update_burnchain {
7,738✔
3631
                    Self::add_burnchain_block(
7,738✔
3632
                        &self.config.chain_config.burnchain,
7,738✔
3633
                        &block_header,
7,738✔
3634
                        blockstack_ops.clone(),
7,738✔
3635
                    );
7,738✔
3636
                }
7,738✔
3637
                (block_header.block_height, block_header.block_hash, epoch_id)
7,738✔
3638
            };
3639

3640
            let missing_pox_anchor_block_hash_opt = if epoch_id < StacksEpochId::Epoch30 {
7,738✔
3641
                self.chain
5,674✔
3642
                    .coord
5,674✔
3643
                    .handle_new_burnchain_block()
5,674✔
3644
                    .unwrap()
5,674✔
3645
                    .into_missing_block_hash()
5,674✔
3646
            } else if self
2,064✔
3647
                .chain
2,064✔
3648
                .coord
2,064✔
3649
                .handle_new_nakamoto_burnchain_block()
2,064✔
3650
                .unwrap()
2,064✔
3651
            {
3652
                None
2,064✔
3653
            } else {
3654
                Some(BlockHeaderHash([0x00; 32]))
×
3655
            };
3656

3657
            let pox_id = {
7,738✔
3658
                let ic = sortdb.index_conn();
7,738✔
3659
                let tip_sort_id = SortitionDB::get_canonical_sortition_tip(sortdb.conn()).unwrap();
7,738✔
3660
                let sortdb_reader = SortitionHandleConn::open_reader(&ic, &tip_sort_id).unwrap();
7,738✔
3661
                sortdb_reader.get_pox_id().unwrap()
7,738✔
3662
            };
3663

3664
            test_debug!(
7,738✔
3665
                "\n\n{:?}: after burn block {:?}, tip PoX ID is {:?}\n\n",
3666
                &self.to_neighbor().addr,
×
3667
                &block_hash,
×
3668
                &pox_id
×
3669
            );
3670

3671
            let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
7,738✔
3672
            self.chain.sortdb = Some(sortdb);
7,738✔
3673
            (
7,738✔
3674
                block_height,
7,738✔
3675
                block_hash,
7,738✔
3676
                tip.consensus_hash,
7,738✔
3677
                missing_pox_anchor_block_hash_opt,
7,738✔
3678
            )
7,738✔
3679
        }
7,738✔
3680

3681
        /// Pre-process an epoch 2.x Stacks block.
3682
        /// Validate it and store it to staging.
3683
        pub fn preprocess_stacks_block(&mut self, block: &StacksBlock) -> Result<bool, String> {
×
3684
            let sortdb = self.chain.sortdb.take().unwrap();
×
3685
            let mut node = self.chain.stacks_node.take().unwrap();
×
3686
            let res = {
×
3687
                let sn = {
×
3688
                    let ic = sortdb.index_conn();
×
3689
                    let tip = SortitionDB::get_canonical_burn_chain_tip(&ic).unwrap();
×
3690
                    let sn_opt = SortitionDB::get_block_snapshot_for_winning_stacks_block(
×
3691
                        &ic,
×
3692
                        &tip.sortition_id,
×
3693
                        &block.block_hash(),
×
3694
                    )
3695
                    .unwrap();
×
3696
                    if sn_opt.is_none() {
×
3697
                        return Err(format!(
×
3698
                            "No such block in canonical burn fork: {}",
×
3699
                            &block.block_hash()
×
3700
                        ));
×
3701
                    }
×
3702
                    sn_opt.unwrap()
×
3703
                };
3704

3705
                let parent_sn = {
×
3706
                    let db_handle = sortdb.index_handle(&sn.sortition_id);
×
3707
                    let parent_sn = db_handle
×
3708
                        .get_block_snapshot(&sn.parent_burn_header_hash)
×
3709
                        .unwrap();
×
3710
                    parent_sn.unwrap()
×
3711
                };
3712

3713
                let ic = sortdb.index_conn();
×
3714
                node.chainstate
×
3715
                    .preprocess_anchored_block(
×
3716
                        &ic,
×
3717
                        &sn.consensus_hash,
×
3718
                        block,
×
3719
                        &parent_sn.consensus_hash,
×
3720
                        5,
3721
                    )
3722
                    .map_err(|e| format!("Failed to preprocess anchored block: {:?}", &e))
×
3723
            };
3724
            if res.is_ok() {
×
3725
                let pox_id = {
×
3726
                    let ic = sortdb.index_conn();
×
3727
                    let tip_sort_id =
×
3728
                        SortitionDB::get_canonical_sortition_tip(sortdb.conn()).unwrap();
×
3729
                    let sortdb_reader =
×
3730
                        SortitionHandleConn::open_reader(&ic, &tip_sort_id).unwrap();
×
3731
                    sortdb_reader.get_pox_id().unwrap()
×
3732
                };
3733
                test_debug!(
×
3734
                    "\n\n{:?}: after stacks block {:?}, tip PoX ID is {:?}\n\n",
3735
                    &self.to_neighbor().addr,
×
3736
                    &block.block_hash(),
×
3737
                    &pox_id
×
3738
                );
3739
                self.chain.coord.handle_new_stacks_block().unwrap();
×
3740
            }
×
3741

3742
            self.chain.sortdb = Some(sortdb);
×
3743
            self.chain.stacks_node = Some(node);
×
3744
            res
×
3745
        }
×
3746

3747
        /// Preprocess epoch 2.x microblocks.
3748
        /// Validate them and store them to staging.
3749
        pub fn preprocess_stacks_microblocks(
×
3750
            &mut self,
×
3751
            microblocks: &[StacksMicroblock],
×
3752
        ) -> Result<bool, String> {
×
3753
            assert!(!microblocks.is_empty());
×
3754
            let sortdb = self.chain.sortdb.take().unwrap();
×
3755
            let mut node = self.chain.stacks_node.take().unwrap();
×
3756
            let res = {
×
3757
                let anchor_block_hash = microblocks[0].header.prev_block.clone();
×
3758
                let sn = {
×
3759
                    let ic = sortdb.index_conn();
×
3760
                    let tip = SortitionDB::get_canonical_burn_chain_tip(&ic).unwrap();
×
3761
                    let sn_opt = SortitionDB::get_block_snapshot_for_winning_stacks_block(
×
3762
                        &ic,
×
3763
                        &tip.sortition_id,
×
3764
                        &anchor_block_hash,
×
3765
                    )
3766
                    .unwrap();
×
3767
                    if sn_opt.is_none() {
×
3768
                        return Err(format!(
×
3769
                            "No such block in canonical burn fork: {}",
×
3770
                            &anchor_block_hash
×
3771
                        ));
×
3772
                    }
×
3773
                    sn_opt.unwrap()
×
3774
                };
3775

3776
                let mut res = Ok(true);
×
3777
                for mblock in microblocks.iter() {
×
3778
                    res = node
×
3779
                        .chainstate
×
3780
                        .preprocess_streamed_microblock(
×
3781
                            &sn.consensus_hash,
×
3782
                            &anchor_block_hash,
×
3783
                            mblock,
×
3784
                        )
3785
                        .map_err(|e| format!("Failed to preprocess microblock: {:?}", &e));
×
3786

3787
                    if res.is_err() {
×
3788
                        break;
×
3789
                    }
×
3790
                }
3791
                res
×
3792
            };
3793

3794
            self.chain.sortdb = Some(sortdb);
×
3795
            self.chain.stacks_node = Some(node);
×
3796
            res
×
3797
        }
×
3798

3799
        /// Store the given epoch 2.x Stacks block and microblock to staging, and then try and
3800
        /// process them.
3801
        pub fn process_stacks_epoch_at_tip(
5,205✔
3802
            &mut self,
5,205✔
3803
            block: &StacksBlock,
5,205✔
3804
            microblocks: &[StacksMicroblock],
5,205✔
3805
        ) {
5,205✔
3806
            let sortdb = self.chain.sortdb.take().unwrap();
5,205✔
3807
            let mut node = self.chain.stacks_node.take().unwrap();
5,205✔
3808
            {
5,205✔
3809
                let ic = sortdb.index_conn();
5,205✔
3810
                let tip = SortitionDB::get_canonical_burn_chain_tip(&ic).unwrap();
5,205✔
3811
                node.chainstate
5,205✔
3812
                    .preprocess_stacks_epoch(&ic, &tip, block, microblocks)
5,205✔
3813
                    .unwrap();
5,205✔
3814
            }
5,205✔
3815
            self.chain.coord.handle_new_stacks_block().unwrap();
5,205✔
3816

3817
            let pox_id = {
5,205✔
3818
                let ic = sortdb.index_conn();
5,205✔
3819
                let tip_sort_id = SortitionDB::get_canonical_sortition_tip(sortdb.conn()).unwrap();
5,205✔
3820
                let sortdb_reader = SortitionHandleConn::open_reader(&ic, &tip_sort_id).unwrap();
5,205✔
3821
                sortdb_reader.get_pox_id().unwrap()
5,205✔
3822
            };
3823
            test_debug!(
5,205✔
3824
                "\n\n{:?}: after stacks block {:?}, tip PoX ID is {:?}\n\n",
3825
                &self.to_neighbor().addr,
×
3826
                &block.block_hash(),
×
3827
                &pox_id
×
3828
            );
3829

3830
            self.chain.sortdb = Some(sortdb);
5,205✔
3831
            self.chain.stacks_node = Some(node);
5,205✔
3832
        }
5,205✔
3833

3834
        /// Store the given epoch 2.x Stacks block and microblock to the given node's staging,
3835
        /// using the given sortition DB as well, and then try and process them.
3836
        fn inner_process_stacks_epoch_at_tip(
87✔
3837
            &mut self,
87✔
3838
            sortdb: &SortitionDB,
87✔
3839
            node: &mut TestStacksNode,
87✔
3840
            block: &StacksBlock,
87✔
3841
            microblocks: &[StacksMicroblock],
87✔
3842
        ) -> Result<(), coordinator_error> {
87✔
3843
            {
3844
                let ic = sortdb.index_conn();
87✔
3845
                let tip = SortitionDB::get_canonical_burn_chain_tip(&ic)?;
87✔
3846
                node.chainstate
87✔
3847
                    .preprocess_stacks_epoch(&ic, &tip, block, microblocks)?;
87✔
3848
            }
3849
            self.chain.coord.handle_new_stacks_block()?;
86✔
3850

3851
            let pox_id = {
86✔
3852
                let ic = sortdb.index_conn();
86✔
3853
                let tip_sort_id = SortitionDB::get_canonical_sortition_tip(sortdb.conn())?;
86✔
3854
                let sortdb_reader = SortitionHandleConn::open_reader(&ic, &tip_sort_id)?;
86✔
3855
                sortdb_reader.get_pox_id()?
86✔
3856
            };
3857
            test_debug!(
86✔
3858
                "\n\n{:?}: after stacks block {:?}, tip PoX ID is {:?}\n\n",
3859
                &self.to_neighbor().addr,
×
3860
                &block.block_hash(),
×
3861
                &pox_id
×
3862
            );
3863
            Ok(())
86✔
3864
        }
87✔
3865

3866
        /// Store the given epoch 2.x Stacks block and microblock to the given node's staging,
3867
        /// and then try and process them.
3868
        pub fn process_stacks_epoch_at_tip_checked(
87✔
3869
            &mut self,
87✔
3870
            block: &StacksBlock,
87✔
3871
            microblocks: &[StacksMicroblock],
87✔
3872
        ) -> Result<(), coordinator_error> {
87✔
3873
            let sortdb = self.chain.sortdb.take().unwrap();
87✔
3874
            let mut node = self.chain.stacks_node.take().unwrap();
87✔
3875
            let res =
87✔
3876
                self.inner_process_stacks_epoch_at_tip(&sortdb, &mut node, block, microblocks);
87✔
3877
            self.chain.sortdb = Some(sortdb);
87✔
3878
            self.chain.stacks_node = Some(node);
87✔
3879
            res
87✔
3880
        }
87✔
3881

3882
        /// Accept a new Stacks block and microblocks via the relayer, and then try to process
3883
        /// them.
3884
        pub fn process_stacks_epoch(
16✔
3885
            &mut self,
16✔
3886
            block: &StacksBlock,
16✔
3887
            consensus_hash: &ConsensusHash,
16✔
3888
            microblocks: &[StacksMicroblock],
16✔
3889
        ) {
16✔
3890
            let sortdb = self.chain.sortdb.take().unwrap();
16✔
3891
            let mut node = self.chain.stacks_node.take().unwrap();
16✔
3892
            {
3893
                let ic = sortdb.index_conn();
16✔
3894
                Relayer::process_new_anchored_block(
16✔
3895
                    &ic,
16✔
3896
                    &mut node.chainstate,
16✔
3897
                    consensus_hash,
16✔
3898
                    block,
16✔
3899
                    0,
3900
                )
3901
                .unwrap();
16✔
3902

3903
                let block_hash = block.block_hash();
16✔
3904
                for mblock in microblocks.iter() {
16✔
3905
                    node.chainstate
×
3906
                        .preprocess_streamed_microblock(consensus_hash, &block_hash, mblock)
×
3907
                        .unwrap();
×
3908
                }
×
3909
            }
3910
            self.chain.coord.handle_new_stacks_block().unwrap();
16✔
3911

3912
            let pox_id = {
16✔
3913
                let ic = sortdb.index_conn();
16✔
3914
                let tip_sort_id = SortitionDB::get_canonical_sortition_tip(sortdb.conn()).unwrap();
16✔
3915
                let sortdb_reader = SortitionHandleConn::open_reader(&ic, &tip_sort_id).unwrap();
16✔
3916
                sortdb_reader.get_pox_id().unwrap()
16✔
3917
            };
3918

3919
            test_debug!(
16✔
3920
                "\n\n{:?}: after stacks block {:?}, tip PoX ID is {:?}\n\n",
3921
                &self.to_neighbor().addr,
×
3922
                &block.block_hash(),
×
3923
                &pox_id
×
3924
            );
3925

3926
            self.chain.sortdb = Some(sortdb);
16✔
3927
            self.chain.stacks_node = Some(node);
16✔
3928
        }
16✔
3929

3930
        pub fn add_empty_burnchain_block(&mut self) -> (u64, BurnchainHeaderHash, ConsensusHash) {
254✔
3931
            self.next_burnchain_block(vec![])
254✔
3932
        }
254✔
3933

3934
        pub fn mine_empty_tenure(&mut self) -> (u64, BurnchainHeaderHash, ConsensusHash) {
2✔
3935
            let (burn_ops, ..) = self.begin_nakamoto_tenure(TenureChangeCause::BlockFound);
2✔
3936
            let result = self.next_burnchain_block(burn_ops);
2✔
3937
            // remove the last block commit so that the testpeer doesn't try to build off of this tenure
3938
            self.chain.miner.block_commits.pop();
2✔
3939
            result
2✔
3940
        }
2✔
3941

3942
        pub fn mempool(&mut self) -> &mut MemPoolDB {
×
3943
            self.mempool.as_mut().unwrap()
×
3944
        }
×
3945

3946
        pub fn chainstate(&mut self) -> &mut StacksChainState {
42,352✔
3947
            &mut self.chain.stacks_node.as_mut().unwrap().chainstate
42,352✔
3948
        }
42,352✔
3949

3950
        pub fn chainstate_ref(&self) -> &StacksChainState {
41,683✔
3951
            &self.chain.stacks_node.as_ref().unwrap().chainstate
41,683✔
3952
        }
41,683✔
3953

3954
        pub fn sortdb(&mut self) -> &mut SortitionDB {
22,460✔
3955
            self.chain.sortdb.as_mut().unwrap()
22,460✔
3956
        }
22,460✔
3957

3958
        pub fn sortdb_ref(&mut self) -> &SortitionDB {
40✔
3959
            self.chain.sortdb.as_ref().unwrap()
40✔
3960
        }
40✔
3961

3962
        pub fn with_dbs<F, R>(&mut self, f: F) -> R
1,176✔
3963
        where
1,176✔
3964
            F: FnOnce(&mut TestPeer, &mut SortitionDB, &mut TestStacksNode, &mut MemPoolDB) -> R,
1,176✔
3965
        {
3966
            let mut sortdb = self.chain.sortdb.take().unwrap();
1,176✔
3967
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
1,176✔
3968
            let mut mempool = self.mempool.take().unwrap();
1,176✔
3969

3970
            let res = f(self, &mut sortdb, &mut stacks_node, &mut mempool);
1,176✔
3971

3972
            self.chain.stacks_node = Some(stacks_node);
1,176✔
3973
            self.chain.sortdb = Some(sortdb);
1,176✔
3974
            self.mempool = Some(mempool);
1,176✔
3975
            res
1,176✔
3976
        }
1,176✔
3977

3978
        pub fn with_db_state<F, R>(&mut self, f: F) -> Result<R, net_error>
3,621✔
3979
        where
3,621✔
3980
            F: FnOnce(
3,621✔
3981
                &mut SortitionDB,
3,621✔
3982
                &mut StacksChainState,
3,621✔
3983
                &mut Relayer,
3,621✔
3984
                &mut MemPoolDB,
3,621✔
3985
            ) -> Result<R, net_error>,
3,621✔
3986
        {
3987
            let mut sortdb = self.chain.sortdb.take().unwrap();
3,621✔
3988
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
3,621✔
3989
            let mut mempool = self.mempool.take().unwrap();
3,621✔
3990

3991
            let res = f(
3,621✔
3992
                &mut sortdb,
3,621✔
3993
                &mut stacks_node.chainstate,
3,621✔
3994
                &mut self.relayer,
3,621✔
3995
                &mut mempool,
3,621✔
3996
            );
3,621✔
3997

3998
            self.chain.stacks_node = Some(stacks_node);
3,621✔
3999
            self.chain.sortdb = Some(sortdb);
3,621✔
4000
            self.mempool = Some(mempool);
3,621✔
4001
            res
3,621✔
4002
        }
3,621✔
4003

4004
        pub fn with_mining_state<F, R>(&mut self, f: F) -> Result<R, net_error>
×
4005
        where
×
4006
            F: FnOnce(
×
4007
                &mut SortitionDB,
×
4008
                &mut TestMiner,
×
4009
                &mut TestMiner,
×
4010
                &mut TestStacksNode,
×
4011
            ) -> Result<R, net_error>,
×
4012
        {
4013
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
×
4014
            let mut sortdb = self.chain.sortdb.take().unwrap();
×
4015
            let res = f(
×
4016
                &mut sortdb,
×
4017
                &mut self.chain.miner,
×
4018
                &mut self.config.chain_config.spending_account,
×
4019
                &mut stacks_node,
×
4020
            );
×
4021
            self.chain.sortdb = Some(sortdb);
×
4022
            self.chain.stacks_node = Some(stacks_node);
×
4023
            res
×
4024
        }
×
4025

4026
        pub fn with_network_state<F, R>(&mut self, f: F) -> Result<R, net_error>
26✔
4027
        where
26✔
4028
            F: FnOnce(
26✔
4029
                &mut SortitionDB,
26✔
4030
                &mut StacksChainState,
26✔
4031
                &mut PeerNetwork,
26✔
4032
                &mut Relayer,
26✔
4033
                &mut MemPoolDB,
26✔
4034
            ) -> Result<R, net_error>,
26✔
4035
        {
4036
            let mut sortdb = self.chain.sortdb.take().unwrap();
26✔
4037
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
26✔
4038
            let mut mempool = self.mempool.take().unwrap();
26✔
4039

4040
            let res = f(
26✔
4041
                &mut sortdb,
26✔
4042
                &mut stacks_node.chainstate,
26✔
4043
                &mut self.network,
26✔
4044
                &mut self.relayer,
26✔
4045
                &mut mempool,
26✔
4046
            );
26✔
4047

4048
            self.chain.stacks_node = Some(stacks_node);
26✔
4049
            self.chain.sortdb = Some(sortdb);
26✔
4050
            self.mempool = Some(mempool);
26✔
4051
            res
26✔
4052
        }
26✔
4053

4054
        pub fn with_peer_state<F, R>(&mut self, f: F) -> Result<R, net_error>
×
4055
        where
×
4056
            F: FnOnce(
×
4057
                &mut TestPeer,
×
4058
                &mut SortitionDB,
×
4059
                &mut StacksChainState,
×
4060
                &mut MemPoolDB,
×
4061
            ) -> Result<R, net_error>,
×
4062
        {
4063
            let mut sortdb = self.chain.sortdb.take().unwrap();
×
4064
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
×
4065
            let mut mempool = self.mempool.take().unwrap();
×
4066

4067
            let res = f(self, &mut sortdb, &mut stacks_node.chainstate, &mut mempool);
×
4068

4069
            self.chain.stacks_node = Some(stacks_node);
×
4070
            self.chain.sortdb = Some(sortdb);
×
4071
            self.mempool = Some(mempool);
×
4072
            res
×
4073
        }
×
4074

4075
        /// Make a tenure with the given transactions. Creates a coinbase tx with the given nonce, and then increments
4076
        /// the provided reference.
4077
        pub fn tenure_with_txs(
4,580✔
4078
            &mut self,
4,580✔
4079
            txs: &[StacksTransaction],
4,580✔
4080
            coinbase_nonce: &mut usize,
4,580✔
4081
        ) -> StacksBlockId {
4,580✔
4082
            let microblock_privkey = self.chain.miner.next_microblock_privkey();
4,580✔
4083
            let microblock_pubkeyhash =
4,580✔
4084
                Hash160::from_node_public_key(&StacksPublicKey::from_private(&microblock_privkey));
4,580✔
4085
            let tip = SortitionDB::get_canonical_burn_chain_tip(
4,580✔
4086
                self.chain.sortdb.as_ref().unwrap().conn(),
4,580✔
4087
            )
4088
            .unwrap();
4,580✔
4089
            let burnchain = self.config.chain_config.burnchain.clone();
4,580✔
4090

4091
            let (burn_ops, stacks_block, microblocks) = self.make_tenure(
4,580✔
4092
                |ref mut miner,
4093
                 ref mut sortdb,
4094
                 ref mut chainstate,
4095
                 vrf_proof,
4096
                 ref parent_opt,
4097
                 ref parent_microblock_header_opt| {
4,580✔
4098
                    let parent_tip = get_parent_tip(parent_opt, chainstate, sortdb);
4,580✔
4099
                    let coinbase_tx = make_coinbase(miner, *coinbase_nonce);
4,580✔
4100

4101
                    let mut block_txs = vec![coinbase_tx];
4,580✔
4102
                    block_txs.extend_from_slice(txs);
4,580✔
4103

4104
                    let block_builder = StacksBlockBuilder::make_regtest_block_builder(
4,580✔
4105
                        &burnchain,
4,580✔
4106
                        &parent_tip,
4,580✔
4107
                        vrf_proof,
4,580✔
4108
                        tip.total_burn,
4,580✔
4109
                        &microblock_pubkeyhash,
4,580✔
4110
                    )
4111
                    .unwrap();
4,580✔
4112
                    let (anchored_block, _size, _cost) =
4,580✔
4113
                        StacksBlockBuilder::make_anchored_block_from_txs(
4,580✔
4114
                            block_builder,
4,580✔
4115
                            chainstate,
4,580✔
4116
                            &sortdb.index_handle(&tip.sortition_id),
4,580✔
4117
                            block_txs,
4,580✔
4118
                        )
4,580✔
4119
                        .unwrap();
4,580✔
4120
                    (anchored_block, vec![])
4,580✔
4121
                },
4,580✔
4122
            );
4123

4124
            let (_, _, consensus_hash) = self.next_burnchain_block(burn_ops);
4,580✔
4125
            self.process_stacks_epoch_at_tip(&stacks_block, &microblocks);
4,580✔
4126

4127
            *coinbase_nonce += 1;
4,580✔
4128

4129
            let tip_id = StacksBlockId::new(&consensus_hash, &stacks_block.block_hash());
4,580✔
4130

4131
            if let Some((start_check_cycle, end_check_cycle)) = self.config.check_pox_invariants {
4,580✔
4132
                pox_2_tests::check_all_stacker_link_invariants(
853✔
4133
                    self,
853✔
4134
                    &tip_id,
853✔
4135
                    start_check_cycle,
853✔
4136
                    end_check_cycle,
853✔
4137
                );
853✔
4138
            }
3,727✔
4139

4140
            self.refresh_burnchain_view();
4,580✔
4141

4142
            let (stacks_tip_ch, stacks_tip_bh) =
4,580✔
4143
                SortitionDB::get_canonical_stacks_chain_tip_hash(self.sortdb().conn()).unwrap();
4,580✔
4144
            assert_eq!(
4,580✔
4145
                self.network.stacks_tip.block_id(),
4,580✔
4146
                StacksBlockId::new(&stacks_tip_ch, &stacks_tip_bh)
4,580✔
4147
            );
4148

4149
            tip_id
4,580✔
4150
        }
4,580✔
4151

4152
        /// Make a tenure, using `tenure_builder` to generate a Stacks block and a list of
4153
        /// microblocks.
4154
        pub fn make_tenure<F>(
5,180✔
4155
            &mut self,
5,180✔
4156
            mut tenure_builder: F,
5,180✔
4157
        ) -> (
5,180✔
4158
            Vec<BlockstackOperationType>,
5,180✔
4159
            StacksBlock,
5,180✔
4160
            Vec<StacksMicroblock>,
5,180✔
4161
        )
5,180✔
4162
        where
5,180✔
4163
            F: FnMut(
5,180✔
4164
                &mut TestMiner,
5,180✔
4165
                &mut SortitionDB,
5,180✔
4166
                &mut StacksChainState,
5,180✔
4167
                &VRFProof,
5,180✔
4168
                Option<&StacksBlock>,
5,180✔
4169
                Option<&StacksMicroblockHeader>,
5,180✔
4170
            ) -> (StacksBlock, Vec<StacksMicroblock>),
5,180✔
4171
        {
4172
            let mut sortdb = self.chain.sortdb.take().unwrap();
5,180✔
4173
            let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
5,180✔
4174

4175
            let mut burn_block = TestBurnchainBlock::new(&tip, 0);
5,180✔
4176
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
5,180✔
4177

4178
            let parent_block_opt = stacks_node.get_last_anchored_block(&self.chain.miner);
5,180✔
4179
            let parent_sortition_opt = parent_block_opt.as_ref().and_then(|parent_block| {
5,180✔
4180
                let ic = sortdb.index_conn();
4,985✔
4181
                SortitionDB::get_block_snapshot_for_winning_stacks_block(
4,985✔
4182
                    &ic,
4,985✔
4183
                    &tip.sortition_id,
4,985✔
4184
                    &parent_block.block_hash(),
4,985✔
4185
                )
4186
                .unwrap()
4,985✔
4187
            });
4,985✔
4188

4189
            let parent_microblock_header_opt = get_last_microblock_header(
5,180✔
4190
                &stacks_node,
5,180✔
4191
                &self.chain.miner,
5,180✔
4192
                parent_block_opt.as_ref(),
5,180✔
4193
            );
4194
            let last_key = stacks_node.get_last_key(&self.chain.miner);
5,180✔
4195

4196
            let network_id = self.config.chain_config.network_id;
5,180✔
4197
            let chainstate_path = self.chain.chainstate_path.clone();
5,180✔
4198
            let burn_block_height = burn_block.block_height;
5,180✔
4199

4200
            let proof = self
5,180✔
4201
                .chain
5,180✔
4202
                .miner
5,180✔
4203
                .make_proof(
5,180✔
4204
                    &last_key.public_key,
5,180✔
4205
                    &burn_block.parent_snapshot.sortition_hash,
5,180✔
4206
                )
4207
                .unwrap_or_else(|| panic!("FATAL: no private key for {:?}", last_key.public_key));
5,180✔
4208

4209
            let (stacks_block, microblocks) = tenure_builder(
5,180✔
4210
                &mut self.chain.miner,
5,180✔
4211
                &mut sortdb,
5,180✔
4212
                &mut stacks_node.chainstate,
5,180✔
4213
                &proof,
5,180✔
4214
                parent_block_opt.as_ref(),
5,180✔
4215
                parent_microblock_header_opt.as_ref(),
5,180✔
4216
            );
5,180✔
4217

4218
            let mut block_commit_op = stacks_node.make_tenure_commitment(
5,180✔
4219
                &sortdb,
5,180✔
4220
                &mut burn_block,
5,180✔
4221
                &mut self.chain.miner,
5,180✔
4222
                &stacks_block,
5,180✔
4223
                microblocks.clone(),
5,180✔
4224
                1000,
4225
                &last_key,
5,180✔
4226
                parent_sortition_opt.as_ref(),
5,180✔
4227
            );
4228

4229
            // patch up block-commit -- these blocks all mine off of genesis
4230
            if stacks_block.header.parent_block == BlockHeaderHash([0u8; 32]) {
5,180✔
4231
                block_commit_op.parent_block_ptr = 0;
199✔
4232
                block_commit_op.parent_vtxindex = 0;
199✔
4233
            }
4,981✔
4234

4235
            let leader_key_op =
5,180✔
4236
                stacks_node.add_key_register(&mut burn_block, &mut self.chain.miner);
5,180✔
4237

4238
            // patch in reward set info
4239
            match get_next_recipients(
5,180✔
4240
                &tip,
5,180✔
4241
                &mut stacks_node.chainstate,
5,180✔
4242
                &mut sortdb,
5,180✔
4243
                &self.config.chain_config.burnchain,
5,180✔
4244
                &OnChainRewardSetProvider::new(),
5,180✔
4245
            ) {
5,180✔
4246
                Ok(recipients) => {
5,179✔
4247
                    block_commit_op.commit_outs = match recipients {
5,179✔
4248
                        Some(info) => {
278✔
4249
                            let mut recipients = info
278✔
4250
                                .recipients
278✔
4251
                                .into_iter()
278✔
4252
                                .map(|x| x.0)
278✔
4253
                                .collect::<Vec<PoxAddress>>();
278✔
4254
                            if recipients.len() == 1 {
278✔
4255
                                recipients.push(PoxAddress::standard_burn_address(false));
50✔
4256
                            }
228✔
4257
                            recipients
278✔
4258
                        }
4259
                        None => {
4260
                            if self
4,901✔
4261
                                .config
4,901✔
4262
                                .chain_config
4,901✔
4263
                                .burnchain
4,901✔
4264
                                .is_in_prepare_phase(burn_block.block_height)
4,901✔
4265
                            {
4266
                                vec![PoxAddress::standard_burn_address(false)]
2,007✔
4267
                            } else {
4268
                                vec![
2,894✔
4269
                                    PoxAddress::standard_burn_address(false),
2,894✔
4270
                                    PoxAddress::standard_burn_address(false),
2,894✔
4271
                                ]
4272
                            }
4273
                        }
4274
                    };
4275
                    test_debug!(
5,179✔
4276
                        "Block commit at height {} has {} recipients: {:?}",
4277
                        block_commit_op.block_height,
4278
                        block_commit_op.commit_outs.len(),
×
4279
                        &block_commit_op.commit_outs
×
4280
                    );
4281
                }
4282
                Err(e) => {
1✔
4283
                    panic!("Failure fetching recipient set: {:?}", e);
1✔
4284
                }
4285
            };
4286

4287
            self.chain.stacks_node = Some(stacks_node);
5,179✔
4288
            self.chain.sortdb = Some(sortdb);
5,179✔
4289
            (
5,179✔
4290
                vec![
5,179✔
4291
                    BlockstackOperationType::LeaderKeyRegister(leader_key_op),
5,179✔
4292
                    BlockstackOperationType::LeaderBlockCommit(block_commit_op),
5,179✔
4293
                ],
5,179✔
4294
                stacks_block,
5,179✔
4295
                microblocks,
5,179✔
4296
            )
5,179✔
4297
        }
5,179✔
4298

4299
        /// Produce a default, non-empty tenure for epoch 2.x
4300
        pub fn make_default_tenure(
97✔
4301
            &mut self,
97✔
4302
        ) -> (
97✔
4303
            Vec<BlockstackOperationType>,
97✔
4304
            StacksBlock,
97✔
4305
            Vec<StacksMicroblock>,
97✔
4306
        ) {
97✔
4307
            let sortdb = self.chain.sortdb.take().unwrap();
97✔
4308
            let mut burn_block = {
97✔
4309
                let sn = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
97✔
4310
                TestBurnchainBlock::new(&sn, 0)
97✔
4311
            };
4312

4313
            let mut stacks_node = self.chain.stacks_node.take().unwrap();
97✔
4314

4315
            let parent_block_opt = stacks_node.get_last_anchored_block(&self.chain.miner);
97✔
4316
            let parent_microblock_header_opt = get_last_microblock_header(
97✔
4317
                &stacks_node,
97✔
4318
                &self.chain.miner,
97✔
4319
                parent_block_opt.as_ref(),
97✔
4320
            );
4321
            let last_key = stacks_node.get_last_key(&self.chain.miner);
97✔
4322

4323
            let network_id = self.config.chain_config.network_id;
97✔
4324
            let chainstate_path = self.chain.chainstate_path.clone();
97✔
4325
            let burn_block_height = burn_block.block_height;
97✔
4326

4327
            let (stacks_block, microblocks, block_commit_op) = stacks_node.mine_stacks_block(
97✔
4328
                &sortdb,
97✔
4329
                &mut self.chain.miner,
97✔
4330
                &mut burn_block,
97✔
4331
                &last_key,
97✔
4332
                parent_block_opt.as_ref(),
97✔
4333
                1000,
4334
                |mut builder, ref mut miner, sortdb| {
97✔
4335
                    let (mut miner_chainstate, _) =
97✔
4336
                        StacksChainState::open(false, network_id, &chainstate_path, None).unwrap();
97✔
4337
                    let sort_iconn = sortdb.index_handle_at_tip();
97✔
4338

4339
                    let mut miner_epoch_info = builder
97✔
4340
                        .pre_epoch_begin(&mut miner_chainstate, &sort_iconn, true)
97✔
4341
                        .unwrap();
97✔
4342
                    let mut epoch = builder
97✔
4343
                        .epoch_begin(&sort_iconn, &mut miner_epoch_info)
97✔
4344
                        .unwrap()
97✔
4345
                        .0;
97✔
4346

4347
                    let (stacks_block, microblocks) =
97✔
4348
                        mine_smart_contract_block_contract_call_microblock(
97✔
4349
                            &mut epoch,
97✔
4350
                            &mut builder,
97✔
4351
                            miner,
97✔
4352
                            burn_block_height as usize,
97✔
4353
                            parent_microblock_header_opt.as_ref(),
97✔
4354
                        );
97✔
4355

4356
                    builder.epoch_finish(epoch).unwrap();
97✔
4357
                    (stacks_block, microblocks)
97✔
4358
                },
97✔
4359
            );
4360

4361
            let leader_key_op =
97✔
4362
                stacks_node.add_key_register(&mut burn_block, &mut self.chain.miner);
97✔
4363

4364
            self.chain.stacks_node = Some(stacks_node);
97✔
4365
            self.chain.sortdb = Some(sortdb);
97✔
4366
            (
97✔
4367
                vec![
97✔
4368
                    BlockstackOperationType::LeaderKeyRegister(leader_key_op),
97✔
4369
                    BlockstackOperationType::LeaderBlockCommit(block_commit_op),
97✔
4370
                ],
97✔
4371
                stacks_block,
97✔
4372
                microblocks,
97✔
4373
            )
97✔
4374
        }
97✔
4375

4376
        pub fn to_neighbor(&self) -> Neighbor {
1,234,592✔
4377
            self.config.to_neighbor()
1,234,592✔
4378
        }
1,234,592✔
4379

4380
        pub fn to_peer_host(&self) -> PeerHost {
147✔
4381
            self.config.to_peer_host()
147✔
4382
        }
147✔
4383

4384
        pub fn get_public_key(&self) -> Secp256k1PublicKey {
4✔
4385
            let local_peer = PeerDB::get_local_peer(self.network.peerdb.conn()).unwrap();
4✔
4386
            Secp256k1PublicKey::from_private(&local_peer.private_key)
4✔
4387
        }
4✔
4388

4389
        pub fn get_peerdb_conn(&self) -> &DBConn {
143✔
4390
            self.network.peerdb.conn()
143✔
4391
        }
143✔
4392

4393
        pub fn get_burnchain_view(&mut self) -> Result<BurnchainView, db_error> {
104✔
4394
            let sortdb = self.chain.sortdb.take().unwrap();
104✔
4395
            let view_res = {
104✔
4396
                let chaintip =
104✔
4397
                    SortitionDB::get_canonical_burn_chain_tip(&sortdb.index_conn()).unwrap();
104✔
4398
                SortitionDB::get_burnchain_view(
104✔
4399
                    &sortdb.index_conn(),
104✔
4400
                    &self.config.chain_config.burnchain,
104✔
4401
                    &chaintip,
104✔
4402
                )
4403
            };
4404
            self.chain.sortdb = Some(sortdb);
104✔
4405
            view_res
104✔
4406
        }
104✔
4407

4408
        pub fn dump_frontier(&self) {
8✔
4409
            let conn = self.network.peerdb.conn();
8✔
4410
            let peers = PeerDB::get_all_peers(conn).unwrap();
8✔
4411
            debug!("--- BEGIN ALL PEERS ({}) ---", peers.len());
8✔
4412
            debug!("{:#?}", &peers);
8✔
4413
            debug!("--- END ALL PEERS ({}) -----", peers.len());
8✔
4414
        }
8✔
4415

4416
        pub fn p2p_socketaddr(&self) -> SocketAddr {
16✔
4417
            SocketAddr::new(
16✔
4418
                IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
16✔
4419
                self.config.server_port,
16✔
4420
            )
4421
        }
16✔
4422

4423
        pub fn make_client_convo(&self) -> ConversationP2P {
16✔
4424
            ConversationP2P::new(
16✔
4425
                self.config.chain_config.network_id,
16✔
4426
                self.config.peer_version,
16✔
4427
                &self.config.chain_config.burnchain,
16✔
4428
                &SocketAddr::new(
16✔
4429
                    IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
16✔
4430
                    self.config.server_port,
16✔
4431
                ),
16✔
4432
                &self.config.connection_opts,
16✔
4433
                false,
4434
                0,
4435
                self.config
16✔
4436
                    .chain_config
16✔
4437
                    .epochs
16✔
4438
                    .clone()
16✔
4439
                    .unwrap_or(StacksEpoch::unit_test_3_0(0)),
16✔
4440
            )
4441
        }
16✔
4442

4443
        pub fn make_client_local_peer(&self, privk: StacksPrivateKey) -> LocalPeer {
16✔
4444
            LocalPeer::new(
16✔
4445
                self.config.chain_config.network_id,
16✔
4446
                self.network.local_peer.parent_network_id,
16✔
4447
                PeerAddress::from_socketaddr(&SocketAddr::new(
16✔
4448
                    IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
16✔
4449
                    self.config.server_port,
16✔
4450
                )),
16✔
4451
                self.config.server_port,
16✔
4452
                Some(privk),
16✔
4453
                u64::MAX,
4454
                UrlString::try_from(format!("http://127.0.0.1:{}", self.config.http_port).as_str())
16✔
4455
                    .unwrap(),
16✔
4456
                vec![],
16✔
4457
            )
4458
        }
16✔
4459

4460
        pub fn get_burn_block_height(&self) -> u64 {
2,353✔
4461
            SortitionDB::get_canonical_burn_chain_tip(
2,353✔
4462
                self.chain
2,353✔
4463
                    .sortdb
2,353✔
4464
                    .as_ref()
2,353✔
4465
                    .expect("Failed to get sortdb")
2,353✔
4466
                    .conn(),
2,353✔
4467
            )
2,353✔
4468
            .expect("Failed to get canonical burn chain tip")
2,353✔
4469
            .block_height
2,353✔
4470
        }
2,353✔
4471

4472
        pub fn get_reward_cycle(&self) -> u64 {
1,176✔
4473
            let block_height = self.get_burn_block_height();
1,176✔
4474
            self.config
1,176✔
4475
                .chain_config
1,176✔
4476
                .burnchain
1,176✔
4477
                .block_height_to_reward_cycle(block_height)
1,176✔
4478
                .unwrap_or_else(|| {
1,176✔
4479
                    panic!("Failed to get reward cycle for block height {block_height}")
×
4480
                })
4481
        }
1,176✔
4482

4483
        /// Verify that the sortition DB migration into Nakamoto worked correctly.
4484
        pub fn check_nakamoto_migration(&mut self) {
140✔
4485
            let mut sortdb = self.chain.sortdb.take().unwrap();
140✔
4486
            let mut node = self.chain.stacks_node.take().unwrap();
140✔
4487
            let chainstate = &mut node.chainstate;
140✔
4488

4489
            let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
140✔
4490
            let epochs = SortitionDB::get_stacks_epochs(sortdb.conn()).unwrap();
140✔
4491
            let epoch_3 = epochs.get(StacksEpochId::Epoch30).unwrap().clone();
140✔
4492

4493
            let mut all_chain_tips = sortdb.get_all_stacks_chain_tips().unwrap();
140✔
4494
            let mut all_preprocessed_reward_sets =
140✔
4495
                SortitionDB::get_all_preprocessed_reward_sets(sortdb.conn()).unwrap();
140✔
4496

4497
            // see that we can reconstruct the canonical chain tips for epoch 2.5 and earlier
4498
            // NOTE: the migration logic DOES NOT WORK and IS NOT MEANT TO WORK with Nakamoto blocks,
4499
            // so test this only with epoch 2 blocks before the epoch2-3 transition.
4500
            let epoch2_sns: Vec<_> = sortdb
140✔
4501
                .get_all_snapshots()
140✔
4502
                .unwrap()
140✔
4503
                .into_iter()
140✔
4504
                .filter(|sn| sn.block_height + 1 < epoch_3.start_height)
7,455✔
4505
                .collect();
140✔
4506

4507
            let epoch2_chs: HashSet<_> = epoch2_sns
140✔
4508
                .iter()
140✔
4509
                .map(|sn| sn.consensus_hash.clone())
5,515✔
4510
                .collect();
140✔
4511

4512
            let expected_epoch2_chain_tips: Vec<_> = all_chain_tips
140✔
4513
                .clone()
140✔
4514
                .into_iter()
140✔
4515
                .filter(|tip| epoch2_chs.contains(&tip.1))
3,955✔
4516
                .collect();
140✔
4517

4518
            let tx = sortdb.tx_begin().unwrap();
140✔
4519
            tx.execute(
140✔
4520
                "CREATE TABLE stacks_chain_tips_backup AS SELECT * FROM stacks_chain_tips;",
140✔
4521
                NO_PARAMS,
140✔
4522
            )
140✔
4523
            .unwrap();
140✔
4524
            tx.execute("DELETE FROM stacks_chain_tips;", NO_PARAMS)
140✔
4525
                .unwrap();
140✔
4526
            tx.commit().unwrap();
140✔
4527

4528
            // NOTE: this considers each and every snapshot, but we only care about epoch2.x
4529
            sortdb.apply_schema_8_stacks_chain_tips(&tip).unwrap();
140✔
4530
            let migrated_epoch2_chain_tips: Vec<_> = sortdb
140✔
4531
                .get_all_stacks_chain_tips()
140✔
4532
                .unwrap()
140✔
4533
                .into_iter()
140✔
4534
                .filter(|tip| epoch2_chs.contains(&tip.1))
9,255✔
4535
                .collect();
140✔
4536

4537
            // what matters is that the last tip is the same, and that each sortition has a chain tip.
4538
            // depending on block arrival order, different sortitions might have witnessed different
4539
            // stacks blocks as their chain tips, however.
4540
            assert_eq!(
140✔
4541
                migrated_epoch2_chain_tips.last().unwrap(),
140✔
4542
                expected_epoch2_chain_tips.last().unwrap()
140✔
4543
            );
4544

4545
            // restore
4546
            let tx = sortdb.tx_begin().unwrap();
140✔
4547
            tx.execute("DROP TABLE stacks_chain_tips;", NO_PARAMS)
140✔
4548
                .unwrap();
140✔
4549
            tx.execute(
140✔
4550
                "ALTER TABLE stacks_chain_tips_backup RENAME TO stacks_chain_tips;",
140✔
4551
                NO_PARAMS,
140✔
4552
            )
140✔
4553
            .unwrap();
140✔
4554
            tx.commit().unwrap();
140✔
4555

4556
            // see that we calculate all the prior reward set infos
4557
            let mut expected_epoch2_reward_sets: Vec<_> =
140✔
4558
                SortitionDB::get_all_preprocessed_reward_sets(sortdb.conn())
140✔
4559
                    .unwrap()
140✔
4560
                    .into_iter()
140✔
4561
                    .filter(|(sort_id, rc_info)| {
555✔
4562
                        let sn = SortitionDB::get_block_snapshot(sortdb.conn(), sort_id)
555✔
4563
                            .unwrap()
555✔
4564
                            .unwrap();
555✔
4565
                        let rc_sn = sortdb
555✔
4566
                            .pox_constants
555✔
4567
                            .block_height_to_reward_cycle(
555✔
4568
                                sortdb.first_block_height,
555✔
4569
                                sn.block_height,
555✔
4570
                            )
4571
                            .unwrap();
555✔
4572
                        let rc_height = sortdb
555✔
4573
                            .pox_constants
555✔
4574
                            .reward_cycle_to_block_height(sortdb.first_block_height, rc_sn + 1);
555✔
4575
                        sn.block_height <= epoch_3.start_height && sn.block_height < rc_height
555✔
4576
                    })
555✔
4577
                    .collect();
140✔
4578

4579
            let tx = sortdb.tx_begin().unwrap();
140✔
4580
            tx.execute("CREATE TABLE preprocessed_reward_sets_backup AS SELECT * FROM preprocessed_reward_sets;", NO_PARAMS).unwrap();
140✔
4581
            tx.execute("DELETE FROM preprocessed_reward_sets;", NO_PARAMS)
140✔
4582
                .unwrap();
140✔
4583
            tx.commit().unwrap();
140✔
4584

4585
            let migrator = SortitionDBMigrator::new(
140✔
4586
                self.config.chain_config.burnchain.clone(),
140✔
4587
                &self.chain.chainstate_path,
140✔
4588
                None,
140✔
4589
            )
4590
            .unwrap();
140✔
4591
            sortdb
140✔
4592
                .apply_schema_8_preprocessed_reward_sets(&tip, migrator)
140✔
4593
                .unwrap();
140✔
4594

4595
            let mut migrated_epoch2_reward_sets: Vec<_> =
140✔
4596
                SortitionDB::get_all_preprocessed_reward_sets(sortdb.conn())
140✔
4597
                    .unwrap()
140✔
4598
                    .into_iter()
140✔
4599
                    .filter(|(sort_id, rc_info)| {
555✔
4600
                        let sn = SortitionDB::get_block_snapshot(sortdb.conn(), sort_id)
555✔
4601
                            .unwrap()
555✔
4602
                            .unwrap();
555✔
4603
                        sn.block_height < epoch_3.start_height
555✔
4604
                    })
555✔
4605
                    .collect();
140✔
4606

4607
            expected_epoch2_reward_sets.sort_by(|a, b| a.0.cmp(&b.0));
690✔
4608
            migrated_epoch2_reward_sets.sort_by(|a, b| a.0.cmp(&b.0));
690✔
4609

4610
            assert_eq!(expected_epoch2_reward_sets, migrated_epoch2_reward_sets);
140✔
4611

4612
            let tx = sortdb.tx_begin().unwrap();
140✔
4613
            tx.execute("DROP TABLE preprocessed_reward_sets;", NO_PARAMS)
140✔
4614
                .unwrap();
140✔
4615
            tx.execute(
140✔
4616
                "ALTER TABLE preprocessed_reward_sets_backup RENAME TO preprocessed_reward_sets;",
140✔
4617
                NO_PARAMS,
140✔
4618
            )
140✔
4619
            .unwrap();
140✔
4620
            tx.commit().unwrap();
140✔
4621

4622
            // sanity check -- restored tables are the same
4623
            let mut restored_chain_tips = sortdb.get_all_stacks_chain_tips().unwrap();
140✔
4624
            let mut restored_reward_sets =
140✔
4625
                SortitionDB::get_all_preprocessed_reward_sets(sortdb.conn()).unwrap();
140✔
4626

4627
            all_chain_tips.sort_by(|a, b| a.0.cmp(&b.0));
20,963✔
4628
            restored_chain_tips.sort_by(|a, b| a.0.cmp(&b.0));
20,963✔
4629

4630
            all_preprocessed_reward_sets.sort_by(|a, b| a.0.cmp(&b.0));
690✔
4631
            restored_reward_sets.sort_by(|a, b| a.0.cmp(&b.0));
690✔
4632

4633
            assert_eq!(restored_chain_tips, all_chain_tips);
140✔
4634
            assert_eq!(restored_reward_sets, all_preprocessed_reward_sets);
140✔
4635

4636
            self.chain.sortdb = Some(sortdb);
140✔
4637
            self.chain.stacks_node = Some(node);
140✔
4638
        }
140✔
4639

4640
        /// Verify that all malleablized blocks are duly processed
4641
        pub fn check_malleablized_blocks(
103✔
4642
            &self,
103✔
4643
            all_blocks: Vec<NakamotoBlock>,
103✔
4644
            expected_siblings: usize,
103✔
4645
        ) {
103✔
4646
            if !self.chain.mine_malleablized_blocks {
103✔
4647
                return;
62✔
4648
            }
41✔
4649
            for block in all_blocks.iter() {
8,335✔
4650
                let sighash = block.header.signer_signature_hash();
8,335✔
4651
                let siblings = self
8,335✔
4652
                    .chainstate_ref()
8,335✔
4653
                    .nakamoto_blocks_db()
8,335✔
4654
                    .get_blocks_at_height(block.header.chain_length);
8,335✔
4655

4656
                debug!("Expect {} siblings: {:?}", expected_siblings, &siblings);
8,335✔
4657
                assert_eq!(siblings.len(), expected_siblings);
8,335✔
4658

4659
                for sibling in siblings {
16,670✔
4660
                    let (processed, orphaned) = NakamotoChainState::get_nakamoto_block_status(
16,670✔
4661
                        self.chainstate_ref().nakamoto_blocks_db(),
16,670✔
4662
                        self.chainstate_ref().db(),
16,670✔
4663
                        &sibling.header.consensus_hash,
16,670✔
4664
                        &sibling.header.block_hash(),
16,670✔
4665
                    )
16,670✔
4666
                    .unwrap()
16,670✔
4667
                    .unwrap();
16,670✔
4668
                    assert!(processed);
16,670✔
4669
                    assert!(!orphaned);
16,670✔
4670
                }
4671
            }
4672
        }
103✔
4673

4674
        /// Set the nakamoto tenure to mine on
4675
        pub fn mine_nakamoto_on(&mut self, parent_tenure: Vec<NakamotoBlock>) {
86✔
4676
            self.chain.nakamoto_parent_tenure_opt = Some(parent_tenure);
86✔
4677
        }
86✔
4678

4679
        /// Clear the tenure to mine on. This causes the miner to build on the canonical tip
4680
        pub fn mine_nakamoto_on_canonical_tip(&mut self) {
×
4681
            self.chain.nakamoto_parent_tenure_opt = None;
×
4682
        }
×
4683

4684
        /// Get an account off of a tip
4685
        pub fn get_account(
140✔
4686
            &mut self,
140✔
4687
            tip: &StacksBlockId,
140✔
4688
            account: &PrincipalData,
140✔
4689
        ) -> StacksAccount {
140✔
4690
            let sortdb = self
140✔
4691
                .chain
140✔
4692
                .sortdb
140✔
4693
                .take()
140✔
4694
                .expect("FATAL: sortdb not restored");
140✔
4695
            let mut node = self
140✔
4696
                .chain
140✔
4697
                .stacks_node
140✔
4698
                .take()
140✔
4699
                .expect("FATAL: chainstate not restored");
140✔
4700

4701
            let acct = node
140✔
4702
                .chainstate
140✔
4703
                .maybe_read_only_clarity_tx(
140✔
4704
                    &sortdb.index_handle_at_block(&node.chainstate, tip).unwrap(),
140✔
4705
                    tip,
140✔
4706
                    |clarity_tx| StacksChainState::get_account(clarity_tx, account),
140✔
4707
                )
4708
                .unwrap()
140✔
4709
                .unwrap();
140✔
4710

4711
            self.chain.sortdb = Some(sortdb);
140✔
4712
            self.chain.stacks_node = Some(node);
140✔
4713
            acct
140✔
4714
        }
140✔
4715
    }
4716
}
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