zakura-network 7.1.1

Networking code for the Zakura node. Internal crate, published to support cargo install zakura
use super::*;
use crate::zakura::{
    testkit::{TraceCapture, TraceValue},
    CloseCause,
};

fn connect(reactor: &mut HeaderSyncReactor, peer: ZakuraPeerId, session_id: u64) {
    let (send, _outbound) = framed_channel(8);
    reactor.handle_peer_connected(PeerSession::from_parts_with_connection(
        peer.clone(),
        session_id,
        send,
        CancellationToken::new(),
        CancellationToken::new(),
        CloseCause::new(),
    ));
}

fn trace_active_request(reactor: &HeaderSyncReactor, peer: &ZakuraPeerId) -> ActiveHeaderRequest {
    let active = reactor
        .peer_work_queue
        .active(peer)
        .expect("the fixture has one active request")
        .clone();
    reactor.emit_header_request(
        peer,
        active.owner.session_id(),
        active.owner.header_authority(),
        active.request_id,
        active.target.status.selected_tip_hash,
        &active.sent_locator,
        active.max_header_count,
        active.tree_aux_schema,
    );
    active
}

#[tokio::test]
async fn busy_outcome_emits_exact_bounded_request_terminal() {
    let mut capture = TraceCapture::for_test("busy_outcome_emits_exact_bounded_request_terminal")
        .expect("trace capture starts");
    let mut startup = startup(CancellationToken::new());
    let anchor = zakura_header_chain::Frontier::new(startup.anchor.0, startup.anchor.1);
    let snapshot = committed_snapshot(anchor);
    let (_snapshots_tx, snapshots_rx) = watch::channel(Some(snapshot.clone()));
    startup.committed_snapshots = Some(snapshots_rx);
    startup.trace = crate::zakura::ZakuraTrace::new(capture.tracer(), "terminal-test");
    let (_, _, mut reactor) =
        build_header_sync_reactor(startup).expect("the traced reactor builds");
    let peer = peer();
    let session_id = 7;
    connect(&mut reactor, peer.clone(), session_id);
    seed_applying_request(&mut reactor, &snapshot, peer.clone(), session_id);
    let active = trace_active_request(&reactor, &peer);
    reactor
        .peer_work_queue
        .active_mut(&peer)
        .expect("the request remains active")
        .phase = HeaderTargetPhase::Receiving;

    reactor.handle_headers_outcome(
        peer.clone(),
        session_id,
        active.owner.header_authority(),
        HeadersOutcome {
            request_id: active.request_id.get(),
            target_tip_hash: active.target.status.selected_tip_hash,
            outcome: HeadersOutcomeCode::Busy,
        },
    );

    capture.flush().await;
    let reader = capture.reader().expect("the trace reloads");
    let header_trace = reader.table(HEADER_SYNC_TABLE.table());
    let target_hash = active.target.status.selected_tip_hash.to_string();
    let anchor_hash = active
        .owner
        .header_authority()
        .branch
        .anchor_hash
        .to_string();
    header_trace.assert_row(
        hs_trace::HEADER_REQUEST_TERMINAL,
        &[
            (hs_trace::SESSION_ID, TraceValue::U64(session_id)),
            (hs_trace::DIRECTION, TraceValue::Str("inbound")),
            (
                hs_trace::REQUEST_ID,
                TraceValue::U64(active.request_id.get()),
            ),
            (hs_trace::BRANCH_ANCHOR, TraceValue::Str(&anchor_hash)),
            (hs_trace::BRANCH_TARGET, TraceValue::Str(&target_hash)),
            (hs_trace::TARGET_HASH, TraceValue::Str(&target_hash)),
            (hs_trace::OUTCOME, TraceValue::Str("busy")),
        ],
    );
    assert!(reactor.peer_work_queue.active(&peer).is_none());
    let _ = capture.finish().await.expect("trace capture finishes");
}

#[tokio::test]
async fn timeout_emits_request_terminal_evidence() {
    let mut capture = TraceCapture::for_test("timeout_emits_request_terminal_evidence")
        .expect("trace capture starts");
    let mut startup = startup(CancellationToken::new());
    let anchor = zakura_header_chain::Frontier::new(startup.anchor.0, startup.anchor.1);
    let snapshot = committed_snapshot(anchor);
    let (_snapshots_tx, snapshots_rx) = watch::channel(Some(snapshot.clone()));
    startup.committed_snapshots = Some(snapshots_rx);
    startup.trace = crate::zakura::ZakuraTrace::new(capture.tracer(), "timeout-test");
    let (_, _, mut reactor) =
        build_header_sync_reactor(startup).expect("the traced reactor builds");
    let peer = peer();
    let session_id = 11;
    connect(&mut reactor, peer.clone(), session_id);
    seed_applying_request(&mut reactor, &snapshot, peer.clone(), session_id);
    trace_active_request(&reactor, &peer);

    let deadline = Instant::now();
    reactor.request_deadlines.insert(peer, deadline);
    reactor.retire_timed_out_requests(deadline);

    capture.flush().await;
    let reader = capture.reader().expect("the trace reloads");
    let rows = reader.table(HEADER_SYNC_TABLE.table()).rows();
    let terminal = rows
        .iter()
        .position(|row| {
            row.get("event").and_then(serde_json::Value::as_str)
                == Some(hs_trace::HEADER_REQUEST_TERMINAL)
        })
        .expect("timeout emits terminal evidence");
    assert_eq!(
        rows[terminal]
            .get(hs_trace::OUTCOME)
            .and_then(serde_json::Value::as_str),
        Some("timed_out")
    );
    assert_eq!(
        rows[terminal]
            .get(hs_trace::DIRECTION)
            .and_then(serde_json::Value::as_str),
        Some("inbound")
    );
    let _ = capture.finish().await.expect("trace capture finishes");
}