Skip to content

Commit

Permalink
feat: add saya service (#1495)
Browse files Browse the repository at this point in the history
* feat: add state diff serialization into felts

* fix: adjust behavior for state diff to felts + add tests

* feat: add saya squeleton

* fix: rework config

* fix: remove unwanted files

* docs: fix docs + add README

* Update crates/saya/core/Cargo.toml

Co-authored-by: Ammar Arif <[email protected]>

* docs: add dependency info in README

---------

Co-authored-by: Ammar Arif <[email protected]>
  • Loading branch information
glihm and kariy authored Feb 1, 2024
1 parent 17c0b14 commit 11bbf57
Show file tree
Hide file tree
Showing 16 changed files with 1,179 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ resolver = "2"
members = [
"bin/dojo-language-server",
"bin/katana",
"bin/saya",
"bin/sozo",
"bin/torii",
"crates/benches",
Expand All @@ -29,6 +30,7 @@ members = [
"crates/katana/runner",
"crates/katana/runner/runner-macro",
"crates/metrics",
"crates/saya/core",
"crates/sozo/signers",
"crates/torii/client",
"crates/torii/server",
Expand Down Expand Up @@ -85,6 +87,9 @@ torii-grpc = { path = "crates/torii/grpc" }
torii-relay = { path = "crates/torii/libp2p" }
torii-server = { path = "crates/torii/server" }

# saya
saya-core = { path = "crates/saya/core" }

# sozo
sozo-signers = { path = "crates/sozo/signers" }

Expand Down
22 changes: 22 additions & 0 deletions bin/saya/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
description = "Settlement service for Katana."
edition.workspace = true
license-file.workspace = true
name = "saya"
repository.workspace = true
version.workspace = true

[dependencies]
anyhow.workspace = true
clap.workspace = true
clap_complete.workspace = true
console.workspace = true
katana-primitives.workspace = true
katana-rpc-api.workspace = true
katana-rpc.workspace = true
saya-core.workspace = true
serde_json.workspace = true
tokio.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true
url.workspace = true
21 changes: 21 additions & 0 deletions bin/saya/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Saya executable

Saya executable runs Saya service with CLI configurations.

Currently, Saya fetches some Katana blocks, and can publish the state update on a Celestia node.

Example:
```bash
cargo run --bin saya -- \
--rpc-url http://localhost:5050 \
--da-chain celestia \
--celestia-node-url http://127.0.0.1:26658 \
--celestia-namespace mynm \
--celestia-node-auth-token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.....
```

## WIP

1. Enhance how options are used for each one of the possible DA.
2. Add a all-in-one toml file to configure the whole Saya service (DA, prover, etc...) to avoid a huge command line.
3. Add subcommands along with saya development for a better experience.
86 changes: 86 additions & 0 deletions bin/saya/src/args/data_availability.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! Data availability options.
use std::fmt::Display;
use std::str::FromStr;

use anyhow::{self, Result};
use clap::builder::PossibleValue;
use clap::{Args, ValueEnum};
use url::Url;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DataAvailabilityChain {
Celestia,
}

// TODO: need to be reworked in order to support
// named options (like celestia options should be one
// option).
#[derive(Debug, Args, Clone)]
pub struct DataAvailabilityOptions {
#[arg(long)]
#[arg(help = "Data availability chain name")]
pub da_chain: Option<DataAvailabilityChain>,

#[command(flatten)]
#[command(next_help_heading = "Celestia")]
pub celestia: CelestiaOptions,
}

#[derive(Debug, Args, Clone)]
pub struct CelestiaOptions {
#[arg(long)]
#[arg(help = "The node url.")]
#[arg(requires = "da_chain")]
#[arg(requires = "celestia_namespace")]
pub celestia_node_url: Option<Url>,

#[arg(long)]
#[arg(help = "An authorization token if required by the node.")]
#[arg(requires = "celestia_node_url")]
pub celestia_node_auth_token: Option<String>,

#[arg(long)]
#[arg(help = "The namespace used to submit blobs.")]
#[arg(requires = "celestia_node_url")]
pub celestia_namespace: Option<String>,
}

// -- Clap enums impls --
//
//
impl Default for DataAvailabilityChain {
fn default() -> Self {
Self::Celestia
}
}

impl ValueEnum for DataAvailabilityChain {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Celestia]
}

fn to_possible_value(&self) -> Option<PossibleValue> {
match self {
Self::Celestia => Some(PossibleValue::new("celestia").alias("cel")),
}
}
}

impl FromStr for DataAvailabilityChain {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self> {
match s {
"celestia" | "cel" => Ok(Self::Celestia),
_ => Err(anyhow::anyhow!("unknown da chain: {}", s)),
}
}
}

impl Display for DataAvailabilityChain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DataAvailabilityChain::Celestia => write!(f, "celestia"),
}
}
}
87 changes: 87 additions & 0 deletions bin/saya/src/args/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//! Saya binary options.
use clap::Parser;
use saya_core::data_availability::celestia::CelestiaConfig;
use saya_core::data_availability::DataAvailabilityConfig;
use saya_core::SayaConfig;
use tracing::Subscriber;
use tracing_subscriber::{fmt, EnvFilter};
use url::Url;

use crate::args::data_availability::{DataAvailabilityChain, DataAvailabilityOptions};

mod data_availability;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct SayaArgs {
/// Specify the Katana URL to fetch data from.
#[arg(long)]
#[arg(value_name = "KATANA URL")]
#[arg(help = "The Katana RPC URL to fetch data from.")]
pub rpc_url: Url,

/// Enable JSON logging.
#[arg(long)]
#[arg(help = "Output logs in JSON format.")]
pub json_log: bool,

/// Specify a block to start fetching data from.
#[arg(short, long, default_value = "0")]
pub start_block: u64,

#[command(flatten)]
#[command(next_help_heading = "Data availability options")]
pub data_availability: DataAvailabilityOptions,
}

impl SayaArgs {
pub fn init_logging(&self) -> Result<(), Box<dyn std::error::Error>> {
const DEFAULT_LOG_FILTER: &str = "info,saya_core=trace";

let builder = fmt::Subscriber::builder().with_env_filter(
EnvFilter::try_from_default_env().or(EnvFilter::try_new(DEFAULT_LOG_FILTER))?,
);

let subscriber: Box<dyn Subscriber + Send + Sync> = if self.json_log {
Box::new(builder.json().finish())
} else {
Box::new(builder.finish())
};

Ok(tracing::subscriber::set_global_default(subscriber)?)
}
}

impl TryFrom<SayaArgs> for SayaConfig {
type Error = &'static str;

fn try_from(args: SayaArgs) -> Result<Self, Self::Error> {
let da_config = match args.data_availability.da_chain {
Some(chain) => Some(match chain {
DataAvailabilityChain::Celestia => {
let conf = args.data_availability.celestia;

DataAvailabilityConfig::Celestia(CelestiaConfig {
node_url: match conf.celestia_node_url {
Some(v) => v,
None => return Err("Celestia config: Node url is required"),
},
namespace: match conf.celestia_namespace {
Some(v) => v,
None => return Err("Celestia config: Namespace is required"),
},
node_auth_token: conf.celestia_node_auth_token,
})
}
}),
None => None,
};

Ok(SayaConfig {
katana_rpc: args.rpc_url,
start_block: args.start_block,
data_availability: da_config,
})
}
}
77 changes: 77 additions & 0 deletions bin/saya/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! Saya executable entry point.
use clap::Parser;
use console::Style;
use saya_core::{Saya, SayaConfig};
use tokio::signal::ctrl_c;

mod args;

use args::SayaArgs;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = SayaArgs::parse();
args.init_logging()?;

let config = args.try_into()?;
print_intro(&config);

let saya = Saya::new(config).await?;
saya.start().await?;

// Wait until Ctrl + C is pressed, then shutdown
ctrl_c().await?;
// handle.stop()?;

Ok(())
}

fn print_intro(config: &SayaConfig) {
println!(
"{}",
Style::new().color256(94).apply_to(
r"
_______ _______ _______
( ____ \( ___ )|\ /|( ___ )
| ( \/| ( ) |( \ / )| ( ) |
| (_____ | (___) | \ (_) / | (___) |
(_____ )| ___ | \ / | ___ |
) || ( ) | ) ( | ( ) |
/\____) || ) ( | | | | ) ( |
\_______)|/ \| \_/ |/ \|
"
)
);

println!(
r"
CONFIGURATION
=============
",
);

if let Some(da_config) = &config.data_availability {
println!(
r"
DATA AVAILBILITY
==================
{da_config}
",
);
}

println!(
r"
PROVER
==================
",
);

println!(
r"
VERIFIER
==================
",
);
}
73 changes: 73 additions & 0 deletions crates/saya/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Saya: settlement service

Saya is a settlement service for Katana.

## Data availability (DA)

Katana being a Starknet sequencer, the [state update](https://docs.starknet.io/documentation/architecture_and_concepts/Network_Architecture/on-chain-data) have to be published on the data availability layer.

Saya is responsible of fetching the state updates from katana to then publish them on the configured DA layer.

## Cairo execution trace

When Katana operates, the internal Starknet state of Katana is updated at each transaction being executed. However, this execution is not run in `proof` mode. This means we have to execute a program named `Starknet OS` where every transaction of a block is replayed.

In the case of the `Starknet OS` run, the `proof` mode is enabled and thus we obtain a cairo execution trace which attest of the correct execution of every transaction of the block.

Saya use [SNOS in rust](https://github.com/keep-starknet-strange/snos) to run the `Starknet OS` with the Cairo VM.

Once the cairo execution trace (PIE format) is generated, it can be sent to a prover.

It's important to note that at this point, we can compute what's called a `fact`, which will be used to generate the proof on.
This `fact` is a hash of the class hash of `Starknet OS` cairo program, combined to the hash of the program's output.
The hash function depends on which verifier will be used (keccak, perdersen, poseidon, ...).

## Prover

The prover is the service responsible of generating a proof for the given `Starknet OS` output.

Saya will be able to use several provers:
* **SHARP**: a StarkWare shared proving service. This service generates the proof AND send the proof and the facts on Ethereum directly.
* **Stone**: [Stone](https://github.com/starkware-libs/stone-prover) is being used to generate the proof associated with the [cairo verifier written by Herodotus](https://github.com/HerodotusDev/cairo-verifier).
* **Platinum**: The [Platinum](https://github.com/lambdaclass/lambdaworks) prover from LambdaClass.

## Verifier and facts registry

The on-chain verifier options so far are:
* **Ethereum**: StarkWare contracts on Ethereum which are tailored to receive the SHARP proofs and facts.
* **Starknet**: Soon, the cairo verifier from Herodotus will enable verification on Starknet.

A verifier comes along a fact registry. A fact registry keep track of which fact (the hash of the program class hash of `Starknet OS` in our case and the hash of it's output) has been proven.

## Library architecture

Currently, Saya contains only module to have the first skeleton of a working service. The idea is to then migrate into crates for each of the components.

The next big step is to have compatibility with SNOS in rust, which is the library responsible of generating the cairo execution trace.

Some work to be done:

1. Add a RPC server to query data from Saya and current progress.
2. Add some parallelism when it's possible, as Saya will inevitably be lagging due to the settlement layer being slower than Katana.

## Dependencies

SNOS, responsible for the cairo execution trace generation, works with Cairo VM main branch with a specific feature.

As one of it's inputs, SNOS in rust requires a `Vec<TransactionExecutionInfo>`, containing the execution info of each transaction of the block. This info is not (yet) stored by Katana neither serializable.

To ensure we've the exact same result, Saya must run the same version (or at least compatible) of the Cairo VM of Katana to replay all the transaction and get their `TransactionExecutionInfo`.

In new Cairo VM version, there are breaking changes as mentioned in the release not, which implies a bump of Cairo VM for Katana and at the same time we could bump to cairo `2.5.0`.
However, papyrus and blockifier which we depend on are still in `-dev` version, where also some breaking changes must be addressed.

* Cairo VM (currently dojo is using 0.8, and others are in 0.9)
* Blockifier (uses Cairo VM and cairo-lang `-dev`)
* Papyrus (used by blockifier and use blockifier and cairo-lang `-dev`)
* cairo-lang (we should support `2.5` now)
* scarb (breaking changes between 2.4 and 2.5 to be addresses, not required to only build saya and SNOS)

## Additional documentation

[Hackmd note](https://hackmd.io/@glihm/saya)
[Overview figma](https://www.figma.com/file/UiQkKjOpACcWihQbF70BbF/Technical-overview?type=whiteboard&node-id=0%3A1&t=0ebbPYytFmDfAkj5-1)
Loading

0 comments on commit 11bbf57

Please sign in to comment.