aboutsummaryrefslogtreecommitdiffstats
path: root/src/swarm/signatures.rs
blob: c1724558cadefc2b49e1243d945ffb7a7273b9a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//! Signatures of changes sent on their own (SIGNATURES), where no sync message took
//! them: a signature made after its change went (`--sign-every`), or one received
//! without the change it vouches for, as from a peer that had it already. Every
//! `SEND_EVERY`, each active peer on our epoch is sent the signatures it wasn't sent yet
//! (`Signatures::newer_than`), if any; it checks them as it checks those sync messages
//! bring (`Signatures::offer`), and sends them on in turn: each actor's latest goes to
//! each node about once.

use std::time::Duration;

use tracing::debug;

use crate::state::AppState;
use crate::transport::{ParsedMessage, SignaturesMessage};

/// How often the signatures the peers weren't sent yet go out.
pub(crate) const SEND_EVERY: Duration = Duration::from_secs(1);

/// Sends the signatures due every `SEND_EVERY`. Runs for as long as the node does.
pub(crate) async fn run(state: AppState) {
    let mut ticks = tokio::time::interval(SEND_EVERY);
    ticks.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
    loop {
        ticks.tick().await;
        send_due(&state).await;
    }
}

/// Each active peer on our epoch: the signatures it wasn't sent yet, if any.
async fn send_due(state: &AppState) {
    for peer in state.active_peers_on_local_epoch().await {
        let Some(peer_state) = state.get_peer_state(&peer).await else {
            continue;
        };
        let doc = std::sync::Arc::clone(&state.doc);
        // Lock order doc -> peer, as in sync generation.
        let message = tokio::task::spawn_blocking(move || {
            let replica = doc.blocking_read();
            let mut peer_state = peer_state.blocking_lock();
            let (signatures, sent) = replica.signatures.newer_than(peer_state.signatures_sent);
            if signatures.is_empty() {
                return None;
            }
            peer_state.signatures_sent = sent;
            Some(ParsedMessage::Signatures(SignaturesMessage {
                epoch: replica.epoch,
                epoch_seq: replica.epoch_seq,
                signatures: serde_bytes::ByteBuf::from(crate::document::signatures::pack(
                    &signatures,
                )),
            }))
        })
        .await;
        if let Ok(Some(message)) = message
            && let Err(error) = state.transport.send_message(&peer, &message).await
        {
            // Lost, they come again with the next sync session (`PeerState::reset_sync`).
            debug!(peer = %peer, error = %format!("{error:#}"), "Couldn't send signatures");
        }
    }
}

/// SIGNATURES from `peer`, of our epoch: checked and kept as those of sync messages.
pub(crate) async fn received(state: &AppState, message: SignaturesMessage) {
    let Some(signatures) = crate::document::signatures::unpack(&message.signatures)
        .filter(|signatures| signatures.len() <= crate::document::signatures::MAX_PER_MESSAGE)
    else {
        return;
    };
    let doc = std::sync::Arc::clone(&state.doc);
    let _ = tokio::task::spawn_blocking(move || {
        let mut replica = doc.blocking_write();
        if replica.epoch == message.epoch {
            let replica = &mut *replica;
            replica.signatures.offer(&replica.doc, signatures);
        }
    })
    .await;
}