Skip to content

Commit e271d0b

Browse files
authored
feat(rust/hermes-ipfs): Construct .syn topic payload (#712)
* wip: implement syn payload Signed-off-by: bkioshn <[email protected]> * add doc, fix test, cleanup Signed-off-by: bkioshn <[email protected]> * cleanup Signed-off-by: bkioshn <[email protected]> * cleanup cargo toml Signed-off-by: bkioshn <[email protected]> * fix type and use ed pk and blake3 Signed-off-by: bkioshn <[email protected]> * fix syn payload syntax Signed-off-by: bkioshn <[email protected]> * mod payload to use blake3 type Signed-off-by: bkioshn <[email protected]> * add blake3 wrapper type Signed-off-by: bkioshn <[email protected]> * fix linter Signed-off-by: bkioshn <[email protected]> * use blake3256 type for state machine Signed-off-by: bkioshn <[email protected]> * generalize cbor decode and encode for syn payload + fix some logic Signed-off-by: bkioshn <[email protected]> * fix logic Signed-off-by: bkioshn <[email protected]> * fix doc Signed-off-by: bkioshn <[email protected]> --------- Signed-off-by: bkioshn <[email protected]>
1 parent 4baaea1 commit e271d0b

File tree

6 files changed

+424
-30
lines changed

6 files changed

+424
-30
lines changed

rust/hermes-ipfs/Cargo.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# cspell: words yamux
2-
31
[package]
42
name = "hermes-ipfs"
53
version = "0.0.9"
@@ -28,7 +26,7 @@ futures = "0.3.31"
2826
libp2p = "0.56.0"
2927
connexa = { version = "0.4.1", features = ["full"] }
3028
minicbor = { version = "0.25.1", features = ["alloc", "derive", "half"], optional = true }
31-
ed25519-dalek = { version = "2.1.1", optional = true}
29+
ed25519-dalek = { version = "2.1.1", optional = true }
3230
catalyst-types = { version = "0.0.11", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "catalyst-types/v0.0.11", optional = true }
3331
tracing = "0.1.43"
3432
rand = "0.9.2"

rust/hermes-ipfs/src/doc_sync/envelope.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ impl<C> Encode<C> for EnvelopePayload {
136136
ctx: &mut C,
137137
) -> Result<(), minicbor::encode::Error<W::Error>> {
138138
e.array(4)?;
139-
e.encode_with(&self.peer, ctx)?
139+
e.encode_with(self.peer, ctx)?
140140
.encode_with(self.seq, &mut CborContext::Tagged)?
141141
.u64(self.ver)?;
142142
<W as Write>::write_all(e.writer_mut(), &self.payload)
@@ -214,7 +214,7 @@ impl<C> Encode<C> for SignedPayloadView<'_> {
214214
_ctx: &mut C,
215215
) -> Result<(), minicbor::encode::Error<W::Error>> {
216216
e.array(5)?;
217-
e.encode(&self.payload.peer)?;
217+
e.encode(self.payload.peer)?;
218218
e.encode_with(self.payload.seq, &mut CborContext::Tagged)?;
219219
e.u64(self.payload.ver)?;
220220
e.writer_mut()
@@ -424,7 +424,7 @@ mod tests {
424424

425425
let mut signed = Encoder::new(Vec::new());
426426
signed.array(5).unwrap();
427-
signed.encode(&payload.peer).unwrap();
427+
signed.encode(payload.peer).unwrap();
428428
signed
429429
.encode_with(payload.seq, &mut CborContext::Tagged)
430430
.unwrap();
@@ -508,7 +508,7 @@ mod tests {
508508
// Construct a fake array of length 4 instead of 5
509509
let mut bad_array = Encoder::new(Vec::new());
510510
bad_array.array(4).unwrap(); // Wrong length
511-
bad_array.encode(&payload.peer).unwrap();
511+
bad_array.encode(payload.peer).unwrap();
512512
bad_array
513513
.encode_with(payload.seq, &mut CborContext::Tagged)
514514
.unwrap();

rust/hermes-ipfs/src/doc_sync/mod.rs

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ mod envelope;
44
mod state_machine;
55

66
pub mod payload;
7+
pub mod syn_payload;
78
pub mod timers;
89

910
use ed25519_dalek::VerifyingKey;
1011
pub use envelope::{Envelope, EnvelopePayload};
11-
use minicbor::{Decode, Encode, Encoder, encode::Write};
12+
use minicbor::{Decode, Encode, Encoder, decode, encode::Write};
1213
pub use state_machine::{StateMachine, StateSnapshot, SyncState};
1314

1415
/// Current document synchronization protocol version.
@@ -36,8 +37,8 @@ fn validate_cid(cid: &crate::Cid) -> bool {
3637

3738
/// Ed25519 public key instance.
3839
/// Wrapper over `ed25519_dalek::VerifyingKey`.
39-
#[derive(Clone, Debug, PartialEq, Eq)]
40-
struct PublicKey(VerifyingKey);
40+
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
41+
pub struct PublicKey(VerifyingKey);
4142

4243
impl<C> Encode<C> for PublicKey {
4344
fn encode<W: Write>(
@@ -63,6 +64,15 @@ impl<'b, C> Decode<'b, C> for PublicKey {
6364
}
6465
}
6566

67+
impl TryFrom<[u8; 32]> for PublicKey {
68+
type Error = ed25519_dalek::SignatureError;
69+
70+
fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
71+
let vk = VerifyingKey::from_bytes(&bytes)?;
72+
Ok(PublicKey(vk))
73+
}
74+
}
75+
6676
/// Ed25519 signature instance.
6777
/// Wrapper over `ed25519_dalek::Signature`.
6878
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -91,3 +101,38 @@ impl<'b, C> Decode<'b, C> for Signature {
91101
.map(Signature)
92102
}
93103
}
104+
105+
/// Blake3-256 hash instance.
106+
/// Wrapper over `blake3::Hash`
107+
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
108+
pub struct Blake3256(blake3::Hash);
109+
110+
impl<C> Encode<C> for Blake3256 {
111+
fn encode<W: Write>(
112+
&self,
113+
e: &mut Encoder<W>,
114+
_ctx: &mut C,
115+
) -> Result<(), minicbor::encode::Error<W::Error>> {
116+
e.bytes(self.0.as_bytes())?;
117+
Ok(())
118+
}
119+
}
120+
121+
impl<'b, C> Decode<'b, C> for Blake3256 {
122+
fn decode(
123+
d: &mut minicbor::Decoder<'b>,
124+
_ctx: &mut C,
125+
) -> Result<Self, minicbor::decode::Error> {
126+
let b: [u8; 32] = d
127+
.bytes()?
128+
.try_into()
129+
.map_err(|_| decode::Error::message("Invalid Blake3256 length").at(d.position()))?;
130+
Ok(Blake3256(blake3::Hash::from(b)))
131+
}
132+
}
133+
134+
impl From<[u8; 32]> for Blake3256 {
135+
fn from(bytes: [u8; 32]) -> Self {
136+
Blake3256(blake3::Hash::from(bytes))
137+
}
138+
}

rust/hermes-ipfs/src/doc_sync/payload.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use minicbor::{
77
encode::{self, Write},
88
};
99

10-
use crate::Cid;
10+
use crate::{Cid, doc_sync::Blake3256};
1111

12-
/// Encoding wrapper over [`hermes_ipfs::Cid`].
12+
/// Encoding wrapper over [`ipld_core::cid::Cid`].
1313
#[derive(Copy, Clone, TransparentWrapper)]
1414
#[repr(transparent)]
1515
struct CborCid(Cid);
@@ -83,14 +83,11 @@ impl<C> Decode<'_, C> for NumericKeys {
8383
}
8484
}
8585

86-
/// [`CommonFields::root`] value alias.
87-
pub type RootHash = [u8; 32];
88-
8986
/// Common fields of `.diff` and `.new` message payload maps.
90-
#[derive(Copy, Clone, Default)]
87+
#[derive(Copy, Clone)]
9188
pub struct CommonFields {
9289
/// Root of the senders SMT with these docs added.
93-
pub root: RootHash,
90+
pub root: Blake3256,
9491
/// Count of the number of docs in the senders.
9592
pub count: u64,
9693
/// Included if this is a reply to a `.syn` topic.
@@ -109,10 +106,10 @@ impl CommonFields {
109106

110107
/// Encodes [`CommonFields::root`] key-value pair.
111108
fn encode_root<W: Write>(
112-
root: &RootHash,
109+
root: &Blake3256,
113110
e: &mut Encoder<W>,
114111
) -> Result<(), encode::Error<W::Error>> {
115-
e.encode(NumericKeys::Root)?.bytes(root)?.ok()
112+
e.encode(NumericKeys::Root)?.encode(root)?.ok()
116113
}
117114

118115
/// Encodes [`CommonFields::count`] key-value pair.
@@ -138,13 +135,11 @@ fn encode_in_reply_to<W: Write>(
138135
}
139136

140137
/// Decodes [`CommonFields::root`] key-value pair.
141-
fn decode_root(d: &mut Decoder<'_>) -> Result<RootHash, decode::Error> {
138+
fn decode_root(d: &mut Decoder<'_>) -> Result<Blake3256, decode::Error> {
142139
if d.decode::<NumericKeys>()
143140
.is_ok_and(|key| matches!(key, NumericKeys::Root))
144141
{
145-
d.bytes()?
146-
.try_into()
147-
.map_err(|err| decode::Error::custom(err).at(d.position()))
142+
d.decode()
148143
} else {
149144
Err(decode::Error::message("Expected `root` key").at(d.position()))
150145
}
@@ -607,7 +602,7 @@ mod tests {
607602
/// Constructs a body from its fields.
608603
pub fn to_cbor(fields: &[fn() -> &'static [Token<'static>]]) -> anyhow::Result<Vec<u8>> {
609604
let mut buf = minicbor::Encoder::new(vec![]);
610-
buf.map(u64::try_from(fields.iter().count())?)?;
605+
buf.map(u64::try_from(fields.len())?)?;
611606
fields
612607
.iter()
613608
.flat_map(|f| f())

rust/hermes-ipfs/src/doc_sync/state_machine.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use std::{
77

88
use tokio::sync::oneshot::error::TryRecvError;
99

10+
use crate::doc_sync::Blake3256;
11+
1012
/// State machine that tracks peer parity for the Doc Sync protocol.
1113
///
1214
/// Transitions follow the protocol spec:
@@ -40,17 +42,17 @@ pub enum StateSnapshot {
4042
#[derive(Clone, Debug, PartialEq, Eq)]
4143
pub struct SyncState {
4244
/// Local sparse merkle tree root.
43-
local: blake3::Hash,
45+
local: Blake3256,
4446
/// Remote sparse merkle tree root.
45-
remote: blake3::Hash,
47+
remote: Blake3256,
4648
}
4749

4850
impl SyncState {
4951
/// Creates a new synchronization view for comparison.
5052
#[must_use]
5153
pub fn new(
52-
local: blake3::Hash,
53-
remote: blake3::Hash,
54+
local: Blake3256,
55+
remote: Blake3256,
5456
) -> Self {
5557
Self { local, remote }
5658
}
@@ -202,8 +204,8 @@ mod tests {
202204
SyncState::new(sig_a, sig_b)
203205
}
204206

205-
fn hash_with(byte: u8) -> blake3::Hash {
206-
blake3::Hash::from_bytes([byte; 32])
207+
fn hash_with(byte: u8) -> Blake3256 {
208+
[byte; 32].into()
207209
}
208210

209211
#[tokio::test]

0 commit comments

Comments
 (0)