Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5ba1d2a
feat: update protos from fibre
mcrakhman Mar 5, 2026
ad4bddc
feat: implement fibre
mcrakhman Mar 8, 2026
f3b7984
feat: wasm support
mcrakhman Mar 8, 2026
5cca018
feat: replace grpc contents
mcrakhman Mar 9, 2026
753e5f2
fix: use correct urls
mcrakhman Mar 9, 2026
8f07565
fix: remove double hashing
mcrakhman Mar 9, 2026
4b631db
fix: correct timestamp marshaling
mcrakhman Mar 9, 2026
580a083
fix: correctly randomize vals
mcrakhman Mar 9, 2026
a113c38
fix: validator proto signing
mcrakhman Mar 9, 2026
be3ace0
fix: map to correct inidices
mcrakhman Mar 9, 2026
9d84e33
refactor: different stuff
mcrakhman Mar 10, 2026
d2babaa
Merge branch 'main' into mcrakhman/fibre
mcrakhman Mar 10, 2026
26a13fd
feat: encode in place
mcrakhman Mar 10, 2026
27ee926
refactor: remove encode to rows
mcrakhman Mar 10, 2026
50993a0
feat: refactor fibre to use grpc client
mcrakhman Mar 10, 2026
2a5bab0
feat: remove some allocs on download
mcrakhman Mar 12, 2026
8de6275
refactor: split into folders
mcrakhman Mar 13, 2026
beeac72
refactor: download and upload logic
mcrakhman Mar 13, 2026
d10c036
feat: update payment promise signing
mcrakhman Mar 14, 2026
2230b06
Merge branch 'main' into mcrakhman/fibre
mcrakhman Mar 14, 2026
8e66964
feat: remove signing key
mcrakhman Mar 15, 2026
a346430
feat: proof in separate routine
mcrakhman Mar 15, 2026
f6fecbd
feat: use task
mcrakhman Mar 15, 2026
e904111
fix: comments
mcrakhman Mar 15, 2026
69275de
fix: fmt
mcrakhman Mar 15, 2026
196e1ab
fix: ci
mcrakhman Mar 15, 2026
f180611
fix: ci and docs
mcrakhman Mar 15, 2026
91e858f
fix: review
mcrakhman Mar 15, 2026
d3f619c
fix: fmt
mcrakhman Mar 15, 2026
c223f85
fix: review
mcrakhman Mar 16, 2026
378da09
fix: continue uploading stuff
mcrakhman Mar 16, 2026
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
35 changes: 35 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ resolver = "2"
members = [
"cli",
"client",
"fibre",
"grpc",
"grpc/grpc-macros",
"node",
Expand Down Expand Up @@ -32,6 +33,8 @@ celestia-grpc = { version = "=1.0.0-rc.2", path = "grpc", default-features = fal
celestia-grpc-macros = { version = "=1.0.0-rc.2", path = "grpc/grpc-macros" }
celestia-rpc = { version = "=1.0.0-rc.2", path = "rpc", default-features = false }
celestia-types = { version = "=1.0.0-rc.2", path = "types", default-features = false }
celestia-fibre = { version = "=1.0.0-rc.2", path = "fibre" }
rsema1d = { version = "=1.0.0-rc.2", path = "rsema1d" }

anyhow = "1.0.40"
async-stream = "0.3.5"
Expand Down
1 change: 1 addition & 0 deletions client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ categories = [
]

[dependencies]
celestia-fibre.workspace = true
celestia-grpc.workspace = true
celestia-proto.workspace = true
celestia-rpc = { workspace = true, default-features = false }
Expand Down
48 changes: 47 additions & 1 deletion client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pub struct Client {
share: ShareApi,
fraud: FraudApi,
blobstream: BlobstreamApi,
fibre: Option<crate::fibre::FibreApi>,
}

pub(crate) struct ClientInner {
Expand All @@ -89,12 +90,31 @@ pub(crate) struct ClientInner {
}

/// A builder for [`Client`].
#[derive(Debug, Default)]
#[derive(Default)]
pub struct ClientBuilder {
rpc_url: Option<String>,
rpc_auth_token: Option<String>,
timeout: Option<Duration>,
grpc_builder: Option<GrpcClientBuilder>,
fibre_client: Option<celestia_fibre::FibreClient>,
}

impl fmt::Debug for ClientBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClientBuilder")
.field("rpc_url", &self.rpc_url)
.field(
"rpc_auth_token",
&self.rpc_auth_token.as_ref().map(|_| "***"),
)
.field("timeout", &self.timeout)
.field("grpc_builder", &self.grpc_builder)
.field(
"fibre_client",
&self.fibre_client.as_ref().map(|_| "FibreClient { .. }"),
)
.finish()
}
}

impl ClientInner {
Expand Down Expand Up @@ -168,6 +188,14 @@ impl Client {
pub fn fraud(&self) -> &FraudApi {
&self.fraud
}

/// Returns fibre API accessor.
///
/// Returns `None` if the client was not configured with a fibre client.
/// Use [`ClientBuilder::fibre_client()`] to provide one.
pub fn fibre(&self) -> Option<&crate::fibre::FibreApi> {
self.fibre.as_ref()
}
}

impl ClientBuilder {
Expand Down Expand Up @@ -295,6 +323,19 @@ impl ClientBuilder {
self
}

/// Set a pre-built [`FibreClient`](celestia_fibre::FibreClient) for the Fibre
/// data availability protocol.
///
/// When set, the client exposes a [`FibreApi`](crate::fibre::FibreApi) via
/// [`Client::fibre()`].
///
/// Use [`FibreClient::from_endpoint()`](celestia_fibre::FibreClient::from_endpoint) for
/// convenient construction from a gRPC endpoint URL.
pub fn fibre_client(mut self, fibre_client: celestia_fibre::FibreClient) -> Self {
self.fibre_client = Some(fibre_client);
self
}

/// Build [`Client`].
pub async fn build(self) -> Result<Client> {
let rpc_url = self.rpc_url.as_ref().ok_or(Error::RpcEndpointNotSet)?;
Expand Down Expand Up @@ -329,6 +370,10 @@ impl ClientBuilder {
chain_id: head.chain_id().to_owned(),
});

let fibre = self
.fibre_client
.map(|fc| crate::fibre::FibreApi::new(inner.clone(), fc));

Ok(Client {
inner: inner.clone(),
blob: BlobApi::new(inner.clone()),
Expand All @@ -337,6 +382,7 @@ impl ClientBuilder {
fraud: FraudApi::new(inner.clone()),
blobstream: BlobstreamApi::new(inner.clone()),
state: StateApi::new(inner.clone()),
fibre,
})
}
}
Expand Down
92 changes: 92 additions & 0 deletions client/src/fibre.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! Fibre API for off-chain data availability.
//!
//! The [`FibreApi`] wraps a [`celestia_fibre::FibreClient`] to provide
//! `put()` and `download()` operations through the celestia-client.

use std::sync::Arc;

use celestia_fibre::{Blob, BlobID, FibreClient, FibreError, PreparedPut};
use celestia_grpc::{SubmittedTx, TxConfig};
use k256::ecdsa::SigningKey;

use crate::Error;
use crate::client::ClientInner;

/// Fibre API for off-chain data availability.
///
/// Provides access to the Fibre DA protocol for uploading and downloading
/// blobs directly to/from validators, bypassing on-chain blob submission.
pub struct FibreApi {
inner: Arc<ClientInner>,
fibre_client: Arc<FibreClient>,
}

impl FibreApi {
pub(crate) fn new(inner: Arc<ClientInner>, fibre_client: FibreClient) -> Self {
Self {
inner,
fibre_client: Arc::new(fibre_client),
}
}

/// Upload data and broadcast `MsgPayForFibre` on-chain.
///
/// Encodes the data into a blob, distributes it to validators via the
/// Fibre protocol, collects signatures, and broadcasts a `MsgPayForFibre`
/// transaction. Returns the prepared put data and a [`SubmittedTx`] handle
/// that can be used to confirm the transaction.
///
/// Requires the client to have a gRPC endpoint and signer configured.
pub async fn put(
&self,
signing_key: &SigningKey,
namespace: &[u8],
data: &[u8],
) -> Result<(PreparedPut, SubmittedTx), Error> {
let grpc = self.inner.grpc()?;
let signer_address = grpc
.get_account_address()
.ok_or(Error::NoAssociatedAddress)?;

let prepared = self
.fibre_client
.put(signing_key, namespace, data, &signer_address.to_string())
.await
.map_err(fibre_err)?;

let submitted = grpc
.broadcast_message(prepared.msg.clone(), TxConfig::default())
.await?;

Ok((prepared, submitted))
}

/// Download and reconstruct a blob by its [`BlobID`].
///
/// Fetches row proofs from validators and reconstructs the original data
/// using erasure coding.
pub async fn download(&self, id: &BlobID) -> Result<Blob, FibreError> {
self.fibre_client.download(id).await
}

/// Returns `true` if the underlying fibre client has been closed.
pub fn is_closed(&self) -> bool {
self.fibre_client.is_closed()
}

/// Close the underlying fibre client.
pub fn close(&self) {
self.fibre_client.close();
}
}

/// Convert a [`FibreError`] into the client [`Error`] type.
fn fibre_err(e: FibreError) -> Error {
match e {
FibreError::Grpc(status) => Error::Grpc(celestia_grpc::Error::from(*status)),
FibreError::GrpcClient(grpc_err) => Error::Grpc(grpc_err),
#[cfg(not(target_arch = "wasm32"))]
FibreError::Transport(t) => Error::Grpc(celestia_grpc::Error::from(t)),
other => Error::Fibre(other),
}
}
19 changes: 18 additions & 1 deletion client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
mod blob;
mod blobstream;
mod client;
mod fibre;
mod fraud;
mod header;
mod share;
Expand All @@ -18,6 +19,7 @@ mod utils;
pub mod api {
pub use crate::blob::BlobApi;
pub use crate::blobstream::BlobstreamApi;
pub use crate::fibre::FibreApi;
pub use crate::fraud::FraudApi;
pub use crate::header::HeaderApi;
pub use crate::share::ShareApi;
Expand All @@ -29,6 +31,15 @@ pub mod api {
pub use celestia_rpc::blob::BlobsAtHeight;
}

/// Fibre API related types.
pub mod fibre {
#[doc(inline)]
pub use celestia_fibre::{
Blob as FibreBlob, BlobID, FibreClient, FibreClientConfig, FibreError, PaymentPromise,
PreparedPut, SignedPaymentPromise,
};
}

/// Share API related types.
pub mod share {
#[doc(inline)]
Expand All @@ -55,7 +66,9 @@ pub mod tx {
#[doc(inline)]
pub use celestia_grpc::grpc::{GasEstimate, TxPriority};
#[doc(inline)]
pub use celestia_grpc::{DocSigner, IntoProtobufAny, SignDoc, TxConfig, TxInfo};
pub use celestia_grpc::{
BroadcastedTx, DocSigner, IntoProtobufAny, SignDoc, SubmittedTx, TxConfig, TxInfo,
};
#[doc(inline)]
pub use k256::ecdsa::signature::{Error as SignatureError, Keypair};
#[doc(inline)]
Expand Down Expand Up @@ -131,6 +144,10 @@ pub enum Error {
/// gRPC endpoint is not set.
#[error("Signer is set but gRPC endpoint is not")]
GrpcEndpointNotSet,

/// An error from the Fibre client library.
#[error("Fibre error: {0}")]
Fibre(celestia_fibre::FibreError),
}

impl From<jsonrpsee_core::ClientError> for Error {
Expand Down
47 changes: 47 additions & 0 deletions fibre/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[package]
name = "celestia-fibre"
version.workspace = true
edition.workspace = true
license = "Apache-2.0"
description = "Client library for the Celestia Fibre data availability protocol"
authors = ["Celestia <contact@celestia.org>"]
homepage = "https://www.celestia.org"
repository = "https://github.com/celestiaorg/lumina"
rust-version = "1.88"

[dependencies]
celestia-grpc.workspace = true
celestia-proto = { workspace = true, features = ["tonic"] }
celestia-types.workspace = true
rsema1d.workspace = true

# Crypto
k256 = { workspace = true, features = ["ecdsa"] }
ed25519-dalek = { version = "2.1", features = ["digest"] }
sha2 = "0.10.8"
rand.workspace = true
chacha8rand = "0.1.2"

# Async runtime
tokio = { workspace = true, features = ["sync", "macros"] }
tokio-util.workspace = true
futures.workspace = true
lumina-utils = { workspace = true, features = ["executor"] }

# gRPC
tonic = { workspace = true, features = ["codegen"] }
prost.workspace = true
tendermint-proto.workspace = true

# Error handling and utilities
thiserror.workspace = true
tracing.workspace = true
hex.workspace = true
async-trait.workspace = true
bech32 = "0.11"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tonic = { workspace = true, features = ["transport"] }

[dev-dependencies]
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "test-util"] }
Loading
Loading