Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
686 changes: 456 additions & 230 deletions Cargo.lock

Large diffs are not rendered by default.

53 changes: 32 additions & 21 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,57 @@ path = "src/main.rs"

[dependencies]
# CLI and async runtime
clap = { version = "4.5", features = ["derive"] }
tokio = { version = "1.46", features = ["full"] }
clap = { version = "=4.5", features = ["derive"] }
tokio = { version = "=1.46", features = ["full"] }

# Serialization and configuration
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.9"
serde = { version = "=1.0", features = ["derive"] }
serde_json = "=1.0"
toml = "=0.9"

# Error handling
thiserror = "2.0"
thiserror = "=2.0"

# Terminal UI
colored = "3.0"
colored = "=3.0"

# Additional utilities
sha2 = "0.10"
hex = "0.4"
chrono = { version = "0.4", features = ["serde"] }
dirs = "6.0"
rpassword = "7.4"
sha2 = "=0.10"
hex = "=0.4"
chrono = { version = "=0.4", features = ["serde"] }
dirs = "=6.0"
rpassword = "=7.4"

# Quantum-Safe Encryption
argon2 = "0.5" # Password-based key derivation (quantum-safe)
rand = "0.9"
aes-gcm = "0.10" # AES-256-GCM (quantum-safe with 256-bit keys)
argon2 = "=0.5" # Password-based key derivation (quantum-safe)
rand = "=0.9"
aes-gcm = "=0.10" # AES-256-GCM (quantum-safe with 256-bit keys)

# Quantus crypto dependencies
rusty-crystals-dilithium = { git = "https://github.com/Quantus-Network/rusty-crystals.git", package = "rusty-crystals-dilithium" }
rusty-crystals-hdwallet = { git = "https://github.com/Quantus-Network/rusty-crystals.git", package = "rusty-crystals-hdwallet" }
poseidon-resonance = { git = "https://github.com/Quantus-Network/poseidon-resonance", branch = "main", features = [

qp-dilithium-crypto = { git = "https://github.com/Quantus-Network/chain", package = "qp-dilithium-crypto" }
qp-poseidon = { version = "0.9.5", default-features = false, features = [
"serde",
] }
dilithium-crypto = { git = "https://github.com/Quantus-Network/chain", package = "dilithium-crypto", tag = "v0.1.6" }
qp-poseidon-core = { version = "0.9.5", default-features = false }
qp-rusty-crystals-dilithium = { version = "1.0.3", default-features = false }
qp-rusty-crystals-hdwallet = { version = "0.1.4" }

# Wormhole and zk-circuits
qp-wormhole-circuit = { version = "0.1.2", default-features = false }
qp-zk-circuits-common = { version = "0.1.2", default-features = false, features = [
"no_random",
] }
trie-db = { version = "=0.29.1", default-features = false }
sp-trie = { git = "https://github.com/Quantus-Network/zk-trie" }

# Blockchain and RPC client
codec = { package = "parity-scale-codec", version = "3.7", features = [
"derive",
] }
sp-core = { version = "37.0.0" }
sp-runtime = { version = "42.0.0" }
sp-runtime = { version = "42.0.0", default-features = false }
sp-storage = { version = "22.0.0", default-features = false }
sp-state-machine = { git = "https://github.com/Quantus-Network/zk-state-machine" }
subxt = "0.43.0"
jsonrpsee = { version = "0.24", features = ["client"] }

Expand Down
75 changes: 66 additions & 9 deletions src/chain/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
//! across all CLI modules.

use crate::{error::QuantusError, log_verbose};
use dilithium_crypto::types::DilithiumSignatureScheme;
use jsonrpsee::ws_client::{WsClient, WsClientBuilder};
use poseidon_resonance::PoseidonHasher;
use sp_core::{crypto::AccountId32, ByteArray};
use jsonrpsee::{
core::{client::ClientT, traits::ToRpcParams},
rpc_params,
ws_client::{WsClient, WsClientBuilder},
};
use qp_dilithium_crypto::types::DilithiumSignatureScheme;
use qp_poseidon::PoseidonHasher;
use serde::{Deserialize, Serialize};
use sp_core::{crypto::AccountId32, ByteArray, Bytes, H256};
use sp_runtime::{traits::IdentifyAccount, MultiAddress};
use sp_storage::StorageKey;
use std::{sync::Arc, time::Duration};
use subxt::{
backend::rpc::RpcClient,
Expand Down Expand Up @@ -44,6 +50,14 @@ impl Config for ChainConfig {
type ExtrinsicParams = DefaultExtrinsicParams<Self>;
}

// Exact structure from
// https://github.com/paritytech/substrate/blob/master/client/rpc-api/src/state/helpers.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReadProof<Hash> {
pub at: Hash,
pub proof: Vec<Bytes>,
}

/// Wrapper around OnlineClient that also stores the node URL and RPC client
#[derive(Clone)]
pub struct QuantusClient {
Expand Down Expand Up @@ -152,6 +166,46 @@ impl QuantusClient {
Ok(latest_hash)
}

pub async fn get_storage_proof_by_keys(
&self,
storage_keys: Vec<StorageKey>,
at_block: Option<H256>,
) -> crate::error::Result<ReadProof<H256>> {
// Optional: debug the exact JSON we're sending
// use jsonrpsee::core::client::ClientT;
let params: jsonrpsee::core::params::ArrayParams = rpc_params![storage_keys, at_block];
let params_clone = params.clone().to_rpc_params()?;
println!("Sending RPC request with params: {params_clone:?}");

let proof: ReadProof<H256> =
self.rpc_client.request("state_getReadProof", params).await.map_err(|e| {
crate::error::QuantusError::NetworkError(format!(
"Failed to fetch storage proof: {e:?}"
))
})?;
Ok(proof)
}

pub async fn get_block_header(
&self,
block_hash: H256,
) -> crate::error::Result<SubstrateHeader<u32, SubxtPoseidonHasher>> {
log_verbose!("🔍 Fetching block header for block: {:?}", block_hash);

let header: SubstrateHeader<u32, SubxtPoseidonHasher> = self
.rpc_client
.request("chain_getHeader", rpc_params![block_hash])
.await
.map_err(|e| {
crate::error::QuantusError::NetworkError(format!(
"Failed to fetch block header: {e:?}"
))
})?;

log_verbose!("📦 Block header: {:?}", header);
Ok(header)
}

/// Get account nonce from the best block (latest) using direct RPC call
/// This bypasses SubXT's default behavior of using finalized blocks
pub async fn get_account_nonce_from_best_block(
Expand Down Expand Up @@ -262,12 +316,12 @@ impl QuantusClient {
}

// Implement subxt::tx::Signer for ResonancePair
impl subxt::tx::Signer<ChainConfig> for dilithium_crypto::types::DilithiumPair {
impl subxt::tx::Signer<ChainConfig> for qp_dilithium_crypto::types::DilithiumPair {
fn account_id(&self) -> <ChainConfig as Config>::AccountId {
let resonance_public =
dilithium_crypto::types::DilithiumPublic::from_slice(self.public.as_slice())
qp_dilithium_crypto::types::DilithiumPublic::from_slice(self.public.as_slice())
.expect("Invalid public key");
<dilithium_crypto::types::DilithiumPublic as IdentifyAccount>::into_account(
<qp_dilithium_crypto::types::DilithiumPublic as IdentifyAccount>::into_account(
resonance_public,
)
}
Expand All @@ -277,7 +331,10 @@ impl subxt::tx::Signer<ChainConfig> for dilithium_crypto::types::DilithiumPair {
// sp_core::Pair::sign returns ResonanceSignatureWithPublic, which we need to wrap in
// ResonanceSignatureScheme
let signature_with_public =
<dilithium_crypto::types::DilithiumPair as sp_core::Pair>::sign(self, signer_payload);
dilithium_crypto::types::DilithiumSignatureScheme::Dilithium(signature_with_public)
<qp_dilithium_crypto::types::DilithiumPair as sp_core::Pair>::sign(
self,
signer_payload,
);
qp_dilithium_crypto::types::DilithiumSignatureScheme::Dilithium(signature_with_public)
}
}
2 changes: 1 addition & 1 deletion src/cli/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
};
use clap::Subcommand;
use colored::Colorize;
use poseidon_resonance::PoseidonHasher;
use qp_poseidon::PoseidonHasher;
use sp_core::crypto::Ss58Codec;
use std::str::FromStr;
use subxt::events::EventDetails;
Expand Down
7 changes: 7 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod storage;
pub mod system;
pub mod tech_collective;
pub mod wallet;
pub mod wormhole;

/// Main CLI commands
#[derive(Subcommand, Debug)]
Expand Down Expand Up @@ -76,6 +77,10 @@ pub enum Commands {
#[command(subcommand)]
TechCollective(tech_collective::TechCollectiveCommands),

/// Wormhole commands
#[command(subcommand)]
Wormhole(wormhole::WormholeCommands),

/// Runtime management commands (requires root/sudo permissions)
#[command(subcommand)]
Runtime(runtime::RuntimeCommands),
Expand Down Expand Up @@ -235,6 +240,8 @@ pub async fn execute_command(
storage::handle_storage_command(storage_cmd, node_url).await,
Commands::TechCollective(tech_collective_cmd) =>
tech_collective::handle_tech_collective_command(tech_collective_cmd, node_url).await,
Commands::Wormhole(wormhole_cmd) =>
wormhole::handle_wormhole_command(wormhole_cmd, node_url).await,
Commands::Runtime(runtime_cmd) =>
runtime::handle_runtime_command(runtime_cmd, node_url).await,
Commands::Call {
Expand Down
11 changes: 9 additions & 2 deletions src/cli/progress_spinner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,16 @@ pub async fn wait_for_tx_confirmation(
) -> Result<bool> {
// Use ProgressSpinner to show waiting progress
let mut spinner = ProgressSpinner::new();
// let mut blocks_sub = client.blocks().subscribe_finalized().await?;

// while let Some(bloock) = blocks_sub.next().await {
// log_verbose!("New finalized block received");
// spinner.tick();
// break;
// }

// For now, we use a simple delay approach similar to substrate-api-client
for _ in 0..10 {
for _ in 0..30 {
spinner.tick();
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
Expand All @@ -52,6 +59,6 @@ pub async fn wait_for_tx_confirmation(
println!();

use crate::log_verbose;
log_verbose!("✅ Transaction likely finalized (after 6s delay)");
log_verbose!("✅ Transaction likely finalized (after 30s delay)");
Ok(true)
}
11 changes: 10 additions & 1 deletion src/cli/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,16 @@ pub async fn get_storage_raw(
// Get the latest block hash to read from the latest state (not finalized)
let latest_block_hash = quantus_client.get_latest_block().await?;

let storage_at = quantus_client.client().storage().at(latest_block_hash);
get_storage_at_block_raw(quantus_client, key, latest_block_hash).await
}

/// Get raw storage value by key
pub async fn get_storage_at_block_raw(
quantus_client: &crate::chain::client::QuantusClient,
key: Vec<u8>,
block_hash: subxt::utils::H256,
) -> crate::error::Result<Option<Vec<u8>>> {
let storage_at = quantus_client.client().storage().at(block_hash);

let result = storage_at.fetch_raw(key).await?;

Expand Down
Loading