diff --git a/Cargo.lock b/Cargo.lock index 7d42555e..979c7983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -848,6 +848,7 @@ dependencies = [ "tendermint-proto", "thiserror", "time", + "wasm-bindgen", "wasm-bindgen-test", ] @@ -5993,9 +5994,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -6004,13 +6005,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.87", @@ -6031,9 +6031,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6041,9 +6041,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -6054,9 +6054,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-bindgen-test" diff --git a/Cargo.toml b/Cargo.toml index 0dbc0957..39133785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ prost-types = "0.13.3" #libp2p = { path = "../../rust-libp2p/libp2p" } #libp2p-core = { path = "../../rust-libp2p/core" } #libp2p-swarm = { path = "../../rust-libp2p/swarm" } +#wasm-bindgen = { path = "../wasm-bindgen" } # Uncomment this if you need debug symbols in release. # Also check node-wasm's `Cargo.toml`. diff --git a/node-wasm/Cargo.toml b/node-wasm/Cargo.toml index 72ec741a..1464e006 100644 --- a/node-wasm/Cargo.toml +++ b/node-wasm/Cargo.toml @@ -24,9 +24,9 @@ crate-type = ["cdylib", "rlib"] [target.'cfg(target_arch = "wasm32")'.dependencies] blockstore.workspace = true -celestia-types.workspace = true +celestia-types = { workspace = true, features = ["wasm-bindgen"] } libp2p = { workspace = true, features = ["serde"] } -lumina-node.workspace = true +lumina-node = { workspace = true, features = ["wasm-bindgen"] } tendermint.workspace = true anyhow = "1.0.86" @@ -46,7 +46,7 @@ tokio = { version = "1.38.0", features = ["sync"] } tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["time"] } tracing-web = "0.1.3" -wasm-bindgen = "0.2.93" +wasm-bindgen = "0.2.99" wasm-bindgen-futures = "0.4.43" web-sys = { version = "0.3.70", features = [ "BroadcastChannel", diff --git a/node-wasm/src/client.rs b/node-wasm/src/client.rs index bd7b4211..0e8cfba0 100644 --- a/node-wasm/src/client.rs +++ b/node-wasm/src/client.rs @@ -4,15 +4,15 @@ use std::time::Duration; use js_sys::Array; use serde::{Deserialize, Serialize}; -use serde_wasm_bindgen::to_value; use tracing::{debug, error}; use wasm_bindgen::prelude::*; use web_sys::BroadcastChannel; +use celestia_types::ExtendedHeader; use lumina_node::blockstore::IndexedDbBlockstore; use lumina_node::network; use lumina_node::node::NodeBuilder; -use lumina_node::store::IndexedDbStore; +use lumina_node::store::{IndexedDbStore, SamplingMetadata}; use crate::commands::{CheckableResponseExt, NodeCommand, SingleHeaderQuery}; use crate::error::{Context, Result}; @@ -200,12 +200,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/celestia-types/latest/celestia_types/struct.ExtendedHeader.html #[wasm_bindgen(js_name = requestHeadHeader)] - pub async fn request_head_header(&self) -> Result { + pub async fn request_head_header(&self) -> Result { let command = NodeCommand::RequestHeader(SingleHeaderQuery::Head); let response = self.worker.exec(command).await?; - let header = response.into_header().check_variant()?; - - header.into() + response.into_header().check_variant()? } /// Request a header for the block with a given hash from the network. @@ -213,12 +211,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/celestia-types/latest/celestia_types/struct.ExtendedHeader.html #[wasm_bindgen(js_name = requestHeaderByHash)] - pub async fn request_header_by_hash(&self, hash: &str) -> Result { + pub async fn request_header_by_hash(&self, hash: &str) -> Result { let command = NodeCommand::RequestHeader(SingleHeaderQuery::ByHash(hash.parse()?)); let response = self.worker.exec(command).await?; - let header = response.into_header().check_variant()?; - - header.into() + response.into_header().check_variant()? } /// Request a header for the block with a given height from the network. @@ -226,12 +222,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/celestia-types/latest/celestia_types/struct.ExtendedHeader.html #[wasm_bindgen(js_name = requestHeaderByHeight)] - pub async fn request_header_by_height(&self, height: u64) -> Result { + pub async fn request_header_by_height(&self, height: u64) -> Result { let command = NodeCommand::RequestHeader(SingleHeaderQuery::ByHeight(height)); let response = self.worker.exec(command).await?; - let header = response.into_header().check_variant()?; - - header.into() + response.into_header().check_variant()? } /// Request headers in range (from, from + amount] from the network. @@ -243,17 +237,12 @@ impl NodeClient { #[wasm_bindgen(js_name = requestVerifiedHeaders)] pub async fn request_verified_headers( &self, - from_header: JsValue, + from: ExtendedHeader, amount: u64, - ) -> Result { - let command = NodeCommand::GetVerifiedHeaders { - from: from_header, - amount, - }; + ) -> Result> { + let command = NodeCommand::GetVerifiedHeaders { from, amount }; let response = self.worker.exec(command).await?; - let headers = response.into_headers().check_variant()?; - - headers.into() + response.into_headers().check_variant()? } /// Get current header syncing info. @@ -271,12 +260,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/celestia-types/latest/celestia_types/struct.ExtendedHeader.html #[wasm_bindgen(js_name = getNetworkHeadHeader)] - pub async fn get_network_head_header(&self) -> Result { + pub async fn get_network_head_header(&self) -> Result> { let command = NodeCommand::LastSeenNetworkHead; let response = self.worker.exec(command).await?; - let header = response.into_last_seen_network_head().check_variant()?; - - header.into() + response.into_last_seen_network_head().check_variant()? } /// Get the latest locally synced header. @@ -284,12 +271,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/celestia-types/latest/celestia_types/struct.ExtendedHeader.html #[wasm_bindgen(js_name = getLocalHeadHeader)] - pub async fn get_local_head_header(&self) -> Result { + pub async fn get_local_head_header(&self) -> Result { let command = NodeCommand::GetHeader(SingleHeaderQuery::Head); let response = self.worker.exec(command).await?; - let header = response.into_header().check_variant()?; - - header.into() + response.into_header().check_variant()? } /// Get a synced header for the block with a given hash. @@ -297,12 +282,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/celestia-types/latest/celestia_types/struct.ExtendedHeader.html #[wasm_bindgen(js_name = getHeaderByHash)] - pub async fn get_header_by_hash(&self, hash: &str) -> Result { + pub async fn get_header_by_hash(&self, hash: &str) -> Result { let command = NodeCommand::GetHeader(SingleHeaderQuery::ByHash(hash.parse()?)); let response = self.worker.exec(command).await?; - let header = response.into_header().check_variant()?; - - header.into() + response.into_header().check_variant()? } /// Get a synced header for the block with a given height. @@ -310,12 +293,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/celestia-types/latest/celestia_types/struct.ExtendedHeader.html #[wasm_bindgen(js_name = getHeaderByHeight)] - pub async fn get_header_by_height(&self, height: u64) -> Result { + pub async fn get_header_by_height(&self, height: u64) -> Result { let command = NodeCommand::GetHeader(SingleHeaderQuery::ByHeight(height)); let response = self.worker.exec(command).await?; - let header = response.into_header().check_variant()?; - - header.into() + response.into_header().check_variant()? } /// Get synced headers from the given heights range. @@ -335,15 +316,13 @@ impl NodeClient { &self, start_height: Option, end_height: Option, - ) -> Result { + ) -> Result> { let command = NodeCommand::GetHeadersRange { start_height, end_height, }; let response = self.worker.exec(command).await?; - let headers = response.into_headers().check_variant()?; - - headers.into() + response.into_headers().check_variant()? } /// Get data sampling metadata of an already sampled height. @@ -351,12 +330,10 @@ impl NodeClient { /// Returns a javascript object with given structure: /// https://docs.rs/lumina-node/latest/lumina_node/store/struct.SamplingMetadata.html #[wasm_bindgen(js_name = getSamplingMetadata)] - pub async fn get_sampling_metadata(&self, height: u64) -> Result { + pub async fn get_sampling_metadata(&self, height: u64) -> Result> { let command = NodeCommand::GetSamplingMetadata { height }; let response = self.worker.exec(command).await?; - let metadata = response.into_sampling_metadata().check_variant()?; - - Ok(to_value(&metadata?)?) + response.into_sampling_metadata().check_variant()? } /// Returns a [`BroadcastChannel`] for events generated by [`Node`]. @@ -438,7 +415,6 @@ mod tests { use gloo_timers::future::sleep; use libp2p::{multiaddr::Protocol, Multiaddr}; use rexie::Rexie; - use serde_wasm_bindgen::from_value; use wasm_bindgen_futures::spawn_local; use wasm_bindgen_test::wasm_bindgen_test; use web_sys::MessageChannel; @@ -460,8 +436,7 @@ mod tests { assert_eq!(info.num_peers, 1); let bridge_head_header = rpc_client.header_network_head().await.unwrap(); - let head_header: ExtendedHeader = - from_value(client.request_head_header().await.unwrap()).unwrap(); + let head_header: ExtendedHeader = client.request_head_header().await.unwrap(); assert_eq!(head_header, bridge_head_header); rpc_client .p2p_close_peer(&PeerId( diff --git a/node-wasm/src/commands.rs b/node-wasm/src/commands.rs index 9f096aa0..163749be 100644 --- a/node-wasm/src/commands.rs +++ b/node-wasm/src/commands.rs @@ -1,23 +1,22 @@ use std::fmt::Debug; use enum_as_inner::EnumAsInner; -use js_sys::Array; use libp2p::Multiaddr; use libp2p::PeerId; use serde::{Deserialize, Serialize}; use tracing::error; -use wasm_bindgen::{JsError, JsValue}; +use wasm_bindgen::JsError; -use celestia_types::hash::Hash; +use celestia_types::{hash::Hash, ExtendedHeader}; use lumina_node::node::{PeerTrackerInfo, SyncingInfo}; use lumina_node::store::SamplingMetadata; use crate::client::WasmNodeConfig; use crate::error::Error; use crate::error::Result; -use crate::utils::JsResult; use crate::wrapper::libp2p::NetworkInfoSnapshot; +#[allow(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub(crate) enum NodeCommand { InternalPing, @@ -40,8 +39,7 @@ pub(crate) enum NodeCommand { GetListeners, RequestHeader(SingleHeaderQuery), GetVerifiedHeaders { - #[serde(with = "serde_wasm_bindgen::preserve")] - from: JsValue, + from: ExtendedHeader, amount: u64, }, GetHeadersRange { @@ -78,9 +76,9 @@ pub(crate) enum WorkerResponse { SetPeerTrust(Result<()>), Connected(Result<()>), Listeners(Result>), - Header(JsResult), - Headers(JsResult), - LastSeenNetworkHead(JsResult), + Header(Result), + Headers(Result, Error>), + LastSeenNetworkHead(Result, Error>), SamplingMetadata(Result>), } diff --git a/node-wasm/src/ports.rs b/node-wasm/src/ports.rs index e06844a0..11ac9bea 100644 --- a/node-wasm/src/ports.rs +++ b/node-wasm/src/ports.rs @@ -38,6 +38,7 @@ impl From for MessagePortLike { #[derive(Debug, Clone, Copy)] pub struct ClientId(usize); +#[allow(clippy::large_enum_variant)] pub(crate) enum ClientMessage { Command { id: ClientId, command: NodeCommand }, AddConnection(JsValue), diff --git a/node-wasm/src/utils.rs b/node-wasm/src/utils.rs index b30d44cc..721e415b 100644 --- a/node-wasm/src/utils.rs +++ b/node-wasm/src/utils.rs @@ -8,9 +8,7 @@ use gloo_timers::future::TimeoutFuture; use js_sys::{Math, Promise}; use libp2p::multiaddr::Protocol; use libp2p::{Multiaddr, PeerId}; -use lumina_node::network; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_repr::{Deserialize_repr, Serialize_repr}; use tracing::{info, warn}; use tracing_subscriber::filter::LevelFilter; @@ -24,6 +22,8 @@ use web_sys::{ ServiceWorker, ServiceWorkerGlobalScope, SharedWorker, SharedWorkerGlobalScope, Worker, }; +use lumina_node::network; + use crate::error::{Context, Error, Result}; /// Supported Celestia networks. @@ -110,49 +110,6 @@ impl WorkerSelf for ServiceWorker { type GlobalScope = ServiceWorkerGlobalScope; } -/// This type is useful in cases where we want to deal with de/serialising `Result`, with -/// [`serde_wasm_bindgen::preserve`] where `T` is a JavaScript object (which are not serializable by -/// Rust standards, but can be passed through unchanged via cast as they implement [`JsCast`]). -/// -/// [`serde_wasm_bindgen::preserve`]: https://docs.rs/serde-wasm-bindgen/latest/serde_wasm_bindgen/preserve -/// [`JsCast`]: https://docs.rs/wasm-bindgen/latest/wasm_bindgen/trait.JsCast.html -#[derive(Serialize, Deserialize, Debug)] -pub(crate) enum JsResult -where - T: JsCast + Debug, - E: Debug, -{ - #[serde(with = "serde_wasm_bindgen::preserve")] - Ok(T), - Err(E), -} - -impl From> for JsResult -where - T: JsCast + Debug, - E: Serialize + DeserializeOwned + Debug, -{ - fn from(result: Result) -> Self { - match result { - Ok(v) => JsResult::Ok(v), - Err(e) => JsResult::Err(e), - } - } -} - -impl From> for Result -where - T: JsCast + Debug, - E: Serialize + DeserializeOwned + Debug, -{ - fn from(result: JsResult) -> Self { - match result { - JsResult::Ok(v) => Ok(v), - JsResult::Err(e) => Err(e), - } - } -} - pub(crate) trait MessageEventExt { fn get_port(&self) -> Option; } diff --git a/node-wasm/src/worker.rs b/node-wasm/src/worker.rs index 2b988a76..d5c62c02 100644 --- a/node-wasm/src/worker.rs +++ b/node-wasm/src/worker.rs @@ -1,9 +1,8 @@ use std::fmt::Debug; -use js_sys::Array; use libp2p::{Multiaddr, PeerId}; use serde::{Deserialize, Serialize}; -use serde_wasm_bindgen::{from_value, to_value}; +use serde_wasm_bindgen::to_value; use thiserror::Error; use tokio::sync::mpsc; use tracing::{error, info, warn}; @@ -11,6 +10,7 @@ use wasm_bindgen::prelude::*; use wasm_bindgen_futures::spawn_local; use web_sys::BroadcastChannel; +use celestia_types::ExtendedHeader; use lumina_node::blockstore::IndexedDbBlockstore; use lumina_node::events::{EventSubscriber, NodeEventInfo}; use lumina_node::node::{Node, SyncingInfo}; @@ -175,63 +175,45 @@ impl NodeWorkerInstance { Ok(()) } - async fn request_header(&mut self, query: SingleHeaderQuery) -> Result { - let header = match query { + async fn request_header(&mut self, query: SingleHeaderQuery) -> Result { + Ok(match query { SingleHeaderQuery::Head => self.node.request_head_header().await, SingleHeaderQuery::ByHash(hash) => self.node.request_header_by_hash(&hash).await, SingleHeaderQuery::ByHeight(height) => self.node.request_header_by_height(height).await, - }?; - to_value(&header).context("could not serialise requested header") + }?) } - async fn get_header(&mut self, query: SingleHeaderQuery) -> Result { - let header = match query { + async fn get_header(&mut self, query: SingleHeaderQuery) -> Result { + Ok(match query { SingleHeaderQuery::Head => self.node.get_local_head_header().await, SingleHeaderQuery::ByHash(hash) => self.node.get_header_by_hash(&hash).await, SingleHeaderQuery::ByHeight(height) => self.node.get_header_by_height(height).await, - }?; - to_value(&header).context("could not serialise requested header") + }?) } - async fn get_verified_headers(&mut self, from: JsValue, amount: u64) -> Result { - let verified_headers = self - .node - .request_verified_headers( - &from_value(from).context("could not deserialise verified header")?, - amount, - ) - .await?; - verified_headers - .iter() - .map(|h| to_value(&h)) - .collect::>() - .context("could not serialise fetched headers") + async fn get_verified_headers( + &mut self, + from: ExtendedHeader, + amount: u64, + ) -> Result> { + Ok(self.node.request_verified_headers(&from, amount).await?) } async fn get_headers_range( &mut self, start_height: Option, end_height: Option, - ) -> Result { - let headers = match (start_height, end_height) { + ) -> Result> { + Ok(match (start_height, end_height) { (None, None) => self.node.get_headers(..).await, (Some(start), None) => self.node.get_headers(start..).await, (None, Some(end)) => self.node.get_headers(..=end).await, (Some(start), Some(end)) => self.node.get_headers(start..=end).await, - }?; - - headers - .iter() - .map(|h| to_value(&h)) - .collect::>() - .context("could not serialise fetched headers") + }?) } - async fn get_last_seen_network_head(&mut self) -> Result { - match self.node.get_network_head_header().await? { - Some(header) => to_value(&header).context("could not serialise head header"), - None => Ok(JsValue::UNDEFINED), - } + async fn get_last_seen_network_head(&mut self) -> Result> { + Ok(self.node.get_network_head_header().await?) } async fn get_sampling_metadata(&mut self, height: u64) -> Result> { @@ -271,24 +253,18 @@ impl NodeWorkerInstance { } NodeCommand::GetListeners => WorkerResponse::Listeners(self.get_listeners().await), NodeCommand::RequestHeader(query) => { - WorkerResponse::Header(self.request_header(query).await.into()) - } - NodeCommand::GetHeader(query) => { - WorkerResponse::Header(self.get_header(query).await.into()) + WorkerResponse::Header(self.request_header(query).await) } + NodeCommand::GetHeader(query) => WorkerResponse::Header(self.get_header(query).await), NodeCommand::GetVerifiedHeaders { from, amount } => { - WorkerResponse::Headers(self.get_verified_headers(from, amount).await.into()) + WorkerResponse::Headers(self.get_verified_headers(from, amount).await) } NodeCommand::GetHeadersRange { start_height, end_height, - } => WorkerResponse::Headers( - self.get_headers_range(start_height, end_height) - .await - .into(), - ), + } => WorkerResponse::Headers(self.get_headers_range(start_height, end_height).await), NodeCommand::LastSeenNetworkHead => { - WorkerResponse::LastSeenNetworkHead(self.get_last_seen_network_head().await.into()) + WorkerResponse::LastSeenNetworkHead(self.get_last_seen_network_head().await) } NodeCommand::GetSamplingMetadata { height } => { WorkerResponse::SamplingMetadata(self.get_sampling_metadata(height).await) diff --git a/node/Cargo.toml b/node/Cargo.toml index d6b24da7..0909daf2 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -89,7 +89,7 @@ pin-project = "1.1.5" rexie = "0.6.2" send_wrapper = { version = "0.6.0", features = ["futures"] } serde-wasm-bindgen = "0.6.5" -wasm-bindgen = "0.2.93" +wasm-bindgen = "0.2.99" wasm-bindgen-futures = "0.4.43" libp2p-websocket-websys = "0.3.3" @@ -111,6 +111,7 @@ tempfile = "3.10.1" [features] test-utils = ["celestia-types/test-utils"] +wasm-bindgen = [] [package.metadata.docs.rs] features = ["test-utils"] diff --git a/node/src/store.rs b/node/src/store.rs index 6b26a638..0bad9664 100644 --- a/node/src/store.rs +++ b/node/src/store.rs @@ -13,6 +13,8 @@ use prost::Message; use serde::{Deserialize, Serialize}; use tendermint_proto::Protobuf; use thiserror::Error; +#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] +use wasm_bindgen::prelude::*; pub use crate::block_ranges::{BlockRange, BlockRanges, BlockRangesError}; pub use crate::store::utils::VerifiedExtendedHeaders; @@ -31,10 +33,13 @@ mod redb_store; pub(crate) mod utils; +// TODO: once https://github.com/rustwasm/wasm-bindgen/pull/4351 is merged, +// this can be replaced with a single common type definition /// Sampling metadata for a block. /// /// This struct persists DAS-ing information in a header store for future reference. #[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[cfg(not(all(feature = "wasm-bindgen", target_arch = "wasm32")))] pub struct SamplingMetadata { /// Indicates whether this node was able to successfuly sample the block pub status: SamplingStatus, @@ -44,8 +49,41 @@ pub struct SamplingMetadata { pub cids: Vec, } +/// Sampling metadata for a block. +/// +/// This struct persists DAS-ing information in a header store for future reference. +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] +#[wasm_bindgen] +pub struct SamplingMetadata { + /// Indicates whether this node was able to successfuly sample the block + pub status: SamplingStatus, + + /// List of CIDs used while sampling. Can be used to remove associated data + /// from Blockstore, when cleaning up the old ExtendedHeaders + #[wasm_bindgen(skip)] + pub cids: Vec, +} + +// TODO: once https://github.com/rustwasm/wasm-bindgen/pull/4351 is merged, +// this can be replaced with a single common type definition /// Sampling status for a block. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg(not(all(feature = "wasm-bindgen", target_arch = "wasm32")))] +pub enum SamplingStatus { + /// Sampling is not done. + #[default] + Unknown, + /// Sampling is done and block is accepted. + Accepted, + /// Sampling is done and block is rejected. + Rejected, +} + +/// Sampling status for a block. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] +#[wasm_bindgen] pub enum SamplingStatus { /// Sampling is not done. #[default] @@ -241,6 +279,19 @@ impl From for StoreError { } } +#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] +#[wasm_bindgen] +impl SamplingMetadata { + /// Return Array of cids + #[wasm_bindgen(getter)] + pub fn cids(&self) -> Vec { + self.cids + .iter() + .map(|cid| js_sys::Uint8Array::from(cid.to_bytes().as_ref())) + .collect() + } +} + #[derive(Message)] struct RawSamplingMetadata { #[prost(bool, tag = "1")] diff --git a/types/Cargo.toml b/types/Cargo.toml index 3084e80e..0353c360 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -40,6 +40,7 @@ serde_repr = { version = "0.1.19", optional = true } sha2 = "0.10.6" thiserror = "1.0.61" time = { version = "0.3.36", default-features = false } +wasm-bindgen = { version = "0.2.99", optional = true } [dev-dependencies] ed25519-consensus = "2.1.0" @@ -57,7 +58,7 @@ wasm-bindgen-test = "0.3.42" default = ["p2p"] p2p = ["dep:libp2p-identity", "dep:multiaddr", "dep:serde_repr"] test-utils = ["dep:ed25519-consensus", "dep:rand"] -wasm-bindgen = ["time/wasm-bindgen"] +wasm-bindgen = ["time/wasm-bindgen", "dep:wasm-bindgen"] [package.metadata.docs.rs] features = ["p2p", "test-utils"] diff --git a/types/src/data_availability_header.rs b/types/src/data_availability_header.rs index 4d2343c5..7c790494 100644 --- a/types/src/data_availability_header.rs +++ b/types/src/data_availability_header.rs @@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize}; use sha2::Sha256; use tendermint::merkle::simple_hash_from_byte_vectors; use tendermint_proto::Protobuf; +#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] +use wasm_bindgen::prelude::*; use crate::consts::appconsts::AppVersion; use crate::consts::data_availability_header::{ @@ -58,6 +60,7 @@ use crate::{ try_from = "RawDataAvailabilityHeader", into = "RawDataAvailabilityHeader" )] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen)] pub struct DataAvailabilityHeader { /// Merkle roots of the [`ExtendedDataSquare`] rows. row_roots: Vec, diff --git a/types/src/extended_header.rs b/types/src/extended_header.rs index 828a32aa..33369c80 100644 --- a/types/src/extended_header.rs +++ b/types/src/extended_header.rs @@ -12,6 +12,8 @@ use tendermint::block::{Commit, Height}; use tendermint::chain::id::Id; use tendermint::{validator, Time}; use tendermint_proto::Protobuf; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::prelude::*; use crate::consts::appconsts::AppVersion; use crate::hash::Hash; @@ -33,6 +35,8 @@ pub type ValidatorSet = validator::Set; ))] const VERIFY_CLOCK_DRIFT: Duration = Duration::from_secs(10); +// TODO: once https://github.com/rustwasm/wasm-bindgen/pull/4351 is merged, +// this can be replaced with a single common type definition /// Block header together with the relevant Data Availability metadata. /// /// [`ExtendedHeader`]s are used to announce and describe the blocks @@ -61,14 +65,76 @@ const VERIFY_CLOCK_DRIFT: Duration = Duration::from_secs(10); /// genesis_header.verify(&fetched_header).expect("Malicious header received"); /// ``` #[derive(Debug, Clone, PartialEq, Eq)] +#[cfg(not(all(feature = "wasm-bindgen", target_arch = "wasm32")))] pub struct ExtendedHeader { /// Tendermint block header. + #[cfg_attr( + all(feature = "wasm-bindgen", target_arch = "wasm32"), + wasm_bindgen(skip) + )] pub header: Header, /// Commit metadata and signatures from validators committing the block. + #[cfg_attr( + all(feature = "wasm-bindgen", target_arch = "wasm32"), + wasm_bindgen(skip) + )] pub commit: Commit, /// Information about the set of validators commiting the block. + #[cfg_attr( + all(feature = "wasm-bindgen", target_arch = "wasm32"), + wasm_bindgen(skip) + )] pub validator_set: ValidatorSet, /// Header of the block data availability. + #[cfg_attr( + all(feature = "wasm-bindgen", target_arch = "wasm32"), + wasm_bindgen(getter_with_clone) + )] + pub dah: DataAvailabilityHeader, +} + +/// Block header together with the relevant Data Availability metadata. +/// +/// [`ExtendedHeader`]s are used to announce and describe the blocks +/// in the Celestia network. +/// +/// Before being used, each header should be validated and verified with a header you trust. +/// +/// # Example +/// +/// ``` +/// # use celestia_types::ExtendedHeader; +/// # fn trusted_genesis_header() -> ExtendedHeader { +/// # let s = include_str!("../test_data/chain1/extended_header_block_1.json"); +/// # serde_json::from_str(s).unwrap() +/// # } +/// # fn some_untrusted_header() -> ExtendedHeader { +/// # let s = include_str!("../test_data/chain1/extended_header_block_27.json"); +/// # serde_json::from_str(s).unwrap() +/// # } +/// let genesis_header = trusted_genesis_header(); +/// +/// // fetch new header +/// let fetched_header = some_untrusted_header(); +/// +/// fetched_header.validate().expect("Invalid block header"); +/// genesis_header.verify(&fetched_header).expect("Malicious header received"); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] +#[wasm_bindgen] +pub struct ExtendedHeader { + /// Tendermint block header. + #[wasm_bindgen(skip)] + pub header: Header, + /// Commit metadata and signatures from validators committing the block. + #[wasm_bindgen(skip)] + pub commit: Commit, + /// Information about the set of validators commiting the block. + #[wasm_bindgen(skip)] + pub validator_set: ValidatorSet, + /// Header of the block data availability. + #[wasm_bindgen(skip)] pub dah: DataAvailabilityHeader, }