Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,10 @@ A cell that names a crate-local test describes the witness that exists today. It
| `node_cli_and_surface_smoke` | Lifecycle/CLI | Networks, `run_node`, config errors, CLI flags (incl. `--conf`, `--peer-timeout=0` refuse / `=1` smoke, unknown conf key ignored, `min_relay_tx_fee=-1` and `network=nope` conf fail), dropped Core `--rpcuser`/`--rpcpassword`/`--rpcport`/`--rpcconnect`/`-rpcport` refuse, help/version (`rbitcoin-cli` help and version do not dial). Usage errors exit 2 before a datadir is touched: unknown flags, missing or bad values, and every concatenated, one-dash, or Core spelling of a native kebab flag. A missing conf, a bad line, a bad `log_level`, conf `rpcuser`/`rpcpassword`, and `max_outbound=0` exit 2 and open no store. Non-hex `--min-chain-work`, a signet challenge or block time off a custom signet, and `--sp-tweaks` with `--prune-seqsigwit` exit 1. One smoke takes the native flag set together; bare network conf lines parse and CLI `--datadir` wins over conf `datadir`; `--datadir-cold` puts seqsigwit on the cold store; a custom signet smokes. Signet: genesis header plus height-1 BIP325 connect. The assembled `NodeConfig` values and help text no surface reports are the node-crate `operator_conf_and_argv` (`--bytes-per-sigop`, `--block-reserved-sigops`, conf `sp_tweaks` together with `prune_seqsigwit`, conf `sp_tweaks=1` at the default dust and `sp_tweaks_dust` of 546, 0, or a non-numeric value, and Electrum or Esplora with and without `--sh-index` plus `--sh-index` alone) |
| `three_stage_confirm_and_parent_pin_surface` | Consensus+query | Split load→scripts→write of pad+spend from genesis (header-plan BIP68 MTP); parent pin; load ready timeout/cancel; instance-owned `last_write` / `last_pin` / `take_window` meters (a second engine's window stays empty); txstat fee / size / weight from the load assemble, restamp and its refuses; same-run create then spend; 546-shaped 2-vout merge + same-block chain + cross-batch head resolve; an already-at-height retry finishes a spend annotate |
| `mempool_under_pressure` | Mempool + RPC (crate) | One entry in `orphanage`, `accept`, `tx_relay`, and `methods_tests`: orphan reserve and expiry, sigops before script, rolling fee floor, cluster cap, parked min-relay orphan, and the package RPC rejects (unsorted, missing inputs, conflict, min-relay parent with maxfeerate child). |
| `mempool_accept_life` | Mempool (crate) | One `ActiveMempool` against one chain view that blocks move. Sigop-adjusted vsize (boundary, min relay, full-pool floor, RBF, package and 1p1c), the raw-weight cluster limit, the shared block sigop budget, and sigop cost plus bytes-per-sigop and reserve overlays across reopen and compact. Orphan parks and re-announce, dry run not parked, parent promotes the child; missing vout, invalid parent, and block-spent coin reject without parking. Full RBF and no return, the staged commit failing closed on a conflict that landed after prepare, pure RBFR unpinning a child, replaced txs out of the cluster count; a ~30 kvB single tx under the vsize cap and the ten-way merge over it. Package order, CPFP, child fail restoring the RBF victim. A block evicts double-spent txs with descendants; a reorg readmits the parent and evicts the BIP68 and coinbase-maturity spends. Raised `-minrelaytxfee`: 1p1c needs a paying child, an unrelated tx does not ride the waiver, child fail takes a promoted spender down. Full pool: a protected lone worst chunk evicts nothing and leaves the floor, the next arrival evicts the CPFP pair together. |
| `rpc_regtest_chain_ops` | RPC (crate) | One regtest hub from genesis through `dispatch`. At genesis: `size_on_disk` is the store walk, IBD comes from the hub and not the stale atomic, buried deployments, `generateblock submit=false` connects nothing, and the priority, mocktime, mockscheduler, submitheader decode, and not-found refuses. The first block pays a p2wpkh address: display-order hashes and txids, raw `getblock` and header, a headers-only child at progress 0.5. Mocktime stamps `generate` and makes a far block `time-too-new`. Coinbase-only blocks: verbosity 1 without a seqsigwit zip, `getnetworkhashps` over chainwork, the empty template and proposal needles, a `time-too-old` header, an invalid parent body that marks its branch, and the `submitblock` merkle, length, coinbase, duplicate, value, and missing-input rejects. After a 120-block pad: the `nblocks=0` window, GBT fee and sigops (bare, P2SH, P2WSH), sigop-adjusted mempool vsize (`getmempoolentry`, package retry, `blockmintxfee`; weight and the Esplora/Electrum histogram stay raw) and a big-sigops cluster under the block budget, a non-DER spend with Core `reject-details`, deprioritise, `generateblock` reject shapes then parent-first mining, a premature coinbase, proposal spend/value/final needles against the chain, default and explicit `maxfeerate`, `testmempoolaccept` known vs mempool vs archived, invalidate and reconsider, and a parked sibling (held `getblock`, `preciousblock`). Last, a mainnet view of the same hub refuses the regtest-only methods and still takes `submitblock`. |
| `sigop_adjusted_size_budget_and_reopen` | Mempool (crate) | One empty `ActiveMempool`: sigop-adjusted vsize (boundary, min relay, full-pool floor, RBF, package and 1p1c), the raw-weight cluster limit, the shared block sigop budget, and sigop cost plus bytes-per-sigop and reserve overlays across reopen and compact. Each beat restores an empty pool. |
| `mempool_accept_life` | Mempool (crate) | One `ActiveMempool` against one chain view that blocks move. Orphan parks and re-announce, dry run not parked, parent promotes the child; missing vout, invalid parent, and block-spent coin reject without parking. Full RBF and no return, the staged commit failing closed on a conflict that landed after prepare, pure RBFR unpinning a child, replaced txs out of the cluster count; a ~30 kvB single tx under the vsize cap and the ten-way merge over it. Package order, CPFP, child fail restoring the RBF victim. A block evicts double-spent txs with descendants; a reorg readmits the parent and evicts the BIP68 and coinbase-maturity spends. Raised `-minrelaytxfee`: 1p1c needs a paying child, an unrelated tx does not ride the waiver, child fail takes a promoted spender down. Full pool: a protected lone worst chunk evicts nothing and leaves the floor, the next arrival evicts the CPFP pair together. |
| `rpc_regtest_from_genesis` | RPC (crate) | One regtest hub from genesis through the first submits. At genesis: `size_on_disk` is the store walk, IBD comes from the hub and not the stale atomic, buried deployments, `generateblock submit=false` connects nothing, and the priority, mocktime, mockscheduler, submitheader decode, and not-found refuses. The first block pays a p2wpkh address: display-order hashes and txids, raw `getblock` and header, a headers-only child at progress 0.5. Mocktime stamps `generate` and makes a far block `time-too-new`. Coinbase-only blocks: verbosity 1 without a seqsigwit zip, `getnetworkhashps` over chainwork, the empty template and proposal needles, a `time-too-old` header, an invalid parent body that marks its branch, and the `submitblock` merkle, length, coinbase, duplicate, value, and missing-input rejects. |
| `rpc_regtest_mature_chain_ops` | RPC (crate) | One regtest hub mined to height 130. The `nblocks=0` window, GBT fee and sigops (bare, P2SH, P2WSH), sigop-adjusted mempool vsize (`getmempoolentry`, package retry, `blockmintxfee`; weight and the Esplora/Electrum histogram stay raw) and a big-sigops cluster under the block budget, a non-DER spend with Core `reject-details`, deprioritise, `generateblock` reject shapes then parent-first mining, a premature coinbase, proposal spend/value/final needles against the chain, default and explicit `maxfeerate`, `testmempoolaccept` known vs mempool vs archived, invalidate and reconsider, and a parked sibling (held `getblock`, `preciousblock`). Last, a mainnet view of the same hub refuses the regtest-only methods and still takes `submitblock`. |
| `block_cache_and_mempool_hub_surface` | Net | BlockCache locator/eviction + MempoolHub accept/remove/reorg on mature chain. Eviction uses body depth 16. |
| `store_error_and_corrupt_paths` | Store | Error/corrupt surfaces |
| `store_table_header_and_idx_corrupt` | Store | Table header/head corrupt open |
Expand Down Expand Up @@ -388,7 +390,8 @@ A cell that names a crate-local test describes the witness that exists today. It
| `a_heavier_header_chain_keeps_its_context` | IBD (crate) | One hub: a challenger keeps median time while the tip is genesis (invalid at the median, valid one second later); that hub then confirms a stale ancestor, a heavier chain from below it replaces the candidate and drops its script skip, and a rewind uses the confirmed base work and height. Retarget is a second hub (period length is not regtest difficulty): a heavier fork across a gap retarget is adopted, rewind restores difficulty before the next retarget, and a challenger keeps its retarget snapshot. Those three checkpoint layouts cannot be one candidate. |
| `analog_selector_keeps_each_boundary_rate` | Mempool (crate) | One selector table: the exact band edge, absolute log distance, the requested quantile, and the two-hundredth nearest window each keep their own rate. |
| `fee_history_gaps_cache_and_reorg_share_one_history` | IBD (crate) | One history: heights without a hurdle are not observations, rates stay cached until a new height, only the newest hashes are kept, and a reorg drops those above. The RPC backfill stays `fee_history_backfills_from_the_chain_when_relay_starts`. |
| `peer_header_dos_and_self_announce` | P2P (**default**, crate) | One hub and one peer: verack order (wtxidrelay and sendaddrv2 stick, ping is logged, redundant verack is ignored, sendaddrv2 and oversized addrv2 after verack disconnect), unknown-parent block and compact, a full header batch continues from its last header, minchainwork stays silent until the floor, bad proof-of-work disconnects and time-too-new does not, empty-locator serves only a hash that has a body, an ancient weaker header disconnects unless noban, externalip is daily, `--no-discover` suppresses it, and onion / i2p replace that clearnet address. A noban peer stays up on a bad block and gathers no score. A version-3 header at CLTV activation logs `bad-version`, and a far stamp logs `time-too-new`. |
| `peer_header_dos_and_self_announce` | P2P (**default**, crate) | One hub and one peer: verack order (wtxidrelay and sendaddrv2 stick, ping is logged, redundant verack is ignored, sendaddrv2 and oversized addrv2 after verack disconnect), unknown-parent block and compact, a full header batch continues from its last header, minchainwork stays silent until the floor, bad proof-of-work disconnects and time-too-new does not, empty-locator serves only a hash that has a body, an ancient weaker header disconnects unless noban. A noban peer stays up on a bad block and gathers no score. A version-3 header at CLTV activation logs `bad-version`, and a far stamp logs `time-too-new`. |
| `self_announce_clearnet_then_overlay` | P2P (**default**, crate) | One address book, no chain: externalip is daily, loopback and `--no-discover` suppress it, then onion and i2p replace that clearnet address on the same book. |
| `p2p_compact_hb_getblocktxn_and_orphan` | P2P (**default**) | One mature pad: HB coinbase `cmpctblock`, 2-tx compact → `getblocktxn` then same-peer compact retry while pending + `blocktxn` connect, unique short-id fill that fails header merkle → `getdata` (not `getblocktxn`, header not `BLOCK_FAILED`) then honest full `block` connects, orphan child GetData then parent accept (INV AlreadyHave), then live `getblocks` → `inv`, inbound `feefilter`, `filterload` disconnect. The pad seals filters through height 1, does not advertise `NODE_COMPACT_FILTERS` while the tip is ahead, stays silent for a stop at the tip, and answers `getcfilters` / `getcfheaders` / `getcfcheckpt` for the sealed height. An oversize locator is rejected. `mempool`, `filteradd`, and `filterclear` disconnect. Live mutated `block` disconnects in `on_block`. Inbound `getdata` of 20 witness blocks serves `MAX_SERVE_BLOCKS` (16); 17th is not queued (`getdata_skips_reconstruct_when_serve_inflight_at_cap`). Same-peer pending compact skip is `same_peer_pending_cmpct_does_not_getblocktxn_again`. Tokio-worker park and park-not-reject logs are `peer_blocksonly_and_orphan_tx` |
| `p2p_feeler_completes_and_closes` | P2P (**default**) | Outbound feeler: VERSION then close (`feeler connection completed`). No live follow; dummy has no completed inbound. Same test: `run_feeler_timed` silence is `Timeout`. Inbound/outbound/plain silence stay `handshake_timeout_after_silence` |
| `p2p_inbound_full_rejects_extra` | P2P (**default**) | `max_inbound=1`: second follow is refused; first inbound stays. Same test: `select_inbound_eviction` 21-cand ranking (4 block + 5 slow + 4 tx + 8 ping → victim in slow). A peer that is only noban is not the eviction victim |
Expand Down
26 changes: 21 additions & 5 deletions crates/rbitcoin-mempool/src/accept_life_journey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,12 +708,11 @@ fn sigop_reopen_and_compact(life: &mut Life, dir: &rbitcoin_store::testutil::Tem
assert_eq!(life.mp.live_count(), 0);
}

/// One mempool from the first orphan to a full pool: relay order, RBF,
/// cluster caps, packages, a block, a reorg, a raised `-minrelaytxfee`, and
/// eviction, against one chain view. Sigop-adjusted size, the shared block
/// sigop budget, and overlay reopen/compact run first on that same pool.
/// Sigop-adjusted size, the block sigop budget, and reopen/compact. Each
/// beat restores an empty pool. This is not the accept-life story: nothing
/// here is a coin that story later spends.
#[test]
fn mempool_accept_life() {
fn sigop_adjusted_size_budget_and_reopen() {
let dir = tmp_dir();
let mut life = Life {
mp: ActiveMempool::open_or_create(&dir).unwrap(),
Expand All @@ -728,6 +727,23 @@ fn mempool_accept_life() {
sigop_rbf_package_and_cluster(&mut life);
sigop_block_budget(&mut life);
sigop_reopen_and_compact(&mut life, &dir);
let _ = std::fs::remove_dir_all(&dir);
}

/// One mempool from the first orphan to a full pool: relay order, RBF,
/// cluster caps, packages, a block, a reorg, a raised `-minrelaytxfee`, and
/// eviction, against one chain view.
#[test]
fn mempool_accept_life() {
let dir = tmp_dir();
let mut life = Life {
mp: ActiveMempool::open_or_create(&dir).unwrap(),
chain: LifeChain::default(),
tip: ChainTipCtx {
height: 200,
mtp: u32::MAX,
},
};
orphan_parks_then_promotes(&mut life);
invalid_or_spent_parent_does_not_park(&mut life);
let (rbf_coin, rbf_winner) = full_rbf_and_staged_commit(&mut life);
Expand Down
18 changes: 13 additions & 5 deletions crates/rbitcoin-net/src/peer_header_dos_journey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,16 @@ async fn ancient_weaker_fork_disconnects(
);
}

/// Clearnet externalip, then onion and i2p on the same address book.
/// `--no-discover` and the external address set here are what the overlay
/// beat checks, and the onion address stays set for the i2p beat.
#[test]
fn self_announce_clearnet_then_overlay() {
let peers = crate::peers::PeerHub::new();
self_announce_clearnet(&peers);
self_announce_overlay(&peers);
}

/// externalip is sent once, again after a day, and not when it is loopback
/// or `--no-discover` is set.
fn self_announce_clearnet(peers: &std::sync::Arc<crate::peers::PeerHub>) {
Expand Down Expand Up @@ -1234,9 +1244,9 @@ fn self_announce_overlay(peers: &std::sync::Arc<crate::peers::PeerHub>) {
);
}

/// One peer. Verack order, unknown parents, header floods, minchainwork,
/// and self-announce share this hub. CLTV activates at 111 so a rejected
/// header can log Core's reason on the same chain.
/// One peer. Verack order, unknown parents, header floods, and minchainwork
/// share this hub. CLTV activates at 111 so a rejected header can log Core's
/// reason on the same chain.
#[tokio::test]
async fn peer_header_dos_and_self_announce() {
let (dir, q) = rbitcoin_query::testutil::tiny_query_labeled("hdr-dos");
Expand Down Expand Up @@ -1277,8 +1287,6 @@ async fn peer_header_dos_and_self_announce() {
}
ancient_weaker_fork_disconnects(&hub, &peers).await;
empty_locator_needs_a_body(&hub);
self_announce_clearnet(&peers);
self_announce_overlay(&peers);

let _ = std::fs::remove_dir_all(src_dir);
let _ = std::fs::remove_dir_all(dir);
Expand Down
Loading
Loading