Skip to content

Commit

Permalink
Add full replication implementation for DA (#396)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
zeegomo authored Sep 13, 2023
1 parent 1eeeeb1 commit d72e54f
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
12 changes: 12 additions & 0 deletions nomos-da/full-replication/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"] }
178 changes: 178 additions & 0 deletions nomos-da/full-replication/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<CertificateStrategy> {
certificate_strategy: CertificateStrategy,
output_buffer: Vec<Bytes>,
attestations: Vec<Attestation>,
output_certificate_buf: Vec<Certificate>,
}

impl<S> FullReplication<S> {
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<Self::Attestation>) -> Certificate;
}

#[derive(Debug, Clone)]
pub struct AbsoluteNumber<A, C> {
num_attestations: usize,
_a: std::marker::PhantomData<A>,
_c: std::marker::PhantomData<C>,
}

impl<A, C> AbsoluteNumber<A, C> {
pub fn new(num_attestations: usize) -> Self {
Self {
num_attestations,
_a: std::marker::PhantomData,
_c: std::marker::PhantomData,
}
}
}

impl CertificateStrategy for AbsoluteNumber<Attestation, Certificate> {
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::<HashSet<_>>()
.len()
== 1
}

fn build(&self, attestations: Vec<Self::Attestation>) -> 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<Self> = hasher as BlobHasher<Self>;

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<Attestation>,
}

impl certificate::Certificate for Certificate {}

// TODO: add generic impl when the trait for Certificate is expanded
impl DaProtocol for FullReplication<AbsoluteNumber<Attestation, Certificate>> {
type Blob = Blob;
type Attestation = Attestation;
type Certificate = Certificate;

fn encode<T: AsRef<[u8]>>(&self, data: T) -> Vec<Self::Blob> {
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<Bytes> {
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::Certificate> {
self.output_certificate_buf.pop()
}

fn validate_certificate(&self, certificate: &Self::Certificate) -> bool {
self.certificate_strategy
.can_build(&certificate.attestations)
}
}

0 comments on commit d72e54f

Please sign in to comment.