Phase B: identifier+key on the wire, JCS envelope signing (FRX/0.5), registry binding
This commit is contained in:
+31
-23
@@ -7,7 +7,7 @@ use std::time::{Duration, Instant};
|
||||
|
||||
use common::{
|
||||
ask, challenge, client, collection, config_for, messages_of_type, poll, poll_messages, publish,
|
||||
query_envelope, register, spawn_relay, spawn_relay_with_capacity, unicast,
|
||||
query_envelope, register, spawn_relay, spawn_relay_with_capacity, test_envelope, unicast,
|
||||
};
|
||||
use frxd::crypto::Keypair;
|
||||
use frxd::index::LocalIndex;
|
||||
@@ -87,7 +87,7 @@ async fn raw_query(
|
||||
timeout_ms: u64,
|
||||
) -> Vec<Value> {
|
||||
register(http, relay_url, asker).await;
|
||||
let envelope = query_envelope(asker, text, 5);
|
||||
let envelope = query_envelope(asker, &asker.public_hex(), text, 5);
|
||||
assert!(
|
||||
publish(http, relay_url, &envelope)
|
||||
.await
|
||||
@@ -100,13 +100,14 @@ async fn raw_query(
|
||||
#[test]
|
||||
fn envelope_field_set_is_fixed() {
|
||||
let key = Keypair::generate();
|
||||
let envelope = Envelope::new(&key, TYPE_QUERY, json!({"qid": "q", "text": "t"}));
|
||||
let envelope = test_envelope(&key, TYPE_QUERY, json!({"qid": "q", "text": "t"}));
|
||||
let value = serde_json::to_value(&envelope).unwrap();
|
||||
assert_eq!(
|
||||
keys(&value),
|
||||
BTreeSet::from([
|
||||
"body".to_string(),
|
||||
"from".to_string(),
|
||||
"key".to_string(),
|
||||
"nonce".to_string(),
|
||||
"sig".to_string(),
|
||||
"ts".to_string(),
|
||||
@@ -337,7 +338,7 @@ async fn entities_are_optional_hints() {
|
||||
"entities": ["Q999999", "not-a-real-entity"],
|
||||
"budget": {"max_results": 5}
|
||||
});
|
||||
let envelope = Envelope::new(&alice, TYPE_QUERY, body);
|
||||
let envelope = test_envelope(&alice, TYPE_QUERY, body);
|
||||
assert!(
|
||||
publish(&http, &relay_url, &envelope)
|
||||
.await
|
||||
@@ -384,7 +385,7 @@ async fn member_directory_filters_senders() {
|
||||
assert_eq!(trusted.len(), 1, "trusted member got no response");
|
||||
|
||||
register(&http, &relay_url, &untrusted).await;
|
||||
let envelope = query_envelope(&untrusted, "rust", 5);
|
||||
let envelope = query_envelope(&untrusted, &untrusted.public_hex(), "rust", 5);
|
||||
assert!(
|
||||
publish(&http, &relay_url, &envelope)
|
||||
.await
|
||||
@@ -424,7 +425,7 @@ async fn rotated_keys_are_accepted_through_previous_listing() {
|
||||
|
||||
let http = client();
|
||||
register(&http, &relay_url, &old_key).await;
|
||||
let envelope = query_envelope(&old_key, "rust", 5);
|
||||
let envelope = query_envelope(&old_key, &old_key.public_hex(), "rust", 5);
|
||||
assert!(
|
||||
publish(&http, &relay_url, &envelope)
|
||||
.await
|
||||
@@ -436,7 +437,7 @@ async fn rotated_keys_are_accepted_through_previous_listing() {
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
frxd::config::save_members(&config.members_path(), &listing(Vec::new())).unwrap();
|
||||
let envelope = query_envelope(&old_key, "rust", 5);
|
||||
let envelope = query_envelope(&old_key, &old_key.public_hex(), "rust", 5);
|
||||
assert!(
|
||||
publish(&http, &relay_url, &envelope)
|
||||
.await
|
||||
@@ -453,7 +454,7 @@ async fn stale_envelopes_are_rejected() {
|
||||
let http = client();
|
||||
let key = Keypair::generate();
|
||||
|
||||
let mut envelope = query_envelope(&key, "stale", 5);
|
||||
let mut envelope = query_envelope(&key, &key.public_hex(), "stale", 5);
|
||||
envelope.ts = frxd::crypto::now_ts().saturating_sub(3600);
|
||||
envelope.sig = key.sign(&frxd::crypto::signing_bytes(&envelope));
|
||||
assert_eq!(
|
||||
@@ -461,7 +462,7 @@ async fn stale_envelopes_are_rejected() {
|
||||
reqwest::StatusCode::BAD_REQUEST
|
||||
);
|
||||
|
||||
let mut response = Envelope::new(
|
||||
let mut response = test_envelope(
|
||||
&key,
|
||||
TYPE_RESPONSE,
|
||||
json!({"qid": "q1", "results": [], "truncated": false, "more_available": 0, "cursor": null}),
|
||||
@@ -484,12 +485,12 @@ async fn backpressure_is_visible_and_recoverable() {
|
||||
let publisher = Keypair::generate();
|
||||
register(&http, &relay_url, &member).await;
|
||||
|
||||
let first = query_envelope(&publisher, "one", 5);
|
||||
let first = query_envelope(&publisher, &publisher.public_hex(), "one", 5);
|
||||
assert_eq!(
|
||||
publish(&http, &relay_url, &first).await.status(),
|
||||
reqwest::StatusCode::ACCEPTED
|
||||
);
|
||||
let second = query_envelope(&publisher, "two", 5);
|
||||
let second = query_envelope(&publisher, &publisher.public_hex(), "two", 5);
|
||||
let response = publish(&http, &relay_url, &second).await;
|
||||
assert_eq!(response.status(), reqwest::StatusCode::TOO_MANY_REQUESTS);
|
||||
assert_eq!(
|
||||
@@ -511,7 +512,7 @@ async fn backpressure_is_visible_and_recoverable() {
|
||||
1
|
||||
);
|
||||
|
||||
let third = query_envelope(&publisher, "three", 5);
|
||||
let third = query_envelope(&publisher, &publisher.public_hex(), "three", 5);
|
||||
assert_eq!(
|
||||
publish(&http, &relay_url, &third).await.status(),
|
||||
reqwest::StatusCode::ACCEPTED
|
||||
@@ -527,7 +528,7 @@ async fn unicast_is_need_to_know() {
|
||||
register(&http, &relay_url, &alice).await;
|
||||
register(&http, &relay_url, &bob).await;
|
||||
|
||||
let query = query_envelope(&alice, "rust", 5);
|
||||
let query = query_envelope(&alice, &alice.public_hex(), "rust", 5);
|
||||
assert!(
|
||||
publish(&http, &relay_url, &query)
|
||||
.await
|
||||
@@ -535,7 +536,7 @@ async fn unicast_is_need_to_know() {
|
||||
.is_success()
|
||||
);
|
||||
|
||||
let response = Envelope::new(
|
||||
let response = test_envelope(
|
||||
&bob,
|
||||
TYPE_RESPONSE,
|
||||
json!({"qid": "q1", "results": [], "truncated": false, "more_available": 0, "cursor": null}),
|
||||
@@ -561,7 +562,7 @@ async fn unicast_to_unknown_member_is_visible() {
|
||||
let relay_url = spawn_relay().await;
|
||||
let http = client();
|
||||
let sender = Keypair::generate();
|
||||
let response = Envelope::new(
|
||||
let response = test_envelope(
|
||||
&sender,
|
||||
TYPE_RESPONSE,
|
||||
json!({"qid": "q1", "results": [], "truncated": false, "more_available": 0, "cursor": null}),
|
||||
@@ -580,12 +581,12 @@ async fn tampered_envelopes_are_rejected() {
|
||||
let relay_url = spawn_relay().await;
|
||||
let http = client();
|
||||
let key = Keypair::generate();
|
||||
let mut envelope = query_envelope(&key, "rust", 5);
|
||||
let mut envelope = query_envelope(&key, &key.public_hex(), "rust", 5);
|
||||
envelope.body = json!({"qid": "q1", "text": "forged", "budget": {"max_results": 5}});
|
||||
let publish_response = publish(&http, &relay_url, &envelope).await;
|
||||
assert_eq!(publish_response.status(), reqwest::StatusCode::BAD_REQUEST);
|
||||
|
||||
let mut response = Envelope::new(
|
||||
let mut response = test_envelope(
|
||||
&key,
|
||||
TYPE_RESPONSE,
|
||||
json!({"qid": "q1", "results": [], "truncated": false, "more_available": 0, "cursor": null}),
|
||||
@@ -854,7 +855,7 @@ async fn aggregates_are_unicast_only_and_ignored_by_nodes() {
|
||||
let sender = Keypair::generate();
|
||||
register(&http, &relay_url, &bob.node.key).await;
|
||||
|
||||
let broadcast = Envelope::new(
|
||||
let broadcast = test_envelope(
|
||||
&sender,
|
||||
TYPE_AGGREGATE,
|
||||
json!({"period": "2026-03", "sent": 1, "passed": 0}),
|
||||
@@ -865,7 +866,7 @@ async fn aggregates_are_unicast_only_and_ignored_by_nodes() {
|
||||
"aggregates must not travel the broadcast channel"
|
||||
);
|
||||
|
||||
let unicast_envelope = Envelope::new(
|
||||
let unicast_envelope = test_envelope(
|
||||
&sender,
|
||||
TYPE_AGGREGATE,
|
||||
json!({"period": "2026-03", "sent": 1, "passed": 0}),
|
||||
@@ -927,7 +928,7 @@ async fn duplicate_delivery_is_answered_once() {
|
||||
let alice = Keypair::generate();
|
||||
let http = client();
|
||||
register(&http, &relay_url, &alice).await;
|
||||
let envelope = query_envelope(&alice, "rust", 5);
|
||||
let envelope = query_envelope(&alice, &alice.public_hex(), "rust", 5);
|
||||
assert!(
|
||||
publish(&http, &relay_url, &envelope)
|
||||
.await
|
||||
@@ -1000,7 +1001,8 @@ async fn multiple_responders_merge_with_provenance() {
|
||||
fn canonical_signing_bytes_are_stable() {
|
||||
let mut envelope = Envelope {
|
||||
msg_type: TYPE_QUERY.to_string(),
|
||||
from: "aa".repeat(32),
|
||||
from: "alice.frx.example".to_string(),
|
||||
key: "aa".repeat(32),
|
||||
ts: 1_700_000_000,
|
||||
nonce: "00112233445566778899aabbccddeeff".to_string(),
|
||||
body: json!({"text": "rust", "qid": "q1", "budget": {"max_results": 5}, "entities": []}),
|
||||
@@ -1011,9 +1013,15 @@ fn canonical_signing_bytes_are_stable() {
|
||||
json!({"entities": [], "budget": {"max_results": 5}, "qid": "q1", "text": "rust"});
|
||||
let second = frxd::crypto::signing_bytes(&envelope);
|
||||
assert_eq!(first, second);
|
||||
let key = frxd::crypto::Keypair::from_hex(&"00".repeat(32)).unwrap();
|
||||
let signature = key.sign(&first);
|
||||
assert_eq!(
|
||||
String::from_utf8(first).unwrap(),
|
||||
"FRX/0.4\nquery\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n1700000000\n00112233445566778899aabbccddeeff\n{\"budget\":{\"max_results\":5},\"entities\":[],\"qid\":\"q1\",\"text\":\"rust\"}"
|
||||
"FRX/0.5\n{\"body\":{\"budget\":{\"max_results\":5},\"entities\":[],\"qid\":\"q1\",\"text\":\"rust\"},\"from\":\"alice.frx.example\",\"key\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"nonce\":\"00112233445566778899aabbccddeeff\",\"ts\":1700000000,\"type\":\"query\"}"
|
||||
);
|
||||
assert_eq!(
|
||||
signature,
|
||||
"2d5df0f85b5ea6b97d5dbc53d316b410c848d52e407583f9740d10889ba5a40fbcf03a930fa7d351689aa657884e3a32e0c6941ff11653a71d75dea58b3e9b09"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1033,7 +1041,7 @@ async fn query_body_without_entities_parses() {
|
||||
let alice = Keypair::generate();
|
||||
let http = client();
|
||||
register(&http, &relay_url, &alice).await;
|
||||
let envelope = Envelope::new(
|
||||
let envelope = test_envelope(
|
||||
&alice,
|
||||
TYPE_QUERY,
|
||||
json!({"qid": "min", "text": "rust", "budget": {"max_results": 5}}),
|
||||
|
||||
Reference in New Issue
Block a user