Skip to content

Commit

Permalink
[saya] Publish proof to Celestia (#2053)
Browse files Browse the repository at this point in the history
* updated celestia and publishing proof

* specifying whether to include proof blob

* fix: cleanup

---------

Co-authored-by: Mateusz Zając <[email protected]>
Co-authored-by: glihm <[email protected]>
  • Loading branch information
3 people authored Jun 22, 2024
1 parent ab33065 commit 0d3c0b0
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 21 deletions.
29 changes: 20 additions & 9 deletions Cargo.lock

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

5 changes: 5 additions & 0 deletions bin/saya/src/args/data_availability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ pub struct CelestiaOptions {
#[arg(help = "The namespace used to submit blobs.")]
#[arg(requires = "celestia_node_url")]
pub celestia_namespace: Option<String>,

#[arg(long)]
#[arg(help = "Whether to include a proof to the publish DA.")]
#[arg(default_value_t = false)]
pub skip_publishing_proof: bool,
}

// -- Clap enums impls --
Expand Down
5 changes: 5 additions & 0 deletions bin/saya/src/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ impl TryFrom<SayaArgs> for SayaConfig {
type Error = Box<dyn std::error::Error>;

fn try_from(args: SayaArgs) -> Result<Self, Self::Error> {
let skip_publishing_proof = args.data_availability.celestia.skip_publishing_proof;

if let Some(config_file) = args.config_file {
let file = File::open(config_file).map_err(|_| "Failed to open config file")?;
let reader = BufReader::new(file);
Expand Down Expand Up @@ -147,6 +149,7 @@ impl TryFrom<SayaArgs> for SayaConfig {
data_availability: da_config,
world_address: args.proof.world_address,
fact_registry_address: args.proof.fact_registry_address,
skip_publishing_proof,
})
}
}
Expand Down Expand Up @@ -181,6 +184,7 @@ mod tests {
celestia_node_url: None,
celestia_node_auth_token: None,
celestia_namespace: None,
skip_publishing_proof: true,
},
},
proof: ProofOptions {
Expand All @@ -199,6 +203,7 @@ mod tests {
"0xd0fa91f4949e9a777ebec071ca3ca6acc1f5cd6c6827f123b798f94e73425027"
);
assert!(!config.store_proofs);
assert!(config.skip_publishing_proof);
assert_eq!(config.start_block, 0);
if let Some(DataAvailabilityConfig::Celestia(celestia_config)) = config.data_availability {
assert_eq!(celestia_config.node_url.as_str(), "http://localhost:26657/");
Expand Down
1 change: 1 addition & 0 deletions bin/saya/src/args/test_saya_config_file.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"world_address": "0x332b8ff41b1b026991fa9b7f0ec352909f8bc33416b65a80527edc988a9b082",
"fact_registry_address": "0x217746a5f74c2e5b6fa92c97e902d8cd78b1fabf1e8081c4aa0d2fe159bc0eb",
"start_block": 0,
"skip_publishing_proof": true,
"data_availability": {
"Celestia": {
"node_url": "http://localhost:26657",
Expand Down
4 changes: 2 additions & 2 deletions crates/saya/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ tracing.workspace = true
url.workspace = true

# TODO: use features for each possible DA.
celestia-rpc = "0.1.1"
celestia-types = "0.1.1"
celestia-rpc = "0.2.0"
celestia-types = "0.2.0"

cairo-felt = "0.9.1"
num-bigint = "0.4.4"
Expand Down
22 changes: 20 additions & 2 deletions crates/saya/core/src/data_availability/celestia/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Display;

use async_trait::async_trait;
use celestia_rpc::{BlobClient, Client};
use celestia_types::blob::SubmitOptions;
use celestia_types::blob::GasPrice;
use celestia_types::nmt::Namespace;
use celestia_types::Blob;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -65,7 +65,25 @@ impl DataAvailabilityClient for CelestiaClient {
// TODO: we may want to use `blob_get` to ensure the state diff has been published
// correctly.
self.client
.blob_submit(&[blob], SubmitOptions::default())
.blob_submit(&[blob], GasPrice::default())
.await
.map_err(|e| Error::Client(format!("Celestia RPC error: {e}")))
}

async fn publish_state_diff_and_proof_felts(
&self,
state_diff: &[FieldElement],
state_diff_proof: &[FieldElement],
) -> DataAvailabilityResult<u64> {
let bytes: Vec<u8> = state_diff.iter().flat_map(|fe| fe.to_bytes_be().to_vec()).collect();
let blob = Blob::new(self.namespace, bytes)?;

let proof_bytes: Vec<u8> =
state_diff_proof.iter().flat_map(|fe| fe.to_bytes_be().to_vec()).collect();
let proof_blob = Blob::new(self.namespace, proof_bytes)?;

self.client
.blob_submit(&[blob, proof_blob], GasPrice::default())
.await
.map_err(|e| Error::Client(format!("Celestia RPC error: {e}")))
}
Expand Down
15 changes: 15 additions & 0 deletions crates/saya/core/src/data_availability/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,21 @@ pub trait DataAvailabilityClient {
&self,
state_diff: &[FieldElement],
) -> DataAvailabilityResult<u64>;

/// Publishes both data and transition proof on the DA layer atomically.
/// Returns the block height in which the state diff was included.
///
/// # Arguments
///
/// * `state_diff` - An array of felt representing the data to be published on the DA layer. We
/// use felt as all fields inside the state diff can be expressed as a felt. Nonce and updates
/// count are limited to 64 bits anyway.
/// * `state_diff_proof` - The serialized transition proof corresponding to the `state_diff`.
async fn publish_state_diff_and_proof_felts(
&self,
state_diff: &[FieldElement],
state_diff_proof: &[FieldElement],
) -> DataAvailabilityResult<u64>;
}

/// Initializes a [`DataAvailabilityClient`] from a [`DataAvailabilityConfig`].
Expand Down
29 changes: 21 additions & 8 deletions crates/saya/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub struct SayaConfig {
pub data_availability: Option<DataAvailabilityConfig>,
pub world_address: FieldElement,
pub fact_registry_address: FieldElement,
pub skip_publishing_proof: bool,
}

fn url_deserializer<'de, D>(deserializer: D) -> Result<Url, D::Error>
Expand Down Expand Up @@ -190,7 +191,9 @@ impl Saya {

let mut state_updates_and_exec_info = vec![];

let (state_updates, da_state_updates): (Vec<_>, Vec<_>) = future::try_join_all(
// The serialized DA is not used here as we only need the state updates to generate the
// proof and the DA data are generated by the `dojo-os`.
let (state_updates, _): (Vec<_>, Vec<_>) = future::try_join_all(
block_numbers
.clone()
.map(|block_number| self.provider.fetch_state_updates(block_number)),
Expand All @@ -205,13 +208,6 @@ impl Saya {
)
.await?;

for da_state_update in da_state_updates {
if let Some(da) = &self.da_client {
// Publish state difference if DA client is available
da.publish_state_diff_felts(&da_state_update).await?;
}
}

state_updates.into_iter().zip(transactions_executions.into_iter()).for_each(
|(state_updates, exec_info)| {
state_updates_and_exec_info.push((state_updates, exec_info));
Expand Down Expand Up @@ -326,6 +322,8 @@ impl Saya {
let (proof, state_diff, (_, last_block)) =
prove_scheduler.proved().await.context("Failed to prove.")?;

trace!(target: LOG_TARGET, last_block, "Processing proven blocks.");

if self.config.store_proofs {
let filename = format!("proof_{}.json", last_block);
let mut file = File::create(filename).await.context("Failed to create proof file.")?;
Expand All @@ -335,6 +333,17 @@ impl Saya {
let serialized_proof: Vec<FieldElement> = parse(&proof)?.into();
let world_da = state_diff.world_da.unwrap();

// Publish state difference if DA client is available
if let Some(da) = &self.da_client {
trace!(target: LOG_TARGET, last_block, "Publishing DA.");

if self.config.skip_publishing_proof {
da.publish_state_diff_felts(&world_da).await?;
} else {
da.publish_state_diff_and_proof_felts(&world_da, &serialized_proof).await?;
}
}

trace!(target: LOG_TARGET, last_block, "Verifying block.");
let (transaction_hash, nonce_after) = verifier::verify(
VerifierIdentifier::HerodotusStarknetSepolia(self.config.fact_registry_address),
Expand All @@ -348,6 +357,10 @@ impl Saya {
let expected_fact = poseidon_hash_many(&[program_hash, program_output_hash]).to_string();
info!(target: LOG_TARGET, expected_fact, "Expected fact.");

// When not waiting for couple of second `apply_diffs` will sometimes fail due to reliance
// on registered fact
tokio::time::sleep(std::time::Duration::from_secs(2)).await;

trace!(target: LOG_TARGET, last_block, "Applying diffs.");
let transaction_hash = dojo_os::starknet_apply_diffs(
self.config.world_address,
Expand Down

0 comments on commit 0d3c0b0

Please sign in to comment.