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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
//! Signed changes end to end (`document::signatures`, `swarm::signatures`): nodes under
//! the identity layer, whose node keys sign, write through the API as a user would,
//! and every node comes to see the writes verified, by signatures that came with sync
//! messages or on their own.
use std::time::Duration;
use serde_json::json;
use super::{Sim, path};
use crate::document::ActorId;
use crate::document::signatures::{Sign, SignEvery};
use crate::document::store;
use crate::state::AppState;
use crate::swarm::bootstrap::Start;
const SOON: Duration = Duration::from_secs(20);
/// Writes `value` at `at` on `node` as a write through the API does, signed at once if
/// `signed` (`?signed=true`); the change's actor and seq.
async fn write_as_user(
node: &AppState,
at: &str,
value: serde_json::Value,
signed: bool,
) -> (ActorId, u64) {
let at = path(at);
let sign = Sign::Write {
path: at.clone(),
asked: signed,
};
node.mutate_doc_for(Some("alice".to_owned()), sign, move |doc| {
store::write_path(doc, &at, &value)
})
.await
.expect("write");
let replica = node.doc.read().await;
let change = replica.doc.get_last_local_change().expect("the write");
(change.actor_id().clone(), change.seq())
}
/// Whether a signature vouches for `actor`'s change `seq` on `node`.
async fn verified(node: &AppState, actor: &ActorId, seq: u64) -> bool {
node.doc.read().await.signatures.verified(actor, seq)
}
/// A write no rule signs is signed as a sync message takes it (`--sign-every sent`, the
/// default), and the signature reaches every node with it.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn writes_leave_signed() {
let sim = Sim::with_identity(61);
let a = sim.start("a", &[], Start::Genesis);
let b = sim.start("b", &["a"], Start::Join);
let c = sim.start("c", &["a"], Start::Join);
sim.wait_ready(&[&a, &b, &c], SOON).await;
let (actor, seq) = write_as_user(&b, "metrics/cpu", json!(42), false).await;
sim.wait_converged(&[&a, &b, &c], SOON).await;
for node in [&a, &b, &c] {
let actor = actor.clone();
sim.wait_until("the write is verified", SOON, move || {
let actor = actor.clone();
async move { verified(node, &actor, seq).await }
})
.await;
}
}
/// With `--sign-every none`, a write no rule signs stays unverified; the node's next
/// signed write vouches for it too, wherever its signature goes.
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn a_signed_write_vouches_for_the_writes_before() {
let sim = Sim::with_identity(62);
let a = sim.start("a", &[], Start::Genesis);
let b = sim.start_with("b", &["a"], Start::Join, |config| {
config.sign_every = SignEvery::Never;
});
let c = sim.start("c", &["a"], Start::Join);
sim.wait_ready(&[&a, &b, &c], SOON).await;
let (actor, first) = write_as_user(&b, "metrics/cpu", json!(42), false).await;
sim.wait_converged(&[&a, &b, &c], SOON).await;
// Signatures go within a second: none came for it. Unless b wrote protocol data
// since (always signed), whose signature vouches for it too.
tokio::time::sleep(Duration::from_secs(3)).await;
let latest = b
.doc
.read()
.await
.doc
.get_last_local_change()
.map(|change| change.seq());
if latest == Some(first) {
for node in [&a, &b, &c] {
assert!(!verified(node, &actor, first).await);
}
}
let (same, second) = write_as_user(&b, "config/mode", json!("fast"), true).await;
assert_eq!(same, actor);
sim.wait_converged(&[&a, &b, &c], SOON).await;
for node in [&a, &b, &c] {
let actor = actor.clone();
sim.wait_until("both writes are verified", SOON, move || {
let actor = actor.clone();
async move {
verified(node, &actor, first).await && verified(node, &actor, second).await
}
})
.await;
}
}
|