From 149b00d2a3ea11910d3076887126d3df63a36292 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Tue, 6 Jan 2026 15:09:03 +0200 Subject: [PATCH 01/31] init: initial structure --- Cargo.lock | 16 ++++++++++++++++ Cargo.toml | 2 +- indexer/Cargo.toml | 19 +++++++++++++++++++ indexer/src/client/mod.rs | 0 indexer/src/lib.rs | 1 + 5 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 indexer/Cargo.toml create mode 100644 indexer/src/client/mod.rs create mode 100644 indexer/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index eeb8f5ba..2c5200ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2198,6 +2198,22 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" +[[package]] +name = "indexer" +version = "0.1.0" +dependencies = [ + "anyhow", + "base58", + "common", + "futures", + "log", + "nssa", + "nssa_core", + "serde", + "storage", + "tokio", +] + [[package]] name = "indexmap" version = "1.9.3" diff --git a/Cargo.toml b/Cargo.toml index 14856d09..f8327864 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ "integration_tests/proc_macro_test_attribute", "examples/program_deployment", "examples/program_deployment/methods", - "examples/program_deployment/methods/guest", + "examples/program_deployment/methods/guest", "indexer", ] [workspace.dependencies] diff --git a/indexer/Cargo.toml b/indexer/Cargo.toml new file mode 100644 index 00000000..4b93ad05 --- /dev/null +++ b/indexer/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "indexer" +version = "0.1.0" +edition = "2024" + +[dependencies] +nssa.workspace = true +nssa_core.workspace = true +common.workspace = true +storage.workspace = true + +base58.workspace = true +anyhow.workspace = true +serde.workspace = true +log.workspace = true + +[dev-dependencies] +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } +futures.workspace = true \ No newline at end of file diff --git a/indexer/src/client/mod.rs b/indexer/src/client/mod.rs new file mode 100644 index 00000000..e69de29b diff --git a/indexer/src/lib.rs b/indexer/src/lib.rs new file mode 100644 index 00000000..39355176 --- /dev/null +++ b/indexer/src/lib.rs @@ -0,0 +1 @@ +pub mod client; \ No newline at end of file From 1a033af5f248d55e95625357c011645c103cc72c Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Wed, 7 Jan 2026 16:29:37 +0200 Subject: [PATCH 02/31] fix: adapting http client --- Cargo.lock | 87 ++++++++++++ Cargo.toml | 6 +- bedrock_client/Cargo.toml | 17 +++ bedrock_client/src/lib.rs | 174 ++++++++++++++++++++++++ bedrock_client/src/paths.rs | 6 + bedrock_client/src/structs/header_id.rs | 103 ++++++++++++++ bedrock_client/src/structs/info.rs | 23 ++++ bedrock_client/src/structs/mod.rs | 40 ++++++ bedrock_client/src/structs/signature.rs | 53 ++++++++ bedrock_client/src/structs/tx.rs | 9 ++ 10 files changed, 517 insertions(+), 1 deletion(-) create mode 100644 bedrock_client/Cargo.toml create mode 100644 bedrock_client/src/lib.rs create mode 100644 bedrock_client/src/paths.rs create mode 100644 bedrock_client/src/structs/header_id.rs create mode 100644 bedrock_client/src/structs/info.rs create mode 100644 bedrock_client/src/structs/mod.rs create mode 100644 bedrock_client/src/structs/signature.rs create mode 100644 bedrock_client/src/structs/tx.rs diff --git a/Cargo.lock b/Cargo.lock index 2c5200ac..e58d4e0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -657,6 +657,23 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" +[[package]] +name = "bedrock_client" +version = "0.1.0" +dependencies = [ + "anyhow", + "const-hex", + "ed25519-dalek", + "futures", + "log", + "reqwest 0.11.27", + "serde", + "serde_json", + "serde_with", + "thiserror", + "url", +] + [[package]] name = "bincode" version = "1.3.3" @@ -1046,6 +1063,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "const-hex" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +dependencies = [ + "cfg-if", + "cpufeatures", + "proptest", + "serde_core", +] + [[package]] name = "const-oid" version = "0.9.6" @@ -1150,6 +1179,33 @@ dependencies = [ "cipher", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "darling" version = "0.20.11" @@ -1407,6 +1463,31 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "serde", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "educe" version = "0.6.0" @@ -1561,6 +1642,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "find-msvc-tools" version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index f8327864..92e3b7d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ "integration_tests/proc_macro_test_attribute", "examples/program_deployment", "examples/program_deployment/methods", - "examples/program_deployment/methods/guest", "indexer", + "examples/program_deployment/methods/guest", "indexer", "bedrock_client", ] [workspace.dependencies] @@ -76,6 +76,10 @@ chrono = "0.4.41" borsh = "1.5.7" base58 = "0.2.0" itertools = "0.14.0" +url = "2.5.4" +const-hex = { default-features = false, features = ["alloc"], version = "1" } +serde_with = { default-features = false, version = "3.14.0" } +ed25519-dalek = { default-features = false, version = "2" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml new file mode 100644 index 00000000..bc0f9277 --- /dev/null +++ b/bedrock_client/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bedrock_client" +version = "0.1.0" +edition = "2024" + +[dependencies] +reqwest.workspace = true +anyhow.workspace = true +serde.workspace = true +log.workspace = true +thiserror.workspace = true +url.workspace = true +futures.workspace = true +serde_json.workspace = true +const-hex.workspace = true +serde_with.workspace = true +ed25519-dalek = { workspace = true, features = ["rand_core", "serde", "zeroize"] } diff --git a/bedrock_client/src/lib.rs b/bedrock_client/src/lib.rs new file mode 100644 index 00000000..d3d4fbb1 --- /dev/null +++ b/bedrock_client/src/lib.rs @@ -0,0 +1,174 @@ +//A copy of http-client from logos-blockchain +//ToDo: replace with dependency + +use std::{fmt::Debug, sync::Arc}; +use reqwest::{Client, ClientBuilder, RequestBuilder, StatusCode, Url}; +use serde::{Serialize, de::DeserializeOwned}; +use futures::{Stream, StreamExt as _}; + +pub mod paths; +pub mod structs; + +use nomos_core::{mantle::SignedMantleTx}; + +use crate::{paths::{CRYPTARCHIA_INFO, CRYPTARCHIA_LIB_STREAM, STORAGE_BLOCK}, structs::{Block, BlockInfo, info::CryptarchiaInfo}}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Internal server error: {0}")] + Server(String), + #[error("Failed to execute request: {0}")] + Client(String), + #[error(transparent)] + Request(#[from] reqwest::Error), + #[error(transparent)] + Url(#[from] url::ParseError), +} + +#[derive(Clone)] +pub struct BasicAuthCredentials { + username: String, + password: Option, +} + +impl BasicAuthCredentials { + #[must_use] + pub const fn new(username: String, password: Option) -> Self { + Self { username, password } + } +} + +#[derive(Clone)] +pub struct CommonHttpClient { + client: Arc, + basic_auth: Option, +} + +impl CommonHttpClient { + #[must_use] + pub fn new(basic_auth: Option) -> Self { + let client = ClientBuilder::new() + .build() + .expect("Client from default settings should be able to build"); + Self { + client: Arc::new(client), + basic_auth, + } + } + + #[must_use] + pub fn new_with_client(client: Client, basic_auth: Option) -> Self { + Self { + client: Arc::new(client), + basic_auth, + } + } + + pub async fn post(&self, request_url: Url, request_body: &Req) -> Result + where + Req: Serialize + ?Sized + Send + Sync, + Res: DeserializeOwned + Send + Sync, + { + let request = self.client.post(request_url).json(request_body); + self.execute_request::(request).await + } + + pub async fn get( + &self, + request_url: Url, + request_body: Option<&Req>, + ) -> Result + where + Req: Serialize + ?Sized + Send + Sync, + Res: DeserializeOwned + Send + Sync, + { + let mut request = self.client.get(request_url); + if let Some(request_body) = request_body { + request = request.json(request_body); + } + self.execute_request::(request).await + } + + async fn execute_request( + &self, + mut request: RequestBuilder, + ) -> Result { + if let Some(basic_auth) = &self.basic_auth { + request = request.basic_auth(&basic_auth.username, basic_auth.password.as_deref()); + } + + let response = request.send().await.map_err(Error::Request)?; + let status = response.status(); + let body = response.text().await.map_err(Error::Request)?; + + match status { + StatusCode::OK => serde_json::from_str(&body) + .map_err(|e| Error::Server(format!("Failed to parse response: {e}"))), + StatusCode::INTERNAL_SERVER_ERROR => Err(Error::Server(body)), + _ => Err(Error::Server(format!( + "Unexpected response [{status}]: {body}", + ))), + } + } + + pub async fn get_lib_stream( + &self, + base_url: Url, + ) -> Result, Error> { + let request_url = base_url + .join(CRYPTARCHIA_LIB_STREAM.trim_start_matches('/')) + .map_err(Error::Url)?; + let mut request = self.client.get(request_url); + + if let Some(basic_auth) = &self.basic_auth { + request = request.basic_auth(&basic_auth.username, basic_auth.password.as_deref()); + } + + let response = request.send().await.map_err(Error::Request)?; + let status = response.status(); + + let lib_stream = response.bytes_stream().filter_map(async |item| { + let bytes = item.ok()?; + serde_json::from_slice::(&bytes).ok() + }); + match status { + StatusCode::OK => Ok(lib_stream), + StatusCode::INTERNAL_SERVER_ERROR => Err(Error::Server("Error".to_owned())), + _ => Err(Error::Server(format!("Unexpected response [{status}]",))), + } + } + + pub async fn get_block_by_id( + &self, + base_url: Url, + header_id: HeaderId, + ) -> Result>, Error> + where + HeaderId: Serialize + Send + Sync, + { + let request_url = base_url + .join(STORAGE_BLOCK.trim_start_matches('/')) + .map_err(Error::Url)?; + self.post(request_url, &header_id).await + } + + /// Get consensus info (tip, height, etc.) + pub async fn consensus_info(&self, base_url: Url) -> Result { + let request_url = base_url + .join(CRYPTARCHIA_INFO.trim_start_matches('/')) + .map_err(Error::Url)?; + self.get::<(), CryptarchiaInfo>(request_url, None).await + } + + /// Get a block by its header ID + pub async fn get_block( + &self, + base_url: Url, + header_id: HeaderId, + ) -> Result>, Error> { + let request_url = base_url + .join(STORAGE_BLOCK.trim_start_matches('/')) + .map_err(Error::Url)?; + self.post(request_url, &header_id).await + } +} diff --git a/bedrock_client/src/paths.rs b/bedrock_client/src/paths.rs new file mode 100644 index 00000000..5149f4d3 --- /dev/null +++ b/bedrock_client/src/paths.rs @@ -0,0 +1,6 @@ +pub const CRYPTARCHIA_INFO: &str = "/cryptarchia/info"; +pub const CRYPTARCHIA_LIB_STREAM: &str = "/cryptarchia/lib-stream"; +pub const STORAGE_BLOCK: &str = "/storage/block"; + +pub const BLOCKS: &str = "/cryptarchia/blocks"; +pub const BLOCKS_STREAM: &str = "/cryptarchia/blocks/stream"; diff --git a/bedrock_client/src/structs/header_id.rs b/bedrock_client/src/structs/header_id.rs new file mode 100644 index 00000000..4b032bc4 --- /dev/null +++ b/bedrock_client/src/structs/header_id.rs @@ -0,0 +1,103 @@ +#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash, PartialOrd, Ord)] +pub struct HeaderId([u8; 32]); + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Invalid header id size: {0}")] + InvalidHeaderIdSize(usize), +} + +impl From<[u8; 32]> for HeaderId { + fn from(id: [u8; 32]) -> Self { + Self(id) + } +} + +impl From for [u8; 32] { + fn from(id: HeaderId) -> Self { + id.0 + } +} + +impl TryFrom<&[u8]> for HeaderId { + type Error = Error; + + fn try_from(slice: &[u8]) -> Result { + if slice.len() != 32 { + return Err(Error::InvalidHeaderIdSize(slice.len())); + } + let mut id = [0u8; 32]; + id.copy_from_slice(slice); + Ok(Self::from(id)) + } +} + +impl AsRef<[u8]> for HeaderId { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash)] +pub struct ContentId([u8; 32]); + +impl From for [u8; 32] { + fn from(id: ContentId) -> Self { + id.0 + } +} + +macro_rules! display_hex_bytes_newtype { + ($newtype:ty) => { + impl core::fmt::Display for $newtype { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "0x")?; + for v in self.0 { + write!(f, "{:02x}", v)?; + } + Ok(()) + } + } + }; +} + +macro_rules! serde_bytes_newtype { + ($newtype:ty, $len:expr) => { + impl serde::Serialize for $newtype { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if serializer.is_human_readable() { + const_hex::const_encode::<$len, false>(&self.0) + .as_str() + .serialize(serializer) + } else { + self.0.serialize(serializer) + } + } + } + + impl<'de> serde::Deserialize<'de> for $newtype { + fn deserialize(deserializer: D) -> Result<$newtype, D::Error> + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + let s = ::deserialize(deserializer)?; + const_hex::decode_to_array(s) + .map(Self) + .map_err(serde::de::Error::custom) + } else { + <[u8; $len] as serde::Deserialize>::deserialize(deserializer).map(Self) + } + } + } + }; +} + +display_hex_bytes_newtype!(HeaderId); +display_hex_bytes_newtype!(ContentId); + +serde_bytes_newtype!(HeaderId, 32); +serde_bytes_newtype!(ContentId, 32); \ No newline at end of file diff --git a/bedrock_client/src/structs/info.rs b/bedrock_client/src/structs/info.rs new file mode 100644 index 00000000..3e33246b --- /dev/null +++ b/bedrock_client/src/structs/info.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; + +use crate::structs::header_id::HeaderId; + +#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Slot(u64); + +#[derive(Clone, Debug, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum State { + Bootstrapping, + Online, +} + +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct CryptarchiaInfo { + pub lib: HeaderId, + pub tip: HeaderId, + pub slot: Slot, + pub height: u64, + pub mode: State, +} \ No newline at end of file diff --git a/bedrock_client/src/structs/mod.rs b/bedrock_client/src/structs/mod.rs new file mode 100644 index 00000000..36c2aab4 --- /dev/null +++ b/bedrock_client/src/structs/mod.rs @@ -0,0 +1,40 @@ +use ed25519_dalek::Signature; +use serde::{Deserialize, Serialize}; + +use crate::structs::{header_id::{ContentId, HeaderId}, info::Slot}; + +pub mod header_id; +pub mod info; +pub mod signature; +pub mod tx; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct BlockInfo { + pub height: u64, + pub header_id: HeaderId, +} + +pub const BEDROCK_VERSION: u8 = 1; + +#[derive(Clone, Debug, Eq, PartialEq, Copy, Serialize, Deserialize)] +#[repr(u8)] +pub enum Version { + Bedrock = BEDROCK_VERSION, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Header { + version: Version, + parent_block: HeaderId, + slot: Slot, + block_root: ContentId, + // Not sure, if need this. + // proof_of_leadership: Groth16LeaderProof, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Block { + header: Header, + signature: Signature, + transactions: Vec, +} \ No newline at end of file diff --git a/bedrock_client/src/structs/signature.rs b/bedrock_client/src/structs/signature.rs new file mode 100644 index 00000000..1075354b --- /dev/null +++ b/bedrock_client/src/structs/signature.rs @@ -0,0 +1,53 @@ +use core::hash::{Hash, Hasher}; + +use ed25519_dalek::SIGNATURE_LENGTH; +use serde::{Deserialize, Serialize}; + +pub const SIGNATURE_SIZE: usize = SIGNATURE_LENGTH; + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +pub struct Signature(ed25519_dalek::Signature); + +impl Signature { + #[must_use] + pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Self { + Self(ed25519_dalek::Signature::from_bytes(bytes)) + } + + #[must_use] + pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] { + self.0.to_bytes() + } + + #[must_use] + pub const fn as_inner(&self) -> &ed25519_dalek::Signature { + &self.0 + } +} + +impl Hash for Signature { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.0.to_bytes().hash(state); + } +} + +impl From for Signature { + fn from(sig: ed25519_dalek::Signature) -> Self { + Self(sig) + } +} + +impl From for ed25519_dalek::Signature { + fn from(sig: Signature) -> Self { + sig.0 + } +} + +impl From<[u8; SIGNATURE_SIZE]> for Signature { + fn from(bytes: [u8; SIGNATURE_SIZE]) -> Self { + Self::from_bytes(&bytes) + } +} diff --git a/bedrock_client/src/structs/tx.rs b/bedrock_client/src/structs/tx.rs new file mode 100644 index 00000000..16c083b0 --- /dev/null +++ b/bedrock_client/src/structs/tx.rs @@ -0,0 +1,9 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct SignedMantleTx { + pub mantle_tx: MantleTx, + pub ops_proofs: Vec, + // Not sure, if we need this. + // ledger_tx_proof: ZkSignature, +} \ No newline at end of file From f817279d33d818fcdd937aedf37e365ce0b38283 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 8 Jan 2026 09:10:00 +0200 Subject: [PATCH 03/31] fix: client as dependency --- Cargo.lock | 1841 +++++++++++++++++++---- Cargo.toml | 10 +- bedrock_client/Cargo.toml | 4 +- bedrock_client/src/lib.rs | 183 +-- bedrock_client/src/paths.rs | 6 - bedrock_client/src/structs/header_id.rs | 103 -- bedrock_client/src/structs/info.rs | 23 - bedrock_client/src/structs/mod.rs | 40 - bedrock_client/src/structs/signature.rs | 53 - bedrock_client/src/structs/tx.rs | 9 - indexer/Cargo.toml | 19 - indexer/src/client/mod.rs | 0 indexer/src/lib.rs | 1 - 13 files changed, 1578 insertions(+), 714 deletions(-) delete mode 100644 bedrock_client/src/paths.rs delete mode 100644 bedrock_client/src/structs/header_id.rs delete mode 100644 bedrock_client/src/structs/info.rs delete mode 100644 bedrock_client/src/structs/mod.rs delete mode 100644 bedrock_client/src/structs/signature.rs delete mode 100644 bedrock_client/src/structs/tx.rs delete mode 100644 indexer/Cargo.toml delete mode 100644 indexer/src/client/mod.rs delete mode 100644 indexer/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index e58d4e0e..0b1b7717 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,7 +69,7 @@ dependencies = [ "actix-rt", "actix-service", "actix-utils", - "base64 0.22.1", + "base64", "bitflags 2.10.0", "bytes", "bytestring", @@ -77,7 +77,7 @@ dependencies = [ "encoding_rs", "foldhash", "futures-core", - "h2", + "h2 0.3.27", "http 0.2.12", "httparse", "httpdate", @@ -236,7 +236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ "crypto-common", - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -367,16 +367,66 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "archery" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" +dependencies = [ + "triomphe", +] + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "ark-bn254" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a22f4561524cd949590d78d7d4c5df8f592430d221f7f3c9497bbafd8972120f" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-std 0.4.0", +] + [[package]] name = "ark-bn254" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" dependencies = [ - "ark-ec", - "ark-ff", + "ark-ec 0.5.0", + "ark-ff 0.5.0", "ark-r1cs-std", - "ark-std", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-crypto-primitives" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3a13b34da09176a8baba701233fdffbaa7c1b1192ce031a3da4e55ce1f1a56" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-relations 0.4.0", + "ark-serialize 0.4.2", + "ark-snark 0.4.0", + "ark-std 0.4.0", + "blake2", + "derivative", + "digest", + "sha2", ] [[package]] @@ -387,12 +437,12 @@ checksum = "1e0c292754729c8a190e50414fd1a37093c786c709899f29c9f7daccecfa855e" dependencies = [ "ahash 0.8.12", "ark-crypto-primitives-macros", - "ark-ec", - "ark-ff", - "ark-relations", - "ark-serialize", - "ark-snark", - "ark-std", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-relations 0.5.1", + "ark-serialize 0.5.0", + "ark-snark 0.5.1", + "ark-std 0.5.0", "blake2", "derivative", "digest", @@ -412,6 +462,23 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ec" version = "0.5.0" @@ -419,10 +486,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" dependencies = [ "ahash 0.8.12", - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", + "ark-ff 0.5.0", + "ark-poly 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", @@ -433,16 +500,36 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "arrayvec", "digest", "educe", @@ -453,6 +540,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-asm" version = "0.5.0" @@ -463,6 +560,19 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-ff-macros" version = "0.5.0" @@ -476,19 +586,47 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "ark-groth16" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ceafa83848c3e390f1cbf124bc3193b3e639b3f02009e0e290809a501b95fc" +dependencies = [ + "ark-crypto-primitives 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-relations 0.4.0", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + [[package]] name = "ark-groth16" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88f1d0f3a534bb54188b8dcc104307db6c56cdae574ddc3212aec0625740fc7e" dependencies = [ - "ark-crypto-primitives", - "ark-ec", - "ark-ff", - "ark-poly", - "ark-relations", - "ark-serialize", - "ark-std", + "ark-crypto-primitives 0.5.0", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-poly 0.5.0", + "ark-relations 0.5.1", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-poly" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" +dependencies = [ + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "hashbrown 0.13.2", ] [[package]] @@ -498,24 +636,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" dependencies = [ "ahash 0.8.12", - "ark-ff", - "ark-serialize", - "ark-std", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", "educe", "fnv", "hashbrown 0.15.5", ] +[[package]] +name = "ark-poly-commit" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a741492629ffcd228337676dc223a28551aa6792eedb8a2a22c767f00df6c89" +dependencies = [ + "ark-crypto-primitives 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-relations 0.4.0", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest", +] + [[package]] name = "ark-r1cs-std" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" dependencies = [ - "ark-ec", - "ark-ff", - "ark-relations", - "ark-std", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-relations 0.5.1", + "ark-std 0.5.0", "educe", "num-bigint", "num-integer", @@ -523,31 +678,66 @@ dependencies = [ "tracing", ] +[[package]] +name = "ark-relations" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00796b6efc05a3f48225e59cb6a2cda78881e7c390872d5786aaf112f31fb4f0" +dependencies = [ + "ark-ff 0.4.2", + "ark-std 0.4.0", + "tracing", + "tracing-subscriber", +] + [[package]] name = "ark-relations" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" dependencies = [ - "ark-ff", - "ark-std", + "ark-ff 0.5.0", + "ark-std 0.5.0", "tracing", "tracing-subscriber", ] +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive 0.4.2", + "ark-std 0.4.0", + "digest", + "num-bigint", +] + [[package]] name = "ark-serialize" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ - "ark-serialize-derive", - "ark-std", + "ark-serialize-derive 0.5.0", + "ark-std 0.5.0", "arrayvec", "digest", "num-bigint", ] +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ark-serialize-derive" version = "0.5.0" @@ -559,16 +749,38 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "ark-snark" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d3cc6833a335bb8a600241889ead68ee89a3cf8448081fb7694c0fe503da63" +dependencies = [ + "ark-ff 0.4.2", + "ark-relations 0.4.0", + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + [[package]] name = "ark-snark" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d368e2848c2d4c129ce7679a7d0d2d612b6a274d3ea6a13bad4445d61b381b88" dependencies = [ - "ark-ff", - "ark-relations", - "ark-serialize", - "ark-std", + "ark-ff 0.5.0", + "ark-relations 0.5.1", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", ] [[package]] @@ -587,6 +799,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -615,6 +833,17 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -627,24 +856,89 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower 0.5.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.4.0", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str", + "match-lookup", +] + [[package]] name = "base58" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -662,15 +956,13 @@ name = "bedrock_client" version = "0.1.0" dependencies = [ "anyhow", - "const-hex", - "ed25519-dalek", + "common-http-client", "futures", "log", - "reqwest 0.11.27", + "reqwest", "serde", "serde_json", - "serde_with", - "thiserror", + "thiserror 2.0.17", "url", ] @@ -767,7 +1059,19 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.7", +] + +[[package]] +name = "blst" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", ] [[package]] @@ -778,9 +1082,9 @@ checksum = "21055e2f49cbbdbfe9f8f96d597c5527b0c6ab7933341fdc2f147180e48a988e" dependencies = [ "duplicate", "maybe-async", - "reqwest 0.12.26", + "reqwest", "serde", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -806,6 +1110,31 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "broadcast-service" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "async-trait", + "derivative", + "futures", + "nomos-core", + "overwatch", + "serde", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bumpalo" version = "3.19.1" @@ -895,7 +1224,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -916,7 +1245,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -931,6 +1260,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "chacha20" version = "0.9.1" @@ -942,6 +1282,35 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "chain-service" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "async-trait", + "broadcast-service", + "bytes", + "cryptarchia-engine", + "cryptarchia-sync", + "futures", + "groth16", + "nomos-core", + "nomos-ledger", + "nomos-network", + "nomos-storage", + "nomos-utils", + "num-bigint", + "overwatch", + "serde", + "serde_with", + "services-utils", + "strum", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-futures", +] + [[package]] name = "chrono" version = "0.4.42" @@ -966,6 +1335,23 @@ dependencies = [ "inout", ] +[[package]] +name = "circuits-prover" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "circuits-utils", + "tempfile", +] + +[[package]] +name = "circuits-utils" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "dirs", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -1023,7 +1409,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -1037,17 +1423,35 @@ name = "common" version = "0.1.0" dependencies = [ "anyhow", - "base64 0.22.1", + "base64", "borsh", "hex", "log", "nssa", "nssa_core", - "reqwest 0.11.27", + "reqwest", "serde", "serde_json", "sha2", - "thiserror", + "thiserror 2.0.17", +] + +[[package]] +name = "common-http-client" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "broadcast-service", + "chain-service", + "futures", + "nomos-core", + "nomos-da-messages", + "nomos-http-api-common", + "reqwest", + "serde", + "serde_json", + "thiserror 1.0.69", + "url", ] [[package]] @@ -1081,12 +1485,27 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -1123,6 +1542,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1147,13 +1575,50 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "cryptarchia-engine" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "cfg_eval", + "nomos-utils", + "serde", + "serde_with", + "thiserror 1.0.69", + "time", + "tracing", +] + +[[package]] +name = "cryptarchia-sync" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "bytes", + "cryptarchia-engine", + "futures", + "nomos-core", + "rand 0.8.5", + "serde", + "serde_with", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "crypto-bigint" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "subtle", "zeroize", @@ -1165,7 +1630,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ - "generic-array", + "generic-array 0.14.7", "rand_core 0.6.4", "typenum", ] @@ -1191,6 +1656,7 @@ dependencies = [ "digest", "fiat-crypto", "rustc_version", + "serde", "subtle", "zeroize", ] @@ -1276,6 +1742,45 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "data-encoding-macro" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + [[package]] name = "der" version = "0.7.10" @@ -1522,7 +2027,7 @@ dependencies = [ "crypto-bigint", "digest", "ff", - "generic-array", + "generic-array 0.14.7", "group", "pem-rfc7468", "pkcs8", @@ -1717,6 +2222,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror 1.0.69", +] + [[package]] name = "futures" version = "0.3.31" @@ -1788,6 +2303,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -1817,6 +2338,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "generic-array" +version = "1.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf57c49a95fd1fe24b90b3033bee6dc7e8f1288d51494cb44e627c295e38542" +dependencies = [ + "rustversion", + "serde_core", + "typenum", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -1860,6 +2392,44 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.8.5", + "smallvec", + "spinning_top", +] + +[[package]] +name = "groth16" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-groth16 0.4.0", + "ark-serialize 0.4.2", + "generic-array 1.3.5", + "hex", + "num-bigint", + "serde", + "serde_json", + "thiserror 2.0.17", +] + [[package]] name = "group" version = "0.13.0" @@ -1890,12 +2460,46 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", + "indexmap 2.12.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.12", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -1960,6 +2564,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1996,17 +2609,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -2026,7 +2628,7 @@ dependencies = [ "bytes", "futures-core", "http 1.4.0", - "http-body 1.0.1", + "http-body", "pin-project-lite", ] @@ -2048,30 +2650,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.10", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.8.1" @@ -2082,9 +2660,11 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2 0.4.13", "http 1.4.0", - "http-body 1.0.1", + "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "pin-utils", @@ -2100,7 +2680,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.4.0", - "hyper 1.8.1", + "hyper", "hyper-util", "rustls", "rustls-pki-types", @@ -2112,15 +2692,18 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.32", + "http-body-util", + "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -2129,22 +2712,24 @@ version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures-channel", "futures-core", "futures-util", "http 1.4.0", - "http-body 1.0.1", - "hyper 1.8.1", + "http-body", + "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", "socket2 0.6.1", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2285,22 +2870,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" -[[package]] -name = "indexer" -version = "0.1.0" -dependencies = [ - "anyhow", - "base58", - "common", - "futures", - "log", - "nssa", - "nssa_core", - "serde", - "storage", - "tokio", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -2344,7 +2913,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ - "generic-array", + "generic-array 0.14.7", ] [[package]] @@ -2354,7 +2923,7 @@ dependencies = [ "actix", "actix-web", "anyhow", - "base64 0.22.1", + "base64", "borsh", "clap", "common", @@ -2405,6 +2974,24 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -2429,6 +3016,31 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jf-crhf" +version = "0.1.1" +source = "git+https://github.com/EspressoSystems/jellyfish?tag=jf-crhf-v0.1.1#8f3dce0bc2bd161b4648f6ac029dcc1a23aaf4c5" +dependencies = [ + "ark-serialize 0.4.2", + "ark-std 0.4.0", +] + +[[package]] +name = "jf-poseidon2" +version = "0.1.0" +source = "git+https://github.com/EspressoSystems/jellyfish.git?rev=dc166cf0f803c3e5067f9dfcc21e3dade986a447#dc166cf0f803c3e5067f9dfcc21e3dade986a447" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ff 0.4.2", + "ark-std 0.4.0", + "displaydoc", + "hex", + "jf-crhf", + "lazy_static", + "nimue", + "zeroize", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -2473,6 +3085,41 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "key-management-system-keys" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "async-trait", + "bytes", + "ed25519-dalek", + "generic-array 1.3.5", + "groth16", + "key-management-system-macros", + "nomos-blend-proofs", + "nomos-utils", + "num-bigint", + "poseidon2", + "rand_core 0.6.4", + "serde", + "subtle", + "thiserror 2.0.17", + "tokio", + "tracing", + "zeroize", + "zksign", +] + +[[package]] +name = "key-management-system-macros" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "key_protocol" version = "0.1.0" @@ -2491,7 +3138,42 @@ dependencies = [ "rand 0.8.5", "serde", "sha2", - "thiserror", + "thiserror 2.0.17", +] + +[[package]] +name = "kzgrs" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ark-bls12-381", + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-poly-commit", + "ark-serialize 0.4.2", + "blake2", + "blst", + "num-bigint", + "num-traits", + "rand 0.8.5", + "thiserror 1.0.69", +] + +[[package]] +name = "kzgrs-backend" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ark-ff 0.4.2", + "ark-poly 0.4.2", + "ark-serialize 0.4.2", + "blake2", + "itertools 0.12.1", + "kzgrs", + "nomos-core", + "rand 0.8.5", + "serde", ] [[package]] @@ -2554,6 +3236,21 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "libp2p-identity" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" +dependencies = [ + "bs58", + "hkdf", + "multihash", + "serde", + "sha2", + "thiserror 2.0.17", + "tracing", +] + [[package]] name = "libredox" version = "0.1.11" @@ -2588,6 +3285,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "light-poseidon" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a1ccadd0bb5a32c196da536fd72c59183de24a055f6bf0513bf845fefab862" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "num-bigint", + "thiserror 1.0.69", +] + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -2647,6 +3356,23 @@ dependencies = [ "libc", ] +[[package]] +name = "match-lookup" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "maybe-async" version = "0.2.10" @@ -2722,6 +3448,58 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mmr" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ark-ff 0.4.2", + "groth16", + "poseidon2", + "rpds", + "serde", +] + +[[package]] +name = "multiaddr" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", +] + +[[package]] +name = "multibase" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" +dependencies = [ + "base-x", + "base256emoji", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" +dependencies = [ + "core2", + "unsigned-varint", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -2739,6 +3517,30 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nimue" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0dc7d3b2b7bd112c0cecf7d6f4f16a174ee7a98e27615b1d08256d0176588f2" +dependencies = [ + "ark-ec 0.4.2", + "ark-ff 0.4.2", + "ark-serialize 0.4.2", + "digest", + "generic-array 0.14.7", + "hex", + "keccak", + "log", + "rand 0.8.5", + "zeroize", +] + +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "no_std_strings" version = "0.1.3" @@ -2755,6 +3557,215 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nomos-blend-crypto" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "blake2", + "groth16", + "nomos-utils", + "poq", + "poseidon2", + "rs-merkle-tree", + "serde", + "subtle", + "thiserror 1.0.69", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "nomos-blend-message" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "blake2", + "derivative", + "groth16", + "itertools 0.14.0", + "key-management-system-keys", + "nomos-blend-crypto", + "nomos-blend-proofs", + "nomos-core", + "nomos-utils", + "serde", + "serde-big-array", + "serde_with", + "thiserror 1.0.69", + "tracing", + "zeroize", +] + +[[package]] +name = "nomos-blend-proofs" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ed25519-dalek", + "generic-array 1.3.5", + "groth16", + "nomos-blend-crypto", + "num-bigint", + "poq", + "serde", + "thiserror 1.0.69", +] + +[[package]] +name = "nomos-core" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ark-ff 0.4.2", + "async-trait", + "bincode", + "blake2", + "bytes", + "const-hex", + "cryptarchia-engine", + "futures", + "generic-array 1.3.5", + "groth16", + "hex", + "key-management-system-keys", + "multiaddr", + "nom 8.0.0", + "nomos-blend-proofs", + "nomos-utils", + "num-bigint", + "pol", + "poseidon2", + "serde", + "strum", + "thiserror 1.0.69", + "tracing", +] + +[[package]] +name = "nomos-da-messages" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "blake2", + "futures", + "kzgrs-backend", + "nomos-core", + "serde", + "tokio", +] + +[[package]] +name = "nomos-http-api-common" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "axum", + "governor", + "key-management-system-keys", + "nomos-core", + "serde", + "serde_json", + "serde_with", + "tower_governor", +] + +[[package]] +name = "nomos-ledger" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "cryptarchia-engine", + "groth16", + "key-management-system-keys", + "mmr", + "nomos-blend-crypto", + "nomos-blend-message", + "nomos-blend-proofs", + "nomos-core", + "nomos-utils", + "num-bigint", + "rand 0.8.5", + "rpds", + "serde", + "thiserror 1.0.69", + "tracing", + "utxotree", +] + +[[package]] +name = "nomos-network" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "async-trait", + "cryptarchia-sync", + "futures", + "nomos-core", + "overwatch", + "serde", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "nomos-storage" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "async-trait", + "bytes", + "cryptarchia-engine", + "futures", + "libp2p-identity", + "multiaddr", + "nomos-core", + "overwatch", + "serde", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "nomos-utils" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "async-trait", + "blake2", + "cipher", + "const-hex", + "humantime", + "overwatch", + "rand 0.8.5", + "serde", + "serde_with", + "time", +] + +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "nssa" version = "0.1.0" @@ -2774,7 +3785,7 @@ dependencies = [ "serde", "sha2", "test_program_methods", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -2790,7 +3801,7 @@ dependencies = [ "risc0-zkvm", "serde", "serde_json", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -2855,6 +3866,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.7.5" @@ -2953,6 +3974,33 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "overwatch" +version = "0.1.0" +source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65adbd55e51f1e3f9eead62432a" +dependencies = [ + "async-trait", + "futures", + "overwatch-derive", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "overwatch-derive" +version = "0.1.0" +source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65adbd55e51f1e3f9eead62432a" +dependencies = [ + "convert_case 0.8.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -2997,6 +4045,26 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -3036,6 +4104,22 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "pol" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "circuits-prover", + "circuits-utils", + "groth16", + "num-bigint", + "num-traits", + "serde", + "serde_json", + "thiserror 2.0.17", + "witness-generator", +] + [[package]] name = "polyval" version = "0.6.2" @@ -3048,12 +4132,39 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "poq" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "circuits-prover", + "circuits-utils", + "groth16", + "num-bigint", + "pol", + "serde", + "serde_json", + "thiserror 2.0.17", + "witness-generator", +] + [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +[[package]] +name = "poseidon2" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ark-bn254 0.4.0", + "ark-ff 0.4.2", + "jf-poseidon2", + "num-bigint", +] + [[package]] name = "postcard" version = "1.1.3" @@ -3099,6 +4210,28 @@ dependencies = [ "toml_edit 0.23.10+spec-1.0.0", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -3188,6 +4321,21 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + [[package]] name = "quinn" version = "0.11.9" @@ -3202,7 +4350,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2 0.6.1", - "thiserror", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -3223,7 +4371,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -3326,6 +4474,15 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.10.0", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -3343,7 +4500,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -3401,65 +4558,30 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - [[package]] name = "reqwest" version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ - "base64 0.22.1", + "base64", "bytes", + "encoding_rs", "futures-channel", "futures-core", "futures-util", + "h2 0.4.13", "http 1.4.0", - "http-body 1.0.1", + "http-body", "http-body-util", - "hyper 1.8.1", + "hyper", "hyper-rustls", + "hyper-tls", "hyper-util", "js-sys", "log", + "mime", + "native-tls", "percent-encoding", "pin-project-lite", "quinn", @@ -3468,11 +4590,12 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "tower-service", "url", @@ -3619,11 +4742,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ff13f9b427254c5264e01aaa32e33f355525299b6829449295905778f3b1e8" dependencies = [ "anyhow", - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-groth16", - "ark-serialize", + "ark-bn254 0.5.0", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-groth16 0.5.0", + "ark-serialize 0.5.0", "bytemuck", "hex", "num-bigint", @@ -3731,6 +4854,16 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "rpds" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e75f485e819d4d3015e6c0d55d02a4fd3db47c1993d9e603e0361fba2bffb34" +dependencies = [ + "archery", + "serde", +] + [[package]] name = "rrs-lib" version = "0.1.0" @@ -3741,6 +4874,25 @@ dependencies = [ "paste", ] +[[package]] +name = "rs-merkle-tree" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7a3ef170810c387d31b64c0b59734abb0839dac2a8d137909e271bfdec9b1e0" +dependencies = [ + "ark-bn254 0.5.0", + "ark-ff 0.5.0", + "byteorder", + "futures", + "light-poseidon", + "quote", + "rand 0.9.2", + "syn 1.0.109", + "thiserror 2.0.17", + "tiny-keccak", + "tokio", +] + [[package]] name = "rsa" version = "0.9.9" @@ -3825,15 +4977,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pki-types" version = "1.13.2" @@ -3881,7 +5024,7 @@ dependencies = [ "sha2", "strum", "tempfile", - "thiserror", + "thiserror 2.0.17", "toml", "yaml-rust2", ] @@ -3933,7 +5076,7 @@ checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", - "generic-array", + "generic-array 0.14.7", "pkcs8", "serdect", "subtle", @@ -4020,7 +5163,7 @@ dependencies = [ "actix-web", "anyhow", "base58", - "base64 0.22.1", + "base64", "borsh", "common", "futures", @@ -4063,6 +5206,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -4096,6 +5248,17 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -4123,7 +5286,7 @@ version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ - "base64 0.22.1", + "base64", "chrono", "hex", "indexmap 1.9.3", @@ -4158,6 +5321,21 @@ dependencies = [ "serde", ] +[[package]] +name = "services-utils" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "async-trait", + "futures", + "log", + "overwatch", + "serde", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + [[package]] name = "sha1" version = "0.10.6" @@ -4253,6 +5431,15 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -4279,6 +5466,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "storage" version = "0.1.0" @@ -4286,7 +5479,7 @@ dependencies = [ "borsh", "common", "rocksdb", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -4344,12 +5537,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -4428,13 +5615,33 @@ dependencies = [ "risc0-zkvm", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", ] [[package]] @@ -4448,6 +5655,15 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "time" version = "0.3.44" @@ -4479,6 +5695,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -4552,6 +5777,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -4636,6 +5873,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.2" @@ -4645,10 +5893,11 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -4661,10 +5910,10 @@ dependencies = [ "bytes", "futures-util", "http 1.4.0", - "http-body 1.0.1", + "http-body", "iri-string", "pin-project-lite", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", ] @@ -4681,6 +5930,22 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "tower_governor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3790eac6ad3fb8d9d96c2b040ae06e2517aa24b067545d1078b96ae72f7bb9a7" +dependencies = [ + "axum", + "forwarded-header-value", + "governor", + "http 1.4.0", + "pin-project", + "thiserror 1.0.69", + "tower 0.4.13", + "tracing", +] + [[package]] name = "tracing" version = "0.1.43" @@ -4714,6 +5979,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "tracing-subscriber" version = "0.2.25" @@ -4723,6 +5998,12 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" + [[package]] name = "try-lock" version = "0.2.5" @@ -4790,6 +6071,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" + [[package]] name = "untrusted" version = "0.9.0" @@ -4820,6 +6107,20 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utxotree" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "ark-ff 0.4.2", + "groth16", + "num-bigint", + "poseidon2", + "rpds", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "valuable" version = "0.1.1" @@ -4845,7 +6146,7 @@ dependencies = [ "anyhow", "async-stream", "base58", - "base64 0.22.1", + "base64", "borsh", "bytemuck", "clap", @@ -5063,6 +6364,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.4.1" @@ -5081,15 +6393,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -5117,21 +6420,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5165,12 +6453,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5183,12 +6465,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5201,12 +6477,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5231,12 +6501,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5249,12 +6513,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5267,12 +6525,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5285,12 +6537,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5312,28 +6558,39 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "witness-generator" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "circuits-utils", + "tempfile", +] + [[package]] name = "writeable" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core 0.6.4", + "serde", + "zeroize", +] + [[package]] name = "yaml-rust2" version = "0.10.4" @@ -5461,3 +6718,19 @@ dependencies = [ "quote", "syn 2.0.111", ] + +[[package]] +name = "zksign" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +dependencies = [ + "circuits-prover", + "circuits-utils", + "groth16", + "num-bigint", + "poseidon2", + "serde", + "serde_json", + "thiserror 2.0.17", + "witness-generator", +] diff --git a/Cargo.toml b/Cargo.toml index 92e3b7d6..431cc2d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ members = [ "integration_tests/proc_macro_test_attribute", "examples/program_deployment", "examples/program_deployment/methods", - "examples/program_deployment/methods/guest", "indexer", "bedrock_client", + "examples/program_deployment/methods/guest", + "bedrock_client", ] [workspace.dependencies] @@ -77,9 +78,8 @@ borsh = "1.5.7" base58 = "0.2.0" itertools = "0.14.0" url = "2.5.4" -const-hex = { default-features = false, features = ["alloc"], version = "1" } -serde_with = { default-features = false, version = "3.14.0" } -ed25519-dalek = { default-features = false, version = "2" } + +common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", branch = "marbella-offsite-2025-12" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", @@ -98,4 +98,4 @@ actix-web = { version = "=4.1.0", default-features = false, features = [ "macros", ] } clap = { version = "4.5.42", features = ["derive", "env"] } -reqwest = { version = "0.11.16", features = ["json"] } +reqwest = { version = "0.12", features = ["json", "rustls-tls", "stream"] } diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml index bc0f9277..8ac9b73b 100644 --- a/bedrock_client/Cargo.toml +++ b/bedrock_client/Cargo.toml @@ -12,6 +12,4 @@ thiserror.workspace = true url.workspace = true futures.workspace = true serde_json.workspace = true -const-hex.workspace = true -serde_with.workspace = true -ed25519-dalek = { workspace = true, features = ["rand_core", "serde", "zeroize"] } +common-http-client.workspace = true \ No newline at end of file diff --git a/bedrock_client/src/lib.rs b/bedrock_client/src/lib.rs index d3d4fbb1..177e9e35 100644 --- a/bedrock_client/src/lib.rs +++ b/bedrock_client/src/lib.rs @@ -1,174 +1,21 @@ -//A copy of http-client from logos-blockchain -//ToDo: replace with dependency +use anyhow::Result; +use common_http_client::CommonHttpClient; +pub use common_http_client::BasicAuthCredentials; +pub use common_http_client::Error; +use reqwest::Client; -use std::{fmt::Debug, sync::Arc}; -use reqwest::{Client, ClientBuilder, RequestBuilder, StatusCode, Url}; -use serde::{Serialize, de::DeserializeOwned}; -use futures::{Stream, StreamExt as _}; +// Simple wrapper +// maybe extend in the future for our purposes +pub struct BedrockClient(pub CommonHttpClient); -pub mod paths; -pub mod structs; +impl BedrockClient { + pub fn new(auth: Option) -> Result { + let client = Client::builder() + //Add more fiedls if needed + .timeout(std::time::Duration::from_secs(60)) + .build()?; -use nomos_core::{mantle::SignedMantleTx}; - -use crate::{paths::{CRYPTARCHIA_INFO, CRYPTARCHIA_LIB_STREAM, STORAGE_BLOCK}, structs::{Block, BlockInfo, info::CryptarchiaInfo}}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Internal server error: {0}")] - Server(String), - #[error("Failed to execute request: {0}")] - Client(String), - #[error(transparent)] - Request(#[from] reqwest::Error), - #[error(transparent)] - Url(#[from] url::ParseError), -} - -#[derive(Clone)] -pub struct BasicAuthCredentials { - username: String, - password: Option, -} - -impl BasicAuthCredentials { - #[must_use] - pub const fn new(username: String, password: Option) -> Self { - Self { username, password } + Ok(BedrockClient(CommonHttpClient::new_with_client(client, auth))) } } -#[derive(Clone)] -pub struct CommonHttpClient { - client: Arc, - basic_auth: Option, -} - -impl CommonHttpClient { - #[must_use] - pub fn new(basic_auth: Option) -> Self { - let client = ClientBuilder::new() - .build() - .expect("Client from default settings should be able to build"); - Self { - client: Arc::new(client), - basic_auth, - } - } - - #[must_use] - pub fn new_with_client(client: Client, basic_auth: Option) -> Self { - Self { - client: Arc::new(client), - basic_auth, - } - } - - pub async fn post(&self, request_url: Url, request_body: &Req) -> Result - where - Req: Serialize + ?Sized + Send + Sync, - Res: DeserializeOwned + Send + Sync, - { - let request = self.client.post(request_url).json(request_body); - self.execute_request::(request).await - } - - pub async fn get( - &self, - request_url: Url, - request_body: Option<&Req>, - ) -> Result - where - Req: Serialize + ?Sized + Send + Sync, - Res: DeserializeOwned + Send + Sync, - { - let mut request = self.client.get(request_url); - if let Some(request_body) = request_body { - request = request.json(request_body); - } - self.execute_request::(request).await - } - - async fn execute_request( - &self, - mut request: RequestBuilder, - ) -> Result { - if let Some(basic_auth) = &self.basic_auth { - request = request.basic_auth(&basic_auth.username, basic_auth.password.as_deref()); - } - - let response = request.send().await.map_err(Error::Request)?; - let status = response.status(); - let body = response.text().await.map_err(Error::Request)?; - - match status { - StatusCode::OK => serde_json::from_str(&body) - .map_err(|e| Error::Server(format!("Failed to parse response: {e}"))), - StatusCode::INTERNAL_SERVER_ERROR => Err(Error::Server(body)), - _ => Err(Error::Server(format!( - "Unexpected response [{status}]: {body}", - ))), - } - } - - pub async fn get_lib_stream( - &self, - base_url: Url, - ) -> Result, Error> { - let request_url = base_url - .join(CRYPTARCHIA_LIB_STREAM.trim_start_matches('/')) - .map_err(Error::Url)?; - let mut request = self.client.get(request_url); - - if let Some(basic_auth) = &self.basic_auth { - request = request.basic_auth(&basic_auth.username, basic_auth.password.as_deref()); - } - - let response = request.send().await.map_err(Error::Request)?; - let status = response.status(); - - let lib_stream = response.bytes_stream().filter_map(async |item| { - let bytes = item.ok()?; - serde_json::from_slice::(&bytes).ok() - }); - match status { - StatusCode::OK => Ok(lib_stream), - StatusCode::INTERNAL_SERVER_ERROR => Err(Error::Server("Error".to_owned())), - _ => Err(Error::Server(format!("Unexpected response [{status}]",))), - } - } - - pub async fn get_block_by_id( - &self, - base_url: Url, - header_id: HeaderId, - ) -> Result>, Error> - where - HeaderId: Serialize + Send + Sync, - { - let request_url = base_url - .join(STORAGE_BLOCK.trim_start_matches('/')) - .map_err(Error::Url)?; - self.post(request_url, &header_id).await - } - - /// Get consensus info (tip, height, etc.) - pub async fn consensus_info(&self, base_url: Url) -> Result { - let request_url = base_url - .join(CRYPTARCHIA_INFO.trim_start_matches('/')) - .map_err(Error::Url)?; - self.get::<(), CryptarchiaInfo>(request_url, None).await - } - - /// Get a block by its header ID - pub async fn get_block( - &self, - base_url: Url, - header_id: HeaderId, - ) -> Result>, Error> { - let request_url = base_url - .join(STORAGE_BLOCK.trim_start_matches('/')) - .map_err(Error::Url)?; - self.post(request_url, &header_id).await - } -} diff --git a/bedrock_client/src/paths.rs b/bedrock_client/src/paths.rs deleted file mode 100644 index 5149f4d3..00000000 --- a/bedrock_client/src/paths.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub const CRYPTARCHIA_INFO: &str = "/cryptarchia/info"; -pub const CRYPTARCHIA_LIB_STREAM: &str = "/cryptarchia/lib-stream"; -pub const STORAGE_BLOCK: &str = "/storage/block"; - -pub const BLOCKS: &str = "/cryptarchia/blocks"; -pub const BLOCKS_STREAM: &str = "/cryptarchia/blocks/stream"; diff --git a/bedrock_client/src/structs/header_id.rs b/bedrock_client/src/structs/header_id.rs deleted file mode 100644 index 4b032bc4..00000000 --- a/bedrock_client/src/structs/header_id.rs +++ /dev/null @@ -1,103 +0,0 @@ -#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash, PartialOrd, Ord)] -pub struct HeaderId([u8; 32]); - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Invalid header id size: {0}")] - InvalidHeaderIdSize(usize), -} - -impl From<[u8; 32]> for HeaderId { - fn from(id: [u8; 32]) -> Self { - Self(id) - } -} - -impl From for [u8; 32] { - fn from(id: HeaderId) -> Self { - id.0 - } -} - -impl TryFrom<&[u8]> for HeaderId { - type Error = Error; - - fn try_from(slice: &[u8]) -> Result { - if slice.len() != 32 { - return Err(Error::InvalidHeaderIdSize(slice.len())); - } - let mut id = [0u8; 32]; - id.copy_from_slice(slice); - Ok(Self::from(id)) - } -} - -impl AsRef<[u8]> for HeaderId { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash)] -pub struct ContentId([u8; 32]); - -impl From for [u8; 32] { - fn from(id: ContentId) -> Self { - id.0 - } -} - -macro_rules! display_hex_bytes_newtype { - ($newtype:ty) => { - impl core::fmt::Display for $newtype { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - write!(f, "0x")?; - for v in self.0 { - write!(f, "{:02x}", v)?; - } - Ok(()) - } - } - }; -} - -macro_rules! serde_bytes_newtype { - ($newtype:ty, $len:expr) => { - impl serde::Serialize for $newtype { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - if serializer.is_human_readable() { - const_hex::const_encode::<$len, false>(&self.0) - .as_str() - .serialize(serializer) - } else { - self.0.serialize(serializer) - } - } - } - - impl<'de> serde::Deserialize<'de> for $newtype { - fn deserialize(deserializer: D) -> Result<$newtype, D::Error> - where - D: serde::Deserializer<'de>, - { - if deserializer.is_human_readable() { - let s = ::deserialize(deserializer)?; - const_hex::decode_to_array(s) - .map(Self) - .map_err(serde::de::Error::custom) - } else { - <[u8; $len] as serde::Deserialize>::deserialize(deserializer).map(Self) - } - } - } - }; -} - -display_hex_bytes_newtype!(HeaderId); -display_hex_bytes_newtype!(ContentId); - -serde_bytes_newtype!(HeaderId, 32); -serde_bytes_newtype!(ContentId, 32); \ No newline at end of file diff --git a/bedrock_client/src/structs/info.rs b/bedrock_client/src/structs/info.rs deleted file mode 100644 index 3e33246b..00000000 --- a/bedrock_client/src/structs/info.rs +++ /dev/null @@ -1,23 +0,0 @@ -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; - -use crate::structs::header_id::HeaderId; - -#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Slot(u64); - -#[derive(Clone, Debug, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum State { - Bootstrapping, - Online, -} - -#[serde_as] -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub struct CryptarchiaInfo { - pub lib: HeaderId, - pub tip: HeaderId, - pub slot: Slot, - pub height: u64, - pub mode: State, -} \ No newline at end of file diff --git a/bedrock_client/src/structs/mod.rs b/bedrock_client/src/structs/mod.rs deleted file mode 100644 index 36c2aab4..00000000 --- a/bedrock_client/src/structs/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -use ed25519_dalek::Signature; -use serde::{Deserialize, Serialize}; - -use crate::structs::{header_id::{ContentId, HeaderId}, info::Slot}; - -pub mod header_id; -pub mod info; -pub mod signature; -pub mod tx; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct BlockInfo { - pub height: u64, - pub header_id: HeaderId, -} - -pub const BEDROCK_VERSION: u8 = 1; - -#[derive(Clone, Debug, Eq, PartialEq, Copy, Serialize, Deserialize)] -#[repr(u8)] -pub enum Version { - Bedrock = BEDROCK_VERSION, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Header { - version: Version, - parent_block: HeaderId, - slot: Slot, - block_root: ContentId, - // Not sure, if need this. - // proof_of_leadership: Groth16LeaderProof, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct Block { - header: Header, - signature: Signature, - transactions: Vec, -} \ No newline at end of file diff --git a/bedrock_client/src/structs/signature.rs b/bedrock_client/src/structs/signature.rs deleted file mode 100644 index 1075354b..00000000 --- a/bedrock_client/src/structs/signature.rs +++ /dev/null @@ -1,53 +0,0 @@ -use core::hash::{Hash, Hasher}; - -use ed25519_dalek::SIGNATURE_LENGTH; -use serde::{Deserialize, Serialize}; - -pub const SIGNATURE_SIZE: usize = SIGNATURE_LENGTH; - -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] -pub struct Signature(ed25519_dalek::Signature); - -impl Signature { - #[must_use] - pub fn from_bytes(bytes: &[u8; SIGNATURE_SIZE]) -> Self { - Self(ed25519_dalek::Signature::from_bytes(bytes)) - } - - #[must_use] - pub fn to_bytes(&self) -> [u8; SIGNATURE_SIZE] { - self.0.to_bytes() - } - - #[must_use] - pub const fn as_inner(&self) -> &ed25519_dalek::Signature { - &self.0 - } -} - -impl Hash for Signature { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - self.0.to_bytes().hash(state); - } -} - -impl From for Signature { - fn from(sig: ed25519_dalek::Signature) -> Self { - Self(sig) - } -} - -impl From for ed25519_dalek::Signature { - fn from(sig: Signature) -> Self { - sig.0 - } -} - -impl From<[u8; SIGNATURE_SIZE]> for Signature { - fn from(bytes: [u8; SIGNATURE_SIZE]) -> Self { - Self::from_bytes(&bytes) - } -} diff --git a/bedrock_client/src/structs/tx.rs b/bedrock_client/src/structs/tx.rs deleted file mode 100644 index 16c083b0..00000000 --- a/bedrock_client/src/structs/tx.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, PartialEq, Eq, Serialize)] -pub struct SignedMantleTx { - pub mantle_tx: MantleTx, - pub ops_proofs: Vec, - // Not sure, if we need this. - // ledger_tx_proof: ZkSignature, -} \ No newline at end of file diff --git a/indexer/Cargo.toml b/indexer/Cargo.toml deleted file mode 100644 index 4b93ad05..00000000 --- a/indexer/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "indexer" -version = "0.1.0" -edition = "2024" - -[dependencies] -nssa.workspace = true -nssa_core.workspace = true -common.workspace = true -storage.workspace = true - -base58.workspace = true -anyhow.workspace = true -serde.workspace = true -log.workspace = true - -[dev-dependencies] -tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } -futures.workspace = true \ No newline at end of file diff --git a/indexer/src/client/mod.rs b/indexer/src/client/mod.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/indexer/src/lib.rs b/indexer/src/lib.rs deleted file mode 100644 index 39355176..00000000 --- a/indexer/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod client; \ No newline at end of file From f400684d5980409346605e6d269c4f1883c26789 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 8 Jan 2026 09:10:45 +0200 Subject: [PATCH 04/31] fix: fmt --- Cargo.toml | 2 +- bedrock_client/Cargo.toml | 2 +- bedrock_client/src/lib.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 431cc2d8..35d0a408 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = [ "integration_tests/proc_macro_test_attribute", "examples/program_deployment", "examples/program_deployment/methods", - "examples/program_deployment/methods/guest", + "examples/program_deployment/methods/guest", "bedrock_client", ] diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml index 8ac9b73b..5a93a1b0 100644 --- a/bedrock_client/Cargo.toml +++ b/bedrock_client/Cargo.toml @@ -12,4 +12,4 @@ thiserror.workspace = true url.workspace = true futures.workspace = true serde_json.workspace = true -common-http-client.workspace = true \ No newline at end of file +common-http-client.workspace = true diff --git a/bedrock_client/src/lib.rs b/bedrock_client/src/lib.rs index 177e9e35..9315f083 100644 --- a/bedrock_client/src/lib.rs +++ b/bedrock_client/src/lib.rs @@ -1,7 +1,6 @@ use anyhow::Result; use common_http_client::CommonHttpClient; -pub use common_http_client::BasicAuthCredentials; -pub use common_http_client::Error; +pub use common_http_client::{BasicAuthCredentials, Error}; use reqwest::Client; // Simple wrapper @@ -15,7 +14,8 @@ impl BedrockClient { .timeout(std::time::Duration::from_secs(60)) .build()?; - Ok(BedrockClient(CommonHttpClient::new_with_client(client, auth))) + Ok(BedrockClient(CommonHttpClient::new_with_client( + client, auth, + ))) } } - From 6b9eec61e5b38de7fb7ff9e8b5c817d24c69a01b Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 8 Jan 2026 09:26:11 +0200 Subject: [PATCH 05/31] fix: ci try 1 --- .../action.yml | 9 + .github/workflows/ci.yml | 10 + Cargo.lock | 6 - bedrock_client/Cargo.toml | 6 - ci_scripts/setup-nomos-circuits.sh | 203 ++++++++++++++++++ 5 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 .github/actions/install-nomos-circuits-compatibility/action.yml create mode 100644 ci_scripts/setup-nomos-circuits.sh diff --git a/.github/actions/install-nomos-circuits-compatibility/action.yml b/.github/actions/install-nomos-circuits-compatibility/action.yml new file mode 100644 index 00000000..41f1e0cc --- /dev/null +++ b/.github/actions/install-nomos-circuits-compatibility/action.yml @@ -0,0 +1,9 @@ +name: Install Nomos circuits +description: Installs Nomos circuits for compatibility +runs: + using: "composite" + steps: + - name: Install risc0 + run: | + /home/runner/lssa/ci_scripts/setup-nomos-circuits.sh + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21c73d29..2a90d2ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,6 +70,8 @@ jobs: - uses: ./.github/actions/install-risc0 + - uses: ./.github/actions/install-nomos-circuits-compatibility + - name: Install active toolchain run: rustup install @@ -95,6 +97,8 @@ jobs: - uses: ./.github/actions/install-risc0 + - uses: ./.github/actions/install-nomos-circuits-compatibility + - name: Install active toolchain run: rustup install @@ -118,6 +122,8 @@ jobs: - uses: ./.github/actions/install-risc0 + - uses: ./.github/actions/install-nomos-circuits-compatibility + - name: Install active toolchain run: rustup install @@ -139,6 +145,8 @@ jobs: - uses: ./.github/actions/install-risc0 + - uses: ./.github/actions/install-nomos-circuits-compatibility + - name: Install active toolchain run: rustup install @@ -161,6 +169,8 @@ jobs: - uses: ./.github/actions/install-risc0 + - uses: ./.github/actions/install-nomos-circuits-compatibility + - name: Install just run: cargo install just diff --git a/Cargo.lock b/Cargo.lock index 0b1b7717..df1c9ca6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,13 +957,7 @@ version = "0.1.0" dependencies = [ "anyhow", "common-http-client", - "futures", - "log", "reqwest", - "serde", - "serde_json", - "thiserror 2.0.17", - "url", ] [[package]] diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml index 5a93a1b0..034a093e 100644 --- a/bedrock_client/Cargo.toml +++ b/bedrock_client/Cargo.toml @@ -6,10 +6,4 @@ edition = "2024" [dependencies] reqwest.workspace = true anyhow.workspace = true -serde.workspace = true -log.workspace = true -thiserror.workspace = true -url.workspace = true -futures.workspace = true -serde_json.workspace = true common-http-client.workspace = true diff --git a/ci_scripts/setup-nomos-circuits.sh b/ci_scripts/setup-nomos-circuits.sh new file mode 100644 index 00000000..151c42a4 --- /dev/null +++ b/ci_scripts/setup-nomos-circuits.sh @@ -0,0 +1,203 @@ +#!/bin/bash +# +# Setup script for nomos-circuits +# +# Usage: ./setup-nomos-circuits.sh [VERSION] [INSTALL_DIR] +# +# Arguments: +# VERSION - Optional. Version to install (default: v0.3.1) +# INSTALL_DIR - Optional. Installation directory (default: $HOME/.nomos-circuits) +# +# Examples: +# ./setup-nomos-circuits.sh # Install default version to default location +# ./setup-nomos-circuits.sh v0.2.0 # Install specific version to default location +# ./setup-nomos-circuits.sh v0.2.0 /opt/circuits # Install to custom location + +# LSSA: needed for compatibility + +set -e + +# Default values +VERSION="${1:-v0.3.1}" +DEFAULT_INSTALL_DIR="$HOME/.nomos-circuits" +INSTALL_DIR="${2:-$DEFAULT_INSTALL_DIR}" +REPO="logos-co/nomos-circuits" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +print_info() { + echo -e "${BLUE}ℹ${NC} $1" +} + +print_success() { + echo -e "${GREEN}✓${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}⚠${NC} $1" +} + +print_error() { + echo -e "${RED}✗${NC} $1" +} + +# Detect OS and architecture +detect_platform() { + local os="" + local arch="" + + # Detect OS + case "$(uname -s)" in + Linux*) os="linux";; + Darwin*) os="macos";; + MINGW*|MSYS*|CYGWIN*) os="windows";; + *) print_error "Unsupported operating system: $(uname -s)"; exit 1;; + esac + + # Detect architecture + case "$(uname -m)" in + x86_64) arch="x86_64";; + aarch64) arch="aarch64";; + arm64) arch="aarch64";; + *) print_error "Unsupported architecture: $(uname -m)"; exit 1;; + esac + + echo "${os}-${arch}" +} + +# Check if installation directory exists and get confirmation +check_existing_installation() { + if [ -d "$INSTALL_DIR" ]; then + print_warning "Installation directory already exists: $INSTALL_DIR" + + # Check if it has a VERSION file + if [ -f "$INSTALL_DIR/VERSION" ]; then + local current_version=$(cat "$INSTALL_DIR/VERSION") + print_info "Currently installed version: $current_version" + fi + + # In non-interactive environments (CI), automatically overwrite + if [ ! -t 0 ]; then + print_info "Non-interactive environment detected, automatically overwriting..." + else + # Interactive environment - ask for confirmation + echo + read -p "Do you want to overwrite it? (y/N): " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + print_info "Installation cancelled." + exit 0 + fi + fi + + print_info "Removing existing installation..." + rm -rf "$INSTALL_DIR" + fi +} + +# Download and extract the release +download_release() { + local platform="$1" + local artifact="nomos-circuits-${VERSION}-${platform}.tar.gz" + local url="https://github.com/${REPO}/releases/download/${VERSION}/${artifact}" + local temp_dir=$(mktemp -d) + + print_info "Downloading nomos-circuits ${VERSION} for ${platform}..." + print_info "URL: $url" + + # Build curl command with optional authentication + local curl_cmd="curl -L" + if [ -n "$GITHUB_TOKEN" ]; then + curl_cmd="$curl_cmd --header 'authorization: Bearer ${GITHUB_TOKEN}'" + fi + curl_cmd="$curl_cmd -o ${temp_dir}/${artifact} $url" + + if ! eval "$curl_cmd"; then + print_error "Failed to download release artifact" + print_error "Please check that version ${VERSION} exists for platform ${platform}" + print_error "Available releases: https://github.com/${REPO}/releases" + rm -rf "$temp_dir" + exit 1 + fi + + print_success "Download complete" + + print_info "Extracting to ${INSTALL_DIR}..." + mkdir -p "$INSTALL_DIR" + + if ! tar -xzf "${temp_dir}/${artifact}" -C "$INSTALL_DIR" --strip-components=1; then + print_error "Failed to extract archive" + rm -rf "$temp_dir" + exit 1 + fi + + rm -rf "$temp_dir" + print_success "Extraction complete" +} + +# Handle macOS code signing/quarantine issues +handle_macos_quarantine() { + print_info "macOS detected: Removing quarantine attributes from executables..." + + # Remove quarantine attribute from all executable files + if find "$INSTALL_DIR" -type f -perm +111 -exec xattr -d com.apple.quarantine {} \; 2>/dev/null; then + print_success "Quarantine attributes removed" + else + print_warning "Could not remove quarantine attributes (they may not exist)" + fi +} + +# Main installation process +main() { + print_info "Setting up nomos-circuits ${VERSION}" + print_info "Installation directory: $INSTALL_DIR" + echo + + # Detect platform + local platform=$(detect_platform) + print_info "Detected platform: $platform" + + # Check existing installation + check_existing_installation + + # Download and extract + download_release "$platform" + + # Handle macOS quarantine if needed + if [[ "$platform" == macos-* ]]; then + echo + handle_macos_quarantine + fi + + echo + print_success "Installation complete!" + echo + print_info "nomos-circuits ${VERSION} is now installed at: $INSTALL_DIR" + print_info "The following circuits are available:" + + # Discover circuits by finding directories that contain a witness_generator + for dir in "$INSTALL_DIR"/*/; do + if [ -d "$dir" ]; then + local circuit_name=$(basename "$dir") + if [ -f "$dir/witness_generator" ]; then + echo " • $circuit_name" + fi + fi + done + + # Only show export instructions if not using the default location + if [ "$INSTALL_DIR" != "$DEFAULT_INSTALL_DIR" ]; then + echo + print_info "Since you're using a custom installation directory, set the environment variable:" + print_info " export NOMOS_CIRCUITS=$INSTALL_DIR" + echo + fi +} + +# Run main +main From fc179dcb51d96d505b4d39b6d31f5a5b6fe91fff Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 8 Jan 2026 09:29:43 +0200 Subject: [PATCH 06/31] fix: ci try 2 --- .github/actions/install-nomos-circuits-compatibility/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/install-nomos-circuits-compatibility/action.yml b/.github/actions/install-nomos-circuits-compatibility/action.yml index 41f1e0cc..cfeae0a4 100644 --- a/.github/actions/install-nomos-circuits-compatibility/action.yml +++ b/.github/actions/install-nomos-circuits-compatibility/action.yml @@ -5,5 +5,5 @@ runs: steps: - name: Install risc0 run: | - /home/runner/lssa/ci_scripts/setup-nomos-circuits.sh + /home/runner/work/lssa/lssa/ci_scripts/setup-nomos-circuits.sh shell: bash From 0a95e3ee62288ed5be424e70836da37084e481df Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 8 Jan 2026 09:32:24 +0200 Subject: [PATCH 07/31] fix: ci run 3 --- .github/actions/install-nomos-circuits-compatibility/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/install-nomos-circuits-compatibility/action.yml b/.github/actions/install-nomos-circuits-compatibility/action.yml index cfeae0a4..74e314cd 100644 --- a/.github/actions/install-nomos-circuits-compatibility/action.yml +++ b/.github/actions/install-nomos-circuits-compatibility/action.yml @@ -5,5 +5,6 @@ runs: steps: - name: Install risc0 run: | + chmod 777 /home/runner/work/lssa/lssa/ci_scripts/setup-nomos-circuits.sh /home/runner/work/lssa/lssa/ci_scripts/setup-nomos-circuits.sh shell: bash From 6f8a7295c233721a7ecfd6dce5b9e8440a79340d Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Thu, 8 Jan 2026 10:13:47 +0200 Subject: [PATCH 08/31] fix: deployment updates --- ci_scripts/deploy.sh | 6 +++++- sequencer_runner/Dockerfile | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ci_scripts/deploy.sh b/ci_scripts/deploy.sh index 7615df03..da23c4ac 100644 --- a/ci_scripts/deploy.sh +++ b/ci_scripts/deploy.sh @@ -52,10 +52,14 @@ if [ -d ".git" ]; then git reset --hard origin/main else echo "Cloning repository..." - git clone https://github.com/vacp2p/nescience-testnet.git . + git clone https://github.com/logos-blockchain/lssa.git . git checkout main fi +# Install Nomos circuits for compatibility +chmod 777 /ci_scripts/setup-nomos-circuits.sh +/ci_scripts/setup-nomos-circuits.sh + # Build sequencer_runner and wallet in release mode echo "Building sequencer_runner" # That could be just `cargo build --release --bin sequencer_runner --bin wallet` diff --git a/sequencer_runner/Dockerfile b/sequencer_runner/Dockerfile index 3b2153c3..b8b79b59 100644 --- a/sequencer_runner/Dockerfile +++ b/sequencer_runner/Dockerfile @@ -9,6 +9,10 @@ RUN apt-get update && apt-get install -y \ clang \ && rm -rf /var/lib/apt/lists/* +# Nomos circuits for compatibility +RUN chmod 777 /ci_scripts/setup-nomos-circuits.sh + && /ci_scripts/setup-nomos-circuits.sh + WORKDIR /sequencer_runner # Planner stage - generates dependency recipe From 3473602efcb1cda43544e8594d5fdfc1143c3b2b Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Fri, 9 Jan 2026 17:06:13 +0200 Subject: [PATCH 09/31] fix: try new branch of client --- .../action.yml | 10 - .github/workflows/ci.yml | 10 - Cargo.lock | 299 ++---------------- Cargo.toml | 2 +- ci_scripts/deploy.sh | 4 - ci_scripts/setup-nomos-circuits.sh | 203 ------------ sequencer_runner/Dockerfile | 4 - 7 files changed, 22 insertions(+), 510 deletions(-) delete mode 100644 .github/actions/install-nomos-circuits-compatibility/action.yml delete mode 100644 ci_scripts/setup-nomos-circuits.sh diff --git a/.github/actions/install-nomos-circuits-compatibility/action.yml b/.github/actions/install-nomos-circuits-compatibility/action.yml deleted file mode 100644 index 74e314cd..00000000 --- a/.github/actions/install-nomos-circuits-compatibility/action.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: Install Nomos circuits -description: Installs Nomos circuits for compatibility -runs: - using: "composite" - steps: - - name: Install risc0 - run: | - chmod 777 /home/runner/work/lssa/lssa/ci_scripts/setup-nomos-circuits.sh - /home/runner/work/lssa/lssa/ci_scripts/setup-nomos-circuits.sh - shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a90d2ec..21c73d29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,8 +70,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-nomos-circuits-compatibility - - name: Install active toolchain run: rustup install @@ -97,8 +95,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-nomos-circuits-compatibility - - name: Install active toolchain run: rustup install @@ -122,8 +118,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-nomos-circuits-compatibility - - name: Install active toolchain run: rustup install @@ -145,8 +139,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-nomos-circuits-compatibility - - name: Install active toolchain run: rustup install @@ -169,8 +161,6 @@ jobs: - uses: ./.github/actions/install-risc0 - - uses: ./.github/actions/install-nomos-circuits-compatibility - - name: Install just run: cargo install just diff --git a/Cargo.lock b/Cargo.lock index df1c9ca6..92f29eff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,15 +367,6 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" -[[package]] -name = "archery" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" -dependencies = [ - "triomphe", -] - [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -1107,7 +1098,7 @@ dependencies = [ [[package]] name = "broadcast-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "async-trait", "derivative", @@ -1276,35 +1267,6 @@ dependencies = [ "cpufeatures", ] -[[package]] -name = "chain-service" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "async-trait", - "broadcast-service", - "bytes", - "cryptarchia-engine", - "cryptarchia-sync", - "futures", - "groth16", - "nomos-core", - "nomos-ledger", - "nomos-network", - "nomos-storage", - "nomos-utils", - "num-bigint", - "overwatch", - "serde", - "serde_with", - "services-utils", - "strum", - "thiserror 1.0.69", - "tokio", - "tracing", - "tracing-futures", -] - [[package]] name = "chrono" version = "0.4.42" @@ -1332,7 +1294,7 @@ dependencies = [ [[package]] name = "circuits-prover" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "circuits-utils", "tempfile", @@ -1341,7 +1303,7 @@ dependencies = [ [[package]] name = "circuits-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "dirs", ] @@ -1433,10 +1395,9 @@ dependencies = [ [[package]] name = "common-http-client" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "broadcast-service", - "chain-service", "futures", "nomos-core", "nomos-da-messages", @@ -1491,15 +1452,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "convert_case" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "convert_case" version = "0.10.0" @@ -1578,7 +1530,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryptarchia-engine" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "cfg_eval", "nomos-utils", @@ -1589,23 +1541,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "cryptarchia-sync" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "bytes", - "cryptarchia-engine", - "futures", - "nomos-core", - "rand 0.8.5", - "serde", - "serde_with", - "thiserror 1.0.69", - "tokio", - "tracing", -] - [[package]] name = "crypto-bigint" version = "0.5.5" @@ -2409,7 +2344,7 @@ dependencies = [ [[package]] name = "groth16" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -3082,7 +3017,7 @@ dependencies = [ [[package]] name = "key-management-system-keys" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "async-trait", "bytes", @@ -3107,7 +3042,7 @@ dependencies = [ [[package]] name = "key-management-system-macros" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "proc-macro2", "quote", @@ -3138,7 +3073,7 @@ dependencies = [ [[package]] name = "kzgrs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "ark-bls12-381", "ark-ec 0.4.2", @@ -3157,7 +3092,7 @@ dependencies = [ [[package]] name = "kzgrs-backend" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "ark-ff 0.4.2", "ark-poly 0.4.2", @@ -3239,7 +3174,6 @@ dependencies = [ "bs58", "hkdf", "multihash", - "serde", "sha2", "thiserror 2.0.17", "tracing", @@ -3442,18 +3376,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "mmr" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "ark-ff 0.4.2", - "groth16", - "poseidon2", - "rpds", - "serde", -] - [[package]] name = "multiaddr" version = "0.18.2" @@ -3563,7 +3485,7 @@ dependencies = [ [[package]] name = "nomos-blend-crypto" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "blake2", "groth16", @@ -3578,32 +3500,10 @@ dependencies = [ "zeroize", ] -[[package]] -name = "nomos-blend-message" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "blake2", - "derivative", - "groth16", - "itertools 0.14.0", - "key-management-system-keys", - "nomos-blend-crypto", - "nomos-blend-proofs", - "nomos-core", - "nomos-utils", - "serde", - "serde-big-array", - "serde_with", - "thiserror 1.0.69", - "tracing", - "zeroize", -] - [[package]] name = "nomos-blend-proofs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", @@ -3618,7 +3518,7 @@ dependencies = [ [[package]] name = "nomos-core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "ark-ff 0.4.2", "async-trait", @@ -3648,7 +3548,7 @@ dependencies = [ [[package]] name = "nomos-da-messages" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "blake2", "futures", @@ -3661,7 +3561,7 @@ dependencies = [ [[package]] name = "nomos-http-api-common" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "axum", "governor", @@ -3673,68 +3573,10 @@ dependencies = [ "tower_governor", ] -[[package]] -name = "nomos-ledger" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "cryptarchia-engine", - "groth16", - "key-management-system-keys", - "mmr", - "nomos-blend-crypto", - "nomos-blend-message", - "nomos-blend-proofs", - "nomos-core", - "nomos-utils", - "num-bigint", - "rand 0.8.5", - "rpds", - "serde", - "thiserror 1.0.69", - "tracing", - "utxotree", -] - -[[package]] -name = "nomos-network" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "async-trait", - "cryptarchia-sync", - "futures", - "nomos-core", - "overwatch", - "serde", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "nomos-storage" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "async-trait", - "bytes", - "cryptarchia-engine", - "futures", - "libp2p-identity", - "multiaddr", - "nomos-core", - "overwatch", - "serde", - "thiserror 1.0.69", - "tokio", - "tracing", -] - [[package]] name = "nomos-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "async-trait", "blake2", @@ -3975,7 +3817,6 @@ source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65a dependencies = [ "async-trait", "futures", - "overwatch-derive", "thiserror 2.0.17", "tokio", "tokio-stream", @@ -3983,18 +3824,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "overwatch-derive" -version = "0.1.0" -source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65adbd55e51f1e3f9eead62432a" -dependencies = [ - "convert_case 0.8.0", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "parking_lot" version = "0.12.5" @@ -4101,7 +3930,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "pol" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "circuits-prover", "circuits-utils", @@ -4129,7 +3958,7 @@ dependencies = [ [[package]] name = "poq" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "circuits-prover", "circuits-utils", @@ -4151,7 +3980,7 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "poseidon2" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -4204,28 +4033,6 @@ dependencies = [ "toml_edit 0.23.10+spec-1.0.0", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "proc-macro2" version = "1.0.103" @@ -4848,16 +4655,6 @@ dependencies = [ "librocksdb-sys", ] -[[package]] -name = "rpds" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e75f485e819d4d3015e6c0d55d02a4fd3db47c1993d9e603e0361fba2bffb34" -dependencies = [ - "archery", - "serde", -] - [[package]] name = "rrs-lib" version = "0.1.0" @@ -5200,15 +4997,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-big-array" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" -dependencies = [ - "serde", -] - [[package]] name = "serde_core" version = "1.0.228" @@ -5315,21 +5103,6 @@ dependencies = [ "serde", ] -[[package]] -name = "services-utils" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "async-trait", - "futures", - "log", - "overwatch", - "serde", - "serde_json", - "thiserror 1.0.69", - "tracing", -] - [[package]] name = "sha1" version = "0.10.6" @@ -5973,16 +5746,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-subscriber" version = "0.2.25" @@ -5992,12 +5755,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "triomphe" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" - [[package]] name = "try-lock" version = "0.2.5" @@ -6101,20 +5858,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "utxotree" -version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" -dependencies = [ - "ark-ff 0.4.2", - "groth16", - "num-bigint", - "poseidon2", - "rpds", - "serde", - "thiserror 1.0.69", -] - [[package]] name = "valuable" version = "0.1.1" @@ -6561,7 +6304,7 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "witness-generator" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "circuits-utils", "tempfile", @@ -6716,7 +6459,7 @@ dependencies = [ [[package]] name = "zksign" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=marbella-offsite-2025-12#b47525f893353b0441a34a62b87c85ad27fb8519" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" dependencies = [ "circuits-prover", "circuits-utils", diff --git a/Cargo.toml b/Cargo.toml index 35d0a408..aa9da563 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ base58 = "0.2.0" itertools = "0.14.0" url = "2.5.4" -common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", branch = "marbella-offsite-2025-12" } +common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", branch = "feat-skip-zk-build" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/ci_scripts/deploy.sh b/ci_scripts/deploy.sh index da23c4ac..e84cac72 100644 --- a/ci_scripts/deploy.sh +++ b/ci_scripts/deploy.sh @@ -56,10 +56,6 @@ else git checkout main fi -# Install Nomos circuits for compatibility -chmod 777 /ci_scripts/setup-nomos-circuits.sh -/ci_scripts/setup-nomos-circuits.sh - # Build sequencer_runner and wallet in release mode echo "Building sequencer_runner" # That could be just `cargo build --release --bin sequencer_runner --bin wallet` diff --git a/ci_scripts/setup-nomos-circuits.sh b/ci_scripts/setup-nomos-circuits.sh deleted file mode 100644 index 151c42a4..00000000 --- a/ci_scripts/setup-nomos-circuits.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/bin/bash -# -# Setup script for nomos-circuits -# -# Usage: ./setup-nomos-circuits.sh [VERSION] [INSTALL_DIR] -# -# Arguments: -# VERSION - Optional. Version to install (default: v0.3.1) -# INSTALL_DIR - Optional. Installation directory (default: $HOME/.nomos-circuits) -# -# Examples: -# ./setup-nomos-circuits.sh # Install default version to default location -# ./setup-nomos-circuits.sh v0.2.0 # Install specific version to default location -# ./setup-nomos-circuits.sh v0.2.0 /opt/circuits # Install to custom location - -# LSSA: needed for compatibility - -set -e - -# Default values -VERSION="${1:-v0.3.1}" -DEFAULT_INSTALL_DIR="$HOME/.nomos-circuits" -INSTALL_DIR="${2:-$DEFAULT_INSTALL_DIR}" -REPO="logos-co/nomos-circuits" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -print_info() { - echo -e "${BLUE}ℹ${NC} $1" -} - -print_success() { - echo -e "${GREEN}✓${NC} $1" -} - -print_warning() { - echo -e "${YELLOW}⚠${NC} $1" -} - -print_error() { - echo -e "${RED}✗${NC} $1" -} - -# Detect OS and architecture -detect_platform() { - local os="" - local arch="" - - # Detect OS - case "$(uname -s)" in - Linux*) os="linux";; - Darwin*) os="macos";; - MINGW*|MSYS*|CYGWIN*) os="windows";; - *) print_error "Unsupported operating system: $(uname -s)"; exit 1;; - esac - - # Detect architecture - case "$(uname -m)" in - x86_64) arch="x86_64";; - aarch64) arch="aarch64";; - arm64) arch="aarch64";; - *) print_error "Unsupported architecture: $(uname -m)"; exit 1;; - esac - - echo "${os}-${arch}" -} - -# Check if installation directory exists and get confirmation -check_existing_installation() { - if [ -d "$INSTALL_DIR" ]; then - print_warning "Installation directory already exists: $INSTALL_DIR" - - # Check if it has a VERSION file - if [ -f "$INSTALL_DIR/VERSION" ]; then - local current_version=$(cat "$INSTALL_DIR/VERSION") - print_info "Currently installed version: $current_version" - fi - - # In non-interactive environments (CI), automatically overwrite - if [ ! -t 0 ]; then - print_info "Non-interactive environment detected, automatically overwriting..." - else - # Interactive environment - ask for confirmation - echo - read -p "Do you want to overwrite it? (y/N): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - print_info "Installation cancelled." - exit 0 - fi - fi - - print_info "Removing existing installation..." - rm -rf "$INSTALL_DIR" - fi -} - -# Download and extract the release -download_release() { - local platform="$1" - local artifact="nomos-circuits-${VERSION}-${platform}.tar.gz" - local url="https://github.com/${REPO}/releases/download/${VERSION}/${artifact}" - local temp_dir=$(mktemp -d) - - print_info "Downloading nomos-circuits ${VERSION} for ${platform}..." - print_info "URL: $url" - - # Build curl command with optional authentication - local curl_cmd="curl -L" - if [ -n "$GITHUB_TOKEN" ]; then - curl_cmd="$curl_cmd --header 'authorization: Bearer ${GITHUB_TOKEN}'" - fi - curl_cmd="$curl_cmd -o ${temp_dir}/${artifact} $url" - - if ! eval "$curl_cmd"; then - print_error "Failed to download release artifact" - print_error "Please check that version ${VERSION} exists for platform ${platform}" - print_error "Available releases: https://github.com/${REPO}/releases" - rm -rf "$temp_dir" - exit 1 - fi - - print_success "Download complete" - - print_info "Extracting to ${INSTALL_DIR}..." - mkdir -p "$INSTALL_DIR" - - if ! tar -xzf "${temp_dir}/${artifact}" -C "$INSTALL_DIR" --strip-components=1; then - print_error "Failed to extract archive" - rm -rf "$temp_dir" - exit 1 - fi - - rm -rf "$temp_dir" - print_success "Extraction complete" -} - -# Handle macOS code signing/quarantine issues -handle_macos_quarantine() { - print_info "macOS detected: Removing quarantine attributes from executables..." - - # Remove quarantine attribute from all executable files - if find "$INSTALL_DIR" -type f -perm +111 -exec xattr -d com.apple.quarantine {} \; 2>/dev/null; then - print_success "Quarantine attributes removed" - else - print_warning "Could not remove quarantine attributes (they may not exist)" - fi -} - -# Main installation process -main() { - print_info "Setting up nomos-circuits ${VERSION}" - print_info "Installation directory: $INSTALL_DIR" - echo - - # Detect platform - local platform=$(detect_platform) - print_info "Detected platform: $platform" - - # Check existing installation - check_existing_installation - - # Download and extract - download_release "$platform" - - # Handle macOS quarantine if needed - if [[ "$platform" == macos-* ]]; then - echo - handle_macos_quarantine - fi - - echo - print_success "Installation complete!" - echo - print_info "nomos-circuits ${VERSION} is now installed at: $INSTALL_DIR" - print_info "The following circuits are available:" - - # Discover circuits by finding directories that contain a witness_generator - for dir in "$INSTALL_DIR"/*/; do - if [ -d "$dir" ]; then - local circuit_name=$(basename "$dir") - if [ -f "$dir/witness_generator" ]; then - echo " • $circuit_name" - fi - fi - done - - # Only show export instructions if not using the default location - if [ "$INSTALL_DIR" != "$DEFAULT_INSTALL_DIR" ]; then - echo - print_info "Since you're using a custom installation directory, set the environment variable:" - print_info " export NOMOS_CIRCUITS=$INSTALL_DIR" - echo - fi -} - -# Run main -main diff --git a/sequencer_runner/Dockerfile b/sequencer_runner/Dockerfile index b8b79b59..3b2153c3 100644 --- a/sequencer_runner/Dockerfile +++ b/sequencer_runner/Dockerfile @@ -9,10 +9,6 @@ RUN apt-get update && apt-get install -y \ clang \ && rm -rf /var/lib/apt/lists/* -# Nomos circuits for compatibility -RUN chmod 777 /ci_scripts/setup-nomos-circuits.sh - && /ci_scripts/setup-nomos-circuits.sh - WORKDIR /sequencer_runner # Planner stage - generates dependency recipe From 7e2a8f4491e4b9de06235a7287a41b894f751bfc Mon Sep 17 00:00:00 2001 From: fryorcraken Date: Wed, 7 Jan 2026 16:10:38 +1100 Subject: [PATCH 10/31] add `wallet account list --long` option Display account details in the `account list` output when using the `-l|--long` option. --- wallet/src/cli/account.rs | 206 ++++++++++++++++++++++++++------------ 1 file changed, 141 insertions(+), 65 deletions(-) diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index d10c8c50..3a55c55f 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -31,7 +31,11 @@ pub enum AccountSubcommand { SyncPrivate {}, /// List all accounts owned by the wallet #[command(visible_alias = "ls")] - List {}, + List { + /// Show detailed account information (like `account get`) + #[arg(short, long)] + long: bool, + }, } /// Represents generic register CLI subcommand @@ -151,6 +155,50 @@ impl From for TokedHoldingAccountView { } } +/// Formats account details for display, returning (description, json_view) +fn format_account_details(account: &Account) -> (String, String) { + let auth_tr_prog_id = Program::authenticated_transfer_program().id(); + let token_prog_id = Program::token().id(); + + match &account.program_owner { + _ if account.program_owner == auth_tr_prog_id => { + let acc_view: AuthenticatedTransferAccountView = account.clone().into(); + ( + "Account owned by authenticated transfer program".to_string(), + serde_json::to_string(&acc_view).unwrap(), + ) + } + _ if account.program_owner == token_prog_id => { + if let Some(token_def) = TokenDefinition::parse(&account.data) { + let acc_view: TokedDefinitionAccountView = token_def.into(); + ( + "Definition account owned by token program".to_string(), + serde_json::to_string(&acc_view).unwrap(), + ) + } else if let Some(token_hold) = TokenHolding::parse(&account.data) { + let acc_view: TokedHoldingAccountView = token_hold.into(); + ( + "Holding account owned by token program".to_string(), + serde_json::to_string(&acc_view).unwrap(), + ) + } else { + let account_hr: HumanReadableAccount = account.clone().into(); + ( + "Unknown token program account".to_string(), + serde_json::to_string(&account_hr).unwrap(), + ) + } + } + _ => { + let account_hr: HumanReadableAccount = account.clone().into(); + ( + "Account".to_string(), + serde_json::to_string(&account_hr).unwrap(), + ) + } + } +} + impl WalletSubcommand for AccountSubcommand { async fn handle_subcommand( self, @@ -184,43 +232,9 @@ impl WalletSubcommand for AccountSubcommand { return Ok(SubcommandReturnValue::Empty); } - let auth_tr_prog_id = Program::authenticated_transfer_program().id(); - let token_prog_id = Program::token().id(); - - let acc_view = match &account.program_owner { - _ if account.program_owner == auth_tr_prog_id => { - let acc_view: AuthenticatedTransferAccountView = account.into(); - - println!("Account owned by authenticated transfer program"); - - serde_json::to_string(&acc_view)? - } - _ if account.program_owner == token_prog_id => { - if let Some(token_def) = TokenDefinition::parse(&account.data) { - let acc_view: TokedDefinitionAccountView = token_def.into(); - - println!("Definition account owned by token program"); - - serde_json::to_string(&acc_view)? - } else if let Some(token_hold) = TokenHolding::parse(&account.data) { - let acc_view: TokedHoldingAccountView = token_hold.into(); - - println!("Holding account owned by token program"); - - serde_json::to_string(&acc_view)? - } else { - anyhow::bail!( - "Invalid data for account {account_id:#?} with token program" - ); - } - } - _ => { - let account_hr: HumanReadableAccount = account.clone().into(); - serde_json::to_string(&account_hr).unwrap() - } - }; - - println!("{}", acc_view); + let (description, json_view) = format_account_details(&account); + println!("{description}"); + println!("{json_view}"); Ok(SubcommandReturnValue::Empty) } @@ -252,35 +266,97 @@ impl WalletSubcommand for AccountSubcommand { Ok(SubcommandReturnValue::SyncedToBlock(curr_last_block)) } - AccountSubcommand::List {} => { + AccountSubcommand::List { long } => { let user_data = &wallet_core.storage.user_data; - let accounts = user_data - .default_pub_account_signing_keys - .keys() - .map(|id| format!("Preconfigured Public/{id}")) - .chain( - user_data - .default_user_private_accounts - .keys() - .map(|id| format!("Preconfigured Private/{id}")), - ) - .chain( - user_data - .public_key_tree - .account_id_map - .iter() - .map(|(id, chain_index)| format!("{chain_index} Public/{id}")), - ) - .chain( - user_data - .private_key_tree - .account_id_map - .iter() - .map(|(id, chain_index)| format!("{chain_index} Private/{id}")), - ) - .format(",\n"); - println!("{accounts}"); + if !long { + let accounts = user_data + .default_pub_account_signing_keys + .keys() + .map(|id| format!("Preconfigured Public/{id}")) + .chain( + user_data + .default_user_private_accounts + .keys() + .map(|id| format!("Preconfigured Private/{id}")), + ) + .chain( + user_data + .public_key_tree + .account_id_map + .iter() + .map(|(id, chain_index)| format!("{chain_index} Public/{id}")), + ) + .chain( + user_data + .private_key_tree + .account_id_map + .iter() + .map(|(id, chain_index)| format!("{chain_index} Private/{id}")), + ) + .format(",\n"); + + println!("{accounts}"); + return Ok(SubcommandReturnValue::Empty); + } + + // Detailed listing with --long flag + // Preconfigured public accounts + for id in user_data.default_pub_account_signing_keys.keys() { + println!("Preconfigured Public/{id}"); + match wallet_core.get_account_public(*id).await { + Ok(account) if account != Account::default() => { + let (description, json_view) = format_account_details(&account); + println!(" {description}"); + println!(" {json_view}"); + } + Ok(_) => println!(" Uninitialized"), + Err(e) => println!(" Error fetching account: {e}"), + } + } + + // Preconfigured private accounts + for id in user_data.default_user_private_accounts.keys() { + println!("Preconfigured Private/{id}"); + match wallet_core.get_account_private(id) { + Some(account) if account != Account::default() => { + let (description, json_view) = format_account_details(&account); + println!(" {description}"); + println!(" {json_view}"); + } + Some(_) => println!(" Uninitialized"), + None => println!(" Not found in local storage"), + } + } + + // Public key tree accounts + for (id, chain_index) in user_data.public_key_tree.account_id_map.iter() { + println!("{chain_index} Public/{id}"); + match wallet_core.get_account_public(*id).await { + Ok(account) if account != Account::default() => { + let (description, json_view) = format_account_details(&account); + println!(" {description}"); + println!(" {json_view}"); + } + Ok(_) => println!(" Uninitialized"), + Err(e) => println!(" Error fetching account: {e}"), + } + } + + // Private key tree accounts + for (id, chain_index) in user_data.private_key_tree.account_id_map.iter() { + println!("{chain_index} Private/{id}"); + match wallet_core.get_account_private(id) { + Some(account) if account != Account::default() => { + let (description, json_view) = format_account_details(&account); + println!(" {description}"); + println!(" {json_view}"); + } + Some(_) => println!(" Uninitialized"), + None => println!(" Not found in local storage"), + } + } + Ok(SubcommandReturnValue::Empty) } } From deb6e9d8393e8d5c6ff86ed66127909d0706c079 Mon Sep 17 00:00:00 2001 From: Danish Arora <35004822+danisharora099@users.noreply.github.com> Date: Mon, 12 Jan 2026 02:43:34 -0500 Subject: [PATCH 11/31] fix: program deployment tutorial (#277) --- examples/program_deployment/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/program_deployment/README.md b/examples/program_deployment/README.md index 14a2baba..b0d59151 100644 --- a/examples/program_deployment/README.md +++ b/examples/program_deployment/README.md @@ -41,13 +41,15 @@ In a second terminal, from the `lssa` root directory, compile the example Risc0 ```bash cargo risczero build --manifest-path examples/program_deployment/methods/guest/Cargo.toml ``` -The compiled `.bin` files will appear under: +Because this repository is organized as a Cargo workspace, build artifacts are written to the +shared `target/` directory at the workspace root by default. The compiled `.bin` files will +appear under: ``` -examples/program_deployment/methods/guest/target/riscv32im-risc0-zkvm-elf/docker/ +target/riscv32im-risc0-zkvm-elf/docker/ ``` For convenience, export this path: ```bash -export EXAMPLE_PROGRAMS_BUILD_DIR=$(pwd)/examples/program_deployment/methods/guest/target/riscv32im-risc0-zkvm-elf/docker +export EXAMPLE_PROGRAMS_BUILD_DIR=$(pwd)/target/riscv32im-risc0-zkvm-elf/docker ``` > [!IMPORTANT] From 529ca9a643a657fdd72d612e1ad6b14e64c1d91c Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Wed, 31 Dec 2025 04:02:25 +0300 Subject: [PATCH 12/31] feat: introduce parallel integration tests, wallet without global vars and etc --- .github/workflows/ci.yml | 28 +- Cargo.lock | 21 +- Cargo.toml | 1 - common/src/sequencer_client.rs | 8 +- .../src/bin/run_hello_world.rs | 9 +- .../src/bin/run_hello_world_private.rs | 9 +- .../bin/run_hello_world_through_tail_call.rs | 9 +- ...n_hello_world_through_tail_call_private.rs | 9 +- .../bin/run_hello_world_with_authorization.rs | 11 +- ...uthorization_through_tail_call_with_pda.rs | 9 +- .../bin/run_hello_world_with_move_function.rs | 9 +- integration_tests/Cargo.toml | 6 +- .../sequencer/sequencer_config.json | 4 +- .../{debug => }/wallet/wallet_config.json | 6 +- .../proc_macro_test_attribute/Cargo.toml | 9 - .../proc_macro_test_attribute/src/lib.rs | 49 - integration_tests/src/lib.rs | 296 +- integration_tests/src/main.rs | 15 - integration_tests/src/test_suite_map.rs | 3154 ----------------- integration_tests/tests/account.rs | 28 + integration_tests/tests/amm.rs | 405 +++ integration_tests/tests/auth_transfer/main.rs | 2 + .../tests/auth_transfer/private.rs | 417 +++ .../tests/auth_transfer/public.rs | 248 ++ integration_tests/tests/config.rs | 33 + integration_tests/tests/keys_restoration.rs | 217 ++ integration_tests/tests/pinata.rs | 155 + integration_tests/tests/program_deployment.rs | 64 + integration_tests/tests/token.rs | 968 +++++ .../{src/tps_test_utils.rs => tests/tps.rs} | 83 +- key_protocol/src/key_protocol_core/mod.rs | 8 + sequencer_core/Cargo.toml | 1 + sequencer_core/src/config.rs | 16 +- sequencer_rpc/src/net_utils.rs | 21 +- sequencer_runner/src/config.rs | 11 - sequencer_runner/src/lib.rs | 14 +- wallet/Cargo.toml | 3 +- wallet/src/cli/account.rs | 12 +- wallet/src/cli/config.rs | 4 +- wallet/src/cli/mod.rs | 87 +- .../src/cli/programs/native_token_transfer.rs | 32 +- wallet/src/cli/programs/pinata.rs | 4 +- wallet/src/cli/programs/token.rs | 64 +- wallet/src/config.rs | 125 +- wallet/src/helperfunctions.rs | 101 +- wallet/src/lib.rs | 140 +- wallet/src/main.rs | 77 +- .../native_token_transfer/private.rs | 3 +- 48 files changed, 3218 insertions(+), 3787 deletions(-) rename integration_tests/configs/{debug => }/sequencer/sequencer_config.json (98%) rename integration_tests/configs/{debug => }/wallet/wallet_config.json (99%) delete mode 100644 integration_tests/proc_macro_test_attribute/Cargo.toml delete mode 100644 integration_tests/proc_macro_test_attribute/src/lib.rs delete mode 100644 integration_tests/src/main.rs delete mode 100644 integration_tests/src/test_suite_map.rs create mode 100644 integration_tests/tests/account.rs create mode 100644 integration_tests/tests/amm.rs create mode 100644 integration_tests/tests/auth_transfer/main.rs create mode 100644 integration_tests/tests/auth_transfer/private.rs create mode 100644 integration_tests/tests/auth_transfer/public.rs create mode 100644 integration_tests/tests/config.rs create mode 100644 integration_tests/tests/keys_restoration.rs create mode 100644 integration_tests/tests/pinata.rs create mode 100644 integration_tests/tests/program_deployment.rs create mode 100644 integration_tests/tests/token.rs rename integration_tests/{src/tps_test_utils.rs => tests/tps.rs} (74%) delete mode 100644 sequencer_runner/src/config.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21c73d29..ace2d07b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: RISC0_SKIP_BUILD: "1" run: cargo clippy -p "*programs" -- -D warnings - unit-tests: + tests: runs-on: ubuntu-latest timeout-minutes: 60 steps: @@ -104,6 +104,7 @@ jobs: - name: Run unit tests env: RISC0_DEV_MODE: "1" + RUST_LOG: "info" run: cargo nextest run --no-fail-fast valid-proof-test: @@ -123,31 +124,8 @@ jobs: - name: Test valid proof env: - NSSA_WALLET_HOME_DIR: ./integration_tests/configs/debug/wallet RUST_LOG: "info" - run: cargo run --bin integration_tests -- ./integration_tests/configs/debug/ test_success_private_transfer_to_another_owned_account - - integration-tests: - runs-on: ubuntu-latest - timeout-minutes: 120 - steps: - - uses: actions/checkout@v5 - with: - ref: ${{ github.head_ref }} - - - uses: ./.github/actions/install-system-deps - - - uses: ./.github/actions/install-risc0 - - - name: Install active toolchain - run: rustup install - - - name: Run integration tests - env: - NSSA_WALLET_HOME_DIR: ./integration_tests/configs/debug/wallet - RUST_LOG: "info" - RISC0_DEV_MODE: "1" - run: cargo run --bin integration_tests -- ./integration_tests/configs/debug/ all + run: cargo test -p integration_tests -- --exact private::private_transfer_to_owned_account artifacts: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 92f29eff..50edef2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2849,20 +2849,18 @@ dependencies = [ name = "integration_tests" version = "0.1.0" dependencies = [ - "actix", "actix-web", "anyhow", "base64", "borsh", - "clap", "common", "env_logger", + "futures", "hex", "key_protocol", "log", "nssa", "nssa_core", - "proc_macro_test_attribute", "sequencer_core", "sequencer_runner", "tempfile", @@ -3804,6 +3802,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "optfield" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "969ccca8ffc4fb105bd131a228107d5c9dd89d9d627edf3295cbe979156f9712" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -4054,10 +4063,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc_macro_test_attribute" -version = "0.1.0" - [[package]] name = "program_deployment" version = "0.1.0" @@ -4941,6 +4946,7 @@ dependencies = [ "nssa", "nssa_core", "serde", + "serde_json", "storage", "tempfile", "tokio", @@ -5897,6 +5903,7 @@ dependencies = [ "log", "nssa", "nssa_core", + "optfield", "rand 0.8.5", "risc0-zkvm", "serde", diff --git a/Cargo.toml b/Cargo.toml index aa9da563..30b4576a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,6 @@ members = [ "program_methods/guest", "test_program_methods", "test_program_methods/guest", - "integration_tests/proc_macro_test_attribute", "examples/program_deployment", "examples/program_deployment/methods", "examples/program_deployment/methods/guest", diff --git a/common/src/sequencer_client.rs b/common/src/sequencer_client.rs index 622c0c11..0cb03f6f 100644 --- a/common/src/sequencer_client.rs +++ b/common/src/sequencer_client.rs @@ -44,8 +44,10 @@ impl SequencerClient { ) -> Result { Ok(Self { client: Client::builder() - //Add more fiedls if needed + // Add more fields if needed .timeout(std::time::Duration::from_secs(60)) + // Should be kept in sync with server keep-alive settings + .pool_idle_timeout(std::time::Duration::from_secs(5)) .build()?, sequencer_addr, basic_auth, @@ -60,6 +62,10 @@ impl SequencerClient { let request = rpc_primitives::message::Request::from_payload_version_2_0(method.to_string(), payload); + log::debug!( + "Calling method {method} with payload {request:?} to sequencer at {}", + self.sequencer_addr + ); let mut call_builder = self.client.post(&self.sequencer_addr); if let Some((username, password)) = &self.basic_auth { diff --git a/examples/program_deployment/src/bin/run_hello_world.rs b/examples/program_deployment/src/bin/run_hello_world.rs index a7dc0fca..3c0c9034 100644 --- a/examples/program_deployment/src/bin/run_hello_world.rs +++ b/examples/program_deployment/src/bin/run_hello_world.rs @@ -3,7 +3,7 @@ use nssa::{ program::Program, public_transaction::{Message, WitnessSet}, }; -use wallet::{WalletCore, helperfunctions::fetch_config}; +use wallet::WalletCore; // Before running this example, compile the `hello_world.rs` guest program with: // @@ -24,11 +24,8 @@ use wallet::{WalletCore, helperfunctions::fetch_config}; #[tokio::main] async fn main() { - // Load wallet config and storage - let wallet_config = fetch_config().await.unwrap(); - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); + // Initialize wallet + let wallet_core = WalletCore::from_env().unwrap(); // Parse arguments // First argument is the path to the program binary diff --git a/examples/program_deployment/src/bin/run_hello_world_private.rs b/examples/program_deployment/src/bin/run_hello_world_private.rs index dcbe59a5..cb66d42f 100644 --- a/examples/program_deployment/src/bin/run_hello_world_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_private.rs @@ -1,5 +1,5 @@ use nssa::{AccountId, program::Program}; -use wallet::{PrivacyPreservingAccount, WalletCore, helperfunctions::fetch_config}; +use wallet::{PrivacyPreservingAccount, WalletCore}; // Before running this example, compile the `hello_world.rs` guest program with: // @@ -22,11 +22,8 @@ use wallet::{PrivacyPreservingAccount, WalletCore, helperfunctions::fetch_config #[tokio::main] async fn main() { - // Load wallet config and storage - let wallet_config = fetch_config().await.unwrap(); - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); + // Initialize wallet + let wallet_core = WalletCore::from_env().unwrap(); // Parse arguments // First argument is the path to the program binary diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call.rs index d7c91f89..56d28084 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call.rs @@ -3,7 +3,7 @@ use nssa::{ program::Program, public_transaction::{Message, WitnessSet}, }; -use wallet::{WalletCore, helperfunctions::fetch_config}; +use wallet::WalletCore; // Before running this example, compile the `simple_tail_call.rs` guest program with: // @@ -24,11 +24,8 @@ use wallet::{WalletCore, helperfunctions::fetch_config}; #[tokio::main] async fn main() { - // Load wallet config and storage - let wallet_config = fetch_config().await.unwrap(); - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); + // Initialize wallet + let wallet_core = WalletCore::from_env().unwrap(); // Parse arguments // First argument is the path to the program binary diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs index 5a014f24..bd67cfe6 100644 --- a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -4,7 +4,7 @@ use nssa::{ AccountId, ProgramId, privacy_preserving_transaction::circuit::ProgramWithDependencies, program::Program, }; -use wallet::{PrivacyPreservingAccount, WalletCore, helperfunctions::fetch_config}; +use wallet::{PrivacyPreservingAccount, WalletCore}; // Before running this example, compile the `simple_tail_call.rs` guest program with: // @@ -25,11 +25,8 @@ use wallet::{PrivacyPreservingAccount, WalletCore, helperfunctions::fetch_config #[tokio::main] async fn main() { - // Load wallet config and storage - let wallet_config = fetch_config().await.unwrap(); - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); + // Initialize wallet + let wallet_core = WalletCore::from_env().unwrap(); // Parse arguments // First argument is the path to the simple_tail_call program binary diff --git a/examples/program_deployment/src/bin/run_hello_world_with_authorization.rs b/examples/program_deployment/src/bin/run_hello_world_with_authorization.rs index 21740ae9..09fd621e 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_authorization.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_authorization.rs @@ -3,7 +3,7 @@ use nssa::{ program::Program, public_transaction::{Message, WitnessSet}, }; -use wallet::{WalletCore, helperfunctions::fetch_config}; +use wallet::WalletCore; // Before running this example, compile the `hello_world_with_authorization.rs` guest program with: // @@ -26,11 +26,8 @@ use wallet::{WalletCore, helperfunctions::fetch_config}; #[tokio::main] async fn main() { - // Load wallet config and storage - let wallet_config = fetch_config().await.unwrap(); - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); + // Initialize wallet + let wallet_core = WalletCore::from_env().unwrap(); // Parse arguments // First argument is the path to the program binary @@ -50,7 +47,7 @@ async fn main() { // Load signing keys to provide authorization let signing_key = wallet_core - .storage + .storage() .user_data .get_pub_account_signing_key(&account_id) .expect("Input account should be a self owned public account"); diff --git a/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs b/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs index 6c673d8b..43839ba9 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_authorization_through_tail_call_with_pda.rs @@ -4,7 +4,7 @@ use nssa::{ public_transaction::{Message, WitnessSet}, }; use nssa_core::program::PdaSeed; -use wallet::{WalletCore, helperfunctions::fetch_config}; +use wallet::WalletCore; // Before running this example, compile the `simple_tail_call.rs` guest program with: // @@ -27,11 +27,8 @@ const PDA_SEED: PdaSeed = PdaSeed::new([37; 32]); #[tokio::main] async fn main() { - // Load wallet config and storage - let wallet_config = fetch_config().await.unwrap(); - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); + // Initialize wallet + let wallet_core = WalletCore::from_env().unwrap(); // Parse arguments // First argument is the path to the program binary diff --git a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs index 4307315a..d879ab6f 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs @@ -1,6 +1,6 @@ use clap::{Parser, Subcommand}; use nssa::{PublicTransaction, program::Program, public_transaction}; -use wallet::{PrivacyPreservingAccount, WalletCore, helperfunctions::fetch_config}; +use wallet::{PrivacyPreservingAccount, WalletCore}; // Before running this example, compile the `hello_world_with_move_function.rs` guest program with: // @@ -62,11 +62,8 @@ async fn main() { let bytecode: Vec = std::fs::read(cli.program_path).unwrap(); let program = Program::new(bytecode).unwrap(); - // Load wallet config and storage - let wallet_config = fetch_config().await.unwrap(); - let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); + // Initialize wallet + let wallet_core = WalletCore::from_env().unwrap(); match cli.command { Command::WritePublic { diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index 52c6e385..b888c177 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -11,16 +11,14 @@ sequencer_runner.workspace = true wallet.workspace = true common.workspace = true key_protocol.workspace = true -proc_macro_test_attribute = { path = "./proc_macro_test_attribute" } -clap = { workspace = true, features = ["derive", "env"] } anyhow.workspace = true env_logger.workspace = true log.workspace = true -actix.workspace = true actix-web.workspace = true base64.workspace = true -tokio.workspace = true +tokio = { workspace = true, features = ["rt-multi-thread", "macros"] } hex.workspace = true tempfile.workspace = true borsh.workspace = true +futures.workspace = true diff --git a/integration_tests/configs/debug/sequencer/sequencer_config.json b/integration_tests/configs/sequencer/sequencer_config.json similarity index 98% rename from integration_tests/configs/debug/sequencer/sequencer_config.json rename to integration_tests/configs/sequencer/sequencer_config.json index db1c7f20..1548bb5b 100644 --- a/integration_tests/configs/debug/sequencer/sequencer_config.json +++ b/integration_tests/configs/sequencer/sequencer_config.json @@ -1,12 +1,12 @@ { - "home": "./sequencer", + "home": "", "override_rust_log": null, "genesis_id": 1, "is_genesis_random": true, "max_num_tx_in_block": 20, "mempool_max_size": 10000, "block_create_timeout_millis": 10000, - "port": 3040, + "port": 0, "initial_accounts": [ { "account_id": "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy", diff --git a/integration_tests/configs/debug/wallet/wallet_config.json b/integration_tests/configs/wallet/wallet_config.json similarity index 99% rename from integration_tests/configs/debug/wallet/wallet_config.json rename to integration_tests/configs/wallet/wallet_config.json index ad7b2792..2abab83e 100644 --- a/integration_tests/configs/debug/wallet/wallet_config.json +++ b/integration_tests/configs/wallet/wallet_config.json @@ -1,10 +1,11 @@ { "override_rust_log": null, - "sequencer_addr": "http://127.0.0.1:3040", + "sequencer_addr": "", "seq_poll_timeout_millis": 12000, "seq_tx_poll_max_blocks": 5, "seq_poll_max_retries": 5, "seq_block_poll_max_amount": 100, + "basic_auth": null, "initial_accounts": [ { "Public": { @@ -542,6 +543,5 @@ } } } - ], - "basic_auth": null + ] } \ No newline at end of file diff --git a/integration_tests/proc_macro_test_attribute/Cargo.toml b/integration_tests/proc_macro_test_attribute/Cargo.toml deleted file mode 100644 index a50c3f37..00000000 --- a/integration_tests/proc_macro_test_attribute/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "proc_macro_test_attribute" -version = "0.1.0" -edition = "2024" - -[dependencies] - -[lib] -proc-macro = true diff --git a/integration_tests/proc_macro_test_attribute/src/lib.rs b/integration_tests/proc_macro_test_attribute/src/lib.rs deleted file mode 100644 index 29852a0b..00000000 --- a/integration_tests/proc_macro_test_attribute/src/lib.rs +++ /dev/null @@ -1,49 +0,0 @@ -extern crate proc_macro; - -use proc_macro::*; - -#[proc_macro_attribute] -pub fn nssa_integration_test(_attr: TokenStream, item: TokenStream) -> TokenStream { - let input = item.to_string(); - - let fn_keyword = "fn "; - let fn_keyword_alternative = "fn\n"; - - let mut start_opt = None; - let mut fn_name = String::new(); - - if let Some(start) = input.find(fn_keyword) { - start_opt = Some(start); - } else if let Some(start) = input.find(fn_keyword_alternative) { - start_opt = Some(start); - } - - if let Some(start) = start_opt { - let rest = &input[start + fn_keyword.len()..]; - if let Some(end) = rest.find(|c: char| c == '(' || c.is_whitespace()) { - let name = &rest[..end]; - fn_name = name.to_string(); - } - } else { - println!("ERROR: keyword fn not found"); - } - - let extension = format!( - r#" - {input} - - function_map.insert("{fn_name}".to_string(), |home_dir: PathBuf| Box::pin(async {{ - let res = pre_test(home_dir).await.unwrap(); - - info!("Waiting for first block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - {fn_name}().await; - - post_test(res).await; - }})); - "# - ); - - extension.parse().unwrap() -} diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index 401238fa..12d718ec 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -1,38 +1,25 @@ -use std::path::PathBuf; +//! This library contains common code for integration tests. + +use std::{net::SocketAddr, path::PathBuf, sync::LazyLock}; use actix_web::dev::ServerHandle; -use anyhow::Result; +use anyhow::{Context as _, Result}; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; -use clap::Parser; use common::{ sequencer_client::SequencerClient, transaction::{EncodedTransaction, NSSATransaction}, }; -use log::{info, warn}; +use futures::FutureExt as _; +use log::debug; use nssa::PrivacyPreservingTransaction; use nssa_core::Commitment; use sequencer_core::config::SequencerConfig; -use sequencer_runner::startup_sequencer; use tempfile::TempDir; use tokio::task::JoinHandle; +use wallet::{WalletCore, config::WalletConfigOverrides}; -use crate::test_suite_map::{prepare_function_map, tps_test}; - -#[macro_use] -extern crate proc_macro_test_attribute; - -pub mod test_suite_map; - -mod tps_test_utils; - -#[derive(Parser, Debug)] -#[clap(version)] -struct Args { - /// Path to configs - home_dir: PathBuf, - /// Test name - test_name: String, -} +// TODO: Remove this and control time from tests +pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; pub const ACC_SENDER: &str = "BLgCRDXYdQPMMWVHYRFGQZbgeHx9frkipa8GtpG2Syqy"; pub const ACC_RECEIVER: &str = "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw"; @@ -40,104 +27,181 @@ pub const ACC_RECEIVER: &str = "Gj1mJy5W7J5pfmLRujmQaLfLMWidNxQ6uwnhb666ZwHw"; pub const ACC_SENDER_PRIVATE: &str = "3oCG8gqdKLMegw4rRfyaMQvuPHpcASt7xwttsmnZLSkw"; pub const ACC_RECEIVER_PRIVATE: &str = "AKTcXgJ1xoynta1Ec7y6Jso1z1JQtHqd7aPQ1h9er6xX"; -pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; - pub const NSSA_PROGRAM_FOR_TEST_DATA_CHANGER: &str = "data_changer.bin"; -fn make_public_account_input_from_str(account_id: &str) -> String { +static LOGGER: LazyLock<()> = LazyLock::new(env_logger::init); + +/// Test context which sets up a sequencer and a wallet for integration tests. +/// +/// It's memory and logically safe to create multiple instances of this struct in parallel tests, +/// as each instance uses its own temporary directories for sequencer and wallet data. +pub struct TestContext { + sequencer_server_handle: ServerHandle, + sequencer_loop_handle: JoinHandle>, + sequencer_client: SequencerClient, + wallet: WalletCore, + _temp_sequencer_dir: TempDir, + _temp_wallet_dir: TempDir, +} + +impl TestContext { + /// Create new test context. + pub async fn new() -> Result { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + + let sequencer_config_path = + PathBuf::from(manifest_dir).join("configs/sequencer/sequencer_config.json"); + + let sequencer_config = SequencerConfig::from_path(&sequencer_config_path) + .context("Failed to create sequencer config from file")?; + + Self::new_with_sequencer_config(sequencer_config).await + } + + /// Create new test context with custom sequencer config. + /// + /// `home` and `port` fields of the provided config will be overridden to meet tests parallelism + /// requirements. + pub async fn new_with_sequencer_config(sequencer_config: SequencerConfig) -> Result { + // Ensure logger is initialized only once + *LOGGER; + + debug!("Test context setup"); + + let (sequencer_server_handle, sequencer_addr, sequencer_loop_handle, temp_sequencer_dir) = + Self::setup_sequencer(sequencer_config) + .await + .context("Failed to setup sequencer")?; + + // Convert 0.0.0.0 to 127.0.0.1 for client connections + // When binding to port 0, the server binds to 0.0.0.0: + // but clients need to connect to 127.0.0.1: to work reliably + let sequencer_addr = if sequencer_addr.ip().is_unspecified() { + format!("http://127.0.0.1:{}", sequencer_addr.port()) + } else { + format!("http://{sequencer_addr}") + }; + + let (wallet, temp_wallet_dir) = Self::setup_wallet(sequencer_addr.clone()) + .await + .context("Failed to setup wallet")?; + + let sequencer_client = + SequencerClient::new(sequencer_addr).context("Failed to create sequencer client")?; + + Ok(Self { + sequencer_server_handle, + sequencer_loop_handle, + sequencer_client, + wallet, + _temp_sequencer_dir: temp_sequencer_dir, + _temp_wallet_dir: temp_wallet_dir, + }) + } + + async fn setup_sequencer( + mut config: SequencerConfig, + ) -> Result<(ServerHandle, SocketAddr, JoinHandle>, TempDir)> { + let temp_sequencer_dir = + tempfile::tempdir().context("Failed to create temp dir for sequencer home")?; + + debug!( + "Using temp sequencer home at {:?}", + temp_sequencer_dir.path() + ); + config.home = temp_sequencer_dir.path().to_owned(); + // Setting port to 0 lets the OS choose a free port for us + config.port = 0; + + let (sequencer_server_handle, sequencer_addr, sequencer_loop_handle) = + sequencer_runner::startup_sequencer(config).await?; + + Ok(( + sequencer_server_handle, + sequencer_addr, + sequencer_loop_handle, + temp_sequencer_dir, + )) + } + + async fn setup_wallet(sequencer_addr: String) -> Result<(WalletCore, TempDir)> { + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let wallet_config_source_path = + PathBuf::from(manifest_dir).join("configs/wallet/wallet_config.json"); + + let temp_wallet_dir = + tempfile::tempdir().context("Failed to create temp dir for wallet home")?; + + let config_path = temp_wallet_dir.path().join("wallet_config.json"); + std::fs::copy(&wallet_config_source_path, &config_path) + .context("Failed to copy wallet config to temp dir")?; + + let storage_path = temp_wallet_dir.path().join("storage.json"); + let config_overrides = WalletConfigOverrides { + sequencer_addr: Some(sequencer_addr), + ..Default::default() + }; + + let wallet = WalletCore::new_init_storage( + config_path, + storage_path, + Some(config_overrides), + "test_pass".to_owned(), + ) + .context("Failed to init wallet")?; + wallet + .store_persistent_data() + .await + .context("Failed to store wallet persistent data")?; + + Ok((wallet, temp_wallet_dir)) + } + + /// Get reference to the wallet. + pub fn wallet(&self) -> &WalletCore { + &self.wallet + } + + /// Get mutable reference to the wallet. + pub fn wallet_mut(&mut self) -> &mut WalletCore { + &mut self.wallet + } + + /// Get reference to the sequencer client. + pub fn sequencer_client(&self) -> &SequencerClient { + &self.sequencer_client + } +} + +impl Drop for TestContext { + fn drop(&mut self) { + debug!("Test context cleanup"); + + let Self { + sequencer_server_handle, + sequencer_loop_handle, + sequencer_client: _, + wallet: _, + _temp_sequencer_dir, + _temp_wallet_dir, + } = self; + + sequencer_loop_handle.abort(); + + // Can't wait here as Drop can't be async, but anyway stop signal should be sent + sequencer_server_handle.stop(true).now_or_never(); + } +} + +pub fn format_public_account_id(account_id: &str) -> String { format!("Public/{account_id}") } -fn make_private_account_input_from_str(account_id: &str) -> String { +pub fn format_private_account_id(account_id: &str) -> String { format!("Private/{account_id}") } -#[allow(clippy::type_complexity)] -pub async fn pre_test( - home_dir: PathBuf, -) -> Result<(ServerHandle, JoinHandle>, TempDir)> { - wallet::cli::execute_setup("test_pass".to_owned()).await?; - - let home_dir_sequencer = home_dir.join("sequencer"); - - let mut sequencer_config = - sequencer_runner::config::from_file(home_dir_sequencer.join("sequencer_config.json")) - .unwrap(); - - let temp_dir_sequencer = replace_home_dir_with_temp_dir_in_configs(&mut sequencer_config); - - let (seq_http_server_handle, sequencer_loop_handle) = - startup_sequencer(sequencer_config).await?; - - Ok(( - seq_http_server_handle, - sequencer_loop_handle, - temp_dir_sequencer, - )) -} - -pub fn replace_home_dir_with_temp_dir_in_configs( - sequencer_config: &mut SequencerConfig, -) -> TempDir { - let temp_dir_sequencer = tempfile::tempdir().unwrap(); - - sequencer_config.home = temp_dir_sequencer.path().to_path_buf(); - - temp_dir_sequencer -} - -#[allow(clippy::type_complexity)] -pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir)) { - let (seq_http_server_handle, sequencer_loop_handle, _) = residual; - - info!("Cleanup"); - - sequencer_loop_handle.abort(); - seq_http_server_handle.stop(true).await; - - let wallet_home = wallet::helperfunctions::get_home().unwrap(); - let persistent_data_home = wallet_home.join("storage.json"); - - // Removing persistent accounts after run to not affect other executions - // Not necessary an error, if fails as there is tests for failure scenario - let _ = std::fs::remove_file(persistent_data_home) - .inspect_err(|err| warn!("Failed to remove persistent data with err {err:#?}")); - - // At this point all of the references to sequencer_core must be lost. - // So they are dropped and tempdirs will be dropped too, -} - -pub async fn main_tests_runner() -> Result<()> { - env_logger::init(); - - let args = Args::parse(); - let Args { - home_dir, - test_name, - } = args; - - let function_map = prepare_function_map(); - - match test_name.as_str() { - "all" => { - // Tests that use default config - for (_, fn_pointer) in function_map { - fn_pointer(home_dir.clone()).await; - } - // Run TPS test with its own specific config - tps_test().await; - } - _ => { - let fn_pointer = function_map.get(&test_name).expect("Unknown test name"); - - fn_pointer(home_dir.clone()).await; - } - } - - Ok(()) -} - -async fn fetch_privacy_preserving_tx( +pub async fn fetch_privacy_preserving_tx( seq_client: &SequencerClient, tx_hash: String, ) -> PrivacyPreservingTransaction { @@ -161,7 +225,7 @@ async fn fetch_privacy_preserving_tx( } } -async fn verify_commitment_is_in_state( +pub async fn verify_commitment_is_in_state( commitment: Commitment, seq_client: &SequencerClient, ) -> bool { @@ -173,15 +237,15 @@ async fn verify_commitment_is_in_state( #[cfg(test)] mod tests { - use crate::{make_private_account_input_from_str, make_public_account_input_from_str}; + use super::{format_private_account_id, format_public_account_id}; #[test] fn correct_account_id_from_prefix() { let account_id1 = "cafecafe"; let account_id2 = "deadbeaf"; - let account_id1_pub = make_public_account_input_from_str(account_id1); - let account_id2_priv = make_private_account_input_from_str(account_id2); + let account_id1_pub = format_public_account_id(account_id1); + let account_id2_priv = format_private_account_id(account_id2); assert_eq!(account_id1_pub, "Public/cafecafe".to_string()); assert_eq!(account_id2_priv, "Private/deadbeaf".to_string()); diff --git a/integration_tests/src/main.rs b/integration_tests/src/main.rs deleted file mode 100644 index 583df2aa..00000000 --- a/integration_tests/src/main.rs +++ /dev/null @@ -1,15 +0,0 @@ -use anyhow::Result; -use integration_tests::main_tests_runner; - -pub const NUM_THREADS: usize = 8; - -fn main() -> Result<()> { - actix::System::with_tokio_rt(|| { - tokio::runtime::Builder::new_multi_thread() - .worker_threads(NUM_THREADS) - .enable_all() - .build() - .unwrap() - }) - .block_on(main_tests_runner()) -} diff --git a/integration_tests/src/test_suite_map.rs b/integration_tests/src/test_suite_map.rs deleted file mode 100644 index e722cc5f..00000000 --- a/integration_tests/src/test_suite_map.rs +++ /dev/null @@ -1,3154 +0,0 @@ -use std::{ - collections::HashMap, - path::PathBuf, - pin::Pin, - str::FromStr, - time::{Duration, Instant}, -}; - -use actix_web::dev::ServerHandle; -use anyhow::Result; -use common::{PINATA_BASE58, sequencer_client::SequencerClient}; -use key_protocol::key_management::key_tree::chain_index::ChainIndex; -use log::info; -use nssa::{AccountId, program::Program}; -use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; -use sequencer_runner::startup_sequencer; -use tempfile::TempDir; -use tokio::task::JoinHandle; -use wallet::{ - WalletCore, - cli::{ - Command, SubcommandReturnValue, - account::{AccountSubcommand, NewSubcommand}, - config::ConfigSubcommand, - programs::{ - amm::AmmProgramAgnosticSubcommand, native_token_transfer::AuthTransferSubcommand, - pinata::PinataProgramAgnosticSubcommand, token::TokenProgramAgnosticSubcommand, - }, - }, - config::PersistentStorage, - helperfunctions::{fetch_config, fetch_persistent_storage}, -}; - -use crate::{ - ACC_RECEIVER, ACC_RECEIVER_PRIVATE, ACC_SENDER, ACC_SENDER_PRIVATE, - NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, - fetch_privacy_preserving_tx, make_private_account_input_from_str, - make_public_account_input_from_str, post_test, pre_test, - replace_home_dir_with_temp_dir_in_configs, tps_test_utils::TpsTestManager, - verify_commitment_is_in_state, -}; - -type TestFunction = fn(PathBuf) -> Pin>>; - -pub fn prepare_function_map() -> HashMap { - let mut function_map: HashMap = HashMap::new(); - - #[nssa_integration_test] - pub async fn test_success() { - info!("########## test_success ##########"); - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(ACC_SENDER), - to: Some(make_public_account_input_from_str(ACC_RECEIVER)), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let acc_1_balance = seq_client - .get_account_balance(ACC_SENDER.to_string()) - .await - .unwrap(); - let acc_2_balance = seq_client - .get_account_balance(ACC_RECEIVER.to_string()) - .await - .unwrap(); - - info!("Balance of sender : {acc_1_balance:#?}"); - info!("Balance of receiver : {acc_2_balance:#?}"); - - assert_eq!(acc_1_balance.balance, 9900); - assert_eq!(acc_2_balance.balance, 20100); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_success_move_to_another_account() { - info!("########## test_success_move_to_another_account ##########"); - let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let PersistentStorage { - accounts: persistent_accounts, - last_synced_block: _, - } = fetch_persistent_storage().await.unwrap(); - - let mut new_persistent_account_id = String::new(); - - for per_acc in persistent_accounts { - if (per_acc.account_id().to_string() != ACC_RECEIVER) - && (per_acc.account_id().to_string() != ACC_SENDER) - { - new_persistent_account_id = per_acc.account_id().to_string(); - } - } - - if new_persistent_account_id == String::new() { - panic!("Failed to produce new account, not present in persistent accounts"); - } - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(ACC_SENDER), - to: Some(make_public_account_input_from_str( - &new_persistent_account_id, - )), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let acc_1_balance = seq_client - .get_account_balance(ACC_SENDER.to_string()) - .await - .unwrap(); - let acc_2_balance = seq_client - .get_account_balance(new_persistent_account_id) - .await - .unwrap(); - - info!("Balance of sender : {acc_1_balance:#?}"); - info!("Balance of receiver : {acc_2_balance:#?}"); - - assert_eq!(acc_1_balance.balance, 9900); - assert_eq!(acc_2_balance.balance, 100); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_failure() { - info!("########## test_failure ##########"); - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(ACC_SENDER), - to: Some(make_public_account_input_from_str(ACC_RECEIVER)), - to_npk: None, - to_ipk: None, - amount: 1000000, - }); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let failed_send = wallet::cli::execute_subcommand(command).await; - - assert!(failed_send.is_err()); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let acc_1_balance = seq_client - .get_account_balance(ACC_SENDER.to_string()) - .await - .unwrap(); - let acc_2_balance = seq_client - .get_account_balance(ACC_RECEIVER.to_string()) - .await - .unwrap(); - - info!("Balance of sender : {acc_1_balance:#?}"); - info!("Balance of receiver : {acc_2_balance:#?}"); - - assert_eq!(acc_1_balance.balance, 10000); - assert_eq!(acc_2_balance.balance, 20000); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_success_two_transactions() { - info!("########## test_success_two_transactions ##########"); - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(ACC_SENDER), - to: Some(make_public_account_input_from_str(ACC_RECEIVER)), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let acc_1_balance = seq_client - .get_account_balance(ACC_SENDER.to_string()) - .await - .unwrap(); - let acc_2_balance = seq_client - .get_account_balance(ACC_RECEIVER.to_string()) - .await - .unwrap(); - - info!("Balance of sender : {acc_1_balance:#?}"); - info!("Balance of receiver : {acc_2_balance:#?}"); - - assert_eq!(acc_1_balance.balance, 9900); - assert_eq!(acc_2_balance.balance, 20100); - - info!("First TX Success!"); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(ACC_SENDER), - to: Some(make_public_account_input_from_str(ACC_RECEIVER)), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let acc_1_balance = seq_client - .get_account_balance(ACC_SENDER.to_string()) - .await - .unwrap(); - let acc_2_balance = seq_client - .get_account_balance(ACC_RECEIVER.to_string()) - .await - .unwrap(); - - info!("Balance of sender : {acc_1_balance:#?}"); - info!("Balance of receiver : {acc_2_balance:#?}"); - - assert_eq!(acc_1_balance.balance, 9800); - assert_eq!(acc_2_balance.balance, 20200); - - info!("Second TX Success!"); - } - - #[nssa_integration_test] - pub async fn test_get_account() { - info!("########## test_get_account ##########"); - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let account = seq_client - .get_account(ACC_SENDER.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - account.program_owner, - Program::authenticated_transfer_program().id() - ); - assert_eq!(account.balance, 10000); - assert!(account.data.is_empty()); - assert_eq!(account.nonce, 0); - } - - /// This test creates a new token using the token program. After creating the token, the test - /// executes a token transfer to a new account. - #[nssa_integration_test] - pub async fn test_success_token_program() { - info!("########## test_success_token_program ##########"); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for receiving a token transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_public_account_input_from_str( - &definition_account_id.to_string(), - ), - supply_account_id: make_public_account_input_from_str(&supply_account_id.to_string()), - name: "A NAME".to_string(), - total_supply: 37, - }; - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!(definition_acc.program_owner, Program::token().id()); - // The data of a token definition account has the following layout: - // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 - // bytes)] - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - // Check the status of the token holding account with the total supply is the expected after - // the execution - let supply_acc = seq_client - .get_account(supply_account_id.to_string()) - .await - .unwrap() - .account; - - // The account must be owned by the token program - assert_eq!(supply_acc.program_owner, Program::token().id()); - // The data of a token definition account has the following layout: - // [ 0x01 || corresponding_token_definition_id (32 bytes) || balance (little endian 16 - // bytes) ] First byte of the data equal to 1 means it's a token holding account - assert_eq!(supply_acc.data.as_ref()[0], 1); - // Bytes from 1 to 33 represent the id of the token this account is associated with. - // In this example, this is a token account of the newly created token, so it is expected - // to be equal to the account_id of the token definition account. - assert_eq!( - &supply_acc.data.as_ref()[1..33], - definition_account_id.to_bytes() - ); - assert_eq!( - u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()), - 37 - ); - - // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_public_account_input_from_str(&supply_account_id.to_string()), - to: Some(make_public_account_input_from_str( - &recipient_account_id.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Check the status of the account at `supply_account_id` is the expected after the - // execution - let supply_acc = seq_client - .get_account(supply_account_id.to_string()) - .await - .unwrap() - .account; - // The account must be owned by the token program - assert_eq!(supply_acc.program_owner, Program::token().id()); - // First byte equal to 1 means it's a token holding account - assert_eq!(supply_acc.data[0], 1); - // Bytes from 1 to 33 represent the id of the token this account is associated with. - assert_eq!(&supply_acc.data[1..33], definition_account_id.to_bytes()); - assert_eq!( - u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()), - 30 - ); - - // Check the status of the account at `recipient_account_id` is the expected after the - // execution - let recipient_acc = seq_client - .get_account(recipient_account_id.to_string()) - .await - .unwrap() - .account; - - // The account must be owned by the token program - assert_eq!(recipient_acc.program_owner, Program::token().id()); - // First byte equal to 1 means it's a token holding account - assert_eq!(recipient_acc.data[0], 1); - // Bytes from 1 to 33 represent the id of the token this account is associated with. - assert_eq!(&recipient_acc.data[1..33], definition_account_id.to_bytes()); - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 7 - ); - - // Burn 3 tokens from `recipient_acc` - let subcommand = TokenProgramAgnosticSubcommand::Burn { - definition: make_public_account_input_from_str(&definition_account_id.to_string()), - holder: make_public_account_input_from_str(&recipient_account_id.to_string()), - amount: 3, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - // Check the status of the account at `recipient_account_id` is the expected after the - // execution - let recipient_acc = seq_client - .get_account(recipient_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 4 - ); - - // Mint 10 tokens at `recipient_acc` - let subcommand = TokenProgramAgnosticSubcommand::Mint { - definition: make_public_account_input_from_str(&definition_account_id.to_string()), - holder: Some(make_public_account_input_from_str( - &recipient_account_id.to_string(), - )), - holder_npk: None, - holder_ipk: None, - amount: 10, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - // Check the status of the account at `recipient_account_id` is the expected after the - // execution - let recipient_acc = seq_client - .get_account(recipient_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 14 - ); - } - - /// This test creates a new private token using the token program. After creating the token, the - /// test executes a private token transfer to a new account. All accounts are private owned - /// except definition which is public. - #[nssa_integration_test] - pub async fn test_success_token_program_private_owned_supply() { - info!("########## test_success_token_program_private_owned_supply ##########"); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition (public) - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for receiving a token transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_public_account_input_from_str( - &definition_account_id.to_string(), - ), - supply_account_id: make_private_account_input_from_str(&supply_account_id.to_string()), - name: "A NAME".to_string(), - total_supply: 37, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!(definition_acc.program_owner, Program::token().id()); - // The data of a token definition account has the following layout: - // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 - // bytes)] - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_private_account_input_from_str(&supply_account_id.to_string()), - to: Some(make_private_account_input_from_str( - &recipient_account_id.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Transfer additional 7 tokens from `supply_acc` to the account at account_id - // `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_private_account_input_from_str(&supply_account_id.to_string()), - to: Some(make_private_account_input_from_str( - &recipient_account_id.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Burn 3 tokens from `recipient_acc` - let subcommand = TokenProgramAgnosticSubcommand::Burn { - definition: make_public_account_input_from_str(&definition_account_id.to_string()), - holder: make_private_account_input_from_str(&recipient_account_id.to_string()), - amount: 3, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Check the status of the account at `recipient_account_id` is the expected after the - // execution - let recipient_acc = wallet_storage - .get_account_private(&recipient_account_id) - .unwrap(); - - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 11 - ); - - // Mint 10 tokens at `recipient_acc` - let subcommand = TokenProgramAgnosticSubcommand::Mint { - definition: make_public_account_input_from_str(&definition_account_id.to_string()), - holder: Some(make_private_account_input_from_str( - &recipient_account_id.to_string(), - )), - holder_npk: None, - holder_ipk: None, - amount: 10, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Check the status of the account at `recipient_account_id` is the expected after the - // execution - let recipient_acc = wallet_storage - .get_account_private(&recipient_account_id) - .unwrap(); - - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 21 - ); - - // Now the same mint, but in foreign way - - // Create new account for receiving a mint transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id2, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let (holder_keys, _) = wallet_storage - .storage - .user_data - .get_private_account(&recipient_account_id2) - .unwrap(); - - // Mint 9 tokens at `recipient_acc2` - let subcommand = TokenProgramAgnosticSubcommand::Mint { - definition: make_public_account_input_from_str(&definition_account_id.to_string()), - holder: None, - holder_npk: Some(hex::encode(holder_keys.nullifer_public_key.0)), - holder_ipk: Some(hex::encode( - holder_keys.incoming_viewing_public_key.0.clone(), - )), - amount: 9, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Sync to claim holder - let command = Command::Account(AccountSubcommand::SyncPrivate {}); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 53, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id2) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Check the status of the account at `recipient_account_id2` is the expected after the - // execution - let recipient_acc = wallet_storage - .get_account_private(&recipient_account_id2) - .unwrap(); - - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 9 - ); - } - - /// This test creates a new private token using the token program. All accounts are private - /// owned except supply which is public. - #[nssa_integration_test] - pub async fn test_success_token_program_private_owned_definition() { - info!("########## test_success_token_program_private_owned_definition ##########"); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition (private) - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { - cci: Some(ChainIndex::root()), - }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder (public) - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { - cci: Some(ChainIndex::root()), - }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_private_account_input_from_str( - &definition_account_id.to_string(), - ), - supply_account_id: make_public_account_input_from_str(&supply_account_id.to_string()), - name: "A NAME".to_string(), - total_supply: 37, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&definition_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - // Check the status of the token definition account is the expected after the execution - let supply_acc = seq_client - .get_account(supply_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!(supply_acc.program_owner, Program::token().id()); - // The data of a token holding account has the following layout: - // [ 0x01 || definition id (32 bytes) || balance (little endian 16 bytes) ] - assert_eq!( - supply_acc.data.as_ref(), - &[ - 1, 128, 101, 5, 31, 43, 36, 97, 108, 164, 92, 25, 157, 173, 5, 14, 194, 121, 239, - 84, 19, 160, 243, 47, 193, 2, 250, 247, 232, 253, 191, 232, 173, 37, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); - - // Create new account for receiving a mint transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id_pr, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new account for receiving a mint transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id_pub, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Mint 10 tokens at `recipient_acc_pub` - let subcommand = TokenProgramAgnosticSubcommand::Mint { - definition: make_private_account_input_from_str(&definition_account_id.to_string()), - holder: Some(make_public_account_input_from_str( - &recipient_account_id_pub.to_string(), - )), - holder_npk: None, - holder_ipk: None, - amount: 10, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = wallet_storage - .get_account_private(&definition_account_id) - .unwrap(); - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - // Check the status of the account at `recipient_account_id_pub` is the expected after the - // execution - let recipient_acc_pub = seq_client - .get_account(recipient_account_id_pub.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(recipient_acc_pub.data[33..].try_into().unwrap()), - 10 - ); - - let (holder_keys, _) = wallet_storage - .storage - .user_data - .get_private_account(&recipient_account_id_pr) - .unwrap(); - - // Mint 5 tokens at `recipient_acc_pr` - let subcommand = TokenProgramAgnosticSubcommand::Mint { - definition: make_private_account_input_from_str(&definition_account_id.to_string()), - holder: None, - holder_npk: Some(hex::encode(holder_keys.nullifer_public_key.0)), - holder_ipk: Some(hex::encode( - holder_keys.incoming_viewing_public_key.0.clone(), - )), - amount: 5, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Sync to claim holder - let command = Command::Account(AccountSubcommand::SyncPrivate {}); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = wallet_storage - .get_account_private(&definition_account_id) - .unwrap(); - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id_pr) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Check the status of the account at `recipient_account_id_pr` is the expected after the - // execution - let recipient_acc_pr = wallet_storage - .get_account_private(&recipient_account_id_pr) - .unwrap(); - - assert_eq!( - u128::from_le_bytes(recipient_acc_pr.data[33..].try_into().unwrap()), - 5 - ); - - // Mint 5 tokens at `recipient_acc_pr` - let subcommand = TokenProgramAgnosticSubcommand::Mint { - definition: make_private_account_input_from_str(&definition_account_id.to_string()), - holder: Some(make_private_account_input_from_str( - &recipient_account_id_pr.to_string(), - )), - holder_npk: None, - holder_ipk: None, - amount: 5, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = wallet_storage - .get_account_private(&definition_account_id) - .unwrap(); - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id_pr) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Check the status of the account at `recipient_account_id_pr` is the expected after the - // execution - let recipient_acc = wallet_storage - .get_account_private(&recipient_account_id_pr) - .unwrap(); - - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 10 - ); - - // Burn 5 tokens at `recipient_acc_pub` - let subcommand = TokenProgramAgnosticSubcommand::Burn { - definition: make_private_account_input_from_str(&definition_account_id.to_string()), - holder: make_public_account_input_from_str(&recipient_account_id_pub.to_string()), - amount: 5, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = wallet_storage - .get_account_private(&definition_account_id) - .unwrap(); - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - // Check the status of the account at `recipient_account_id_pub` is the expected after the - // execution - let recipient_acc_pub = seq_client - .get_account(recipient_account_id_pub.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(recipient_acc_pub.data[33..].try_into().unwrap()), - 5 - ); - - // Burn 5 tokens at `recipient_acc_pr` - let subcommand = TokenProgramAgnosticSubcommand::Burn { - definition: make_private_account_input_from_str(&definition_account_id.to_string()), - holder: make_private_account_input_from_str(&recipient_account_id_pr.to_string()), - amount: 5, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = wallet_storage - .get_account_private(&definition_account_id) - .unwrap(); - - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id_pr) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Check the status of the account at `recipient_account_id_pr` is the expected after the - // execution - let recipient_acc = wallet_storage - .get_account_private(&recipient_account_id_pr) - .unwrap(); - - assert_eq!( - u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), - 5 - ); - } - - /// This test creates a new private token using the token program. All accounts are private - /// owned. - #[nssa_integration_test] - pub async fn test_success_token_program_private_owned_definition_and_supply() { - info!( - "########## test_success_token_program_private_owned_definition_and_supply ##########" - ); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition (private) - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { - cci: Some(ChainIndex::root()), - }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { - cci: Some(ChainIndex::root()), - }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_private_account_input_from_str( - &definition_account_id.to_string(), - ), - supply_account_id: make_private_account_input_from_str(&supply_account_id.to_string()), - name: "A NAME".to_string(), - total_supply: 37, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&definition_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - let definition_acc = wallet_storage - .get_account_private(&definition_account_id) - .unwrap(); - let supply_acc = wallet_storage - .get_account_private(&supply_account_id) - .unwrap(); - - assert_eq!(definition_acc.program_owner, Program::token().id()); - // The data of a token definition account has the following layout: - // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 - // bytes)] - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - assert_eq!(supply_acc.program_owner, Program::token().id()); - // The data of a token holding account has the following layout: - // [ 0x01 || definition id (32 bytes) || balance (little endian 16 bytes) ] - assert_eq!( - supply_acc.data.as_ref(), - &[ - 1, 128, 101, 5, 31, 43, 36, 97, 108, 164, 92, 25, 157, 173, 5, 14, 194, 121, 239, - 84, 19, 160, 243, 47, 193, 2, 250, 247, 232, 253, 191, 232, 173, 37, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - ] - ); - } - - /// This test creates a new private token using the token program. After creating the token, the - /// test executes a private token transfer to a new account. - #[nssa_integration_test] - pub async fn test_success_token_program_private_claiming_path() { - info!("########## test_success_token_program_private_claiming_path ##########"); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition (public) - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for receiving a token transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_public_account_input_from_str( - &definition_account_id.to_string(), - ), - supply_account_id: make_private_account_input_from_str(&supply_account_id.to_string()), - name: "A NAME".to_string(), - total_supply: 37, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!(definition_acc.program_owner, Program::token().id()); - // The data of a token definition account has the following layout: - // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 - // bytes)] - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - let (recipient_keys, _) = wallet_storage - .storage - .user_data - .get_private_account(&recipient_account_id) - .unwrap(); - - // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_private_account_input_from_str(&supply_account_id.to_string()), - to: None, - to_npk: Some(hex::encode(recipient_keys.nullifer_public_key.0)), - to_ipk: Some(hex::encode( - recipient_keys.incoming_viewing_public_key.0.clone(), - )), - amount: 7, - }; - - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let command = Command::Account(AccountSubcommand::SyncPrivate {}); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - } - - /// This test creates a new public token using the token program. After creating the token, the - /// test executes a shielded token transfer to a new account. All accounts are owned except - /// definition. - #[nssa_integration_test] - pub async fn test_success_token_program_shielded_owned() { - info!("########## test_success_token_program_shielded_owned ##########"); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition (public) - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder (public) - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for receiving a token transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_public_account_input_from_str( - &definition_account_id.to_string(), - ), - supply_account_id: make_public_account_input_from_str(&supply_account_id.to_string()), - name: "A NAME".to_string(), - total_supply: 37, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!(definition_acc.program_owner, Program::token().id()); - // The data of a token definition account has the following layout: - // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 - // bytes)] - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_public_account_input_from_str(&supply_account_id.to_string()), - to: Some(make_private_account_input_from_str( - &recipient_account_id.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - // Transfer additional 7 tokens from `supply_acc` to the account at account_id - // `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_public_account_input_from_str(&supply_account_id.to_string()), - to: Some(make_private_account_input_from_str( - &recipient_account_id.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment2 = wallet_storage - .get_private_account_commitment(&recipient_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - } - - /// This test creates a new private token using the token program. After creating the token, the - /// test executes a deshielded token transfer to a new account. All accounts are owned - /// except definition. - #[nssa_integration_test] - pub async fn test_success_token_program_deshielded_owned() { - info!("########## test_success_token_program_deshielded_owned ##########"); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition (public) - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for receiving a token transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_public_account_input_from_str( - &definition_account_id.to_string(), - ), - supply_account_id: make_private_account_input_from_str(&supply_account_id.to_string()), - name: "A NAME".to_string(), - total_supply: 37, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - // Check the status of the token definition account is the expected after the execution - let definition_acc = seq_client - .get_account(definition_account_id.to_string()) - .await - .unwrap() - .account; - - assert_eq!(definition_acc.program_owner, Program::token().id()); - // The data of a token definition account has the following layout: - // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 - // bytes)] - assert_eq!( - definition_acc.data.as_ref(), - &[ - 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 - ] - ); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_private_account_input_from_str(&supply_account_id.to_string()), - to: Some(make_public_account_input_from_str( - &recipient_account_id.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - // Transfer additional 7 tokens from `supply_acc` to the account at account_id - // `recipient_account_id` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_private_account_input_from_str(&supply_account_id.to_string()), - to: Some(make_public_account_input_from_str( - &recipient_account_id.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&supply_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - } - - #[nssa_integration_test] - pub async fn test_success_private_transfer_to_another_owned_account() { - info!("########## test_success_private_transfer_to_another_owned_account ##########"); - let from: AccountId = ACC_SENDER_PRIVATE.parse().unwrap(); - let to: AccountId = ACC_RECEIVER_PRIVATE.parse().unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_private_account_input_from_str(&from.to_string()), - to: Some(make_private_account_input_from_str(&to.to_string())), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&from) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - let new_commitment2 = wallet_storage.get_private_account_commitment(&to).unwrap(); - assert!(verify_commitment_is_in_state(new_commitment2, &seq_client).await); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_success_private_transfer_to_another_foreign_account() { - info!("########## test_success_private_transfer_to_another_foreign_account ##########"); - let from: AccountId = ACC_SENDER_PRIVATE.parse().unwrap(); - let to_npk = NullifierPublicKey([42; 32]); - let to_npk_string = hex::encode(to_npk.0); - let to_ipk = Secp256k1Point::from_scalar(to_npk.0); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_private_account_input_from_str(&from.to_string()), - to: None, - to_npk: Some(to_npk_string), - to_ipk: Some(hex::encode(to_ipk.0)), - amount: 100, - }); - - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = - wallet::cli::execute_subcommand(command).await.unwrap() - else { - panic!("invalid subcommand return value"); - }; - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&from) - .unwrap(); - - let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; - assert_eq!(tx.message.new_commitments[0], new_commitment1); - - assert_eq!(tx.message.new_commitments.len(), 2); - for commitment in tx.message.new_commitments.into_iter() { - assert!(verify_commitment_is_in_state(commitment, &seq_client).await); - } - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_success_private_transfer_to_another_owned_account_claiming_path() { - info!( - "########## test_success_private_transfer_to_another_owned_account_claiming_path ##########" - ); - let from: AccountId = ACC_SENDER_PRIVATE.parse().unwrap(); - - let command = - Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })); - - let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - let SubcommandReturnValue::RegisterAccount { - account_id: to_account_id, - } = sub_ret - else { - panic!("FAILED TO REGISTER ACCOUNT"); - }; - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) - .await - .unwrap(); - - let (to_keys, _) = wallet_storage - .storage - .user_data - .get_private_account(&to_account_id) - .cloned() - .unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_private_account_input_from_str(&from.to_string()), - to: None, - to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)), - to_ipk: Some(hex::encode(to_keys.incoming_viewing_public_key.0)), - amount: 100, - }); - - let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { - panic!("FAILED TO SEND TX"); - }; - - let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; - - let command = Command::Account(AccountSubcommand::SyncPrivate {}); - wallet::cli::execute_subcommand(command).await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&from) - .unwrap(); - assert_eq!(tx.message.new_commitments[0], new_commitment1); - - assert_eq!(tx.message.new_commitments.len(), 2); - for commitment in tx.message.new_commitments.into_iter() { - assert!(verify_commitment_is_in_state(commitment, &seq_client).await); - } - - let to_res_acc = wallet_storage.get_account_private(&to_account_id).unwrap(); - - assert_eq!(to_res_acc.balance, 100); - - info!("Success!"); - } - - // #[nssa_integration_test] - // pub async fn test_success_private_transfer_to_another_owned_account_cont_run_path() { - // info!( - // "########## test_success_private_transfer_to_another_owned_account_cont_run_path - // ##########" ); - // let continious_run_handle = tokio::spawn(wallet::cli::execute_continious_run()); - - // let from: AccountId = ACC_SENDER_PRIVATE.parse().unwrap(); - - // let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private {})); - - // let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - // let SubcommandReturnValue::RegisterAccount { - // account_id: to_account_id, - // } = sub_ret - // else { - // panic!("FAILED TO REGISTER ACCOUNT"); - // }; - - // let wallet_config = fetch_config().await.unwrap(); - // let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - // let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) - // .await - // .unwrap(); - - // let (to_keys, _) = wallet_storage - // .storage - // .user_data - // .user_private_accounts - // .get(&to_account_id) - // .cloned() - // .unwrap(); - - // let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - // from: make_private_account_input_from_str(&from.to_string()), - // to: None, - // to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)), - // to_ipk: Some(hex::encode(to_keys.incoming_viewing_public_key.0)), - // amount: 100, - // }); - - // let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - // let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { - // panic!("FAILED TO SEND TX"); - // }; - - // let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash.clone()).await; - // println!("Waiting for next blocks to check if continoius run fetch account"); - // tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - // tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - // .await - // .unwrap(); - - // assert_eq!(tx.message.new_commitments.len(), 2); - // for commitment in tx.message.new_commitments.into_iter() { - // assert!(verify_commitment_is_in_state(commitment, &seq_client).await); - // } - - // let to_res_acc = wallet_storage.get_account_private(&to_account_id).unwrap(); - - // assert_eq!(to_res_acc.balance, 100); - - // continious_run_handle.abort(); - - // info!("Success!"); - // } - - #[nssa_integration_test] - pub async fn test_success_deshielded_transfer_to_another_account() { - info!("########## test_success_deshielded_transfer_to_another_account ##########"); - let from: AccountId = ACC_SENDER_PRIVATE.parse().unwrap(); - let to: AccountId = ACC_RECEIVER.parse().unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_private_account_input_from_str(&from.to_string()), - to: Some(make_public_account_input_from_str(&to.to_string())), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) - .await - .unwrap(); - - let from_acc = wallet_storage.get_account_private(&from).unwrap(); - assert_eq!(from_acc.balance, 10000); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let from_acc = wallet_storage.get_account_private(&from).unwrap(); - let new_commitment = wallet_storage - .get_private_account_commitment(&from) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment, &seq_client).await); - - let acc_2_balance = seq_client - .get_account_balance(to.to_string()) - .await - .unwrap(); - - assert_eq!(from_acc.balance, 10000 - 100); - assert_eq!(acc_2_balance.balance, 20100); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_success_shielded_transfer_to_another_owned_account() { - info!("########## test_success_shielded_transfer_to_another_owned_account ##########"); - let from: AccountId = ACC_SENDER.parse().unwrap(); - let to: AccountId = ACC_RECEIVER_PRIVATE.parse().unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(&from.to_string()), - to: Some(make_private_account_input_from_str(&to.to_string())), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let acc_to = wallet_storage.get_account_private(&to).unwrap(); - let new_commitment = wallet_storage.get_private_account_commitment(&to).unwrap(); - assert!(verify_commitment_is_in_state(new_commitment, &seq_client).await); - - let acc_from_balance = seq_client - .get_account_balance(from.to_string()) - .await - .unwrap(); - - assert_eq!(acc_from_balance.balance, 9900); - assert_eq!(acc_to.balance, 20000 + 100); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_success_shielded_transfer_to_another_foreign_account() { - info!("########## test_success_shielded_transfer_to_another_foreign_account ##########"); - let to_npk = NullifierPublicKey([42; 32]); - let to_npk_string = hex::encode(to_npk.0); - let to_ipk = Secp256k1Point::from_scalar(to_npk.0); - let from: AccountId = ACC_SENDER.parse().unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(&from.to_string()), - to: None, - to_npk: Some(to_npk_string), - to_ipk: Some(hex::encode(to_ipk.0)), - amount: 100, - }); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = - wallet::cli::execute_subcommand(command).await.unwrap() - else { - panic!("invalid subcommand return value"); - }; - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let tx = fetch_privacy_preserving_tx(&seq_client, tx_hash).await; - - let acc_1_balance = seq_client - .get_account_balance(from.to_string()) - .await - .unwrap(); - - assert!( - verify_commitment_is_in_state(tx.message.new_commitments[0].clone(), &seq_client).await - ); - - assert_eq!(acc_1_balance.balance, 9900); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_pinata() { - info!("########## test_pinata ##########"); - let pinata_account_id = PINATA_BASE58; - - let pinata_prize = 150; - let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { - to: make_public_account_input_from_str(ACC_SENDER), - }); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let pinata_balance_pre = seq_client - .get_account_balance(pinata_account_id.to_string()) - .await - .unwrap() - .balance; - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let pinata_balance_post = seq_client - .get_account_balance(pinata_account_id.to_string()) - .await - .unwrap() - .balance; - - let winner_balance_post = seq_client - .get_account_balance(ACC_SENDER.to_string()) - .await - .unwrap() - .balance; - - assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize); - assert_eq!(winner_balance_post, 10000 + pinata_prize); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_program_deployment() { - info!("########## test program deployment ##########"); - - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let binary_filepath: PathBuf = PathBuf::from(manifest_dir) - .join("../artifacts/test_program_methods") - .join(NSSA_PROGRAM_FOR_TEST_DATA_CHANGER); - - let command = Command::DeployProgram { - binary_filepath: binary_filepath.clone(), - }; - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // The program is the data changer and takes one account as input. - // We pass an uninitialized account and we expect after execution to be owned by the data - // changer program (NSSA account claiming mechanism) with data equal to [0] (due to program - // logic) - // - let bytecode = std::fs::read(binary_filepath).unwrap(); - let data_changer = Program::new(bytecode).unwrap(); - let account_id: AccountId = "11".repeat(16).parse().unwrap(); - let message = nssa::public_transaction::Message::try_new( - data_changer.id(), - vec![account_id], - vec![], - vec![0], - ) - .unwrap(); - let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); - let transaction = nssa::PublicTransaction::new(message, witness_set); - let _response = seq_client.send_tx_public(transaction).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let post_state_account = seq_client - .get_account(account_id.to_string()) - .await - .unwrap() - .account; - assert_eq!(post_state_account.program_owner, data_changer.id()); - assert_eq!(post_state_account.balance, 0); - assert_eq!(post_state_account.data.as_ref(), &[0]); - assert_eq!(post_state_account.nonce, 0); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_authenticated_transfer_initialize_function() { - info!("########## test initialize account for authenticated transfer ##########"); - let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })); - let SubcommandReturnValue::RegisterAccount { account_id } = - wallet::cli::execute_subcommand(command).await.unwrap() - else { - panic!("Error creating account"); - }; - - let command = Command::AuthTransfer(AuthTransferSubcommand::Init { - account_id: make_public_account_input_from_str(&account_id.to_string()), - }); - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Checking correct execution"); - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let account = seq_client - .get_account(account_id.to_string()) - .await - .unwrap() - .account; - - let expected_program_owner = Program::authenticated_transfer_program().id(); - let expected_nonce = 1; - let expected_balance = 0; - - assert_eq!(account.program_owner, expected_program_owner); - assert_eq!(account.balance, expected_balance); - assert_eq!(account.nonce, expected_nonce); - assert!(account.data.is_empty()); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_authenticated_transfer_initialize_function_private() { - info!("########## test initialize private account for authenticated transfer ##########"); - let command = - Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })); - let SubcommandReturnValue::RegisterAccount { account_id } = - wallet::cli::execute_subcommand(command).await.unwrap() - else { - panic!("Error creating account"); - }; - - let command = Command::AuthTransfer(AuthTransferSubcommand::Init { - account_id: make_private_account_input_from_str(&account_id.to_string()), - }); - wallet::cli::execute_subcommand(command).await.unwrap(); - - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct execution"); - let command = Command::Account(AccountSubcommand::SyncPrivate {}); - wallet::cli::execute_subcommand(command).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - let account = wallet_storage.get_account_private(&account_id).unwrap(); - - let expected_program_owner = Program::authenticated_transfer_program().id(); - let expected_balance = 0; - - assert_eq!(account.program_owner, expected_program_owner); - assert_eq!(account.balance, expected_balance); - assert!(account.data.is_empty()); - } - - #[nssa_integration_test] - pub async fn test_pinata_private_receiver() { - info!("########## test_pinata_private_receiver ##########"); - let pinata_account_id = PINATA_BASE58; - let pinata_prize = 150; - - let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { - to: make_private_account_input_from_str(ACC_SENDER_PRIVATE), - }); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let pinata_balance_pre = seq_client - .get_account_balance(pinata_account_id.to_string()) - .await - .unwrap() - .balance; - - let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = - wallet::cli::execute_subcommand(command).await.unwrap() - else { - panic!("invalid subcommand return value"); - }; - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let pinata_balance_post = seq_client - .get_account_balance(pinata_account_id.to_string()) - .await - .unwrap() - .balance; - - let command = Command::Account(AccountSubcommand::SyncPrivate {}); - wallet::cli::execute_subcommand(command).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&ACC_SENDER_PRIVATE.parse().unwrap()) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_pinata_private_receiver_new_account() { - info!("########## test_pinata_private_receiver_new_account ##########"); - let pinata_account_id = PINATA_BASE58; - let pinata_prize = 150; - - // Create new account for the token supply holder (private) - let SubcommandReturnValue::RegisterAccount { - account_id: winner_account_id, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Private { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { - to: make_private_account_input_from_str(&winner_account_id.to_string()), - }); - - let wallet_config = fetch_config().await.unwrap(); - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - let pinata_balance_pre = seq_client - .get_account_balance(pinata_account_id.to_string()) - .await - .unwrap() - .balance; - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("Checking correct balance move"); - let pinata_balance_post = seq_client - .get_account_balance(pinata_account_id.to_string()) - .await - .unwrap() - .balance; - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config) - .await - .unwrap(); - - let new_commitment1 = wallet_storage - .get_private_account_commitment(&winner_account_id) - .unwrap(); - assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await); - - assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_modify_config_fields() { - info!("########## test_modify_config_fields ##########"); - - let wallet_config = fetch_config().await.unwrap(); - let old_seq_poll_timeout_millis = wallet_config.seq_poll_timeout_millis; - - // Change config field - let command = Command::Config(ConfigSubcommand::Set { - key: "seq_poll_timeout_millis".to_string(), - value: "1000".to_string(), - }); - wallet::cli::execute_subcommand(command).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - - assert_eq!(wallet_config.seq_poll_timeout_millis, 1000); - - // Return how it was at the beginning - let command = Command::Config(ConfigSubcommand::Set { - key: "seq_poll_timeout_millis".to_string(), - value: old_seq_poll_timeout_millis.to_string(), - }); - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_keys_restoration() { - info!("########## test_keys_restoration ##########"); - let from: AccountId = ACC_SENDER_PRIVATE.parse().unwrap(); - - let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private { - cci: Some(ChainIndex::root()), - })); - - let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - let SubcommandReturnValue::RegisterAccount { - account_id: to_account_id1, - } = sub_ret - else { - panic!("FAILED TO REGISTER ACCOUNT"); - }; - - let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private { - cci: Some(ChainIndex::from_str("/0").unwrap()), - })); - - let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - let SubcommandReturnValue::RegisterAccount { - account_id: to_account_id2, - } = sub_ret - else { - panic!("FAILED TO REGISTER ACCOUNT"); - }; - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_private_account_input_from_str(&from.to_string()), - to: Some(make_private_account_input_from_str( - &to_account_id1.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 100, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_private_account_input_from_str(&from.to_string()), - to: Some(make_private_account_input_from_str( - &to_account_id2.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 101, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let from: AccountId = ACC_SENDER.parse().unwrap(); - - let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { - cci: Some(ChainIndex::root()), - })); - - let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - let SubcommandReturnValue::RegisterAccount { - account_id: to_account_id3, - } = sub_ret - else { - panic!("FAILED TO REGISTER ACCOUNT"); - }; - - let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { - cci: Some(ChainIndex::from_str("/0").unwrap()), - })); - - let sub_ret = wallet::cli::execute_subcommand(command).await.unwrap(); - let SubcommandReturnValue::RegisterAccount { - account_id: to_account_id4, - } = sub_ret - else { - panic!("FAILED TO REGISTER ACCOUNT"); - }; - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(&from.to_string()), - to: Some(make_public_account_input_from_str( - &to_account_id3.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 102, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(&from.to_string()), - to: Some(make_public_account_input_from_str( - &to_account_id4.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 103, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - info!("########## PREPARATION END ##########"); - - wallet::cli::execute_keys_restoration("test_pass".to_string(), 10) - .await - .unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) - .await - .unwrap(); - - let acc1 = wallet_storage - .storage - .user_data - .private_key_tree - .get_node(to_account_id1) - .expect("Acc 1 should be restored"); - - let acc2 = wallet_storage - .storage - .user_data - .private_key_tree - .get_node(to_account_id2) - .expect("Acc 2 should be restored"); - - let _ = wallet_storage - .storage - .user_data - .public_key_tree - .get_node(to_account_id3) - .expect("Acc 3 should be restored"); - - let _ = wallet_storage - .storage - .user_data - .public_key_tree - .get_node(to_account_id4) - .expect("Acc 4 should be restored"); - - assert_eq!( - acc1.value.1.program_owner, - Program::authenticated_transfer_program().id() - ); - assert_eq!( - acc2.value.1.program_owner, - Program::authenticated_transfer_program().id() - ); - - assert_eq!(acc1.value.1.balance, 100); - assert_eq!(acc2.value.1.balance, 101); - - info!("########## TREE CHECKS END ##########"); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_private_account_input_from_str(&to_account_id1.to_string()), - to: Some(make_private_account_input_from_str( - &to_account_id2.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 10, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let command = Command::AuthTransfer(AuthTransferSubcommand::Send { - from: make_public_account_input_from_str(&to_account_id3.to_string()), - to: Some(make_public_account_input_from_str( - &to_account_id4.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 11, - }); - - wallet::cli::execute_subcommand(command).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()) - .await - .unwrap(); - - let comm1 = wallet_storage - .get_private_account_commitment(&to_account_id1) - .expect("Acc 1 commitment should exist"); - let comm2 = wallet_storage - .get_private_account_commitment(&to_account_id2) - .expect("Acc 2 commitment should exist"); - - assert!(verify_commitment_is_in_state(comm1, &seq_client).await); - assert!(verify_commitment_is_in_state(comm2, &seq_client).await); - - let acc3 = seq_client - .get_account_balance(to_account_id3.to_string()) - .await - .expect("Acc 3 must be present in public state"); - let acc4 = seq_client - .get_account_balance(to_account_id4.to_string()) - .await - .expect("Acc 4 must be present in public state"); - - assert_eq!(acc3.balance, 91); - assert_eq!(acc4.balance, 114); - - info!("Success!"); - } - - #[nssa_integration_test] - pub async fn test_amm_public() { - info!("########## test_amm_public ##########"); - let wallet_config = fetch_config().await.unwrap(); - - // Create new account for the token definition - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id_1, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id_1, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for receiving a token transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id_1, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token definition - let SubcommandReturnValue::RegisterAccount { - account_id: definition_account_id_2, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for the token supply holder - let SubcommandReturnValue::RegisterAccount { - account_id: supply_account_id_2, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - // Create new account for receiving a token transaction - let SubcommandReturnValue::RegisterAccount { - account_id: recipient_account_id_2, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_public_account_input_from_str( - &definition_account_id_1.to_string(), - ), - supply_account_id: make_public_account_input_from_str(&supply_account_id_1.to_string()), - name: "A NAM1".to_string(), - total_supply: 37, - }; - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_1` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_public_account_input_from_str(&supply_account_id_1.to_string()), - to: Some(make_public_account_input_from_str( - &recipient_account_id_1.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - // Create new token - let subcommand = TokenProgramAgnosticSubcommand::New { - definition_account_id: make_public_account_input_from_str( - &definition_account_id_2.to_string(), - ), - supply_account_id: make_public_account_input_from_str(&supply_account_id_2.to_string()), - name: "A NAM2".to_string(), - total_supply: 37, - }; - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_1` - let subcommand = TokenProgramAgnosticSubcommand::Send { - from: make_public_account_input_from_str(&supply_account_id_2.to_string()), - to: Some(make_public_account_input_from_str( - &recipient_account_id_2.to_string(), - )), - to_npk: None, - to_ipk: None, - amount: 7, - }; - - wallet::cli::execute_subcommand(Command::Token(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - info!("=================== SETUP FINISHED ==============="); - - // Create new AMM - - // Setup accounts - // Create new account for the user holding lp - let SubcommandReturnValue::RegisterAccount { - account_id: user_holding_lp, - } = wallet::cli::execute_subcommand(Command::Account(AccountSubcommand::New( - NewSubcommand::Public { cci: None }, - ))) - .await - .unwrap() - else { - panic!("invalid subcommand return value"); - }; - - // Send creation tx - let subcommand = AmmProgramAgnosticSubcommand::New { - user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), - user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), - user_holding_lp: make_public_account_input_from_str(&user_holding_lp.to_string()), - balance_a: 3, - balance_b: 3, - }; - - wallet::cli::execute_subcommand(Command::AMM(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let user_holding_a_acc = seq_client - .get_account(recipient_account_id_1.to_string()) - .await - .unwrap() - .account; - - let user_holding_b_acc = seq_client - .get_account(recipient_account_id_2.to_string()) - .await - .unwrap() - .account; - - let user_holding_lp_acc = seq_client - .get_account(user_holding_lp.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), - 4 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), - 4 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), - 3 - ); - - info!("=================== AMM DEFINITION FINISHED ==============="); - - // Make swap - - let subcommand = AmmProgramAgnosticSubcommand::Swap { - user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), - user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), - amount_in: 2, - min_amount_out: 1, - token_definition: definition_account_id_1.to_string(), - }; - - wallet::cli::execute_subcommand(Command::AMM(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let user_holding_a_acc = seq_client - .get_account(recipient_account_id_1.to_string()) - .await - .unwrap() - .account; - - let user_holding_b_acc = seq_client - .get_account(recipient_account_id_2.to_string()) - .await - .unwrap() - .account; - - let user_holding_lp_acc = seq_client - .get_account(user_holding_lp.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), - 2 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), - 5 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), - 3 - ); - - info!("=================== FIRST SWAP FINISHED ==============="); - - // Make swap - - let subcommand = AmmProgramAgnosticSubcommand::Swap { - user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), - user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), - amount_in: 2, - min_amount_out: 1, - token_definition: definition_account_id_2.to_string(), - }; - - wallet::cli::execute_subcommand(Command::AMM(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let user_holding_a_acc = seq_client - .get_account(recipient_account_id_1.to_string()) - .await - .unwrap() - .account; - - let user_holding_b_acc = seq_client - .get_account(recipient_account_id_2.to_string()) - .await - .unwrap() - .account; - - let user_holding_lp_acc = seq_client - .get_account(user_holding_lp.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), - 4 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), - 3 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), - 3 - ); - - info!("=================== SECOND SWAP FINISHED ==============="); - - // Add liquidity - - let subcommand = AmmProgramAgnosticSubcommand::AddLiquidity { - user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), - user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), - user_holding_lp: make_public_account_input_from_str(&user_holding_lp.to_string()), - min_amount_lp: 1, - max_amount_a: 2, - max_amount_b: 2, - }; - - wallet::cli::execute_subcommand(Command::AMM(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let user_holding_a_acc = seq_client - .get_account(recipient_account_id_1.to_string()) - .await - .unwrap() - .account; - - let user_holding_b_acc = seq_client - .get_account(recipient_account_id_2.to_string()) - .await - .unwrap() - .account; - - let user_holding_lp_acc = seq_client - .get_account(user_holding_lp.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), - 3 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), - 1 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), - 4 - ); - - info!("=================== ADD LIQ FINISHED ==============="); - - // Remove liquidity - - let subcommand = AmmProgramAgnosticSubcommand::RemoveLiquidity { - user_holding_a: make_public_account_input_from_str(&recipient_account_id_1.to_string()), - user_holding_b: make_public_account_input_from_str(&recipient_account_id_2.to_string()), - user_holding_lp: make_public_account_input_from_str(&user_holding_lp.to_string()), - balance_lp: 2, - min_amount_a: 1, - min_amount_b: 1, - }; - - wallet::cli::execute_subcommand(Command::AMM(subcommand)) - .await - .unwrap(); - info!("Waiting for next block creation"); - tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; - - let user_holding_a_acc = seq_client - .get_account(recipient_account_id_1.to_string()) - .await - .unwrap() - .account; - - let user_holding_b_acc = seq_client - .get_account(recipient_account_id_2.to_string()) - .await - .unwrap() - .account; - - let user_holding_lp_acc = seq_client - .get_account(user_holding_lp.to_string()) - .await - .unwrap() - .account; - - assert_eq!( - u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), - 5 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), - 4 - ); - - assert_eq!( - u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), - 2 - ); - - info!("Success!"); - } - - println!("{function_map:#?}"); - - function_map -} - -#[allow(clippy::type_complexity)] -async fn pre_tps_test( - test: &TpsTestManager, -) -> Result<(ServerHandle, JoinHandle>, TempDir)> { - info!("Generating tps test config"); - let mut sequencer_config = test.generate_tps_test_config(); - info!("Done"); - - let temp_dir_sequencer = replace_home_dir_with_temp_dir_in_configs(&mut sequencer_config); - - let (seq_http_server_handle, sequencer_loop_handle) = - startup_sequencer(sequencer_config).await?; - - Ok(( - seq_http_server_handle, - sequencer_loop_handle, - temp_dir_sequencer, - )) -} - -pub async fn tps_test() { - let num_transactions = 300 * 5; - let target_tps = 12; - let tps_test = TpsTestManager::new(target_tps, num_transactions); - - let target_time = tps_test.target_time(); - info!("Target time: {:?} seconds", target_time.as_secs()); - let res = pre_tps_test(&tps_test).await.unwrap(); - - let wallet_config = fetch_config().await.unwrap(); - let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); - - info!("TPS test begin"); - let txs = tps_test.build_public_txs(); - let now = Instant::now(); - - let mut tx_hashes = vec![]; - for (i, tx) in txs.into_iter().enumerate() { - let tx_hash = seq_client.send_tx_public(tx).await.unwrap().tx_hash; - info!("Sent tx {i}"); - tx_hashes.push(tx_hash); - } - - for (i, tx_hash) in tx_hashes.iter().enumerate() { - loop { - if now.elapsed().as_millis() > target_time.as_millis() { - panic!("TPS test failed by timeout"); - } - - let tx_obj = seq_client - .get_transaction_by_hash(tx_hash.clone()) - .await - .inspect_err(|err| { - log::warn!( - "Failed to get transaction by hash {tx_hash:#?} with error: {err:#?}" - ) - }); - - if let Ok(tx_obj) = tx_obj - && tx_obj.transaction.is_some() - { - info!("Found tx {i} with hash {tx_hash}"); - break; - } - } - } - let time_elapsed = now.elapsed().as_secs(); - - info!("TPS test finished successfully"); - info!("Target TPS: {}", target_tps); - info!( - "Processed {} transactions in {}s", - tx_hashes.len(), - time_elapsed - ); - info!("Target time: {:?}s", target_time.as_secs()); - - post_test(res).await; -} diff --git a/integration_tests/tests/account.rs b/integration_tests/tests/account.rs new file mode 100644 index 00000000..e5e700dd --- /dev/null +++ b/integration_tests/tests/account.rs @@ -0,0 +1,28 @@ +use anyhow::Result; +use integration_tests::{ACC_SENDER, TestContext}; +use log::info; +use nssa::program::Program; +use tokio::test; + +#[test] +async fn get_existing_account() -> Result<()> { + let ctx = TestContext::new().await?; + + let account = ctx + .sequencer_client() + .get_account(ACC_SENDER.to_string()) + .await? + .account; + + assert_eq!( + account.program_owner, + Program::authenticated_transfer_program().id() + ); + assert_eq!(account.balance, 10000); + assert!(account.data.is_empty()); + assert_eq!(account.nonce, 0); + + info!("Successfully retrieved account with correct details"); + + Ok(()) +} diff --git a/integration_tests/tests/amm.rs b/integration_tests/tests/amm.rs new file mode 100644 index 00000000..073cd03d --- /dev/null +++ b/integration_tests/tests/amm.rs @@ -0,0 +1,405 @@ +use std::time::Duration; + +use anyhow::Result; +use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_public_account_id}; +use log::info; +use tokio::test; +use wallet::cli::{ + Command, SubcommandReturnValue, + account::{AccountSubcommand, NewSubcommand}, + programs::{amm::AmmProgramAgnosticSubcommand, token::TokenProgramAgnosticSubcommand}, +}; + +#[test] +async fn amm_public() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create new account for the token definition + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id_1, + } = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await? + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for the token supply holder + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id_1, + } = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await? + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for receiving a token transaction + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id_1, + } = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await? + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for the token definition + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id_2, + } = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await? + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for the token supply holder + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id_2, + } = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await? + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for receiving a token transaction + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id_2, + } = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await? + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new token + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_public_account_id(&definition_account_id_1.to_string()), + supply_account_id: format_public_account_id(&supply_account_id_1.to_string()), + name: "A NAM1".to_string(), + total_supply: 37, + }; + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_1` + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: format_public_account_id(&supply_account_id_1.to_string()), + to: Some(format_public_account_id( + &recipient_account_id_1.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Create new token + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_public_account_id(&definition_account_id_2.to_string()), + supply_account_id: format_public_account_id(&supply_account_id_2.to_string()), + name: "A NAM2".to_string(), + total_supply: 37, + }; + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Transfer 7 tokens from `supply_acc` to the account at account_id `recipient_account_id_2` + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: format_public_account_id(&supply_account_id_2.to_string()), + to: Some(format_public_account_id( + &recipient_account_id_2.to_string(), + )), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("=================== SETUP FINISHED ==============="); + + // Create new AMM + + // Setup accounts + // Create new account for the user holding lp + let SubcommandReturnValue::RegisterAccount { + account_id: user_holding_lp, + } = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await? + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Send creation tx + let subcommand = AmmProgramAgnosticSubcommand::New { + user_holding_a: format_public_account_id(&recipient_account_id_1.to_string()), + user_holding_b: format_public_account_id(&recipient_account_id_2.to_string()), + user_holding_lp: format_public_account_id(&user_holding_lp.to_string()), + balance_a: 3, + balance_b: 3, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_1.to_string()) + .await? + .account; + + let user_holding_b_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_2.to_string()) + .await? + .account; + + let user_holding_lp_acc = ctx + .sequencer_client() + .get_account(user_holding_lp.to_string()) + .await? + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 4 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 4 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 3 + ); + + info!("=================== AMM DEFINITION FINISHED ==============="); + + // Make swap + + let subcommand = AmmProgramAgnosticSubcommand::Swap { + user_holding_a: format_public_account_id(&recipient_account_id_1.to_string()), + user_holding_b: format_public_account_id(&recipient_account_id_2.to_string()), + amount_in: 2, + min_amount_out: 1, + token_definition: definition_account_id_1.to_string(), + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_1.to_string()) + .await? + .account; + + let user_holding_b_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_2.to_string()) + .await? + .account; + + let user_holding_lp_acc = ctx + .sequencer_client() + .get_account(user_holding_lp.to_string()) + .await? + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 2 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 5 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 3 + ); + + info!("=================== FIRST SWAP FINISHED ==============="); + + // Make swap + + let subcommand = AmmProgramAgnosticSubcommand::Swap { + user_holding_a: format_public_account_id(&recipient_account_id_1.to_string()), + user_holding_b: format_public_account_id(&recipient_account_id_2.to_string()), + amount_in: 2, + min_amount_out: 1, + token_definition: definition_account_id_2.to_string(), + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_1.to_string()) + .await? + .account; + + let user_holding_b_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_2.to_string()) + .await? + .account; + + let user_holding_lp_acc = ctx + .sequencer_client() + .get_account(user_holding_lp.to_string()) + .await? + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 4 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 3 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 3 + ); + + info!("=================== SECOND SWAP FINISHED ==============="); + + // Add liquidity + + let subcommand = AmmProgramAgnosticSubcommand::AddLiquidity { + user_holding_a: format_public_account_id(&recipient_account_id_1.to_string()), + user_holding_b: format_public_account_id(&recipient_account_id_2.to_string()), + user_holding_lp: format_public_account_id(&user_holding_lp.to_string()), + min_amount_lp: 1, + max_amount_a: 2, + max_amount_b: 2, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_1.to_string()) + .await? + .account; + + let user_holding_b_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_2.to_string()) + .await? + .account; + + let user_holding_lp_acc = ctx + .sequencer_client() + .get_account(user_holding_lp.to_string()) + .await? + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 3 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 1 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 4 + ); + + info!("=================== ADD LIQ FINISHED ==============="); + + // Remove liquidity + + let subcommand = AmmProgramAgnosticSubcommand::RemoveLiquidity { + user_holding_a: format_public_account_id(&recipient_account_id_1.to_string()), + user_holding_b: format_public_account_id(&recipient_account_id_2.to_string()), + user_holding_lp: format_public_account_id(&user_holding_lp.to_string()), + balance_lp: 2, + min_amount_a: 1, + min_amount_b: 1, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::AMM(subcommand)).await?; + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let user_holding_a_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_1.to_string()) + .await? + .account; + + let user_holding_b_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_2.to_string()) + .await? + .account; + + let user_holding_lp_acc = ctx + .sequencer_client() + .get_account(user_holding_lp.to_string()) + .await? + .account; + + assert_eq!( + u128::from_le_bytes(user_holding_a_acc.data[33..].try_into().unwrap()), + 5 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_b_acc.data[33..].try_into().unwrap()), + 4 + ); + + assert_eq!( + u128::from_le_bytes(user_holding_lp_acc.data[33..].try_into().unwrap()), + 2 + ); + + info!("Success!"); + + Ok(()) +} diff --git a/integration_tests/tests/auth_transfer/main.rs b/integration_tests/tests/auth_transfer/main.rs new file mode 100644 index 00000000..c97008bd --- /dev/null +++ b/integration_tests/tests/auth_transfer/main.rs @@ -0,0 +1,2 @@ +mod private; +mod public; diff --git a/integration_tests/tests/auth_transfer/private.rs b/integration_tests/tests/auth_transfer/private.rs new file mode 100644 index 00000000..4674ed0b --- /dev/null +++ b/integration_tests/tests/auth_transfer/private.rs @@ -0,0 +1,417 @@ +use std::time::Duration; + +use anyhow::{Context as _, Result}; +use integration_tests::{ + ACC_RECEIVER, ACC_RECEIVER_PRIVATE, ACC_SENDER, ACC_SENDER_PRIVATE, + TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, fetch_privacy_preserving_tx, + format_private_account_id, format_public_account_id, verify_commitment_is_in_state, +}; +use log::info; +use nssa::{AccountId, program::Program}; +use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; +use tokio::test; +use wallet::cli::{ + Command, SubcommandReturnValue, + account::{AccountSubcommand, NewSubcommand}, + programs::native_token_transfer::AuthTransferSubcommand, +}; + +#[test] +async fn private_transfer_to_owned_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let from: AccountId = ACC_SENDER_PRIVATE.parse()?; + let to: AccountId = ACC_RECEIVER_PRIVATE.parse()?; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&from.to_string()), + to: Some(format_private_account_id(&to.to_string())), + to_npk: None, + to_ipk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = ctx + .wallet() + .get_private_account_commitment(&from) + .context("Failed to get private account commitment for sender")?; + assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await); + + let new_commitment2 = ctx + .wallet() + .get_private_account_commitment(&to) + .context("Failed to get private account commitment for receiver")?; + assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await); + + info!("Successfully transferred privately to owned account"); + + Ok(()) +} + +#[test] +async fn private_transfer_to_foreign_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let from: AccountId = ACC_SENDER_PRIVATE.parse()?; + let to_npk = NullifierPublicKey([42; 32]); + let to_npk_string = hex::encode(to_npk.0); + let to_ipk = Secp256k1Point::from_scalar(to_npk.0); + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&from.to_string()), + to: None, + to_npk: Some(to_npk_string), + to_ipk: Some(hex::encode(to_ipk.0)), + amount: 100, + }); + + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = result else { + anyhow::bail!("Expected PrivacyPreservingTransfer return value"); + }; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = ctx + .wallet() + .get_private_account_commitment(&from) + .context("Failed to get private account commitment for sender")?; + + let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash.clone()).await; + assert_eq!(tx.message.new_commitments[0], new_commitment1); + + assert_eq!(tx.message.new_commitments.len(), 2); + for commitment in tx.message.new_commitments.into_iter() { + assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await); + } + + info!("Successfully transferred privately to foreign account"); + + Ok(()) +} + +#[test] +async fn deshielded_transfer_to_public_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let from: AccountId = ACC_SENDER_PRIVATE.parse()?; + let to: AccountId = ACC_RECEIVER.parse()?; + + // Check initial balance of the private sender + let from_acc = ctx + .wallet() + .get_account_private(&from) + .context("Failed to get sender's private account")?; + assert_eq!(from_acc.balance, 10000); + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&from.to_string()), + to: Some(format_public_account_id(&to.to_string())), + to_npk: None, + to_ipk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let from_acc = ctx + .wallet() + .get_account_private(&from) + .context("Failed to get sender's private account")?; + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&from) + .context("Failed to get private account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + let acc_2_balance = ctx + .sequencer_client() + .get_account_balance(to.to_string()) + .await?; + + assert_eq!(from_acc.balance, 9900); + assert_eq!(acc_2_balance.balance, 20100); + + info!("Successfully deshielded transfer to public account"); + + Ok(()) +} + +#[test] +async fn private_transfer_to_owned_account_using_claiming_path() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let from: AccountId = ACC_SENDER_PRIVATE.parse()?; + + // Create a new private account + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })); + + let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::RegisterAccount { + account_id: to_account_id, + } = sub_ret + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Get the keys for the newly created account + let (to_keys, _) = ctx + .wallet() + .storage() + .user_data + .get_private_account(&to_account_id) + .cloned() + .context("Failed to get private account")?; + + // Send to this account using claiming path (using npk and ipk instead of account ID) + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&from.to_string()), + to: None, + to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)), + to_ipk: Some(hex::encode(to_keys.incoming_viewing_public_key.0)), + amount: 100, + }); + + let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { + anyhow::bail!("Expected PrivacyPreservingTransfer return value"); + }; + + let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash.clone()).await; + + // Sync the wallet to claim the new account + let command = Command::Account(AccountSubcommand::SyncPrivate {}); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + let new_commitment1 = ctx + .wallet() + .get_private_account_commitment(&from) + .context("Failed to get private account commitment for sender")?; + assert_eq!(tx.message.new_commitments[0], new_commitment1); + + assert_eq!(tx.message.new_commitments.len(), 2); + for commitment in tx.message.new_commitments.into_iter() { + assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await); + } + + let to_res_acc = ctx + .wallet() + .get_account_private(&to_account_id) + .context("Failed to get recipient's private account")?; + assert_eq!(to_res_acc.balance, 100); + + info!("Successfully transferred using claiming path"); + + Ok(()) +} + +#[test] +async fn shielded_transfer_to_owned_private_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let from: AccountId = ACC_SENDER.parse()?; + let to: AccountId = ACC_RECEIVER_PRIVATE.parse()?; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(&from.to_string()), + to: Some(format_private_account_id(&to.to_string())), + to_npk: None, + to_ipk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let acc_to = ctx + .wallet() + .get_account_private(&to) + .context("Failed to get receiver's private account")?; + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&to) + .context("Failed to get receiver's commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + let acc_from_balance = ctx + .sequencer_client() + .get_account_balance(from.to_string()) + .await?; + + assert_eq!(acc_from_balance.balance, 9900); + assert_eq!(acc_to.balance, 20100); + + info!("Successfully shielded transfer to owned private account"); + + Ok(()) +} + +#[test] +async fn shielded_transfer_to_foreign_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let to_npk = NullifierPublicKey([42; 32]); + let to_npk_string = hex::encode(to_npk.0); + let to_ipk = Secp256k1Point::from_scalar(to_npk.0); + let from: AccountId = ACC_SENDER.parse()?; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(&from.to_string()), + to: None, + to_npk: Some(to_npk_string), + to_ipk: Some(hex::encode(to_ipk.0)), + amount: 100, + }); + + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = result else { + anyhow::bail!("Expected PrivacyPreservingTransfer return value"); + }; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash).await; + + let acc_1_balance = ctx + .sequencer_client() + .get_account_balance(from.to_string()) + .await?; + + assert!( + verify_commitment_is_in_state( + tx.message.new_commitments[0].clone(), + ctx.sequencer_client() + ) + .await + ); + + assert_eq!(acc_1_balance.balance, 9900); + + info!("Successfully shielded transfer to foreign account"); + + Ok(()) +} + +#[test] +#[ignore = "Flaky, TODO: #197"] +async fn private_transfer_to_owned_account_continuous_run_path() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // NOTE: This test needs refactoring - continuous run mode doesn't work well with TestContext + // The original implementation spawned wallet::cli::execute_continuous_run() in background + // but this conflicts with TestContext's wallet management + + let from: AccountId = ACC_SENDER_PRIVATE.parse()?; + + // Create a new private account + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })); + let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + let SubcommandReturnValue::RegisterAccount { + account_id: to_account_id, + } = sub_ret + else { + anyhow::bail!("Failed to register account"); + }; + + // Get the newly created account's keys + let (to_keys, _) = ctx + .wallet() + .storage() + .user_data + .get_private_account(&to_account_id) + .cloned() + .context("Failed to get private account")?; + + // Send transfer using nullifier and incoming viewing public keys + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&from.to_string()), + to: None, + to_npk: Some(hex::encode(to_keys.nullifer_public_key.0)), + to_ipk: Some(hex::encode(to_keys.incoming_viewing_public_key.0)), + amount: 100, + }); + + let sub_ret = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { + anyhow::bail!("Failed to send transaction"); + }; + + let tx = fetch_privacy_preserving_tx(ctx.sequencer_client(), tx_hash.clone()).await; + + info!("Waiting for next blocks to check if continuous run fetches account"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify commitments are in state + assert_eq!(tx.message.new_commitments.len(), 2); + for commitment in tx.message.new_commitments.into_iter() { + assert!(verify_commitment_is_in_state(commitment, ctx.sequencer_client()).await); + } + + // Verify receiver account balance + let to_res_acc = ctx + .wallet() + .get_account_private(&to_account_id) + .context("Failed to get receiver account")?; + + assert_eq!(to_res_acc.balance, 100); + + Ok(()) +} + +#[test] +async fn initialize_private_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })); + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::RegisterAccount { account_id } = result else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Init { + account_id: format_private_account_id(&account_id.to_string()), + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Syncing private accounts"); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&account_id) + .context("Failed to get private account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + let account = ctx + .wallet() + .get_account_private(&account_id) + .context("Failed to get private account")?; + + assert_eq!( + account.program_owner, + Program::authenticated_transfer_program().id() + ); + assert_eq!(account.balance, 0); + assert!(account.data.is_empty()); + + info!("Successfully initialized private account"); + + Ok(()) +} diff --git a/integration_tests/tests/auth_transfer/public.rs b/integration_tests/tests/auth_transfer/public.rs new file mode 100644 index 00000000..467e3ebc --- /dev/null +++ b/integration_tests/tests/auth_transfer/public.rs @@ -0,0 +1,248 @@ +use std::time::Duration; + +use anyhow::Result; +use integration_tests::{ + ACC_RECEIVER, ACC_SENDER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_public_account_id, +}; +use log::info; +use nssa::program::Program; +use tokio::test; +use wallet::cli::{ + Command, SubcommandReturnValue, + account::{AccountSubcommand, NewSubcommand}, + programs::native_token_transfer::AuthTransferSubcommand, +}; + +#[test] +async fn successful_transfer_to_existing_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(ACC_SENDER), + to: Some(format_public_account_id(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking correct balance move"); + let acc_1_balance = ctx + .sequencer_client() + .get_account_balance(ACC_SENDER.to_string()) + .await?; + let acc_2_balance = ctx + .sequencer_client() + .get_account_balance(ACC_RECEIVER.to_string()) + .await?; + + info!("Balance of sender: {acc_1_balance:#?}"); + info!("Balance of receiver: {acc_2_balance:#?}"); + + assert_eq!(acc_1_balance.balance, 9900); + assert_eq!(acc_2_balance.balance, 20100); + + Ok(()) +} + +#[test] +pub async fn successful_transfer_to_new_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command) + .await + .unwrap(); + + let new_persistent_account_id = ctx + .wallet() + .storage() + .user_data + .account_ids() + .map(ToString::to_string) + .find(|acc_id| acc_id != ACC_SENDER && acc_id != ACC_RECEIVER) + .expect("Failed to find newly created account in the wallet storage"); + + if new_persistent_account_id == String::new() { + panic!("Failed to produce new account, not present in persistent accounts"); + } + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(ACC_SENDER), + to: Some(format_public_account_id(&new_persistent_account_id)), + to_npk: None, + to_ipk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking correct balance move"); + let acc_1_balance = ctx + .sequencer_client() + .get_account_balance(ACC_SENDER.to_string()) + .await?; + let acc_2_balance = ctx + .sequencer_client() + .get_account_balance(new_persistent_account_id) + .await?; + + info!("Balance of sender: {acc_1_balance:#?}"); + info!("Balance of receiver: {acc_2_balance:#?}"); + + assert_eq!(acc_1_balance.balance, 9900); + assert_eq!(acc_2_balance.balance, 100); + + Ok(()) +} + +#[test] +async fn failed_transfer_with_insufficient_balance() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(ACC_SENDER), + to: Some(format_public_account_id(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, + amount: 1000000, + }); + + let failed_send = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await; + assert!(failed_send.is_err()); + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking balances unchanged"); + let acc_1_balance = ctx + .sequencer_client() + .get_account_balance(ACC_SENDER.to_string()) + .await?; + let acc_2_balance = ctx + .sequencer_client() + .get_account_balance(ACC_RECEIVER.to_string()) + .await?; + + info!("Balance of sender: {acc_1_balance:#?}"); + info!("Balance of receiver: {acc_2_balance:#?}"); + + assert_eq!(acc_1_balance.balance, 10000); + assert_eq!(acc_2_balance.balance, 20000); + + Ok(()) +} + +#[test] +async fn two_consecutive_successful_transfers() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // First transfer + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(ACC_SENDER), + to: Some(format_public_account_id(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking correct balance move after first transfer"); + let acc_1_balance = ctx + .sequencer_client() + .get_account_balance(ACC_SENDER.to_string()) + .await?; + let acc_2_balance = ctx + .sequencer_client() + .get_account_balance(ACC_RECEIVER.to_string()) + .await?; + + info!("Balance of sender: {acc_1_balance:#?}"); + info!("Balance of receiver: {acc_2_balance:#?}"); + + assert_eq!(acc_1_balance.balance, 9900); + assert_eq!(acc_2_balance.balance, 20100); + + info!("First TX Success!"); + + // Second transfer + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(ACC_SENDER), + to: Some(format_public_account_id(ACC_RECEIVER)), + to_npk: None, + to_ipk: None, + amount: 100, + }); + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking correct balance move after second transfer"); + let acc_1_balance = ctx + .sequencer_client() + .get_account_balance(ACC_SENDER.to_string()) + .await?; + let acc_2_balance = ctx + .sequencer_client() + .get_account_balance(ACC_RECEIVER.to_string()) + .await?; + + info!("Balance of sender: {acc_1_balance:#?}"); + info!("Balance of receiver: {acc_2_balance:#?}"); + + assert_eq!(acc_1_balance.balance, 9800); + assert_eq!(acc_2_balance.balance, 20200); + + info!("Second TX Success!"); + + Ok(()) +} + +#[test] +async fn initialize_public_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })); + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::RegisterAccount { account_id } = result else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Init { + account_id: format_public_account_id(&account_id.to_string()), + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Checking correct execution"); + let account = ctx + .sequencer_client() + .get_account(account_id.to_string()) + .await? + .account; + + assert_eq!( + account.program_owner, + Program::authenticated_transfer_program().id() + ); + assert_eq!(account.balance, 0); + assert_eq!(account.nonce, 1); + assert!(account.data.is_empty()); + + info!("Successfully initialized public account"); + + Ok(()) +} diff --git a/integration_tests/tests/config.rs b/integration_tests/tests/config.rs new file mode 100644 index 00000000..ca800d0f --- /dev/null +++ b/integration_tests/tests/config.rs @@ -0,0 +1,33 @@ +use anyhow::Result; +use integration_tests::TestContext; +use log::info; +use tokio::test; +use wallet::cli::{Command, config::ConfigSubcommand}; + +#[test] +async fn modify_config_field() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let old_seq_poll_timeout_millis = ctx.wallet().config().seq_poll_timeout_millis; + + // Change config field + let command = Command::Config(ConfigSubcommand::Set { + key: "seq_poll_timeout_millis".to_string(), + value: "1000".to_string(), + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + let new_seq_poll_timeout_millis = ctx.wallet().config().seq_poll_timeout_millis; + assert_eq!(new_seq_poll_timeout_millis, 1000); + + // Return how it was at the beginning + let command = Command::Config(ConfigSubcommand::Set { + key: "seq_poll_timeout_millis".to_string(), + value: old_seq_poll_timeout_millis.to_string(), + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Successfully modified and restored config field"); + + Ok(()) +} diff --git a/integration_tests/tests/keys_restoration.rs b/integration_tests/tests/keys_restoration.rs new file mode 100644 index 00000000..9076c87f --- /dev/null +++ b/integration_tests/tests/keys_restoration.rs @@ -0,0 +1,217 @@ +use std::{str::FromStr, time::Duration}; + +use anyhow::Result; +use integration_tests::{ + ACC_SENDER, ACC_SENDER_PRIVATE, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, + format_private_account_id, format_public_account_id, verify_commitment_is_in_state, +}; +use key_protocol::key_management::key_tree::chain_index::ChainIndex; +use log::info; +use nssa::{AccountId, program::Program}; +use tokio::test; +use wallet::cli::{ + Command, SubcommandReturnValue, + account::{AccountSubcommand, NewSubcommand}, + programs::native_token_transfer::AuthTransferSubcommand, +}; + +#[test] +async fn restore_keys_from_seed() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let from: AccountId = ACC_SENDER_PRIVATE.parse()?; + + // Create first private account at root + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private { + cci: Some(ChainIndex::root()), + })); + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::RegisterAccount { + account_id: to_account_id1, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create second private account at /0 + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Private { + cci: Some(ChainIndex::from_str("/0")?), + })); + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::RegisterAccount { + account_id: to_account_id2, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Send to first private account + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&from.to_string()), + to: Some(format_private_account_id(&to_account_id1.to_string())), + to_npk: None, + to_ipk: None, + amount: 100, + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + // Send to second private account + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&from.to_string()), + to: Some(format_private_account_id(&to_account_id2.to_string())), + to_npk: None, + to_ipk: None, + amount: 101, + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + let from: AccountId = ACC_SENDER.parse()?; + + // Create first public account at root + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { + cci: Some(ChainIndex::root()), + })); + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::RegisterAccount { + account_id: to_account_id3, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create second public account at /0 + let command = Command::Account(AccountSubcommand::New(NewSubcommand::Public { + cci: Some(ChainIndex::from_str("/0")?), + })); + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::RegisterAccount { + account_id: to_account_id4, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Send to first public account + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(&from.to_string()), + to: Some(format_public_account_id(&to_account_id3.to_string())), + to_npk: None, + to_ipk: None, + amount: 102, + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + // Send to second public account + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(&from.to_string()), + to: Some(format_public_account_id(&to_account_id4.to_string())), + to_npk: None, + to_ipk: None, + amount: 103, + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Preparation complete, performing keys restoration"); + + // Restore keys from seed + wallet::cli::execute_keys_restoration(ctx.wallet_mut(), 10).await?; + + // Verify restored private accounts + let acc1 = ctx + .wallet() + .storage() + .user_data + .private_key_tree + .get_node(to_account_id1) + .expect("Acc 1 should be restored"); + + let acc2 = ctx + .wallet() + .storage() + .user_data + .private_key_tree + .get_node(to_account_id2) + .expect("Acc 2 should be restored"); + + // Verify restored public accounts + let _acc3 = ctx + .wallet() + .storage() + .user_data + .public_key_tree + .get_node(to_account_id3) + .expect("Acc 3 should be restored"); + + let _acc4 = ctx + .wallet() + .storage() + .user_data + .public_key_tree + .get_node(to_account_id4) + .expect("Acc 4 should be restored"); + + assert_eq!( + acc1.value.1.program_owner, + Program::authenticated_transfer_program().id() + ); + assert_eq!( + acc2.value.1.program_owner, + Program::authenticated_transfer_program().id() + ); + + assert_eq!(acc1.value.1.balance, 100); + assert_eq!(acc2.value.1.balance, 101); + + info!("Tree checks passed, testing restored accounts can transact"); + + // Test that restored accounts can send transactions + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_private_account_id(&to_account_id1.to_string()), + to: Some(format_private_account_id(&to_account_id2.to_string())), + to_npk: None, + to_ipk: None, + amount: 10, + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + let command = Command::AuthTransfer(AuthTransferSubcommand::Send { + from: format_public_account_id(&to_account_id3.to_string()), + to: Some(format_public_account_id(&to_account_id4.to_string())), + to_npk: None, + to_ipk: None, + amount: 11, + }); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify commitments exist for private accounts + let comm1 = ctx + .wallet() + .get_private_account_commitment(&to_account_id1) + .expect("Acc 1 commitment should exist"); + let comm2 = ctx + .wallet() + .get_private_account_commitment(&to_account_id2) + .expect("Acc 2 commitment should exist"); + + assert!(verify_commitment_is_in_state(comm1, ctx.sequencer_client()).await); + assert!(verify_commitment_is_in_state(comm2, ctx.sequencer_client()).await); + + // Verify public account balances + let acc3 = ctx + .sequencer_client() + .get_account_balance(to_account_id3.to_string()) + .await?; + let acc4 = ctx + .sequencer_client() + .get_account_balance(to_account_id4.to_string()) + .await?; + + assert_eq!(acc3.balance, 91); // 102 - 11 + assert_eq!(acc4.balance, 114); // 103 + 11 + + info!("Successfully restored keys and verified transactions"); + + Ok(()) +} diff --git a/integration_tests/tests/pinata.rs b/integration_tests/tests/pinata.rs new file mode 100644 index 00000000..c627cea2 --- /dev/null +++ b/integration_tests/tests/pinata.rs @@ -0,0 +1,155 @@ +use std::time::Duration; + +use anyhow::{Context as _, Result}; +use common::PINATA_BASE58; +use integration_tests::{ + ACC_SENDER, ACC_SENDER_PRIVATE, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, + format_private_account_id, format_public_account_id, verify_commitment_is_in_state, +}; +use log::info; +use tokio::test; +use wallet::cli::{ + Command, SubcommandReturnValue, + account::{AccountSubcommand, NewSubcommand}, + programs::pinata::PinataProgramAgnosticSubcommand, +}; + +#[test] +async fn claim_pinata_to_public_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let pinata_prize = 150; + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to: format_public_account_id(ACC_SENDER), + }); + + let pinata_balance_pre = ctx + .sequencer_client() + .get_account_balance(PINATA_BASE58.to_string()) + .await? + .balance; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Checking correct balance move"); + let pinata_balance_post = ctx + .sequencer_client() + .get_account_balance(PINATA_BASE58.to_string()) + .await? + .balance; + + let winner_balance_post = ctx + .sequencer_client() + .get_account_balance(ACC_SENDER.to_string()) + .await? + .balance; + + assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize); + assert_eq!(winner_balance_post, 10000 + pinata_prize); + + info!("Successfully claimed pinata to public account"); + + Ok(()) +} + +#[test] +async fn claim_pinata_to_existing_private_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let pinata_prize = 150; + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to: format_private_account_id(ACC_SENDER_PRIVATE), + }); + + let pinata_balance_pre = ctx + .sequencer_client() + .get_account_balance(PINATA_BASE58.to_string()) + .await? + .balance; + + let result = wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash: _ } = result else { + anyhow::bail!("Expected PrivacyPreservingTransfer return value"); + }; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + info!("Syncing private accounts"); + let command = Command::Account(AccountSubcommand::SyncPrivate {}); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&ACC_SENDER_PRIVATE.parse()?) + .context("Failed to get private account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + let pinata_balance_post = ctx + .sequencer_client() + .get_account_balance(PINATA_BASE58.to_string()) + .await? + .balance; + + assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize); + + info!("Successfully claimed pinata to existing private account"); + + Ok(()) +} + +#[test] +async fn claim_pinata_to_new_private_account() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let pinata_prize = 150; + + // Create new private account + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: winner_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + let command = Command::Pinata(PinataProgramAgnosticSubcommand::Claim { + to: format_private_account_id(&winner_account_id.to_string()), + }); + + let pinata_balance_pre = ctx + .sequencer_client() + .get_account_balance(PINATA_BASE58.to_string()) + .await? + .balance; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&winner_account_id) + .context("Failed to get private account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + let pinata_balance_post = ctx + .sequencer_client() + .get_account_balance(PINATA_BASE58.to_string()) + .await? + .balance; + + assert_eq!(pinata_balance_post, pinata_balance_pre - pinata_prize); + + info!("Successfully claimed pinata to new private account"); + + Ok(()) +} diff --git a/integration_tests/tests/program_deployment.rs b/integration_tests/tests/program_deployment.rs new file mode 100644 index 00000000..25771ec2 --- /dev/null +++ b/integration_tests/tests/program_deployment.rs @@ -0,0 +1,64 @@ +use std::{path::PathBuf, time::Duration}; + +use anyhow::Result; +use integration_tests::{ + NSSA_PROGRAM_FOR_TEST_DATA_CHANGER, TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, +}; +use log::info; +use nssa::{AccountId, program::Program}; +use tokio::test; +use wallet::cli::Command; + +#[test] +async fn deploy_and_execute_program() -> Result<()> { + let mut ctx = TestContext::new().await?; + + let manifest_dir = env!("CARGO_MANIFEST_DIR"); + let binary_filepath: PathBuf = PathBuf::from(manifest_dir) + .join("../artifacts/test_program_methods") + .join(NSSA_PROGRAM_FOR_TEST_DATA_CHANGER); + + let command = Command::DeployProgram { + binary_filepath: binary_filepath.clone(), + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // The program is the data changer and takes one account as input. + // We pass an uninitialized account and we expect after execution to be owned by the data + // changer program (NSSA account claiming mechanism) with data equal to [0] (due to program + // logic) + let bytecode = std::fs::read(binary_filepath)?; + let data_changer = Program::new(bytecode)?; + let account_id: AccountId = "11".repeat(16).parse()?; + let message = nssa::public_transaction::Message::try_new( + data_changer.id(), + vec![account_id], + vec![], + vec![0], + )?; + let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); + let transaction = nssa::PublicTransaction::new(message, witness_set); + let _response = ctx.sequencer_client().send_tx_public(transaction).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let post_state_account = ctx + .sequencer_client() + .get_account(account_id.to_string()) + .await? + .account; + + assert_eq!(post_state_account.program_owner, data_changer.id()); + assert_eq!(post_state_account.balance, 0); + assert_eq!(post_state_account.data.as_ref(), &[0]); + assert_eq!(post_state_account.nonce, 0); + + info!("Successfully deployed and executed program"); + + Ok(()) +} diff --git a/integration_tests/tests/token.rs b/integration_tests/tests/token.rs new file mode 100644 index 00000000..9a8b714a --- /dev/null +++ b/integration_tests/tests/token.rs @@ -0,0 +1,968 @@ +use std::time::Duration; + +use anyhow::{Context as _, Result}; +use integration_tests::{ + TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, format_private_account_id, + format_public_account_id, verify_commitment_is_in_state, +}; +use key_protocol::key_management::key_tree::chain_index::ChainIndex; +use log::info; +use nssa::program::Program; +use tokio::test; +use wallet::cli::{ + Command, SubcommandReturnValue, + account::{AccountSubcommand, NewSubcommand}, + programs::token::TokenProgramAgnosticSubcommand, +}; + +#[test] +async fn create_and_transfer_public_token() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create new account for the token definition + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for the token supply holder + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for receiving a token transaction + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new token + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_public_account_id(&definition_account_id.to_string()), + supply_account_id: format_public_account_id(&supply_account_id.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Check the status of the token definition account + let definition_acc = ctx + .sequencer_client() + .get_account(definition_account_id.to_string()) + .await? + .account; + + assert_eq!(definition_acc.program_owner, Program::token().id()); + // The data of a token definition account has the following layout: + // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) || metadata id (32 bytes)] + assert_eq!( + definition_acc.data.as_ref(), + &[ + 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + + // Check the status of the token holding account with the total supply + let supply_acc = ctx + .sequencer_client() + .get_account(supply_account_id.to_string()) + .await? + .account; + + // The account must be owned by the token program + assert_eq!(supply_acc.program_owner, Program::token().id()); + // The data of a token holding account has the following layout: + // [ 0x01 || corresponding_token_definition_id (32 bytes) || balance (little endian 16 bytes) ] + // First byte of the data equal to 1 means it's a token holding account + assert_eq!(supply_acc.data.as_ref()[0], 1); + // Bytes from 1 to 33 represent the id of the token this account is associated with + assert_eq!( + &supply_acc.data.as_ref()[1..33], + definition_account_id.to_bytes() + ); + assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 37); + + // Transfer 7 tokens from supply_acc to recipient_account_id + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: format_public_account_id(&supply_account_id.to_string()), + to: Some(format_public_account_id(&recipient_account_id.to_string())), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Check the status of the supply account after transfer + let supply_acc = ctx + .sequencer_client() + .get_account(supply_account_id.to_string()) + .await? + .account; + assert_eq!(supply_acc.program_owner, Program::token().id()); + assert_eq!(supply_acc.data[0], 1); + assert_eq!(&supply_acc.data[1..33], definition_account_id.to_bytes()); + assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30); + + // Check the status of the recipient account after transfer + let recipient_acc = ctx + .sequencer_client() + .get_account(recipient_account_id.to_string()) + .await? + .account; + assert_eq!(recipient_acc.program_owner, Program::token().id()); + assert_eq!(recipient_acc.data[0], 1); + assert_eq!(&recipient_acc.data[1..33], definition_account_id.to_bytes()); + assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7); + + // Burn 3 tokens from recipient_acc + let subcommand = TokenProgramAgnosticSubcommand::Burn { + definition: format_public_account_id(&definition_account_id.to_string()), + holder: format_public_account_id(&recipient_account_id.to_string()), + amount: 3, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Check the status of the token definition account after burn + let definition_acc = ctx + .sequencer_client() + .get_account(definition_account_id.to_string()) + .await? + .account; + + assert_eq!( + definition_acc.data.as_ref(), + &[ + 0, 65, 32, 78, 65, 77, 69, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + + // Check the status of the recipient account after burn + let recipient_acc = ctx + .sequencer_client() + .get_account(recipient_account_id.to_string()) + .await? + .account; + + assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 4); + + // Mint 10 tokens at recipient_acc + let subcommand = TokenProgramAgnosticSubcommand::Mint { + definition: format_public_account_id(&definition_account_id.to_string()), + holder: Some(format_public_account_id(&recipient_account_id.to_string())), + holder_npk: None, + holder_ipk: None, + amount: 10, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Check the status of the token definition account after mint + let definition_acc = ctx + .sequencer_client() + .get_account(definition_account_id.to_string()) + .await? + .account; + + assert_eq!( + definition_acc.data.as_ref(), + &[ + 0, 65, 32, 78, 65, 77, 69, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + + // Check the status of the recipient account after mint + let recipient_acc = ctx + .sequencer_client() + .get_account(recipient_account_id.to_string()) + .await? + .account; + + assert_eq!( + u128::from_le_bytes(recipient_acc.data[33..].try_into()?), + 14 + ); + + info!("Successfully created and transferred public token"); + + Ok(()) +} + +#[test] +async fn create_and_transfer_token_with_private_supply() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create new account for the token definition (public) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for the token supply holder (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new account for receiving a token transaction (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create new token + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_public_account_id(&definition_account_id.to_string()), + supply_account_id: format_private_account_id(&supply_account_id.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Check the status of the token definition account + let definition_acc = ctx + .sequencer_client() + .get_account(definition_account_id.to_string()) + .await? + .account; + + assert_eq!(definition_acc.program_owner, Program::token().id()); + assert_eq!( + definition_acc.data.as_ref(), + &[ + 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + + let new_commitment1 = ctx + .wallet() + .get_private_account_commitment(&supply_account_id) + .context("Failed to get supply account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await); + + // Transfer 7 tokens from supply_acc to recipient_account_id + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: format_private_account_id(&supply_account_id.to_string()), + to: Some(format_private_account_id(&recipient_account_id.to_string())), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = ctx + .wallet() + .get_private_account_commitment(&supply_account_id) + .context("Failed to get supply account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment1, ctx.sequencer_client()).await); + + let new_commitment2 = ctx + .wallet() + .get_private_account_commitment(&recipient_account_id) + .context("Failed to get recipient account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await); + + // Burn 3 tokens from recipient_acc + let subcommand = TokenProgramAgnosticSubcommand::Burn { + definition: format_public_account_id(&definition_account_id.to_string()), + holder: format_private_account_id(&recipient_account_id.to_string()), + amount: 3, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Check the token definition account after burn + let definition_acc = ctx + .sequencer_client() + .get_account(definition_account_id.to_string()) + .await? + .account; + + assert_eq!( + definition_acc.data.as_ref(), + &[ + 0, 65, 32, 78, 65, 77, 69, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + + let new_commitment2 = ctx + .wallet() + .get_private_account_commitment(&recipient_account_id) + .context("Failed to get recipient account commitment")?; + assert!(verify_commitment_is_in_state(new_commitment2, ctx.sequencer_client()).await); + + // Check the recipient account balance after burn + let recipient_acc = ctx + .wallet() + .get_account_private(&recipient_account_id) + .context("Failed to get recipient account")?; + + assert_eq!( + u128::from_le_bytes(recipient_acc.data[33..].try_into()?), + 4 // 7 - 3 + ); + + info!("Successfully created and transferred token with private supply"); + + Ok(()) +} + +#[test] +async fn create_token_with_private_definition() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create token definition account (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { + cci: Some(ChainIndex::root()), + })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create supply account (public) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { + cci: Some(ChainIndex::root()), + })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create token with private definition + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_private_account_id(&definition_account_id.to_string()), + supply_account_id: format_public_account_id(&supply_account_id.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify private definition commitment + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&definition_account_id) + .context("Failed to get definition commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + // Verify supply account + let supply_acc = ctx + .sequencer_client() + .get_account(supply_account_id.to_string()) + .await? + .account; + + assert_eq!(supply_acc.program_owner, Program::token().id()); + assert_eq!(supply_acc.data.as_ref()[0], 1); + assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 37); + + // Create private recipient account + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id_private, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create public recipient account + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id_public, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Mint to public account + let subcommand = TokenProgramAgnosticSubcommand::Mint { + definition: format_private_account_id(&definition_account_id.to_string()), + holder: Some(format_public_account_id( + &recipient_account_id_public.to_string(), + )), + holder_npk: None, + holder_ipk: None, + amount: 10, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify definition account has updated supply + let definition_acc = ctx + .wallet() + .get_account_private(&definition_account_id) + .context("Failed to get definition account")?; + + assert_eq!( + u128::from_le_bytes(definition_acc.data[7..23].try_into()?), + 47 // 37 + 10 + ); + + // Verify public recipient received tokens + let recipient_acc = ctx + .sequencer_client() + .get_account(recipient_account_id_public.to_string()) + .await? + .account; + + assert_eq!( + u128::from_le_bytes(recipient_acc.data[33..].try_into()?), + 10 + ); + + // Mint to private account + let subcommand = TokenProgramAgnosticSubcommand::Mint { + definition: format_private_account_id(&definition_account_id.to_string()), + holder: Some(format_private_account_id( + &recipient_account_id_private.to_string(), + )), + holder_npk: None, + holder_ipk: None, + amount: 5, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify private recipient commitment + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&recipient_account_id_private) + .context("Failed to get recipient commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + // Verify private recipient balance + let recipient_acc_private = ctx + .wallet() + .get_account_private(&recipient_account_id_private) + .context("Failed to get private recipient account")?; + + assert_eq!( + u128::from_le_bytes(recipient_acc_private.data[33..].try_into()?), + 5 + ); + + info!("Successfully created token with private definition and minted to both account types"); + + Ok(()) +} + +#[test] +async fn create_token_with_private_definition_and_supply() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create token definition account (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create supply account (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create token with both private definition and supply + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_private_account_id(&definition_account_id.to_string()), + supply_account_id: format_private_account_id(&supply_account_id.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify definition commitment + let definition_commitment = ctx + .wallet() + .get_private_account_commitment(&definition_account_id) + .context("Failed to get definition commitment")?; + assert!(verify_commitment_is_in_state(definition_commitment, ctx.sequencer_client()).await); + + // Verify supply commitment + let supply_commitment = ctx + .wallet() + .get_private_account_commitment(&supply_account_id) + .context("Failed to get supply commitment")?; + assert!(verify_commitment_is_in_state(supply_commitment, ctx.sequencer_client()).await); + + // Verify supply balance + let supply_acc = ctx + .wallet() + .get_account_private(&supply_account_id) + .context("Failed to get supply account")?; + + assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 37); + + // Create recipient account + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Transfer tokens + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: format_private_account_id(&supply_account_id.to_string()), + to: Some(format_private_account_id(&recipient_account_id.to_string())), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify both commitments updated + let supply_commitment = ctx + .wallet() + .get_private_account_commitment(&supply_account_id) + .context("Failed to get supply commitment")?; + assert!(verify_commitment_is_in_state(supply_commitment, ctx.sequencer_client()).await); + + let recipient_commitment = ctx + .wallet() + .get_private_account_commitment(&recipient_account_id) + .context("Failed to get recipient commitment")?; + assert!(verify_commitment_is_in_state(recipient_commitment, ctx.sequencer_client()).await); + + // Verify balances + let supply_acc = ctx + .wallet() + .get_account_private(&supply_account_id) + .context("Failed to get supply account")?; + assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30); + + let recipient_acc = ctx + .wallet() + .get_account_private(&recipient_account_id) + .context("Failed to get recipient account")?; + assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7); + + info!("Successfully created and transferred token with both private definition and supply"); + + Ok(()) +} + +#[test] +async fn shielded_token_transfer() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create token definition account (public) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create supply account (public) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create recipient account (private) for shielded transfer + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create token + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_public_account_id(&definition_account_id.to_string()), + supply_account_id: format_public_account_id(&supply_account_id.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Perform shielded transfer: public supply -> private recipient + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: format_public_account_id(&supply_account_id.to_string()), + to: Some(format_private_account_id(&recipient_account_id.to_string())), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify supply account balance + let supply_acc = ctx + .sequencer_client() + .get_account(supply_account_id.to_string()) + .await? + .account; + assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30); + + // Verify recipient commitment exists + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&recipient_account_id) + .context("Failed to get recipient commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + // Verify recipient balance + let recipient_acc = ctx + .wallet() + .get_account_private(&recipient_account_id) + .context("Failed to get recipient account")?; + assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7); + + info!("Successfully performed shielded token transfer"); + + Ok(()) +} + +#[test] +async fn deshielded_token_transfer() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create token definition account (public) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create supply account (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create recipient account (public) for deshielded transfer + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Public { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create token with private supply + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_public_account_id(&definition_account_id.to_string()), + supply_account_id: format_private_account_id(&supply_account_id.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Perform deshielded transfer: private supply -> public recipient + let subcommand = TokenProgramAgnosticSubcommand::Send { + from: format_private_account_id(&supply_account_id.to_string()), + to: Some(format_public_account_id(&recipient_account_id.to_string())), + to_npk: None, + to_ipk: None, + amount: 7, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Verify supply account commitment exists + let new_commitment = ctx + .wallet() + .get_private_account_commitment(&supply_account_id) + .context("Failed to get supply commitment")?; + assert!(verify_commitment_is_in_state(new_commitment, ctx.sequencer_client()).await); + + // Verify supply balance + let supply_acc = ctx + .wallet() + .get_account_private(&supply_account_id) + .context("Failed to get supply account")?; + assert_eq!(u128::from_le_bytes(supply_acc.data[33..].try_into()?), 30); + + // Verify recipient balance + let recipient_acc = ctx + .sequencer_client() + .get_account(recipient_account_id.to_string()) + .await? + .account; + assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 7); + + info!("Successfully performed deshielded token transfer"); + + Ok(()) +} + +#[test] +async fn token_claiming_path_with_private_accounts() -> Result<()> { + let mut ctx = TestContext::new().await?; + + // Create token definition account (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: definition_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create supply account (private) + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: supply_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Create token + let subcommand = TokenProgramAgnosticSubcommand::New { + definition_account_id: format_private_account_id(&definition_account_id.to_string()), + supply_account_id: format_private_account_id(&supply_account_id.to_string()), + name: "A NAME".to_string(), + total_supply: 37, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Create new private account for claiming path + let result = wallet::cli::execute_subcommand( + ctx.wallet_mut(), + Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None })), + ) + .await?; + let SubcommandReturnValue::RegisterAccount { + account_id: recipient_account_id, + } = result + else { + anyhow::bail!("Expected RegisterAccount return value"); + }; + + // Get keys for foreign mint (claiming path) + let (holder_keys, _) = ctx + .wallet() + .storage() + .user_data + .get_private_account(&recipient_account_id) + .cloned() + .context("Failed to get private account keys")?; + + // Mint using claiming path (foreign account) + let subcommand = TokenProgramAgnosticSubcommand::Mint { + definition: format_private_account_id(&definition_account_id.to_string()), + holder: None, + holder_npk: Some(hex::encode(holder_keys.nullifer_public_key.0)), + holder_ipk: Some(hex::encode(holder_keys.incoming_viewing_public_key.0)), + amount: 9, + }; + + wallet::cli::execute_subcommand(ctx.wallet_mut(), Command::Token(subcommand)).await?; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Sync to claim the account + let command = Command::Account(AccountSubcommand::SyncPrivate {}); + wallet::cli::execute_subcommand(ctx.wallet_mut(), command).await?; + + // Verify commitment exists + let recipient_commitment = ctx + .wallet() + .get_private_account_commitment(&recipient_account_id) + .context("Failed to get recipient commitment")?; + assert!(verify_commitment_is_in_state(recipient_commitment, ctx.sequencer_client()).await); + + // Verify balance + let recipient_acc = ctx + .wallet() + .get_account_private(&recipient_account_id) + .context("Failed to get recipient account")?; + assert_eq!(u128::from_le_bytes(recipient_acc.data[33..].try_into()?), 9); + + info!("Successfully minted tokens using claiming path"); + + Ok(()) +} diff --git a/integration_tests/src/tps_test_utils.rs b/integration_tests/tests/tps.rs similarity index 74% rename from integration_tests/src/tps_test_utils.rs rename to integration_tests/tests/tps.rs index 154253c8..b62a3ce7 100644 --- a/integration_tests/src/tps_test_utils.rs +++ b/integration_tests/tests/tps.rs @@ -1,6 +1,9 @@ -use std::time::Duration; +use std::time::{Duration, Instant}; +use anyhow::Result; +use integration_tests::TestContext; use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; +use log::info; use nssa::{ Account, AccountId, PrivacyPreservingTransaction, PrivateKey, PublicKey, PublicTransaction, privacy_preserving_transaction::{self as pptx, circuit}, @@ -13,6 +16,78 @@ use nssa_core::{ encryption::IncomingViewingPublicKey, }; use sequencer_core::config::{AccountInitialData, CommitmentsInitialData, SequencerConfig}; +use tokio::test; + +// TODO: Make a proper benchmark instead of an ad-hoc test +#[test] +pub async fn tps_test() -> Result<()> { + let num_transactions = 300 * 5; + let target_tps = 12; + + let tps_test = TpsTestManager::new(target_tps, num_transactions); + let ctx = TestContext::new_with_sequencer_config(tps_test.generate_sequencer_config()).await?; + + let target_time = tps_test.target_time(); + info!( + "TPS test begin. Target time is {target_time:?} for {num_transactions} transactions ({target_tps} TPS)" + ); + + let txs = tps_test.build_public_txs(); + let now = Instant::now(); + + let mut tx_hashes = vec![]; + for (i, tx) in txs.into_iter().enumerate() { + let tx_hash = ctx + .sequencer_client() + .send_tx_public(tx) + .await + .unwrap() + .tx_hash; + info!("Sent tx {i}"); + tx_hashes.push(tx_hash); + } + + for (i, tx_hash) in tx_hashes.iter().enumerate() { + loop { + if now.elapsed().as_millis() > target_time.as_millis() { + panic!("TPS test failed by timeout"); + } + + let tx_obj = ctx + .sequencer_client() + .get_transaction_by_hash(tx_hash.clone()) + .await + .inspect_err(|err| { + log::warn!( + "Failed to get transaction by hash {tx_hash:#?} with error: {err:#?}" + ) + }); + + if let Ok(tx_obj) = tx_obj + && tx_obj.transaction.is_some() + { + info!("Found tx {i} with hash {tx_hash}"); + break; + } + } + } + let time_elapsed = now.elapsed().as_secs(); + + let tx_processed = tx_hashes.len(); + let actual_tps = tx_processed as u64 / time_elapsed; + info!("Processed {tx_processed} transactions in {time_elapsed:?} ({actual_tps} TPS)",); + + assert_eq!(tx_processed, num_transactions); + + assert!( + time_elapsed <= target_time.as_secs(), + "Elapsed time {time_elapsed:?} exceeded target time {target_time:?}" + ); + + info!("TPS test finished successfully"); + + Ok(()) +} pub(crate) struct TpsTestManager { public_keypairs: Vec<(PrivateKey, AccountId)>, @@ -32,7 +107,7 @@ impl TpsTestManager { let account_id = AccountId::from(&public_key); (private_key, account_id) }) - .collect::>(); + .collect(); Self { public_keypairs, target_tps, @@ -72,7 +147,7 @@ impl TpsTestManager { /// Generates a sequencer configuration with initial balance in a number of public accounts. /// The transactions generated with the function `build_public_txs` will be valid in a node /// started with the config from this method. - pub(crate) fn generate_tps_test_config(&self) -> SequencerConfig { + pub(crate) fn generate_sequencer_config(&self) -> SequencerConfig { // Create public public keypairs let initial_public_accounts = self .public_keypairs @@ -118,7 +193,7 @@ impl TpsTestManager { /// it may take a while to run. In normal execution of the node this transaction will be accepted /// only once. Disabling the node's nullifier uniqueness check allows to submit this transaction /// multiple times with the purpose of testing the node's processing performance. -#[allow(unused)] +#[expect(dead_code, reason = "No idea if we need this, should we remove it?")] fn build_privacy_transaction() -> PrivacyPreservingTransaction { let program = Program::authenticated_transfer_program(); let sender_nsk = [1; 32]; diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs index b46c46c9..4814552a 100644 --- a/key_protocol/src/key_protocol_core/mod.rs +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -165,6 +165,14 @@ impl NSSAUserData { .map(Into::into) } } + + pub fn account_ids(&self) -> impl Iterator { + self.default_pub_account_signing_keys + .keys() + .chain(self.public_key_tree.account_id_map.keys()) + .chain(self.default_user_private_accounts.keys()) + .chain(self.private_key_tree.account_id_map.keys()) + } } impl Default for NSSAUserData { diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index 06164e9e..a844c524 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -13,6 +13,7 @@ mempool.workspace = true base58.workspace = true anyhow.workspace = true serde.workspace = true +serde_json.workspace = true tempfile.workspace = true chrono.workspace = true log.workspace = true diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 2f4ee3be..4ef08803 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -1,5 +1,10 @@ -use std::path::PathBuf; +use std::{ + fs::File, + io::BufReader, + path::{Path, PathBuf}, +}; +use anyhow::Result; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] @@ -43,3 +48,12 @@ pub struct SequencerConfig { /// Sequencer own signing key pub signing_key: [u8; 32], } + +impl SequencerConfig { + pub fn from_path(config_home: &Path) -> Result { + let file = File::open(config_home)?; + let reader = BufReader::new(file); + + Ok(serde_json::from_reader(reader)?) + } +} diff --git a/sequencer_rpc/src/net_utils.rs b/sequencer_rpc/src/net_utils.rs index 33eacae7..8b9b7e64 100644 --- a/sequencer_rpc/src/net_utils.rs +++ b/sequencer_rpc/src/net_utils.rs @@ -1,4 +1,4 @@ -use std::{io, sync::Arc}; +use std::{io, net::SocketAddr, sync::Arc}; use actix_cors::Cors; use actix_web::{App, Error as HttpError, HttpResponse, HttpServer, http, middleware, web}; @@ -42,25 +42,24 @@ fn get_cors(cors_allowed_origins: &[String]) -> Cors { .max_age(3600) } -#[allow(clippy::too_many_arguments)] pub fn new_http_server( config: RpcConfig, seuquencer_core: Arc>, mempool_handle: MemPoolHandle, -) -> io::Result { +) -> io::Result<(actix_web::dev::Server, SocketAddr)> { let RpcConfig { addr, cors_allowed_origins, limits_config, } = config; - info!(target:NETWORK, "Starting http server at {addr}"); + info!(target:NETWORK, "Starting HTTP server at {addr}"); let handler = web::Data::new(JsonHandler { sequencer_state: seuquencer_core.clone(), mempool_handle, }); // HTTP server - Ok(HttpServer::new(move || { + let http_server = HttpServer::new(move || { App::new() .wrap(get_cors(&cors_allowed_origins)) .app_data(handler.clone()) @@ -70,6 +69,14 @@ pub fn new_http_server( }) .bind(addr)? .shutdown_timeout(SHUTDOWN_TIMEOUT_SECS) - .disable_signals() - .run()) + .disable_signals(); + + let [addr] = http_server + .addrs() + .try_into() + .expect("Exactly one address bound is expected for sequencer HTTP server"); + + info!(target:NETWORK, "HTTP server started at {addr}"); + + Ok((http_server.run(), addr)) } diff --git a/sequencer_runner/src/config.rs b/sequencer_runner/src/config.rs deleted file mode 100644 index 58f539bc..00000000 --- a/sequencer_runner/src/config.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::{fs::File, io::BufReader, path::PathBuf}; - -use anyhow::Result; -use sequencer_core::config::SequencerConfig; - -pub fn from_file(config_home: PathBuf) -> Result { - let file = File::open(config_home)?; - let reader = BufReader::new(file); - - Ok(serde_json::from_reader(reader)?) -} diff --git a/sequencer_runner/src/lib.rs b/sequencer_runner/src/lib.rs index e9f4a844..5c1ab920 100644 --- a/sequencer_runner/src/lib.rs +++ b/sequencer_runner/src/lib.rs @@ -1,4 +1,4 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{net::SocketAddr, path::PathBuf, sync::Arc}; use actix_web::dev::ServerHandle; use anyhow::Result; @@ -9,8 +9,6 @@ use sequencer_core::{SequencerCore, config::SequencerConfig}; use sequencer_rpc::new_http_server; use tokio::{sync::Mutex, task::JoinHandle}; -pub mod config; - pub const RUST_LOG: &str = "RUST_LOG"; #[derive(Parser, Debug)] @@ -22,7 +20,7 @@ struct Args { pub async fn startup_sequencer( app_config: SequencerConfig, -) -> Result<(ServerHandle, JoinHandle>)> { +) -> Result<(ServerHandle, SocketAddr, JoinHandle>)> { let block_timeout = app_config.block_create_timeout_millis; let port = app_config.port; @@ -32,7 +30,7 @@ pub async fn startup_sequencer( let seq_core_wrapped = Arc::new(Mutex::new(sequencer_core)); - let http_server = new_http_server( + let (http_server, addr) = new_http_server( RpcConfig::with_port(port), Arc::clone(&seq_core_wrapped), mempool_handle, @@ -61,7 +59,7 @@ pub async fn startup_sequencer( } }); - Ok((http_server_handle, main_loop_handle)) + Ok((http_server_handle, addr, main_loop_handle)) } pub async fn main_runner() -> Result<()> { @@ -70,7 +68,7 @@ pub async fn main_runner() -> Result<()> { let args = Args::parse(); let Args { home_dir } = args; - let app_config = config::from_file(home_dir.join("sequencer_config.json"))?; + let app_config = SequencerConfig::from_path(&home_dir.join("sequencer_config.json"))?; if let Some(ref rust_log) = app_config.override_rust_log { info!("RUST_LOG env var set to {rust_log:?}"); @@ -81,7 +79,7 @@ pub async fn main_runner() -> Result<()> { } // ToDo: Add restart on failures - let (_, main_loop_handle) = startup_sequencer(app_config).await?; + let (_, _, main_loop_handle) = startup_sequencer(app_config).await?; main_loop_handle.await??; diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index 9e4b0785..0f88af26 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -25,6 +25,7 @@ rand.workspace = true itertools.workspace = true sha2.workspace = true futures.workspace = true +risc0-zkvm.workspace = true async-stream = "0.3.6" indicatif = { version = "0.18.3", features = ["improved_unicode"] } -risc0-zkvm.workspace = true +optfield = "0.4.0" diff --git a/wallet/src/cli/account.rs b/wallet/src/cli/account.rs index 3a55c55f..58e19847 100644 --- a/wallet/src/cli/account.rs +++ b/wallet/src/cli/account.rs @@ -68,9 +68,7 @@ impl WalletSubcommand for NewSubcommand { "Generated new account with account_id Public/{account_id} at path {chain_index}" ); - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::RegisterAccount { account_id }) } @@ -93,9 +91,7 @@ impl WalletSubcommand for NewSubcommand { hex::encode(key.incoming_viewing_public_key.to_bytes()) ); - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::RegisterAccount { account_id }) } @@ -257,9 +253,7 @@ impl WalletSubcommand for AccountSubcommand { { wallet_core.last_synced_block = curr_last_block; - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent data at {path:#?}"); + wallet_core.store_persistent_data().await?; } else { wallet_core.sync_to_block(curr_last_block).await?; } diff --git a/wallet/src/cli/config.rs b/wallet/src/cli/config.rs index bfebf9bf..15467160 100644 --- a/wallet/src/cli/config.rs +++ b/wallet/src/cli/config.rs @@ -108,9 +108,7 @@ impl WalletSubcommand for ConfigSubcommand { } } - let path = wallet_core.store_config_changes().await?; - - println!("Stored changed config at {path:#?}"); + wallet_core.store_config_changes().await? } ConfigSubcommand::Description { key } => match key.as_str() { "override_rust_log" => { diff --git a/wallet/src/cli/mod.rs b/wallet/src/cli/mod.rs index cf3b2f1c..c742ecbd 100644 --- a/wallet/src/cli/mod.rs +++ b/wallet/src/cli/mod.rs @@ -15,7 +15,6 @@ use crate::{ pinata::PinataProgramAgnosticSubcommand, token::TokenProgramAgnosticSubcommand, }, }, - helperfunctions::{fetch_config, fetch_persistent_storage, merge_auth_config}, }; pub mod account; @@ -97,43 +96,22 @@ pub enum SubcommandReturnValue { SyncedToBlock(u64), } -pub async fn execute_subcommand(command: Command) -> Result { - execute_subcommand_with_auth(command, None).await -} - -pub async fn execute_subcommand_with_auth( +pub async fn execute_subcommand( + wallet_core: &mut WalletCore, command: Command, - auth: Option, ) -> Result { - if fetch_persistent_storage().await.is_err() { - println!("Persistent storage not found, need to execute setup"); - - let password = read_password_from_stdin()?; - execute_setup_with_auth(password, auth.clone()).await?; - } - - let wallet_config = fetch_config().await?; - let wallet_config = merge_auth_config(wallet_config, auth.clone())?; - let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config).await?; - let subcommand_ret = match command { Command::AuthTransfer(transfer_subcommand) => { - transfer_subcommand - .handle_subcommand(&mut wallet_core) - .await? + transfer_subcommand.handle_subcommand(wallet_core).await? } Command::ChainInfo(chain_subcommand) => { - chain_subcommand.handle_subcommand(&mut wallet_core).await? + chain_subcommand.handle_subcommand(wallet_core).await? } Command::Account(account_subcommand) => { - account_subcommand - .handle_subcommand(&mut wallet_core) - .await? + account_subcommand.handle_subcommand(wallet_core).await? } Command::Pinata(pinata_subcommand) => { - pinata_subcommand - .handle_subcommand(&mut wallet_core) - .await? + pinata_subcommand.handle_subcommand(wallet_core).await? } Command::CheckHealth {} => { let remote_program_ids = wallet_core @@ -165,18 +143,15 @@ pub async fn execute_subcommand_with_auth( SubcommandReturnValue::Empty } - Command::Token(token_subcommand) => { - token_subcommand.handle_subcommand(&mut wallet_core).await? - } - Command::AMM(amm_subcommand) => amm_subcommand.handle_subcommand(&mut wallet_core).await?, + Command::Token(token_subcommand) => token_subcommand.handle_subcommand(wallet_core).await?, + Command::AMM(amm_subcommand) => amm_subcommand.handle_subcommand(wallet_core).await?, Command::Config(config_subcommand) => { - config_subcommand - .handle_subcommand(&mut wallet_core) - .await? + config_subcommand.handle_subcommand(wallet_core).await? } Command::RestoreKeys { depth } => { let password = read_password_from_stdin()?; - execute_keys_restoration_with_auth(password, depth, auth).await?; + wallet_core.reset_storage(password)?; + execute_keys_restoration(wallet_core, depth).await?; SubcommandReturnValue::Empty } @@ -200,14 +175,7 @@ pub async fn execute_subcommand_with_auth( Ok(subcommand_ret) } -pub async fn execute_continuous_run() -> Result<()> { - execute_continuous_run_with_auth(None).await -} -pub async fn execute_continuous_run_with_auth(auth: Option) -> Result<()> { - let config = fetch_config().await?; - let config = merge_auth_config(config, auth)?; - let mut wallet_core = WalletCore::start_from_config_update_chain(config.clone()).await?; - +pub async fn execute_continuous_run(wallet_core: &mut WalletCore) -> Result<()> { loop { let latest_block_num = wallet_core .sequencer_client @@ -217,7 +185,7 @@ pub async fn execute_continuous_run_with_auth(auth: Option) -> Result<() wallet_core.sync_to_block(latest_block_num).await?; tokio::time::sleep(std::time::Duration::from_millis( - config.seq_poll_timeout_millis, + wallet_core.config().seq_poll_timeout_millis, )) .await; } @@ -233,34 +201,7 @@ pub fn read_password_from_stdin() -> Result { Ok(password.trim().to_string()) } -pub async fn execute_setup(password: String) -> Result<()> { - execute_setup_with_auth(password, None).await -} - -pub async fn execute_setup_with_auth(password: String, auth: Option) -> Result<()> { - let config = fetch_config().await?; - let config = merge_auth_config(config, auth)?; - let wallet_core = WalletCore::start_from_config_new_storage(config.clone(), password).await?; - - wallet_core.store_persistent_data().await?; - - Ok(()) -} - -pub async fn execute_keys_restoration(password: String, depth: u32) -> Result<()> { - execute_keys_restoration_with_auth(password, depth, None).await -} - -pub async fn execute_keys_restoration_with_auth( - password: String, - depth: u32, - auth: Option, -) -> Result<()> { - let config = fetch_config().await?; - let config = merge_auth_config(config, auth)?; - let mut wallet_core = - WalletCore::start_from_config_new_storage(config.clone(), password.clone()).await?; - +pub async fn execute_keys_restoration(wallet_core: &mut WalletCore, depth: u32) -> Result<()> { wallet_core .storage .user_data diff --git a/wallet/src/cli/programs/native_token_transfer.rs b/wallet/src/cli/programs/native_token_transfer.rs index 7868a7cd..0cfc0fb6 100644 --- a/wallet/src/cli/programs/native_token_transfer.rs +++ b/wallet/src/cli/programs/native_token_transfer.rs @@ -69,9 +69,7 @@ impl WalletSubcommand for AuthTransferSubcommand { println!("Transaction data is {transfer_tx:?}"); - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; } AccountPrivacyKind::Private => { let account_id = account_id.parse()?; @@ -96,9 +94,7 @@ impl WalletSubcommand for AuthTransferSubcommand { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; } } @@ -337,9 +333,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -381,9 +375,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -421,9 +413,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -454,9 +444,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommandShielded { let tx_hash = res.tx_hash; - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -500,9 +488,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -520,9 +506,7 @@ impl WalletSubcommand for NativeTokenTransferProgramSubcommand { println!("Transaction data is {transfer_tx:?}"); - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::Empty) } diff --git a/wallet/src/cli/programs/pinata.rs b/wallet/src/cli/programs/pinata.rs index e0b65709..192d3f47 100644 --- a/wallet/src/cli/programs/pinata.rs +++ b/wallet/src/cli/programs/pinata.rs @@ -168,9 +168,7 @@ impl WalletSubcommand for PinataProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } diff --git a/wallet/src/cli/programs/token.rs b/wallet/src/cli/programs/token.rs index 25d61ff3..b5ea1b34 100644 --- a/wallet/src/cli/programs/token.rs +++ b/wallet/src/cli/programs/token.rs @@ -740,9 +740,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -790,9 +788,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -831,9 +827,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -872,9 +866,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -923,9 +915,7 @@ impl WalletSubcommand for TokenProgramSubcommandPrivate { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -971,9 +961,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1009,9 +997,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1047,9 +1033,7 @@ impl WalletSubcommand for TokenProgramSubcommandDeshielded { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1102,9 +1086,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { println!("Transaction data is {:?}", tx.message); } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1140,9 +1122,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1178,9 +1158,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1216,9 +1194,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1262,9 +1238,7 @@ impl WalletSubcommand for TokenProgramSubcommandShielded { println!("Transaction data is {:?}", tx.message); } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1323,9 +1297,7 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1371,9 +1343,7 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } @@ -1419,9 +1389,7 @@ impl WalletSubcommand for CreateNewTokenProgramSubcommand { )?; } - let path = wallet_core.store_persistent_data().await?; - - println!("Stored persistent accounts at {path:#?}"); + wallet_core.store_persistent_data().await?; Ok(SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash }) } diff --git a/wallet/src/config.rs b/wallet/src/config.rs index c06ccc49..45407b6d 100644 --- a/wallet/src/config.rs +++ b/wallet/src/config.rs @@ -1,11 +1,17 @@ -use std::str::FromStr; +use std::{ + io::{BufReader, Write as _}, + path::Path, + str::FromStr, +}; +use anyhow::{Context as _, Result}; use key_protocol::key_management::{ KeyChain, key_tree::{ chain_index::ChainIndex, keys_private::ChildKeysPrivate, keys_public::ChildKeysPublic, }, }; +use log::warn; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -105,6 +111,25 @@ pub struct PersistentStorage { pub last_synced_block: u64, } +impl PersistentStorage { + pub fn from_path(path: &Path) -> Result { + match std::fs::File::open(path) { + Ok(file) => { + let storage_content = BufReader::new(file); + Ok(serde_json::from_reader(storage_content)?) + } + Err(err) => match err.kind() { + std::io::ErrorKind::NotFound => { + anyhow::bail!("Not found, please setup roots from config command beforehand"); + } + _ => { + anyhow::bail!("IO error {err:#?}"); + } + }, + } + } +} + impl InitialAccountData { pub fn account_id(&self) -> nssa::AccountId { match &self { @@ -172,9 +197,11 @@ pub struct GasConfig { pub gas_limit_runtime: u64, } +#[optfield::optfield(pub WalletConfigOverrides, rewrap, attrs = (derive(Debug, Default)))] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct WalletConfig { /// Override rust log (env var logging level) + #[serde(skip_serializing_if = "Option::is_none")] pub override_rust_log: Option, /// Sequencer URL pub sequencer_addr: String, @@ -189,6 +216,7 @@ pub struct WalletConfig { /// Initial accounts for wallet pub initial_accounts: Vec, /// Basic authentication credentials + #[serde(skip_serializing_if = "Option::is_none")] pub basic_auth: Option, } @@ -748,3 +776,98 @@ impl Default for WalletConfig { } } } + +impl WalletConfig { + pub fn from_path_or_initialize_default(config_path: &Path) -> Result { + match std::fs::File::open(config_path) { + Ok(file) => { + let reader = std::io::BufReader::new(file); + Ok(serde_json::from_reader(reader)?) + } + Err(err) if err.kind() == std::io::ErrorKind::NotFound => { + println!("Config not found, setting up default config"); + + let config_home = config_path.parent().ok_or_else(|| { + anyhow::anyhow!( + "Could not get parent directory of config file at {config_path:#?}" + ) + })?; + std::fs::create_dir_all(config_home)?; + + println!("Created configs dir at path {config_home:#?}"); + + let mut file = std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(config_path)?; + + let config = WalletConfig::default(); + let default_config_serialized = serde_json::to_vec_pretty(&config).unwrap(); + + file.write_all(&default_config_serialized)?; + + println!("Configs set up"); + Ok(config) + } + Err(err) => Err(err).context("IO error"), + } + } + + pub fn apply_overrides(&mut self, overrides: WalletConfigOverrides) { + let WalletConfig { + override_rust_log, + sequencer_addr, + seq_poll_timeout_millis, + seq_tx_poll_max_blocks, + seq_poll_max_retries, + seq_block_poll_max_amount, + initial_accounts, + basic_auth, + } = self; + + let WalletConfigOverrides { + override_rust_log: o_override_rust_log, + sequencer_addr: o_sequencer_addr, + seq_poll_timeout_millis: o_seq_poll_timeout_millis, + seq_tx_poll_max_blocks: o_seq_tx_poll_max_blocks, + seq_poll_max_retries: o_seq_poll_max_retries, + seq_block_poll_max_amount: o_seq_block_poll_max_amount, + initial_accounts: o_initial_accounts, + basic_auth: o_basic_auth, + } = overrides; + + if let Some(v) = o_override_rust_log { + warn!("Overriding wallet config 'override_rust_log' to {v:#?}"); + *override_rust_log = v; + } + if let Some(v) = o_sequencer_addr { + warn!("Overriding wallet config 'sequencer_addr' to {v}"); + *sequencer_addr = v; + } + if let Some(v) = o_seq_poll_timeout_millis { + warn!("Overriding wallet config 'seq_poll_timeout_millis' to {v}"); + *seq_poll_timeout_millis = v; + } + if let Some(v) = o_seq_tx_poll_max_blocks { + warn!("Overriding wallet config 'seq_tx_poll_max_blocks' to {v}"); + *seq_tx_poll_max_blocks = v; + } + if let Some(v) = o_seq_poll_max_retries { + warn!("Overriding wallet config 'seq_poll_max_retries' to {v}"); + *seq_poll_max_retries = v; + } + if let Some(v) = o_seq_block_poll_max_amount { + warn!("Overriding wallet config 'seq_block_poll_max_amount' to {v}"); + *seq_block_poll_max_amount = v; + } + if let Some(v) = o_initial_accounts { + warn!("Overriding wallet config 'initial_accounts' to {v:#?}"); + *initial_accounts = v; + } + if let Some(v) = o_basic_auth { + warn!("Overriding wallet config 'basic_auth' to {v:#?}"); + *basic_auth = v; + } + } +} diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index 5f1dcf77..23bf4bb8 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -7,23 +7,22 @@ use nssa::Account; use nssa_core::account::Nonce; use rand::{RngCore, rngs::OsRng}; use serde::Serialize; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; use crate::{ HOME_DIR_ENV_VAR, config::{ - BasicAuth, InitialAccountData, InitialAccountDataPrivate, InitialAccountDataPublic, - PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, WalletConfig, + InitialAccountData, InitialAccountDataPrivate, InitialAccountDataPublic, + PersistentAccountDataPrivate, PersistentAccountDataPublic, PersistentStorage, }, }; /// Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed. -pub fn get_home_nssa_var() -> Result { +fn get_home_nssa_var() -> Result { Ok(PathBuf::from_str(&std::env::var(HOME_DIR_ENV_VAR)?)?) } /// Get home dir for wallet. Env var `HOME` must be set before execution to succeed. -pub fn get_home_default_path() -> Result { +fn get_home_default_path() -> Result { std::env::home_dir() .map(|path| path.join(".nssa").join("wallet")) .ok_or(anyhow::anyhow!("Failed to get HOME")) @@ -38,96 +37,20 @@ pub fn get_home() -> Result { } } -/// Fetch config from default home -pub async fn fetch_config() -> Result { - let config_home = get_home()?; - let mut config_needs_setup = false; - - let config = match tokio::fs::OpenOptions::new() - .read(true) - .open(config_home.join("wallet_config.json")) - .await - { - Ok(mut file) => { - let mut config_contents = vec![]; - file.read_to_end(&mut config_contents).await?; - - serde_json::from_slice(&config_contents)? - } - Err(err) => match err.kind() { - std::io::ErrorKind::NotFound => { - config_needs_setup = true; - - println!("Config not found, setting up default config"); - - WalletConfig::default() - } - _ => anyhow::bail!("IO error {err:#?}"), - }, - }; - - if config_needs_setup { - tokio::fs::create_dir_all(&config_home).await?; - - println!("Created configs dir at path {config_home:#?}"); - - let mut file = tokio::fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(config_home.join("wallet_config.json")) - .await?; - - let default_config_serialized = - serde_json::to_vec_pretty(&WalletConfig::default()).unwrap(); - - file.write_all(&default_config_serialized).await?; - - println!("Configs setted up"); - } - - Ok(config) +/// Fetch config path from default home +pub fn fetch_config_path() -> Result { + let home = get_home()?; + let config_path = home.join("wallet_config.json"); + Ok(config_path) } -/// Parse CLI auth string and merge with config auth, prioritizing CLI -pub fn merge_auth_config( - mut config: WalletConfig, - cli_auth: Option, -) -> Result { - if let Some(auth_str) = cli_auth { - let cli_auth_config: BasicAuth = auth_str.parse()?; - - if config.basic_auth.is_some() { - println!("Warning: CLI auth argument takes precedence over config basic-auth"); - } - - config.basic_auth = Some(cli_auth_config); - } - Ok(config) -} - -/// Fetch data stored at home +/// Fetch path to data storage from default home /// /// File must be created through setup beforehand. -pub async fn fetch_persistent_storage() -> Result { +pub fn fetch_persistent_storage_path() -> Result { let home = get_home()?; let accs_path = home.join("storage.json"); - let mut storage_content = vec![]; - - match tokio::fs::File::open(accs_path).await { - Ok(mut file) => { - file.read_to_end(&mut storage_content).await?; - Ok(serde_json::from_slice(&storage_content)?) - } - Err(err) => match err.kind() { - std::io::ErrorKind::NotFound => { - anyhow::bail!("Not found, please setup roots from config command beforehand"); - } - _ => { - anyhow::bail!("IO error {err:#?}"); - } - }, - } + Ok(accs_path) } /// Produces data for storage diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index bad6435a..7c3da737 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, sync::Arc}; -use anyhow::Result; +use anyhow::{Context, Result}; use base64::{Engine, engine::general_purpose::STANDARD as BASE64}; use chain_storage::WalletChainStore; use common::{ @@ -25,10 +25,8 @@ pub use privacy_preserving_tx::PrivacyPreservingAccount; use tokio::io::AsyncWriteExt; use crate::{ - config::PersistentStorage, - helperfunctions::{ - fetch_persistent_storage, get_home, produce_data_for_storage, produce_random_nonces, - }, + config::{PersistentStorage, WalletConfigOverrides}, + helperfunctions::{produce_data_for_storage, produce_random_nonces}, poller::TxPoller, }; @@ -124,91 +122,133 @@ impl TokenHolding { } pub struct WalletCore { - pub storage: WalletChainStore, - pub poller: TxPoller, + config_path: PathBuf, + storage: WalletChainStore, + storage_path: PathBuf, + poller: TxPoller, + // TODO: Make all fields private pub sequencer_client: Arc, pub last_synced_block: u64, } impl WalletCore { - pub async fn start_from_config_update_chain(config: WalletConfig) -> Result { - let basic_auth = config - .basic_auth - .as_ref() - .map(|auth| (auth.username.clone(), auth.password.clone())); - let client = Arc::new(SequencerClient::new_with_auth( - config.sequencer_addr.clone(), - basic_auth, - )?); - let tx_poller = TxPoller::new(config.clone(), client.clone()); + /// Construct wallet using [`HOME_DIR_ENV_VAR`] env var for paths or user home dir if not set. + pub fn from_env() -> Result { + let config_path = helperfunctions::fetch_config_path()?; + let storage_path = helperfunctions::fetch_persistent_storage_path()?; + Self::new_update_chain(config_path, storage_path, None) + } + + pub fn new_update_chain( + config_path: PathBuf, + storage_path: PathBuf, + config_overrides: Option, + ) -> Result { let PersistentStorage { accounts: persistent_accounts, last_synced_block, - } = fetch_persistent_storage().await?; + } = PersistentStorage::from_path(&storage_path) + .with_context(|| format!("Failed to read persistent storage at {storage_path:#?}"))?; - let storage = WalletChainStore::new(config, persistent_accounts)?; - - Ok(Self { - storage, - poller: tx_poller, - sequencer_client: client.clone(), + Self::new( + config_path, + storage_path, + config_overrides, + |config| WalletChainStore::new(config, persistent_accounts), last_synced_block, - }) + ) } - pub async fn start_from_config_new_storage( - config: WalletConfig, + pub fn new_init_storage( + config_path: PathBuf, + storage_path: PathBuf, + config_overrides: Option, password: String, ) -> Result { + Self::new( + config_path, + storage_path, + config_overrides, + |config| WalletChainStore::new_storage(config, password), + 0, + ) + } + + fn new( + config_path: PathBuf, + storage_path: PathBuf, + config_overrides: Option, + storage_ctor: impl FnOnce(WalletConfig) -> Result, + last_synced_block: u64, + ) -> Result { + let mut config = WalletConfig::from_path_or_initialize_default(&config_path) + .with_context(|| format!("Failed to deserialize wallet config at {config_path:#?}"))?; + if let Some(config_overrides) = config_overrides { + config.apply_overrides(config_overrides); + } + let basic_auth = config .basic_auth .as_ref() .map(|auth| (auth.username.clone(), auth.password.clone())); - let client = Arc::new(SequencerClient::new_with_auth( + let sequencer_client = Arc::new(SequencerClient::new_with_auth( config.sequencer_addr.clone(), basic_auth, )?); - let tx_poller = TxPoller::new(config.clone(), client.clone()); + let tx_poller = TxPoller::new(config.clone(), Arc::clone(&sequencer_client)); - let storage = WalletChainStore::new_storage(config, password)?; + let storage = storage_ctor(config)?; Ok(Self { + config_path, + storage_path, storage, poller: tx_poller, - sequencer_client: client.clone(), - last_synced_block: 0, + sequencer_client, + last_synced_block, }) } - /// Store persistent data at home - pub async fn store_persistent_data(&self) -> Result { - let home = get_home()?; - let storage_path = home.join("storage.json"); + /// Get configuration with applied overrides + pub fn config(&self) -> &WalletConfig { + &self.storage.wallet_config + } - let data = produce_data_for_storage(&self.storage.user_data, self.last_synced_block); - let storage = serde_json::to_vec_pretty(&data)?; + /// Get storage + pub fn storage(&self) -> &WalletChainStore { + &self.storage + } - let mut storage_file = tokio::fs::File::create(storage_path.as_path()).await?; - storage_file.write_all(&storage).await?; - - info!("Stored data at {storage_path:#?}"); - - Ok(storage_path) + /// Reset storage + pub fn reset_storage(&mut self, password: String) -> Result<()> { + self.storage = WalletChainStore::new_storage(self.storage.wallet_config.clone(), password)?; + Ok(()) } /// Store persistent data at home - pub async fn store_config_changes(&self) -> Result { - let home = get_home()?; - let config_path = home.join("wallet_config.json"); + pub async fn store_persistent_data(&self) -> Result<()> { + let data = produce_data_for_storage(&self.storage.user_data, self.last_synced_block); + let storage = serde_json::to_vec_pretty(&data)?; + + let mut storage_file = tokio::fs::File::create(&self.storage_path).await?; + storage_file.write_all(&storage).await?; + + println!("Stored persistent accounts at {:#?}", self.storage_path); + + Ok(()) + } + + /// Store persistent data at home + pub async fn store_config_changes(&self) -> Result<()> { let config = serde_json::to_vec_pretty(&self.storage.wallet_config)?; - let mut config_file = tokio::fs::File::create(config_path.as_path()).await?; + let mut config_file = tokio::fs::File::create(&self.config_path).await?; config_file.write_all(&config).await?; - info!("Stored data at {config_path:#?}"); + info!("Stored data at {:#?}", self.config_path); - Ok(config_path) + Ok(()) } pub fn create_new_account_public( diff --git a/wallet/src/main.rs b/wallet/src/main.rs index 708b1fcf..045b1b83 100644 --- a/wallet/src/main.rs +++ b/wallet/src/main.rs @@ -1,36 +1,65 @@ -use anyhow::Result; +use anyhow::{Context as _, Result}; use clap::{CommandFactory as _, Parser as _}; -use tokio::runtime::Builder; -use wallet::cli::{Args, execute_continuous_run_with_auth, execute_subcommand_with_auth}; - -pub const NUM_THREADS: usize = 2; +use wallet::{ + WalletCore, + cli::{Args, execute_continuous_run, execute_subcommand, read_password_from_stdin}, + config::WalletConfigOverrides, + helperfunctions::{fetch_config_path, fetch_persistent_storage_path}, +}; // TODO #169: We have sample configs for sequencer, but not for wallet // TODO #168: Why it requires config as a directory? Maybe better to deduce directory from config // file path? // TODO #172: Why it requires config as env var while sequencer_runner accepts as // argument? -fn main() -> Result<()> { - let runtime = Builder::new_multi_thread() - .worker_threads(NUM_THREADS) - .enable_all() - .build() - .unwrap(); - - let args = Args::parse(); +#[tokio::main] +async fn main() -> Result<()> { + let Args { + continuous_run, + auth, + command, + } = Args::parse(); env_logger::init(); - runtime.block_on(async move { - if let Some(command) = args.command { - let _output = execute_subcommand_with_auth(command, args.auth).await?; - Ok(()) - } else if args.continuous_run { - execute_continuous_run_with_auth(args.auth).await + let config_path = fetch_config_path().context("Could not fetch config path")?; + let storage_path = + fetch_persistent_storage_path().context("Could not fetch persistent storage path")?; + + // Override basic auth if provided via CLI + let config_overrides = WalletConfigOverrides { + basic_auth: auth.map(|auth| auth.parse()).transpose()?.map(Some), + ..Default::default() + }; + + if let Some(command) = command { + let mut wallet = if !storage_path.exists() { + // TODO: Maybe move to `WalletCore::from_env()` or similar? + + println!("Persistent storage not found, need to execute setup"); + + let password = read_password_from_stdin()?; + let wallet = WalletCore::new_init_storage( + config_path, + storage_path, + Some(config_overrides), + password, + )?; + + wallet.store_persistent_data().await?; + wallet } else { - let help = Args::command().render_long_help(); - println!("{help}"); - Ok(()) - } - }) + WalletCore::new_update_chain(config_path, storage_path, Some(config_overrides))? + }; + let _output = execute_subcommand(&mut wallet, command).await?; + Ok(()) + } else if continuous_run { + let mut wallet = + WalletCore::new_update_chain(config_path, storage_path, Some(config_overrides))?; + execute_continuous_run(&mut wallet).await + } else { + let help = Args::command().render_long_help(); + println!("{help}"); + Ok(()) + } } diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index 0baeeac8..da98aed1 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -15,11 +15,10 @@ impl NativeTokenTransfer<'_> { let instruction: u128 = 0; self.0 - .send_privacy_preserving_tx_with_pre_check( + .send_privacy_preserving_tx( vec![PrivacyPreservingAccount::PrivateOwned(from)], &Program::serialize_instruction(instruction).unwrap(), &Program::authenticated_transfer_program().into(), - |_| Ok(()), ) .await .map(|(resp, secrets)| { From f9032a676aa7004b9c9b2fc2df30a71fcb67fee3 Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Wed, 31 Dec 2025 04:33:18 +0300 Subject: [PATCH 13/31] chore: fix cargo machete --- Cargo.lock | 1 - sequencer_runner/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 50edef2f..9ccc65b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4989,7 +4989,6 @@ dependencies = [ "log", "sequencer_core", "sequencer_rpc", - "serde_json", "tokio", ] diff --git a/sequencer_runner/Cargo.toml b/sequencer_runner/Cargo.toml index 72001451..55f56dec 100644 --- a/sequencer_runner/Cargo.toml +++ b/sequencer_runner/Cargo.toml @@ -10,7 +10,6 @@ sequencer_rpc.workspace = true clap = { workspace = true, features = ["derive", "env"] } anyhow.workspace = true -serde_json.workspace = true env_logger.workspace = true log.workspace = true actix.workspace = true From 4f5c3a831e021acafcafb88fa7ad7743ef28b01f Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Wed, 31 Dec 2025 13:50:11 +0300 Subject: [PATCH 14/31] fix: skip tps_test in ci --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ace2d07b..38161a0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,11 +101,11 @@ jobs: - name: Install nextest run: cargo install cargo-nextest - - name: Run unit tests + - name: Run tests env: RISC0_DEV_MODE: "1" RUST_LOG: "info" - run: cargo nextest run --no-fail-fast + run: cargo nextest run --no-fail-fast -- --skip tps_test valid-proof-test: runs-on: ubuntu-latest From 72a3e406b849de27c21770164e52eb1845358d0a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 6 Jan 2026 16:57:03 -0300 Subject: [PATCH 15/31] add r0vm binary to runtime stage --- sequencer_runner/Dockerfile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sequencer_runner/Dockerfile b/sequencer_runner/Dockerfile index 3b2153c3..84df3f3c 100644 --- a/sequencer_runner/Dockerfile +++ b/sequencer_runner/Dockerfile @@ -7,6 +7,7 @@ RUN apt-get update && apt-get install -y \ libssl-dev \ libclang-dev \ clang \ + curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /sequencer_runner @@ -31,6 +32,14 @@ RUN cargo build --release --bin sequencer_runner # Strip debug symbols to reduce binary size RUN strip /sequencer_runner/target/release/sequencer_runner +# Install r0vm +RUN curl -L https://risczero.com/install | bash +ENV PATH="/root/.cargo/bin:/root/.risc0/bin:${PATH}" +RUN rzup install +RUN cp "$(which r0vm)" /usr/local/bin/r0vm +RUN test -x /usr/local/bin/r0vm +RUN r0vm --version + # Runtime stage - minimal image FROM debian:trixie-slim @@ -47,6 +56,9 @@ RUN useradd -m -u 1000 -s /bin/bash sequencer_user && \ # Copy binary from builder COPY --from=builder --chown=sequencer_user:sequencer_user /sequencer_runner/target/release/sequencer_runner /usr/local/bin/sequencer_runner +# Copy r0vm binary from builder +COPY --from=builder --chown=sequencer_user:sequencer_user /usr/local/bin/r0vm /usr/local/bin/r0vm + # Copy entrypoint script COPY sequencer_runner/docker-entrypoint.sh /docker-entrypoint.sh RUN chmod +x /docker-entrypoint.sh @@ -71,6 +83,9 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ # Run the application ENV RUST_LOG=info +# Set explicit location for r0vm binary +ENV RISC0_SERVER_PATH=/usr/local/bin/r0vm + USER root ENTRYPOINT ["/docker-entrypoint.sh"] From ccce2213da731e671af15e85859f994d0e855256 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 13 Jan 2026 16:53:00 -0300 Subject: [PATCH 16/31] sequencer posts blocks to bedrock --- Cargo.lock | 332 ++++++++++++++++-- Cargo.toml | 5 +- bedrock_client/Cargo.toml | 1 + common/src/block.rs | 2 +- .../configs/sequencer/sequencer_config.json | 6 +- integration_tests/tests/tps.rs | 1 + sequencer_core/Cargo.toml | 7 + sequencer_core/src/block_settlement_client.rs | 118 +++++++ sequencer_core/src/config.rs | 10 + sequencer_core/src/lib.rs | 56 ++- sequencer_rpc/src/process.rs | 1 + sequencer_runner/Cargo.toml | 1 + .../configs/debug/sequencer_config.json | 8 +- sequencer_runner/src/lib.rs | 2 +- 14 files changed, 502 insertions(+), 48 deletions(-) create mode 100644 sequencer_core/src/block_settlement_client.rs diff --git a/Cargo.lock b/Cargo.lock index 9ccc65b4..deee8b1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,15 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "archery" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" +dependencies = [ + "triomphe", +] + [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -948,6 +957,7 @@ version = "0.1.0" dependencies = [ "anyhow", "common-http-client", + "key-management-system-service", "reqwest", ] @@ -1098,7 +1108,7 @@ dependencies = [ [[package]] name = "broadcast-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "async-trait", "derivative", @@ -1267,6 +1277,35 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "chain-service" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "async-trait", + "broadcast-service", + "bytes", + "cryptarchia-engine", + "cryptarchia-sync", + "futures", + "groth16", + "nomos-core", + "nomos-ledger", + "nomos-network", + "nomos-storage", + "nomos-utils", + "num-bigint", + "overwatch", + "serde", + "serde_with", + "services-utils", + "strum", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-futures", +] + [[package]] name = "chrono" version = "0.4.42" @@ -1294,7 +1333,7 @@ dependencies = [ [[package]] name = "circuits-prover" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "circuits-utils", "tempfile", @@ -1303,7 +1342,7 @@ dependencies = [ [[package]] name = "circuits-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "dirs", ] @@ -1395,9 +1434,10 @@ dependencies = [ [[package]] name = "common-http-client" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "broadcast-service", + "chain-service", "futures", "nomos-core", "nomos-da-messages", @@ -1452,6 +1492,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -1530,7 +1579,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryptarchia-engine" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "cfg_eval", "nomos-utils", @@ -1541,6 +1590,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "cryptarchia-sync" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "bytes", + "cryptarchia-engine", + "futures", + "nomos-core", + "rand 0.8.5", + "serde", + "serde_with", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -1707,7 +1773,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.111", ] [[package]] @@ -2344,7 +2410,7 @@ dependencies = [ [[package]] name = "groth16" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -2653,7 +2719,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -3015,7 +3081,7 @@ dependencies = [ [[package]] name = "key-management-system-keys" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "async-trait", "bytes", @@ -3040,13 +3106,28 @@ dependencies = [ [[package]] name = "key-management-system-macros" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "proc-macro2", "quote", "syn 2.0.111", ] +[[package]] +name = "key-management-system-service" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "async-trait", + "key-management-system-keys", + "log", + "overwatch", + "serde", + "thiserror 2.0.17", + "tokio", + "tracing", +] + [[package]] name = "key_protocol" version = "0.1.0" @@ -3071,7 +3152,7 @@ dependencies = [ [[package]] name = "kzgrs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "ark-bls12-381", "ark-ec 0.4.2", @@ -3090,7 +3171,7 @@ dependencies = [ [[package]] name = "kzgrs-backend" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "ark-ff 0.4.2", "ark-poly 0.4.2", @@ -3172,6 +3253,7 @@ dependencies = [ "bs58", "hkdf", "multihash", + "serde", "sha2", "thiserror 2.0.17", "tracing", @@ -3374,6 +3456,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mmr" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "ark-ff 0.4.2", + "groth16", + "poseidon2", + "rpds", + "serde", +] + [[package]] name = "multiaddr" version = "0.18.2" @@ -3483,7 +3577,7 @@ dependencies = [ [[package]] name = "nomos-blend-crypto" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "blake2", "groth16", @@ -3498,10 +3592,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "nomos-blend-message" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "blake2", + "derivative", + "groth16", + "itertools 0.14.0", + "key-management-system-keys", + "nomos-blend-crypto", + "nomos-blend-proofs", + "nomos-core", + "nomos-utils", + "serde", + "serde-big-array", + "serde_with", + "thiserror 1.0.69", + "tracing", + "zeroize", +] + [[package]] name = "nomos-blend-proofs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", @@ -3516,7 +3632,7 @@ dependencies = [ [[package]] name = "nomos-core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "ark-ff 0.4.2", "async-trait", @@ -3546,7 +3662,7 @@ dependencies = [ [[package]] name = "nomos-da-messages" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "blake2", "futures", @@ -3559,7 +3675,7 @@ dependencies = [ [[package]] name = "nomos-http-api-common" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "axum", "governor", @@ -3571,10 +3687,68 @@ dependencies = [ "tower_governor", ] +[[package]] +name = "nomos-ledger" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "cryptarchia-engine", + "groth16", + "key-management-system-keys", + "mmr", + "nomos-blend-crypto", + "nomos-blend-message", + "nomos-blend-proofs", + "nomos-core", + "nomos-utils", + "num-bigint", + "rand 0.8.5", + "rpds", + "serde", + "thiserror 1.0.69", + "tracing", + "utxotree", +] + +[[package]] +name = "nomos-network" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "async-trait", + "cryptarchia-sync", + "futures", + "nomos-core", + "overwatch", + "serde", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "nomos-storage" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "async-trait", + "bytes", + "cryptarchia-engine", + "futures", + "libp2p-identity", + "multiaddr", + "nomos-core", + "overwatch", + "serde", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "nomos-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "async-trait", "blake2", @@ -3826,6 +4000,7 @@ source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65a dependencies = [ "async-trait", "futures", + "overwatch-derive", "thiserror 2.0.17", "tokio", "tokio-stream", @@ -3833,6 +4008,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "overwatch-derive" +version = "0.1.0" +source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65adbd55e51f1e3f9eead62432a" +dependencies = [ + "convert_case 0.8.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -3939,7 +4126,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "pol" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "circuits-prover", "circuits-utils", @@ -3967,7 +4154,7 @@ dependencies = [ [[package]] name = "poq" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "circuits-prover", "circuits-utils", @@ -3989,7 +4176,7 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "poseidon2" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -4042,6 +4229,28 @@ dependencies = [ "toml_edit 0.23.10+spec-1.0.0", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -4155,7 +4364,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror 2.0.17", "tokio", "tracing", @@ -4192,7 +4401,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] @@ -4660,6 +4869,16 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "rpds" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e75f485e819d4d3015e6c0d55d02a4fd3db47c1993d9e603e0361fba2bffb34" +dependencies = [ + "archery", + "serde", +] + [[package]] name = "rrs-lib" version = "0.1.0" @@ -4938,18 +5157,25 @@ version = "0.1.0" dependencies = [ "anyhow", "base58", + "bedrock_client", + "borsh", "chrono", "common", "futures", + "key-management-system-service", "log", "mempool", + "nomos-core", "nssa", "nssa_core", + "rand 0.8.5", + "reqwest", "serde", "serde_json", "storage", "tempfile", "tokio", + "zksign", ] [[package]] @@ -4990,6 +5216,7 @@ dependencies = [ "sequencer_core", "sequencer_rpc", "tokio", + "zksign", ] [[package]] @@ -5002,6 +5229,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -5108,6 +5344,21 @@ dependencies = [ "serde", ] +[[package]] +name = "services-utils" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "async-trait", + "futures", + "log", + "overwatch", + "serde", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5751,6 +6002,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "tracing-subscriber" version = "0.2.25" @@ -5760,6 +6021,12 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" + [[package]] name = "try-lock" version = "0.2.5" @@ -5863,6 +6130,21 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utxotree" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" +dependencies = [ + "ark-ff 0.4.2", + "groth16", + "nomos-core", + "num-bigint", + "poseidon2", + "rpds", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "valuable" version = "0.1.1" @@ -6310,7 +6592,7 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "witness-generator" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "circuits-utils", "tempfile", @@ -6465,7 +6747,7 @@ dependencies = [ [[package]] name = "zksign" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#b89238be3ad8111b9975e1023b87d8672d0edd74" dependencies = [ "circuits-prover", "circuits-utils", diff --git a/Cargo.toml b/Cargo.toml index 30b4576a..35b6597a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ sequencer_rpc = { path = "sequencer_rpc" } sequencer_runner = { path = "sequencer_runner" } wallet = { path = "wallet" } test_program_methods = { path = "test_program_methods" } +bedrock_client = { path = "bedrock_client" } tokio = { version = "1.28.2", features = [ "net", @@ -78,7 +79,9 @@ base58 = "0.2.0" itertools = "0.14.0" url = "2.5.4" -common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", branch = "feat-skip-zk-build" } +common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } +key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } +nomos-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml index 034a093e..e771f0c8 100644 --- a/bedrock_client/Cargo.toml +++ b/bedrock_client/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" reqwest.workspace = true anyhow.workspace = true common-http-client.workspace = true +key-management-system-service.workspace = true diff --git a/common/src/block.rs b/common/src/block.rs index baba1e42..944b8989 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -43,7 +43,7 @@ pub struct Block { pub body: BlockBody, } -#[derive(Debug, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct HashableBlockData { pub block_id: BlockId, pub prev_block_hash: BlockHash, diff --git a/integration_tests/configs/sequencer/sequencer_config.json b/integration_tests/configs/sequencer/sequencer_config.json index 1548bb5b..51611141 100644 --- a/integration_tests/configs/sequencer/sequencer_config.json +++ b/integration_tests/configs/sequencer/sequencer_config.json @@ -154,5 +154,7 @@ 37, 37, 37 - ] -} \ No newline at end of file + ], + "bedrock_channel_id": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], + "bedrock_node_url": "http://localhost:18080" +} diff --git a/integration_tests/tests/tps.rs b/integration_tests/tests/tps.rs index b62a3ce7..e5bd5d05 100644 --- a/integration_tests/tests/tps.rs +++ b/integration_tests/tests/tps.rs @@ -185,6 +185,7 @@ impl TpsTestManager { initial_accounts: initial_public_accounts, initial_commitments: vec![initial_commitment], signing_key: [37; 32], + bedrock_config: None } } } diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index a844c524..c0cd33ee 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -17,6 +17,13 @@ serde_json.workspace = true tempfile.workspace = true chrono.workspace = true log.workspace = true +bedrock_client.workspace = true +key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } +nomos-core = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } +zksign = { git = "https://github.com/logos-blockchain/logos-blockchain.git", default-features = false } +rand.workspace = true +reqwest.workspace = true +borsh.workspace = true [features] default = [] diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs new file mode 100644 index 00000000..8950ad22 --- /dev/null +++ b/sequencer_core/src/block_settlement_client.rs @@ -0,0 +1,118 @@ +use std::{fs, path::Path}; + +use anyhow::Result; +use bedrock_client::BedrockClient; +use common::block::HashableBlockData; +use key_management_system_service::keys::{ED25519_SECRET_KEY_SIZE, Ed25519Key, Ed25519PublicKey}; +use nomos_core::mantle::{ + MantleTx, Op, OpProof, SignedMantleTx, Transaction, TxHash, ledger, + ops::channel::{ChannelId, MsgId, inscribe::InscriptionOp}, +}; +use reqwest::Url; + +use crate::config::BedrockConfig; + +pub struct BlockSettlementClient { + bedrock_node_url: Url, + bedrock_client: BedrockClient, + bedrock_signing_key: Ed25519Key, + bedrock_channel_id: ChannelId, +} + +impl BlockSettlementClient { + pub fn new(home: &Path, config: &BedrockConfig) -> Self { + let bedrock_signing_key = + load_or_create_signing_key(&home.join("bedrock_signing_key")).unwrap(); + let bedrock_node_url = Url::parse(&config.node_url).unwrap(); + let bedrock_channel_id = ChannelId::from(config.channel_id); + let bedrock_client = + BedrockClient::new(None).expect("Bedrock client should be able to initialize"); + Self { + bedrock_node_url, + bedrock_client, + bedrock_signing_key, + bedrock_channel_id, + } + } + + /// Create and sign a transaction for inscribing data + pub fn create_inscribe_tx(&self, data: Vec, parent: MsgId) -> SignedMantleTx { + let verifying_key_bytes = self.bedrock_signing_key.public_key().to_bytes(); + let verifying_key = + Ed25519PublicKey::from_bytes(&verifying_key_bytes).expect("valid ed25519 public key"); + + let inscribe_op = InscriptionOp { + channel_id: self.bedrock_channel_id, + inscription: data, + parent, + signer: verifying_key, + }; + + let ledger_tx = ledger::Tx::new(vec![], vec![]); + + let inscribe_tx = MantleTx { + ops: vec![Op::ChannelInscribe(inscribe_op)], + ledger_tx, + storage_gas_price: 0, + execution_gas_price: 0, + }; + + let tx_hash = inscribe_tx.hash(); + let signature_bytes = self + .bedrock_signing_key + .sign_payload(tx_hash.as_signing_bytes().as_ref()) + .to_bytes(); + let signature = + key_management_system_service::keys::Ed25519Signature::from_bytes(&signature_bytes); + + SignedMantleTx { + ops_proofs: vec![OpProof::Ed25519Sig(signature)], + ledger_tx_proof: empty_ledger_signature(&tx_hash), + mantle_tx: inscribe_tx, + } + } + + /// Post a transaction to the node and wait for inclusion + pub async fn post_and_wait(&self, block_data: &HashableBlockData) -> Result { + let msg_id: MsgId = { + let mut this = [0; 32]; + this[0..8].copy_from_slice(&block_data.block_id.to_le_bytes()); + this.into() + }; + + let inscription_data = borsh::to_vec(&block_data)?; + let tx = self.create_inscribe_tx(inscription_data, msg_id); + + // Post the transaction + self.bedrock_client + .0 + .post_transaction(self.bedrock_node_url.clone(), tx.clone()) + .await?; + + Ok(block_data.block_id) + } +} + +/// Load signing key from file or generate a new one if it doesn't exist +fn load_or_create_signing_key(path: &Path) -> Result { + if path.exists() { + let key_bytes = fs::read(path).map_err(|_| ())?; + if key_bytes.len() != ED25519_SECRET_KEY_SIZE { + // TODO: proper error + return Err(()); + } + let key_array: [u8; ED25519_SECRET_KEY_SIZE] = + key_bytes.try_into().expect("length already checked"); + Ok(Ed25519Key::from_bytes(&key_array)) + } else { + let mut key_bytes = [0u8; ED25519_SECRET_KEY_SIZE]; + rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut key_bytes); + fs::write(path, key_bytes).map_err(|_| ())?; + Ok(Ed25519Key::from_bytes(&key_bytes)) + } +} + +fn empty_ledger_signature(tx_hash: &TxHash) -> key_management_system_service::keys::ZkSignature { + key_management_system_service::keys::ZkKey::multi_sign(&[], tx_hash.as_ref()) + .expect("multi-sign with empty key set works") +} diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 4ef08803..ed42fa3a 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -47,6 +47,16 @@ pub struct SequencerConfig { pub initial_commitments: Vec, /// Sequencer own signing key pub signing_key: [u8; 32], + /// Bedrock configuration options + pub bedrock_config: Option, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct BedrockConfig { + /// Bedrock channel ID + pub channel_id: [u8; 32], + /// Bedrock Url + pub node_url: String, } impl SequencerConfig { diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 8e193ff6..52b389f0 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -13,8 +13,9 @@ use log::warn; use mempool::{MemPool, MemPoolHandle}; use serde::{Deserialize, Serialize}; -use crate::block_store::SequencerBlockStore; +use crate::{block_settlement_client::BlockSettlementClient, block_store::SequencerBlockStore}; +mod block_settlement_client; pub mod block_store; pub mod config; @@ -24,6 +25,7 @@ pub struct SequencerCore { mempool: MemPool, sequencer_config: SequencerConfig, chain_height: u64, + block_settlement_client: Option, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -87,12 +89,18 @@ impl SequencerCore { state.add_pinata_program(PINATA_BASE58.parse().unwrap()); let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size); + let block_settlement = config + .bedrock_config + .as_ref() + .map(|bedrock_config| BlockSettlementClient::new(&config.home, bedrock_config)); + let mut this = Self { state, block_store, mempool, chain_height: config.genesis_id, sequencer_config: config, + block_settlement_client: block_settlement, }; this.sync_state_with_stored_blocks(); @@ -137,9 +145,21 @@ impl SequencerCore { Ok(tx) } + pub async fn produce_new_block_and_post_to_settlement_layer(&mut self) -> Result { + let block_data = self.produce_new_block_with_mempool_transactions()?; + + if let Some(block_settlement) = &self.block_settlement_client { + block_settlement.post_and_wait(&block_data).await?; + log::info!("Posted block data to Bedrock"); + } + + Ok(self.chain_height) + } + /// Produces new block from transactions in mempool - pub fn produce_new_block_with_mempool_transactions(&mut self) -> Result { + pub fn produce_new_block_with_mempool_transactions(&mut self) -> Result { let now = Instant::now(); + let new_block_height = self.chain_height + 1; let mut valid_transactions = vec![]; @@ -167,8 +187,6 @@ impl SequencerCore { let curr_time = chrono::Utc::now().timestamp_millis() as u64; - let num_txs_in_block = valid_transactions.len(); - let hashable_data = HashableBlockData { block_id: new_block_height, transactions: valid_transactions, @@ -176,14 +194,16 @@ impl SequencerCore { timestamp: curr_time, }; - let block = hashable_data.into_block(self.block_store.signing_key()); + let block = hashable_data + .clone() + .into_block(self.block_store.signing_key()); self.block_store.put_block_at_id(block)?; self.chain_height = new_block_height; // TODO: Consider switching to `tracing` crate to have more structured and consistent logs - // e.g. + // // e.g. // // ``` // info!( @@ -194,11 +214,10 @@ impl SequencerCore { // ``` log::info!( "Created block with {} transactions in {} seconds", - num_txs_in_block, + hashable_data.transactions.len(), now.elapsed().as_secs() ); - - Ok(self.chain_height) + Ok(hashable_data) } pub fn state(&self) -> &nssa::V02State { @@ -277,6 +296,7 @@ mod tests { initial_accounts, initial_commitments: vec![], signing_key: *sequencer_sign_key_for_testing().value(), + bedrock_config: None, } } @@ -619,9 +639,9 @@ mod tests { let tx = common::test_utils::produce_dummy_empty_transaction(); mempool_handle.push(tx).await.unwrap(); - let block_id = sequencer.produce_new_block_with_mempool_transactions(); - assert!(block_id.is_ok()); - assert_eq!(block_id.unwrap(), genesis_height + 1); + let block = sequencer.produce_new_block_with_mempool_transactions(); + assert!(block.is_ok()); + assert_eq!(block.unwrap().block_id, genesis_height + 1); } #[tokio::test] @@ -658,7 +678,8 @@ mod tests { // Create block let current_height = sequencer .produce_new_block_with_mempool_transactions() - .unwrap(); + .unwrap() + .block_id; let block = sequencer .block_store .get_block_at_id(current_height) @@ -697,7 +718,8 @@ mod tests { mempool_handle.push(tx.clone()).await.unwrap(); let current_height = sequencer .produce_new_block_with_mempool_transactions() - .unwrap(); + .unwrap() + .block_id; let block = sequencer .block_store .get_block_at_id(current_height) @@ -708,7 +730,8 @@ mod tests { mempool_handle.push(tx.clone()).await.unwrap(); let current_height = sequencer .produce_new_block_with_mempool_transactions() - .unwrap(); + .unwrap() + .block_id; let block = sequencer .block_store .get_block_at_id(current_height) @@ -743,7 +766,8 @@ mod tests { mempool_handle.push(tx.clone()).await.unwrap(); let current_height = sequencer .produce_new_block_with_mempool_transactions() - .unwrap(); + .unwrap() + .block_id; let block = sequencer .block_store .get_block_at_id(current_height) diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index 387abf28..b89993f9 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -388,6 +388,7 @@ mod tests { initial_accounts, initial_commitments: vec![], signing_key: *sequencer_sign_key_for_testing().value(), + bedrock_config: None, } } diff --git a/sequencer_runner/Cargo.toml b/sequencer_runner/Cargo.toml index 55f56dec..8ee9f1db 100644 --- a/sequencer_runner/Cargo.toml +++ b/sequencer_runner/Cargo.toml @@ -9,6 +9,7 @@ sequencer_core = { workspace = true, features = ["testnet"] } sequencer_rpc.workspace = true clap = { workspace = true, features = ["derive", "env"] } +zksign = { git = "https://github.com/logos-blockchain/logos-blockchain.git", default-features = false } anyhow.workspace = true env_logger.workspace = true log.workspace = true diff --git a/sequencer_runner/configs/debug/sequencer_config.json b/sequencer_runner/configs/debug/sequencer_config.json index 58348f68..8d602d37 100644 --- a/sequencer_runner/configs/debug/sequencer_config.json +++ b/sequencer_runner/configs/debug/sequencer_config.json @@ -154,5 +154,9 @@ 37, 37, 37 - ] -} \ No newline at end of file + ], + "bedrock_config": { + "channel_id": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], + "node_url": "http://localhost:34485" + } +} diff --git a/sequencer_runner/src/lib.rs b/sequencer_runner/src/lib.rs index 5c1ab920..dd4f38a0 100644 --- a/sequencer_runner/src/lib.rs +++ b/sequencer_runner/src/lib.rs @@ -50,7 +50,7 @@ pub async fn startup_sequencer( let id = { let mut state = seq_core_wrapped.lock().await; - state.produce_new_block_with_mempool_transactions()? + state.produce_new_block_and_post_to_settlement_layer().await? }; info!("Block with id {id} created"); From 26806daedc12a2056f27e6f8002fd78d54a7fd7d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 14 Jan 2026 23:32:45 -0300 Subject: [PATCH 17/31] fix config --- integration_tests/configs/sequencer/sequencer_config.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/integration_tests/configs/sequencer/sequencer_config.json b/integration_tests/configs/sequencer/sequencer_config.json index 51611141..575d3de3 100644 --- a/integration_tests/configs/sequencer/sequencer_config.json +++ b/integration_tests/configs/sequencer/sequencer_config.json @@ -154,7 +154,5 @@ 37, 37, 37 - ], - "bedrock_channel_id": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], - "bedrock_node_url": "http://localhost:18080" + ] } From 3dbe31501ce11682cad7223bc6c7f47307d2f11c Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 14 Jan 2026 23:45:58 -0300 Subject: [PATCH 18/31] fix merge --- sequencer_core/src/config.rs | 9 --------- sequencer_runner/configs/debug/sequencer_config.json | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index 8b4d991c..ed42fa3a 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -67,12 +67,3 @@ impl SequencerConfig { Ok(serde_json::from_reader(reader)?) } } - -impl SequencerConfig { - pub fn from_path(config_home: &Path) -> Result { - let file = File::open(config_home)?; - let reader = BufReader::new(file); - - Ok(serde_json::from_reader(reader)?) - } -} diff --git a/sequencer_runner/configs/debug/sequencer_config.json b/sequencer_runner/configs/debug/sequencer_config.json index 8d602d37..ab65ea70 100644 --- a/sequencer_runner/configs/debug/sequencer_config.json +++ b/sequencer_runner/configs/debug/sequencer_config.json @@ -157,6 +157,6 @@ ], "bedrock_config": { "channel_id": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], - "node_url": "http://localhost:34485" + "node_url": "http://localhost:45403" } } From aeb3fd3932d3e71679fb30a620763f6907c7b3f4 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 15 Jan 2026 00:28:40 -0300 Subject: [PATCH 19/31] replace unwrap with expect --- sequencer_core/src/block_settlement_client.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index 8950ad22..6ade6860 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -12,6 +12,7 @@ use reqwest::Url; use crate::config::BedrockConfig; +/// A component that posts block data to logos blockchain pub struct BlockSettlementClient { bedrock_node_url: Url, bedrock_client: BedrockClient, @@ -21,9 +22,9 @@ pub struct BlockSettlementClient { impl BlockSettlementClient { pub fn new(home: &Path, config: &BedrockConfig) -> Self { - let bedrock_signing_key = - load_or_create_signing_key(&home.join("bedrock_signing_key")).unwrap(); - let bedrock_node_url = Url::parse(&config.node_url).unwrap(); + let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key")) + .expect("Signing key should load or be created successfully"); + let bedrock_node_url = Url::parse(&config.node_url).expect("Bedrock URL should be a valid URL"); let bedrock_channel_id = ChannelId::from(config.channel_id); let bedrock_client = BedrockClient::new(None).expect("Bedrock client should be able to initialize"); From 8da515da6b772bf902f3e0d25776ff00f1cb17fd Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Thu, 15 Jan 2026 00:29:45 -0300 Subject: [PATCH 20/31] fmt --- integration_tests/tests/tps.rs | 2 +- sequencer_core/src/block_settlement_client.rs | 3 ++- sequencer_runner/src/lib.rs | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/integration_tests/tests/tps.rs b/integration_tests/tests/tps.rs index e5bd5d05..87f7e45a 100644 --- a/integration_tests/tests/tps.rs +++ b/integration_tests/tests/tps.rs @@ -185,7 +185,7 @@ impl TpsTestManager { initial_accounts: initial_public_accounts, initial_commitments: vec![initial_commitment], signing_key: [37; 32], - bedrock_config: None + bedrock_config: None, } } } diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index 6ade6860..1964f37f 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -24,7 +24,8 @@ impl BlockSettlementClient { pub fn new(home: &Path, config: &BedrockConfig) -> Self { let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key")) .expect("Signing key should load or be created successfully"); - let bedrock_node_url = Url::parse(&config.node_url).expect("Bedrock URL should be a valid URL"); + let bedrock_node_url = + Url::parse(&config.node_url).expect("Bedrock URL should be a valid URL"); let bedrock_channel_id = ChannelId::from(config.channel_id); let bedrock_client = BedrockClient::new(None).expect("Bedrock client should be able to initialize"); diff --git a/sequencer_runner/src/lib.rs b/sequencer_runner/src/lib.rs index dd4f38a0..fd4a6c08 100644 --- a/sequencer_runner/src/lib.rs +++ b/sequencer_runner/src/lib.rs @@ -50,7 +50,9 @@ pub async fn startup_sequencer( let id = { let mut state = seq_core_wrapped.lock().await; - state.produce_new_block_and_post_to_settlement_layer().await? + state + .produce_new_block_and_post_to_settlement_layer() + .await? }; info!("Block with id {id} created"); From 4105eecacec617dbc04cc3af3efd16c0b0f0629d Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 16 Jan 2026 19:50:54 -0300 Subject: [PATCH 21/31] fix msg id in mantle tx --- sequencer_core/src/block_settlement_client.rs | 21 ++++++++++--------- sequencer_core/src/lib.rs | 2 +- .../configs/debug/sequencer_config.json | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index 1964f37f..68a95dd3 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -18,6 +18,7 @@ pub struct BlockSettlementClient { bedrock_client: BedrockClient, bedrock_signing_key: Ed25519Key, bedrock_channel_id: ChannelId, + last_message_id: MsgId, } impl BlockSettlementClient { @@ -34,11 +35,12 @@ impl BlockSettlementClient { bedrock_client, bedrock_signing_key, bedrock_channel_id, + last_message_id: MsgId::from([0; 32]), } } /// Create and sign a transaction for inscribing data - pub fn create_inscribe_tx(&self, data: Vec, parent: MsgId) -> SignedMantleTx { + pub fn create_inscribe_tx(&self, data: Vec) -> SignedMantleTx { let verifying_key_bytes = self.bedrock_signing_key.public_key().to_bytes(); let verifying_key = Ed25519PublicKey::from_bytes(&verifying_key_bytes).expect("valid ed25519 public key"); @@ -46,7 +48,7 @@ impl BlockSettlementClient { let inscribe_op = InscriptionOp { channel_id: self.bedrock_channel_id, inscription: data, - parent, + parent: self.last_message_id, signer: verifying_key, }; @@ -75,15 +77,9 @@ impl BlockSettlementClient { } /// Post a transaction to the node and wait for inclusion - pub async fn post_and_wait(&self, block_data: &HashableBlockData) -> Result { - let msg_id: MsgId = { - let mut this = [0; 32]; - this[0..8].copy_from_slice(&block_data.block_id.to_le_bytes()); - this.into() - }; - + pub async fn post_and_wait(&mut self, block_data: &HashableBlockData) -> Result { let inscription_data = borsh::to_vec(&block_data)?; - let tx = self.create_inscribe_tx(inscription_data, msg_id); + let tx = self.create_inscribe_tx(inscription_data); // Post the transaction self.bedrock_client @@ -91,6 +87,11 @@ impl BlockSettlementClient { .post_transaction(self.bedrock_node_url.clone(), tx.clone()) .await?; + match tx.mantle_tx.ops.first() { + Some(Op::ChannelInscribe(inscribe)) => self.last_message_id = inscribe.id(), + _ => {} + } + Ok(block_data.block_id) } } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 52b389f0..561f5ef6 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -148,7 +148,7 @@ impl SequencerCore { pub async fn produce_new_block_and_post_to_settlement_layer(&mut self) -> Result { let block_data = self.produce_new_block_with_mempool_transactions()?; - if let Some(block_settlement) = &self.block_settlement_client { + if let Some(block_settlement) = self.block_settlement_client.as_mut() { block_settlement.post_and_wait(&block_data).await?; log::info!("Posted block data to Bedrock"); } diff --git a/sequencer_runner/configs/debug/sequencer_config.json b/sequencer_runner/configs/debug/sequencer_config.json index ab65ea70..ad43ba65 100644 --- a/sequencer_runner/configs/debug/sequencer_config.json +++ b/sequencer_runner/configs/debug/sequencer_config.json @@ -157,6 +157,6 @@ ], "bedrock_config": { "channel_id": [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], - "node_url": "http://localhost:45403" + "node_url": "http://localhost:8080" } } From 654e92f0d703d465f598bb7a8ec8992747d8125a Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 19 Jan 2026 14:26:48 -0300 Subject: [PATCH 22/31] clippy --- sequencer_core/src/block_settlement_client.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index 68a95dd3..c1af22f4 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -87,9 +87,8 @@ impl BlockSettlementClient { .post_transaction(self.bedrock_node_url.clone(), tx.clone()) .await?; - match tx.mantle_tx.ops.first() { - Some(Op::ChannelInscribe(inscribe)) => self.last_message_id = inscribe.id(), - _ => {} + if let Some(Op::ChannelInscribe(inscribe)) = tx.mantle_tx.ops.first() { + self.last_message_id = inscribe.id(); } Ok(block_data.block_id) From 66d01d69d72565a1a77d7def95069b2cba7fcc84 Mon Sep 17 00:00:00 2001 From: Pravdyvy Date: Tue, 20 Jan 2026 10:26:22 +0200 Subject: [PATCH 23/31] fix: dep fix --- Cargo.lock | 333 +++++++++++++++++++++++++++++++++++--- Cargo.toml | 2 +- bedrock_client/src/lib.rs | 2 +- 3 files changed, 311 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ccc65b4..00c7c418 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,15 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "archery" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e0a5f99dfebb87bb342d0f53bb92c81842e100bbb915223e38349580e5441d" +dependencies = [ + "triomphe", +] + [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -1098,7 +1107,7 @@ dependencies = [ [[package]] name = "broadcast-service" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "async-trait", "derivative", @@ -1267,6 +1276,36 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "chain-service" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "async-trait", + "broadcast-service", + "bytes", + "cryptarchia-engine", + "cryptarchia-sync", + "futures", + "groth16", + "nomos-core", + "nomos-ledger", + "nomos-network", + "nomos-storage", + "nomos-time", + "nomos-utils", + "num-bigint", + "overwatch", + "serde", + "serde_with", + "services-utils", + "strum", + "thiserror 1.0.69", + "tokio", + "tracing", + "tracing-futures", +] + [[package]] name = "chrono" version = "0.4.42" @@ -1294,7 +1333,7 @@ dependencies = [ [[package]] name = "circuits-prover" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "circuits-utils", "tempfile", @@ -1303,7 +1342,7 @@ dependencies = [ [[package]] name = "circuits-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "dirs", ] @@ -1395,9 +1434,10 @@ dependencies = [ [[package]] name = "common-http-client" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "broadcast-service", + "chain-service", "futures", "nomos-core", "nomos-da-messages", @@ -1452,6 +1492,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.10.0" @@ -1530,7 +1579,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryptarchia-engine" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "cfg_eval", "nomos-utils", @@ -1538,6 +1587,24 @@ dependencies = [ "serde_with", "thiserror 1.0.69", "time", + "tokio", + "tracing", +] + +[[package]] +name = "cryptarchia-sync" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "bytes", + "cryptarchia-engine", + "futures", + "nomos-core", + "rand 0.8.5", + "serde", + "serde_with", + "thiserror 1.0.69", + "tokio", "tracing", ] @@ -2344,7 +2411,7 @@ dependencies = [ [[package]] name = "groth16" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "ark-bn254 0.4.0", "ark-ec 0.4.2", @@ -3015,7 +3082,7 @@ dependencies = [ [[package]] name = "key-management-system-keys" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "async-trait", "bytes", @@ -3033,6 +3100,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tracing", + "x25519-dalek", "zeroize", "zksign", ] @@ -3040,7 +3108,7 @@ dependencies = [ [[package]] name = "key-management-system-macros" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "proc-macro2", "quote", @@ -3071,7 +3139,7 @@ dependencies = [ [[package]] name = "kzgrs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "ark-bls12-381", "ark-ec 0.4.2", @@ -3090,7 +3158,7 @@ dependencies = [ [[package]] name = "kzgrs-backend" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "ark-ff 0.4.2", "ark-poly 0.4.2", @@ -3172,6 +3240,7 @@ dependencies = [ "bs58", "hkdf", "multihash", + "serde", "sha2", "thiserror 2.0.17", "tracing", @@ -3374,6 +3443,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mmr" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "ark-ff 0.4.2", + "groth16", + "poseidon2", + "rpds", + "serde", +] + [[package]] name = "multiaddr" version = "0.18.2" @@ -3483,7 +3564,7 @@ dependencies = [ [[package]] name = "nomos-blend-crypto" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "blake2", "groth16", @@ -3491,17 +3572,35 @@ dependencies = [ "poq", "poseidon2", "rs-merkle-tree", - "serde", - "subtle", "thiserror 1.0.69", - "x25519-dalek", +] + +[[package]] +name = "nomos-blend-message" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "blake2", + "derivative", + "groth16", + "itertools 0.14.0", + "key-management-system-keys", + "nomos-blend-crypto", + "nomos-blend-proofs", + "nomos-core", + "nomos-utils", + "serde", + "serde-big-array", + "serde_with", + "thiserror 1.0.69", + "tracing", "zeroize", ] [[package]] name = "nomos-blend-proofs" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "ed25519-dalek", "generic-array 1.3.5", @@ -3516,7 +3615,7 @@ dependencies = [ [[package]] name = "nomos-core" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "ark-ff 0.4.2", "async-trait", @@ -3546,7 +3645,7 @@ dependencies = [ [[package]] name = "nomos-da-messages" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "blake2", "futures", @@ -3559,7 +3658,7 @@ dependencies = [ [[package]] name = "nomos-http-api-common" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "axum", "governor", @@ -3571,10 +3670,86 @@ dependencies = [ "tower_governor", ] +[[package]] +name = "nomos-ledger" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "cryptarchia-engine", + "groth16", + "key-management-system-keys", + "mmr", + "nomos-blend-crypto", + "nomos-blend-message", + "nomos-blend-proofs", + "nomos-core", + "nomos-utils", + "num-bigint", + "rand 0.8.5", + "rpds", + "serde", + "thiserror 1.0.69", + "tracing", + "utxotree", +] + +[[package]] +name = "nomos-network" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "async-trait", + "cryptarchia-sync", + "futures", + "nomos-core", + "overwatch", + "serde", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "nomos-storage" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "async-trait", + "bytes", + "cryptarchia-engine", + "futures", + "libp2p-identity", + "multiaddr", + "nomos-core", + "overwatch", + "serde", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "nomos-time" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "async-trait", + "cryptarchia-engine", + "futures", + "log", + "overwatch", + "sntpc", + "thiserror 2.0.17", + "time", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "nomos-utils" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "async-trait", "blake2", @@ -3826,6 +4001,7 @@ source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65a dependencies = [ "async-trait", "futures", + "overwatch-derive", "thiserror 2.0.17", "tokio", "tokio-stream", @@ -3833,6 +4009,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "overwatch-derive" +version = "0.1.0" +source = "git+https://github.com/logos-co/Overwatch?rev=f5a9902#f5a99022f389d65adbd55e51f1e3f9eead62432a" +dependencies = [ + "convert_case 0.8.0", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -3939,7 +4127,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "pol" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "circuits-prover", "circuits-utils", @@ -3967,7 +4155,7 @@ dependencies = [ [[package]] name = "poq" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "circuits-prover", "circuits-utils", @@ -3989,7 +4177,7 @@ checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "poseidon2" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "ark-bn254 0.4.0", "ark-ff 0.4.2", @@ -4042,6 +4230,28 @@ dependencies = [ "toml_edit 0.23.10+spec-1.0.0", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -4660,6 +4870,16 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "rpds" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e75f485e819d4d3015e6c0d55d02a4fd3db47c1993d9e603e0361fba2bffb34" +dependencies = [ + "archery", + "serde", +] + [[package]] name = "rrs-lib" version = "0.1.0" @@ -5002,6 +5222,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -5108,6 +5337,21 @@ dependencies = [ "serde", ] +[[package]] +name = "services-utils" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "async-trait", + "futures", + "log", + "overwatch", + "serde", + "serde_json", + "thiserror 1.0.69", + "tracing", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5167,6 +5411,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "sntpc" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78f778a0f82b3cf5d75f858eceee38e84d5292f1d03415e88cc4ec45ca6ba8a2" +dependencies = [ + "cfg-if", + "tokio", +] + [[package]] name = "socket2" version = "0.4.10" @@ -5751,6 +6005,16 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "tracing-subscriber" version = "0.2.25" @@ -5760,6 +6024,12 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" + [[package]] name = "try-lock" version = "0.2.5" @@ -5863,6 +6133,21 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utxotree" +version = "0.1.0" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" +dependencies = [ + "ark-ff 0.4.2", + "groth16", + "nomos-core", + "num-bigint", + "poseidon2", + "rpds", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "valuable" version = "0.1.1" @@ -6310,7 +6595,7 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "witness-generator" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "circuits-utils", "tempfile", @@ -6465,7 +6750,7 @@ dependencies = [ [[package]] name = "zksign" version = "0.1.0" -source = "git+https://github.com/logos-blockchain/logos-blockchain.git?branch=feat-skip-zk-build#fde542bb8398189fe5e9bad688a527618261f63d" +source = "git+https://github.com/logos-blockchain/logos-blockchain.git#0e88327ca7faa705dd4498316ad48caadb5b8b4e" dependencies = [ "circuits-prover", "circuits-utils", diff --git a/Cargo.toml b/Cargo.toml index 30b4576a..eed73b7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ base58 = "0.2.0" itertools = "0.14.0" url = "2.5.4" -common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git", branch = "feat-skip-zk-build" } +common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } rocksdb = { version = "0.24.0", default-features = false, features = [ "snappy", diff --git a/bedrock_client/src/lib.rs b/bedrock_client/src/lib.rs index 9315f083..36a3ac7a 100644 --- a/bedrock_client/src/lib.rs +++ b/bedrock_client/src/lib.rs @@ -10,7 +10,7 @@ pub struct BedrockClient(pub CommonHttpClient); impl BedrockClient { pub fn new(auth: Option) -> Result { let client = Client::builder() - //Add more fiedls if needed + //Add more fields if needed .timeout(std::time::Duration::from_secs(60)) .build()?; From e397d6c5766ccb76cc3cb0b5f511612ccc0e89db Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Tue, 20 Jan 2026 11:50:20 -0300 Subject: [PATCH 24/31] add bedrock status flag to blocks and last_finalized_block metadata info to sequencer db --- common/src/block.rs | 17 +++++++++--- common/src/test_utils.rs | 2 +- sequencer_core/src/block_store.rs | 4 +-- sequencer_core/src/lib.rs | 4 +-- storage/src/lib.rs | 45 +++++++++++++++++++++++-------- 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/common/src/block.rs b/common/src/block.rs index 944b8989..84b7a419 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -23,7 +23,7 @@ pub type BlockHash = [u8; 32]; pub type BlockId = u64; pub type TimeStamp = u64; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] pub struct BlockHeader { pub block_id: BlockId, pub prev_block_hash: BlockHash, @@ -32,15 +32,23 @@ pub struct BlockHeader { pub signature: nssa::Signature, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] pub struct BlockBody { pub transactions: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)] +pub enum BedrockStatus { + Pending, + Safe, + Finalized, +} + +#[derive(Debug, BorshSerialize, BorshDeserialize)] pub struct Block { pub header: BlockHeader, pub body: BlockBody, + pub bedrock_status: BedrockStatus, } #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] @@ -52,7 +60,7 @@ pub struct HashableBlockData { } impl HashableBlockData { - pub fn into_block(self, signing_key: &nssa::PrivateKey) -> Block { + pub fn into_pending_block(self, signing_key: &nssa::PrivateKey) -> Block { let data_bytes = borsh::to_vec(&self).unwrap(); let signature = nssa::Signature::new(signing_key, &data_bytes); let hash = OwnHasher::hash(&data_bytes); @@ -67,6 +75,7 @@ impl HashableBlockData { body: BlockBody { transactions: self.transactions, }, + bedrock_status: BedrockStatus::Pending, } } } diff --git a/common/src/test_utils.rs b/common/src/test_utils.rs index 7e3c0ba8..1125b86e 100644 --- a/common/src/test_utils.rs +++ b/common/src/test_utils.rs @@ -30,7 +30,7 @@ pub fn produce_dummy_block( transactions, }; - block_data.into_block(&sequencer_sign_key_for_testing()) + block_data.into_pending_block(&sequencer_sign_key_for_testing()) } pub fn produce_dummy_empty_transaction() -> EncodedTransaction { diff --git a/sequencer_core/src/block_store.rs b/sequencer_core/src/block_store.rs index 67535022..cd9aa194 100644 --- a/sequencer_core/src/block_store.rs +++ b/sequencer_core/src/block_store.rs @@ -46,7 +46,7 @@ impl SequencerBlockStore { } pub fn get_block_at_id(&self, id: u64) -> Result { - Ok(self.dbio.get_block(id)?.into_block(&self.signing_key)) + Ok(self.dbio.get_block(id)?) } pub fn put_block_at_id(&mut self, block: Block) -> Result<()> { @@ -113,7 +113,7 @@ mod tests { transactions: vec![], }; - let genesis_block = genesis_block_hashable_data.into_block(&signing_key); + let genesis_block = genesis_block_hashable_data.into_pending_block(&signing_key); // Start an empty node store let mut node_store = SequencerBlockStore::open_db_with_genesis(path, Some(genesis_block), signing_key) diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 561f5ef6..a7a16826 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -53,7 +53,7 @@ impl SequencerCore { }; let signing_key = nssa::PrivateKey::try_new(config.signing_key).unwrap(); - let genesis_block = hashable_data.into_block(&signing_key); + let genesis_block = hashable_data.into_pending_block(&signing_key); // Sequencer should panic if unable to open db, // as fixing this issue may require actions non-native to program scope @@ -196,7 +196,7 @@ impl SequencerCore { let block = hashable_data .clone() - .into_block(self.block_store.signing_key()); + .into_pending_block(self.block_store.signing_key()); self.block_store.put_block_at_id(block)?; diff --git a/storage/src/lib.rs b/storage/src/lib.rs index 87b78705..883684c2 100644 --- a/storage/src/lib.rs +++ b/storage/src/lib.rs @@ -1,6 +1,6 @@ use std::{path::Path, sync::Arc}; -use common::block::{Block, HashableBlockData}; +use common::block::Block; use error::DbError; use rocksdb::{ BoundColumnFamily, ColumnFamilyDescriptor, DBWithThreadMode, MultiThreaded, Options, @@ -26,6 +26,8 @@ pub const DB_META_FIRST_BLOCK_IN_DB_KEY: &str = "first_block_in_db"; pub const DB_META_LAST_BLOCK_IN_DB_KEY: &str = "last_block_in_db"; /// Key base for storing metainformation which describe if first block has been set pub const DB_META_FIRST_BLOCK_SET_KEY: &str = "first_block_set"; +/// Key base for storing metainformation about the last finalized block on Bedrock +pub const DB_META_LAST_FINALIZED_BLOCK_ID: &str = "last_finalized_block_id"; /// Key base for storing snapshot which describe block id pub const DB_SNAPSHOT_BLOCK_ID_KEY: &str = "block_id"; @@ -75,6 +77,7 @@ impl RocksDBIO { dbio.put_meta_first_block_in_db(block)?; dbio.put_meta_is_first_block_set()?; dbio.put_meta_last_block_in_db(block_id)?; + dbio.put_meta_last_finalized_block_id(None)?; Ok(dbio) } else { @@ -232,6 +235,28 @@ impl RocksDBIO { Ok(()) } + pub fn put_meta_last_finalized_block_id(&self, block_id: Option) -> DbResult<()> { + let cf_meta = self.meta_column(); + self.db + .put_cf( + &cf_meta, + borsh::to_vec(&DB_META_LAST_FINALIZED_BLOCK_ID).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize DB_META_LAST_FINALIZED_BLOCK_ID".to_string()), + ) + })?, + borsh::to_vec(&block_id).map_err(|err| { + DbError::borsh_cast_message( + err, + Some("Failed to serialize last block id".to_string()), + ) + })?, + ) + .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; + Ok(()) + } + pub fn put_meta_is_first_block_set(&self) -> DbResult<()> { let cf_meta = self.meta_column(); self.db @@ -269,7 +294,7 @@ impl RocksDBIO { Some("Failed to serialize block id".to_string()), ) })?, - borsh::to_vec(&HashableBlockData::from(block)).map_err(|err| { + borsh::to_vec(&block).map_err(|err| { DbError::borsh_cast_message( err, Some("Failed to serialize block data".to_string()), @@ -280,7 +305,7 @@ impl RocksDBIO { Ok(()) } - pub fn get_block(&self, block_id: u64) -> DbResult { + pub fn get_block(&self, block_id: u64) -> DbResult { let cf_block = self.block_column(); let res = self .db @@ -296,14 +321,12 @@ impl RocksDBIO { .map_err(|rerr| DbError::rocksdb_cast_message(rerr, None))?; if let Some(data) = res { - Ok( - borsh::from_slice::(&data).map_err(|serr| { - DbError::borsh_cast_message( - serr, - Some("Failed to deserialize block data".to_string()), - ) - })?, - ) + Ok(borsh::from_slice::(&data).map_err(|serr| { + DbError::borsh_cast_message( + serr, + Some("Failed to deserialize block data".to_string()), + ) + })?) } else { Err(DbError::db_interaction_error( "Block on this id not found".to_string(), From 56295d12d86a478bb8d6c4cbd4687cb3357fdf3c Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 21 Jan 2026 10:38:18 -0300 Subject: [PATCH 25/31] use anyhow for block settlement client error handling --- sequencer_core/src/block_settlement_client.rs | 20 ++++++++----------- sequencer_core/src/config.rs | 3 ++- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index c1af22f4..e14b1b47 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -1,6 +1,6 @@ use std::{fs, path::Path}; -use anyhow::Result; +use anyhow::{Result, anyhow}; use bedrock_client::BedrockClient; use common::block::HashableBlockData; use key_management_system_service::keys::{ED25519_SECRET_KEY_SIZE, Ed25519Key, Ed25519PublicKey}; @@ -25,8 +25,7 @@ impl BlockSettlementClient { pub fn new(home: &Path, config: &BedrockConfig) -> Self { let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key")) .expect("Signing key should load or be created successfully"); - let bedrock_node_url = - Url::parse(&config.node_url).expect("Bedrock URL should be a valid URL"); + let bedrock_node_url = config.node_url.clone(); let bedrock_channel_id = ChannelId::from(config.channel_id); let bedrock_client = BedrockClient::new(None).expect("Bedrock client should be able to initialize"); @@ -96,20 +95,17 @@ impl BlockSettlementClient { } /// Load signing key from file or generate a new one if it doesn't exist -fn load_or_create_signing_key(path: &Path) -> Result { +fn load_or_create_signing_key(path: &Path) -> Result { if path.exists() { - let key_bytes = fs::read(path).map_err(|_| ())?; - if key_bytes.len() != ED25519_SECRET_KEY_SIZE { - // TODO: proper error - return Err(()); - } - let key_array: [u8; ED25519_SECRET_KEY_SIZE] = - key_bytes.try_into().expect("length already checked"); + let key_bytes = fs::read(path)?; + let key_array: [u8; ED25519_SECRET_KEY_SIZE] = key_bytes + .try_into() + .map_err(|_| anyhow!("Found key with incorrect length"))?; Ok(Ed25519Key::from_bytes(&key_array)) } else { let mut key_bytes = [0u8; ED25519_SECRET_KEY_SIZE]; rand::RngCore::fill_bytes(&mut rand::thread_rng(), &mut key_bytes); - fs::write(path, key_bytes).map_err(|_| ())?; + fs::write(path, key_bytes)?; Ok(Ed25519Key::from_bytes(&key_bytes)) } } diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index ed42fa3a..5911cc52 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -5,6 +5,7 @@ use std::{ }; use anyhow::Result; +use reqwest::Url; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Clone)] @@ -56,7 +57,7 @@ pub struct BedrockConfig { /// Bedrock channel ID pub channel_id: [u8; 32], /// Bedrock Url - pub node_url: String, + pub node_url: Url, } impl SequencerConfig { From f04b299b1ab2e49221e585ac585cd5192e521629 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Wed, 21 Jan 2026 11:47:28 -0300 Subject: [PATCH 26/31] minor changes --- Cargo.lock | 1 + bedrock_client/Cargo.toml | 1 + bedrock_client/src/lib.rs | 24 +++++++++--- sequencer_core/src/block_settlement_client.rs | 39 ++++++++----------- sequencer_core/src/lib.rs | 12 +++--- 5 files changed, 42 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c543c49..2b5e93cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,6 +957,7 @@ version = "0.1.0" dependencies = [ "anyhow", "common-http-client", + "nomos-core", "reqwest", ] diff --git a/bedrock_client/Cargo.toml b/bedrock_client/Cargo.toml index 034a093e..e6f6f02a 100644 --- a/bedrock_client/Cargo.toml +++ b/bedrock_client/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" reqwest.workspace = true anyhow.workspace = true common-http-client.workspace = true +nomos-core.workspace = true diff --git a/bedrock_client/src/lib.rs b/bedrock_client/src/lib.rs index 9315f083..a5e0a049 100644 --- a/bedrock_client/src/lib.rs +++ b/bedrock_client/src/lib.rs @@ -1,21 +1,33 @@ use anyhow::Result; use common_http_client::CommonHttpClient; pub use common_http_client::{BasicAuthCredentials, Error}; -use reqwest::Client; +use nomos_core::mantle::SignedMantleTx; +use reqwest::{Client, Url}; // Simple wrapper // maybe extend in the future for our purposes -pub struct BedrockClient(pub CommonHttpClient); +pub struct BedrockClient { + http_client: CommonHttpClient, + node_url: Url, +} impl BedrockClient { - pub fn new(auth: Option) -> Result { + pub fn new(auth: Option, node_url: Url) -> Result { let client = Client::builder() //Add more fiedls if needed .timeout(std::time::Duration::from_secs(60)) .build()?; - Ok(BedrockClient(CommonHttpClient::new_with_client( - client, auth, - ))) + let http_client = CommonHttpClient::new_with_client(client, auth); + Ok(Self { + http_client, + node_url, + }) + } + + pub async fn post_transaction(&self, tx: SignedMantleTx) -> Result<(), Error> { + self.http_client + .post_transaction(self.node_url.clone(), tx) + .await } } diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index e14b1b47..58f4d7f4 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -8,13 +8,11 @@ use nomos_core::mantle::{ MantleTx, Op, OpProof, SignedMantleTx, Transaction, TxHash, ledger, ops::channel::{ChannelId, MsgId, inscribe::InscriptionOp}, }; -use reqwest::Url; use crate::config::BedrockConfig; /// A component that posts block data to logos blockchain pub struct BlockSettlementClient { - bedrock_node_url: Url, bedrock_client: BedrockClient, bedrock_signing_key: Ed25519Key, bedrock_channel_id: ChannelId, @@ -22,24 +20,21 @@ pub struct BlockSettlementClient { } impl BlockSettlementClient { - pub fn new(home: &Path, config: &BedrockConfig) -> Self { - let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key")) - .expect("Signing key should load or be created successfully"); - let bedrock_node_url = config.node_url.clone(); + pub fn try_new(home: &Path, config: &BedrockConfig) -> Result { + let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key"))?; let bedrock_channel_id = ChannelId::from(config.channel_id); - let bedrock_client = - BedrockClient::new(None).expect("Bedrock client should be able to initialize"); - Self { - bedrock_node_url, + let bedrock_client = BedrockClient::new(None, config.node_url.clone())?; + let channel_genesis_msg = MsgId::from([0; 32]); + Ok(Self { bedrock_client, bedrock_signing_key, bedrock_channel_id, - last_message_id: MsgId::from([0; 32]), - } + last_message_id: channel_genesis_msg, + }) } /// Create and sign a transaction for inscribing data - pub fn create_inscribe_tx(&self, data: Vec) -> SignedMantleTx { + pub fn create_inscribe_tx(&self, data: Vec) -> (SignedMantleTx, MsgId) { let verifying_key_bytes = self.bedrock_signing_key.public_key().to_bytes(); let verifying_key = Ed25519PublicKey::from_bytes(&verifying_key_bytes).expect("valid ed25519 public key"); @@ -50,12 +45,14 @@ impl BlockSettlementClient { parent: self.last_message_id, signer: verifying_key, }; + let inscribe_op_id = inscribe_op.id(); let ledger_tx = ledger::Tx::new(vec![], vec![]); let inscribe_tx = MantleTx { ops: vec![Op::ChannelInscribe(inscribe_op)], ledger_tx, + // Altruistic test config storage_gas_price: 0, execution_gas_price: 0, }; @@ -68,27 +65,23 @@ impl BlockSettlementClient { let signature = key_management_system_service::keys::Ed25519Signature::from_bytes(&signature_bytes); - SignedMantleTx { + let signed_mantle_tx = SignedMantleTx { ops_proofs: vec![OpProof::Ed25519Sig(signature)], ledger_tx_proof: empty_ledger_signature(&tx_hash), mantle_tx: inscribe_tx, - } + }; + (signed_mantle_tx, inscribe_op_id) } /// Post a transaction to the node and wait for inclusion pub async fn post_and_wait(&mut self, block_data: &HashableBlockData) -> Result { let inscription_data = borsh::to_vec(&block_data)?; - let tx = self.create_inscribe_tx(inscription_data); + let (tx, new_msg_id) = self.create_inscribe_tx(inscription_data); // Post the transaction - self.bedrock_client - .0 - .post_transaction(self.bedrock_node_url.clone(), tx.clone()) - .await?; + self.bedrock_client.post_transaction(tx).await?; - if let Some(Op::ChannelInscribe(inscribe)) = tx.mantle_tx.ops.first() { - self.last_message_id = inscribe.id(); - } + self.last_message_id = new_msg_id; Ok(block_data.block_id) } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index a7a16826..89cafc4c 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -89,10 +89,10 @@ impl SequencerCore { state.add_pinata_program(PINATA_BASE58.parse().unwrap()); let (mempool, mempool_handle) = MemPool::new(config.mempool_max_size); - let block_settlement = config - .bedrock_config - .as_ref() - .map(|bedrock_config| BlockSettlementClient::new(&config.home, bedrock_config)); + let block_settlement_client = config.bedrock_config.as_ref().map(|bedrock_config| { + BlockSettlementClient::try_new(&config.home, bedrock_config) + .expect("Block settlement client should be constructible") + }); let mut this = Self { state, @@ -100,7 +100,7 @@ impl SequencerCore { mempool, chain_height: config.genesis_id, sequencer_config: config, - block_settlement_client: block_settlement, + block_settlement_client, }; this.sync_state_with_stored_blocks(); @@ -203,7 +203,7 @@ impl SequencerCore { self.chain_height = new_block_height; // TODO: Consider switching to `tracing` crate to have more structured and consistent logs - // // e.g. + // e.g. // // ``` // info!( From a8f41838acc1e0a4cef1c835f6915bde0aa8839a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Pavl=C3=ADn?= Date: Thu, 22 Jan 2026 09:29:54 +0100 Subject: [PATCH 27/31] bug(wallet): conflicting flag -h for transaction info --- wallet/src/cli/chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wallet/src/cli/chain.rs b/wallet/src/cli/chain.rs index 419fa5e2..17ecd020 100644 --- a/wallet/src/cli/chain.rs +++ b/wallet/src/cli/chain.rs @@ -19,7 +19,7 @@ pub enum ChainSubcommand { /// Get transaction at hash from sequencer Transaction { /// hash - valid 32 byte hex string - #[arg(short, long)] + #[arg(short = 't', long)] hash: String, }, } From 6e745494c2c6ad9f7a127f60b7fa5d9fb0182610 Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Mon, 26 Jan 2026 16:25:06 +0300 Subject: [PATCH 28/31] fix: install cargo-nextest with locked option --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38161a0f..a8efedd9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,7 +99,7 @@ jobs: run: rustup install - name: Install nextest - run: cargo install cargo-nextest + run: cargo install --locked cargo-nextest - name: Run tests env: From 02702213dce649816c9ce6237984116763396564 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy <41742639+schouhy@users.noreply.github.com> Date: Mon, 26 Jan 2026 13:34:38 -0300 Subject: [PATCH 29/31] Update sequencer_core/src/block_settlement_client.rs Co-authored-by: Daniil Polyakov --- sequencer_core/src/block_settlement_client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index c41efa52..c0c727a8 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -23,9 +23,9 @@ pub struct BlockSettlementClient { impl BlockSettlementClient { pub fn try_new(home: &Path, config: &BedrockConfig) -> Result { - let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key"))?; + let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key")).context("Failed to load or create signing key")?; let bedrock_channel_id = ChannelId::from(config.channel_id); - let bedrock_client = BedrockClient::new(None, config.node_url.clone())?; + let bedrock_client = BedrockClient::new(None, config.node_url.clone()).context("Failed to initialize bedrock client")?; let channel_genesis_msg = MsgId::from([0; 32]); Ok(Self { bedrock_client, From e680544ca3dc1acb239900f0a3d00cd61396c070 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Mon, 26 Jan 2026 13:46:47 -0300 Subject: [PATCH 30/31] use context --- sequencer_core/src/block_settlement_client.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sequencer_core/src/block_settlement_client.rs b/sequencer_core/src/block_settlement_client.rs index c0c727a8..0aa22420 100644 --- a/sequencer_core/src/block_settlement_client.rs +++ b/sequencer_core/src/block_settlement_client.rs @@ -1,6 +1,6 @@ use std::{fs, path::Path}; -use anyhow::{Result, anyhow}; +use anyhow::{Context, Result, anyhow}; use bedrock_client::BedrockClient; use common::block::HashableBlockData; use logos_blockchain_core::mantle::{ @@ -23,9 +23,11 @@ pub struct BlockSettlementClient { impl BlockSettlementClient { pub fn try_new(home: &Path, config: &BedrockConfig) -> Result { - let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key")).context("Failed to load or create signing key")?; + let bedrock_signing_key = load_or_create_signing_key(&home.join("bedrock_signing_key")) + .context("Failed to load or create signing key")?; let bedrock_channel_id = ChannelId::from(config.channel_id); - let bedrock_client = BedrockClient::new(None, config.node_url.clone()).context("Failed to initialize bedrock client")?; + let bedrock_client = BedrockClient::new(None, config.node_url.clone()) + .context("Failed to initialize bedrock client")?; let channel_genesis_msg = MsgId::from([0; 32]); Ok(Self { bedrock_client, From 2288a725d23b8446b471e790764eab5fd2ea2bf2 Mon Sep 17 00:00:00 2001 From: Daniil Polyakov Date: Sat, 24 Jan 2026 04:05:59 +0300 Subject: [PATCH 31/31] feat: implement indexer service skeleton --- Cargo.lock | 547 ++++++++++++++- Cargo.toml | 15 +- artifacts/program_methods/amm.bin | Bin 460392 -> 460392 bytes .../authenticated_transfer.bin | Bin 380124 -> 380112 bytes artifacts/program_methods/pinata.bin | Bin 382720 -> 382736 bytes artifacts/program_methods/pinata_token.bin | Bin 390412 -> 390380 bytes .../privacy_preserving_circuit.bin | Bin 501580 -> 501580 bytes artifacts/program_methods/token.bin | Bin 418660 -> 418724 bytes artifacts/test_program_methods/burner.bin | Bin 376928 -> 376928 bytes .../test_program_methods/chain_caller.bin | Bin 390632 -> 390632 bytes .../test_program_methods/changer_claimer.bin | Bin 380008 -> 380056 bytes artifacts/test_program_methods/claimer.bin | Bin 375816 -> 375844 bytes .../test_program_methods/data_changer.bin | Bin 378544 -> 378544 bytes .../test_program_methods/extra_output.bin | Bin 375924 -> 375924 bytes .../malicious_authorization_changer.bin | Bin 382160 -> 382160 bytes artifacts/test_program_methods/minter.bin | Bin 375844 -> 375856 bytes .../test_program_methods/missing_output.bin | Bin 376008 -> 375992 bytes .../modified_transfer.bin | Bin 379396 -> 379380 bytes .../test_program_methods/nonce_changer.bin | Bin 375944 -> 375960 bytes artifacts/test_program_methods/noop.bin | Bin 375640 -> 375656 bytes .../program_owner_changer.bin | Bin 375868 -> 375868 bytes .../simple_balance_transfer.bin | Bin 377756 -> 377740 bytes indexer_service/Cargo.toml | 17 + indexer_service/Dockerfile | 64 ++ indexer_service/docker-compose.yml | 9 + indexer_service/protocol/Cargo.toml | 18 + indexer_service/protocol/src/convert.rs | 648 ++++++++++++++++++ indexer_service/protocol/src/lib.rs | 230 +++++++ indexer_service/rpc/Cargo.toml | 15 + indexer_service/rpc/src/lib.rs | 40 ++ indexer_service/src/lib.rs | 1 + indexer_service/src/main.rs | 72 ++ indexer_service/src/service.rs | 36 + nssa/core/src/account.rs | 4 + nssa/core/src/encoding.rs | 20 + .../privacy_preserving_transaction/circuit.rs | 10 + .../privacy_preserving_transaction/message.rs | 8 +- .../transaction.rs | 2 +- .../witness_set.rs | 14 + .../program_deployment_transaction/message.rs | 4 + .../transaction.rs | 4 + nssa/src/public_transaction/message.rs | 8 +- nssa/src/public_transaction/witness_set.rs | 10 + nssa/src/signature/mod.rs | 2 +- 44 files changed, 1774 insertions(+), 24 deletions(-) create mode 100644 indexer_service/Cargo.toml create mode 100644 indexer_service/Dockerfile create mode 100644 indexer_service/docker-compose.yml create mode 100644 indexer_service/protocol/Cargo.toml create mode 100644 indexer_service/protocol/src/convert.rs create mode 100644 indexer_service/protocol/src/lib.rs create mode 100644 indexer_service/rpc/Cargo.toml create mode 100644 indexer_service/rpc/src/lib.rs create mode 100644 indexer_service/src/lib.rs create mode 100644 indexer_service/src/main.rs create mode 100644 indexer_service/src/service.rs diff --git a/Cargo.lock b/Cargo.lock index 16baf5a4..b71a3f2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1177,6 +1177,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cexpr" version = "0.6.0" @@ -1310,6 +1316,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "common" version = "0.1.0" @@ -1399,6 +1415,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -1412,7 +1438,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "libc", ] @@ -2151,6 +2177,10 @@ name = "futures-timer" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +dependencies = [ + "gloo-timers", + "send_wrapper", +] [[package]] name = "futures-util" @@ -2235,6 +2265,52 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 1.4.0", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.69", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "governor" version = "0.6.3" @@ -2507,6 +2583,7 @@ dependencies = [ "http 1.4.0", "hyper", "hyper-util", + "log", "rustls", "rustls-pki-types", "tokio", @@ -2695,6 +2772,45 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee796ad498c8d9a1d68e477df8f754ed784ef875de1414ebdaf169f70a6a784" +[[package]] +name = "indexer_service" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "clap", + "env_logger", + "indexer_service_protocol", + "indexer_service_rpc", + "jsonrpsee", + "log", + "tokio", + "tokio-util", +] + +[[package]] +name = "indexer_service_protocol" +version = "0.1.0" +dependencies = [ + "base64", + "borsh", + "common", + "nssa", + "nssa_core", + "schemars 1.2.0", + "serde", +] + +[[package]] +name = "indexer_service_rpc" +version = "0.1.0" +dependencies = [ + "indexer_service_protocol", + "jsonrpsee", + "schemars 1.2.0", + "serde_json", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2855,6 +2971,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.34" @@ -2875,6 +3013,178 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpsee" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98" +dependencies = [ + "base64", + "futures-channel", + "futures-util", + "gloo-net", + "http 1.4.0", + "jsonrpsee-core", + "pin-project", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "soketto", + "thiserror 2.0.17", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" +dependencies = [ + "async-trait", + "bytes", + "futures-timer", + "futures-util", + "http 1.4.0", + "http-body", + "http-body-util", + "jsonrpsee-types", + "parking_lot", + "pin-project", + "rand 0.9.2", + "rustc-hash", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tower 0.5.2", + "tracing", + "wasm-bindgen-futures", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" +dependencies = [ + "base64", + "http-body", + "hyper", + "hyper-rustls", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "rustls", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tower 0.5.2", + "url", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da3f8ab5ce1bb124b6d082e62dffe997578ceaf0aeb9f3174a214589dc00f07" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f" +dependencies = [ + "futures-util", + "http 1.4.0", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tokio-util", + "tower 0.5.2", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" +dependencies = [ + "http 1.4.0", + "serde", + "serde_json", + "thiserror 2.0.17", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "tower 0.5.2", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" +dependencies = [ + "http 1.4.0", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "tower 0.5.2", + "url", +] + [[package]] name = "k256" version = "0.13.4" @@ -3717,10 +4027,10 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -3967,6 +4277,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "openssl-sys" version = "0.9.111" @@ -4324,7 +4640,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.1", "thiserror 2.0.17", "tokio", "tracing", @@ -4829,6 +5145,12 @@ dependencies = [ "librocksdb-sys", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rpds" version = "1.2.0" @@ -4944,6 +5266,7 @@ version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -4952,6 +5275,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe 0.2.1", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", +] + [[package]] name = "rustls-pki-types" version = "1.13.2" @@ -4962,6 +5297,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.5.1", + "security-framework-sys", + "webpki-root-certs 0.26.11", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.8" @@ -5004,6 +5366,15 @@ dependencies = [ "yaml-rust2", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.28" @@ -5027,16 +5398,29 @@ dependencies = [ [[package]] name = "schemars" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", + "schemars_derive", "serde", "serde_json", ] +[[package]] +name = "schemars_derive" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.111", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -5085,7 +5469,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.10.0", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.10.0", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -5111,6 +5508,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "sequencer_core" version = "0.1.0" @@ -5216,6 +5619,17 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "serde_json" version = "1.0.145" @@ -5273,7 +5687,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.12.1", "schemars 0.9.0", - "schemars 1.1.0", + "schemars 1.2.0", "serde_core", "serde_json", "serde_with_macros", @@ -5401,6 +5815,22 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "soketto" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" +dependencies = [ + "base64", + "bytes", + "futures", + "http 1.4.0", + "httparse", + "log", + "rand 0.8.5", + "sha1", +] + [[package]] name = "spin" version = "0.9.8" @@ -5540,7 +5970,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5791,12 +6221,13 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -6125,6 +6556,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wallet" version = "0.1.0" @@ -6270,6 +6711,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" +dependencies = [ + "webpki-root-certs 1.0.5", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "1.0.4" @@ -6380,6 +6839,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -6407,6 +6875,21 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -6440,6 +6923,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -6452,6 +6941,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -6464,6 +6959,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -6488,6 +6989,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -6500,6 +7007,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -6512,6 +7025,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -6524,6 +7043,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index f14f2559..3b1c9f12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,16 +2,19 @@ resolver = "3" members = [ "integration_tests", - "sequencer_runner", "storage", "key_protocol", - "sequencer_rpc", "mempool", "wallet", - "sequencer_core", "common", "nssa", "nssa/core", + "sequencer_core", + "sequencer_rpc", + "sequencer_runner", + "indexer_service", + "indexer_service/protocol", + "indexer_service/rpc", "program_methods", "program_methods/guest", "test_program_methods", @@ -32,6 +35,9 @@ key_protocol = { path = "key_protocol" } sequencer_core = { path = "sequencer_core" } sequencer_rpc = { path = "sequencer_rpc" } sequencer_runner = { path = "sequencer_runner" } +indexer_service = { path = "indexer_service" } +indexer_service_protocol = { path = "indexer_service/protocol" } +indexer_service_rpc = { path = "indexer_service/rpc" } wallet = { path = "wallet" } test_program_methods = { path = "test_program_methods" } bedrock_client = { path = "bedrock_client" } @@ -42,6 +48,7 @@ tokio = { version = "1.28.2", features = [ "sync", "fs", ] } +tokio-util = "0.7.18" risc0-zkvm = { version = "3.0.3", features = ['std'] } risc0-build = "3.0.3" anyhow = "1.0.98" @@ -52,6 +59,7 @@ serde = { version = "1.0.60", default-features = false, features = ["derive"] } serde_json = "1.0.81" actix = "0.13.0" actix-cors = "0.6.1" +jsonrpsee = "0.26.0" futures = "0.3" actix-rt = "*" lazy_static = "1.5.0" @@ -78,6 +86,7 @@ borsh = "1.5.7" base58 = "0.2.0" itertools = "0.14.0" url = "2.5.4" +schemars = "1.2.0" logos-blockchain-common-http-client = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } logos-blockchain-key-management-system-service = { git = "https://github.com/logos-blockchain/logos-blockchain.git" } diff --git a/artifacts/program_methods/amm.bin b/artifacts/program_methods/amm.bin index 1a2fabca8568da47267a038942e14d8ebf476f44..30cb7662bcb42bfeb9469c5a34e1e36a97043760 100644 GIT binary patch delta 977 zcmaFSBlDt1rlEzgg{g(Pg=Gut*Q>$?hUS(A7AD5#hGqsvh6aX4=BCpx*0PCAm$}Be z1e@&k%hy<0xv|MifBlek16B#f*R1N&rg}L!Wx0AlJ1}kh@QC#^R-G3wunJF~|CCi> zI^z@8^;nev4cCx1#$!0h_UXD$SvhfOSD#*hOBQCo1y;QLpLG2P}ZzQC`?Co5x$FQYs>$12nQ N>n-c{UvJs+BmfJw|K$Jx delta 977 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

1l`Wr2-m@xj(j3Tcm+kP z7W8L$X6xnvg0hQ&nvSLzN}O@boNNVoC)+KG*?$1s_;N##%?XjOvSfn<7*Jybo7+xWhC)H9TCw$3pF) zaA%F&|Hm09YcQMd_vL?}$o|ONm#;Rvk(`8+z9InSu=jKt(***Ejs&n3uHyyd?D_38 z_(=2zq)HWWk4N}5f!3U*0^A9&BxAL@GVnKR@=ks5p(}g!?wi%8PoKWM``*|mGc%q4 zNNC$Wy=!8R($eYEdi+8h`f0WQ!}wneTl;JMQ?*}?hwfbC&kW7~i@)EmdRkYnU)M47 zrr+>IDESBUu1V>?A-9d+_Di4EJ8S%1^Aw03xu->S=vD%Z5_R?dj1C6E0- Dda|Yz delta 8722 zcmai(dwdnuwSd>moIrR8Bt{4jW#)hoBuEkt5EW~p$U~zhh!8YLfM_E`!5Vo;^gu;R z6(uOwgMtN=R+LCUkqJr#yl_i@f`ltptU(Ab0eL?xVwC&MIeRk0t^Ueie0x3i+H0@9 z_MVfPSKHLS+NL5!3(dK*qgFmW=b0SsgB*R^w(yxA0WDSq6ptI==Lv_jkZU;UyU4j&e&z9T5zlzctB z6m}P^9T6FLr^h$L)j1~7#sdBlF3k-JBhdsUe@B9R`orPsTTD5t^v}WP(GSG*2TSjx zB3t^KlFERSfiuTM&cXpL#-+zG7+^|@;_YzKAX7}mQ%{KOBjC|0fVGiRU+7i&OK@9w zrz!Pn-k(s*&c%)57fy=YeOFMLDzk@AiPXUEx(}QdNzV&P6D7}&ocf2p&ANl(RfA0# zsN8Hi!~W)*QmCT2K2rCkK0BxOkVyRyQ)<g`ClXlUa#zi!-0HnH|N8PV0Tr!;1xdZ zaGqqdN>@G#uJGi8(p!OnR(tZC9} zZ%{%}4oJ4Wa+kh4F8u8U&cP!jKn4CfzIQxeN}2L=0Pa*`Mgw!^?O|vV^6?b7AKYB& zZ-VoDoIv>$!hMzeK6utcrc}l_EE^e^heEDWXc4E$et4oy@R?2Ug6^(;jFQy zOi=oR8fj87I4JS3v+yI0H7O}&=TxD*#nx61aWj3lF4s5Fr1?FbK{>nw1=m4kQ%#=5 zVH4%B+2xuHK|WOR9q?K>S@G;h-G2Qp&9*ZRIOqB~;whhOO0r7uJ(p?HcbX~bs5=S$ z2yPBL(>dIsg(gX`sdy$_J<*JAoFiWY7r?Ik5FGN^4{mvh|FBdu;GZC+)uj_W1JCeH zup0JHZ~zYZCb%m>letq&*{K4Y4)1u1%G6>DSO@1$H6<@94{LIcfujuUQ3l-;H7R|< zsJo-OEF;xz;X%l?$h(L2F$wM4 zX=3`0*NBL9ggx3GdGv@rQX6S+Kce5Hhu8kZA+4ZPjMz&0{47#Di`u0}I;sDd$|#s& zN~w|`qcS=nH&q}7%_!aHJ>CFMfSohv*q??&u)Eb(bIy)uAC_4R1iS$`2lw?k=uEAo z`5f$Q%V8_@oKjl zmVMzA^qrDuLtAbRyWJp-ZgtYL56XB3a_?=3Osr0tyoZ9*Pn`@G+lx=><2Aj>6?Wno z{pUDss-5tI-ZDO;h~t#I*t^NTfafmqPED{YTYI19cu6E*GZt#a_9@Nipod@XqRII; zOzEfEW^2sX!lnwmb2r>!?q;+Ep5b%%4&15GbAJ?`g}uAbP?{#Su$zb#aNj?hQe;FE zlPImaU%G{Z?Zp9OUGt3LA^PDcuiegvQ%8Ff<@MI22D#gwU$W;Uu&!`nIv3Fz?;`sP zTnZlQzo2b0KPWhS^qSk*9mJ%_w7wq97s`6R`FRJqjy?tNm zf_anGJR{&5pL{8t>XRRVWp9IhSbE*a%gv`S2~J0$QU#Fq8%`>6H^Jj!dB|JndvGoK zNh-ko@X381x4wx>=YYop;pBs+EQ$ILYw{%nYZ-8_u?Mqp02e8T=ir$H&{nN5D@T*X zuzLch!v260ZoZV4{y)Cy7N0yoiG5|~cQslXIZbnUN*y-kq?$PG4oc+^C95)V@lH+{ zT&_5D7mm=+RXirLIL)X^FsabcST8}R?NQy0kw$nJTUgGxo5r_AOMN+7G7UPMR_;7G9==?xxgzx7H+h1_o)$ z;6vDFAV1lUW^m%eXaP7w&Ndb9CJIy;r<>`I6xbb;{R_w`dt^^zx-n`lS~GlJzJLdw^FF;2hjJr)Z^{gHkmkcF zzN>Q=T#Ee_cy=1yK%myB?Fbew0pT&T3J%6xa58=mPcHDJyYIhUPH1d*yw`?)C}SazLB+ zvt+YM_-K3aB%_C}ZLqgbF}4MA+WwY=&#@ZHqztZj$!aK*({M?J6}^C*#NJxM!l!z1 z{=u$$${499{GIXDjBj@1xAUhPyjwQe8)h1_<3}|c%LZ+>L{!42K1`w1S`Gaq{t@J# zT5?qF%*6>bAYWl*Kgw6p?}HMj{7;9|zqT5_7yf9koMViP3y;Ci8uVChG>?CQt6|(m z`7r#EkJHA}*L?cp;kmHesaL|Qee$#LI-h+X;{4d-usp#)mCs-;yunkD?Z4CH6CaN# z9VsW+9~VxE!MIQVz6(@zgJ+a(UoXFF1 zmS;Cd2u43ho%7YWT$9W>EQH7%9t010iLWBX&%--j2ugvPcPpIs zVo+)nw|tUw2Oo^->ymG8c-vT~=e$UvCs{#Z+oCNdbsPt9t{2!UI0bH}9Nk!|$=$H~ z1bNBcP-*a!V4|%pH^Pmy+wJMAj4DHW)y}9ksx+;sUAM+K5vOHFwy)z!yTE>Qy>U?s zhxjS<Fa^KxR>hoiqAuJ06# zUZW1T*&{OKWzL(@e*rH2lNrqi^01t(6KS=>5|c*c7+k-X-(o+F{U+q_LO6A+6}`tC z-T_bB8hiUY{K`HiSVwEtWA=ZA%eGsxf;>8MW4}n&JX6lA021J%on!Z6qVPhwnSs!4 zOsE`8gUgAylQO7=v(CoezK%nimOn5j_POYA>u+IShW-RESZc};6+rM1zs4=}^o!sL zyJDR&F^q$E8K~T3MIYOa!`{dE(G!Px1mL869K7fqQ&y;e%Fl@ubqPlA1V_K)1%6J1 z2P%0U-02%Hle}AX2}e-!u{|nw#ZHK2l$+aJ#M30@Rr75u8Y|x@U?rO5ohx=GN4H~s_D0*TG&(lGPSI0{7i-$s#3hn}qyx zEBe-R{N=&5$mdai(FvYopp0f%rVKX0OD4sBrg98&ui*RUve>D1_*po8XY3Yo_%k?U z6M(XB*_y-Z~AOv!c!x%qf_s_~ZlOTuwz1a%aB3!SSi#pycA$ z88ABYB|?!n4`zo)!@2C)MwP->;PkJoXbg^gH(X2&Y*G51yYRCRHR8nS$cx~LgxHI~ z;e%We{tOMgh75&$DSC&2!Z%`{0FJ>PICLdNq9&Nzoi7vuEL6M;-boRaD$c%^nSC|1 z2u`6hW-0j(@CrUL`YP_xgY%d5Pw(80VIX!aVk=kx&;8Df-epce^{{^hyFwhp&-vI> zMfqeOO&*J|1;mjcx@?90t-CafjL_Omm7(ofoWE7H!U-y(%8?wwIFv`D`oWB@&mrdX<|+5Z!C zgePBV$sRW#xEg);SY{Sue-#;2@-gsCc&6gs4{)q47UX2ux&Pl`V13Yw_yQc?#frYI zofUO^kUdWh$`-Za2jDE&o!|{Pn?{qTCfEztx8N!$Z4c@{F z$ldeeNqi&*f>NQ1_{JyrHG$UbcEVbCDH*FYl!G1isIA6;Z<6}-?9(f=XL`>**Y(Q0 zuFrKD8NGW+WWzs(XEF@D*p#^T!j+TvKe6H~j;uPQ`Z zEf1u&PybbU%t1=1&wtd&`VSiILf8G8hCO~&p#Qu`#eeGodu?vqWKH|r?saF}{HOj8 Dyh*Ih diff --git a/artifacts/program_methods/pinata.bin b/artifacts/program_methods/pinata.bin index 278e88f42488f973152721dbe0151153b495d13b..50d9648d13cc1dfb084fd2cc8c3ae1bfc64065f7 100644 GIT binary patch delta 8791 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 V$HiH7qvK|3T2JfIytr59{4X3%xW@nh delta 8794 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 diff --git a/artifacts/program_methods/pinata_token.bin b/artifacts/program_methods/pinata_token.bin index 4a4c8bb602d1fc1b06820338faf8b39b4124316a..57c73fbc0c3fbba749cff71aa664929e96bc9aef 100644 GIT binary patch delta 9817 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 delta 9807 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 V$0=Z?YjC|m4yhY1FvC?Nfgfu2NNoTB delta 1269 zcmaiz!Ak-`6voxux89<(qTTYSLS(bM?hX=3;G^ zqLUp8<|tiLgy`SskVN-Vx-~PMfA5>`?f1Rybi7ZdF)3R-3 znMqs2NJm&BuNM$i4LhF8?dId$0jzTe=ye*DG2YuTg|8&F6)to4CK&8gJOX}Hq$Q%N_Fu<35+|bS8h>Eq5oT!n7+OB=_Ns33_0MlGPQOl|#15@; z3XRdfWsv;2JVBPuV3P%#^U~@h-%_i=8gtnjSIP=>saq){z}W?%deGx+om2mSZVBmw zkbcw!7ouW8gMDiii;pCxh;L+$ndi@M9wf~+4AaCVe99p$k+8678n$KGD2Y%K8y?cz amLkgUdW->DxB}N3WRObS1sZqdaNrv*k4cCC diff --git a/artifacts/program_methods/token.bin b/artifacts/program_methods/token.bin index 5f6d3781756d1ac7fe3fe94d07cb875970b8bd19..3475c76764725b0e54f043c7af61482a5b95ac56 100644 GIT binary patch delta 9600 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 delta 9537 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 K)z_h?@A(%Fuum@l diff --git a/artifacts/test_program_methods/burner.bin b/artifacts/test_program_methods/burner.bin index 96e339c2a998e1eedde88178b78416daa0ac5baa..f21a5499b9f36938b8e9d12ac924273121052802 100644 GIT binary patch delta 478 zcmaFRApW31yrG4$g{g&k3yaQt5kpH;V@nGQOG6`b17jmILrWu5hUtYTnAN0=^>T8` za`mt%nBFj-<&-cMiS4lqSTY%Ln>lR}3kNoR(*>`w7*F?G#3GJc@AUaV&A6qd4UKTS z7i9c){lzRln6MkL@ieo#ByQ~v%ULv}a7sghOWM!?r;6!d1I+Qt>thWUutrPV8X4tT QW!fJsXW9N>IctLi0Qg9Hg#Z8m delta 478 zcmaFRApW31yrG4$g{g&k3yaQt5i=80b0Y%_BST{g6H8MdFtlWtUU-68P0Cm=C#Nh| z4~v574f9z}31gAi9=m`glM%O>(-yICVAD5U@EVKpbiYL`;<)urpAXcGTUy%C2)BDd z#&6eO%<_W?y8#FEiAHEgiQ@C%*;(K42;bU4J{2V42>-fr#IeaQI|5+%gHIr z)x#!Vc!lK@HjUf;uCin@3SnxQevqHlWcuH0EDqSTf$ftvvBYg5#6iZm6hMq;zriAo z&G_wxH(2VJvB^L@YJ$sHXh4|ZmzOp)!mnWZ+B+;9IIW-l9-pkVDIUW&hOo-CXWV7k Ko^hA8K>`4E7l1tg delta 477 zcmaFyTKvUp@rD-07N!>FEiAHEgbj_13@i-I3@r?Sl!2jvrK#EU#@j6FQl@%2Ic2$e z*yIbZu$;oCal7ADmP|%$GSl;~vKUYQdyT~bt1QSuX%kD_Hi8wH8sk;~HlO_li#Rs( zw;SGIsbj__1M#W}ZgaswVTNB`+RzBUg6V7TuyEkCe)@ZSveKq_4Br^SD$|~Emt}j# IUDgH(0ROOn9RL6T diff --git a/artifacts/test_program_methods/changer_claimer.bin b/artifacts/test_program_methods/changer_claimer.bin index 692d152bcb8130a3dbb08caedf7e7a216e3a073f..024480a1790e7af67c5365fc04dee3da466cb86b 100644 GIT binary patch delta 8765 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 j#OR(kf72gxNhrf^vo5U2# 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 TwdN6CIfC%Mws54^hCluXu&<|P delta 8673 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 G$^QjbGMBsn diff --git a/artifacts/test_program_methods/data_changer.bin b/artifacts/test_program_methods/data_changer.bin index 86fde894fd910a0af2b203b71324b31aa906148c..2443b1d4df4b847cbc0b603b790d9f2db98a5fdb 100644 GIT binary patch delta 463 zcmdncE54yuyrG4$g{g&k3(JQkf=0#$mWF0#hUSJw7KWC_rsmTbm$DdO5q4V2at2d) zyZkbiY(`A+>4|q(jHjPl&f4|q(jHjPl&fFEiC%8MGTFM4J}PA%ni+fw4t$qiGc~j^o@s^)ufH}a&pRY z^$fA7nBFp*<+LysiS0>qSh5(g$xK(6!=fQ=fXB>>Dy+iOFEiC%8MT|_042>)ejm!)U4UEjqjm=Fh7^ZJL%&aDDte2Bh zmaAuoMaA@%*(|4pu}ExBn!}RCh)rg?!Wcp Wv4%~+L>8I$AB$PG|5(ggBLM(k7l01{ diff --git a/artifacts/test_program_methods/malicious_authorization_changer.bin b/artifacts/test_program_methods/malicious_authorization_changer.bin index 8f80ab581394fc23c3d96e9b5414b71c4cbf888c..e6882add9af5201377337d8eca9cf8874577df72 100644 GIT binary patch delta 488 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 S{iA)dgz)Gqp72@w`hNh&b+df{ delta 8678 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 QQ>1D0tyc%dEPeL>0eqXRWB>pF diff --git a/artifacts/test_program_methods/missing_output.bin b/artifacts/test_program_methods/missing_output.bin index 4994ae3f16f692b6e8e09af0fb958d2ad4d89f1d..873f61a87eeb4698c16f40703498f31db11a14d2 100644 GIT binary patch delta 8702 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 h?_V4tT1A(?u;U_J9jPt*i{~x$DwGjXS delta 8717 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 LOdb{c!V~`sBU88J diff --git a/artifacts/test_program_methods/nonce_changer.bin b/artifacts/test_program_methods/nonce_changer.bin index 017de8b3bb5eda6b393c4bb2d8cbbadcd6c2e5b6..6a11964735dad4073cc4c8229c6706743eb0084e 100644 GIT binary patch delta 8698 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 delta 8695 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 diff --git a/artifacts/test_program_methods/noop.bin b/artifacts/test_program_methods/noop.bin index 23195ef2ddb854f396062d1f0c0291a6b2fb8398..ecb1662df5f7343f9938161a6afe2d89b1530b4e 100644 GIT binary patch delta 8703 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 delta 8703 zcmai(e^^#kw#U~w=S9#^0nrpO&v~UpqXNXh!VrHnsH7l{*{BIg6BQ~Z{_65{#7v!r zQM{V3mWue;#A7s~qQ_gNqGVQPtNrp{Oq;&+H0@9 z*4lfYx8Z7!nyWnuL#49Sefr7QAITs7+Hh&laC!4#{kLz4TtnRyKtz%()0xvmVGf>D zOH?(?P2E_&9_&hVQ%~m8;Hl9n)q!PMqS&)UjgIk)z&9Pd7#$np-PF|@KY=RHa19N2 zSq&tAPZW#++<``b3&7J@{u=lK4Dx0E82mH1Gjm5{=MgzUlA4W{O8K^|hx|a4Jlsw9 zvwR9T9_(H4MsTfz&w~Ajs924)pqGBc{3G0i7Slny!8Fny-wO7BRHat#Uw9Gy;UJgQ ze*#sbL48c6a5M-Ht^A273G9+A_WcIz$axh)Mc$JQGw-l&}7^f1tnet

lLvP0_bukO9KePz_>Cxhno7B>=OB3Dbk#Im%-iuc(FzBr zf!BlmxqlIOn}aWb%fRt0A50R}rK?nGu}+)O(1;Gn+@S#+hYlsosggvYqwP#y19mz1 zYj8n^N;%xWAB?PrXZ^rp;a3!i_Rql1aiHuH0X@d+`81hE`AC$G2Je97;BW^U9V9vp zh29A4(N&@n7%+(^m<$d;e{ZRI)01&Z9xpv*>^vnBI}`S_&Qhr-J2<_gMCJ2Uie!6_ zf|r8D^uoadJ7GOwm3a*~a;9o-oRC+7tFmqRWpJru{It8;>DTE98k`gK_isNzA^0W~ zipUTXRDhimG=rTJ{He1<{y8euaQ^~u@;pQ)FYr@v1X zSiuY6uv}CgR!{;CMt@6zMBjl&&b9l$*$w*Ok$pV=B6!$X?~cl}H&~)8*pShXOP&X6 ziKaUEIJhC-_UtWiDcHN!5vVfz?fyD>(2!?05Q$iED2M`Y zb11mbU80C5?3HiFGUH!SDNC^o&bV8m!em?i5Aa0r0J3tU89cI0uFDjSC|%svAX6?l z#=(2Rp)hb56o^=f=_Sz?7!b$`7l8YM{h8kf7di~Q1^yOW^%Tn!LL@o}{po^T$krvE z!0+XA`ZQFfk*{MOUP#b2lxfH5mIlst@JG-SkN#fI_UnxpcgQorm9XP}p7(8Vxx=m> z!4t=|IZCFL#+>tVS4qFpN|Xsf2Ob!Eh3Ew^Le;D;i@=BMW0XDs=RqzE5!Gic46XW` z9lJg#%jMuu$b|!2z)QiA%pFjzj@#oA#ApZ~-)0#_s5qL@LDW+*!E8gTmuE{-u(A3F z`L>U=%vgO%?&=ryVi;EJu@AC3us_(FG!NX1-Al3c?(kG!mW!m>#++8UudHwGCsED@ zl}2*a)G#~-tg@X^{UOJ2uS4g8(;e#efGO2he+}%yc<(wBkn9t|UO#*QE`LjL5{(>#+*_SD8G$aPNYbB+km%$N`yAT| zt_KJ6g3g1tK#wz-o1dY4 zG!!}vJpwM+ZEr}xLlUj92T&S#H2RO^3ElvI5mPEA1YNU zV84Al3N`Etl^U6SA4f5sYWra{ct6;i5Pt>dIrwvM$qAKC^7zhwz;WjoKOXE4?q|6n zh*qN^1r1)uf91&tR+>^@879&CQ*JZBB-w{5!E38gL3sTw;5vuiL5au@aBoX5%>y69 zc(@07jOc?RodV@t6dM+=W!DH>!IpU6sV{@Nw%om700t*wNUs?+~)=iB4mYa**dS zCxA=9$;`7nT3=;H(EW(m;OVx5ZjCk0_f?)&^k+sQWgzf+@@;U<1aC;l)BrAY4BVQG zd&qhFp3(p=2YYK=`V$BZM@U@&Cu4jUcGP|2P%|&8wAivQni9sNgX7d%0?x#M2p;ee z*fD_ojFx^#`Hbbl5kKMBw-nKgF}Nl);2y({IsmT2M3pQbnA6I=Q;6Bu-(o)Wqzk|yhhhqac8RNV2dIqGy!43z_ zF)9WqsPRWoV`^LMH^VhJsvI27_K(NBNtbP2>)H9RaxU=J2$TSqM#yxtME5d^W0ixx z`qJ}w8U|<-$HP}ZeX^g{R>I0K&cDXH&ptF00TOx_eys)oCBI%t%{ z`eG-c68mbD%{=!{I2vYn`^$r<91Z1Y@TTOvDM&f{kS7B?8S*|*AkOsbQ;eWt$}4_> z?<2=@duinFvQ{+DC{9vV`AA_#)Hvl_@4z8=?Jj*pqhsvwnTGa)@{lYYH&U~e6RyOt z8OWaHT3aCKV86d;ZGmtLT(?X!&lqv2rDQ^1j&10F8yRzy>2lynw68<^F*b0XQ9W0g z?JHFoiT|an^2_M)XDsl5M#LUl_B@^xUutbnif%7JUZGJV?@Xte@Q@=g#)5Cpcasn6 zF94TRYi%!rwMNuZWx9_(3wkyza1)kmI%+3)Js4_DZUw*P;Mf-tkdFQ@gLA>&TDl*+ z&LO`IE^>??o-NU4n{|2(4XYdjJ_Z-t9jJDeL|Yx4m4ibb48NELZU?V%a3gp%*t?(y z=OFVO zcV#NL;3jsP`Q5n^Em~|({!j2ou$zsI%$4YKu-ChD!C!#Ai`ff4?vURASKIR86h9AV zr=x=o&I5b%;4ruV`~XkrGan0c$lo?n*D14o^bIdbl$~#nTe1KX-{o%m^i%~t{F>W* zFpB&MS}0M_GAsm^3!Vs$S&nB9^Ez<(5;sled27J_uezy1aAj#Iii^dmEd#W>qo5Hz2M?=m|%})My%jt z;Kq+NDuW*dcd3K^MCe0A3r+@?{cK%Ug9vw!vFC|4Vg;fEiw@ht;f@tYUmyxDQfVS9 z+za+wXZLRfH+*FkIx#-69v<4SnYUxXIpDIDDqUy&)eZRi3jHbIKpiga@Dn}_6suIm z9qz&Rz?wd8v*rs0AL5GO6&(7q zO65F((g8_|m`G=i2XCy=s3-67B5?U>jkd5nECBLPH1jqm^iKg#JfqQi#Gg6g8)#^( z)yyxGqC*up6d%@VSizHlc(Eu}Rtx!RaLE_eAtd-)aOrz6fcu98;f3JfY2Z8uZv_X4 zGK=|51AMySYF?nxM&@V1Q@7h$FfbVRgm+ci@3Q*q;4H|!F>?d#Gu?dv3&CX$ZUQ^&#DsgWZGW}}>K@ep#j~&=PhTa9Q-DBNNg#oVn@ww-Mb@dhe2v{$x zvh#Qr8mzRiJn#XEt(h9Hz@IWB$9W`KQ>0S^6 zj~rIbvsLJ;jY0lJU{7**1w#kJ!XwuEQ*^ipJ_#0uMDQ9snCmO>;e@&K2$WaN-qR6a zwFzIAcniiQVG>6SXXK-k{Ck>o@S(n8$w!UPzkxOPVmi5ATP49Z}4ZDqRKdf*mxC)%h-03M?^k6`YR}Nk)A!vC)hrrE>=Jl{n z4QS{Yq>;)4lJKCp4ws6;B=Y(SxWrd8UsS??qj;Aj55@WJvOF|(8h+CW2V;PcR|tl_ zc-GhPY23@fnaru+8t`P3vHu&;5ZTQ>J}-c)G&`t9O~(rga&IME18xS_vBIO^NNk}u zEr+IKe6W3kngyor8m-{|i5W=iyRjhQVI3ClK|@?G+hU(*P?`E@=G|JX=wWbO7dIW} z73YA=6Ym|qt3t|E(3w2@(Jl5Ib*TE~w>_SurZgp_X^Ae@+wB_sYSP1*ttgP1g z{~H<_PHAoD`~ErD^98v3;?X7wiTo03`B@mi6OIS_UxESn!BQA>8a(nI>%+C+*>iDM z{uNg+4&tQw_?Up>JC3*DB3MOfE>&2;IivcZGU0T$=pln5qvN8642g}5jf;tn8Zs!_ zvwEYezvt;UUB167UG%2w)87*qPrl`PUiv+Wsj&Cp$Op5t=g)lb_h$V5D_1v9m(4EK zGiHy4C0eexvfJLB_spSNE5KO$SS>i_@% diff --git a/artifacts/test_program_methods/program_owner_changer.bin b/artifacts/test_program_methods/program_owner_changer.bin index db1f87fad394c77e82426eb258f53c6782651e0c..9d3364258f56e93655bbbbf267c7b32cb30393f0 100644 GIT binary patch delta 463 zcmdnFEiAug37eUlTUuBcn;Kb~8=IJ#m>L)xPB)y#qAqQ$my=VL zt7nK!f&Xll)7W%w*PX+X#fVL2y5AfYFEiAug2^$z#T3Q;JSz4Nyni-o|npqfIOgEgzqAqQ$my=VL zt7nK!f&Xll)7W%w*PX+X#fVL2y5AfY566*a$o&N?~A^Wpi6?_PVo?6uck zd!L)~SKF4n+O{-WI=ifWC#mQBq~9e;e@>Doe5$uPMpQJ=M=gm+l4Ys`uW|9T6GUl) zebkEO{|hbxM>1amkL{yUEm)Q%NDnrJ2lnz6y* z;N{K)Dg}qaz&Ms40#8B!jhS1YA({$q&OFM<-zN`{q(_Y{`{moRKII#tvJ@ZnVEKA* zG1y-41#rv|hr54ERMB6>YODqA1|NkSBW8l=JW=Ws&io{BMWRaAc>W3SQN-i1=8vEb z7l^jGMg@KKz} z9_no*eIcLn>PLRUIhlq8aNs4kiK-{6RKRu)gQK5S&A^1;4kS@e7w3Qzz@a>U8#u+q zBuNwiC$RhpaM5IyHd?IHRt%J4LMl(V1wIa5&pa7UbA~$yc|ADI#ovMbQ&h_1`SA*P zn2$ohV&P4_5@kMzo#R6J1e}^?XL_VeV;V`c5d*eDIk>>Z)r}?U0)ut|_Gl^5H3Tq< z9Xt;ni}`k^*&NJ2DG!&{8GBEPb3F_36uhWXBq#XArV_=?Rmsos9s_&8!o5iFfMybf zf>q}A;EESibK`{kOK=+4mWPC5LtOeZz%Khb9mRm#K|=F89jpb@#ZjYJ=%`)CN{Qt;6;@}%%b+Df#?)l}vNx15oO z%a>aQ<+Jj5X_%39RvsYh8-76#UW{5%ti9-R6PLzoC|-}nCs7({^Hs`ad2_UhXvkHL zq!4_(*x}RQ9I!ZaV(%iFAeCTyuam$Dj=oMCF;MR^&_)!C%fKLTXP1E+_eoSY(pma$ ztaE3PN;w?hOQ^blOa6CoB)A&_5{JWkzeFJoa$Tmu7)Wjykm)t>4R>zA

~=S2QFF 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 for AccountId { + fn from(value: nssa_core::account::AccountId) -> Self { + Self { + value: value.into_value(), + } + } +} + +impl From for nssa_core::account::AccountId { + fn from(value: AccountId) -> Self { + let AccountId { value } = value; + nssa_core::account::AccountId::new(value) + } +} + +impl From for Account { + fn from(value: nssa_core::account::Account) -> Self { + let nssa_core::account::Account { + program_owner, + balance, + data, + nonce, + } = value; + + Self { + program_owner, + balance, + data: data.into(), + nonce, + } + } +} + +impl TryFrom for nssa_core::account::Account { + type Error = nssa_core::account::data::DataTooBigError; + + fn try_from(value: Account) -> Result { + let Account { + program_owner, + balance, + data, + nonce, + } = value; + + Ok(nssa_core::account::Account { + program_owner, + balance, + data: data.try_into()?, + nonce, + }) + } +} + +impl From for Data { + fn from(value: nssa_core::account::Data) -> Self { + Self(value.into_inner()) + } +} + +impl TryFrom for nssa_core::account::Data { + type Error = nssa_core::account::data::DataTooBigError; + + fn try_from(value: Data) -> Result { + nssa_core::account::Data::try_from(value.0) + } +} + +// ============================================================================ +// Commitment and Nullifier conversions +// ============================================================================ + +impl From for Commitment { + fn from(value: nssa_core::Commitment) -> Self { + Self(value.to_byte_array()) + } +} + +impl From for nssa_core::Commitment { + fn from(value: Commitment) -> Self { + nssa_core::Commitment::from_byte_array(value.0) + } +} + +impl From for Nullifier { + fn from(value: nssa_core::Nullifier) -> Self { + Self(value.to_byte_array()) + } +} + +impl From for nssa_core::Nullifier { + fn from(value: Nullifier) -> Self { + nssa_core::Nullifier::from_byte_array(value.0) + } +} + +impl From for CommitmentSetDigest { + fn from(value: nssa_core::CommitmentSetDigest) -> Self { + Self(value) + } +} + +impl From for nssa_core::CommitmentSetDigest { + fn from(value: CommitmentSetDigest) -> Self { + value.0 + } +} + +// ============================================================================ +// Encryption-related conversions +// ============================================================================ + +impl From for Ciphertext { + fn from(value: nssa_core::encryption::Ciphertext) -> Self { + Self(value.into_inner()) + } +} + +impl From for nssa_core::encryption::Ciphertext { + fn from(value: Ciphertext) -> Self { + nssa_core::encryption::Ciphertext::from_inner(value.0) + } +} + +impl From for EphemeralPublicKey { + fn from(value: nssa_core::encryption::EphemeralPublicKey) -> Self { + Self(value.0) + } +} + +impl From for nssa_core::encryption::EphemeralPublicKey { + fn from(value: EphemeralPublicKey) -> Self { + nssa_core::encryption::shared_key_derivation::Secp256k1Point(value.0) + } +} + +// ============================================================================ +// Signature and PublicKey conversions +// ============================================================================ + +impl From for Signature { + fn from(value: nssa::Signature) -> Self { + let nssa::Signature { value } = value; + Self(value) + } +} + +impl From for nssa::Signature { + fn from(value: Signature) -> Self { + let Signature(sig_value) = value; + nssa::Signature { value: sig_value } + } +} + +impl From for PublicKey { + fn from(value: nssa::PublicKey) -> Self { + Self(*value.value()) + } +} + +impl TryFrom for nssa::PublicKey { + type Error = nssa::error::NssaError; + + fn try_from(value: PublicKey) -> Result { + nssa::PublicKey::try_new(value.0) + } +} + +// ============================================================================ +// Proof conversions +// ============================================================================ + +impl From for Proof { + fn from(value: nssa::privacy_preserving_transaction::circuit::Proof) -> Self { + Self(value.into_inner()) + } +} + +impl From for nssa::privacy_preserving_transaction::circuit::Proof { + fn from(value: Proof) -> Self { + nssa::privacy_preserving_transaction::circuit::Proof::from_inner(value.0) + } +} + +// ============================================================================ +// EncryptedAccountData conversions +// ============================================================================ + +impl From + for EncryptedAccountData +{ + fn from(value: nssa::privacy_preserving_transaction::message::EncryptedAccountData) -> Self { + Self { + ciphertext: value.ciphertext.into(), + epk: value.epk.into(), + view_tag: value.view_tag, + } + } +} + +impl From + for nssa::privacy_preserving_transaction::message::EncryptedAccountData +{ + fn from(value: EncryptedAccountData) -> Self { + Self { + ciphertext: value.ciphertext.into(), + epk: value.epk.into(), + view_tag: value.view_tag, + } + } +} + +// ============================================================================ +// Transaction Message conversions +// ============================================================================ + +impl From for PublicMessage { + fn from(value: nssa::public_transaction::Message) -> Self { + let nssa::public_transaction::Message { + program_id, + account_ids, + nonces, + instruction_data, + } = value; + Self { + program_id, + account_ids: account_ids.into_iter().map(Into::into).collect(), + nonces, + instruction_data, + } + } +} + +impl From for nssa::public_transaction::Message { + fn from(value: PublicMessage) -> Self { + let PublicMessage { + program_id, + account_ids, + nonces, + instruction_data, + } = value; + Self::new_preserialized( + program_id, + account_ids.into_iter().map(Into::into).collect(), + nonces, + instruction_data, + ) + } +} + +impl From for PrivacyPreservingMessage { + fn from(value: nssa::privacy_preserving_transaction::message::Message) -> Self { + let nssa::privacy_preserving_transaction::message::Message { + public_account_ids, + nonces, + public_post_states, + encrypted_private_post_states, + new_commitments, + new_nullifiers, + } = value; + Self { + public_account_ids: public_account_ids.into_iter().map(Into::into).collect(), + nonces, + public_post_states: public_post_states.into_iter().map(Into::into).collect(), + encrypted_private_post_states: encrypted_private_post_states + .into_iter() + .map(Into::into) + .collect(), + new_commitments: new_commitments.into_iter().map(Into::into).collect(), + new_nullifiers: new_nullifiers + .into_iter() + .map(|(n, d)| (n.into(), d.into())) + .collect(), + } + } +} + +impl TryFrom for nssa::privacy_preserving_transaction::message::Message { + type Error = nssa_core::account::data::DataTooBigError; + + fn try_from(value: PrivacyPreservingMessage) -> Result { + let PrivacyPreservingMessage { + public_account_ids, + nonces, + public_post_states, + encrypted_private_post_states, + new_commitments, + new_nullifiers, + } = value; + Ok(Self { + public_account_ids: public_account_ids.into_iter().map(Into::into).collect(), + nonces, + public_post_states: public_post_states + .into_iter() + .map(TryInto::try_into) + .collect::, _>>()?, + encrypted_private_post_states: encrypted_private_post_states + .into_iter() + .map(Into::into) + .collect(), + new_commitments: new_commitments.into_iter().map(Into::into).collect(), + new_nullifiers: new_nullifiers + .into_iter() + .map(|(n, d)| (n.into(), d.into())) + .collect(), + }) + } +} + +impl From for ProgramDeploymentMessage { + fn from(value: nssa::program_deployment_transaction::Message) -> Self { + Self { + bytecode: value.into_bytecode(), + } + } +} + +impl From for nssa::program_deployment_transaction::Message { + fn from(value: ProgramDeploymentMessage) -> Self { + let ProgramDeploymentMessage { bytecode } = value; + Self::new(bytecode) + } +} + +// ============================================================================ +// WitnessSet conversions +// ============================================================================ + +impl TryFrom for WitnessSet { + type Error = (); + + fn try_from(_value: nssa::public_transaction::WitnessSet) -> Result { + // Public transaction witness sets don't have proofs, so we can't convert them directly + Err(()) + } +} + +impl From for WitnessSet { + fn from(value: nssa::privacy_preserving_transaction::witness_set::WitnessSet) -> Self { + let (sigs_and_pks, proof) = value.into_raw_parts(); + Self { + signatures_and_public_keys: sigs_and_pks + .into_iter() + .map(|(sig, pk)| (sig.into(), pk.into())) + .collect(), + proof: proof.into(), + } + } +} + +impl TryFrom for nssa::privacy_preserving_transaction::witness_set::WitnessSet { + type Error = nssa::error::NssaError; + + fn try_from(value: WitnessSet) -> Result { + let WitnessSet { + signatures_and_public_keys, + proof, + } = value; + let signatures_and_public_keys = signatures_and_public_keys + .into_iter() + .map(|(sig, pk)| Ok((sig.into(), pk.try_into()?))) + .collect::, Self::Error>>()?; + + Ok(Self::from_raw_parts( + signatures_and_public_keys, + proof.into(), + )) + } +} + +// ============================================================================ +// Transaction conversions +// ============================================================================ + +impl From for PublicTransaction { + fn from(value: nssa::PublicTransaction) -> Self { + Self { + message: value.message().clone().into(), + witness_set: WitnessSet { + signatures_and_public_keys: value + .witness_set() + .signatures_and_public_keys() + .iter() + .map(|(sig, pk)| (sig.clone().into(), pk.clone().into())) + .collect(), + proof: Proof(vec![]), // Public transactions don't have proofs + }, + } + } +} + +impl TryFrom for nssa::PublicTransaction { + type Error = nssa::error::NssaError; + + fn try_from(value: PublicTransaction) -> Result { + let PublicTransaction { + message, + witness_set, + } = value; + let WitnessSet { + signatures_and_public_keys, + proof: _, + } = witness_set; + Ok(Self::new( + message.into(), + nssa::public_transaction::WitnessSet::from_raw_parts( + signatures_and_public_keys + .into_iter() + .map(|(sig, pk)| Ok((sig.into(), pk.try_into()?))) + .collect::, Self::Error>>()?, + ), + )) + } +} + +impl From for PrivacyPreservingTransaction { + fn from(value: nssa::PrivacyPreservingTransaction) -> Self { + Self { + message: value.message().clone().into(), + witness_set: value.witness_set().clone().into(), + } + } +} + +impl TryFrom for nssa::PrivacyPreservingTransaction { + type Error = nssa::error::NssaError; + + fn try_from(value: PrivacyPreservingTransaction) -> Result { + let PrivacyPreservingTransaction { + message, + witness_set, + } = value; + Ok(Self::new( + message.try_into().map_err(|_| { + nssa::error::NssaError::InvalidInput("Data too big error".to_string()) + })?, + witness_set.try_into()?, + )) + } +} + +impl From for ProgramDeploymentTransaction { + fn from(value: nssa::ProgramDeploymentTransaction) -> Self { + Self { + message: value.into_message().into(), + } + } +} + +impl From for nssa::ProgramDeploymentTransaction { + fn from(value: ProgramDeploymentTransaction) -> Self { + let ProgramDeploymentTransaction { message } = value; + Self::new(message.into()) + } +} + +impl From for Transaction { + fn from(value: common::transaction::NSSATransaction) -> Self { + match value { + common::transaction::NSSATransaction::Public(tx) => Transaction::Public(tx.into()), + common::transaction::NSSATransaction::PrivacyPreserving(tx) => { + Transaction::PrivacyPreserving(tx.into()) + } + common::transaction::NSSATransaction::ProgramDeployment(tx) => { + Transaction::ProgramDeployment(tx.into()) + } + } + } +} + +impl TryFrom for common::transaction::NSSATransaction { + type Error = nssa::error::NssaError; + + fn try_from(value: Transaction) -> Result { + match value { + Transaction::Public(tx) => { + Ok(common::transaction::NSSATransaction::Public(tx.try_into()?)) + } + Transaction::PrivacyPreserving(tx) => Ok( + common::transaction::NSSATransaction::PrivacyPreserving(tx.try_into()?), + ), + Transaction::ProgramDeployment(tx) => Ok( + common::transaction::NSSATransaction::ProgramDeployment(tx.into()), + ), + } + } +} + +// ============================================================================ +// Block conversions +// ============================================================================ + +impl From for BlockHeader { + fn from(value: common::block::BlockHeader) -> Self { + let common::block::BlockHeader { + block_id, + prev_block_hash, + hash, + timestamp, + signature, + } = value; + Self { + block_id, + prev_block_hash: Hash(prev_block_hash), + hash: Hash(hash), + timestamp, + signature: signature.into(), + } + } +} + +impl TryFrom for common::block::BlockHeader { + type Error = nssa::error::NssaError; + + fn try_from(value: BlockHeader) -> Result { + let BlockHeader { + block_id, + prev_block_hash, + hash, + timestamp, + signature, + } = value; + Ok(Self { + block_id, + prev_block_hash: prev_block_hash.0, + hash: hash.0, + timestamp, + signature: signature.into(), + }) + } +} + +impl TryFrom for BlockBody { + type Error = std::io::Error; + + fn try_from(value: common::block::BlockBody) -> Result { + // Note: EncodedTransaction doesn't have a direct conversion to NSSATransaction + // This conversion will decode and re-encode the transactions + use borsh::BorshDeserialize as _; + + let common::block::BlockBody { transactions } = value; + + let transactions = transactions + .into_iter() + .map(|encoded_tx| match encoded_tx.tx_kind { + common::transaction::TxKind::Public => { + nssa::PublicTransaction::try_from_slice(&encoded_tx.encoded_transaction_data) + .map(|tx| Transaction::Public(tx.into())) + } + common::transaction::TxKind::PrivacyPreserving => { + nssa::PrivacyPreservingTransaction::try_from_slice( + &encoded_tx.encoded_transaction_data, + ) + .map(|tx| Transaction::PrivacyPreserving(tx.into())) + } + common::transaction::TxKind::ProgramDeployment => { + nssa::ProgramDeploymentTransaction::try_from_slice( + &encoded_tx.encoded_transaction_data, + ) + .map(|tx| Transaction::ProgramDeployment(tx.into())) + } + }) + .collect::, _>>()?; + + Ok(Self { transactions }) + } +} + +impl TryFrom for common::block::BlockBody { + type Error = nssa::error::NssaError; + + fn try_from(value: BlockBody) -> Result { + let BlockBody { transactions } = value; + + let transactions = transactions + .into_iter() + .map(|tx| { + let nssa_tx: common::transaction::NSSATransaction = tx.try_into()?; + Ok::<_, nssa::error::NssaError>(nssa_tx.into()) + }) + .collect::, _>>()?; + + Ok(Self { transactions }) + } +} + +impl TryFrom for Block { + type Error = std::io::Error; + + fn try_from(value: common::block::Block) -> Result { + let common::block::Block { + header, + body, + bedrock_status, + } = value; + + Ok(Self { + header: header.into(), + body: body.try_into()?, + bedrock_status: bedrock_status.into(), + }) + } +} + +impl TryFrom for common::block::Block { + type Error = nssa::error::NssaError; + + fn try_from(value: Block) -> Result { + let Block { + header, + body, + bedrock_status, + } = value; + + Ok(Self { + header: header.try_into()?, + body: body.try_into()?, + bedrock_status: bedrock_status.into(), + }) + } +} + +impl From for BedrockStatus { + fn from(value: common::block::BedrockStatus) -> Self { + match value { + common::block::BedrockStatus::Pending => Self::Pending, + common::block::BedrockStatus::Safe => Self::Safe, + common::block::BedrockStatus::Finalized => Self::Finalized, + } + } +} + +impl From for common::block::BedrockStatus { + fn from(value: BedrockStatus) -> Self { + match value { + BedrockStatus::Pending => Self::Pending, + BedrockStatus::Safe => Self::Safe, + BedrockStatus::Finalized => Self::Finalized, + } + } +} diff --git a/indexer_service/protocol/src/lib.rs b/indexer_service/protocol/src/lib.rs new file mode 100644 index 00000000..8189f7d8 --- /dev/null +++ b/indexer_service/protocol/src/lib.rs @@ -0,0 +1,230 @@ +//! This crate defines the protocol types used by the indexer service. +//! +//! Currently it mostly mimics types from `nssa_core`, but it's important to have a separate crate +//! to define a stable interface for the indexer service RPCs which evolves in its own way. + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "convert")] +mod convert; + +pub type Nonce = u128; + +pub type ProgramId = [u32; 8]; + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct AccountId { + #[serde(with = "base64::arr")] + #[schemars(with = "String", description = "base64-encoded account ID")] + pub value: [u8; 32], +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Account { + pub program_owner: ProgramId, + pub balance: u128, + pub data: Data, + pub nonce: Nonce, +} + +pub type BlockId = u64; +pub type TimeStamp = u64; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Block { + pub header: BlockHeader, + pub body: BlockBody, + pub bedrock_status: BedrockStatus, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct BlockHeader { + pub block_id: BlockId, + pub prev_block_hash: Hash, + pub hash: Hash, + pub timestamp: TimeStamp, + pub signature: Signature, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Signature( + #[serde(with = "base64::arr")] + #[schemars(with = "String", description = "base64-encoded signature")] + pub [u8; 64], +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct BlockBody { + pub transactions: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub enum Transaction { + Public(PublicTransaction), + PrivacyPreserving(PrivacyPreservingTransaction), + ProgramDeployment(ProgramDeploymentTransaction), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct PublicTransaction { + pub message: PublicMessage, + pub witness_set: WitnessSet, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct PrivacyPreservingTransaction { + pub message: PrivacyPreservingMessage, + pub witness_set: WitnessSet, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct PublicMessage { + pub program_id: ProgramId, + pub account_ids: Vec, + pub nonces: Vec, + pub instruction_data: InstructionData, +} + +pub type InstructionData = Vec; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct PrivacyPreservingMessage { + pub public_account_ids: Vec, + pub nonces: Vec, + pub public_post_states: Vec, + pub encrypted_private_post_states: Vec, + pub new_commitments: Vec, + pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct WitnessSet { + pub signatures_and_public_keys: Vec<(Signature, PublicKey)>, + pub proof: Proof, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Proof( + #[serde(with = "base64")] + #[schemars(with = "String", description = "base64-encoded proof")] + pub Vec, +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct EncryptedAccountData { + pub ciphertext: Ciphertext, + pub epk: EphemeralPublicKey, + pub view_tag: ViewTag, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct ProgramDeploymentTransaction { + pub message: ProgramDeploymentMessage, +} + +pub type ViewTag = u8; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Ciphertext( + #[serde(with = "base64")] + #[schemars(with = "String", description = "base64-encoded ciphertext")] + pub Vec, +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct PublicKey( + #[serde(with = "base64::arr")] + #[schemars(with = "String", description = "base64-encoded public key")] + pub [u8; 32], +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct EphemeralPublicKey( + #[serde(with = "base64")] + #[schemars(with = "String", description = "base64-encoded ephemeral public key")] + pub Vec, +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Commitment( + #[serde(with = "base64::arr")] + #[schemars(with = "String", description = "base64-encoded commitment")] + pub [u8; 32], +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Nullifier( + #[serde(with = "base64::arr")] + #[schemars(with = "String", description = "base64-encoded nullifier")] + pub [u8; 32], +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct CommitmentSetDigest( + #[serde(with = "base64::arr")] + #[schemars(with = "String", description = "base64-encoded commitment set digest")] + pub [u8; 32], +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct ProgramDeploymentMessage { + #[serde(with = "base64")] + #[schemars(with = "String", description = "base64-encoded program bytecode")] + pub bytecode: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Data( + #[serde(with = "base64")] + #[schemars(with = "String", description = "base64-encoded account data")] + pub Vec, +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub struct Hash( + #[serde(with = "base64::arr")] + #[schemars(with = "String", description = "base64-encoded hash")] + pub [u8; 32], +); + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +pub enum BedrockStatus { + Pending, + Safe, + Finalized, +} + +mod base64 { + use base64::prelude::{BASE64_STANDARD, Engine as _}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + pub mod arr { + use super::*; + + pub fn serialize(v: &[u8], s: S) -> Result { + super::serialize(v, s) + } + + pub fn deserialize<'de, const N: usize, D: Deserializer<'de>>( + d: D, + ) -> Result<[u8; N], D::Error> { + let vec = super::deserialize(d)?; + vec.try_into().map_err(|_| { + serde::de::Error::custom(format!("Invalid length, expected {N} bytes")) + }) + } + } + + pub fn serialize(v: &[u8], s: S) -> Result { + let base64 = BASE64_STANDARD.encode(v); + String::serialize(&base64, s) + } + + pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result, D::Error> { + let base64 = String::deserialize(d)?; + BASE64_STANDARD + .decode(base64.as_bytes()) + .map_err(serde::de::Error::custom) + } +} diff --git a/indexer_service/rpc/Cargo.toml b/indexer_service/rpc/Cargo.toml new file mode 100644 index 00000000..f77c5abf --- /dev/null +++ b/indexer_service/rpc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "indexer_service_rpc" +version = "0.1.0" +edition = "2024" + +[dependencies] +indexer_service_protocol = { workspace = true } + +jsonrpsee = { workspace = true, features = ["macros"] } +serde_json.workspace = true +schemars.workspace = true + +[features] +client = ["jsonrpsee/client"] +server = ["jsonrpsee/server"] diff --git a/indexer_service/rpc/src/lib.rs b/indexer_service/rpc/src/lib.rs new file mode 100644 index 00000000..c1c4a560 --- /dev/null +++ b/indexer_service/rpc/src/lib.rs @@ -0,0 +1,40 @@ +use indexer_service_protocol::{Account, AccountId, Block, BlockId, Hash, Transaction}; +use jsonrpsee::{core::SubscriptionResult, proc_macros::rpc, types::ErrorObjectOwned}; + +#[cfg(all(not(feature = "server"), not(feature = "client")))] +compile_error!("At least one of `server` or `client` features must be enabled."); + +#[cfg_attr(feature = "server", rpc(server))] +#[cfg_attr(feature = "client", rpc(client))] +pub trait Rpc { + #[method(name = "get_schema")] + fn get_schema(&self) -> Result { + // TODO: Canonical solution would be to provide `describe` method returning OpenRPC spec, + // But for now it's painful to implement, although can be done if really needed. + // Currently we can wait until we can auto-generated it: https://github.com/paritytech/jsonrpsee/issues/737 + // and just return JSON schema. + + // Block schema contains all other types used in the protocol, so it's sufficient to return + // its schema. + let block_schema = schemars::schema_for!(Block); + Ok(serde_json::to_value(block_schema).expect("Schema serialization should not fail")) + } + + #[subscription(name = "subscribeToBlocks", item = Vec)] + async fn subscribe_to_blocks(&self, from: BlockId) -> SubscriptionResult; + + #[method(name = "getBlockById")] + async fn get_block_by_id(&self, block_id: BlockId) -> Result; + + #[method(name = "getBlockByHash")] + async fn get_block_by_hash(&self, block_hash: Hash) -> Result; + + #[method(name = "getLastBlockId")] + async fn get_last_block_id(&self) -> Result; + + #[method(name = "getAccount")] + async fn get_account(&self, account_id: AccountId) -> Result; + + #[method(name = "getTransaction")] + async fn get_transaction(&self, tx_hash: Hash) -> Result; +} diff --git a/indexer_service/src/lib.rs b/indexer_service/src/lib.rs new file mode 100644 index 00000000..1f278a4d --- /dev/null +++ b/indexer_service/src/lib.rs @@ -0,0 +1 @@ +pub mod service; diff --git a/indexer_service/src/main.rs b/indexer_service/src/main.rs new file mode 100644 index 00000000..bfdd3259 --- /dev/null +++ b/indexer_service/src/main.rs @@ -0,0 +1,72 @@ +use std::net::SocketAddr; + +use anyhow::{Context as _, Result}; +use clap::Parser; +use indexer_service_rpc::RpcServer as _; +use jsonrpsee::server::Server; +use log::{error, info}; +use tokio_util::sync::CancellationToken; + +#[derive(Debug, Parser)] +#[clap(version)] +struct Args { + #[clap(short, long, default_value = "8779")] + port: u16, +} + +#[tokio::main] +async fn main() -> Result<()> { + env_logger::init(); + + let args = Args::parse(); + + let cancellation_token = listen_for_shutdown_signal(); + + let handle = run_server(args.port).await?; + let handle_clone = handle.clone(); + + tokio::select! { + _ = cancellation_token.cancelled() => { + info!("Shutting down server..."); + } + _ = handle_clone.stopped() => { + error!("Server stopped unexpectedly"); + } + } + + info!("Server shutdown complete"); + + Ok(()) +} + +async fn run_server(port: u16) -> Result { + let server = Server::builder() + .build(SocketAddr::from(([0, 0, 0, 0], port))) + .await + .context("Failed to build RPC server")?; + + let addr = server + .local_addr() + .context("Failed to get local address of RPC server")?; + + info!("Starting Indexer Service RPC server on {addr}"); + + let handle = server.start(indexer_service::service::IndexerService.into_rpc()); + Ok(handle) +} + +fn listen_for_shutdown_signal() -> CancellationToken { + let cancellation_token = CancellationToken::new(); + let cancellation_token_clone = cancellation_token.clone(); + + tokio::spawn(async move { + if let Err(err) = tokio::signal::ctrl_c().await { + error!("Failed to listen for Ctrl-C signal: {err}"); + return; + } + info!("Received Ctrl-C signal"); + cancellation_token_clone.cancel(); + }); + + cancellation_token +} diff --git a/indexer_service/src/service.rs b/indexer_service/src/service.rs new file mode 100644 index 00000000..46c5fb2d --- /dev/null +++ b/indexer_service/src/service.rs @@ -0,0 +1,36 @@ +use indexer_service_protocol::{Account, AccountId, Block, BlockId, Hash, Transaction}; +use jsonrpsee::{core::SubscriptionResult, types::ErrorObjectOwned}; + +pub struct IndexerService; + +// `async_trait` is required by `jsonrpsee` +#[async_trait::async_trait] +impl indexer_service_rpc::RpcServer for IndexerService { + async fn subscribe_to_blocks( + &self, + _subscription_sink: jsonrpsee::PendingSubscriptionSink, + _from: BlockId, + ) -> SubscriptionResult { + todo!() + } + + async fn get_block_by_id(&self, _block_id: BlockId) -> Result { + todo!() + } + + async fn get_block_by_hash(&self, _block_hash: Hash) -> Result { + todo!() + } + + async fn get_last_block_id(&self) -> Result { + todo!() + } + + async fn get_account(&self, _account_id: AccountId) -> Result { + todo!() + } + + async fn get_transaction(&self, _tx_hash: Hash) -> Result { + todo!() + } +} diff --git a/nssa/core/src/account.rs b/nssa/core/src/account.rs index 55ab0ded..b1f41b65 100644 --- a/nssa/core/src/account.rs +++ b/nssa/core/src/account.rs @@ -68,6 +68,10 @@ impl AccountId { pub fn value(&self) -> &[u8; 32] { &self.value } + + pub fn into_value(self) -> [u8; 32] { + self.value + } } impl AsRef<[u8]> for AccountId { diff --git a/nssa/core/src/encoding.rs b/nssa/core/src/encoding.rs index 24ac050c..34be3782 100644 --- a/nssa/core/src/encoding.rs +++ b/nssa/core/src/encoding.rs @@ -69,6 +69,11 @@ impl Commitment { self.0 } + #[cfg(feature = "host")] + pub fn from_byte_array(bytes: [u8; 32]) -> Self { + Self(bytes) + } + #[cfg(feature = "host")] pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut bytes = [0u8; 32]; @@ -89,6 +94,11 @@ impl Nullifier { self.0 } + #[cfg(feature = "host")] + pub fn from_byte_array(bytes: [u8; 32]) -> Self { + Self(bytes) + } + pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut bytes = [0u8; 32]; cursor.read_exact(&mut bytes)?; @@ -106,6 +116,16 @@ impl Ciphertext { bytes } + #[cfg(feature = "host")] + pub fn into_inner(self) -> Vec { + self.0 + } + + #[cfg(feature = "host")] + pub fn from_inner(inner: Vec) -> Self { + Self(inner) + } + #[cfg(feature = "host")] pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { let mut u32_bytes = [0; 4]; diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 1b490de8..1ebe90f3 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -20,6 +20,16 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct Proof(pub(crate) Vec); +impl Proof { + pub fn into_inner(self) -> Vec { + self.0 + } + + pub fn from_inner(inner: Vec) -> Self { + Self(inner) + } +} + #[derive(Clone)] pub struct ProgramWithDependencies { pub program: Program, diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index 6d195321..f507e65c 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -45,12 +45,12 @@ impl EncryptedAccountData { #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct Message { - pub(crate) public_account_ids: Vec, - pub(crate) nonces: Vec, - pub(crate) public_post_states: Vec, + pub public_account_ids: Vec, + pub nonces: Vec, + pub public_post_states: Vec, pub encrypted_private_post_states: Vec, pub new_commitments: Vec, - pub(crate) new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, + pub new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, } impl Message { diff --git a/nssa/src/privacy_preserving_transaction/transaction.rs b/nssa/src/privacy_preserving_transaction/transaction.rs index 2cb0889b..34649d2d 100644 --- a/nssa/src/privacy_preserving_transaction/transaction.rs +++ b/nssa/src/privacy_preserving_transaction/transaction.rs @@ -16,7 +16,7 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct PrivacyPreservingTransaction { pub message: Message, - witness_set: WitnessSet, + pub witness_set: WitnessSet, } impl PrivacyPreservingTransaction { diff --git a/nssa/src/privacy_preserving_transaction/witness_set.rs b/nssa/src/privacy_preserving_transaction/witness_set.rs index b38b0fb9..365b61b9 100644 --- a/nssa/src/privacy_preserving_transaction/witness_set.rs +++ b/nssa/src/privacy_preserving_transaction/witness_set.rs @@ -46,4 +46,18 @@ impl WitnessSet { pub fn proof(&self) -> &Proof { &self.proof } + + pub fn into_raw_parts(self) -> (Vec<(Signature, PublicKey)>, Proof) { + (self.signatures_and_public_keys, self.proof) + } + + pub fn from_raw_parts( + signatures_and_public_keys: Vec<(Signature, PublicKey)>, + proof: Proof, + ) -> Self { + Self { + signatures_and_public_keys, + proof, + } + } } diff --git a/nssa/src/program_deployment_transaction/message.rs b/nssa/src/program_deployment_transaction/message.rs index 65e9ec27..41c4e10a 100644 --- a/nssa/src/program_deployment_transaction/message.rs +++ b/nssa/src/program_deployment_transaction/message.rs @@ -9,4 +9,8 @@ impl Message { pub fn new(bytecode: Vec) -> Self { Self { bytecode } } + + pub fn into_bytecode(self) -> Vec { + self.bytecode + } } diff --git a/nssa/src/program_deployment_transaction/transaction.rs b/nssa/src/program_deployment_transaction/transaction.rs index c5f31a1c..6002aded 100644 --- a/nssa/src/program_deployment_transaction/transaction.rs +++ b/nssa/src/program_deployment_transaction/transaction.rs @@ -14,6 +14,10 @@ impl ProgramDeploymentTransaction { Self { message } } + pub fn into_message(self) -> Message { + self.message + } + pub(crate) fn validate_and_produce_public_state_diff( &self, state: &V02State, diff --git a/nssa/src/public_transaction/message.rs b/nssa/src/public_transaction/message.rs index d8bd2da0..36a20fbb 100644 --- a/nssa/src/public_transaction/message.rs +++ b/nssa/src/public_transaction/message.rs @@ -9,10 +9,10 @@ use crate::{AccountId, error::NssaError, program::Program}; #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct Message { - pub(crate) program_id: ProgramId, - pub(crate) account_ids: Vec, - pub(crate) nonces: Vec, - pub(crate) instruction_data: InstructionData, + pub program_id: ProgramId, + pub account_ids: Vec, + pub nonces: Vec, + pub instruction_data: InstructionData, } impl Message { diff --git a/nssa/src/public_transaction/witness_set.rs b/nssa/src/public_transaction/witness_set.rs index 09a35a4e..9b9cd290 100644 --- a/nssa/src/public_transaction/witness_set.rs +++ b/nssa/src/public_transaction/witness_set.rs @@ -37,6 +37,16 @@ impl WitnessSet { pub fn signatures_and_public_keys(&self) -> &[(Signature, PublicKey)] { &self.signatures_and_public_keys } + + pub fn into_raw_parts(self) -> Vec<(Signature, PublicKey)> { + self.signatures_and_public_keys + } + + pub fn from_raw_parts(signatures_and_public_keys: Vec<(Signature, PublicKey)>) -> Self { + Self { + signatures_and_public_keys, + } + } } #[cfg(test)] diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 780ad634..f76c480a 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -8,7 +8,7 @@ use rand::{RngCore, rngs::OsRng}; #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct Signature { - value: [u8; 64], + pub value: [u8; 64], } impl Signature {