From d72e54f9be37c2dc38628bf78e3690438b5f8b6b Mon Sep 17 00:00:00 2001 From: Giacomo Pasini Date: Wed, 13 Sep 2023 11:01:20 +0200 Subject: [PATCH] Add full replication implementation for DA (#396) Add an initial simple but functional implementation for a data availability protocol. Full replication simply encodes bytes in a single blob which is replicated in all nodes. --- Cargo.toml | 1 + nomos-da/full-replication/Cargo.toml | 12 ++ nomos-da/full-replication/src/lib.rs | 178 +++++++++++++++++++++++++++ 3 files changed, 191 insertions(+) create mode 100644 nomos-da/full-replication/Cargo.toml create mode 100644 nomos-da/full-replication/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 09d907a4..b41a12b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "nomos-services/data-availability", "nomos-da/reed-solomon", "nomos-da/kzg", + "nomos-da/full-replication", "nodes/nomos-node", "simulations", "consensus-engine", diff --git a/nomos-da/full-replication/Cargo.toml b/nomos-da/full-replication/Cargo.toml new file mode 100644 index 00000000..207a1582 --- /dev/null +++ b/nomos-da/full-replication/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "full-replication" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +blake2 = { version = "0.10" } +bytes = { versino = "1.3", features = ["serde"] } +nomos-core = { path = "../../nomos-core" } +serde = { version = "1.0", features = ["derive"] } \ No newline at end of file diff --git a/nomos-da/full-replication/src/lib.rs b/nomos-da/full-replication/src/lib.rs new file mode 100644 index 00000000..ad712f1d --- /dev/null +++ b/nomos-da/full-replication/src/lib.rs @@ -0,0 +1,178 @@ +// internal +use nomos_core::da::{ + attestation, + blob::{self, BlobHasher}, + certificate, DaProtocol, +}; +// std +use std::collections::HashSet; +// crates +use blake2::{ + digest::{Update, VariableOutput}, + Blake2bVar, +}; +use bytes::Bytes; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone)] +pub struct FullReplication { + certificate_strategy: CertificateStrategy, + output_buffer: Vec, + attestations: Vec, + output_certificate_buf: Vec, +} + +impl FullReplication { + pub fn new(strategy: S) -> Self { + Self { + certificate_strategy: strategy, + output_buffer: Vec::new(), + attestations: Vec::new(), + output_certificate_buf: Vec::new(), + } + } +} + +// TODO: maybe abstract in a general library? +trait CertificateStrategy { + type Attestation: attestation::Attestation; + type Certificate: certificate::Certificate; + + fn can_build(&self, attestations: &[Self::Attestation]) -> bool; + fn build(&self, attestations: Vec) -> Certificate; +} + +#[derive(Debug, Clone)] +pub struct AbsoluteNumber { + num_attestations: usize, + _a: std::marker::PhantomData, + _c: std::marker::PhantomData, +} + +impl AbsoluteNumber { + pub fn new(num_attestations: usize) -> Self { + Self { + num_attestations, + _a: std::marker::PhantomData, + _c: std::marker::PhantomData, + } + } +} + +impl CertificateStrategy for AbsoluteNumber { + type Attestation = Attestation; + type Certificate = Certificate; + + fn can_build(&self, attestations: &[Self::Attestation]) -> bool { + attestations.len() >= self.num_attestations + && attestations + .iter() + .map(|a| &a.blob) + .collect::>() + .len() + == 1 + } + + fn build(&self, attestations: Vec) -> Certificate { + assert!(self.can_build(&attestations)); + Certificate { attestations } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] + +pub struct Blob { + data: Bytes, +} + +fn hasher(blob: &Blob) -> [u8; 32] { + let mut hasher = Blake2bVar::new(32).unwrap(); + hasher.update(&blob.data); + let mut output = [0; 32]; + hasher.finalize_variable(&mut output).unwrap(); + output +} + +impl blob::Blob for Blob { + type Hash = [u8; 32]; + const HASHER: BlobHasher = hasher as BlobHasher; + + fn as_bytes(&self) -> bytes::Bytes { + self.data.clone() + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Attestation { + blob: [u8; 32], + voter: [u8; 32], +} + +impl attestation::Attestation for Attestation { + type Blob = Blob; + fn blob(&self) -> [u8; 32] { + self.blob + } + fn as_bytes(&self) -> Bytes { + Bytes::from([self.blob.as_ref(), self.voter.as_ref()].concat()) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Certificate { + attestations: Vec, +} + +impl certificate::Certificate for Certificate {} + +// TODO: add generic impl when the trait for Certificate is expanded +impl DaProtocol for FullReplication> { + type Blob = Blob; + type Attestation = Attestation; + type Certificate = Certificate; + + fn encode>(&self, data: T) -> Vec { + vec![Blob { + data: Bytes::copy_from_slice(data.as_ref()), + }] + } + + fn recv_blob(&mut self, blob: Self::Blob) { + self.output_buffer.push(blob.data); + } + + fn extract(&mut self) -> Option { + self.output_buffer.pop() + } + + fn attest(&self, blob: &Self::Blob) -> Self::Attestation { + Attestation { + blob: hasher(blob), + // TODO: voter id? + voter: [0; 32], + } + } + + fn validate_attestation(&self, blob: &Self::Blob, attestation: &Self::Attestation) -> bool { + hasher(blob) == attestation.blob + } + + fn recv_attestation(&mut self, attestation: Self::Attestation) { + self.attestations.push(attestation); + if self.certificate_strategy.can_build(&self.attestations) { + self.output_certificate_buf.push( + self.certificate_strategy + .build(std::mem::take(&mut self.attestations)), + ); + } + } + + fn certify_dispersal(&mut self) -> Option { + self.output_certificate_buf.pop() + } + + fn validate_certificate(&self, certificate: &Self::Certificate) -> bool { + self.certificate_strategy + .can_build(&certificate.attestations) + } +}