Skip to content
Closed
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
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ant-protocol"
version = "2.3.0"
version = "3.0.0"
edition = "2021"
authors = ["MaidSafe Developers <dev@maidsafe.net>"]
description = "Wire protocol for the Autonomi decentralized network (WithAutonomi fork)"
Expand Down
72 changes: 65 additions & 7 deletions src/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,17 @@ use bytes::Bytes;
use serde::{Deserialize, Serialize};

/// Protocol identifier for chunk operations.
pub const CHUNK_PROTOCOL_ID: &str = "autonomi.ant.chunk.v1";
///
/// Bumped to `v2` for ADR-0005: the quote request gains a required
/// `report_nonce` and the quote response a required `audit_report` field.
/// Because `ChunkMessage` is postcard-encoded (non-self-describing), old and
/// new peers cannot interoperate — a distinct protocol id makes the cutover
/// explicit so a `v1` peer and a `v2` peer never negotiate a chunk stream.
pub const CHUNK_PROTOCOL_ID: &str = "autonomi.ant.chunk.v2";

/// Current protocol version.
pub const PROTOCOL_VERSION: u16 = 1;
/// Current protocol version. Bumped to 2 for the ADR-0005 wire change
/// (see [`CHUNK_PROTOCOL_ID`]).
pub const PROTOCOL_VERSION: u16 = 2;

/// Maximum chunk size in bytes (4MB).
pub const MAX_CHUNK_SIZE: usize = 4 * 1024 * 1024;
Expand Down Expand Up @@ -236,16 +243,22 @@ pub struct ChunkQuoteRequest {
pub data_size: u64,
/// Data type identifier (0 for chunks).
pub data_type: u32,
/// ADR-0005: client-generated nonce echoed (and signed) by the responder's
/// audit report, forcing the report to be generated fresh for this request
/// — it cannot be precomputed or replayed. Postcard-encoded, so this is a
/// hard-cutover field (see [`ChunkQuoteResponse::Success::commitment`]).
pub report_nonce: [u8; 32],
}

impl ChunkQuoteRequest {
/// Create a new quote request.
#[must_use]
pub fn new(address: XorName, data_size: u64) -> Self {
pub fn new(address: XorName, data_size: u64, report_nonce: [u8; 32]) -> Self {
Self {
address,
data_size,
data_type: DATA_TYPE_CHUNK,
report_nonce,
}
}
}
Expand Down Expand Up @@ -282,6 +295,15 @@ pub enum ChunkQuoteResponse {
/// which ARE rmp-encoded, where tail `serde(default)` is decode-compatible.)
#[serde(default)]
commitment: Option<Vec<u8>>,
/// ADR-0005: the responder's serialized signed [`crate::payment::AuditReport`]
/// — its raw audit tally for the peers it observes near this address,
/// bound to the request's `report_nonce`. The client aggregates a
/// quorum of these to decide reward eligibility at collection time;
/// reports are never forwarded in payment bundles. `None` while the
/// node has nothing to report. Opaque bytes, hard-cutover like the
/// `commitment` field above.
#[serde(default)]
audit_report: Option<Vec<u8>>,
},
/// Quote generation failed.
Error(ProtocolError),
Expand All @@ -305,6 +327,30 @@ pub struct MerkleCandidateQuoteRequest {
pub data_size: u64,
/// Client-provided merkle payment timestamp (unix seconds).
pub merkle_payment_timestamp: u64,
/// ADR-0005: client-generated nonce echoed (and signed) by the responder's
/// audit report — same semantics as [`ChunkQuoteRequest::report_nonce`].
pub report_nonce: [u8; 32],
}

impl MerkleCandidateQuoteRequest {
/// Create a new merkle candidate quote request. Mirrors
/// [`ChunkQuoteRequest::new`] so both request types have a constructor
/// rather than only the single-node one.
#[must_use]
pub fn new(
address: XorName,
data_size: u64,
merkle_payment_timestamp: u64,
report_nonce: [u8; 32],
) -> Self {
Self {
address,
data_type: DATA_TYPE_CHUNK,
data_size,
merkle_payment_timestamp,
report_nonce,
}
}
}

/// Response with a merkle candidate quote.
Expand All @@ -329,6 +375,12 @@ pub enum MerkleCandidateQuoteResponse {
/// this is a hard-cutover field, not an interop guarantee.
#[serde(default)]
commitment: Option<Vec<u8>>,
/// ADR-0005: the responder's serialized signed [`crate::payment::AuditReport`],
/// bound to the request's `report_nonce` — same semantics as
/// [`ChunkQuoteResponse::Success::audit_report`]. Consumed at
/// collection time only; never enters the merkle payment bundle.
#[serde(default)]
audit_report: Option<Vec<u8>>,
},
/// Quote generation failed.
Error(ProtocolError),
Expand Down Expand Up @@ -523,7 +575,7 @@ mod tests {
#[test]
fn test_quote_request_encode_decode() {
let address = [0x34; 32];
let request = ChunkQuoteRequest::new(address, 1024);
let request = ChunkQuoteRequest::new(address, 1024, [0x77; 32]);
let msg = ChunkMessage {
request_id: 1,
body: ChunkMessageBody::QuoteRequest(request),
Expand All @@ -537,6 +589,7 @@ mod tests {
assert_eq!(req.address, address);
assert_eq!(req.data_size, 1024);
assert_eq!(req.data_type, DATA_TYPE_CHUNK);
assert_eq!(req.report_nonce, [0x77; 32]);
} else {
panic!("expected QuoteRequest");
}
Expand Down Expand Up @@ -580,8 +633,8 @@ mod tests {

#[test]
fn test_constants() {
assert_eq!(CHUNK_PROTOCOL_ID, "autonomi.ant.chunk.v1");
assert_eq!(PROTOCOL_VERSION, 1);
assert_eq!(CHUNK_PROTOCOL_ID, "autonomi.ant.chunk.v2");
assert_eq!(PROTOCOL_VERSION, 2);
assert_eq!(MAX_CHUNK_SIZE, 4 * 1024 * 1024);
assert_eq!(DATA_TYPE_CHUNK, 0);
}
Expand All @@ -604,6 +657,7 @@ mod tests {
data_type: DATA_TYPE_CHUNK,
data_size: 2048,
merkle_payment_timestamp: 1_700_000_000,
report_nonce: [0x88; 32],
};
let msg = ChunkMessage {
request_id: 500,
Expand All @@ -619,6 +673,7 @@ mod tests {
assert_eq!(req.data_type, DATA_TYPE_CHUNK);
assert_eq!(req.data_size, 2048);
assert_eq!(req.merkle_payment_timestamp, 1_700_000_000);
assert_eq!(req.report_nonce, [0x88; 32]);
} else {
panic!("expected MerkleCandidateQuoteRequest");
}
Expand All @@ -630,6 +685,7 @@ mod tests {
let response = MerkleCandidateQuoteResponse::Success {
candidate_node: candidate_node_bytes.clone(),
commitment: Some(vec![0x11, 0x22]),
audit_report: Some(vec![0x33, 0x44]),
};
let msg = ChunkMessage {
request_id: 501,
Expand All @@ -644,11 +700,13 @@ mod tests {
MerkleCandidateQuoteResponse::Success {
candidate_node,
commitment,
audit_report,
},
) = decoded.body
{
assert_eq!(candidate_node, candidate_node_bytes);
assert_eq!(commitment, Some(vec![0x11, 0x22]));
assert_eq!(audit_report, Some(vec![0x33, 0x44]));
} else {
panic!("expected MerkleCandidateQuoteResponse::Success");
}
Expand Down
Loading
Loading