diff --git a/Cargo.lock b/Cargo.lock index d3abb5dd..bd77dd15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4841,10 +4841,10 @@ dependencies = [ "async-trait", "celestia-rpc", "celestia-types", - "ed25519-consensus", "log", "prism-common", "prism-errors", + "prism-keys", "prism-serde", "serde", "sp1-sdk", @@ -4879,11 +4879,11 @@ name = "prism-lightclient" version = "0.1.0" dependencies = [ "anyhow", - "ed25519-consensus", "log", "prism-common", "prism-da", "prism-errors", + "prism-keys", "sp1-sdk", "tokio", ] @@ -4894,10 +4894,9 @@ version = "0.1.0" dependencies = [ "anyhow", "axum 0.6.20", - "ed25519-consensus", "jmt", - "keystore-rs", "log", + "paste", "prism-common", "prism-da", "prism-errors", @@ -4955,11 +4954,11 @@ name = "prism-tests" version = "0.1.0" dependencies = [ "anyhow", - "keystore-rs", "log", "pretty_env_logger", "prism-common", "prism-da", + "prism-keys", "prism-lightclient", "prism-prover", "prism-storage", @@ -4975,6 +4974,7 @@ dependencies = [ "anyhow", "jmt", "log", + "paste", "prism-common", "prism-errors", "prism-keys", diff --git a/Cargo.toml b/Cargo.toml index c018660c..79ee4bb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,6 +108,7 @@ jmt = { git = "https://github.com/deltadevsde/jmt", branch = "rehashing-circuit" sha2 = "0.10.8" tempfile = "3.14.0" auto_impl = "1.2.0" +paste = "1.0.15" # prism prism-common = { path = "crates/common" } diff --git a/ci/run-validator.sh b/ci/run-validator.sh index 53775965..f35cbdf6 100755 --- a/ci/run-validator.sh +++ b/ci/run-validator.sh @@ -99,7 +99,8 @@ provision_light_nodes() { "$NODE_NAME" \ "$light_address" \ "$BRIDGE_COINS" \ - --fees 21000utia + --fees 21000utia \ + --broadcast-mode block done echo "Provisioning finished." @@ -160,12 +161,18 @@ provision_bridge_nodes() { "$NODE_NAME" \ "$bridge_address" \ "$BRIDGE_COINS" \ - --fees 21000utia + --fees 21000utia \ + --broadcast-mode block done echo "Provisioning finished." } +provision() { + provision_bridge_nodes + provision_light_nodes +} + # Set up the validator for a private alone network. # Based on # https://github.com/celestiaorg/celestia-app/blob/main/scripts/single-node.sh @@ -202,8 +209,7 @@ setup_private_validator() { main() { # Configure stuff setup_private_validator - provision_bridge_nodes & - provision_light_nodes & + provision & # Start the celestia-app echo "Configuration finished. Running a validator node..." celestia-appd start --api.enable --grpc.enable --force-no-bbr diff --git a/crates/cli/src/cfg.rs b/crates/cli/src/cfg.rs index cea3ab63..afe153d7 100644 --- a/crates/cli/src/cfg.rs +++ b/crates/cli/src/cfg.rs @@ -36,6 +36,12 @@ pub struct CommandArgs { #[arg(long)] verifying_key: Option, + /// The algorithm used for the verifying key. + /// + /// Can be one of: `ed25519`, `secp256k1`, `secp256r1`. + #[arg(long, default_value = "ed25519")] + verifying_key_algorithm: Option, + #[arg(long)] config_path: Option, @@ -97,6 +103,7 @@ pub struct Config { pub da_layer: DALayerOption, pub redis_config: Option, pub verifying_key: Option, + pub verifying_key_algorithm: String, } impl Default for Config { @@ -107,6 +114,7 @@ impl Default for Config { da_layer: DALayerOption::default(), redis_config: Some(RedisConfig::default()), verifying_key: None, + verifying_key_algorithm: "ed25519".to_string(), } } } @@ -201,6 +209,9 @@ fn apply_command_line_args(config: Config, args: CommandArgs) -> Config { }), da_layer: config.da_layer, verifying_key: args.verifying_key.or(config.verifying_key), + verifying_key_algorithm: args + .verifying_key_algorithm + .unwrap_or(config.verifying_key_algorithm), } } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index b1a84552..4361851c 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -4,14 +4,14 @@ mod node_types; use cfg::{initialize_da_layer, load_config, Cli, Commands}; use clap::Parser; use keystore_rs::{KeyChain, KeyStore, KeyStoreType}; -use prism_keys::VerifyingKey; +use prism_keys::{CryptoAlgorithm, SigningKey, VerifyingKey}; +use std::io::{Error, ErrorKind}; use node_types::NodeType; use prism_lightclient::LightClient; use prism_prover::Prover; use prism_storage::RedisConnection; -use std::sync::Arc; - +use std::{str::FromStr, sync::Arc}; #[macro_use] extern crate log; @@ -24,105 +24,120 @@ async fn main() -> std::io::Result<()> { let node: Arc = match cli.command { Commands::LightClient(args) => { let config = load_config(args.clone()) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; let da = initialize_da_layer(&config) .await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; let celestia_config = config.celestia_config.ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::NotFound, - "celestia configuration not found", - ) + Error::new(ErrorKind::NotFound, "celestia configuration not found") })?; - let prover_vk = config.verifying_key.and_then(|s| s.try_into().ok()).and_then( - |vk: VerifyingKey| match vk { - VerifyingKey::Ed25519(key) => Some(key), - _ => None, - }, - ); + let verifying_key_algorithm = + CryptoAlgorithm::from_str(&config.verifying_key_algorithm).map_err(|_| { + Error::new( + ErrorKind::InvalidInput, + "invalid verifying key algorithm format", + ) + })?; + let prover_vk = VerifyingKey::from_algorithm_and_bytes( + verifying_key_algorithm, + config.verifying_key.unwrap().as_bytes(), + ) + .map_err(|e| Error::new(ErrorKind::InvalidInput, e.to_string()))?; - Arc::new(LightClient::new(da, celestia_config, prover_vk)) + Arc::new(LightClient::new(da, celestia_config, Some(prover_vk))) } Commands::Prover(args) => { let config = load_config(args.clone()) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; let da = initialize_da_layer(&config) .await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; - let redis_config = config.clone().redis_config.ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::NotFound, - "redis configuration not found", - ) - })?; + let redis_config = config + .clone() + .redis_config + .ok_or_else(|| Error::new(ErrorKind::NotFound, "redis configuration not found"))?; let redis_connections = RedisConnection::new(&redis_config) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; - let signing_key = KeyStoreType::KeyChain(KeyChain) + let signing_key_chain = KeyStoreType::KeyChain(KeyChain) .get_signing_key() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; + + let verifying_key_algorithm = + CryptoAlgorithm::from_str(&config.verifying_key_algorithm).map_err(|_| { + Error::new( + ErrorKind::InvalidInput, + "invalid verifying key algorithm format", + ) + })?; + let signing_key = SigningKey::from_algorithm_and_bytes( + verifying_key_algorithm, + signing_key_chain.as_bytes(), + ) + .map_err(|e| Error::new(ErrorKind::InvalidInput, format!("Invalid signing key: {}", e)))?; + let verifying_key = signing_key.verifying_key(); let prover_cfg = prism_prover::Config { prover: true, batcher: true, webserver: config.webserver.unwrap_or_default(), signing_key: signing_key.clone(), - verifying_key: signing_key.verification_key(), + verifying_key: verifying_key.clone(), start_height: config.celestia_config.unwrap_or_default().start_height, }; - info!( - "prover verifying key: {}", - VerifyingKey::from(prover_cfg.verifying_key) - ); - Arc::new( Prover::new(Arc::new(Box::new(redis_connections)), da, &prover_cfg).map_err( |e| { error!("error initializing prover: {}", e); - std::io::Error::new(std::io::ErrorKind::Other, e.to_string()) + Error::new(ErrorKind::Other, e.to_string()) }, )?, ) } Commands::FullNode(args) => { let config = load_config(args.clone()) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; let da = initialize_da_layer(&config) .await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; - let redis_config = config.clone().redis_config.ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::NotFound, - "redis configuration not found", - ) - })?; + let redis_config = config + .clone() + .redis_config + .ok_or_else(|| Error::new(ErrorKind::NotFound, "redis configuration not found"))?; let redis_connections = RedisConnection::new(&redis_config) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; - let signing_key = KeyStoreType::KeyChain(KeyChain) + let signing_key_chain = KeyStoreType::KeyChain(KeyChain) .get_signing_key() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| Error::new(ErrorKind::Other, e.to_string()))?; + + let verifying_key_algorithm = + CryptoAlgorithm::from_str(&config.verifying_key_algorithm).map_err(|_| { + Error::new( + ErrorKind::InvalidInput, + "invalid verifying key algorithm format", + ) + })?; + let signing_key = SigningKey::from_algorithm_and_bytes( + verifying_key_algorithm, + signing_key_chain.as_bytes(), + ) + .map_err(|e| Error::new(ErrorKind::InvalidInput, format!("Invalid signing key: {}", e)))?; let prover_vk = config .verifying_key - .and_then(|s| s.try_into().ok()) - .and_then(|vk: VerifyingKey| match vk { - VerifyingKey::Ed25519(key) => Some(key), - _ => None, - }) - .ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::NotFound, - "prover verifying key not found", - ) + .ok_or_else(|| Error::new(ErrorKind::NotFound, "prover verifying key not found")) + .and_then(|vk| { + VerifyingKey::from_algorithm_and_bytes(verifying_key_algorithm, vk.as_bytes()) + .map_err(|e| Error::new(ErrorKind::InvalidInput, e.to_string())) })?; let prover_cfg = prism_prover::Config { @@ -138,12 +153,12 @@ async fn main() -> std::io::Result<()> { Prover::new(Arc::new(Box::new(redis_connections)), da, &prover_cfg).map_err( |e| { error!("error initializing prover: {}", e); - std::io::Error::new(std::io::ErrorKind::Other, e.to_string()) + Error::new(ErrorKind::Other, e.to_string()) }, )?, ) } }; - node.start().await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + node.start().await.map_err(|e| Error::new(ErrorKind::Other, e.to_string())) } diff --git a/crates/common/src/transaction_builder.rs b/crates/common/src/transaction_builder.rs index 7444d3e0..563e87ad 100644 --- a/crates/common/src/transaction_builder.rs +++ b/crates/common/src/transaction_builder.rs @@ -6,7 +6,7 @@ use crate::{ operation::{Operation, ServiceChallenge, ServiceChallengeInput, SignatureBundle}, transaction::Transaction, }; -use prism_keys::{SigningKey, VerifyingKey}; +use prism_keys::{CryptoAlgorithm, SigningKey, VerifyingKey}; enum PostCommitAction { UpdateStorageOnly, RememberServiceKey(String, SigningKey), @@ -80,9 +80,15 @@ impl TransactionBuilder { self.accounts.get(id) } - pub fn register_service_with_random_keys(&mut self, id: &str) -> UncommittedTransaction { - let random_service_challenge_key = SigningKey::new_ed25519(); - let random_service_signing_key = SigningKey::new_ed25519(); + pub fn register_service_with_random_keys( + &mut self, + algorithm: CryptoAlgorithm, + id: &str, + ) -> UncommittedTransaction { + let random_service_challenge_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create challenge key"); + let random_service_signing_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create signing key"); self.register_service(id, random_service_challenge_key, random_service_signing_key) } @@ -111,10 +117,12 @@ impl TransactionBuilder { pub fn create_account_with_random_key_signed( &mut self, + algorithm: CryptoAlgorithm, id: &str, service_id: &str, ) -> UncommittedTransaction { - let account_signing_key = SigningKey::new_ed25519(); + let account_signing_key = SigningKey::new_with_algorithm(algorithm) + .expect("Failed to create account signing key"); self.create_account_signed(id, service_id, account_signing_key) } @@ -133,11 +141,13 @@ impl TransactionBuilder { pub fn create_account_with_random_key( &mut self, + algorithm: CryptoAlgorithm, id: &str, service_id: &str, service_signing_key: &SigningKey, ) -> UncommittedTransaction { - let account_signing_key = SigningKey::new_ed25519(); + let account_signing_key = SigningKey::new_with_algorithm(algorithm) + .expect("Failed to create account signing key"); self.create_account(id, service_id, service_signing_key, account_signing_key) } @@ -170,16 +180,26 @@ impl TransactionBuilder { } } - pub fn add_random_key_verified_with_root(&mut self, id: &str) -> UncommittedTransaction { + pub fn add_random_key_verified_with_root( + &mut self, + algorithm: CryptoAlgorithm, + id: &str, + ) -> UncommittedTransaction { let Some(account_signing_key) = self.account_keys.get(id).cloned() else { panic!("No existing account key for {}", id) }; - self.add_random_key(id, &account_signing_key) + self.add_random_key(algorithm, id, &account_signing_key) } - pub fn add_random_key(&mut self, id: &str, signing_key: &SigningKey) -> UncommittedTransaction { - let random_key = SigningKey::new_ed25519().into(); + pub fn add_random_key( + &mut self, + algorithm: CryptoAlgorithm, + id: &str, + signing_key: &SigningKey, + ) -> UncommittedTransaction { + let random_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create random key").into(); self.add_key(id, random_key, signing_key) } @@ -245,20 +265,24 @@ impl TransactionBuilder { pub fn add_randomly_signed_data( &mut self, + algorithm: CryptoAlgorithm, id: &str, value: Vec, signing_key: &SigningKey, ) -> UncommittedTransaction { - let value_signing_key = SigningKey::new_ed25519(); + let value_signing_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create value signing key"); self.add_signed_data(id, value, &value_signing_key, signing_key) } pub fn add_randomly_signed_data_verified_with_root( &mut self, + algorithm: CryptoAlgorithm, id: &str, value: Vec, ) -> UncommittedTransaction { - let value_signing_key = SigningKey::new_ed25519(); + let value_signing_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create value signing key"); self.add_signed_data_verified_with_root(id, value, &value_signing_key) } diff --git a/crates/da/Cargo.toml b/crates/da/Cargo.toml index 75499582..44cde2a0 100644 --- a/crates/da/Cargo.toml +++ b/crates/da/Cargo.toml @@ -13,7 +13,6 @@ readme.workspace = true [dependencies] async-trait = { workspace = true } serde = { workspace = true } -ed25519-consensus = { workspace = true } tokio = { workspace = true } log = { workspace = true } celestia-rpc = { workspace = true } @@ -22,4 +21,5 @@ anyhow = { workspace = true } prism-common = { workspace = true } prism-errors = { workspace = true } prism-serde = { workspace = true } +prism-keys = { workspace = true } sp1-sdk = { workspace = true } diff --git a/crates/da/src/celestia.rs b/crates/da/src/celestia.rs index 6652fe27..db5da034 100644 --- a/crates/da/src/celestia.rs +++ b/crates/da/src/celestia.rs @@ -2,7 +2,7 @@ use crate::{DataAvailabilityLayer, FinalizedEpoch}; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use celestia_rpc::{BlobClient, Client, HeaderClient}; -use celestia_types::{nmt::Namespace, Blob, TxConfig, AppVersion}; +use celestia_types::{nmt::Namespace, AppVersion, Blob, TxConfig}; use log::{debug, error, trace, warn}; use prism_common::transaction::Transaction; use prism_errors::{DataAvailabilityError, GeneralError}; diff --git a/crates/da/src/lib.rs b/crates/da/src/lib.rs index c39c6209..5da4cc68 100644 --- a/crates/da/src/lib.rs +++ b/crates/da/src/lib.rs @@ -1,7 +1,7 @@ use anyhow::Result; use async_trait::async_trait; -use ed25519_consensus::{Signature, SigningKey, VerificationKey as VerifyingKey}; use prism_common::{digest::Digest, transaction::Transaction}; +use prism_keys::{Signature, SigningKey, VerifyingKey}; use prism_serde::{ binary::ToBinary, hex::{FromHex, ToHex}, @@ -50,16 +50,11 @@ impl FinalizedEpoch { let signature_bytes = Vec::::from_hex(signature) .map_err(|e| anyhow::anyhow!("Failed to decode signature: {}", e))?; - if signature_bytes.len() != 64 { - return Err(anyhow::anyhow!("Invalid signature length")); - } + let signature: Signature = + Signature::from_algorithm_and_bytes(vk.algorithm(), signature_bytes.as_slice()) + .map_err(|_| anyhow::anyhow!("Invalid signature length"))?; - let signature: Signature = signature_bytes - .as_slice() - .try_into() - .map_err(|_| anyhow::anyhow!("Invalid signature length"))?; - - vk.verify(&signature, &message) + vk.verify_signature(&message, &signature) .map_err(|e| anyhow::anyhow!("Signature verification failed: {}", e))?; Ok(()) } diff --git a/crates/keys/src/algorithm.rs b/crates/keys/src/algorithm.rs index cf188749..9439024d 100644 --- a/crates/keys/src/algorithm.rs +++ b/crates/keys/src/algorithm.rs @@ -7,3 +7,16 @@ pub enum CryptoAlgorithm { Secp256k1, Secp256r1, } + +impl std::str::FromStr for CryptoAlgorithm { + type Err = (); + + fn from_str(input: &str) -> Result { + match input.to_lowercase().as_str() { + "ed25519" => Ok(CryptoAlgorithm::Ed25519), + "secp256k1" => Ok(CryptoAlgorithm::Secp256k1), + "secp256r1" => Ok(CryptoAlgorithm::Secp256r1), + _ => Err(()), + } + } +} diff --git a/crates/keys/src/lib.rs b/crates/keys/src/lib.rs index cbae9f83..d85bc643 100644 --- a/crates/keys/src/lib.rs +++ b/crates/keys/src/lib.rs @@ -10,128 +10,4 @@ pub use signing_keys::*; pub use verifying_keys::*; #[cfg(test)] -mod tests { - use super::*; - use ed25519_consensus::SigningKey as Ed25519SigningKey; - use prism_serde::base64::ToBase64; - use rand::rngs::OsRng; - use secp256k1::SecretKey as Secp256k1SigningKey; - - #[test] - fn test_reparsed_verifying_keys_are_equal_to_original() { - let verifying_key_ed25519 = SigningKey::new_ed25519().verifying_key(); - let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_bytes( - verifying_key_ed25519.algorithm(), - &verifying_key_ed25519.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_verifying_key, verifying_key_ed25519); - - let verifying_key_secp256k1 = SigningKey::new_secp256k1().verifying_key(); - let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_bytes( - verifying_key_secp256k1.algorithm(), - &verifying_key_secp256k1.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_verifying_key, verifying_key_secp256k1); - - let verifying_key_secp256r1 = SigningKey::new_secp256r1().verifying_key(); - let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_bytes( - verifying_key_secp256r1.algorithm(), - &verifying_key_secp256r1.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_verifying_key, verifying_key_secp256r1); - } - - #[test] - fn test_reparsed_signing_keys_are_equal_to_original() { - let signing_key_ed25519 = SigningKey::new_ed25519(); - let re_parsed_signing_key = SigningKey::from_algorithm_and_bytes( - signing_key_ed25519.algorithm(), - &signing_key_ed25519.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_signing_key, signing_key_ed25519); - - let signing_key_secp256k1 = SigningKey::new_secp256k1(); - let re_parsed_signing_key = SigningKey::from_algorithm_and_bytes( - signing_key_secp256k1.algorithm(), - &signing_key_secp256k1.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_signing_key, signing_key_secp256k1); - - let signing_key_secp256r1 = SigningKey::new_secp256r1(); - let re_parsed_signing_key = SigningKey::from_algorithm_and_bytes( - signing_key_secp256r1.algorithm(), - &signing_key_secp256r1.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_signing_key, signing_key_secp256r1); - } - - #[test] - fn test_reparsed_signatures_are_equal_to_original() { - let message = b"test message"; - - let signature_ed25519 = SigningKey::new_ed25519().sign(message); - let re_parsed_signature = Signature::from_algorithm_and_bytes( - signature_ed25519.algorithm(), - &signature_ed25519.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_signature, signature_ed25519); - - let signature_secp256k1 = SigningKey::new_secp256k1().sign(message); - let re_parsed_signature = Signature::from_algorithm_and_bytes( - signature_secp256k1.algorithm(), - &signature_secp256k1.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_signature, signature_secp256k1); - - let signature_secp256r1 = SigningKey::new_secp256r1().sign(message); - let re_parsed_signature = Signature::from_algorithm_and_bytes( - signature_secp256r1.algorithm(), - &signature_secp256r1.to_bytes(), - ) - .unwrap(); - assert_eq!(re_parsed_signature, signature_secp256r1); - } - - #[test] - fn test_verifying_key_from_string_ed25519() { - let original_key: VerifyingKey = - SigningKey::Ed25519(Box::new(Ed25519SigningKey::new(OsRng))).into(); - let encoded = original_key.to_bytes().to_base64(); - - let result = VerifyingKey::try_from(encoded); - assert!(result.is_ok()); - - let decoded_key = result.unwrap(); - assert_eq!(decoded_key.to_bytes(), original_key.to_bytes()); - } - - #[test] - fn test_verifying_key_from_string_secp256k1() { - let original_key: VerifyingKey = - SigningKey::Secp256k1(Secp256k1SigningKey::new(&mut OsRng)).into(); - let encoded = original_key.to_bytes().to_base64(); - - let result = VerifyingKey::try_from(encoded); - assert!(result.is_ok()); - - let decoded_key = result.unwrap(); - assert_eq!(decoded_key.to_bytes(), original_key.to_bytes()); - } - - #[test] - fn test_verifying_key_from_string_invalid_length() { - let invalid_bytes: [u8; 31] = [1; 31]; - let encoded = invalid_bytes.to_base64(); - - let result = VerifyingKey::try_from(encoded); - assert!(result.is_err()); - } -} +mod tests; diff --git a/crates/keys/src/signatures.rs b/crates/keys/src/signatures.rs index 8b62191c..8906938c 100644 --- a/crates/keys/src/signatures.rs +++ b/crates/keys/src/signatures.rs @@ -4,7 +4,6 @@ use p256::ecdsa::Signature as Secp256r1Signature; use secp256k1::ecdsa::Signature as Secp256k1Signature; use serde::{Deserialize, Serialize}; -use std::{self}; use crate::{payload::CryptoPayload, CryptoAlgorithm}; diff --git a/crates/keys/src/signing_keys.rs b/crates/keys/src/signing_keys.rs index 8b953946..fb7a0c2e 100644 --- a/crates/keys/src/signing_keys.rs +++ b/crates/keys/src/signing_keys.rs @@ -30,6 +30,14 @@ impl SigningKey { SigningKey::Secp256r1(Secp256r1SigningKey::random(&mut OsRng)) } + pub fn new_with_algorithm(algorithm: CryptoAlgorithm) -> Result { + match algorithm { + CryptoAlgorithm::Ed25519 => Ok(SigningKey::new_ed25519()), + CryptoAlgorithm::Secp256k1 => Ok(SigningKey::new_secp256k1()), + CryptoAlgorithm::Secp256r1 => Ok(SigningKey::new_secp256r1()), + } + } + pub fn verifying_key(&self) -> VerifyingKey { self.clone().into() } diff --git a/crates/keys/src/tests.rs b/crates/keys/src/tests.rs new file mode 100644 index 00000000..77b70fbd --- /dev/null +++ b/crates/keys/src/tests.rs @@ -0,0 +1,140 @@ +#[cfg(test)] +mod key_tests { + use crate::{Signature, SigningKey, VerifyingKey}; + use ed25519_consensus::SigningKey as Ed25519SigningKey; + use p256::ecdsa::SigningKey as Secp256r1SigningKey; + use prism_serde::base64::ToBase64; + use rand::rngs::OsRng; + use secp256k1::SecretKey as Secp256k1SigningKey; + + #[test] + fn test_reparsed_verifying_keys_are_equal_to_original() { + let verifying_key_ed25519 = SigningKey::new_ed25519().verifying_key(); + let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_bytes( + verifying_key_ed25519.algorithm(), + &verifying_key_ed25519.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_verifying_key, verifying_key_ed25519); + + let verifying_key_secp256k1 = SigningKey::new_secp256k1().verifying_key(); + let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_bytes( + verifying_key_secp256k1.algorithm(), + &verifying_key_secp256k1.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_verifying_key, verifying_key_secp256k1); + + let verifying_key_secp256r1 = SigningKey::new_secp256r1().verifying_key(); + let re_parsed_verifying_key = VerifyingKey::from_algorithm_and_bytes( + verifying_key_secp256r1.algorithm(), + &verifying_key_secp256r1.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_verifying_key, verifying_key_secp256r1); + } + + #[test] + fn test_reparsed_signing_keys_are_equal_to_original() { + let signing_key_ed25519 = SigningKey::new_ed25519(); + let re_parsed_signing_key = SigningKey::from_algorithm_and_bytes( + signing_key_ed25519.algorithm(), + &signing_key_ed25519.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_signing_key, signing_key_ed25519); + + let signing_key_secp256k1 = SigningKey::new_secp256k1(); + let re_parsed_signing_key = SigningKey::from_algorithm_and_bytes( + signing_key_secp256k1.algorithm(), + &signing_key_secp256k1.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_signing_key, signing_key_secp256k1); + + let signing_key_secp256r1 = SigningKey::new_secp256r1(); + let re_parsed_signing_key = SigningKey::from_algorithm_and_bytes( + signing_key_secp256r1.algorithm(), + &signing_key_secp256r1.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_signing_key, signing_key_secp256r1); + } + + #[test] + fn test_reparsed_signatures_are_equal_to_original() { + let message = b"test message"; + + let signature_ed25519 = SigningKey::new_ed25519().sign(message); + let re_parsed_signature = Signature::from_algorithm_and_bytes( + signature_ed25519.algorithm(), + &signature_ed25519.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_signature, signature_ed25519); + + let signature_secp256k1 = SigningKey::new_secp256k1().sign(message); + let re_parsed_signature = Signature::from_algorithm_and_bytes( + signature_secp256k1.algorithm(), + &signature_secp256k1.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_signature, signature_secp256k1); + + let signature_secp256r1 = SigningKey::new_secp256r1().sign(message); + let re_parsed_signature = Signature::from_algorithm_and_bytes( + signature_secp256r1.algorithm(), + &signature_secp256r1.to_bytes(), + ) + .unwrap(); + assert_eq!(re_parsed_signature, signature_secp256r1); + } + + #[test] + fn test_verifying_key_from_string_ed25519() { + let original_key: VerifyingKey = + SigningKey::Ed25519(Box::new(Ed25519SigningKey::new(OsRng))).into(); + let encoded = original_key.to_bytes().to_base64(); + + let result = VerifyingKey::try_from(encoded); + assert!(result.is_ok()); + + let decoded_key = result.unwrap(); + assert_eq!(decoded_key.to_bytes(), original_key.to_bytes()); + } + + #[test] + fn test_verifying_key_from_string_secp256k1() { + let original_key: VerifyingKey = + SigningKey::Secp256k1(Secp256k1SigningKey::new(&mut OsRng)).into(); + let encoded = original_key.to_bytes().to_base64(); + + let result = VerifyingKey::try_from(encoded); + assert!(result.is_ok()); + + let decoded_key = result.unwrap(); + assert_eq!(decoded_key.to_bytes(), original_key.to_bytes()); + } + + #[test] + fn test_verifying_key_from_string_secp256r1() { + let original_key: VerifyingKey = + SigningKey::Secp256r1(Secp256r1SigningKey::random(&mut OsRng)).into(); + let encoded = original_key.to_bytes().to_base64(); + + let result = VerifyingKey::try_from(encoded); + assert!(result.is_ok()); + + let decoded_key = result.unwrap(); + assert_eq!(decoded_key.to_bytes(), original_key.to_bytes()); + } + + #[test] + fn test_verifying_key_from_string_invalid_length() { + let invalid_bytes: [u8; 31] = [1; 31]; + let encoded = invalid_bytes.to_base64(); + + let result = VerifyingKey::try_from(encoded); + assert!(result.is_err()); + } +} diff --git a/crates/keys/src/verifying_keys.rs b/crates/keys/src/verifying_keys.rs index 065c9f3f..60e5fa3b 100644 --- a/crates/keys/src/verifying_keys.rs +++ b/crates/keys/src/verifying_keys.rs @@ -191,8 +191,8 @@ impl FromBase64 for VerifyingKey { /// /// Depending on the length of the input string, the function will attempt to /// decode it and create a `VerifyingKey` instance. According to the specifications, - /// the input string should be either [32 bytes (Ed25519)](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5) or [33/65 bytes (Secp256k1)](https://www.secg.org/sec1-v2.pdf). - /// The secp256k1 key can be either compressed (33 bytes) or uncompressed (65 bytes). + /// the input string should be either [32 bytes (Ed25519)](https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.5) or [33/65 bytes (Secp256k1 or Secp256r1)](https://www.secg.org/sec1-v2.pdf). + /// The secp256k1 and secp256r1 keys can be either compressed (33 bytes) or uncompressed (65 bytes). /// /// # Returns /// @@ -208,9 +208,13 @@ impl FromBase64 for VerifyingKey { Ok(VerifyingKey::Ed25519(vk)) } 33 | 65 => { - let vk = Secp256k1VerifyingKey::from_slice(bytes.as_slice()) - .map_err(|e| anyhow!("Invalid Secp256k1 key: {}", e))?; - Ok(VerifyingKey::Secp256k1(vk)) + if let Ok(vk) = Secp256k1VerifyingKey::from_slice(bytes.as_slice()) { + Ok(VerifyingKey::Secp256k1(vk)) + } else if let Ok(vk) = Secp256r1VerifyingKey::from_sec1_bytes(bytes.as_slice()) { + Ok(VerifyingKey::Secp256r1(vk)) + } else { + Err(anyhow!("Invalid curve type")) + } } _ => Err(anyhow!("Invalid public key length")), } diff --git a/crates/node_types/lightclient/Cargo.toml b/crates/node_types/lightclient/Cargo.toml index e1cecfd8..a1ef5f97 100644 --- a/crates/node_types/lightclient/Cargo.toml +++ b/crates/node_types/lightclient/Cargo.toml @@ -13,11 +13,11 @@ test_utils = [] mock_prover = [] [dependencies] -ed25519-consensus = { workspace = true } tokio = { workspace = true } log = { workspace = true } anyhow = { workspace = true } prism-common = { workspace = true, features = ["test_utils"] } prism-errors = { workspace = true } prism-da = { workspace = true } +prism-keys = { workspace = true } sp1-sdk = { workspace = true } diff --git a/crates/node_types/lightclient/src/lightclient.rs b/crates/node_types/lightclient/src/lightclient.rs index ee0ea9a6..75c7d2a1 100644 --- a/crates/node_types/lightclient/src/lightclient.rs +++ b/crates/node_types/lightclient/src/lightclient.rs @@ -1,8 +1,8 @@ use anyhow::{Context, Result}; -use ed25519_consensus::VerificationKey as VerifyingKey; use prism_common::digest::Digest; use prism_da::{celestia::CelestiaConfig, DataAvailabilityLayer}; use prism_errors::{DataAvailabilityError, GeneralError}; +use prism_keys::VerifyingKey; use sp1_sdk::{ProverClient, SP1VerifyingKey}; use std::{self, sync::Arc}; use tokio::{sync::broadcast, task::spawn}; @@ -72,7 +72,7 @@ impl LightClient { // TODO: Issue #144 if let Some(pubkey) = &self.prover_pubkey { - match finalized_epoch.verify_signature(*pubkey) { + match finalized_epoch.verify_signature(pubkey.clone()) { Ok(_) => trace!("valid signature for epoch {}", finalized_epoch.height), Err(e) => panic!("invalid signature in epoch {}: {:?}", i, e), } diff --git a/crates/node_types/prover/Cargo.toml b/crates/node_types/prover/Cargo.toml index af10a470..ea29dcc1 100644 --- a/crates/node_types/prover/Cargo.toml +++ b/crates/node_types/prover/Cargo.toml @@ -18,10 +18,8 @@ tower-http = { workspace = true } utoipa = { workspace = true } utoipa-swagger-ui = { workspace = true } serde = { workspace = true } -ed25519-consensus = { workspace = true } tokio = { workspace = true } log = { workspace = true } -keystore-rs = { workspace = true } anyhow = { workspace = true } jmt = { workspace = true } prism-common = { workspace = true, features = ["test_utils"] } @@ -31,3 +29,6 @@ prism-errors = { workspace = true } prism-keys = { workspace = true } prism-da = { workspace = true } sp1-sdk = { workspace = true } + +[dev-dependencies] +paste = { workspace = true } diff --git a/crates/node_types/prover/src/prover/mod.rs b/crates/node_types/prover/src/prover/mod.rs index 12cf7bc3..c6d15485 100644 --- a/crates/node_types/prover/src/prover/mod.rs +++ b/crates/node_types/prover/src/prover/mod.rs @@ -1,9 +1,8 @@ use anyhow::{anyhow, bail, Context, Result}; -use ed25519_consensus::{SigningKey, VerificationKey}; use jmt::KeyHash; -use keystore_rs::create_signing_key; use prism_common::{account::Account, digest::Digest, transaction::Transaction}; use prism_errors::DataAvailabilityError; +use prism_keys::{CryptoAlgorithm, SigningKey, VerifyingKey}; use prism_storage::database::Database; use prism_tree::{ hasher::TreeHasher, @@ -44,7 +43,7 @@ pub struct Config { /// Key used to verify incoming [`FinalizedEpochs`]. /// This is not necessarily the counterpart to signing_key, as fullnodes must use the [`verifying_key`] of the prover. - pub verifying_key: VerificationKey, + pub verifying_key: VerifyingKey, /// DA layer height the prover should start syncing transactions from. pub start_height: u64, @@ -52,19 +51,43 @@ pub struct Config { impl Default for Config { fn default() -> Self { - let signing_key = create_signing_key(); + let signing_key = SigningKey::new_ed25519(); Config { prover: true, batcher: true, webserver: WebServerConfig::default(), signing_key: signing_key.clone(), - verifying_key: signing_key.verification_key(), + verifying_key: signing_key.verifying_key(), start_height: 1, } } } +#[allow(dead_code)] +impl Config { + /// Creates a new Config instance with the specified key algorithm. + /// + /// # Arguments + /// * `algorithm` - The key algorithm to use for signing and verification + /// + /// # Returns + /// A Result containing the Config or an error if key creation fails + fn default_with_key_algorithm(algorithm: CryptoAlgorithm) -> Result { + let signing_key = + SigningKey::new_with_algorithm(algorithm).context("Failed to create signing key")?; + + Ok(Config { + prover: true, + batcher: true, + webserver: WebServerConfig::default(), + signing_key: signing_key.clone(), + verifying_key: signing_key.verifying_key(), + start_height: 1, + }) + } +} + #[allow(dead_code)] pub struct Prover { pub db: Arc, @@ -267,10 +290,10 @@ impl Prover { } // TODO: Issue #144 - match epoch.verify_signature(self.cfg.verifying_key) { - Ok(_) => trace!("valid signature for epoch {}", epoch.height), - Err(e) => panic!("invalid signature in epoch {}: {:?}", epoch.height, e), - } + epoch + .verify_signature(self.cfg.verifying_key.clone()) + .with_context(|| format!("Invalid signature in epoch {}", epoch.height))?; + trace!("valid signature for epoch {}", epoch.height); let prev_commitment = self.db.get_commitment(¤t_epoch)?; diff --git a/crates/node_types/prover/src/prover/tests.rs b/crates/node_types/prover/src/prover/tests.rs index 90034736..0ab9b118 100644 --- a/crates/node_types/prover/src/prover/tests.rs +++ b/crates/node_types/prover/src/prover/tests.rs @@ -1,6 +1,6 @@ use super::*; use prism_common::transaction_builder::TransactionBuilder; -use prism_keys::{SigningKey, VerifyingKey}; +use prism_keys::{CryptoAlgorithm, SigningKey, VerifyingKey}; use std::{self, sync::Arc, time::Duration}; use tokio::spawn; @@ -8,36 +8,37 @@ use prism_da::memory::InMemoryDataAvailabilityLayer; use prism_storage::inmemory::InMemoryDatabase; // Helper function to create a test prover instance -async fn create_test_prover() -> Arc { +async fn create_test_prover(algorithm: CryptoAlgorithm) -> Arc { let (da_layer, _rx, _brx) = InMemoryDataAvailabilityLayer::new(1); let da_layer = Arc::new(da_layer); let db: Arc> = Arc::new(Box::new(InMemoryDatabase::new())); - let cfg = Config::default(); + let cfg = Config::default_with_key_algorithm(algorithm).unwrap(); Arc::new(Prover::new(db.clone(), da_layer, &cfg).unwrap()) } -fn create_mock_transactions(service_id: String) -> Vec { +fn create_mock_transactions(algorithm: CryptoAlgorithm, service_id: String) -> Vec { let mut transaction_builder = TransactionBuilder::new(); vec![ - transaction_builder.register_service_with_random_keys(&service_id).commit(), + transaction_builder.register_service_with_random_keys(algorithm, &service_id).commit(), transaction_builder - .create_account_with_random_key_signed("user1@example.com", &service_id) + .create_account_with_random_key_signed(algorithm, "user1@example.com", &service_id) .commit(), transaction_builder - .create_account_with_random_key_signed("user2@example.com", &service_id) + .create_account_with_random_key_signed(algorithm, "user2@example.com", &service_id) + .commit(), + transaction_builder + .add_random_key_verified_with_root(algorithm, "user1@example.com") .commit(), - transaction_builder.add_random_key_verified_with_root("user1@example.com").commit(), ] } -#[tokio::test] -async fn test_validate_and_queue_update() { - let prover = create_test_prover().await; +async fn test_validate_and_queue_update(algorithm: CryptoAlgorithm) { + let prover = create_test_prover(algorithm).await; let mut transaction_builder = TransactionBuilder::new(); let transaction = - transaction_builder.register_service_with_random_keys("test_service").commit(); + transaction_builder.register_service_with_random_keys(algorithm, "test_service").commit(); prover.clone().validate_and_queue_update(transaction.clone()).await.unwrap(); @@ -47,15 +48,14 @@ async fn test_validate_and_queue_update() { assert_eq!(pending_transactions.len(), 2); } -#[tokio::test] -async fn test_process_transactions() { - let prover = create_test_prover().await; +async fn test_process_transactions(algorithm: CryptoAlgorithm) { + let prover = create_test_prover(algorithm).await; let mut transaction_builder = TransactionBuilder::new(); let register_service_transaction = - transaction_builder.register_service_with_random_keys("test_service").commit(); + transaction_builder.register_service_with_random_keys(algorithm, "test_service").commit(); let create_account_transaction = transaction_builder - .create_account_with_random_key_signed("test_account", "test_service") + .create_account_with_random_key_signed(algorithm, "test_account", "test_service") .commit(); let proof = prover.process_transaction(register_service_transaction).await.unwrap(); @@ -64,7 +64,7 @@ async fn test_process_transactions() { let proof = prover.process_transaction(create_account_transaction.clone()).await.unwrap(); assert!(matches!(proof, Proof::Insert(_))); - let new_key = SigningKey::new_ed25519(); + let new_key = SigningKey::new_with_algorithm(algorithm).expect("Failed to create new key"); let add_key_transaction = transaction_builder .add_key_verified_with_root("test_account", new_key.clone().into()) .commit(); @@ -85,45 +85,44 @@ async fn test_process_transactions() { assert!(matches!(proof, Proof::Update(_))); } -#[tokio::test] -async fn test_execute_block_with_invalid_tx() { - let prover = create_test_prover().await; +async fn test_execute_block_with_invalid_tx(algorithm: CryptoAlgorithm) { + let prover = create_test_prover(algorithm).await; let mut tx_builder = TransactionBuilder::new(); - let new_key_1 = SigningKey::new_ed25519(); + let new_key_1 = SigningKey::new_with_algorithm(algorithm).expect("Failed to create new key"); let new_key_vk: VerifyingKey = new_key_1.clone().into(); let transactions = vec![ - tx_builder.register_service_with_random_keys("service_id").commit(), - tx_builder.create_account_with_random_key_signed("account_id", "service_id").commit(), + tx_builder.register_service_with_random_keys(algorithm, "service_id").commit(), + tx_builder + .create_account_with_random_key_signed(algorithm, "account_id", "service_id") + .commit(), // add new key, so it will be index = 1 tx_builder.add_key_verified_with_root("account_id", new_key_vk.clone()).commit(), // revoke new key again tx_builder.revoke_key_verified_with_root("account_id", new_key_vk).commit(), // and adding in same block. // both of these transactions are valid individually, but when processed together it will fail. - tx_builder.add_random_key("account_id", &new_key_1).build(), + tx_builder.add_random_key(algorithm, "account_id", &new_key_1).build(), ]; let proofs = prover.execute_block(transactions).await.unwrap(); assert_eq!(proofs.len(), 4); } -#[tokio::test] -async fn test_execute_block() { - let prover = create_test_prover().await; +async fn test_execute_block(algorithm: CryptoAlgorithm) { + let prover = create_test_prover(algorithm).await; - let transactions = create_mock_transactions("test_service".to_string()); + let transactions = create_mock_transactions(algorithm, "test_service".to_string()); let proofs = prover.execute_block(transactions).await.unwrap(); assert_eq!(proofs.len(), 4); } -#[tokio::test] -async fn test_finalize_new_epoch() { - let prover = create_test_prover().await; - let transactions = create_mock_transactions("test_service".to_string()); +async fn test_finalize_new_epoch(algorithm: CryptoAlgorithm) { + let prover = create_test_prover(algorithm).await; + let transactions = create_mock_transactions(algorithm, "test_service".to_string()); let prev_commitment = prover.get_commitment().await.unwrap(); prover.finalize_new_epoch(0, transactions).await.unwrap(); @@ -132,13 +131,12 @@ async fn test_finalize_new_epoch() { assert_ne!(prev_commitment, new_commitment); } -#[tokio::test] -async fn test_restart_sync_from_scratch() { +async fn test_restart_sync_from_scratch(algorithm: CryptoAlgorithm) { let (da_layer, _rx, mut brx) = InMemoryDataAvailabilityLayer::new(1); let da_layer = Arc::new(da_layer); let db1: Arc> = Arc::new(Box::new(InMemoryDatabase::new())); let db2: Arc> = Arc::new(Box::new(InMemoryDatabase::new())); - let cfg = Config::default(); + let cfg = Config::default_with_key_algorithm(algorithm).unwrap(); let prover = Arc::new(Prover::new(db1.clone(), da_layer.clone(), &cfg).unwrap()); let runner = prover.clone(); @@ -146,7 +144,7 @@ async fn test_restart_sync_from_scratch() { runner.run().await.unwrap(); }); - let transactions = create_mock_transactions("test_service".to_string()); + let transactions = create_mock_transactions(algorithm, "test_service".to_string()); for transaction in transactions { prover.clone().validate_and_queue_update(transaction).await.unwrap(); @@ -176,12 +174,11 @@ async fn test_restart_sync_from_scratch() { } } -#[tokio::test] -async fn test_load_persisted_state() { +async fn test_load_persisted_state(algorithm: CryptoAlgorithm) { let (da_layer, _rx, mut brx) = InMemoryDataAvailabilityLayer::new(1); let da_layer = Arc::new(da_layer); let db: Arc> = Arc::new(Box::new(InMemoryDatabase::new())); - let cfg = Config::default(); + let cfg = Config::default_with_key_algorithm(algorithm).unwrap(); let prover = Arc::new(Prover::new(db.clone(), da_layer.clone(), &cfg).unwrap()); let runner = prover.clone(); @@ -189,7 +186,7 @@ async fn test_load_persisted_state() { runner.run().await.unwrap(); }); - let transactions = create_mock_transactions("test_service".to_string()); + let transactions = create_mock_transactions(algorithm, "test_service".to_string()); for transaction in transactions { prover.clone().validate_and_queue_update(transaction).await.unwrap(); @@ -212,3 +209,32 @@ async fn test_load_persisted_state() { prover2.get_commitment().await.unwrap() ); } + +macro_rules! generate_algorithm_tests { + ($test_fn:ident) => { + paste::paste! { + #[tokio::test] + async fn [<$test_fn _ed25519>]() { + $test_fn(CryptoAlgorithm::Ed25519).await; + } + + #[tokio::test] + async fn [<$test_fn _secp256k1>]() { + $test_fn(CryptoAlgorithm::Secp256k1).await; + } + + #[tokio::test] + async fn [<$test_fn _secp256r1>]() { + $test_fn(CryptoAlgorithm::Secp256r1).await; + } + } + }; +} + +generate_algorithm_tests!(test_validate_and_queue_update); +generate_algorithm_tests!(test_process_transactions); +generate_algorithm_tests!(test_execute_block_with_invalid_tx); +generate_algorithm_tests!(test_execute_block); +generate_algorithm_tests!(test_finalize_new_epoch); +generate_algorithm_tests!(test_restart_sync_from_scratch); +generate_algorithm_tests!(test_load_persisted_state); diff --git a/crates/storage/src/inmemory.rs b/crates/storage/src/inmemory.rs index bc7a2e00..bd744ccc 100644 --- a/crates/storage/src/inmemory.rs +++ b/crates/storage/src/inmemory.rs @@ -92,14 +92,9 @@ impl TreeWriter for InMemoryDatabase { impl Database for InMemoryDatabase { fn get_commitment(&self, epoch: &u64) -> Result { - self.commitments - .lock() - .unwrap() - .get(epoch) - .cloned() - .ok_or_else(|| { - DatabaseError::NotFoundError(format!("commitment from epoch_{}", epoch)).into() - }) + self.commitments.lock().unwrap().get(epoch).cloned().ok_or_else(|| { + DatabaseError::NotFoundError(format!("commitment from epoch_{}", epoch)).into() + }) } fn set_commitment(&self, epoch: &u64, commitment: &Digest) -> Result<()> { diff --git a/crates/tests/Cargo.toml b/crates/tests/Cargo.toml index 01e1cd9b..79826f01 100644 --- a/crates/tests/Cargo.toml +++ b/crates/tests/Cargo.toml @@ -17,10 +17,10 @@ mock_prover = [] log.workspace = true pretty_env_logger.workspace = true anyhow.workspace = true -keystore-rs.workspace = true prism-common = { workspace = true, features = ["test_utils"] } prism-storage.workspace = true prism-prover = { workspace = true, features = ["mock_prover"] } +prism-keys = { workspace = true } prism-lightclient.workspace = true prism-da.workspace = true rand.workspace = true diff --git a/crates/tests/src/lib.rs b/crates/tests/src/lib.rs index b6027b43..17c6d6ef 100644 --- a/crates/tests/src/lib.rs +++ b/crates/tests/src/lib.rs @@ -4,12 +4,12 @@ extern crate log; use anyhow::Result; -use keystore_rs::create_signing_key; use prism_common::transaction_builder::TransactionBuilder; use prism_da::{ celestia::{CelestiaConfig, CelestiaConnection}, DataAvailabilityLayer, }; +use prism_keys::{CryptoAlgorithm, SigningKey}; use prism_lightclient::LightClient; use prism_prover::Prover; use prism_storage::{rocksdb::RocksDBConnection, Database}; @@ -42,11 +42,16 @@ async fn test_light_client_prover_talking() -> Result<()> { ..CelestiaConfig::default() }; + let mut rng = StdRng::from_entropy(); + let prover_algorithm = CryptoAlgorithm::Ed25519; + let service_algorithm = random_algorithm(&mut rng); + let bridge_da_layer = Arc::new(CelestiaConnection::new(&bridge_cfg, None).await.unwrap()); let lc_da_layer = Arc::new(CelestiaConnection::new(&lc_cfg, None).await.unwrap()); let db = setup_db(); - let signing_key = create_signing_key(); - let pubkey = signing_key.verification_key(); + let signing_key = SigningKey::new_with_algorithm(prover_algorithm) + .map_err(|e| anyhow::anyhow!("Failed to generate signing key: {}", e))?; + let pubkey = signing_key.verifying_key(); let prover_cfg = prism_prover::Config { signing_key, @@ -74,11 +79,10 @@ async fn test_light_client_prover_talking() -> Result<()> { }); spawn(async move { - let mut rng = StdRng::from_entropy(); - let mut transaction_builder = TransactionBuilder::new(); - let register_service_req = - transaction_builder.register_service_with_random_keys("test_service").commit(); + let register_service_req = transaction_builder + .register_service_with_random_keys(service_algorithm, "test_service") + .commit(); let mut i = 0; @@ -91,8 +95,14 @@ async fn test_light_client_prover_talking() -> Result<()> { for _ in 0..num_new_accounts { let random_user_id = format!("{}@gmail.com", i); let new_acc = transaction_builder - .create_account_with_random_key_signed(random_user_id.as_str(), "test_service") + .create_account_with_random_key_signed( + random_algorithm(&mut rng), + random_user_id.as_str(), + "test_service", + ) .commit(); + + log::info!("builder accounts: {:?}", transaction_builder.get_account(&random_user_id)); match prover.clone().validate_and_queue_update(new_acc).await { Ok(_) => { i += 1; @@ -109,8 +119,9 @@ async fn test_light_client_prover_talking() -> Result<()> { .get(rng.gen_range(0..added_account_ids.len())) .map_or("Could not find random account id", |id| id.as_str()); - let update_acc = - transaction_builder.add_random_key_verified_with_root(acc_id).commit(); + let update_acc = transaction_builder + .add_random_key_verified_with_root(random_algorithm(&mut rng), acc_id) + .commit(); match prover.clone().validate_and_queue_update(update_acc).await { Ok(_) => (), @@ -119,7 +130,7 @@ async fn test_light_client_prover_talking() -> Result<()> { } } - tokio::time::sleep(Duration::from_millis(5000)).await; + tokio::time::sleep(Duration::from_secs(5)).await; } }); @@ -127,10 +138,18 @@ async fn test_light_client_prover_talking() -> Result<()> { let initial_height = rx.recv().await.unwrap(); while let Ok(height) = rx.recv().await { debug!("received height {}", height); - if height >= initial_height + 100 { + if height >= initial_height + 50 { break; } } Ok(()) } + +fn random_algorithm(rng: &mut StdRng) -> CryptoAlgorithm { + [ + CryptoAlgorithm::Ed25519, + CryptoAlgorithm::Secp256k1, + CryptoAlgorithm::Secp256r1, + ][rng.gen_range(0..3)] +} diff --git a/crates/tree/Cargo.toml b/crates/tree/Cargo.toml index 0005baf8..b94d46d7 100644 --- a/crates/tree/Cargo.toml +++ b/crates/tree/Cargo.toml @@ -21,5 +21,8 @@ prism-errors.workspace = true prism-keys.workspace = true prism-serde.workspace = true +[dev-dependencies] +paste = { workspace = true } + [features] default = [] diff --git a/crates/tree/src/tests.rs b/crates/tree/src/tests.rs index c5acd138..1b7a2795 100644 --- a/crates/tree/src/tests.rs +++ b/crates/tree/src/tests.rs @@ -2,26 +2,25 @@ use std::sync::Arc; use jmt::{mock::MockTreeStore, KeyHash}; use prism_common::transaction_builder::TransactionBuilder; -use prism_keys::SigningKey; +use prism_keys::{CryptoAlgorithm, SigningKey}; use crate::{ hasher::TreeHasher, key_directory_tree::KeyDirectoryTree, proofs::Proof, snarkable_tree::SnarkableTree, AccountResponse::*, }; -#[test] -fn test_insert_and_get() { +fn test_insert_and_get(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); let Proof::Insert(insert_proof) = tree.process_transaction(service_tx).unwrap() else { panic!("Processing transaction did not return the expected insert proof"); }; assert!(insert_proof.verify().is_ok()); let account_tx = - tx_builder.create_account_with_random_key_signed("acc_1", "service_1").commit(); + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").commit(); let Proof::Insert(insert_proof) = tree.process_transaction(account_tx).unwrap() else { panic!("Processing transaction did not return the expected insert proof"); @@ -40,15 +39,16 @@ fn test_insert_and_get() { assert!(membership_proof.verify_existence(&account).is_ok()); } -#[test] -fn test_insert_for_nonexistent_service_fails() { +fn test_insert_for_nonexistent_service_fails(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_signing_key = SigningKey::new_ed25519(); + let service_signing_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create service signing key"); let invalid_account_tx = tx_builder .create_account_with_random_key( + algorithm, "acc_1", "service_id_that_does_not_exist", &service_signing_key, @@ -59,18 +59,19 @@ fn test_insert_for_nonexistent_service_fails() { assert!(insertion_result.is_err()); } -#[test] -fn test_insert_with_invalid_service_challenge_fails() { +fn test_insert_with_invalid_service_challenge_fails(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); // The correct way was to use the key from service registration, // but here we want things to break - let incorrect_service_signing_key = SigningKey::new_ed25519(); + let incorrect_service_signing_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create service signing key"); - let initial_acc_signing_key = SigningKey::new_ed25519(); + let initial_acc_signing_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create account signing key"); let acc_with_invalid_challenge_tx = tx_builder .create_account( @@ -90,16 +91,15 @@ fn test_insert_with_invalid_service_challenge_fails() { assert!(create_account_result.is_err()); } -#[test] -fn test_insert_duplicate_key() { +fn test_insert_duplicate_key(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); let account_tx = - tx_builder.create_account_with_random_key_signed("acc_1", "service_1").commit(); + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").commit(); let account_with_same_id_tx = - tx_builder.create_account_with_random_key_signed("acc_1", "service_1").build(); + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").build(); let Proof::Insert(insert_proof) = tree.process_transaction(service_tx).unwrap() else { panic!("Processing service registration failed") @@ -115,18 +115,18 @@ fn test_insert_duplicate_key() { assert!(create_acc_with_same_id_result.is_err()); } -#[test] -fn test_update_existing_key() { +fn test_update_existing_key(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); - let acc_tx = tx_builder.create_account_with_random_key_signed("acc_1", "service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); + let acc_tx = + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").commit(); tree.process_transaction(service_tx).unwrap(); tree.process_transaction(acc_tx).unwrap(); - let key_tx = tx_builder.add_random_key_verified_with_root("acc_1").commit(); + let key_tx = tx_builder.add_random_key_verified_with_root(algorithm, "acc_1").commit(); let Proof::Update(update_proof) = tree.process_transaction(key_tx).unwrap() else { panic!("Processing key update failed") @@ -139,45 +139,33 @@ fn test_update_existing_key() { assert!(matches!(get_result, Found(acc, _) if *acc == *test_account)); } -#[test] -fn test_update_non_existing_key() { +fn test_update_non_existing_key(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); tree.process_transaction(service_tx).unwrap(); // This is a signing key not known to the storage yet - let random_signing_key = SigningKey::new_ed25519(); + let random_signing_key = + SigningKey::new_with_algorithm(algorithm).expect("Failed to create random signing key"); // This transaction shall be invalid, because it is signed with an unknown key - let invalid_key_tx = tx_builder.add_random_key("acc_1", &random_signing_key).build(); + let invalid_key_tx = tx_builder.add_random_key(algorithm, "acc_1", &random_signing_key).build(); let result = tree.process_transaction(invalid_key_tx); assert!(result.is_err()); } -#[test] -fn test_get_non_existing_key() { - let tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); - - let result = tree.get(KeyHash::with::("non_existing_id")).unwrap(); - - let NotFound(non_membership_proof) = result else { - panic!("Account found for key while it was expected to be missing"); - }; - - assert!(non_membership_proof.verify_nonexistence().is_ok()); -} - -#[test] -fn test_multiple_inserts_and_updates() { +fn test_multiple_inserts_and_updates(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); - let acc1_tx = tx_builder.create_account_with_random_key_signed("acc_1", "service_1").commit(); - let acc2_tx = tx_builder.create_account_with_random_key_signed("acc_2", "service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); + let acc1_tx = + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").commit(); + let acc2_tx = + tx_builder.create_account_with_random_key_signed(algorithm, "acc_2", "service_1").commit(); tree.process_transaction(service_tx).unwrap(); @@ -185,7 +173,7 @@ fn test_multiple_inserts_and_updates() { tree.process_transaction(acc2_tx).unwrap(); // Do insert and update accounts using the correct key indices - let key_1_tx = tx_builder.add_random_key_verified_with_root("acc_1").commit(); + let key_1_tx = tx_builder.add_random_key_verified_with_root(algorithm, "acc_1").commit(); tree.process_transaction(key_1_tx).unwrap(); let data_1_tx = tx_builder @@ -194,7 +182,7 @@ fn test_multiple_inserts_and_updates() { tree.process_transaction(data_1_tx).unwrap(); let data_2_tx = tx_builder - .add_randomly_signed_data_verified_with_root("acc_2", b"signed".to_vec()) + .add_randomly_signed_data_verified_with_root(algorithm, "acc_2", b"signed".to_vec()) .commit(); tree.process_transaction(data_2_tx).unwrap(); @@ -208,24 +196,25 @@ fn test_multiple_inserts_and_updates() { assert!(matches!(get_result2, Found(acc, _) if *acc == *test_acc2)); } -#[test] -fn test_interleaved_inserts_and_updates() { +fn test_interleaved_inserts_and_updates(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); - let acc1_tx = tx_builder.create_account_with_random_key_signed("acc_1", "service_1").commit(); - let acc2_tx = tx_builder.create_account_with_random_key_signed("acc_2", "service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); + let acc1_tx = + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").commit(); + let acc2_tx = + tx_builder.create_account_with_random_key_signed(algorithm, "acc_2", "service_1").commit(); tree.process_transaction(service_tx).unwrap(); tree.process_transaction(acc1_tx).unwrap(); - let add_key_to_1_tx = tx_builder.add_random_key_verified_with_root("acc_1").commit(); + let add_key_to_1_tx = tx_builder.add_random_key_verified_with_root(algorithm, "acc_1").commit(); tree.process_transaction(add_key_to_1_tx).unwrap(); tree.process_transaction(acc2_tx).unwrap(); - let add_key_to_2_tx = tx_builder.add_random_key_verified_with_root("acc_2").commit(); + let add_key_to_2_tx = tx_builder.add_random_key_verified_with_root(algorithm, "acc_2").commit(); let last_proof = tree.process_transaction(add_key_to_2_tx).unwrap(); // Update account_2 using the correct key index @@ -244,14 +233,13 @@ fn test_interleaved_inserts_and_updates() { assert_eq!(update_proof.new_root, tree.get_commitment().unwrap()); } -#[test] -fn test_root_hash_changes() { +fn test_root_hash_changes(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); let account1_tx = - tx_builder.create_account_with_random_key_signed("acc_1", "service_1").commit(); + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").commit(); tree.process_transaction(service_tx).unwrap(); @@ -262,16 +250,15 @@ fn test_root_hash_changes() { assert_ne!(root_before, root_after); } -#[test] -fn test_batch_writing() { +fn test_batch_writing(algorithm: CryptoAlgorithm) { let mut tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); let mut tx_builder = TransactionBuilder::new(); - let service_tx = tx_builder.register_service_with_random_keys("service_1").commit(); + let service_tx = tx_builder.register_service_with_random_keys(algorithm, "service_1").commit(); let account1_tx = - tx_builder.create_account_with_random_key_signed("acc_1", "service_1").commit(); + tx_builder.create_account_with_random_key_signed(algorithm, "acc_1", "service_1").commit(); let account2_tx = - tx_builder.create_account_with_random_key_signed("acc_2", "service_1").commit(); + tx_builder.create_account_with_random_key_signed(algorithm, "acc_2", "service_1").commit(); tree.process_transaction(service_tx).unwrap(); @@ -302,3 +289,48 @@ fn test_batch_writing() { assert!(matches!(get_result1, Found(acc, _) if *acc == *test_acc1)); assert!(matches!(get_result2, Found(acc, _) if *acc == *test_acc2)); } + +macro_rules! generate_algorithm_tests { + ($test_fn:ident) => { + paste::paste! { + #[test] + fn [<$test_fn _ed25519>]() { + $test_fn(CryptoAlgorithm::Ed25519); + } + + #[test] + fn [<$test_fn _secp256k1>]() { + $test_fn(CryptoAlgorithm::Secp256k1); + } + + #[test] + fn [<$test_fn _secp256r1>]() { + $test_fn(CryptoAlgorithm::Secp256r1); + } + } + }; +} + +generate_algorithm_tests!(test_insert_and_get); +generate_algorithm_tests!(test_insert_for_nonexistent_service_fails); +generate_algorithm_tests!(test_insert_with_invalid_service_challenge_fails); +generate_algorithm_tests!(test_insert_duplicate_key); +generate_algorithm_tests!(test_update_existing_key); +generate_algorithm_tests!(test_update_non_existing_key); +generate_algorithm_tests!(test_multiple_inserts_and_updates); +generate_algorithm_tests!(test_interleaved_inserts_and_updates); +generate_algorithm_tests!(test_root_hash_changes); +generate_algorithm_tests!(test_batch_writing); + +#[test] +fn test_get_non_existing_key() { + let tree = KeyDirectoryTree::new(Arc::new(MockTreeStore::default())); + + let result = tree.get(KeyHash::with::("non_existing_id")).unwrap(); + + let NotFound(non_membership_proof) = result else { + panic!("Account found for key while it was expected to be missing"); + }; + + assert!(non_membership_proof.verify_nonexistence().is_ok()); +} diff --git a/elf/riscv32im-succinct-zkvm-elf b/elf/riscv32im-succinct-zkvm-elf index dbcc7981..f528a390 100755 Binary files a/elf/riscv32im-succinct-zkvm-elf and b/elf/riscv32im-succinct-zkvm-elf differ diff --git a/justfile b/justfile index ac1c6715..9773824f 100644 --- a/justfile +++ b/justfile @@ -67,14 +67,13 @@ integration-test: #!/usr/bin/env bash set -euo pipefail - just celestia-up + for curve in ed25519 secp256k1 secp256r1; do + just celestia-up - echo "Running integration tests..." - if ! cargo test -p prism-tests --lib --release --features mock_prover; then - echo "Integration tests failed." - fi + cargo test -p prism-tests --lib --release --features mock_prover - just celestia-down + just celestia-down + done check: @echo "Running cargo udeps..."