• 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

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

17
use std::collections::{HashMap, HashSet};
18
use std::{fmt, fs};
19

20
use clarity::vm::types::QualifiedContractIdentifier;
21
use rand::seq::SliceRandom;
22
use rand::{thread_rng, RngCore};
23
use rusqlite::types::ToSql;
24
use rusqlite::{params, Connection, OpenFlags, OptionalExtension, Row, Transaction};
25
use stacks_common::types::net::PeerAddress;
26
use stacks_common::types::sqlite::NO_PARAMS;
27
use stacks_common::util;
28
use stacks_common::util::get_epoch_time_secs;
29
use stacks_common::util::hash::{bin_bytes, hex_bytes, to_bin, to_hex, Hash160, Sha512Trunc256Sum};
30
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
31

32
use crate::burnchains::PrivateKey;
33
use crate::chainstate::stacks::{StacksPrivateKey, StacksPublicKey};
34
#[cfg(any(test, feature = "testing"))]
35
use crate::core::NETWORK_P2P_PORT;
36
use crate::net::asn::ASEntry4;
37
use crate::net::{Neighbor, NeighborAddress, NeighborKey, ServiceFlags};
38
use crate::util_lib::db::{
39
    query_count, query_row, query_row_panic, query_rows, sqlite_open, tx_begin_immediate,
40
    u64_to_sql, DBConn, Error as db_error, FromColumn, FromRow,
41
};
42
use crate::util_lib::strings::UrlString;
43

44
pub const PEERDB_VERSION: &str = "3";
45

46
const NUM_SLOTS: usize = 8;
47

48
impl FromColumn<PeerAddress> for PeerAddress {
49
    fn from_column(row: &Row, column_name: &str) -> Result<PeerAddress, db_error> {
35,812,038✔
50
        let addrbytes_bin: String = row.get_unwrap(column_name);
35,812,038✔
51
        if addrbytes_bin.len() != 128 {
35,812,038✔
52
            error!("Unparsable peer address {}", addrbytes_bin);
×
53
            return Err(db_error::ParseError);
×
54
        }
35,812,038✔
55
        let addrbytes = bin_bytes(&addrbytes_bin).map_err(|_e| {
35,812,038✔
56
            error!("Unparseable peer address {}", addrbytes_bin);
×
57
            db_error::ParseError
×
58
        })?;
×
59

60
        let addrbytes_slice: [u8; 16] = addrbytes.try_into().map_err(|e: Vec<u8>| {
35,812,038✔
61
            error!("Peer address has {} bytes; expected 16", e.len());
×
62
            db_error::ParseError
×
63
        })?;
×
64

65
        Ok(PeerAddress(addrbytes_slice))
35,812,038✔
66
    }
35,812,038✔
67
}
68

69
impl FromRow<QualifiedContractIdentifier> for QualifiedContractIdentifier {
70
    fn from_row(row: &Row) -> Result<QualifiedContractIdentifier, db_error> {
10,424,061✔
71
        let cid_str: String = row.get_unwrap("smart_contract_id");
10,424,061✔
72
        let cid =
10,424,061✔
73
            QualifiedContractIdentifier::parse(&cid_str).map_err(|_e| db_error::ParseError)?;
10,424,061✔
74

75
        Ok(cid)
10,424,061✔
76
    }
10,424,061✔
77
}
78

79
#[derive(PartialEq, Clone)]
80
pub struct LocalPeer {
81
    pub network_id: u32,
82
    pub parent_network_id: u32,
83
    nonce: [u8; 32],
84
    pub private_key: Secp256k1PrivateKey,
85
    pub private_key_expire: u64,
86

87
    pub addrbytes: PeerAddress,
88
    pub port: u16,
89
    pub services: u16,
90
    pub data_url: UrlString,
91
    pub stacker_dbs: Vec<QualifiedContractIdentifier>,
92

93
    // filled in and curated at runtime
94
    pub public_ip_address: Option<(PeerAddress, u16)>,
95
}
96

97
impl fmt::Display for LocalPeer {
98
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
900,570✔
99
        write!(f, "local")?;
900,570✔
100
        match &self.public_ip_address {
900,570✔
101
            None => write!(f, "::UNKNOWN"),
443,412✔
102
            Some((addr, port)) => write!(f, "::{}", addr.to_socketaddr(*port)),
457,158✔
103
        }
104
    }
900,570✔
105
}
106

107
impl fmt::Debug for LocalPeer {
108
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
900,570✔
109
        write!(f, "{self}")
900,570✔
110
    }
900,570✔
111
}
112

113
impl LocalPeer {
114
    pub fn new(
3,358✔
115
        network_id: u32,
3,358✔
116
        parent_network_id: u32,
3,358✔
117
        addrbytes: PeerAddress,
3,358✔
118
        port: u16,
3,358✔
119
        privkey: Option<Secp256k1PrivateKey>,
3,358✔
120
        key_expire: u64,
3,358✔
121
        data_url: UrlString,
3,358✔
122
        stacker_dbs: Vec<QualifiedContractIdentifier>,
3,358✔
123
    ) -> LocalPeer {
3,358✔
124
        let mut pkey = privkey.unwrap_or(Secp256k1PrivateKey::random());
3,358✔
125
        pkey.set_compress_public(true);
3,358✔
126

127
        let mut rng = thread_rng();
3,358✔
128
        let mut my_nonce = [0u8; 32];
3,358✔
129

130
        rng.fill_bytes(&mut my_nonce);
3,358✔
131

132
        let addr = addrbytes;
3,358✔
133
        let port = port;
3,358✔
134
        let services = (ServiceFlags::RELAY as u16)
3,358✔
135
            | (ServiceFlags::RPC as u16)
3,358✔
136
            | (ServiceFlags::STACKERDB as u16);
3,358✔
137

138
        info!(
3,358✔
139
            "Will be authenticating p2p messages with the following";
140
            "public key" => &Secp256k1PublicKey::from_private(&pkey).to_hex(),
3,358✔
141
            "services" => &to_hex(&services.to_be_bytes()),
3,358✔
142
            "Stacker DBs" => stacker_dbs.iter().map(|cid| cid.to_string()).collect::<Vec<String>>().join(",")
3,358✔
143
        );
144

145
        LocalPeer {
3,358✔
146
            network_id,
3,358✔
147
            parent_network_id,
3,358✔
148
            nonce: my_nonce,
3,358✔
149
            private_key: pkey,
3,358✔
150
            private_key_expire: key_expire,
3,358✔
151
            addrbytes: addr,
3,358✔
152
            port,
3,358✔
153
            services,
3,358✔
154
            data_url,
3,358✔
155
            public_ip_address: None,
3,358✔
156
            stacker_dbs,
3,358✔
157
        }
3,358✔
158
    }
3,358✔
159

160
    pub fn to_neighbor_addr(&self) -> NeighborAddress {
20,151,315✔
161
        NeighborAddress {
20,151,315✔
162
            addrbytes: self.addrbytes.clone(),
20,151,315✔
163
            port: self.port,
20,151,315✔
164
            public_key_hash: Hash160::from_node_public_key(&StacksPublicKey::from_private(
20,151,315✔
165
                &self.private_key,
20,151,315✔
166
            )),
20,151,315✔
167
        }
20,151,315✔
168
    }
20,151,315✔
169

170
    /// Best-effort attempt to calculate a publicly-routable neighbor address for local peer
171
    pub fn to_public_neighbor_addr(&self) -> NeighborAddress {
1✔
172
        if let Some((peer_addr, peer_port)) = self.public_ip_address.as_ref() {
1✔
173
            NeighborAddress {
×
174
                addrbytes: peer_addr.clone(),
×
175
                port: *peer_port,
×
176
                public_key_hash: Hash160::from_node_public_key(&StacksPublicKey::from_private(
×
177
                    &self.private_key,
×
178
                )),
×
179
            }
×
180
        } else {
181
            self.to_neighbor_addr()
1✔
182
        }
183
    }
1✔
184
}
185

186
impl FromRow<LocalPeer> for LocalPeer {
187
    fn from_row(row: &Row) -> Result<LocalPeer, db_error> {
16,074,748✔
188
        let network_id: u32 = row.get_unwrap("network_id");
16,074,748✔
189
        let parent_network_id: u32 = row.get_unwrap("parent_network_id");
16,074,748✔
190
        let nonce_hex: String = row.get_unwrap("nonce");
16,074,748✔
191
        let privkey = Secp256k1PrivateKey::from_column(row, "private_key")?;
16,074,748✔
192
        let privkey_expire = u64::from_column(row, "private_key_expire")?;
16,074,748✔
193
        let addrbytes: PeerAddress = PeerAddress::from_column(row, "addrbytes")?;
16,074,748✔
194
        let port: u16 = row.get_unwrap("port");
16,074,748✔
195
        let services: u16 = row.get_unwrap("services");
16,074,748✔
196
        let data_url_str: String = row.get_unwrap("data_url");
16,074,748✔
197
        let stackerdbs_json: Option<String> = row.get_unwrap("stacker_dbs");
16,074,748✔
198

199
        let nonce_bytes = hex_bytes(&nonce_hex).map_err(|_e| {
16,074,748✔
200
            error!("Unparseable local peer nonce {nonce_hex}");
×
201
            db_error::ParseError
×
202
        })?;
×
203

204
        let nonce_bytes_len = nonce_bytes.len();
16,074,748✔
205
        let Ok(nonce) = nonce_bytes.try_into() else {
16,074,748✔
206
            error!("Peer nonce has {nonce_bytes_len} bytes: {nonce_hex}");
×
207
            return Err(db_error::ParseError);
×
208
        };
209

210
        let data_url = UrlString::try_from(data_url_str).map_err(|_e| db_error::ParseError)?;
16,074,748✔
211
        let stacker_dbs: Vec<QualifiedContractIdentifier> =
16,074,748✔
212
            if let Some(stackerdbs_json) = stackerdbs_json {
16,074,748✔
213
                serde_json::from_str(&stackerdbs_json).map_err(|_| db_error::ParseError)?
16,074,748✔
214
            } else {
215
                vec![]
×
216
            };
217

218
        Ok(LocalPeer {
16,074,748✔
219
            network_id,
16,074,748✔
220
            parent_network_id,
16,074,748✔
221
            private_key: privkey,
16,074,748✔
222
            nonce,
16,074,748✔
223
            private_key_expire: privkey_expire,
16,074,748✔
224
            addrbytes,
16,074,748✔
225
            port,
16,074,748✔
226
            services,
16,074,748✔
227
            data_url,
16,074,748✔
228
            public_ip_address: None,
16,074,748✔
229
            stacker_dbs,
16,074,748✔
230
        })
16,074,748✔
231
    }
16,074,748✔
232
}
233

234
impl FromRow<ASEntry4> for ASEntry4 {
235
    fn from_row(row: &Row) -> Result<ASEntry4, db_error> {
12✔
236
        let prefix: u32 = row.get_unwrap("prefix");
12✔
237
        let mask: u8 = row.get_unwrap("mask");
12✔
238
        let asn: u32 = row.get_unwrap("asn");
12✔
239
        let org: u32 = row.get_unwrap("org");
12✔
240

241
        Ok(ASEntry4 {
12✔
242
            prefix,
12✔
243
            mask,
12✔
244
            asn,
12✔
245
            org,
12✔
246
        })
12✔
247
    }
12✔
248
}
249

250
impl FromRow<Neighbor> for Neighbor {
251
    fn from_row(row: &Row) -> Result<Neighbor, db_error> {
19,737,274✔
252
        let peer_version: u32 = row.get_unwrap("peer_version");
19,737,274✔
253
        let network_id: u32 = row.get_unwrap("network_id");
19,737,274✔
254
        let addrbytes: PeerAddress = PeerAddress::from_column(row, "addrbytes")?;
19,737,274✔
255
        let port: u16 = row.get_unwrap("port");
19,737,274✔
256
        let mut public_key: Secp256k1PublicKey =
19,737,274✔
257
            Secp256k1PublicKey::from_column(row, "public_key")?;
19,737,274✔
258
        let expire_block_height = u64::from_column(row, "expire_block_height")?;
19,737,274✔
259
        let last_contact_time = u64::from_column(row, "last_contact_time")?;
19,737,274✔
260
        let asn: u32 = row.get_unwrap("asn");
19,737,274✔
261
        let org: u32 = row.get_unwrap("org");
19,737,274✔
262
        let allowed: i64 = row.get_unwrap("allowed");
19,737,274✔
263
        let denied: i64 = row.get_unwrap("denied");
19,737,274✔
264
        let in_degree: u32 = row.get_unwrap("in_degree");
19,737,274✔
265
        let out_degree: u32 = row.get_unwrap("out_degree");
19,737,274✔
266

267
        public_key.set_compressed(true);
19,737,274✔
268

269
        Ok(Neighbor {
19,737,274✔
270
            addr: NeighborKey {
19,737,274✔
271
                peer_version,
19,737,274✔
272
                network_id,
19,737,274✔
273
                addrbytes,
19,737,274✔
274
                port,
19,737,274✔
275
            },
19,737,274✔
276
            public_key,
19,737,274✔
277
            expire_block: expire_block_height,
19,737,274✔
278
            last_contact_time,
19,737,274✔
279
            asn,
19,737,274✔
280
            org,
19,737,274✔
281
            allowed,
19,737,274✔
282
            denied,
19,737,274✔
283
            in_degree,
19,737,274✔
284
            out_degree,
19,737,274✔
285
        })
19,737,274✔
286
    }
19,737,274✔
287
}
288

289
// In what is likely an abuse of Sqlite, the peer database is structured such that the `frontier`
290
// table stores peers keyed by a deterministically-chosen random "slot," instead of their IP/port.
291
// (i.e. the slot is determined by a cryptographic the hash of the IP/port).  The reason for this
292
// is to facilitate randomized peer eviction when the frontier table gets too big -- if a peer's
293
// possible slots are taken, then the _existing_ peer is pinged to see if it is still online.  If
294
// it is still online, the new peer will _not_ be inserted.  If it is offline, then it will be.
295
// This is done to ensure that the frontier represents live, long-lived peers to the greatest
296
// extent possible.
297

298
const PEERDB_INITIAL_SCHEMA: &[&str] = &[
299
    r#"
300
    CREATE TABLE frontier(
301
        peer_version INTEGER NOT NULL,
302
        network_id INTEGER NOT NULL,
303
        addrbytes TEXT NOT NULL,
304
        port INTEGER NOT NULL,
305
        public_key TEXT NOT NULL,
306
        expire_block_height INTEGER NOT NULL,
307
        last_contact_time INTEGER NOT NULL,
308
        asn INTEGER NOT NULL,
309
        org INTEGER NOT NULL,
310
        allowed INTEGER NOT NULL,
311
        denied INTEGER NOT NULL,
312
        initial INTEGER NOT NULL,   -- 1 if this was one of the initial neighbors, 0 otherwise
313
        in_degree INTEGER NOT NULL,
314
        out_degree INTEGER NOT NULL,
315

316
        -- used to deterministically insert and evict
317
        slot INTEGER UNIQUE NOT NULL,
318

319
        PRIMARY KEY(slot)
320
    );"#,
321
    r#"
322
    CREATE TABLE asn4(
323
        prefix INTEGER NOT NULL,
324
        mask INTEGER NOT NULL,
325

326
        asn INTEGER NOT NULL,
327
        org INTEGER,
328

329
        PRIMARY KEY(prefix,mask)
330
    );"#,
331
    "CREATE TABLE db_config(version TEXT NOT NULL);",
332
    r#"
333
    CREATE TABLE local_peer(
334
        network_id INT NOT NULL,
335
        parent_network_id INT NOT NULL,
336
        nonce TEXT NOT NULL,
337
        private_key TEXT NOT NULL,
338
        private_key_expire INTEGER NOT NULL,
339
        addrbytes TEXT NOT NULL,
340
        port INTEGER NOT NULL,
341
        services INTEGER NOT NULL,
342
        data_url TEXT NOT NULL
343
    );"#,
344
    r#"
345
    CREATE TABLE allowed_prefixes(
346
        prefix TEXT NOT NULL,
347
        mask INTEGER NOT NULL
348
    );"#,
349
    r#"
350
    CREATE TABLE denied_prefixes(
351
        prefix TEXT NOT NULL,
352
        mask INTEGER NOT NULL
353
    );"#,
354
];
355

356
const PEERDB_INDEXES: &[&str] =
357
    &["CREATE INDEX IF NOT EXISTS peer_address_index ON frontier(network_id,addrbytes,port);"];
358

359
const PEERDB_SCHEMA_2: &[&str] = &[
360
    r#"PRAGMA foreign_keys = ON;"#,
361
    r#"
362
    CREATE TABLE stackerdb_peers(
363
        smart_contract_id TEXT NOT NULL,
364
        peer_slot INTEGER NOT NULL,
365
        PRIMARY KEY(smart_contract_id,peer_slot),
366
        FOREIGN KEY(peer_slot) REFERENCES frontier(slot) ON DELETE CASCADE
367
    );
368
    "#,
369
    r#"
370
    CREATE INDEX IF NOT EXISTS index_stackedb_peers_by_contract ON stackerdb_peers(smart_contract_id);
371
    "#,
372
    r#"
373
    CREATE INDEX IF NOT EXISTS index_stackedb_peers_by_slot ON stackerdb_peers(peer_slot);
374
    "#,
375
    r#"
376
    ALTER TABLE local_peer ADD COLUMN stacker_dbs TEXT;
377
    "#,
378
    r#"
379
    UPDATE db_config SET version = 2;
380
    "#,
381
];
382

383
const PEERDB_SCHEMA_3: &[&str] = &[
384
    r#"
385
    ALTER TABLE frontier ADD COLUMN public BOOL NOT NULL DEFAULT 0;
386
    "#,
387
    "UPDATE db_config SET version = 3;",
388
];
389

390
#[derive(Debug)]
391
pub struct PeerDB {
392
    pub conn: Connection,
393
    pub readwrite: bool,
394
}
395

396
impl PeerDB {
397
    fn instantiate(
3,341✔
398
        &mut self,
3,341✔
399
        network_id: u32,
3,341✔
400
        parent_network_id: u32,
3,341✔
401
        privkey_opt: Option<Secp256k1PrivateKey>,
3,341✔
402
        key_expires: u64,
3,341✔
403
        data_url: UrlString,
3,341✔
404
        p2p_addr: PeerAddress,
3,341✔
405
        p2p_port: u16,
3,341✔
406
        asn4_entries: &[ASEntry4],
3,341✔
407
        initial_neighbors: &[Neighbor],
3,341✔
408
        stacker_dbs: &[QualifiedContractIdentifier],
3,341✔
409
    ) -> Result<(), db_error> {
3,341✔
410
        let localpeer = LocalPeer::new(
3,341✔
411
            network_id,
3,341✔
412
            parent_network_id,
3,341✔
413
            p2p_addr,
3,341✔
414
            p2p_port,
3,341✔
415
            privkey_opt,
3,341✔
416
            key_expires,
3,341✔
417
            data_url,
3,341✔
418
            vec![],
3,341✔
419
        );
420

421
        let tx = self.tx_begin()?;
3,341✔
422

423
        for row_text in PEERDB_INITIAL_SCHEMA {
20,046✔
424
            tx.execute_batch(row_text).map_err(db_error::SqliteError)?;
20,046✔
425
        }
426

427
        tx.execute(
3,341✔
428
            "INSERT INTO db_config (version) VALUES (?1)",
3,341✔
429
            &[&"1".to_string()],
3,341✔
430
        )
3,341✔
431
        .map_err(db_error::SqliteError)?;
3,341✔
432

433
        PeerDB::apply_schema_migrations(&tx)?;
3,341✔
434

435
        let local_peer_args = params![
3,341✔
436
            network_id,
437
            parent_network_id,
438
            to_hex(&localpeer.nonce),
3,341✔
439
            to_hex(&localpeer.private_key.to_bytes()),
3,341✔
440
            u64_to_sql(key_expires)?,
3,341✔
441
            to_bin(localpeer.addrbytes.as_bytes()),
3,341✔
442
            localpeer.port,
443
            localpeer.services,
444
            localpeer.data_url.as_str(),
3,341✔
445
            serde_json::to_string(stacker_dbs)
3,341✔
446
                .expect("FATAL: failed to serialize stacker db contract addresses"),
3,341✔
447
        ];
448

449
        tx.execute("INSERT INTO local_peer (network_id, parent_network_id, nonce, private_key, private_key_expire, addrbytes, port, services, data_url, stacker_dbs) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10)", local_peer_args)
3,341✔
450
            .map_err(db_error::SqliteError)?;
3,341✔
451

452
        for neighbor in initial_neighbors.iter() {
3,464✔
453
            // since this is a neighbor the node operator is declaring exists, we treat it as
454
            // freshly-contacted.
455
            let mut neighbor = neighbor.clone();
954✔
456
            neighbor.last_contact_time = get_epoch_time_secs();
954✔
457

458
            // do we have this neighbor already?
459
            test_debug!("Add initial neighbor {:?}", &neighbor);
954✔
460
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &[])?;
954✔
461
            if !res {
954✔
462
                warn!("Failed to insert neighbor {:?}", &neighbor);
×
463
            }
954✔
464
        }
465

466
        for asn4 in asn4_entries {
3,341✔
467
            PeerDB::asn4_insert(&tx, asn4)?;
6✔
468
        }
469

470
        for neighbor in initial_neighbors {
3,464✔
471
            PeerDB::set_initial_peer(
954✔
472
                &tx,
954✔
473
                neighbor.addr.network_id,
954✔
474
                &neighbor.addr.addrbytes,
954✔
475
                neighbor.addr.port,
954✔
476
            )?;
×
477
        }
478

479
        tx.commit().map_err(db_error::SqliteError)?;
3,341✔
480

481
        self.add_indexes()?;
3,341✔
482
        Ok(())
3,341✔
483
    }
3,341✔
484

485
    fn add_indexes(&mut self) -> Result<(), db_error> {
3,341✔
486
        debug!("Add indexes to peer DB");
3,341✔
487
        let tx = self.tx_begin()?;
3,341✔
488
        for row_text in PEERDB_INDEXES {
3,341✔
489
            tx.execute_batch(row_text).map_err(db_error::SqliteError)?;
3,341✔
490
        }
491
        tx.commit()?;
3,341✔
492
        Ok(())
3,341✔
493
    }
3,341✔
494

495
    fn get_schema_version(conn: &Connection) -> Result<String, db_error> {
10,067✔
496
        let version = conn
10,067✔
497
            .query_row("SELECT MAX(version) from db_config", NO_PARAMS, |row| {
10,067✔
498
                row.get(0)
10,067✔
499
            })
10,067✔
500
            .optional()?
10,067✔
501
            .unwrap_or("1".to_string());
10,067✔
502
        Ok(version)
10,067✔
503
    }
10,067✔
504

505
    /// Tag each address in the peer DB as public if its address is not private.
506
    /// Happens as part of the schema 3 migration
507
    fn update_peerdb_public_addrs(tx: &Transaction) -> Result<(), db_error> {
3,343✔
508
        let all_peers = Self::get_all_peers(tx)?;
3,343✔
509
        for peer in all_peers {
3,343✔
510
            let public = !peer.addr.addrbytes.is_in_private_range();
16✔
511
            debug!("Marking peer {:?} as public? {}", &peer, public);
16✔
512
            Self::update_peer(tx, &peer)?;
16✔
513
        }
514
        Ok(())
3,343✔
515
    }
3,343✔
516

517
    #[cfg_attr(test, mutants::skip)]
518
    fn apply_schema_2(tx: &Transaction) -> Result<(), db_error> {
3,341✔
519
        test_debug!("Apply schema 2 to peer DB");
3,341✔
520
        for row_text in PEERDB_SCHEMA_2 {
20,046✔
521
            tx.execute_batch(row_text).map_err(db_error::SqliteError)?;
20,046✔
522
        }
523
        Ok(())
3,341✔
524
    }
3,341✔
525

526
    #[cfg_attr(test, mutants::skip)]
527
    fn apply_schema_3(tx: &Transaction) -> Result<(), db_error> {
3,341✔
528
        test_debug!("Apply schema 3 to peer DB");
3,341✔
529
        for row_text in PEERDB_SCHEMA_3 {
6,682✔
530
            tx.execute_batch(row_text).map_err(db_error::SqliteError)?;
6,682✔
531
        }
532
        Self::update_peerdb_public_addrs(tx)?;
3,341✔
533
        Ok(())
3,341✔
534
    }
3,341✔
535

536
    fn apply_schema_migrations(tx: &Transaction) -> Result<String, db_error> {
3,356✔
537
        test_debug!("Apply any schema migrations");
3,356✔
538
        let expected_version = PEERDB_VERSION.to_string();
3,356✔
539
        let mut ret = None;
3,356✔
540
        loop {
541
            match PeerDB::get_schema_version(tx) {
10,038✔
542
                Ok(version) => {
10,038✔
543
                    if ret.is_none() {
10,038✔
544
                        ret = Some(version.clone());
3,356✔
545
                    }
6,682✔
546
                    if version == "1" {
10,038✔
547
                        PeerDB::apply_schema_2(tx)?;
3,341✔
548
                    } else if version == "2" {
6,697✔
549
                        PeerDB::apply_schema_3(tx)?;
3,341✔
550
                    } else if version == expected_version {
3,356✔
551
                        return Ok(ret.expect("unreachable"));
3,356✔
552
                    } else {
553
                        panic!("The schema version of the peer DB is invalid.")
×
554
                    }
555
                }
556
                Err(e) => panic!("Error obtaining the version of the peer DB: {:?}", e),
×
557
            }
558
        }
559
    }
3,356✔
560

561
    pub fn update_local_peer(
707✔
562
        &mut self,
707✔
563
        network_id: u32,
707✔
564
        parent_network_id: u32,
707✔
565
        data_url: UrlString,
707✔
566
        p2p_port: u16,
707✔
567
        stacker_dbs: &[QualifiedContractIdentifier],
707✔
568
    ) -> Result<(), db_error> {
707✔
569
        let local_peer_args = params![
707✔
570
            p2p_port,
571
            data_url.as_str(),
707✔
572
            serde_json::to_string(stacker_dbs)
707✔
573
                .expect("FATAL: unable to serialize Vec<QualifiedContractIdentifier>"),
707✔
574
            network_id,
575
            parent_network_id,
576
        ];
577

578
        match self.conn.execute("UPDATE local_peer SET port = ?1, data_url = ?2, stacker_dbs = ?3 WHERE network_id = ?4 AND parent_network_id = ?5",
707✔
579
                                local_peer_args) {
707✔
580
            Ok(_) => Ok(()),
707✔
581
            Err(e) => Err(db_error::SqliteError(e))
×
582
        }
583
    }
707✔
584

585
    fn reset_denies(tx: &Transaction) -> Result<(), db_error> {
30✔
586
        tx.execute("UPDATE frontier SET denied = 0", NO_PARAMS)
30✔
587
            .map_err(db_error::SqliteError)?;
30✔
588
        Ok(())
30✔
589
    }
30✔
590

591
    fn reset_allows(tx: &Transaction) -> Result<(), db_error> {
30✔
592
        tx.execute("UPDATE frontier SET allowed = 0", NO_PARAMS)
30✔
593
            .map_err(db_error::SqliteError)?;
30✔
594
        Ok(())
30✔
595
    }
30✔
596

597
    fn refresh_denies(tx: &Transaction) -> Result<(), db_error> {
30✔
598
        PeerDB::reset_denies(tx)?;
30✔
599
        let deny_cidrs = PeerDB::get_denied_cidrs(tx)?;
30✔
600
        for (prefix, mask) in deny_cidrs.into_iter() {
30✔
601
            debug!("Refresh deny {}/{}", &prefix, mask);
1✔
602
            PeerDB::apply_cidr_filter(tx, &prefix, mask, "denied", i64::MAX)?;
1✔
603
        }
604
        Ok(())
30✔
605
    }
30✔
606

607
    fn refresh_allows(tx: &Transaction) -> Result<(), db_error> {
30✔
608
        PeerDB::reset_allows(tx)?;
30✔
609
        let allow_cidrs = PeerDB::get_allowed_cidrs(tx)?;
30✔
610
        for (prefix, mask) in allow_cidrs.into_iter() {
30✔
611
            debug!("Refresh allow {}/{}", &prefix, mask);
1✔
612
            PeerDB::apply_cidr_filter(tx, &prefix, mask, "allowed", i64::MAX)?;
1✔
613
        }
614
        Ok(())
30✔
615
    }
30✔
616

617
    /// Open the burn database at the given path.  Open read-only or read/write.
618
    /// If opened for read/write and it doesn't exist, instantiate it.
619
    pub fn connect(
3,355✔
620
        path: &String,
3,355✔
621
        readwrite: bool,
3,355✔
622
        network_id: u32,
3,355✔
623
        parent_network_id: u32,
3,355✔
624
        privkey_opt: Option<Secp256k1PrivateKey>,
3,355✔
625
        key_expires: u64,
3,355✔
626
        p2p_addr: PeerAddress,
3,355✔
627
        p2p_port: u16,
3,355✔
628
        data_url: UrlString,
3,355✔
629
        asn4_recs: &[ASEntry4],
3,355✔
630
        initial_neighbors: Option<&[Neighbor]>,
3,355✔
631
        stacker_dbs: &[QualifiedContractIdentifier],
3,355✔
632
    ) -> Result<PeerDB, db_error> {
3,355✔
633
        let mut create_flag = false;
3,355✔
634
        let open_flags = if fs::metadata(path).is_err() {
3,355✔
635
            // need to create
636
            if readwrite {
3,326✔
637
                create_flag = true;
3,326✔
638
                OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE
3,326✔
639
            } else {
640
                return Err(db_error::NoDBError);
×
641
            }
642
        } else {
643
            // can just open
644
            // NOTE: we may need to apply some migrations, so always open read-write at this point.
645
            OpenFlags::SQLITE_OPEN_READ_WRITE
29✔
646
        };
647

648
        let conn = sqlite_open(path, open_flags, false)?;
3,355✔
649

650
        let mut db = PeerDB { conn, readwrite };
3,355✔
651

652
        if create_flag {
3,355✔
653
            // instantiate!
654
            match initial_neighbors {
3,326✔
655
                Some(neighbors) => {
3,320✔
656
                    db.instantiate(
3,320✔
657
                        network_id,
3,320✔
658
                        parent_network_id,
3,320✔
659
                        privkey_opt,
3,320✔
660
                        key_expires,
3,320✔
661
                        data_url,
3,320✔
662
                        p2p_addr,
3,320✔
663
                        p2p_port,
3,320✔
664
                        asn4_recs,
3,320✔
665
                        neighbors,
3,320✔
666
                        stacker_dbs,
3,320✔
667
                    )?;
×
668
                }
669
                None => {
670
                    db.instantiate(
6✔
671
                        network_id,
6✔
672
                        parent_network_id,
6✔
673
                        privkey_opt,
6✔
674
                        key_expires,
6✔
675
                        data_url,
6✔
676
                        p2p_addr,
6✔
677
                        p2p_port,
6✔
678
                        asn4_recs,
6✔
679
                        &[],
6✔
680
                        stacker_dbs,
6✔
681
                    )?;
×
682
                }
683
            }
684
        } else {
685
            let peerdb_version = PeerDB::get_schema_version(&db.conn)?;
29✔
686
            if peerdb_version != PEERDB_VERSION {
29✔
687
                let tx = db.tx_begin()?;
×
688
                PeerDB::apply_schema_migrations(&tx)?;
×
689
                tx.commit()?;
×
690
            }
29✔
691

692
            db.update_local_peer(
29✔
693
                network_id,
29✔
694
                parent_network_id,
29✔
695
                data_url,
29✔
696
                p2p_port,
29✔
697
                stacker_dbs,
29✔
698
            )?;
×
699

700
            let tx = db.tx_begin()?;
29✔
701
            PeerDB::refresh_allows(&tx)?;
29✔
702
            PeerDB::refresh_denies(&tx)?;
29✔
703
            PeerDB::clear_initial_peers(&tx)?;
29✔
704
            if let Some(privkey) = privkey_opt {
29✔
705
                PeerDB::set_local_private_key(&tx, &privkey, key_expires)?;
28✔
706
            }
1✔
707

708
            if let Some(neighbors) = initial_neighbors {
29✔
709
                for neighbor in neighbors {
27✔
710
                    PeerDB::set_initial_peer(
18✔
711
                        &tx,
18✔
712
                        neighbor.addr.network_id,
18✔
713
                        &neighbor.addr.addrbytes,
18✔
714
                        neighbor.addr.port,
18✔
715
                    )?;
×
716
                }
717
            }
2✔
718

719
            tx.commit()?;
29✔
720
        }
721
        debug!("Opened PeerDB {} readwrite={}", &path, readwrite);
3,355✔
722

723
        // *now* instantiate the DB with the appropriate sql flags
724
        let open_flags = if readwrite {
3,355✔
725
            OpenFlags::SQLITE_OPEN_READ_WRITE
3,355✔
726
        } else {
727
            OpenFlags::SQLITE_OPEN_READ_ONLY
×
728
        };
729

730
        let conn = sqlite_open(path, open_flags, true)?;
3,355✔
731

732
        let db = PeerDB { conn, readwrite };
3,355✔
733
        Ok(db)
3,355✔
734
    }
3,355✔
735

736
    /// Open an existing peer DB
737
    pub fn open(path: &str, readwrite: bool) -> Result<PeerDB, db_error> {
39✔
738
        if fs::metadata(path).is_err() {
39✔
739
            return Err(db_error::NoDBError);
×
740
        }
39✔
741

742
        let open_flags = if readwrite {
39✔
743
            OpenFlags::SQLITE_OPEN_READ_WRITE
39✔
744
        } else {
745
            OpenFlags::SQLITE_OPEN_READ_ONLY
×
746
        };
747
        let conn = sqlite_open(path, open_flags, true)?;
39✔
748

749
        let db = PeerDB { conn, readwrite };
39✔
750

751
        Ok(db)
39✔
752
    }
39✔
753

754
    /// Open a peer database in memory (used for testing)
755
    #[cfg(any(test, feature = "testing"))]
756
    pub fn connect_memory(
15✔
757
        network_id: u32,
15✔
758
        parent_network_id: u32,
15✔
759
        key_expires: u64,
15✔
760
        data_url: UrlString,
15✔
761
        asn4_entries: &[ASEntry4],
15✔
762
        initial_neighbors: &[Neighbor],
15✔
763
    ) -> Result<PeerDB, db_error> {
15✔
764
        let conn = Connection::open_in_memory().map_err(db_error::SqliteError)?;
15✔
765

766
        let mut db = PeerDB {
15✔
767
            conn,
15✔
768
            readwrite: true,
15✔
769
        };
15✔
770

771
        db.instantiate(
15✔
772
            network_id,
15✔
773
            parent_network_id,
15✔
774
            None,
15✔
775
            key_expires,
15✔
776
            data_url,
15✔
777
            PeerAddress::from_ipv4(127, 0, 0, 1),
15✔
778
            NETWORK_P2P_PORT,
779
            asn4_entries,
15✔
780
            initial_neighbors,
15✔
781
            &[],
15✔
782
        )?;
×
783

784
        let tx = db.tx_begin()?;
15✔
785
        PeerDB::apply_schema_migrations(&tx)?;
15✔
786
        tx.commit()?;
15✔
787
        Ok(db)
15✔
788
    }
15✔
789

790
    pub fn conn(&self) -> &Connection {
74,934,116✔
791
        &self.conn
74,934,116✔
792
    }
74,934,116✔
793

794
    pub fn tx_begin(&mut self) -> Result<Transaction<'_>, db_error> {
9,345,108✔
795
        if !self.readwrite {
9,345,108✔
796
            return Err(db_error::ReadOnly);
×
797
        }
9,345,108✔
798

799
        let tx = tx_begin_immediate(&mut self.conn)?;
9,345,108✔
800
        Ok(tx)
9,345,108✔
801
    }
9,345,108✔
802

803
    /// Read the local peer record
804
    pub fn get_local_peer(conn: &DBConn) -> Result<LocalPeer, db_error> {
16,074,748✔
805
        let qry = "SELECT * FROM local_peer LIMIT 1";
16,074,748✔
806
        let local_peer_opt = query_row_panic(conn, qry, NO_PARAMS, || {
16,074,748✔
807
            "Got multiple LocalPeer rows".into()
×
808
        })?;
×
809

810
        Ok(local_peer_opt.expect("Got 0 LocalPeer rows"))
16,074,748✔
811
    }
16,074,748✔
812

813
    /// Set the local IP address and port
814
    pub fn set_local_ipaddr(
710✔
815
        tx: &Transaction,
710✔
816
        addrbytes: &PeerAddress,
710✔
817
        port: u16,
710✔
818
    ) -> Result<(), db_error> {
710✔
819
        tx.execute(
710✔
820
            "UPDATE local_peer SET addrbytes = ?1, port = ?2",
710✔
821
            params![to_bin(addrbytes.as_bytes()), port], // TODO: double check if delete as_ref here
710✔
822
        )
710✔
823
        .map_err(db_error::SqliteError)?;
710✔
824

825
        Ok(())
710✔
826
    }
710✔
827

828
    /// Set local service availability
829
    pub fn set_local_services(tx: &Transaction, services: u16) -> Result<(), db_error> {
3,257✔
830
        tx.execute("UPDATE local_peer SET services = ?1", params![services])
3,257✔
831
            .map_err(db_error::SqliteError)?;
3,257✔
832

833
        Ok(())
3,257✔
834
    }
3,257✔
835

836
    /// Set local private key and expiry
837
    pub fn set_local_private_key(
707✔
838
        tx: &Transaction,
707✔
839
        privkey: &Secp256k1PrivateKey,
707✔
840
        expire_block: u64,
707✔
841
    ) -> Result<(), db_error> {
707✔
842
        let args = params![to_hex(&privkey.to_bytes()), u64_to_sql(expire_block)?];
707✔
843
        tx.execute(
707✔
844
            "UPDATE local_peer SET private_key = ?1, private_key_expire = ?2",
707✔
845
            args,
707✔
846
        )
707✔
847
        .map_err(db_error::SqliteError)?;
707✔
848

849
        Ok(())
707✔
850
    }
707✔
851

852
    /// Re-key and return the new local peer
853
    pub fn rekey(&mut self, new_expire_block: u64) -> Result<LocalPeer, db_error> {
3✔
854
        if new_expire_block > (1 << 63) - 1 {
3✔
855
            return Err(db_error::Overflow);
×
856
        }
3✔
857

858
        let new_key = Secp256k1PrivateKey::random();
3✔
859
        {
860
            let tx = self.tx_begin()?;
3✔
861

862
            PeerDB::set_local_private_key(&tx, &new_key, new_expire_block)?;
3✔
863
            tx.commit().map_err(db_error::SqliteError)?;
3✔
864
        }
865

866
        PeerDB::get_local_peer(self.conn())
3✔
867
    }
3✔
868

869
    /// Calculate the "slots" in the peer database where this peer can be inserted.
870
    /// Slots are distributed uniformly at random between 0 and 2**24.
871
    /// NUM_SLOTS will be returned.
872
    pub fn peer_slots(
4,199✔
873
        conn: &DBConn,
4,199✔
874
        network_id: u32,
4,199✔
875
        peer_addr: &PeerAddress,
4,199✔
876
        peer_port: u16,
4,199✔
877
    ) -> Result<Vec<u32>, db_error> {
4,199✔
878
        let local_peer = PeerDB::get_local_peer(conn)?;
4,199✔
879
        let mut ret = vec![];
4,199✔
880
        for i in 0..NUM_SLOTS {
33,592✔
881
            // pack peer address, port, and index.
33,592✔
882
            // Randomize with local nonce
33,592✔
883
            let mut bytes = vec![];
33,592✔
884
            bytes.extend_from_slice(&local_peer.nonce);
33,592✔
885
            bytes.push(i as u8);
33,592✔
886
            bytes.extend_from_slice(peer_addr.as_bytes());
33,592✔
887

33,592✔
888
            bytes.push((peer_port & 0xff) as u8);
33,592✔
889
            bytes.push((peer_port >> 8) as u8);
33,592✔
890

33,592✔
891
            bytes.push(((network_id & 0xff000000) >> 24) as u8);
33,592✔
892
            bytes.push(((network_id & 0x00ff0000) >> 16) as u8);
33,592✔
893
            bytes.push(((network_id & 0x0000ff00) >> 8) as u8);
33,592✔
894
            bytes.push((network_id & 0x000000ff) as u8);
33,592✔
895

33,592✔
896
            let h = Sha512Trunc256Sum::from_data(&bytes[..]);
33,592✔
897
            let slot: u32 = (h.as_bytes()[0] as u32)
33,592✔
898
                | ((h.as_bytes()[1] as u32) << 8)
33,592✔
899
                | ((h.as_bytes()[2] as u32) << 16);
33,592✔
900

33,592✔
901
            ret.push(slot);
33,592✔
902
        }
33,592✔
903
        Ok(ret)
4,199✔
904
    }
4,199✔
905

906
    /// Group a list of peers by public key, and return the one with the highest last-contact time
907
    fn query_peers(
63,576,770✔
908
        conn: &Connection,
63,576,770✔
909
        qry: &str,
63,576,770✔
910
        args: &[&dyn ToSql],
63,576,770✔
911
    ) -> Result<Vec<Neighbor>, db_error> {
63,576,770✔
912
        let peers: Vec<Neighbor> = query_rows(conn, qry, args)?;
63,576,770✔
913
        let mut grouped_by_public_key: HashMap<Secp256k1PublicKey, Neighbor> = HashMap::new();
63,576,770✔
914
        for peer in peers.into_iter() {
66,742,375✔
915
            if let Some(cur_peer) = grouped_by_public_key.get_mut(&peer.public_key) {
19,737,271✔
916
                if cur_peer.last_contact_time < peer.last_contact_time {
7✔
917
                    *cur_peer = peer;
×
918
                }
7✔
919
            } else {
19,737,264✔
920
                grouped_by_public_key.insert(peer.public_key.clone(), peer);
19,737,264✔
921
            }
19,737,264✔
922
        }
923
        Ok(grouped_by_public_key.into_values().collect())
63,576,770✔
924
    }
63,576,770✔
925

926
    /// Query a single peer.
927
    /// If multiple rows are returned, then only the first-found row is reported.
928
    fn query_peer(
8,669,478✔
929
        conn: &Connection,
8,669,478✔
930
        qry: &str,
8,669,478✔
931
        args: &[&dyn ToSql],
8,669,478✔
932
    ) -> Result<Option<Neighbor>, db_error> {
8,669,478✔
933
        let mut peers = Self::query_peers(conn, qry, args)?;
8,669,478✔
934
        Ok(peers.pop())
8,669,478✔
935
    }
8,669,478✔
936

937
    /// Get a peer from the DB.
938
    pub fn get_peer(
8,669,474✔
939
        conn: &DBConn,
8,669,474✔
940
        network_id: u32,
8,669,474✔
941
        peer_addr: &PeerAddress,
8,669,474✔
942
        peer_port: u16,
8,669,474✔
943
    ) -> Result<Option<Neighbor>, db_error> {
8,669,474✔
944
        let qry = "SELECT * FROM frontier WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3";
8,669,474✔
945
        let args = params![network_id, peer_addr.to_bin(), peer_port,];
8,669,474✔
946
        Self::query_peer(conn, qry, args)
8,669,474✔
947
    }
8,669,474✔
948

949
    pub fn has_peer(
4,208✔
950
        conn: &DBConn,
4,208✔
951
        network_id: u32,
4,208✔
952
        peer_addr: &PeerAddress,
4,208✔
953
        peer_port: u16,
4,208✔
954
    ) -> Result<bool, db_error> {
4,208✔
955
        let qry = "SELECT 1 FROM frontier WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3";
4,208✔
956
        let args = params![network_id, peer_addr.to_bin(), peer_port];
4,208✔
957
        Ok(query_row::<i64, _>(conn, qry, args)?
4,208✔
958
            .map(|x| x == 1)
4,208✔
959
            .unwrap_or(false))
4,208✔
960
    }
4,208✔
961

962
    /// Get peer by port (used in tests where the IP address doesn't really matter)
963
    #[cfg(any(test, feature = "testing"))]
964
    pub fn get_peer_by_port(
4✔
965
        conn: &DBConn,
4✔
966
        network_id: u32,
4✔
967
        peer_port: u16,
4✔
968
    ) -> Result<Option<Neighbor>, db_error> {
4✔
969
        let qry = "SELECT * FROM frontier WHERE network_id = ?1 AND port = ?2";
4✔
970
        let args = params![network_id, peer_port];
4✔
971
        Self::query_peer(conn, qry, args)
4✔
972
    }
4✔
973

974
    /// Get a peer record at a particular slot
975
    pub fn get_peer_at(
4,219✔
976
        conn: &DBConn,
4,219✔
977
        network_id: u32,
4,219✔
978
        slot: u32,
4,219✔
979
    ) -> Result<Option<Neighbor>, db_error> {
4,219✔
980
        let qry = "SELECT * FROM frontier WHERE network_id = ?1 AND slot = ?2";
4,219✔
981
        let args = params![network_id, slot];
4,219✔
982

983
        // N.B. we don't use Self::query_peer() here because `slot` is the primary key
984
        query_row::<Neighbor, _>(conn, qry, args)
4,219✔
985
    }
4,219✔
986

987
    /// Is there any peer at a particular slot?
988
    pub fn has_peer_at(conn: &DBConn, network_id: u32, slot: u32) -> Result<bool, db_error> {
4,204✔
989
        let qry = "SELECT 1 FROM frontier WHERE network_id = ?1 AND slot = ?2";
4,204✔
990
        let args = params![network_id, slot];
4,204✔
991
        Ok(query_row::<i64, _>(conn, qry, args)?
4,204✔
992
            .map(|x| x == 1)
4,204✔
993
            .unwrap_or(false))
4,204✔
994
    }
4,204✔
995

996
    /// Is a peer denied?
997
    pub fn is_peer_denied(
427,504✔
998
        conn: &DBConn,
427,504✔
999
        network_id: u32,
427,504✔
1000
        peer_addr: &PeerAddress,
427,504✔
1001
        peer_port: u16,
427,504✔
1002
    ) -> Result<bool, db_error> {
427,504✔
1003
        match PeerDB::get_peer(conn, network_id, peer_addr, peer_port)? {
427,504✔
1004
            Some(neighbor) => {
394,707✔
1005
                if neighbor.is_denied() {
394,707✔
1006
                    return Ok(true);
20✔
1007
                }
394,687✔
1008
                if PeerDB::is_address_denied(conn, &neighbor.addr.addrbytes)? {
394,687✔
1009
                    return Ok(true);
×
1010
                }
394,687✔
1011
                return Ok(false);
394,687✔
1012
            }
1013
            None => {
1014
                if PeerDB::is_address_denied(conn, peer_addr)? {
32,797✔
1015
                    return Ok(true);
×
1016
                }
32,797✔
1017
                return Ok(false);
32,797✔
1018
            }
1019
        }
1020
    }
427,504✔
1021

1022
    /// Is a peer always allowed?
1023
    pub fn is_peer_always_allowed(
×
1024
        conn: &DBConn,
×
1025
        network_id: u32,
×
1026
        peer_addr: &PeerAddress,
×
1027
        peer_port: u16,
×
1028
    ) -> Result<bool, db_error> {
×
1029
        match PeerDB::get_peer(conn, network_id, peer_addr, peer_port)? {
×
1030
            Some(neighbor) => {
×
1031
                if neighbor.allowed < 0 {
×
1032
                    return Ok(true);
×
1033
                }
×
1034
                return Ok(false);
×
1035
            }
1036
            None => {
1037
                return Ok(false);
×
1038
            }
1039
        }
1040
    }
×
1041

1042
    /// Get all always-allowed peers
1043
    pub fn get_always_allowed_peers(
5,680,045✔
1044
        conn: &DBConn,
5,680,045✔
1045
        network_id: u32,
5,680,045✔
1046
    ) -> Result<Vec<Neighbor>, db_error> {
5,680,045✔
1047
        let local_peer = Self::get_local_peer(conn)?;
5,680,045✔
1048
        let sql = "SELECT * FROM frontier WHERE allowed < 0 AND network_id = ?1 ORDER BY RANDOM()";
5,680,045✔
1049
        let allow_rows: Vec<Neighbor> = Self::query_peers(conn, sql, params![&network_id])?;
5,680,045✔
1050
        Ok(allow_rows
5,680,045✔
1051
            .into_iter()
5,680,045✔
1052
            .filter(|neighbor| {
5,704,708✔
1053
                // omit local peer if it ever gets entered by mistake, since we can't talk to
1054
                // ourselves.
1055
                neighbor.public_key.to_bytes_compressed()
177,863✔
1056
                    != StacksPublicKey::from_private(&local_peer.private_key).to_bytes_compressed()
177,863✔
1057
            })
177,863✔
1058
            .collect())
5,680,045✔
1059
    }
5,680,045✔
1060

1061
    /// Get the bootstrap peers
1062
    pub fn get_bootstrap_peers(conn: &DBConn, network_id: u32) -> Result<Vec<Neighbor>, db_error> {
9,198,871✔
1063
        let sql = "SELECT * FROM frontier WHERE initial = 1 AND network_id = ?1 ORDER BY RANDOM()";
9,198,871✔
1064
        let allow_rows = Self::query_peers(conn, sql, params![&network_id])?;
9,198,871✔
1065
        Ok(allow_rows)
9,198,871✔
1066
    }
9,198,871✔
1067

1068
    /// Insert or replace stacker DB contract IDs for a peer, given its slot
1069
    pub fn insert_or_replace_stacker_dbs(
4,200✔
1070
        tx: &Transaction,
4,200✔
1071
        slot: u32,
4,200✔
1072
        smart_contracts: &[QualifiedContractIdentifier],
4,200✔
1073
    ) -> Result<(), db_error> {
4,200✔
1074
        for cid in smart_contracts {
16,487✔
1075
            test_debug!("Add Stacker DB contract to slot {}: {}", slot, cid);
15,823✔
1076
            let args = params![cid.to_string(), slot];
15,823✔
1077
            tx.execute("INSERT OR REPLACE INTO stackerdb_peers (smart_contract_id,peer_slot) VALUES (?1,?2)", args)
15,823✔
1078
                .map_err(db_error::SqliteError)?;
15,823✔
1079
        }
1080
        Ok(())
4,199✔
1081
    }
4,200✔
1082

1083
    /// Drop all stacker DB contract IDs for a peer, given its slot
1084
    pub fn drop_stacker_dbs(tx: &Transaction, slot: u32) -> Result<(), db_error> {
28✔
1085
        tx.execute("DELETE FROM stackerdb_peers WHERE peer_slot = ?1", &[&slot])
28✔
1086
            .map_err(db_error::SqliteError)?;
28✔
1087
        Ok(())
28✔
1088
    }
28✔
1089

1090
    /// Insert or replace a neighbor into a given slot
1091
    pub fn insert_or_replace_peer(
4,216✔
1092
        tx: &Transaction,
4,216✔
1093
        neighbor: &Neighbor,
4,216✔
1094
        slot: u32,
4,216✔
1095
    ) -> Result<(), db_error> {
4,216✔
1096
        let old_peer_opt = PeerDB::get_peer_at(tx, neighbor.addr.network_id, slot)?;
4,216✔
1097

1098
        let neighbor_args = params![
4,216✔
1099
            neighbor.addr.peer_version,
1100
            neighbor.addr.network_id,
1101
            to_bin(neighbor.addr.addrbytes.as_bytes()),
4,216✔
1102
            neighbor.addr.port,
1103
            to_hex(&neighbor.public_key.to_bytes_compressed()),
4,216✔
1104
            u64_to_sql(neighbor.expire_block)?,
4,216✔
1105
            u64_to_sql(neighbor.last_contact_time)?,
4,216✔
1106
            neighbor.asn,
1107
            neighbor.org,
1108
            neighbor.allowed,
1109
            neighbor.denied,
1110
            neighbor.in_degree,
1111
            neighbor.out_degree,
1112
            0i64,
1113
            slot,
1114
            !neighbor.addr.addrbytes.is_in_private_range()
4,216✔
1115
        ];
1116

1117
        tx.execute("INSERT OR REPLACE INTO frontier (peer_version, network_id, addrbytes, port, public_key, expire_block_height, last_contact_time, asn, org, allowed, denied, in_degree, out_degree, initial, slot, public) \
4,216✔
1118
                   VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)", neighbor_args)
4,216✔
1119
            .map_err(db_error::SqliteError)?;
4,216✔
1120

1121
        if let Some(old_peer) = old_peer_opt {
4,216✔
1122
            if old_peer.addr != neighbor.addr
2✔
1123
                || old_peer.public_key.to_bytes_compressed()
2✔
1124
                    != neighbor.public_key.to_bytes_compressed()
2✔
1125
            {
1126
                // the peer for this slot changed. Drop the associated stacker DB records
1127
                debug!("Peer at slot {} changed; dropping its DBs", slot);
×
1128
                PeerDB::drop_stacker_dbs(tx, slot)?;
×
1129
            }
2✔
1130
        }
4,214✔
1131

1132
        Ok(())
4,216✔
1133
    }
4,216✔
1134

1135
    /// Remove a peer from the peer database, as well as its stacker DB contracts
1136
    pub fn drop_peer(
3✔
1137
        tx: &Transaction,
3✔
1138
        network_id: u32,
3✔
1139
        peer_addr: &PeerAddress,
3✔
1140
        peer_port: u16,
3✔
1141
    ) -> Result<(), db_error> {
3✔
1142
        let slot_opt = Self::find_peer_slot(tx, network_id, peer_addr, peer_port)?;
3✔
1143
        tx.execute(
3✔
1144
            "DELETE FROM frontier WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3",
3✔
1145
            params![network_id, peer_addr.to_bin(), peer_port,],
3✔
1146
        )
3✔
1147
        .map_err(db_error::SqliteError)?;
3✔
1148

1149
        if let Some(slot) = slot_opt {
3✔
1150
            Self::drop_stacker_dbs(tx, slot)?;
3✔
1151
        }
×
1152
        Ok(())
3✔
1153
    }
3✔
1154

1155
    /// Is a peer one of this node's initial neighbors?
1156
    /// Only checks IP address.
1157
    pub fn is_initial_peer(
24,613✔
1158
        conn: &DBConn,
24,613✔
1159
        network_id: u32,
24,613✔
1160
        peer_addr: &PeerAddress,
24,613✔
1161
        peer_port: u16,
24,613✔
1162
    ) -> Result<bool, db_error> {
24,613✔
1163
        let res: Option<i64> = query_row(
24,613✔
1164
            conn,
24,613✔
1165
            "SELECT initial FROM frontier WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3 ORDER BY last_contact_time DESC LIMIT 1",
24,613✔
1166
            params![network_id, peer_addr.to_bin(), peer_port],
24,613✔
1167
        )?;
×
1168

1169
        match res {
24,613✔
1170
            Some(x) => Ok(x != 0),
24,571✔
1171
            None => Ok(false),
42✔
1172
        }
1173
    }
24,613✔
1174

1175
    /// Set a peer as an initial peer.
1176
    /// Does so for all rows with the given IP address.
1177
    pub fn set_initial_peer(
1,012✔
1178
        tx: &Transaction,
1,012✔
1179
        network_id: u32,
1,012✔
1180
        peer_addr: &PeerAddress,
1,012✔
1181
        peer_port: u16,
1,012✔
1182
    ) -> Result<(), db_error> {
1,012✔
1183
        tx.execute("UPDATE frontier SET initial = 1 WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3",
1,012✔
1184
                    params![network_id, peer_addr.to_bin(), peer_port])
1,012✔
1185
            .map_err(db_error::SqliteError)?;
1,012✔
1186

1187
        Ok(())
1,012✔
1188
    }
1,012✔
1189

1190
    /// clear all initial peers
1191
    fn clear_initial_peers(tx: &Transaction) -> Result<(), db_error> {
29✔
1192
        tx.execute("UPDATE frontier SET initial = 0", NO_PARAMS)
29✔
1193
            .map_err(db_error::SqliteError)?;
29✔
1194

1195
        Ok(())
29✔
1196
    }
29✔
1197

1198
    /// Set/unset allow flag for a peer
1199
    /// Pass -1 for "always"
1200
    pub fn set_allow_peer(
926✔
1201
        tx: &Transaction,
926✔
1202
        network_id: u32,
926✔
1203
        peer_addr: &PeerAddress,
926✔
1204
        peer_port: u16,
926✔
1205
        allow_deadline: i64,
926✔
1206
    ) -> Result<(), db_error> {
926✔
1207
        let num_updated = tx.execute("UPDATE frontier SET allowed = ?1 WHERE network_id = ?2 AND addrbytes = ?3 AND port = ?4",
926✔
1208
                   params![allow_deadline, network_id, peer_addr.to_bin(), peer_port])
926✔
1209
            .map_err(db_error::SqliteError)?;
926✔
1210

1211
        if num_updated == 0 {
926✔
1212
            // we're preemptively allowing
1213
            let nk = NeighborKey {
1✔
1214
                peer_version: 0,
1✔
1215
                network_id,
1✔
1216
                addrbytes: peer_addr.clone(),
1✔
1217
                port: peer_port,
1✔
1218
            };
1✔
1219
            let empty_key = StacksPublicKey::from_private(&StacksPrivateKey::random());
1✔
1220
            let mut empty_neighbor = Neighbor::empty(&nk, &empty_key, 0);
1✔
1221

1222
            empty_neighbor.allowed = allow_deadline;
1✔
1223

1224
            debug!("Preemptively allow peer {:?}", &nk);
1✔
1225
            if !PeerDB::try_insert_peer(tx, &empty_neighbor, &[])? {
1✔
1226
                let mut slots = PeerDB::peer_slots(tx, network_id, peer_addr, peer_port)?;
×
1227
                let slot = slots.pop().expect("BUG: no slots");
×
1228
                warn!(
×
1229
                    "Forcing replacement of peer at slot {} for allowed peer {:?}",
1230
                    slot, &empty_neighbor.addr
×
1231
                );
1232
                PeerDB::insert_or_replace_peer(tx, &empty_neighbor, slot)?;
×
1233
            }
1✔
1234
        }
925✔
1235

1236
        Ok(())
926✔
1237
    }
926✔
1238

1239
    /// Set/unset deny flag for a peer
1240
    /// negative values aren't allowed
1241
    pub fn set_deny_peer(
1✔
1242
        tx: &Transaction,
1✔
1243
        network_id: u32,
1✔
1244
        peer_addr: &PeerAddress,
1✔
1245
        peer_port: u16,
1✔
1246
        deny_deadline: u64,
1✔
1247
    ) -> Result<(), db_error> {
1✔
1248
        let args = params![
1✔
1249
            u64_to_sql(deny_deadline)?,
1✔
1250
            network_id,
1251
            peer_addr.to_bin(),
1✔
1252
            peer_port,
1253
        ];
1254
        let num_updated = tx.execute("UPDATE frontier SET denied = ?1 WHERE network_id = ?2 AND addrbytes = ?3 AND port = ?4", args)
1✔
1255
            .map_err(db_error::SqliteError)?;
1✔
1256

1257
        if num_updated == 0 {
1✔
1258
            // we're preemptively denying
1259
            let nk = NeighborKey {
1✔
1260
                peer_version: 0,
1✔
1261
                network_id,
1✔
1262
                addrbytes: peer_addr.clone(),
1✔
1263
                port: peer_port,
1✔
1264
            };
1✔
1265
            let empty_key = StacksPublicKey::from_private(&StacksPrivateKey::random());
1✔
1266
            let mut empty_neighbor = Neighbor::empty(&nk, &empty_key, 0);
1✔
1267

1268
            empty_neighbor.denied = deny_deadline as i64;
1✔
1269

1270
            debug!("Preemptively deny peer {:?}", &nk);
1✔
1271
            if !PeerDB::try_insert_peer(tx, &empty_neighbor, &[])? {
1✔
1272
                let mut slots = PeerDB::peer_slots(tx, network_id, peer_addr, peer_port)?;
×
1273
                let slot = slots.pop().expect("BUG: no slots");
×
1274
                warn!(
×
1275
                    "Forcing replacement of peer at slot {} for denied peer {:?}",
1276
                    slot, &empty_neighbor.addr
×
1277
                );
1278
                PeerDB::insert_or_replace_peer(tx, &empty_neighbor, slot)?;
×
1279
            }
1✔
1280
        }
×
1281

1282
        Ok(())
1✔
1283
    }
1✔
1284

1285
    /// Update an existing peer's entries.  Does nothing if the peer is not present.
1286
    pub fn update_peer(tx: &Transaction, neighbor: &Neighbor) -> Result<(), db_error> {
602,877✔
1287
        let old_peer_opt = PeerDB::get_peer(
602,877✔
1288
            tx,
602,877✔
1289
            neighbor.addr.network_id,
602,877✔
1290
            &neighbor.addr.addrbytes,
602,877✔
1291
            neighbor.addr.port,
602,877✔
1292
        )?;
×
1293

1294
        let args = params![
602,877✔
1295
            neighbor.addr.peer_version,
1296
            to_hex(&neighbor.public_key.to_bytes_compressed()),
602,877✔
1297
            u64_to_sql(neighbor.expire_block)?,
602,877✔
1298
            u64_to_sql(neighbor.last_contact_time)?,
602,877✔
1299
            neighbor.asn,
1300
            neighbor.org,
1301
            neighbor.allowed,
1302
            neighbor.denied,
1303
            neighbor.in_degree,
1304
            neighbor.out_degree,
1305
            !neighbor.addr.addrbytes.is_in_private_range(),
602,877✔
1306
            neighbor.addr.network_id,
1307
            to_bin(neighbor.addr.addrbytes.as_bytes()),
602,877✔
1308
            neighbor.addr.port,
1309
        ];
1310

1311
        tx.execute("UPDATE frontier SET peer_version = ?1, public_key = ?2, expire_block_height = ?3, last_contact_time = ?4, asn = ?5, org = ?6, allowed = ?7, denied = ?8, in_degree = ?9, out_degree = ?10, public = ?11 \
602,877✔
1312
                    WHERE network_id = ?12 AND addrbytes = ?13 AND port = ?14", args)
602,877✔
1313
            .map_err(db_error::SqliteError)?;
602,877✔
1314

1315
        if let Some(old_peer) = old_peer_opt {
602,877✔
1316
            let slot_opt = Self::find_peer_slot(
602,876✔
1317
                tx,
602,876✔
1318
                neighbor.addr.network_id,
602,876✔
1319
                &neighbor.addr.addrbytes,
602,876✔
1320
                neighbor.addr.port,
602,876✔
1321
            )?;
×
1322
            if old_peer.public_key.to_bytes_compressed()
602,876✔
1323
                != neighbor.public_key.to_bytes_compressed()
602,876✔
1324
            {
1325
                // this peer has re-keyed, so it might be a new peer altogether.
1326
                // require it to re-announce its DBs
1327
                if let Some(slot) = slot_opt {
25✔
1328
                    debug!("Peer at slot {} changed; dropping its DBs", slot);
25✔
1329
                    PeerDB::drop_stacker_dbs(tx, slot)?;
25✔
1330
                }
×
1331
            }
602,851✔
1332
        }
1✔
1333
        Ok(())
602,877✔
1334
    }
602,877✔
1335

1336
    /// Find a peer's slot in the DB.
1337
    /// Return Some(slot id) if the peer is inserted
1338
    /// Return None if not.
1339
    fn find_peer_slot(
1,244,170✔
1340
        conn: &Connection,
1,244,170✔
1341
        network_id: u32,
1,244,170✔
1342
        addrbytes: &PeerAddress,
1,244,170✔
1343
        port: u16,
1,244,170✔
1344
    ) -> Result<Option<u32>, db_error> {
1,244,170✔
1345
        let qry =
1,244,170✔
1346
            "SELECT slot FROM frontier WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3";
1,244,170✔
1347
        let args = params![network_id, addrbytes.to_bin(), port];
1,244,170✔
1348
        Ok(query_row::<u32, _>(conn, qry, args)?)
1,244,170✔
1349
    }
1,244,170✔
1350

1351
    /// Get the list of stacker DB contract IDs for a given set of slots.
1352
    /// The list will contain distinct contract IDs.
1353
    fn get_stacker_dbs_by_slot(
340,825✔
1354
        conn: &Connection,
340,825✔
1355
        used_slot: u32,
340,825✔
1356
    ) -> Result<Vec<QualifiedContractIdentifier>, db_error> {
340,825✔
1357
        let mut db_set = HashSet::new();
340,825✔
1358
        let qry = "SELECT smart_contract_id FROM stackerdb_peers WHERE peer_slot = ?1";
340,825✔
1359
        let dbs = query_rows(conn, qry, &[&used_slot])?;
340,825✔
1360
        for cid in dbs.into_iter() {
4,008,264✔
1361
            db_set.insert(cid);
3,860,908✔
1362
        }
3,860,908✔
1363

1364
        Ok(db_set.into_iter().collect())
340,825✔
1365
    }
340,825✔
1366

1367
    /// Get the slots for all peers that replicate a particular stacker DB
1368
    fn get_stacker_db_slots(
×
1369
        conn: &Connection,
×
1370
        smart_contract: &QualifiedContractIdentifier,
×
1371
    ) -> Result<Vec<u32>, db_error> {
×
1372
        let qry = "SELECT peer_slot FROM stackerdb_peers WHERE smart_contract_id = ?1";
×
1373
        let args = params![smart_contract.to_string()];
×
1374
        query_rows(conn, qry, args)
×
1375
    }
×
1376

1377
    /// Get a peer's advertized stacker DBs
1378
    pub fn static_get_peer_stacker_dbs(
343,011✔
1379
        conn: &Connection,
343,011✔
1380
        neighbor: &Neighbor,
343,011✔
1381
    ) -> Result<Vec<QualifiedContractIdentifier>, db_error> {
343,011✔
1382
        let used_slot_opt = PeerDB::find_peer_slot(
343,011✔
1383
            conn,
343,011✔
1384
            neighbor.addr.network_id,
343,011✔
1385
            &neighbor.addr.addrbytes,
343,011✔
1386
            neighbor.addr.port,
343,011✔
1387
        )?;
×
1388
        if let Some(used_slot) = used_slot_opt {
343,011✔
1389
            Self::get_stacker_dbs_by_slot(conn, used_slot)
340,821✔
1390
        } else {
1391
            Ok(vec![])
2,190✔
1392
        }
1393
    }
343,011✔
1394

1395
    /// Get a peer's advertized stacker DBs by their IDs.
1396
    pub fn get_peer_stacker_dbs(
44,720✔
1397
        &self,
44,720✔
1398
        neighbor: &Neighbor,
44,720✔
1399
    ) -> Result<Vec<QualifiedContractIdentifier>, db_error> {
44,720✔
1400
        PeerDB::static_get_peer_stacker_dbs(&self.conn, neighbor)
44,720✔
1401
    }
44,720✔
1402

1403
    /// Update an existing peer's stacker DB IDs.
1404
    /// Calculates the delta between what's in the DB now, and what's in `dbs`, and deletes the
1405
    /// records absent from `dbs` and adds records not present in the DB.
1406
    /// Does nothing if the peer is not present.
1407
    pub fn update_peer_stacker_dbs(
298,280✔
1408
        tx: &Transaction,
298,280✔
1409
        neighbor: &Neighbor,
298,280✔
1410
        dbs: &[QualifiedContractIdentifier],
298,280✔
1411
    ) -> Result<(), db_error> {
298,280✔
1412
        let slot = if let Some(slot) = PeerDB::find_peer_slot(
298,280✔
1413
            tx,
298,280✔
1414
            neighbor.addr.network_id,
298,280✔
1415
            &neighbor.addr.addrbytes,
298,280✔
1416
            neighbor.addr.port,
298,280✔
1417
        )? {
×
1418
            slot
298,280✔
1419
        } else {
1420
            return Ok(());
×
1421
        };
1422
        let cur_dbs_set: HashSet<_> = PeerDB::static_get_peer_stacker_dbs(tx, neighbor)?
298,280✔
1423
            .into_iter()
298,280✔
1424
            .collect();
298,280✔
1425
        let new_dbs_set: HashSet<QualifiedContractIdentifier> = dbs.iter().cloned().collect();
298,280✔
1426
        let to_insert: Vec<_> = new_dbs_set.difference(&cur_dbs_set).collect();
298,280✔
1427
        let to_delete: Vec<_> = cur_dbs_set.difference(&new_dbs_set).collect();
298,280✔
1428

1429
        let sql = "DELETE FROM stackerdb_peers WHERE smart_contract_id = ?1 AND peer_slot = ?2";
298,280✔
1430
        for cid in to_delete.into_iter() {
298,280✔
1431
            test_debug!("Delete Stacker DB for {:?}: {}", &neighbor.addr, &cid);
4,139✔
1432
            let args = params![cid.to_string(), slot];
4,139✔
1433
            tx.execute(sql, args).map_err(db_error::SqliteError)?;
4,139✔
1434
        }
1435

1436
        let sql =
298,280✔
1437
            "INSERT OR REPLACE INTO stackerdb_peers (smart_contract_id,peer_slot) VALUES (?1,?2)";
298,280✔
1438
        for cid in to_insert.iter() {
298,280✔
1439
            test_debug!("Add Stacker DB for {:?}: {}", &neighbor.addr, &cid);
16,092✔
1440
            let args = params![cid.to_string(), slot];
16,092✔
1441
            tx.execute(sql, args).map_err(db_error::SqliteError)?;
16,092✔
1442
        }
1443

1444
        Ok(())
298,280✔
1445
    }
298,280✔
1446

1447
    /// Try to insert a peer at one of its slots.
1448
    /// Does not insert the peer if it is already present, but will instead try to update it with
1449
    /// this peer's information.
1450
    /// If at least one slot was empty, or if the peer is already present and can be updated, then insert/update the peer and return true.
1451
    /// If all slots are occupied, return false.
1452
    pub fn try_insert_peer(
4,208✔
1453
        tx: &Transaction,
4,208✔
1454
        neighbor: &Neighbor,
4,208✔
1455
        stacker_dbs: &[QualifiedContractIdentifier],
4,208✔
1456
    ) -> Result<bool, db_error> {
4,208✔
1457
        let present = PeerDB::has_peer(
4,208✔
1458
            tx,
4,208✔
1459
            neighbor.addr.network_id,
4,208✔
1460
            &neighbor.addr.addrbytes,
4,208✔
1461
            neighbor.addr.port,
4,208✔
1462
        )?;
×
1463
        if present {
4,208✔
1464
            // already here
1465
            PeerDB::update_peer(tx, neighbor)?;
11✔
1466
            PeerDB::update_peer_stacker_dbs(tx, neighbor, stacker_dbs)?;
11✔
1467
            return Ok(true);
11✔
1468
        }
4,197✔
1469

1470
        let slots = PeerDB::peer_slots(
4,197✔
1471
            tx,
4,197✔
1472
            neighbor.addr.network_id,
4,197✔
1473
            &neighbor.addr.addrbytes,
4,197✔
1474
            neighbor.addr.port,
4,197✔
1475
        )?;
×
1476
        for slot in slots.iter() {
4,204✔
1477
            let used_slot = PeerDB::has_peer_at(tx, neighbor.addr.network_id, *slot)?;
4,204✔
1478
            if !used_slot {
4,204✔
1479
                // have a spare slot!
1480
                PeerDB::insert_or_replace_peer(tx, neighbor, *slot)?;
4,196✔
1481
                PeerDB::insert_or_replace_stacker_dbs(tx, *slot, stacker_dbs)?;
4,196✔
1482
                return Ok(true);
4,196✔
1483
            }
8✔
1484
        }
1485

1486
        // no slots free
1487
        return Ok(false);
1✔
1488
    }
4,208✔
1489

1490
    /// Add a cidr prefix
1491
    fn add_cidr_prefix(
9✔
1492
        tx: &Transaction,
9✔
1493
        table: &str,
9✔
1494
        prefix: &PeerAddress,
9✔
1495
        mask: u32,
9✔
1496
    ) -> Result<(), db_error> {
9✔
1497
        let args = params![prefix.to_bin(), mask];
9✔
1498
        tx.execute(
9✔
1499
            &format!(
9✔
1500
                "INSERT OR REPLACE INTO {} (prefix, mask) VALUES (?1, ?2)",
9✔
1501
                table
9✔
1502
            ),
9✔
1503
            args,
9✔
1504
        )
9✔
1505
        .map_err(db_error::SqliteError)?;
9✔
1506
        Ok(())
9✔
1507
    }
9✔
1508

1509
    /// Remove a cidr prefix
1510
    fn remove_cidr_prefix(
×
1511
        tx: &Transaction,
×
1512
        table: &str,
×
1513
        prefix: &PeerAddress,
×
1514
        mask: u32,
×
1515
    ) -> Result<(), db_error> {
×
1516
        let args = params![prefix.to_bin(), mask];
×
1517
        tx.execute(
×
1518
            &format!("DELETE FROM {} WHERE prefix = ?1 AND mask = ?2", table),
×
1519
            args,
×
1520
        )
×
1521
        .map_err(db_error::SqliteError)?;
×
1522
        Ok(())
×
1523
    }
×
1524

1525
    /// Get all cidr prefixes from a given table
1526
    fn get_cidr_prefixes(conn: &DBConn, table: &str) -> Result<Vec<(PeerAddress, u32)>, db_error> {
427,552✔
1527
        let sql_query = format!("SELECT prefix, mask FROM {}", table);
427,552✔
1528
        let mut stmt = conn.prepare(&sql_query)?;
427,552✔
1529
        let rows_res_iter = stmt
427,552✔
1530
            .query_and_then(NO_PARAMS, |row| {
427,552✔
1531
                let prefix = PeerAddress::from_column(row, "prefix")?;
16✔
1532
                let mask: u32 = row.get_unwrap("mask");
16✔
1533
                let res: Result<(PeerAddress, u32), db_error> = Ok((prefix, mask));
16✔
1534
                res
16✔
1535
            })
16✔
1536
            .map_err(db_error::SqliteError)?;
427,552✔
1537

1538
        let mut ret = vec![];
427,552✔
1539
        for row_res in rows_res_iter {
427,552✔
1540
            ret.push(row_res?);
16✔
1541
        }
1542

1543
        Ok(ret)
427,552✔
1544
    }
427,552✔
1545

1546
    /// Get all deny CIDR prefixes
1547
    pub fn get_denied_cidrs(conn: &DBConn) -> Result<Vec<(PeerAddress, u32)>, db_error> {
427,521✔
1548
        PeerDB::get_cidr_prefixes(conn, "denied_prefixes")
427,521✔
1549
    }
427,521✔
1550

1551
    /// Get all allow CIDR prefixes
1552
    pub fn get_allowed_cidrs(conn: &DBConn) -> Result<Vec<(PeerAddress, u32)>, db_error> {
31✔
1553
        PeerDB::get_cidr_prefixes(conn, "allowed_prefixes")
31✔
1554
    }
31✔
1555

1556
    /// Check to see if an address is denied by one of the CIDR deny rows
1557
    pub fn is_address_denied(conn: &DBConn, addr: &PeerAddress) -> Result<bool, db_error> {
427,490✔
1558
        let denied_rows = PeerDB::get_denied_cidrs(conn)?;
427,490✔
1559
        let addr_int = u128::from_be_bytes(addr.as_bytes().to_owned());
427,490✔
1560

1561
        for (prefix, mask) in denied_rows.into_iter() {
427,490✔
1562
            let addr_mask = !((1u128 << (128 - mask)) - 1);
10✔
1563
            let mask_int = u128::from_be_bytes(prefix.as_bytes().to_owned()) & addr_mask;
10✔
1564
            if mask_int == (addr_int & addr_mask) {
10✔
1565
                return Ok(true);
3✔
1566
            }
7✔
1567
        }
1568

1569
        Ok(false)
427,487✔
1570
    }
427,490✔
1571

1572
    /// Convert a prefix address and mask to its hex representation
1573
    fn cidr_prefix_to_string(prefix: &PeerAddress, mask: u32) -> String {
7✔
1574
        assert!(mask > 0 && mask <= 128);
7✔
1575
        let s = to_bin(
7✔
1576
            &(u128::from_be_bytes(prefix.as_bytes().to_owned()) & !((1u128 << (128 - mask)) - 1))
7✔
1577
                .to_be_bytes(),
7✔
1578
        );
1579
        s
7✔
1580
    }
7✔
1581

1582
    /// Update the given column to be equal to the given value for all addresses that match the given
1583
    /// CIDR prefix
1584
    fn apply_cidr_filter(
7✔
1585
        tx: &Transaction,
7✔
1586
        prefix: &PeerAddress,
7✔
1587
        mask: u32,
7✔
1588
        column: &str,
7✔
1589
        value: i64,
7✔
1590
    ) -> Result<(), db_error> {
7✔
1591
        assert!(mask > 0 && mask <= 128);
7✔
1592
        let prefix_txt = PeerDB::cidr_prefix_to_string(prefix, mask);
7✔
1593
        let args = params![value, mask, prefix_txt];
7✔
1594
        tx.execute(
7✔
1595
            &format!(
7✔
1596
                "UPDATE frontier SET {} = ?1 WHERE SUBSTR(addrbytes,1,?2) = SUBSTR(?3,1,?2)",
7✔
1597
                column
7✔
1598
            ),
7✔
1599
            args,
7✔
1600
        )
7✔
1601
        .map_err(db_error::SqliteError)?;
7✔
1602
        Ok(())
7✔
1603
    }
7✔
1604

1605
    /// Set a allowed CIDR prefix
1606
    pub fn add_allow_cidr(
1✔
1607
        tx: &Transaction,
1✔
1608
        prefix: &PeerAddress,
1✔
1609
        mask: u32,
1✔
1610
    ) -> Result<(), db_error> {
1✔
1611
        assert!(mask > 0 && mask <= 128);
1✔
1612
        PeerDB::add_cidr_prefix(tx, "allowed_prefixes", prefix, mask)?;
1✔
1613

1614
        debug!("Apply allow {}/{}", &prefix, mask);
1✔
1615
        PeerDB::apply_cidr_filter(tx, prefix, mask, "allowed", -1)?;
1✔
1616
        Ok(())
1✔
1617
    }
1✔
1618

1619
    /// Set a denied CIDR prefix
1620
    pub fn add_deny_cidr(
4✔
1621
        tx: &Transaction,
4✔
1622
        prefix: &PeerAddress,
4✔
1623
        mask: u32,
4✔
1624
    ) -> Result<(), db_error> {
4✔
1625
        assert!(mask > 0 && mask <= 128);
4✔
1626
        PeerDB::add_cidr_prefix(tx, "denied_prefixes", prefix, mask)?;
4✔
1627

1628
        debug!("Apply deny {}/{}", &prefix, mask);
4✔
1629
        PeerDB::apply_cidr_filter(tx, prefix, mask, "denied", i64::MAX)?;
4✔
1630
        Ok(())
4✔
1631
    }
4✔
1632

1633
    /// Get random neighbors, optionally always including allowed neighbors.
1634
    /// Private IPs may be returned, if known.
1635
    pub fn get_random_neighbors(
15✔
1636
        conn: &DBConn,
15✔
1637
        network_id: u32,
15✔
1638
        network_epoch: u8,
15✔
1639
        peer_version: u32,
15✔
1640
        count: u32,
15✔
1641
        block_height: u64,
15✔
1642
        always_include_allowed: bool,
15✔
1643
    ) -> Result<Vec<Neighbor>, db_error> {
15✔
1644
        Self::get_fresh_random_neighbors(
15✔
1645
            conn,
15✔
1646
            network_id,
15✔
1647
            network_epoch,
15✔
1648
            peer_version,
15✔
1649
            0,
1650
            count,
15✔
1651
            block_height,
15✔
1652
            always_include_allowed,
15✔
1653
            false,
1654
        )
1655
    }
15✔
1656

1657
    /// Get random neighbors, optionally always including allowed neighbors
1658
    pub fn get_fresh_random_neighbors(
1,114,439✔
1659
        conn: &DBConn,
1,114,439✔
1660
        network_id: u32,
1,114,439✔
1661
        network_epoch: u8,
1,114,439✔
1662
        peer_version: u32,
1,114,439✔
1663
        min_age: u64,
1,114,439✔
1664
        count: u32,
1,114,439✔
1665
        block_height: u64,
1,114,439✔
1666
        always_include_allowed: bool,
1,114,439✔
1667
        public_only: bool,
1,114,439✔
1668
    ) -> Result<Vec<Neighbor>, db_error> {
1,114,439✔
1669
        let mut ret = vec![];
1,114,439✔
1670

1671
        // UTC time
1672
        let now_secs = util::get_epoch_time_secs();
1,114,439✔
1673
        // Extract the epoch from the peer_version. The epoch is stored in the last byte.
1674
        let node_peer_version = peer_version & 0x000000ff;
1,114,439✔
1675

1676
        if always_include_allowed {
1,114,439✔
1677
            // always include allowed neighbors, freshness be damned
1678
            // the peer_version check mirrors the check in `has_acceptable_epoch`:
1679
            //    (my_epoch <= peer_epoch) OR (curr_epoch <= peer_epoch)
1680
            let allow_qry = r#"
8✔
1681
                SELECT *
8✔
1682
                FROM frontier
8✔
1683
                WHERE network_id = ?1
8✔
1684
                  AND denied < ?2
8✔
1685
                  AND (allowed < 0 OR ?3 < allowed)
8✔
1686
                  AND (?4 <= (peer_version & 0x000000ff) OR ?5 <= (peer_version & 0x000000ff))"#;
8✔
1687

1688
            let allow_args = params![
8✔
1689
                network_id,
1690
                u64_to_sql(now_secs)?,
8✔
1691
                u64_to_sql(now_secs)?,
8✔
1692
                node_peer_version,
1693
                network_epoch,
1694
            ];
1695
            let mut allow_rows = Self::query_peers(conn, allow_qry, allow_args)?;
8✔
1696

1697
            if allow_rows.len() >= (count as usize) {
8✔
1698
                // return a random subset
1699
                allow_rows.shuffle(&mut thread_rng());
3✔
1700
                allow_rows.truncate(count as usize);
3✔
1701
                return Ok(allow_rows);
3✔
1702
            }
5✔
1703

1704
            ret.append(&mut allow_rows);
5✔
1705
        }
1,114,431✔
1706
        if (ret.len() as u32) >= count {
1,114,436✔
1707
            return Ok(ret);
×
1708
        }
1,114,436✔
1709
        // In case we don't have enough allowed peers, fill in also with non-allowed, randomly-chosen, fresh peers
1710

1711
        // only include public peers if requested
1712
        let use_public_condition = if public_only { "AND public = 1" } else { "" };
1,114,436✔
1713

1714
        // If always_include_allowed is true, we've already collected all allowed peers above,
1715
        // so exclude them from this query to avoid duplicates
1716
        let include_allowed_condition = if always_include_allowed {
1,114,436✔
1717
            "AND (allowed >= 0 AND allowed <= ?5)"
5✔
1718
        } else {
1719
            ""
1,114,431✔
1720
        };
1721

1722
        let random_peers_qry = format!(
1,114,436✔
1723
            r#"
1724
            SELECT *
1725
            FROM frontier
1726
            WHERE network_id = ?1
1727
              AND last_contact_time >= ?2
1728
              AND ?3 < expire_block_height
1729
              AND denied < ?4
1730
              {include_allowed_condition}
1731
              AND (?6 <= (peer_version & 0x000000ff) OR ?7 <= (peer_version & 0x000000ff))
1732
              {use_public_condition}
1733
            ORDER BY RANDOM()
1734
            LIMIT ?8"#
1735
        );
1736

1737
        let random_peers_args = params![
1,114,436✔
1738
            network_id,
1739
            u64_to_sql(min_age)?,
1,114,436✔
1740
            u64_to_sql(block_height)?,
1,114,436✔
1741
            u64_to_sql(now_secs)?,
1,114,436✔
1742
            u64_to_sql(now_secs)?,
1,114,436✔
1743
            node_peer_version,
1744
            network_epoch,
1745
            (count - (ret.len() as u32)),
1,114,436✔
1746
        ];
1747
        let mut random_peers = Self::query_peers(conn, &random_peers_qry, random_peers_args)?;
1,114,436✔
1748

1749
        ret.append(&mut random_peers);
1,114,436✔
1750
        Ok(ret)
1,114,436✔
1751
    }
1,114,439✔
1752

1753
    /// Get an randomized initial set of peers.
1754
    /// -- always include all allowed neighbors
1755
    /// -- never include denied neighbors
1756
    /// -- for neighbors that are neither allowed nor denied, sample them randomly as long as they're fresh.
1757
    pub fn get_initial_neighbors(
4✔
1758
        conn: &DBConn,
4✔
1759
        network_id: u32,
4✔
1760
        network_epoch: u8,
4✔
1761
        peer_version: u32,
4✔
1762
        count: u32,
4✔
1763
        block_height: u64,
4✔
1764
    ) -> Result<Vec<Neighbor>, db_error> {
4✔
1765
        PeerDB::get_random_neighbors(
4✔
1766
            conn,
4✔
1767
            network_id,
4✔
1768
            network_epoch,
4✔
1769
            peer_version,
4✔
1770
            count,
4✔
1771
            block_height,
4✔
1772
            true,
1773
        )
1774
    }
4✔
1775

1776
    /// Get a randomized set of peers for walking the peer graph.
1777
    /// -- selects peers at random even if not allowed
1778
    /// -- may include private IPs
1779
    #[cfg_attr(test, mutants::skip)]
1780
    pub fn get_random_walk_neighbors(
811,905✔
1781
        conn: &DBConn,
811,905✔
1782
        network_id: u32,
811,905✔
1783
        network_epoch: u8,
811,905✔
1784
        peer_version: u32,
811,905✔
1785
        min_age: u64,
811,905✔
1786
        count: u32,
811,905✔
1787
        block_height: u64,
811,905✔
1788
    ) -> Result<Vec<Neighbor>, db_error> {
811,905✔
1789
        PeerDB::get_fresh_random_neighbors(
811,905✔
1790
            conn,
811,905✔
1791
            network_id,
811,905✔
1792
            network_epoch,
811,905✔
1793
            peer_version,
811,905✔
1794
            min_age,
811,905✔
1795
            count,
811,905✔
1796
            block_height,
811,905✔
1797
            false,
1798
            false,
1799
        )
1800
    }
811,905✔
1801

1802
    /// Add an IPv4 <--> ASN mapping
1803
    /// Used during db instantiation
1804
    fn asn4_insert(tx: &Transaction, asn4: &ASEntry4) -> Result<(), db_error> {
6✔
1805
        tx.execute(
6✔
1806
            "INSERT OR REPLACE INTO asn4 (prefix, mask, asn, org) VALUES (?1, ?2, ?3, ?4)",
6✔
1807
            params![asn4.prefix, asn4.mask, asn4.asn, asn4.org,],
6✔
1808
        )
6✔
1809
        .map_err(db_error::SqliteError)?;
6✔
1810

1811
        Ok(())
6✔
1812
    }
6✔
1813

1814
    /// Classify an IPv4 address to its AS number.
1815
    /// This method doesn't have to be particularly efficient since it's off the critical path.
1816
    pub fn asn4_lookup(conn: &DBConn, addrbits: &PeerAddress) -> Result<Option<u32>, db_error> {
967,386✔
1817
        // must be an IPv4 address
1818
        if !addrbits.is_ipv4() {
967,386✔
1819
            return Err(db_error::TypeError);
1✔
1820
        }
967,385✔
1821

1822
        // NOTE: sqlite3 will use the machine's endianness here
1823
        let addr_u32 = addrbits.ipv4_bits().unwrap();
967,385✔
1824

1825
        let qry = "SELECT * FROM asn4 WHERE prefix = (?1 & ~((1 << (32 - mask)) - 1)) ORDER BY prefix DESC LIMIT 1";
967,385✔
1826
        let args = params![addr_u32];
967,385✔
1827
        Ok(query_row::<ASEntry4, _>(conn, qry, args)?.map(|entry| entry.asn))
967,385✔
1828
    }
967,386✔
1829

1830
    /// Classify an IP address to its AS number
1831
    #[cfg_attr(test, mutants::skip)]
1832
    pub fn asn_lookup(conn: &DBConn, addrbits: &PeerAddress) -> Result<Option<u32>, db_error> {
967,380✔
1833
        if addrbits.is_ipv4() {
967,380✔
1834
            PeerDB::asn4_lookup(conn, addrbits)
967,380✔
1835
        } else {
1836
            // TODO
1837
            Ok(None)
×
1838
        }
1839
    }
967,380✔
1840

1841
    /// Count the number of nodes in a given AS
1842
    #[cfg_attr(test, mutants::skip)]
1843
    pub fn asn_count(conn: &DBConn, asn: u32) -> Result<u64, db_error> {
595,466✔
1844
        let qry = "SELECT COUNT(*) FROM frontier WHERE asn = ?1";
595,466✔
1845
        let args = params![asn];
595,466✔
1846
        let count = query_count(conn, qry, args)?;
595,466✔
1847
        Ok(count as u64)
595,466✔
1848
    }
595,466✔
1849

1850
    #[cfg_attr(test, mutants::skip)]
1851
    pub fn get_frontier_size(conn: &DBConn) -> Result<u64, db_error> {
17,822✔
1852
        let qry = "SELECT COUNT(*) FROM frontier";
17,822✔
1853
        let count = query_count(conn, qry, NO_PARAMS)?;
17,822✔
1854
        Ok(count as u64)
17,822✔
1855
    }
17,822✔
1856

1857
    pub fn get_all_peers(conn: &DBConn) -> Result<Vec<Neighbor>, db_error> {
443,480✔
1858
        let qry = "SELECT * FROM frontier ORDER BY addrbytes ASC, port ASC";
443,480✔
1859
        let rows = Self::query_peers(conn, qry, NO_PARAMS)?;
443,480✔
1860
        Ok(rows)
443,480✔
1861
    }
443,480✔
1862

1863
    /// Find out which peers replicate a particular stacker DB.
1864
    /// Return a randomized list of up to the given size, where all
1865
    /// peers returned have a last-contact time greater than the given minimum age.
1866
    pub fn find_stacker_db_replicas(
38,470,452✔
1867
        conn: &DBConn,
38,470,452✔
1868
        network_id: u32,
38,470,452✔
1869
        smart_contract: &QualifiedContractIdentifier,
38,470,452✔
1870
        min_age: u64,
38,470,452✔
1871
        max_count: usize,
38,470,452✔
1872
    ) -> Result<Vec<Neighbor>, db_error> {
38,470,452✔
1873
        if max_count == 0 {
38,470,452✔
1874
            return Ok(vec![]);
1✔
1875
        }
38,470,451✔
1876
        let qry = "SELECT frontier.* FROM frontier JOIN stackerdb_peers ON stackerdb_peers.peer_slot = frontier.slot WHERE stackerdb_peers.smart_contract_id = ?1 AND frontier.network_id = ?2 AND frontier.last_contact_time >= ?3 ORDER BY RANDOM() LIMIT ?4";
38,470,451✔
1877
        let max_count_u32 = u32::try_from(max_count).unwrap_or(u32::MAX);
38,470,451✔
1878
        let args = params![
38,470,451✔
1879
            smart_contract.to_string(),
38,470,451✔
1880
            network_id,
1881
            u64_to_sql(min_age)?,
38,470,451✔
1882
            max_count_u32,
1883
        ];
1884
        Self::query_peers(conn, qry, args)
38,470,451✔
1885
    }
38,470,452✔
1886
}
1887

1888
#[cfg(any(test, feature = "testing"))]
1889
mod test {
1890
    #[allow(unused)]
1891
    use clarity::vm::types::StandardPrincipalData;
1892
    #[allow(unused)]
1893
    use clarity::vm::ContractName;
1894
    use stacks_common::types::net::PeerAddress;
1895

1896
    #[allow(unused)]
1897
    use super::*;
1898
    #[allow(unused)]
1899
    use crate::core::{
1900
        NETWORK_ID_MAINNET, PEER_VERSION_EPOCH_2_0, PEER_VERSION_EPOCH_3_0, PEER_VERSION_EPOCH_3_1,
1901
        PEER_VERSION_TESTNET_MAJOR,
1902
    };
1903

1904
    impl PeerDB {
1905
        /// test the `public` flag
1906
        pub fn is_public(
24✔
1907
            conn: &DBConn,
24✔
1908
            network_id: u32,
24✔
1909
            peer_addr: &PeerAddress,
24✔
1910
            peer_port: u16,
24✔
1911
        ) -> Result<bool, db_error> {
24✔
1912
            let qry = "SELECT public FROM frontier WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3";
24✔
1913
            let args = params![network_id, peer_addr.to_bin(), peer_port,];
24✔
1914
            let public: bool = query_row(conn, qry, args)?.ok_or(db_error::NotFoundError)?;
24✔
1915
            Ok(public)
24✔
1916
        }
24✔
1917
    }
1918

1919
    /// Test storage, retrieval, and mutation of LocalPeer, including its stacker DB contract IDs
1920
    #[test]
1921
    fn test_local_peer() {
1✔
1922
        let mut db = PeerDB::connect_memory(
1✔
1923
            0x9abcdef0,
1924
            12345,
1925
            0,
1926
            UrlString::from_literal("http://foo.com"),
1✔
1927
            &[],
1✔
1928
            &[],
1✔
1929
        )
1930
        .unwrap();
1✔
1931
        let local_peer = PeerDB::get_local_peer(db.conn()).unwrap();
1✔
1932

1933
        assert_eq!(local_peer.network_id, 0x9abcdef0);
1✔
1934
        assert_eq!(local_peer.parent_network_id, 12345);
1✔
1935
        assert_eq!(local_peer.private_key_expire, 0);
1✔
1936
        assert_eq!(
1✔
1937
            local_peer.data_url,
1938
            UrlString::try_from("http://foo.com".to_string()).unwrap()
1✔
1939
        );
1940
        assert_eq!(local_peer.port, NETWORK_P2P_PORT);
1✔
1941
        assert_eq!(local_peer.addrbytes, PeerAddress::from_ipv4(127, 0, 0, 1));
1✔
1942
        assert_eq!(
1✔
1943
            local_peer.services,
1944
            (ServiceFlags::RELAY as u16)
1✔
1945
                | (ServiceFlags::RPC as u16)
1✔
1946
                | (ServiceFlags::STACKERDB as u16)
1✔
1947
        );
1948
        assert_eq!(local_peer.stacker_dbs, vec![]);
1✔
1949

1950
        let mut stackerdbs = vec![
1✔
1951
            QualifiedContractIdentifier::new(
1✔
1952
                StandardPrincipalData::new(0x01, [0x02; 20]).unwrap(),
1✔
1953
                ContractName::from_literal("db-1"),
1✔
1954
            ),
1955
            QualifiedContractIdentifier::new(
1✔
1956
                StandardPrincipalData::new(0x02, [0x03; 20]).unwrap(),
1✔
1957
                ContractName::from_literal("db-2"),
1✔
1958
            ),
1959
        ];
1960
        stackerdbs.sort();
1✔
1961

1962
        db.update_local_peer(
1✔
1963
            0x9abcdef0,
1964
            12345,
1965
            UrlString::try_from("http://bar.com".to_string()).unwrap(),
1✔
1966
            4567,
1967
            &stackerdbs,
1✔
1968
        )
1969
        .unwrap();
1✔
1970

1971
        let mut local_peer = PeerDB::get_local_peer(db.conn()).unwrap();
1✔
1972
        local_peer.stacker_dbs.sort();
1✔
1973

1974
        assert_eq!(
1✔
1975
            local_peer.data_url,
1976
            UrlString::try_from("http://bar.com".to_string()).unwrap()
1✔
1977
        );
1978
        assert_eq!(local_peer.port, 4567);
1✔
1979
        assert_eq!(local_peer.stacker_dbs, stackerdbs);
1✔
1980
    }
1✔
1981

1982
    /// Test PeerDB::insert_or_replace_peer() to verify that PeerDB::get_peer() will fetch the
1983
    /// latest peer's state.  Tests mutation of peer rows as well.
1984
    #[test]
1985
    fn test_peer_insert_and_retrieval() {
1✔
1986
        let neighbor = Neighbor {
1✔
1987
            addr: NeighborKey {
1✔
1988
                peer_version: 0x12345678,
1✔
1989
                network_id: 0x9abcdef0,
1✔
1990
                addrbytes: PeerAddress([
1✔
1991
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
1992
                    0x0d, 0x0e, 0x0f,
1✔
1993
                ]),
1✔
1994
                port: 12345,
1✔
1995
            },
1✔
1996
            public_key: Secp256k1PublicKey::from_hex(
1✔
1997
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
1998
            )
1✔
1999
            .unwrap(),
1✔
2000
            expire_block: 23456,
1✔
2001
            last_contact_time: 1552509642,
1✔
2002
            allowed: -1,
1✔
2003
            denied: -1,
1✔
2004
            asn: 34567,
1✔
2005
            org: 45678,
1✔
2006
            in_degree: 1,
1✔
2007
            out_degree: 1,
1✔
2008
        };
1✔
2009

2010
        let mut db = PeerDB::connect_memory(
1✔
2011
            0x9abcdef0,
2012
            12345,
2013
            0,
2014
            UrlString::from_literal("http://foo.com"),
1✔
2015
            &[],
1✔
2016
            &[],
1✔
2017
        )
2018
        .unwrap();
1✔
2019

2020
        let neighbor_before_opt = PeerDB::get_peer(
1✔
2021
            db.conn(),
1✔
2022
            0x9abcdef0,
2023
            &PeerAddress([
1✔
2024
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
1✔
2025
                0x0e, 0x0f,
1✔
2026
            ]),
1✔
2027
            12345,
2028
        )
2029
        .unwrap();
1✔
2030
        assert_eq!(neighbor_before_opt, None);
1✔
2031

2032
        {
1✔
2033
            let tx = db.tx_begin().unwrap();
1✔
2034
            PeerDB::insert_or_replace_peer(&tx, &neighbor, 0).unwrap();
1✔
2035
            tx.commit().unwrap();
1✔
2036
        }
1✔
2037

2038
        let neighbor_opt = PeerDB::get_peer(
1✔
2039
            db.conn(),
1✔
2040
            0x9abcdef0,
2041
            &PeerAddress([
1✔
2042
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
1✔
2043
                0x0e, 0x0f,
1✔
2044
            ]),
1✔
2045
            12345,
2046
        )
2047
        .unwrap();
1✔
2048
        assert_eq!(neighbor_opt, Some(neighbor.clone()));
1✔
2049

2050
        let neighbor_at_opt = PeerDB::get_peer_at(db.conn(), 0x9abcdef0, 0).unwrap();
1✔
2051
        assert_eq!(neighbor_at_opt, Some(neighbor.clone()));
1✔
2052

2053
        let neighbor_not_at_opt = PeerDB::get_peer_at(db.conn(), 0x9abcdef0, 1).unwrap();
1✔
2054
        assert_eq!(neighbor_not_at_opt, None);
1✔
2055

2056
        let neighbor_not_at_opt_network = PeerDB::get_peer_at(db.conn(), 0x9abcdef1, 0).unwrap();
1✔
2057
        assert_eq!(neighbor_not_at_opt_network, None);
1✔
2058

2059
        {
1✔
2060
            let tx = db.tx_begin().unwrap();
1✔
2061
            PeerDB::insert_or_replace_peer(&tx, &neighbor, 0).unwrap();
1✔
2062
            tx.commit().unwrap();
1✔
2063
        }
1✔
2064
    }
1✔
2065

2066
    /// Verify that PeerDB::insert_or_replace_peer() will maintain each peer's stacker DB contract
2067
    /// IDs. New peers' contract IDs get added, and dropped peers' contract IDs get removed.
2068
    #[test]
2069
    fn test_insert_or_replace_stacker_dbs() {
1✔
2070
        let mut db = PeerDB::connect_memory(
1✔
2071
            0x9abcdef0,
2072
            12345,
2073
            0,
2074
            UrlString::from_literal("http://foo.com"),
1✔
2075
            &[],
1✔
2076
            &[],
1✔
2077
        )
2078
        .unwrap();
1✔
2079

2080
        // the neighbors to whom this DB corresponds
2081
        let neighbor_1 = Neighbor {
1✔
2082
            addr: NeighborKey {
1✔
2083
                peer_version: 0x12345678,
1✔
2084
                network_id: 0x9abcdef0,
1✔
2085
                addrbytes: PeerAddress([
1✔
2086
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
2087
                    0x0d, 0x0e, 0x0f,
1✔
2088
                ]),
1✔
2089
                port: 12345,
1✔
2090
            },
1✔
2091
            public_key: Secp256k1PublicKey::from_hex(
1✔
2092
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
2093
            )
1✔
2094
            .unwrap(),
1✔
2095
            expire_block: 23456,
1✔
2096
            last_contact_time: 1552509642,
1✔
2097
            allowed: -1,
1✔
2098
            denied: -1,
1✔
2099
            asn: 34567,
1✔
2100
            org: 45678,
1✔
2101
            in_degree: 1,
1✔
2102
            out_degree: 1,
1✔
2103
        };
1✔
2104

2105
        let neighbor_2 = Neighbor {
1✔
2106
            addr: NeighborKey {
1✔
2107
                peer_version: 0x12345678,
1✔
2108
                network_id: 0x9abcdef0,
1✔
2109
                addrbytes: PeerAddress([
1✔
2110
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
2111
                    0x0d, 0x0e, 0x10,
1✔
2112
                ]),
1✔
2113
                port: 12346,
1✔
2114
            },
1✔
2115
            public_key: Secp256k1PublicKey::from_hex(
1✔
2116
                "02845147b61e1308d0e7fd4e801ca8e93535bd075d3f45bb2452f4415fa616ed10",
1✔
2117
            )
1✔
2118
            .unwrap(),
1✔
2119
            expire_block: 23457,
1✔
2120
            last_contact_time: 1552509643,
1✔
2121
            allowed: -1,
1✔
2122
            denied: -1,
1✔
2123
            asn: 34568,
1✔
2124
            org: 45679,
1✔
2125
            in_degree: 2,
1✔
2126
            out_degree: 2,
1✔
2127
        };
1✔
2128

2129
        let tx = db.tx_begin().unwrap();
1✔
2130
        PeerDB::insert_or_replace_peer(&tx, &neighbor_1, 1).unwrap();
1✔
2131
        PeerDB::insert_or_replace_peer(&tx, &neighbor_2, 2).unwrap();
1✔
2132
        tx.commit().unwrap();
1✔
2133

2134
        // basic storage and retrieval
2135
        let mut stackerdbs = vec![
1✔
2136
            QualifiedContractIdentifier::new(
1✔
2137
                StandardPrincipalData::new(0x01, [0x02; 20]).unwrap(),
1✔
2138
                ContractName::from_literal("db-1"),
1✔
2139
            ),
2140
            QualifiedContractIdentifier::new(
1✔
2141
                StandardPrincipalData::new(0x02, [0x03; 20]).unwrap(),
1✔
2142
                ContractName::from_literal("db-2"),
1✔
2143
            ),
2144
        ];
2145
        stackerdbs.sort();
1✔
2146

2147
        let tx = db.tx_begin().unwrap();
1✔
2148
        PeerDB::insert_or_replace_stacker_dbs(&tx, 1, &stackerdbs).unwrap();
1✔
2149
        tx.commit().unwrap();
1✔
2150

2151
        let mut fetched_stackerdbs = PeerDB::get_stacker_dbs_by_slot(&db.conn, 1).unwrap();
1✔
2152
        fetched_stackerdbs.sort();
1✔
2153
        assert_eq!(stackerdbs, fetched_stackerdbs);
1✔
2154

2155
        // can add the same DBs to a different slot
2156
        let tx = db.tx_begin().unwrap();
1✔
2157
        PeerDB::insert_or_replace_stacker_dbs(&tx, 2, &stackerdbs).unwrap();
1✔
2158
        tx.commit().unwrap();
1✔
2159

2160
        let mut fetched_stackerdbs = PeerDB::get_stacker_dbs_by_slot(&db.conn, 2).unwrap();
1✔
2161
        fetched_stackerdbs.sort();
1✔
2162
        assert_eq!(stackerdbs, fetched_stackerdbs);
1✔
2163

2164
        // adding DBs to the same slot just grows the total list
2165
        let mut new_stackerdbs = vec![
1✔
2166
            QualifiedContractIdentifier::new(
1✔
2167
                StandardPrincipalData::new(0x03, [0x04; 20]).unwrap(),
1✔
2168
                ContractName::from_literal("db-3"),
1✔
2169
            ),
2170
            QualifiedContractIdentifier::new(
1✔
2171
                StandardPrincipalData::new(0x04, [0x05; 20]).unwrap(),
1✔
2172
                ContractName::from_literal("db-5"),
1✔
2173
            ),
2174
        ];
2175
        new_stackerdbs.sort();
1✔
2176

2177
        let mut all_stackerdbs = stackerdbs.clone();
1✔
2178
        all_stackerdbs.extend_from_slice(&new_stackerdbs);
1✔
2179
        all_stackerdbs.sort();
1✔
2180

2181
        let tx = db.tx_begin().unwrap();
1✔
2182
        PeerDB::insert_or_replace_stacker_dbs(&tx, 1, &new_stackerdbs).unwrap();
1✔
2183
        tx.commit().unwrap();
1✔
2184

2185
        let mut fetched_stackerdbs = PeerDB::get_stacker_dbs_by_slot(&db.conn, 1).unwrap();
1✔
2186
        fetched_stackerdbs.sort();
1✔
2187
        assert_eq!(fetched_stackerdbs, all_stackerdbs);
1✔
2188

2189
        // can't add a DB to a non-existant peer
2190
        let tx = db.tx_begin().unwrap();
1✔
2191
        PeerDB::insert_or_replace_stacker_dbs(&tx, 3, &stackerdbs).unwrap_err();
1✔
2192
        tx.commit().unwrap();
1✔
2193

2194
        // deleting a peer deletes the associated DB
2195
        let tx = db.tx_begin().unwrap();
1✔
2196
        PeerDB::drop_peer(
1✔
2197
            &tx,
1✔
2198
            neighbor_1.addr.network_id,
1✔
2199
            &neighbor_1.addr.addrbytes,
1✔
2200
            neighbor_1.addr.port,
1✔
2201
        )
2202
        .unwrap();
1✔
2203
        tx.commit().unwrap();
1✔
2204

2205
        // can't get the DB
2206
        let fetched_stackerdbs = PeerDB::get_stacker_dbs_by_slot(&db.conn, 1).unwrap();
1✔
2207
        assert_eq!(fetched_stackerdbs, vec![]);
1✔
2208
    }
1✔
2209

2210
    /// Test PeerDB::try_insert_peer() with no stacker DB contracts.  Simply verifies storage and
2211
    /// retrieval works.
2212
    #[test]
2213
    fn test_try_insert_peer() {
1✔
2214
        let neighbor = Neighbor {
1✔
2215
            addr: NeighborKey {
1✔
2216
                peer_version: 0x12345678,
1✔
2217
                network_id: 0x9abcdef0,
1✔
2218
                addrbytes: PeerAddress([
1✔
2219
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
2220
                    0x0d, 0x0e, 0x0f,
1✔
2221
                ]),
1✔
2222
                port: 12345,
1✔
2223
            },
1✔
2224
            public_key: Secp256k1PublicKey::from_hex(
1✔
2225
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
2226
            )
1✔
2227
            .unwrap(),
1✔
2228
            expire_block: 23456,
1✔
2229
            last_contact_time: 1552509642,
1✔
2230
            allowed: -1,
1✔
2231
            denied: -1,
1✔
2232
            asn: 34567,
1✔
2233
            org: 45678,
1✔
2234
            in_degree: 1,
1✔
2235
            out_degree: 1,
1✔
2236
        };
1✔
2237

2238
        let mut db = PeerDB::connect_memory(
1✔
2239
            0x9abcdef0,
2240
            12345,
2241
            0,
2242
            UrlString::from_literal("http://foo.com"),
1✔
2243
            &[],
1✔
2244
            &[],
1✔
2245
        )
2246
        .unwrap();
1✔
2247

2248
        {
2249
            let tx = db.tx_begin().unwrap();
1✔
2250
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap();
1✔
2251
            tx.commit().unwrap();
1✔
2252

2253
            assert!(res);
1✔
2254
        }
2255

2256
        let neighbor_opt = PeerDB::get_peer(
1✔
2257
            db.conn(),
1✔
2258
            0x9abcdef0,
2259
            &PeerAddress([
1✔
2260
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
1✔
2261
                0x0e, 0x0f,
1✔
2262
            ]),
1✔
2263
            12345,
2264
        )
2265
        .unwrap();
1✔
2266
        assert_eq!(neighbor_opt, Some(neighbor.clone()));
1✔
2267

2268
        // idempotent
2269
        {
2270
            let tx = db.tx_begin().unwrap();
1✔
2271
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap();
1✔
2272
            tx.commit().unwrap();
1✔
2273

2274
            assert!(res);
1✔
2275
        }
2276

2277
        // put a peer in all the slots
2278
        let mut new_neighbor = neighbor.clone();
1✔
2279
        new_neighbor.addr.port += 1;
1✔
2280
        let slots = PeerDB::peer_slots(
1✔
2281
            db.conn(),
1✔
2282
            neighbor.addr.network_id,
1✔
2283
            &neighbor.addr.addrbytes,
1✔
2284
            neighbor.addr.port,
1✔
2285
        )
2286
        .unwrap();
1✔
2287
        for slot in slots {
8✔
2288
            let tx = db.tx_begin().unwrap();
8✔
2289
            PeerDB::insert_or_replace_peer(&tx, &neighbor, slot).unwrap();
8✔
2290
            tx.commit().unwrap();
8✔
2291
        }
8✔
2292

2293
        // succeeds because it's the same peer
2294
        {
2295
            let tx = db.tx_begin().unwrap();
1✔
2296
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap();
1✔
2297
            tx.commit().unwrap();
1✔
2298

2299
            assert!(res);
1✔
2300
        }
2301

2302
        // put neighbor at new_neighbor's slots
2303
        let slots = PeerDB::peer_slots(
1✔
2304
            db.conn(),
1✔
2305
            new_neighbor.addr.network_id,
1✔
2306
            &new_neighbor.addr.addrbytes,
1✔
2307
            new_neighbor.addr.port,
1✔
2308
        )
2309
        .unwrap();
1✔
2310
        for slot in slots {
8✔
2311
            let tx = db.tx_begin().unwrap();
8✔
2312
            PeerDB::insert_or_replace_peer(&tx, &neighbor, slot).unwrap();
8✔
2313
            tx.commit().unwrap();
8✔
2314
        }
8✔
2315

2316
        // fails because it's a different peer
2317
        {
2318
            let tx = db.tx_begin().unwrap();
1✔
2319
            let res = PeerDB::try_insert_peer(&tx, &new_neighbor, &[]).unwrap();
1✔
2320
            tx.commit().unwrap();
1✔
2321

2322
            assert!(!res);
1✔
2323
        }
2324
    }
1✔
2325

2326
    /// Test PeerDB::try_insert_peer() with different lists of stacker DB contract IDs.
2327
    /// Verify that the peer's contract IDs are updated on each call to try_insert_peer()
2328
    #[test]
2329
    fn test_try_insert_peer_with_stackerdbs() {
1✔
2330
        let neighbor = Neighbor {
1✔
2331
            addr: NeighborKey {
1✔
2332
                peer_version: 0x12345678,
1✔
2333
                network_id: 0x9abcdef0,
1✔
2334
                addrbytes: PeerAddress([
1✔
2335
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
2336
                    0x0d, 0x0e, 0x0f,
1✔
2337
                ]),
1✔
2338
                port: 12345,
1✔
2339
            },
1✔
2340
            public_key: Secp256k1PublicKey::from_hex(
1✔
2341
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
2342
            )
1✔
2343
            .unwrap(),
1✔
2344
            expire_block: 23456,
1✔
2345
            last_contact_time: 1552509642,
1✔
2346
            allowed: -1,
1✔
2347
            denied: -1,
1✔
2348
            asn: 34567,
1✔
2349
            org: 45678,
1✔
2350
            in_degree: 1,
1✔
2351
            out_degree: 1,
1✔
2352
        };
1✔
2353

2354
        let key1 = Secp256k1PrivateKey::random();
1✔
2355

2356
        let path = "/tmp/test-peerdb-try_insert_peer_with_stackerdbs.db".to_string();
1✔
2357
        if fs::metadata(&path).is_ok() {
1✔
2358
            fs::remove_file(&path).unwrap();
×
2359
        }
1✔
2360
        let mut db = PeerDB::connect(
1✔
2361
            &path,
1✔
2362
            true,
2363
            0x9abcdef0,
2364
            12345,
2365
            Some(key1.clone()),
1✔
2366
            i64::MAX as u64,
1✔
2367
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
2368
            12345,
2369
            UrlString::try_from("http://foo.com").unwrap(),
1✔
2370
            &[],
1✔
2371
            None,
1✔
2372
            &[],
1✔
2373
        )
2374
        .unwrap();
1✔
2375

2376
        let mut stackerdbs = vec![
1✔
2377
            QualifiedContractIdentifier::new(
1✔
2378
                StandardPrincipalData::new(0x01, [0x02; 20]).unwrap(),
1✔
2379
                ContractName::from_literal("db-1"),
1✔
2380
            ),
2381
            QualifiedContractIdentifier::new(
1✔
2382
                StandardPrincipalData::new(0x02, [0x03; 20]).unwrap(),
1✔
2383
                ContractName::from_literal("db-2"),
1✔
2384
            ),
2385
        ];
2386
        stackerdbs.sort();
1✔
2387

2388
        {
2389
            let tx = db.tx_begin().unwrap();
1✔
2390
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &stackerdbs).unwrap();
1✔
2391
            tx.commit().unwrap();
1✔
2392

2393
            assert!(res);
1✔
2394
        }
2395

2396
        let neighbor_opt = PeerDB::get_peer(
1✔
2397
            db.conn(),
1✔
2398
            0x9abcdef0,
2399
            &PeerAddress([
1✔
2400
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
1✔
2401
                0x0e, 0x0f,
1✔
2402
            ]),
1✔
2403
            12345,
2404
        )
2405
        .unwrap();
1✔
2406
        assert_eq!(neighbor_opt, Some(neighbor.clone()));
1✔
2407

2408
        let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
1✔
2409
        neighbor_stackerdbs.sort();
1✔
2410
        assert_eq!(neighbor_stackerdbs, stackerdbs);
1✔
2411

2412
        // insert new stacker DBs -- keep one the same, and add a different one
2413
        let mut changed_stackerdbs = vec![
1✔
2414
            QualifiedContractIdentifier::new(
1✔
2415
                StandardPrincipalData::new(0x01, [0x02; 20]).unwrap(),
1✔
2416
                ContractName::from_literal("db-1"),
1✔
2417
            ),
2418
            QualifiedContractIdentifier::new(
1✔
2419
                StandardPrincipalData::new(0x03, [0x04; 20]).unwrap(),
1✔
2420
                ContractName::from_literal("db-3"),
1✔
2421
            ),
2422
        ];
2423
        changed_stackerdbs.sort();
1✔
2424

2425
        {
2426
            let tx = db.tx_begin().unwrap();
1✔
2427
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &changed_stackerdbs).unwrap();
1✔
2428
            tx.commit().unwrap();
1✔
2429

2430
            // peer already present
2431
            assert!(res);
1✔
2432
        }
2433

2434
        let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
1✔
2435
        neighbor_stackerdbs.sort();
1✔
2436
        assert_eq!(neighbor_stackerdbs, changed_stackerdbs);
1✔
2437

2438
        // clear stacker DBs
2439
        {
2440
            let tx = db.tx_begin().unwrap();
1✔
2441
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap();
1✔
2442
            tx.commit().unwrap();
1✔
2443

2444
            // peer already present
2445
            assert!(res);
1✔
2446
        }
2447

2448
        let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
1✔
2449
        neighbor_stackerdbs.sort();
1✔
2450
        assert_eq!(neighbor_stackerdbs, []);
1✔
2451

2452
        // add back stacker DBs
2453
        let mut new_stackerdbs = vec![
1✔
2454
            QualifiedContractIdentifier::new(
1✔
2455
                StandardPrincipalData::new(0x04, [0x05; 20]).unwrap(),
1✔
2456
                ContractName::from_literal("db-4"),
1✔
2457
            ),
2458
            QualifiedContractIdentifier::new(
1✔
2459
                StandardPrincipalData::new(0x05, [0x06; 20]).unwrap(),
1✔
2460
                ContractName::from_literal("db-5"),
1✔
2461
            ),
2462
        ];
2463
        new_stackerdbs.sort();
1✔
2464

2465
        {
2466
            let tx = db.tx_begin().unwrap();
1✔
2467
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &new_stackerdbs).unwrap();
1✔
2468
            tx.commit().unwrap();
1✔
2469

2470
            // peer already present
2471
            assert!(res);
1✔
2472
        }
2473

2474
        let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
1✔
2475
        neighbor_stackerdbs.sort();
1✔
2476
        assert_eq!(neighbor_stackerdbs, new_stackerdbs);
1✔
2477

2478
        // replace all stacker DBs.
2479
        // Do it twice -- it should be idempotent
2480
        for _ in 0..2 {
1✔
2481
            let mut replace_stackerdbs = vec![
2✔
2482
                QualifiedContractIdentifier::new(
2✔
2483
                    StandardPrincipalData::new(0x06, [0x07; 20]).unwrap(),
2✔
2484
                    ContractName::from_literal("db-6"),
2✔
2485
                ),
2486
                QualifiedContractIdentifier::new(
2✔
2487
                    StandardPrincipalData::new(0x07, [0x08; 20]).unwrap(),
2✔
2488
                    ContractName::from_literal("db-7"),
2✔
2489
                ),
2490
            ];
2491
            replace_stackerdbs.sort();
2✔
2492

2493
            {
2494
                let tx = db.tx_begin().unwrap();
2✔
2495
                let res = PeerDB::try_insert_peer(&tx, &neighbor, &replace_stackerdbs).unwrap();
2✔
2496
                tx.commit().unwrap();
2✔
2497

2498
                // peer already present
2499
                assert!(res);
2✔
2500
            }
2501

2502
            let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
2✔
2503
            neighbor_stackerdbs.sort();
2✔
2504
            assert_eq!(neighbor_stackerdbs, replace_stackerdbs);
2✔
2505
        }
2506

2507
        // a peer re-keying will drop its stacker DBs
2508
        let new_neighbor = neighbor.clone();
1✔
2509

2510
        // drop the peer.  the stacker DBs should disappear as well
2511
        {
1✔
2512
            let tx = db.tx_begin().unwrap();
1✔
2513
            PeerDB::drop_peer(
1✔
2514
                &tx,
1✔
2515
                neighbor.addr.network_id,
1✔
2516
                &neighbor.addr.addrbytes,
1✔
2517
                neighbor.addr.port,
1✔
2518
            )
1✔
2519
            .unwrap();
1✔
2520
            tx.commit().unwrap();
1✔
2521
        }
1✔
2522

2523
        let deleted_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
1✔
2524
        assert!(deleted_stackerdbs.is_empty());
1✔
2525
    }
1✔
2526

2527
    /// Test PeerDB::find_stacker_db_replicas().  Verifies that we can find a list of neighbors
2528
    /// that serve a particular stacker DB, given their contract IDs
2529
    #[test]
2530
    fn test_find_stacker_db_replicas() {
1✔
2531
        let neighbor = Neighbor {
1✔
2532
            addr: NeighborKey {
1✔
2533
                peer_version: 0x12345678,
1✔
2534
                network_id: 0x9abcdef0,
1✔
2535
                addrbytes: PeerAddress([
1✔
2536
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
2537
                    0x0d, 0x0e, 0x0f,
1✔
2538
                ]),
1✔
2539
                port: 12345,
1✔
2540
            },
1✔
2541
            public_key: Secp256k1PublicKey::from_hex(
1✔
2542
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
2543
            )
1✔
2544
            .unwrap(),
1✔
2545
            expire_block: 23456,
1✔
2546
            last_contact_time: 1552509642,
1✔
2547
            allowed: -1,
1✔
2548
            denied: -1,
1✔
2549
            asn: 34567,
1✔
2550
            org: 45678,
1✔
2551
            in_degree: 1,
1✔
2552
            out_degree: 1,
1✔
2553
        };
1✔
2554

2555
        let key1 = Secp256k1PrivateKey::random();
1✔
2556

2557
        let path = "/tmp/test-peerdb-find_stacker_db_replicas.db".to_string();
1✔
2558
        if fs::metadata(&path).is_ok() {
1✔
2559
            fs::remove_file(&path).unwrap();
×
2560
        }
1✔
2561
        let mut db = PeerDB::connect(
1✔
2562
            &path,
1✔
2563
            true,
2564
            0x9abcdef0,
2565
            12345,
2566
            Some(key1.clone()),
1✔
2567
            i64::MAX as u64,
1✔
2568
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
2569
            12345,
2570
            UrlString::try_from("http://foo.com").unwrap(),
1✔
2571
            &[],
1✔
2572
            None,
1✔
2573
            &[],
1✔
2574
        )
2575
        .unwrap();
1✔
2576

2577
        let mut stackerdbs = vec![
1✔
2578
            QualifiedContractIdentifier::new(
1✔
2579
                StandardPrincipalData::new(0x01, [0x02; 20]).unwrap(),
1✔
2580
                ContractName::from_literal("db-1"),
1✔
2581
            ),
2582
            QualifiedContractIdentifier::new(
1✔
2583
                StandardPrincipalData::new(0x02, [0x03; 20]).unwrap(),
1✔
2584
                ContractName::from_literal("db-2"),
1✔
2585
            ),
2586
        ];
2587
        stackerdbs.sort();
1✔
2588

2589
        {
2590
            let tx = db.tx_begin().unwrap();
1✔
2591
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &stackerdbs).unwrap();
1✔
2592
            tx.commit().unwrap();
1✔
2593

2594
            assert!(res);
1✔
2595
        }
2596

2597
        let replicas =
1✔
2598
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &stackerdbs[0], 0, 1).unwrap();
1✔
2599
        assert_eq!(replicas.len(), 1);
1✔
2600
        assert_eq!(replicas[0], neighbor);
1✔
2601

2602
        let replicas =
1✔
2603
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &stackerdbs[0], 0, 2).unwrap();
1✔
2604
        assert_eq!(replicas.len(), 1);
1✔
2605
        assert_eq!(replicas[0], neighbor);
1✔
2606

2607
        let replicas =
1✔
2608
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &stackerdbs[0], 0, 0).unwrap();
1✔
2609
        assert!(replicas.is_empty());
1✔
2610

2611
        let replicas =
1✔
2612
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef1, &stackerdbs[0], 0, 1).unwrap();
1✔
2613
        assert!(replicas.is_empty());
1✔
2614

2615
        // insert new stacker DBs -- keep one the same, and add a different one
2616
        let mut changed_stackerdbs = vec![
1✔
2617
            QualifiedContractIdentifier::new(
1✔
2618
                StandardPrincipalData::new(0x01, [0x02; 20]).unwrap(),
1✔
2619
                ContractName::from_literal("db-1"),
1✔
2620
            ),
2621
            QualifiedContractIdentifier::new(
1✔
2622
                StandardPrincipalData::new(0x03, [0x04; 20]).unwrap(),
1✔
2623
                ContractName::from_literal("db-3"),
1✔
2624
            ),
2625
        ];
2626
        changed_stackerdbs.sort();
1✔
2627

2628
        {
2629
            let tx = db.tx_begin().unwrap();
1✔
2630
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &changed_stackerdbs).unwrap();
1✔
2631
            tx.commit().unwrap();
1✔
2632

2633
            // peer already present, and we were able to update
2634
            assert!(res);
1✔
2635
        }
2636

2637
        let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
1✔
2638
        neighbor_stackerdbs.sort();
1✔
2639
        assert_eq!(neighbor_stackerdbs, changed_stackerdbs);
1✔
2640

2641
        let replicas =
1✔
2642
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &changed_stackerdbs[0], 0, 1)
1✔
2643
                .unwrap();
1✔
2644
        assert_eq!(replicas.len(), 1);
1✔
2645
        assert_eq!(replicas[0], neighbor);
1✔
2646

2647
        let replicas =
1✔
2648
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &changed_stackerdbs[1], 0, 1)
1✔
2649
                .unwrap();
1✔
2650
        assert_eq!(replicas.len(), 1);
1✔
2651
        assert_eq!(replicas[0], neighbor);
1✔
2652

2653
        // query stacker DBs filtering by last-contact time
2654
        let replicas = PeerDB::find_stacker_db_replicas(
1✔
2655
            &db.conn,
1✔
2656
            0x9abcdef0,
2657
            &changed_stackerdbs[1],
1✔
2658
            1552509641,
2659
            1,
2660
        )
2661
        .unwrap();
1✔
2662
        assert_eq!(replicas.len(), 1);
1✔
2663
        assert_eq!(replicas[0], neighbor);
1✔
2664

2665
        let replicas = PeerDB::find_stacker_db_replicas(
1✔
2666
            &db.conn,
1✔
2667
            0x9abcdef0,
2668
            &changed_stackerdbs[1],
1✔
2669
            1552509642,
2670
            1,
2671
        )
2672
        .unwrap();
1✔
2673
        assert_eq!(replicas.len(), 1);
1✔
2674
        assert_eq!(replicas[0], neighbor);
1✔
2675

2676
        let replicas = PeerDB::find_stacker_db_replicas(
1✔
2677
            &db.conn,
1✔
2678
            0x9abcdef0,
2679
            &changed_stackerdbs[1],
1✔
2680
            1552509643,
2681
            1,
2682
        )
2683
        .unwrap();
1✔
2684
        assert!(replicas.is_empty());
1✔
2685

2686
        // clear stacker DBs
2687
        {
2688
            let tx = db.tx_begin().unwrap();
1✔
2689
            let res = PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap();
1✔
2690
            tx.commit().unwrap();
1✔
2691

2692
            // peer already present, and we were able to update
2693
            assert!(res);
1✔
2694
        }
2695

2696
        let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
1✔
2697
        neighbor_stackerdbs.sort();
1✔
2698
        assert_eq!(neighbor_stackerdbs, []);
1✔
2699

2700
        let replicas =
1✔
2701
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &changed_stackerdbs[0], 0, 1)
1✔
2702
                .unwrap();
1✔
2703
        assert!(replicas.is_empty());
1✔
2704

2705
        let replicas =
1✔
2706
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &changed_stackerdbs[1], 0, 1)
1✔
2707
                .unwrap();
1✔
2708
        assert!(replicas.is_empty());
1✔
2709

2710
        let mut replace_stackerdbs = vec![
1✔
2711
            QualifiedContractIdentifier::new(
1✔
2712
                StandardPrincipalData::new(0x06, [0x07; 20]).unwrap(),
1✔
2713
                ContractName::from_literal("db-6"),
1✔
2714
            ),
2715
            QualifiedContractIdentifier::new(
1✔
2716
                StandardPrincipalData::new(0x07, [0x08; 20]).unwrap(),
1✔
2717
                ContractName::from_literal("db-7"),
1✔
2718
            ),
2719
        ];
2720
        replace_stackerdbs.sort();
1✔
2721

2722
        // replace all stacker DBs.
2723
        // Do it twice -- it should be idempotent
2724
        for _ in 0..2 {
1✔
2725
            {
2726
                let tx = db.tx_begin().unwrap();
2✔
2727
                let res = PeerDB::try_insert_peer(&tx, &neighbor, &replace_stackerdbs).unwrap();
2✔
2728
                tx.commit().unwrap();
2✔
2729

2730
                // peer already present and we were able to update
2731
                assert!(res);
2✔
2732
            }
2733

2734
            let mut neighbor_stackerdbs = db.get_peer_stacker_dbs(&neighbor).unwrap();
2✔
2735
            neighbor_stackerdbs.sort();
2✔
2736
            assert_eq!(neighbor_stackerdbs, replace_stackerdbs);
2✔
2737

2738
            let replicas =
2✔
2739
                PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdee0, &stackerdbs[0], 0, 1)
2✔
2740
                    .unwrap();
2✔
2741
            assert!(replicas.is_empty());
2✔
2742

2743
            let replicas =
2✔
2744
                PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdee0, &stackerdbs[1], 0, 1)
2✔
2745
                    .unwrap();
2✔
2746
            assert!(replicas.is_empty());
2✔
2747

2748
            let replicas = PeerDB::find_stacker_db_replicas(
2✔
2749
                &db.conn,
2✔
2750
                0x9abcdef0,
2751
                &changed_stackerdbs[0],
2✔
2752
                0,
2753
                1,
2754
            )
2755
            .unwrap();
2✔
2756
            assert!(replicas.is_empty());
2✔
2757

2758
            let replicas = PeerDB::find_stacker_db_replicas(
2✔
2759
                &db.conn,
2✔
2760
                0x9abcdef0,
2761
                &changed_stackerdbs[1],
2✔
2762
                0,
2763
                1,
2764
            )
2765
            .unwrap();
2✔
2766
            assert!(replicas.is_empty());
2✔
2767

2768
            let replicas = PeerDB::find_stacker_db_replicas(
2✔
2769
                &db.conn,
2✔
2770
                0x9abcdef0,
2771
                &replace_stackerdbs[0],
2✔
2772
                0,
2773
                1,
2774
            )
2775
            .unwrap();
2✔
2776
            assert_eq!(replicas.len(), 1);
2✔
2777
            assert_eq!(replicas[0], neighbor);
2✔
2778

2779
            let replicas = PeerDB::find_stacker_db_replicas(
2✔
2780
                &db.conn,
2✔
2781
                0x9abcdef0,
2782
                &replace_stackerdbs[1],
2✔
2783
                0,
2784
                1,
2785
            )
2786
            .unwrap();
2✔
2787
            assert_eq!(replicas.len(), 1);
2✔
2788
            assert_eq!(replicas[0], neighbor);
2✔
2789
        }
2790

2791
        // drop the peer.  the stacker DBs should disappear as well
2792
        {
1✔
2793
            let tx = db.tx_begin().unwrap();
1✔
2794
            PeerDB::drop_peer(
1✔
2795
                &tx,
1✔
2796
                neighbor.addr.network_id,
1✔
2797
                &neighbor.addr.addrbytes,
1✔
2798
                neighbor.addr.port,
1✔
2799
            )
1✔
2800
            .unwrap();
1✔
2801
            tx.commit().unwrap();
1✔
2802
        }
1✔
2803

2804
        let replicas =
1✔
2805
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &stackerdbs[0], 0, 1).unwrap();
1✔
2806
        assert!(replicas.is_empty());
1✔
2807

2808
        let replicas =
1✔
2809
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &stackerdbs[1], 0, 1).unwrap();
1✔
2810
        assert!(replicas.is_empty());
1✔
2811

2812
        let replicas =
1✔
2813
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &changed_stackerdbs[0], 0, 1)
1✔
2814
                .unwrap();
1✔
2815
        assert!(replicas.is_empty());
1✔
2816

2817
        let replicas =
1✔
2818
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &changed_stackerdbs[1], 0, 1)
1✔
2819
                .unwrap();
1✔
2820
        assert!(replicas.is_empty());
1✔
2821

2822
        let replicas =
1✔
2823
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &replace_stackerdbs[0], 0, 1)
1✔
2824
                .unwrap();
1✔
2825
        assert!(replicas.is_empty());
1✔
2826

2827
        let replicas =
1✔
2828
            PeerDB::find_stacker_db_replicas(&db.conn, 0x9abcdef0, &replace_stackerdbs[1], 0, 1)
1✔
2829
                .unwrap();
1✔
2830
        assert!(replicas.is_empty());
1✔
2831
    }
1✔
2832

2833
    /// Tests DB instantiation with initial neighbors. Verifies that initial neighbors are present in the
2834
    /// DB, and can be loaded with PeerDB::get_initial_neighbors()
2835
    #[test]
2836
    fn test_initial_neighbors() {
1✔
2837
        let mut initial_neighbors = vec![];
1✔
2838
        let now_secs = util::get_epoch_time_secs();
1✔
2839
        for i in 0..10 {
10✔
2840
            initial_neighbors.push(Neighbor {
10✔
2841
                addr: NeighborKey {
10✔
2842
                    peer_version: 0x12345678,
10✔
2843
                    network_id: 0x9abcdef0,
10✔
2844
                    addrbytes: PeerAddress([i as u8; 16]),
10✔
2845
                    port: i,
10✔
2846
                },
10✔
2847
                public_key: Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random()),
10✔
2848
                expire_block: (i + 23456) as u64,
10✔
2849
                last_contact_time: (1552509642 + (i as u64)),
10✔
2850
                allowed: (now_secs + 600) as i64,
10✔
2851
                denied: -1,
10✔
2852
                asn: (34567 + i) as u32,
10✔
2853
                org: (45678 + i) as u32,
10✔
2854
                in_degree: 1,
10✔
2855
                out_degree: 1,
10✔
2856
            });
10✔
2857
        }
10✔
2858

2859
        for i in 10..20 {
10✔
2860
            initial_neighbors.push(Neighbor {
10✔
2861
                addr: NeighborKey {
10✔
2862
                    peer_version: 0x12345678,
10✔
2863
                    network_id: 0x9abcdef0,
10✔
2864
                    addrbytes: PeerAddress([i as u8; 16]),
10✔
2865
                    port: i,
10✔
2866
                },
10✔
2867
                public_key: Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random()),
10✔
2868
                expire_block: (i + 23456) as u64,
10✔
2869
                last_contact_time: (1552509642 + (i as u64)),
10✔
2870
                allowed: 0,
10✔
2871
                denied: -1,
10✔
2872
                asn: (34567 + i) as u32,
10✔
2873
                org: (45678 + i) as u32,
10✔
2874
                in_degree: 1,
10✔
2875
                out_degree: 1,
10✔
2876
            });
10✔
2877
        }
10✔
2878

2879
        fn are_present(ne: &[Neighbor], nei: &[Neighbor]) -> bool {
4✔
2880
            for n in ne {
40✔
2881
                let mut found = false;
40✔
2882
                for ni in nei {
331✔
2883
                    if *n == *ni {
331✔
2884
                        found = true;
40✔
2885
                        break;
40✔
2886
                    }
291✔
2887
                }
2888
                if !found {
40✔
2889
                    return false;
×
2890
                }
40✔
2891
            }
2892
            return true;
4✔
2893
        }
4✔
2894

2895
        let db = PeerDB::connect_memory(
1✔
2896
            0x9abcdef0,
2897
            12345,
2898
            0,
2899
            UrlString::from_literal("http://foo.com"),
1✔
2900
            &[],
1✔
2901
            &initial_neighbors,
1✔
2902
        )
2903
        .unwrap();
1✔
2904

2905
        let n5 = PeerDB::get_initial_neighbors(db.conn(), 0x9abcdef0, 0x78, 0x18000078, 5, 23455)
1✔
2906
            .unwrap();
1✔
2907
        assert!(are_present(&n5, &initial_neighbors));
1✔
2908

2909
        let n10 = PeerDB::get_initial_neighbors(db.conn(), 0x9abcdef0, 0x78, 0x18000078, 10, 23455)
1✔
2910
            .unwrap();
1✔
2911
        assert!(are_present(&n10, &initial_neighbors));
1✔
2912

2913
        let n20 = PeerDB::get_initial_neighbors(db.conn(), 0x9abcdef0, 0x78, 0x18000078, 20, 23455)
1✔
2914
            .unwrap();
1✔
2915
        assert!(are_present(&initial_neighbors, &n20));
1✔
2916

2917
        let n15_fresh =
1✔
2918
            PeerDB::get_initial_neighbors(db.conn(), 0x9abcdef0, 0x78, 0x18000078, 15, 23456 + 14)
1✔
2919
                .unwrap();
1✔
2920
        assert!(are_present(&n15_fresh[10..15], &initial_neighbors[10..20]));
1✔
2921
        for n in &n15_fresh[10..15] {
5✔
2922
            assert!(n.expire_block > 23456 + 14);
5✔
2923
            assert!(n.allowed == 0);
5✔
2924
        }
2925

2926
        for neighbor in &initial_neighbors {
20✔
2927
            assert!(PeerDB::is_initial_peer(
20✔
2928
                db.conn(),
20✔
2929
                neighbor.addr.network_id,
20✔
2930
                &neighbor.addr.addrbytes,
20✔
2931
                neighbor.addr.port
20✔
2932
            )
2933
            .unwrap());
20✔
2934
        }
2935
    }
1✔
2936

2937
    /// Tests DB instantiation with initial neighbors, and verifies that initial neighbors can be
2938
    /// queried by epoch -- only peers with the current or newer epoch will be fetched.
2939
    #[test]
2940
    fn test_get_neighbors_in_current_epoch() {
1✔
2941
        let mut initial_neighbors = vec![];
1✔
2942
        let now_secs = util::get_epoch_time_secs();
1✔
2943
        for i in 0..10 {
10✔
2944
            // epoch 2.0 neighbors
10✔
2945
            initial_neighbors.push(Neighbor {
10✔
2946
                addr: NeighborKey {
10✔
2947
                    peer_version: 0x18000000,
10✔
2948
                    network_id: 0x9abcdef0,
10✔
2949
                    addrbytes: PeerAddress([i as u8; 16]),
10✔
2950
                    port: i,
10✔
2951
                },
10✔
2952
                public_key: Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random()),
10✔
2953
                expire_block: (i + 23456) as u64,
10✔
2954
                last_contact_time: (1552509642 + (i as u64)),
10✔
2955
                allowed: -1,
10✔
2956
                denied: -1,
10✔
2957
                asn: (34567 + i) as u32,
10✔
2958
                org: (45678 + i) as u32,
10✔
2959
                in_degree: 1,
10✔
2960
                out_degree: 1,
10✔
2961
            });
10✔
2962
        }
10✔
2963

2964
        for i in 10..20 {
10✔
2965
            // epoch 2.05 neighbors
10✔
2966
            initial_neighbors.push(Neighbor {
10✔
2967
                addr: NeighborKey {
10✔
2968
                    peer_version: 0x18000005,
10✔
2969
                    network_id: 0x9abcdef0,
10✔
2970
                    addrbytes: PeerAddress([i as u8; 16]),
10✔
2971
                    port: i,
10✔
2972
                },
10✔
2973
                public_key: Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random()),
10✔
2974
                expire_block: (i + 23456) as u64,
10✔
2975
                last_contact_time: (1552509642 + (i as u64)),
10✔
2976
                allowed: -1,
10✔
2977
                denied: -1,
10✔
2978
                asn: (34567 + i) as u32,
10✔
2979
                org: (45678 + i) as u32,
10✔
2980
                in_degree: 1,
10✔
2981
                out_degree: 1,
10✔
2982
            });
10✔
2983
        }
10✔
2984

2985
        fn are_present(ne: &[Neighbor], nei: &[Neighbor]) -> bool {
10✔
2986
            for n in ne {
100✔
2987
                let mut found = false;
100✔
2988
                for ni in nei {
1,333✔
2989
                    if *n == *ni {
1,333✔
2990
                        found = true;
100✔
2991
                        break;
100✔
2992
                    }
1,233✔
2993
                }
2994
                if !found {
100✔
2995
                    eprintln!("Not found: {:?}", &n);
×
2996
                    return false;
×
2997
                }
100✔
2998
            }
2999
            return true;
10✔
3000
        }
10✔
3001

3002
        let db = PeerDB::connect_memory(
1✔
3003
            0x9abcdef0,
3004
            12345,
3005
            0,
3006
            UrlString::from_literal("http://foo.com"),
1✔
3007
            &[],
1✔
3008
            &initial_neighbors,
1✔
3009
        )
3010
        .unwrap();
1✔
3011

3012
        // epoch 2.0
3013
        let n5 =
1✔
3014
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x00, 0x18000000, 5, 23455, false)
1✔
3015
                .unwrap();
1✔
3016
        assert_eq!(n5.len(), 5);
1✔
3017
        assert!(are_present(&n5, &initial_neighbors));
1✔
3018

3019
        let n10 =
1✔
3020
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x00, 0x18000000, 10, 23455, false)
1✔
3021
                .unwrap();
1✔
3022
        assert_eq!(n10.len(), 10);
1✔
3023
        assert!(are_present(&n10, &initial_neighbors));
1✔
3024

3025
        let n20 =
1✔
3026
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x00, 0x18000000, 20, 23455, false)
1✔
3027
                .unwrap();
1✔
3028
        assert_eq!(n20.len(), 20);
1✔
3029
        assert!(are_present(&initial_neighbors, &n20));
1✔
3030

3031
        // epoch 2.05
3032
        let n5 =
1✔
3033
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x05, 0x18000005, 5, 23455, false)
1✔
3034
                .unwrap();
1✔
3035
        assert_eq!(n5.len(), 5);
1✔
3036
        assert!(are_present(&n5, &initial_neighbors));
1✔
3037
        for n in n5 {
5✔
3038
            assert_eq!(n.addr.peer_version, 0x18000005);
5✔
3039
        }
3040

3041
        let n10 =
1✔
3042
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x05, 0x18000005, 10, 23455, false)
1✔
3043
                .unwrap();
1✔
3044
        assert_eq!(n10.len(), 10);
1✔
3045
        assert!(are_present(&n10, &initial_neighbors));
1✔
3046
        for n in n10 {
10✔
3047
            assert_eq!(n.addr.peer_version, 0x18000005);
10✔
3048
        }
3049

3050
        let n20 =
1✔
3051
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x05, 0x18000005, 20, 23455, false)
1✔
3052
                .unwrap();
1✔
3053
        assert_eq!(n20.len(), 10); // only 10 such neighbors are recent enough
1✔
3054
        assert!(are_present(&n20, &initial_neighbors));
1✔
3055
        for n in n20 {
10✔
3056
            assert_eq!(n.addr.peer_version, 0x18000005);
10✔
3057
        }
3058

3059
        // peer version is past 2.05 but the current epoch is still 2.05 / always_include_allowed=false
3060
        let n20 =
1✔
3061
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x05, 0x18000006, 20, 23455, false)
1✔
3062
                .unwrap();
1✔
3063
        assert_eq!(n20.len(), 10);
1✔
3064
        assert!(are_present(&n20, &initial_neighbors));
1✔
3065

3066
        // peer version is past 2.05 but the current epoch is still 2.05 / always_include_allowed=true
3067
        let n20 =
1✔
3068
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x05, 0x18000006, 20, 23455, true)
1✔
3069
                .unwrap();
1✔
3070
        assert_eq!(n20.len(), 10);
1✔
3071
        assert!(are_present(&n20, &initial_neighbors));
1✔
3072

3073
        // current epoch is past 2.05, but peer version is 2.05 / always_include_allowed=false
3074
        let n20 =
1✔
3075
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x06, 0x18000005, 20, 23455, false)
1✔
3076
                .unwrap();
1✔
3077
        assert_eq!(n20.len(), 10);
1✔
3078
        assert!(are_present(&n20, &initial_neighbors));
1✔
3079

3080
        // current epoch is past 2.05, but peer version is 2.05 / always_include_allowed=true
3081
        let n20 =
1✔
3082
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x06, 0x18000005, 20, 23455, true)
1✔
3083
                .unwrap();
1✔
3084
        assert_eq!(n20.len(), 10);
1✔
3085
        assert!(are_present(&n20, &initial_neighbors));
1✔
3086

3087
        // post epoch 2.05 -- no such neighbors
3088
        let n20 =
1✔
3089
            PeerDB::get_random_neighbors(db.conn(), 0x9abcdef0, 0x06, 0x18000006, 20, 23455, false)
1✔
3090
                .unwrap();
1✔
3091
        assert_eq!(n20.len(), 0);
1✔
3092
    }
1✔
3093

3094
    /// Verifies that PeerDB::asn4_lookup() correctly classifies IPv4 address into their AS numbers
3095
    #[test]
3096
    fn asn4_insert_lookup() {
1✔
3097
        let asn4_table = vec![
1✔
3098
            ASEntry4 {
1✔
3099
                prefix: 0x01020200,
1✔
3100
                mask: 24,
1✔
3101
                asn: 1,
1✔
3102
                org: 0,
1✔
3103
            },
1✔
3104
            ASEntry4 {
1✔
3105
                prefix: 0x01020200,
1✔
3106
                mask: 23,
1✔
3107
                asn: 2,
1✔
3108
                org: 0,
1✔
3109
            },
1✔
3110
            ASEntry4 {
1✔
3111
                prefix: 0x01020000,
1✔
3112
                mask: 16,
1✔
3113
                asn: 3,
1✔
3114
                org: 0,
1✔
3115
            },
1✔
3116
            ASEntry4 {
1✔
3117
                prefix: 0x02030000,
1✔
3118
                mask: 16,
1✔
3119
                asn: 4,
1✔
3120
                org: 0,
1✔
3121
            },
1✔
3122
        ];
3123

3124
        let db = PeerDB::connect_memory(
1✔
3125
            0x9abcdef0,
3126
            12345,
3127
            0,
3128
            UrlString::from_literal("http://foo.com"),
1✔
3129
            &asn4_table,
1✔
3130
            &[],
1✔
3131
        )
3132
        .unwrap();
1✔
3133

3134
        let asn1_addr = PeerAddress([
1✔
3135
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02,
1✔
3136
            0x02, 0x04,
1✔
3137
        ]);
1✔
3138
        let asn2_addr = PeerAddress([
1✔
3139
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02,
1✔
3140
            0x03, 0x10,
1✔
3141
        ]);
1✔
3142
        let asn3_addr = PeerAddress([
1✔
3143
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x02,
1✔
3144
            0x13, 0x10,
1✔
3145
        ]);
1✔
3146
        let asn4_addr = PeerAddress([
1✔
3147
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x03,
1✔
3148
            0x13, 0x10,
1✔
3149
        ]);
1✔
3150

3151
        // classify addresses
3152
        let asn1_opt = PeerDB::asn4_lookup(db.conn(), &asn1_addr).unwrap();
1✔
3153
        assert_eq!(asn1_opt, Some(1));
1✔
3154

3155
        let asn2_opt = PeerDB::asn4_lookup(db.conn(), &asn2_addr).unwrap();
1✔
3156
        assert_eq!(asn2_opt, Some(2));
1✔
3157

3158
        let asn3_opt = PeerDB::asn4_lookup(db.conn(), &asn3_addr).unwrap();
1✔
3159
        assert_eq!(asn3_opt, Some(3));
1✔
3160

3161
        let asn4_opt = PeerDB::asn4_lookup(db.conn(), &asn4_addr).unwrap();
1✔
3162
        assert_eq!(asn4_opt, Some(4));
1✔
3163

3164
        // invalid -- not an ipv4 address
3165
        let asn4_invalid_addr = PeerAddress([
1✔
3166
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x02, 0x03,
1✔
3167
            0x13, 0x10,
1✔
3168
        ]);
1✔
3169
        let asn_invalid_opt = PeerDB::asn4_lookup(db.conn(), &asn4_invalid_addr);
1✔
3170
        assert!(matches!(asn_invalid_opt, Err(db_error::TypeError)));
1✔
3171

3172
        // not present
3173
        let asn4_missing_addr = PeerAddress([
1✔
3174
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x02, 0x04,
1✔
3175
            0x13, 0x10,
1✔
3176
        ]);
1✔
3177
        let asn_missing_opt = PeerDB::asn4_lookup(db.conn(), &asn4_missing_addr).unwrap();
1✔
3178
        assert!(asn_missing_opt.is_none());
1✔
3179
    }
1✔
3180

3181
    /// Verifies that PeerDB::set_deny_peer() and PeerDB::set_allow_peer() will mark peers'
3182
    /// `denied` and `allowed` columns appropriately.
3183
    #[test]
3184
    fn test_peer_preemptive_deny_allow() {
1✔
3185
        let mut db = PeerDB::connect_memory(
1✔
3186
            0x9abcdef0,
3187
            12345,
3188
            0,
3189
            UrlString::from_literal("http://foo.com"),
1✔
3190
            &[],
1✔
3191
            &[],
1✔
3192
        )
3193
        .unwrap();
1✔
3194
        {
1✔
3195
            let tx = db.tx_begin().unwrap();
1✔
3196
            PeerDB::set_deny_peer(&tx, 0x9abcdef0, &PeerAddress([0x1; 16]), 12345, 10000000)
1✔
3197
                .unwrap();
1✔
3198
            PeerDB::set_allow_peer(&tx, 0x9abcdef0, &PeerAddress([0x2; 16]), 12345, 20000000)
1✔
3199
                .unwrap();
1✔
3200
            tx.commit().unwrap();
1✔
3201
        }
1✔
3202

3203
        let peer_denied = PeerDB::get_peer(db.conn(), 0x9abcdef0, &PeerAddress([0x1; 16]), 12345)
1✔
3204
            .unwrap()
1✔
3205
            .unwrap();
1✔
3206
        let peer_allowed = PeerDB::get_peer(db.conn(), 0x9abcdef0, &PeerAddress([0x2; 16]), 12345)
1✔
3207
            .unwrap()
1✔
3208
            .unwrap();
1✔
3209

3210
        assert_eq!(peer_denied.denied, 10000000);
1✔
3211
        assert_eq!(peer_allowed.allowed, 20000000);
1✔
3212
    }
1✔
3213

3214
    /// Verifies that PeerDB::add_cidr_prefix(), PeerDB::get_denied_cidrs(), and
3215
    /// PeerDB::get_allowed_cidrs() correctly store and load CIDR prefixes
3216
    #[test]
3217
    fn test_peer_cidr_lists() {
1✔
3218
        let mut db = PeerDB::connect_memory(
1✔
3219
            0x9abcdef0,
3220
            12345,
3221
            0,
3222
            UrlString::from_literal("http://foo.com"),
1✔
3223
            &[],
1✔
3224
            &[],
1✔
3225
        )
3226
        .unwrap();
1✔
3227
        {
1✔
3228
            let tx = db.tx_begin().unwrap();
1✔
3229
            PeerDB::add_cidr_prefix(&tx, "denied_prefixes", &PeerAddress([0x1; 16]), 64).unwrap();
1✔
3230
            PeerDB::add_cidr_prefix(&tx, "allowed_prefixes", &PeerAddress([0x2; 16]), 96).unwrap();
1✔
3231
            tx.commit().unwrap();
1✔
3232
        }
1✔
3233

3234
        let deny_cidrs = PeerDB::get_denied_cidrs(db.conn()).unwrap();
1✔
3235
        let allow_cidrs = PeerDB::get_allowed_cidrs(db.conn()).unwrap();
1✔
3236

3237
        assert_eq!(deny_cidrs, vec![(PeerAddress([0x1; 16]), 64)]);
1✔
3238
        assert_eq!(allow_cidrs, vec![(PeerAddress([0x2; 16]), 96)]);
1✔
3239
    }
1✔
3240

3241
    /// Verifies that an IPv4 peer will be treated as denied if its IPv4 CIDR prefix is denied.
3242
    /// Tests PeerDB::is_address_denied()
3243
    #[test]
3244
    fn test_peer_is_denied() {
1✔
3245
        let mut db = PeerDB::connect_memory(
1✔
3246
            0x9abcdef0,
3247
            12345,
3248
            0,
3249
            UrlString::from_literal("http://foo.com"),
1✔
3250
            &[],
1✔
3251
            &[],
1✔
3252
        )
3253
        .unwrap();
1✔
3254
        {
1✔
3255
            let tx = db.tx_begin().unwrap();
1✔
3256
            PeerDB::add_deny_cidr(
1✔
3257
                &tx,
1✔
3258
                &PeerAddress([
1✔
3259
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3260
                    0x00, 0x00, 0x00,
1✔
3261
                ]),
1✔
3262
                64,
1✔
3263
            )
1✔
3264
            .unwrap();
1✔
3265
            PeerDB::add_deny_cidr(
1✔
3266
                &tx,
1✔
3267
                &PeerAddress([
1✔
3268
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x11,
1✔
3269
                    0x22, 0x33, 0x44,
1✔
3270
                ]),
1✔
3271
                128,
1✔
3272
            )
1✔
3273
            .unwrap();
1✔
3274
            tx.commit().unwrap();
1✔
3275
        }
1✔
3276

3277
        assert!(PeerDB::is_address_denied(
1✔
3278
            db.conn(),
1✔
3279
            &PeerAddress([
1✔
3280
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3281
                0x00, 0x01
1✔
3282
            ])
1✔
3283
        )
3284
        .unwrap());
1✔
3285
        assert!(PeerDB::is_address_denied(
1✔
3286
            db.conn(),
1✔
3287
            &PeerAddress([
1✔
3288
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3289
                0x00, 0x01
1✔
3290
            ])
1✔
3291
        )
3292
        .unwrap());
1✔
3293
        assert!(!PeerDB::is_address_denied(
1✔
3294
            db.conn(),
1✔
3295
            &PeerAddress([
1✔
3296
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3297
                0x00, 0x01
1✔
3298
            ])
1✔
3299
        )
1✔
3300
        .unwrap());
1✔
3301
        assert!(!PeerDB::is_address_denied(
1✔
3302
            db.conn(),
1✔
3303
            &PeerAddress([
1✔
3304
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3305
                0x00, 0x01
1✔
3306
            ])
1✔
3307
        )
1✔
3308
        .unwrap());
1✔
3309
        assert!(PeerDB::is_address_denied(
1✔
3310
            db.conn(),
1✔
3311
            &PeerAddress([
1✔
3312
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x11, 0x22,
1✔
3313
                0x33, 0x44
1✔
3314
            ])
1✔
3315
        )
3316
        .unwrap());
1✔
3317
        assert!(!PeerDB::is_address_denied(
1✔
3318
            db.conn(),
1✔
3319
            &PeerAddress([
1✔
3320
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x11, 0x22,
1✔
3321
                0x33, 0x45
1✔
3322
            ])
1✔
3323
        )
1✔
3324
        .unwrap());
1✔
3325
    }
1✔
3326

3327
    /// Verifies that an IPv4 address can be denied and later allowed by a change in denied/allowed CIDR prefixes.
3328
    /// Tests that a peer will go from having a positive denied value to a negative denied value
3329
    /// when its CIDR prefix is explicitly allowed.
3330
    #[test]
3331
    fn test_peer_deny_allow_cidr() {
1✔
3332
        let neighbor_1 = Neighbor {
1✔
3333
            addr: NeighborKey {
1✔
3334
                peer_version: 0x12345678,
1✔
3335
                network_id: 0x9abcdef0,
1✔
3336
                addrbytes: PeerAddress([
1✔
3337
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
3338
                    0x0d, 0x0e, 0x0f,
1✔
3339
                ]),
1✔
3340
                port: 12345,
1✔
3341
            },
1✔
3342
            public_key: Secp256k1PublicKey::from_hex(
1✔
3343
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
3344
            )
1✔
3345
            .unwrap(),
1✔
3346
            expire_block: 23456,
1✔
3347
            last_contact_time: 1552509642,
1✔
3348
            allowed: 12345,
1✔
3349
            denied: 67890,
1✔
3350
            asn: 34567,
1✔
3351
            org: 45678,
1✔
3352
            in_degree: 1,
1✔
3353
            out_degree: 1,
1✔
3354
        };
1✔
3355

3356
        let neighbor_2 = Neighbor {
1✔
3357
            addr: NeighborKey {
1✔
3358
                peer_version: 0x12345678,
1✔
3359
                network_id: 0x9abcdef0,
1✔
3360
                addrbytes: PeerAddress([
1✔
3361
                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1✔
3362
                    0x1d, 0x1e, 0x1f,
1✔
3363
                ]),
1✔
3364
                port: 12345,
1✔
3365
            },
1✔
3366
            public_key: Secp256k1PublicKey::from_hex(
1✔
3367
                "02287c1f1b280b5dde764b146976f6bad3fb485a3df9b1ad2d8ddc5719e7e91ff2",
1✔
3368
            )
1✔
3369
            .unwrap(),
1✔
3370
            expire_block: 23456,
1✔
3371
            last_contact_time: 1552509642,
1✔
3372
            allowed: 12345,
1✔
3373
            denied: 67890,
1✔
3374
            asn: 34567,
1✔
3375
            org: 45678,
1✔
3376
            in_degree: 1,
1✔
3377
            out_degree: 1,
1✔
3378
        };
1✔
3379

3380
        let mut db = PeerDB::connect_memory(
1✔
3381
            0x9abcdef0,
3382
            12345,
3383
            0,
3384
            UrlString::from_literal("http://foo.com"),
1✔
3385
            &[],
1✔
3386
            &[neighbor_1.clone(), neighbor_2.clone()],
1✔
3387
        )
3388
        .unwrap();
1✔
3389

3390
        let n1 = PeerDB::get_peer(
1✔
3391
            db.conn(),
1✔
3392
            neighbor_1.addr.network_id,
1✔
3393
            &neighbor_1.addr.addrbytes,
1✔
3394
            neighbor_1.addr.port,
1✔
3395
        )
3396
        .unwrap()
1✔
3397
        .unwrap();
1✔
3398
        assert_eq!(n1.allowed, 12345);
1✔
3399
        assert_eq!(n1.denied, 67890);
1✔
3400

3401
        {
1✔
3402
            // ban peer 1 by banning a prefix
1✔
3403
            let tx = db.tx_begin().unwrap();
1✔
3404
            PeerDB::add_deny_cidr(
1✔
3405
                &tx,
1✔
3406
                &PeerAddress([
1✔
3407
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3408
                    0x00, 0x00, 0x00,
1✔
3409
                ]),
1✔
3410
                64,
1✔
3411
            )
1✔
3412
            .unwrap();
1✔
3413
            tx.commit().unwrap();
1✔
3414
        }
1✔
3415

3416
        let n1 = PeerDB::get_peer(
1✔
3417
            db.conn(),
1✔
3418
            neighbor_1.addr.network_id,
1✔
3419
            &neighbor_1.addr.addrbytes,
1✔
3420
            neighbor_1.addr.port,
1✔
3421
        )
3422
        .unwrap()
1✔
3423
        .unwrap();
1✔
3424
        let n2 = PeerDB::get_peer(
1✔
3425
            db.conn(),
1✔
3426
            neighbor_2.addr.network_id,
1✔
3427
            &neighbor_2.addr.addrbytes,
1✔
3428
            neighbor_2.addr.port,
1✔
3429
        )
3430
        .unwrap()
1✔
3431
        .unwrap();
1✔
3432
        assert_eq!(n1.allowed, 12345);
1✔
3433
        assert_eq!(n1.denied, i64::MAX);
1✔
3434
        assert_eq!(n2.allowed, 12345);
1✔
3435
        assert_eq!(n2.denied, 67890);
1✔
3436

3437
        {
1✔
3438
            // unban peer 1 by unbanning a (different) prefix
1✔
3439
            let tx = db.tx_begin().unwrap();
1✔
3440
            PeerDB::add_allow_cidr(
1✔
3441
                &tx,
1✔
3442
                &PeerAddress([
1✔
3443
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3444
                    0x00, 0x00, 0x00,
1✔
3445
                ]),
1✔
3446
                48,
1✔
3447
            )
1✔
3448
            .unwrap();
1✔
3449
            tx.commit().unwrap();
1✔
3450
        }
1✔
3451

3452
        let n1 = PeerDB::get_peer(
1✔
3453
            db.conn(),
1✔
3454
            neighbor_1.addr.network_id,
1✔
3455
            &neighbor_1.addr.addrbytes,
1✔
3456
            neighbor_1.addr.port,
1✔
3457
        )
3458
        .unwrap()
1✔
3459
        .unwrap();
1✔
3460
        let n2 = PeerDB::get_peer(
1✔
3461
            db.conn(),
1✔
3462
            neighbor_2.addr.network_id,
1✔
3463
            &neighbor_2.addr.addrbytes,
1✔
3464
            neighbor_2.addr.port,
1✔
3465
        )
3466
        .unwrap()
1✔
3467
        .unwrap();
1✔
3468

3469
        assert_eq!(n1.allowed, -1);
1✔
3470
        assert_eq!(n1.denied, i64::MAX);
1✔
3471
        assert_eq!(n2.allowed, 12345);
1✔
3472
        assert_eq!(n2.denied, 67890);
1✔
3473
    }
1✔
3474

3475
    /// Tests that PeerDB::refresh_allowed() and PeerDB::refresh_denied() re-apply CIDR allow/deny
3476
    /// rules to the DB.  Peers that match an allowed CIDR prefix remain allowed (or, if not
3477
    /// allowed, are marked as allowed), and peers that match a denied CIDR prefix remain denied
3478
    /// (or are marked as denied if the new prefixes require it).
3479
    #[test]
3480
    fn test_peer_refresh_cidr() {
1✔
3481
        let neighbor_1 = Neighbor {
1✔
3482
            addr: NeighborKey {
1✔
3483
                peer_version: 0x12345678,
1✔
3484
                network_id: 0x9abcdef0,
1✔
3485
                addrbytes: PeerAddress([
1✔
3486
                    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
3487
                    0x0d, 0x0e, 0x0f,
1✔
3488
                ]),
1✔
3489
                port: 12345,
1✔
3490
            },
1✔
3491
            public_key: Secp256k1PublicKey::from_hex(
1✔
3492
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
3493
            )
1✔
3494
            .unwrap(),
1✔
3495
            expire_block: 23456,
1✔
3496
            last_contact_time: 1552509642,
1✔
3497
            allowed: 1234,
1✔
3498
            denied: 5678,
1✔
3499
            asn: 34567,
1✔
3500
            org: 45678,
1✔
3501
            in_degree: 1,
1✔
3502
            out_degree: 1,
1✔
3503
        };
1✔
3504

3505
        let neighbor_2 = Neighbor {
1✔
3506
            addr: NeighborKey {
1✔
3507
                peer_version: 0x12345678,
1✔
3508
                network_id: 0x9abcdef0,
1✔
3509
                addrbytes: PeerAddress([
1✔
3510
                    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
1✔
3511
                    0x1d, 0x1e, 0x1f,
1✔
3512
                ]),
1✔
3513
                port: 12345,
1✔
3514
            },
1✔
3515
            public_key: Secp256k1PublicKey::from_hex(
1✔
3516
                "02287c1f1b280b5dde764b146976f6bad3fb485a3df9b1ad2d8ddc5719e7e91ff2",
1✔
3517
            )
1✔
3518
            .unwrap(),
1✔
3519
            expire_block: 23456,
1✔
3520
            last_contact_time: 1552509642,
1✔
3521
            allowed: 1234,
1✔
3522
            denied: 5678,
1✔
3523
            asn: 34567,
1✔
3524
            org: 45678,
1✔
3525
            in_degree: 1,
1✔
3526
            out_degree: 1,
1✔
3527
        };
1✔
3528

3529
        let mut db = PeerDB::connect_memory(
1✔
3530
            0x9abcdef0,
3531
            12345,
3532
            0,
3533
            UrlString::from_literal("http://foo.com"),
1✔
3534
            &[],
1✔
3535
            &[neighbor_1.clone(), neighbor_2.clone()],
1✔
3536
        )
3537
        .unwrap();
1✔
3538
        {
1✔
3539
            let tx = db.tx_begin().unwrap();
1✔
3540
            PeerDB::add_cidr_prefix(&tx, "denied_prefixes", &PeerAddress([0x00; 16]), 8).unwrap();
1✔
3541
            PeerDB::add_cidr_prefix(&tx, "allowed_prefixes", &PeerAddress([0x01; 16]), 8).unwrap();
1✔
3542
            tx.commit().unwrap();
1✔
3543
        }
1✔
3544

3545
        let n1 = PeerDB::get_peer(
1✔
3546
            db.conn(),
1✔
3547
            neighbor_1.addr.network_id,
1✔
3548
            &neighbor_1.addr.addrbytes,
1✔
3549
            neighbor_1.addr.port,
1✔
3550
        )
3551
        .unwrap()
1✔
3552
        .unwrap();
1✔
3553
        let n2 = PeerDB::get_peer(
1✔
3554
            db.conn(),
1✔
3555
            neighbor_2.addr.network_id,
1✔
3556
            &neighbor_2.addr.addrbytes,
1✔
3557
            neighbor_2.addr.port,
1✔
3558
        )
3559
        .unwrap()
1✔
3560
        .unwrap();
1✔
3561

3562
        assert_eq!(n1.denied, 5678);
1✔
3563
        assert_eq!(n2.denied, 5678);
1✔
3564

3565
        assert_eq!(n1.allowed, 1234);
1✔
3566
        assert_eq!(n2.allowed, 1234);
1✔
3567

3568
        {
1✔
3569
            let tx = db.tx_begin().unwrap();
1✔
3570
            PeerDB::refresh_denies(&tx).unwrap();
1✔
3571
            PeerDB::refresh_allows(&tx).unwrap();
1✔
3572
            tx.commit().unwrap();
1✔
3573
        }
1✔
3574

3575
        let n1 = PeerDB::get_peer(
1✔
3576
            db.conn(),
1✔
3577
            neighbor_1.addr.network_id,
1✔
3578
            &neighbor_1.addr.addrbytes,
1✔
3579
            neighbor_1.addr.port,
1✔
3580
        )
3581
        .unwrap()
1✔
3582
        .unwrap();
1✔
3583
        let n2 = PeerDB::get_peer(
1✔
3584
            db.conn(),
1✔
3585
            neighbor_2.addr.network_id,
1✔
3586
            &neighbor_2.addr.addrbytes,
1✔
3587
            neighbor_2.addr.port,
1✔
3588
        )
3589
        .unwrap()
1✔
3590
        .unwrap();
1✔
3591

3592
        assert_eq!(n1.denied, i64::MAX);
1✔
3593
        assert_eq!(n2.denied, 0); // refreshed; no longer denied
1✔
3594

3595
        assert_eq!(n1.allowed, 0);
1✔
3596
        assert_eq!(n2.allowed, 0);
1✔
3597
    }
1✔
3598

3599
    /// Test PeerDB::connect() with different private keys.  Verify that LocalPeer reflects the
3600
    /// latest key.
3601
    #[test]
3602
    fn test_connect_new_key() {
1✔
3603
        let key1 = Secp256k1PrivateKey::random();
1✔
3604
        let key2 = Secp256k1PrivateKey::random();
1✔
3605

3606
        let path = "/tmp/test-connect-new-key.db".to_string();
1✔
3607
        if fs::metadata(&path).is_ok() {
1✔
3608
            fs::remove_file(&path).unwrap();
×
3609
        }
1✔
3610

3611
        let db = PeerDB::connect(
1✔
3612
            &path,
1✔
3613
            true,
3614
            0x80000000,
3615
            0,
3616
            Some(key1.clone()),
1✔
3617
            i64::MAX as u64,
1✔
3618
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3619
            12345,
3620
            UrlString::try_from("http://foo.com").unwrap(),
1✔
3621
            &[],
1✔
3622
            None,
1✔
3623
            &[],
1✔
3624
        )
3625
        .unwrap();
1✔
3626
        let local_peer = PeerDB::get_local_peer(db.conn()).unwrap();
1✔
3627
        assert_eq!(local_peer.private_key, key1);
1✔
3628

3629
        assert!(fs::metadata(&path).is_ok());
1✔
3630

3631
        let db = PeerDB::connect(
1✔
3632
            &path,
1✔
3633
            true,
3634
            0x80000000,
3635
            0,
3636
            None,
1✔
3637
            i64::MAX as u64,
1✔
3638
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3639
            12345,
3640
            UrlString::try_from("http://foo.com").unwrap(),
1✔
3641
            &[],
1✔
3642
            None,
1✔
3643
            &[],
1✔
3644
        )
3645
        .unwrap();
1✔
3646
        let local_peer = PeerDB::get_local_peer(db.conn()).unwrap();
1✔
3647
        assert_eq!(local_peer.private_key, key1);
1✔
3648

3649
        let db = PeerDB::connect(
1✔
3650
            &path,
1✔
3651
            true,
3652
            0x80000000,
3653
            0,
3654
            Some(key2.clone()),
1✔
3655
            i64::MAX as u64,
1✔
3656
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3657
            12345,
3658
            UrlString::try_from("http://foo.com").unwrap(),
1✔
3659
            &[],
1✔
3660
            None,
1✔
3661
            &[],
1✔
3662
        )
3663
        .unwrap();
1✔
3664
        let local_peer = PeerDB::get_local_peer(db.conn()).unwrap();
1✔
3665
        assert_eq!(local_peer.private_key, key2);
1✔
3666
    }
1✔
3667

3668
    /// Test DB instantiation -- it must work.
3669
    #[test]
3670
    fn test_db_instantiation() {
1✔
3671
        let key1 = Secp256k1PrivateKey::random();
1✔
3672

3673
        let path = "/tmp/test-peerdb-instantiation.db".to_string();
1✔
3674
        if fs::metadata(&path).is_ok() {
1✔
3675
            fs::remove_file(&path).unwrap();
×
3676
        }
1✔
3677

3678
        let db = PeerDB::connect(
1✔
3679
            &path,
1✔
3680
            true,
3681
            0x80000000,
3682
            0,
3683
            Some(key1.clone()),
1✔
3684
            i64::MAX as u64,
1✔
3685
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3686
            12345,
3687
            UrlString::try_from("http://foo.com").unwrap(),
1✔
3688
            &[],
1✔
3689
            None,
1✔
3690
            &[],
1✔
3691
        )
3692
        .unwrap();
1✔
3693
    }
1✔
3694

3695
    /// Test `public` setting in DB migration
3696
    #[test]
3697
    fn test_db_schema_3_public_ip_migration() {
1✔
3698
        let key = Secp256k1PrivateKey::random();
1✔
3699

3700
        let path = "/tmp/test-peerdb-schema-3-public-ip-migration.db".to_string();
1✔
3701
        if fs::metadata(&path).is_ok() {
1✔
3702
            fs::remove_file(&path).unwrap();
×
3703
        }
1✔
3704
        let mut db = PeerDB::connect(
1✔
3705
            &path,
1✔
3706
            true,
3707
            0x80000000,
3708
            0,
3709
            Some(key.clone()),
1✔
3710
            i64::MAX as u64,
1✔
3711
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3712
            12345,
3713
            UrlString::try_from("http://foo.com").unwrap(),
1✔
3714
            &[],
1✔
3715
            None,
1✔
3716
            &[],
1✔
3717
        )
3718
        .unwrap();
1✔
3719

3720
        let private_addrbytes = [
1✔
3721
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3722
            PeerAddress::from_ipv4(192, 168, 0, 1),
1✔
3723
            PeerAddress::from_ipv4(172, 16, 0, 1),
1✔
3724
            PeerAddress::from_ipv4(10, 0, 0, 1),
1✔
3725
            PeerAddress([
1✔
3726
                0xfc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1✔
3727
                0x0d, 0x0e,
1✔
3728
            ]),
1✔
3729
            PeerAddress([
1✔
3730
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1✔
3731
                0x00, 0x01,
1✔
3732
            ]),
1✔
3733
        ];
1✔
3734

3735
        let public_addrbytes = [
1✔
3736
            PeerAddress::from_ipv4(1, 2, 3, 4),
1✔
3737
            PeerAddress([
1✔
3738
                0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
1✔
3739
                0xff, 0x00,
1✔
3740
            ]),
1✔
3741
        ];
1✔
3742

3743
        let mut neighbor = Neighbor {
1✔
3744
            addr: NeighborKey {
1✔
3745
                peer_version: 0x12345678,
1✔
3746
                network_id: 0x9abcdef0,
1✔
3747
                addrbytes: PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3748
                port: 12345,
1✔
3749
            },
1✔
3750
            public_key: Secp256k1PublicKey::from_hex(
1✔
3751
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
3752
            )
1✔
3753
            .unwrap(),
1✔
3754
            expire_block: 23456,
1✔
3755
            last_contact_time: 1552509642,
1✔
3756
            allowed: -1,
1✔
3757
            denied: -1,
1✔
3758
            asn: 34567,
1✔
3759
            org: 45678,
1✔
3760
            in_degree: 1,
1✔
3761
            out_degree: 1,
1✔
3762
        };
1✔
3763

3764
        // force public and see if it gets reverted
3765
        let tx = db.tx_begin().unwrap();
1✔
3766

3767
        for private in private_addrbytes.iter() {
6✔
3768
            neighbor.addr.addrbytes = private.clone();
6✔
3769
            neighbor.public_key = Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
6✔
3770
            assert!(PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap());
6✔
3771
        }
3772
        for public in public_addrbytes.iter() {
2✔
3773
            neighbor.addr.addrbytes = public.clone();
2✔
3774
            neighbor.public_key = Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
2✔
3775
            assert!(PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap());
2✔
3776
        }
3777
        tx.execute("UPDATE frontier SET public = 1", params![])
1✔
3778
            .unwrap();
1✔
3779
        tx.commit().unwrap();
1✔
3780

3781
        // make sure they're all listed as public (even if erroneously)
3782
        for private in private_addrbytes.iter() {
6✔
3783
            assert!(PeerDB::is_public(
6✔
3784
                &db.conn,
6✔
3785
                neighbor.addr.network_id,
6✔
3786
                private,
6✔
3787
                neighbor.addr.port
6✔
3788
            )
3789
            .unwrap());
6✔
3790
        }
3791
        for public in public_addrbytes.iter() {
2✔
3792
            assert!(PeerDB::is_public(
2✔
3793
                &db.conn,
2✔
3794
                neighbor.addr.network_id,
2✔
3795
                public,
2✔
3796
                neighbor.addr.port
2✔
3797
            )
3798
            .unwrap());
2✔
3799
        }
3800

3801
        let tx = db.tx_begin().unwrap();
1✔
3802
        PeerDB::update_peerdb_public_addrs(&tx).unwrap();
1✔
3803

3804
        // fixed
3805
        for private in private_addrbytes.iter() {
6✔
3806
            assert!(
6✔
3807
                !PeerDB::is_public(&tx, neighbor.addr.network_id, private, neighbor.addr.port)
6✔
3808
                    .unwrap()
6✔
3809
            );
3810
        }
3811
        for public in public_addrbytes.iter() {
2✔
3812
            assert!(
2✔
3813
                PeerDB::is_public(&tx, neighbor.addr.network_id, public, neighbor.addr.port)
2✔
3814
                    .unwrap()
2✔
3815
            );
3816
        }
3817

3818
        // now do the opposite
3819
        tx.execute("UPDATE frontier SET public = 0", params![])
1✔
3820
            .unwrap();
1✔
3821
        tx.commit().unwrap();
1✔
3822

3823
        let tx = db.tx_begin().unwrap();
1✔
3824
        PeerDB::update_peerdb_public_addrs(&tx).unwrap();
1✔
3825

3826
        // fixed
3827
        for private in private_addrbytes.iter() {
6✔
3828
            assert!(
6✔
3829
                !PeerDB::is_public(&tx, neighbor.addr.network_id, private, neighbor.addr.port)
6✔
3830
                    .unwrap()
6✔
3831
            );
3832
        }
3833
        for public in public_addrbytes.iter() {
2✔
3834
            assert!(
2✔
3835
                PeerDB::is_public(&tx, neighbor.addr.network_id, public, neighbor.addr.port)
2✔
3836
                    .unwrap()
2✔
3837
            );
3838
        }
3839
        tx.commit().unwrap();
1✔
3840
    }
1✔
3841

3842
    /// Verify that multiple peers with the same public key are coalesced by last-contact-time
3843
    #[test]
3844
    fn test_query_peers() {
1✔
3845
        let key = Secp256k1PrivateKey::random();
1✔
3846

3847
        let path = "/tmp/test-query-peers.db".to_string();
1✔
3848
        if fs::metadata(&path).is_ok() {
1✔
3849
            fs::remove_file(&path).unwrap();
×
3850
        }
1✔
3851
        let mut db = PeerDB::connect(
1✔
3852
            &path,
1✔
3853
            true,
3854
            0x80000000,
3855
            0,
3856
            Some(key.clone()),
1✔
3857
            i64::MAX as u64,
1✔
3858
            PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3859
            12345,
3860
            UrlString::try_from("http://foo.com").unwrap(),
1✔
3861
            &[],
1✔
3862
            None,
1✔
3863
            &[],
1✔
3864
        )
3865
        .unwrap();
1✔
3866

3867
        let mut neighbor = Neighbor {
1✔
3868
            addr: NeighborKey {
1✔
3869
                peer_version: 0x12345678,
1✔
3870
                network_id: 0x9abcdef0,
1✔
3871
                addrbytes: PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3872
                port: 12345,
1✔
3873
            },
1✔
3874
            public_key: Secp256k1PublicKey::from_hex(
1✔
3875
                "02fa66b66f8971a8cd4d20ffded09674e030f0f33883f337f34b95ad4935bac0e3",
1✔
3876
            )
1✔
3877
            .unwrap(),
1✔
3878
            expire_block: 23456,
1✔
3879
            last_contact_time: 1552509642,
1✔
3880
            allowed: -1,
1✔
3881
            denied: -1,
1✔
3882
            asn: 34567,
1✔
3883
            org: 45678,
1✔
3884
            in_degree: 1,
1✔
3885
            out_degree: 1,
1✔
3886
        };
1✔
3887

3888
        let tx = db.tx_begin().unwrap();
1✔
3889
        for i in 0..10 {
10✔
3890
            neighbor.addr.port = (i + 1024) as u16;
10✔
3891
            neighbor.last_contact_time = (i + 1552509642) as u64;
10✔
3892
            assert!(PeerDB::try_insert_peer(&tx, &neighbor, &[]).unwrap());
10✔
3893
        }
3894
        tx.commit().unwrap();
1✔
3895

3896
        // only one peer returned, and it's the one with the highest last-contact time
3897
        let mut peers = PeerDB::query_peers(
1✔
3898
            &db.conn,
1✔
3899
            "SELECT * FROM frontier WHERE network_id = ?1 AND addrbytes = ?2 AND port = ?3",
1✔
3900
            params![
1✔
3901
                &neighbor.addr.network_id,
1✔
3902
                &to_bin(neighbor.addr.addrbytes.as_bytes()),
1✔
3903
                &neighbor.addr.port
1✔
3904
            ],
3905
        )
3906
        .unwrap();
1✔
3907
        assert_eq!(peers.len(), 1);
1✔
3908

3909
        let peer = peers.pop().unwrap();
1✔
3910
        assert_eq!(peer.addr.port, 1033);
1✔
3911
        assert_eq!(peer.last_contact_time, 1552509651);
1✔
3912
    }
1✔
3913

3914
    #[test]
3915
    fn test_get_fresh_random_neighbors_allowed_logic() {
1✔
3916
        let now_secs = util::get_epoch_time_secs();
1✔
3917
        let current_block_height = 1000;
1✔
3918

3919
        let network_id = NETWORK_ID_MAINNET;
1✔
3920
        let query_network_epoch_param = PEER_VERSION_EPOCH_3_0; // Query for peers supporting at least 3.0
1✔
3921
        let query_peer_version_param = PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32; // Query for peers supporting at least 3.0
1✔
3922
        let min_age_fresh = now_secs - 7200; // Fresh if contacted in last 2 hours
1✔
3923

3924
        let mut db = PeerDB::connect_memory(
1✔
3925
            network_id,
1✔
3926
            0,
3927
            0,
3928
            UrlString::from_literal("http://test.com"),
1✔
3929
            &[],
1✔
3930
            &[],
1✔
3931
        )
3932
        .unwrap();
1✔
3933

3934
        let base_neighbor = Neighbor {
1✔
3935
            addr: NeighborKey {
1✔
3936
                peer_version: PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32, // Default to a matching epoch
1✔
3937
                network_id,
1✔
3938
                addrbytes: PeerAddress::from_ipv4(127, 0, 0, 1),
1✔
3939
                port: 10000, // Will change per peer
1✔
3940
            },
1✔
3941
            public_key: Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random()),
1✔
3942
            expire_block: current_block_height + 100,
1✔
3943
            last_contact_time: now_secs - 10,
1✔
3944
            allowed: 0,
1✔
3945
            denied: 0,
1✔
3946
            asn: 123,
1✔
3947
            org: 456,
1✔
3948
            in_degree: 1,
1✔
3949
            out_degree: 1,
1✔
3950
        };
1✔
3951

3952
        let mut peers_to_insert = Vec::new();
1✔
3953

3954
        // 1. Always Allowed (Fresh, Epoch 3.0)
3955
        let mut n_always_allowed = base_neighbor.clone();
1✔
3956
        n_always_allowed.addr.port = 10001;
1✔
3957
        n_always_allowed.addr.peer_version =
1✔
3958
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
3959
        n_always_allowed.public_key =
1✔
3960
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
3961
        n_always_allowed.allowed = -1;
1✔
3962
        peers_to_insert.push(n_always_allowed.clone());
1✔
3963

3964
        // 2. Temporarily Allowed - Valid (Fresh, Epoch 3.1 - newer)
3965
        let mut n_temp_allowed_valid = base_neighbor.clone();
1✔
3966
        n_temp_allowed_valid.addr.port = 10002;
1✔
3967
        n_temp_allowed_valid.addr.peer_version =
1✔
3968
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_1 as u32;
1✔
3969
        n_temp_allowed_valid.public_key =
1✔
3970
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
3971
        n_temp_allowed_valid.allowed = (now_secs + 3600) as i64;
1✔
3972
        peers_to_insert.push(n_temp_allowed_valid.clone());
1✔
3973

3974
        // 3. Temporarily Allowed - Expired (Fresh, Epoch 3.0)
3975
        let mut n_temp_allowed_expired = base_neighbor.clone();
1✔
3976
        n_temp_allowed_expired.addr.port = 10003;
1✔
3977
        n_temp_allowed_expired.addr.peer_version =
1✔
3978
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
3979
        n_temp_allowed_expired.public_key =
1✔
3980
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
3981
        n_temp_allowed_expired.allowed = (now_secs - 3600) as i64;
1✔
3982
        peers_to_insert.push(n_temp_allowed_expired.clone());
1✔
3983

3984
        // 4. Neutral (allowed = 0) (Fresh, Epoch 3.0)
3985
        let mut n_neutral = base_neighbor.clone();
1✔
3986
        n_neutral.addr.port = 10004;
1✔
3987
        n_neutral.addr.peer_version = PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
3988
        n_neutral.public_key = Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
3989
        n_neutral.allowed = 0;
1✔
3990
        peers_to_insert.push(n_neutral.clone());
1✔
3991

3992
        // 5. Denied (Fresh, Epoch 3.0) - Should not be picked
3993
        let mut n_denied = base_neighbor.clone();
1✔
3994
        n_denied.addr.port = 10005;
1✔
3995
        n_denied.addr.peer_version = PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
3996
        n_denied.public_key = Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
3997
        n_denied.denied = (now_secs + 3600) as i64;
1✔
3998
        peers_to_insert.push(n_denied.clone());
1✔
3999

4000
        // 6. Denied - Expired (Effectively NOT Denied) (Fresh, Epoch 3.0)
4001
        let mut n_denied_expired = base_neighbor.clone();
1✔
4002
        n_denied_expired.addr.port = 10006;
1✔
4003
        n_denied_expired.addr.peer_version =
1✔
4004
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
4005
        n_denied_expired.public_key =
1✔
4006
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
4007
        n_denied_expired.denied = (now_secs - 3600) as i64;
1✔
4008
        peers_to_insert.push(n_denied_expired.clone());
1✔
4009

4010
        // 7. Always Allowed - But actually Denied (Fresh, Epoch 3.0) - Should not be picked
4011
        let mut n_always_allowed_denied = base_neighbor.clone();
1✔
4012
        n_always_allowed_denied.addr.port = 10007;
1✔
4013
        n_always_allowed_denied.addr.peer_version =
1✔
4014
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
4015
        n_always_allowed_denied.public_key =
1✔
4016
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
4017
        n_always_allowed_denied.allowed = -1;
1✔
4018
        n_always_allowed_denied.denied = (now_secs + 3600) as i64;
1✔
4019
        peers_to_insert.push(n_always_allowed_denied.clone());
1✔
4020

4021
        // 8. Temp Allowed Valid - But actually Denied (Fresh, Epoch 3.0) - Should not be picked
4022
        let mut n_temp_allowed_valid_denied = base_neighbor.clone();
1✔
4023
        n_temp_allowed_valid_denied.addr.port = 10008;
1✔
4024
        n_temp_allowed_valid_denied.addr.peer_version =
1✔
4025
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
4026
        n_temp_allowed_valid_denied.public_key =
1✔
4027
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
4028
        n_temp_allowed_valid_denied.allowed = (now_secs + 3600) as i64;
1✔
4029
        n_temp_allowed_valid_denied.denied = (now_secs + 3600) as i64;
1✔
4030
        peers_to_insert.push(n_temp_allowed_valid_denied.clone());
1✔
4031

4032
        // 9. Not Fresh LCT - But Always Allowed (Epoch 3.0) (For always_include_allowed=true test)
4033
        let mut n_not_fresh_lct_always_allowed = base_neighbor.clone();
1✔
4034
        n_not_fresh_lct_always_allowed.addr.port = 10009;
1✔
4035
        n_not_fresh_lct_always_allowed.addr.peer_version =
1✔
4036
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
4037
        n_not_fresh_lct_always_allowed.public_key =
1✔
4038
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
4039
        n_not_fresh_lct_always_allowed.allowed = -1;
1✔
4040
        n_not_fresh_lct_always_allowed.last_contact_time = min_age_fresh - 10;
1✔
4041
        peers_to_insert.push(n_not_fresh_lct_always_allowed.clone());
1✔
4042

4043
        // 10. Not Fresh Expire - But Temp Allowed Valid (Epoch 3.0) (For always_include_allowed=true test)
4044
        let mut n_not_fresh_expire_temp_allowed = base_neighbor.clone();
1✔
4045
        n_not_fresh_expire_temp_allowed.addr.port = 10010;
1✔
4046
        n_not_fresh_expire_temp_allowed.addr.peer_version =
1✔
4047
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_3_0 as u32;
1✔
4048
        n_not_fresh_expire_temp_allowed.public_key =
1✔
4049
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
4050
        n_not_fresh_expire_temp_allowed.allowed = (now_secs + 3600) as i64;
1✔
4051
        n_not_fresh_expire_temp_allowed.expire_block = current_block_height - 10;
1✔
4052
        peers_to_insert.push(n_not_fresh_expire_temp_allowed.clone());
1✔
4053

4054
        // 11. Old Epoch Peer (Fresh, Neutral) - Should be filtered by query_network_epoch_param
4055
        let mut n_old_epoch_peer = base_neighbor.clone();
1✔
4056
        n_old_epoch_peer.addr.port = 10012; // New port for this peer
1✔
4057
        n_old_epoch_peer.addr.peer_version =
1✔
4058
            PEER_VERSION_TESTNET_MAJOR | PEER_VERSION_EPOCH_2_0 as u32; // Older epoch
1✔
4059
        n_old_epoch_peer.public_key =
1✔
4060
            Secp256k1PublicKey::from_private(&Secp256k1PrivateKey::random());
1✔
4061
        n_old_epoch_peer.allowed = 0; // Neutral
1✔
4062
        n_old_epoch_peer.denied = 0; // Not denied
1✔
4063
        n_old_epoch_peer.last_contact_time = now_secs - 10; // Fresh LCT
1✔
4064
        n_old_epoch_peer.expire_block = current_block_height + 100; // Fresh expire
1✔
4065
        peers_to_insert.push(n_old_epoch_peer.clone());
1✔
4066

4067
        {
4068
            let tx = db.tx_begin().unwrap();
1✔
4069
            for peer in &peers_to_insert {
11✔
4070
                assert!(PeerDB::try_insert_peer(&tx, peer, &[]).unwrap());
11✔
4071
            }
4072
            tx.commit().unwrap();
1✔
4073
        }
4074

4075
        // --- Test Case 1: always_include_allowed = false ---
4076
        // Expected to pick from fresh, non-denied peers matching query_network_epoch_param.
4077
        // Candidates: n_always_allowed, n_temp_allowed_valid, n_temp_allowed_expired, n_neutral, n_denied_expired (5 total)
4078
        // n_old_epoch_peer is filtered out due to its older epoch.
4079
        let count_false = 5;
1✔
4080
        let results_false = PeerDB::get_fresh_random_neighbors(
1✔
4081
            db.conn(),
1✔
4082
            network_id,
1✔
4083
            query_network_epoch_param,
1✔
4084
            query_peer_version_param,
1✔
4085
            min_age_fresh,
1✔
4086
            count_false,
1✔
4087
            current_block_height,
1✔
4088
            false,
4089
            false,
4090
        )
4091
        .unwrap();
1✔
4092

4093
        assert_eq!(results_false.len() as u32, count_false, "Should get all fresh, non-denied peers matching epoch for always_include_allowed=false");
1✔
4094

4095
        let result_ports_false: HashSet<u16> = results_false.iter().map(|p| p.addr.port).collect();
1✔
4096
        let expected_candidate_ports_false: HashSet<u16> = [
1✔
4097
            n_always_allowed.addr.port,
1✔
4098
            n_temp_allowed_valid.addr.port,
1✔
4099
            n_temp_allowed_expired.addr.port,
1✔
4100
            n_neutral.addr.port,
1✔
4101
            n_denied_expired.addr.port,
1✔
4102
        ]
1✔
4103
        .iter()
1✔
4104
        .cloned()
1✔
4105
        .collect();
1✔
4106

4107
        assert_eq!(
1✔
4108
            result_ports_false, expected_candidate_ports_false,
4109
            "Mismatch in candidates for always_include_allowed=false with epoch filtering"
4110
        );
4111
        assert!(
1✔
4112
            !result_ports_false.contains(&n_old_epoch_peer.addr.port),
1✔
4113
            "Old epoch peer should be filtered out"
4114
        );
4115

4116
        // --- Test Case 2: always_include_allowed = true ---
4117
        // Phase 1 (allow_qry picks, matching query_network_epoch_param):
4118
        //   - n_always_allowed (Fresh, Allowed<0, Epoch 3.0) -> YES
4119
        //   - n_temp_allowed_valid (Fresh, TempAllowedValid, Epoch 3.1) -> YES
4120
        //   - n_not_fresh_lct_always_allowed (NotFreshLCT, Allowed<0, Epoch 3.0) -> YES
4121
        //   - n_not_fresh_expire_temp_allowed (NotFreshExpire, TempAllowedValid, Epoch 3.0) -> YES
4122
        // Phase 1 count = 4.
4123
        // Phase 2 (random_peers_qry with include_allowed_condition, freshness from params, and matching query_network_epoch_param):
4124
        //   - n_temp_allowed_expired (Fresh, TempAllowedExpired, Epoch 3.0) -> YES
4125
        //   - n_neutral (Fresh, Neutral, Epoch 3.0) -> YES
4126
        //   - n_denied_expired (Fresh, DeniedExpired, NeutralAllowed, Epoch 3.0) -> YES
4127
        // Phase 2 count = 3.
4128
        // Total possible unique peers = 7 (n_old_epoch_peer still filtered out)
4129

4130
        let count_true = 7;
1✔
4131
        let results_true = PeerDB::get_fresh_random_neighbors(
1✔
4132
            db.conn(),
1✔
4133
            network_id,
1✔
4134
            query_network_epoch_param,
1✔
4135
            query_peer_version_param,
1✔
4136
            min_age_fresh,
1✔
4137
            count_true,
1✔
4138
            current_block_height,
1✔
4139
            true,
4140
            false,
4141
        )
4142
        .unwrap();
1✔
4143

4144
        assert_eq!(
1✔
4145
            results_true.len() as u32,
1✔
4146
            count_true,
4147
            "Should get all 7 expected peers for always_include_allowed=true with epoch filtering"
4148
        );
4149

4150
        let result_ports_true: HashSet<u16> = results_true.iter().map(|p| p.addr.port).collect();
1✔
4151
        let expected_candidate_ports_true: HashSet<u16> = [
1✔
4152
            n_always_allowed.addr.port,
1✔
4153
            n_temp_allowed_valid.addr.port,
1✔
4154
            n_temp_allowed_expired.addr.port,
1✔
4155
            n_neutral.addr.port,
1✔
4156
            n_denied_expired.addr.port,
1✔
4157
            n_not_fresh_lct_always_allowed.addr.port,
1✔
4158
            n_not_fresh_expire_temp_allowed.addr.port,
1✔
4159
        ]
1✔
4160
        .iter()
1✔
4161
        .cloned()
1✔
4162
        .collect();
1✔
4163

4164
        assert_eq!(
1✔
4165
            result_ports_true, expected_candidate_ports_true,
4166
            "Mismatch in candidates for always_include_allowed=true with epoch filtering"
4167
        );
4168
        assert!(
1✔
4169
            !result_ports_true.contains(&n_old_epoch_peer.addr.port),
1✔
4170
            "Old epoch peer should be filtered out for always_include_allowed=true as well"
4171
        );
4172

4173
        // Test Case 2 Small: Verify that when count is less than or equal to the number of Phase 1 candidates,
4174
        // the result should only contain Phase 1 candidates (only allowed peers), when always_include_allowed=true.
4175
        // This ensures we prioritize Phase 1 candidates over Phase 2 candidates when we have a limited count.
4176
        let count_true_small = 4;
1✔
4177
        let results_true_small = PeerDB::get_fresh_random_neighbors(
1✔
4178
            db.conn(),
1✔
4179
            network_id,
1✔
4180
            query_network_epoch_param,
1✔
4181
            query_peer_version_param,
1✔
4182
            min_age_fresh,
1✔
4183
            count_true_small,
1✔
4184
            current_block_height,
1✔
4185
            true,
4186
            false,
4187
        )
4188
        .unwrap();
1✔
4189
        assert_eq!(results_true_small.len() as u32, count_true_small);
1✔
4190
        let result_ports_true_small: HashSet<u16> =
1✔
4191
            results_true_small.iter().map(|p| p.addr.port).collect();
1✔
4192

4193
        let phase1_candidate_ports: HashSet<u16> = [
1✔
4194
            n_always_allowed.addr.port,                // Epoch 3.0
1✔
4195
            n_temp_allowed_valid.addr.port,            // Epoch 3.1
1✔
4196
            n_not_fresh_lct_always_allowed.addr.port,  // Epoch 3.0
1✔
4197
            n_not_fresh_expire_temp_allowed.addr.port, // Epoch 3.0
1✔
4198
        ]
1✔
4199
        .iter()
1✔
4200
        .cloned()
1✔
4201
        .collect();
1✔
4202

4203
        for port in result_ports_true_small {
4✔
4204
            assert!(phase1_candidate_ports.contains(&port), "Peers for always_include_allowed=true with small count should come from Phase 1 candidates (epoch filtered)");
4✔
4205
        }
4206
    }
1✔
4207
}
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