diff --git a/chain-signatures/node/src/protocol/presignature.rs b/chain-signatures/node/src/protocol/presignature.rs index faa459c2d..093990ecd 100644 --- a/chain-signatures/node/src/protocol/presignature.rs +++ b/chain-signatures/node/src/protocol/presignature.rs @@ -9,6 +9,7 @@ use cait_sith::protocol::{Action, InitializationError, Participant, ProtocolErro use cait_sith::{KeygenOutput, PresignArguments, PresignOutput}; use chrono::Utc; use crypto_shared::PublicKey; +use k256::elliptic_curve::CurveArithmetic; use k256::Secp256k1; use mpc_contract::config::ProtocolConfig; use serde::ser::SerializeStruct; @@ -26,12 +27,27 @@ use near_account_id::AccountId; pub type PresignatureId = u64; /// A completed presignature. +#[derive(Clone)] pub struct Presignature { pub id: PresignatureId, pub output: PresignOutput, pub participants: Vec, } +impl Default for Presignature { + fn default() -> Self { + Self { + id: 1, + output: PresignOutput { + big_r: ::AffinePoint::default(), + k: ::Scalar::ZERO, + sigma: ::Scalar::ONE, + }, + participants: vec![Participant::from(1), Participant::from(2)], + } + } +} + impl Serialize for Presignature { fn serialize(&self, serializer: S) -> Result where @@ -718,22 +734,11 @@ const fn first_8_bytes(input: [u8; 32]) -> [u8; 8] { #[cfg(test)] mod tests { - use cait_sith::{protocol::Participant, PresignOutput}; - use k256::{elliptic_curve::CurveArithmetic, Secp256k1}; - use crate::protocol::presignature::Presignature; #[tokio::test] async fn test_presignature_serialize_deserialize() { - let presignature = Presignature { - id: 1, - output: PresignOutput { - big_r: ::AffinePoint::default(), - k: ::Scalar::ZERO, - sigma: ::Scalar::ONE, - }, - participants: vec![Participant::from(1), Participant::from(2)], - }; + let presignature = Presignature::default(); // Serialize Presignature to JSON let serialized = diff --git a/integration-tests/README.md b/integration-tests/README.md index c14113225..ff947addb 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -7,6 +7,7 @@ Running integration tests requires you to have relayer and sandbox docker images ```BASH docker pull ghcr.io/near/os-relayer docker pull ghcr.io/near/sandbox +docker pull redis:7.2.5 ``` For M1 you may want to pull the following image instead: diff --git a/integration-tests/chain-signatures/src/containers.rs b/integration-tests/chain-signatures/src/containers.rs index 8f058b4b4..6275f2c58 100644 --- a/integration-tests/chain-signatures/src/containers.rs +++ b/integration-tests/chain-signatures/src/containers.rs @@ -633,7 +633,7 @@ impl<'a> Redis<'a> { pub const CONTAINER_CONFIG_PATH: &'static str = "/usr/local/etc/redis/redis.conf"; pub async fn run(docker_client: &'a DockerClient, network: &str) -> anyhow::Result> { - tracing::info!("Running Redis container with hardcoded config..."); + tracing::info!("Running Redis container..."); let image = GenericImage::new("redis", "7.2.5") .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections")) @@ -660,10 +660,7 @@ impl<'a> Redis<'a> { let full_address = format!("redis://{}:{}/", ip_address, Self::CONTAINER_PORT); let local_address = format!("redis://127.0.0.1:{}/", host_port); - tracing::info!( - "Redis container is running with hardcoded config at {}", - full_address - ); + tracing::info!("Redis container is running at {}", full_address); Ok(Redis { container, diff --git a/integration-tests/chain-signatures/tests/cases/mod.rs b/integration-tests/chain-signatures/tests/cases/mod.rs index 2cb2cad70..ee64a7ed7 100644 --- a/integration-tests/chain-signatures/tests/cases/mod.rs +++ b/integration-tests/chain-signatures/tests/cases/mod.rs @@ -1,8 +1,10 @@ use std::str::FromStr; +use std::sync::Arc; use crate::actions::{self, add_latency, wait_for}; use crate::with_multichain_nodes; +use cait_sith::protocol::Participant; use crypto_shared::{self, derive_epsilon, derive_key, x_coordinate, ScalarExt}; use integration_tests_chain_signatures::containers::{self, DockerClient}; use integration_tests_chain_signatures::MultichainConfig; @@ -10,10 +12,15 @@ use k256::elliptic_curve::point::AffineCoordinates; use mpc_contract::config::Config; use mpc_contract::update::ProposeUpdateArgs; use mpc_node::kdf::into_eth_sig; -use mpc_node::test_utils; +use mpc_node::protocol::presignature::{Presignature, PresignatureManager}; +use mpc_node::storage::presignature_storage::LockPresignatureStorageBox; use mpc_node::types::LatestBlockHeight; use mpc_node::util::NearPublicKeyExt; +use mpc_node::{storage, test_utils}; +use near_account_id::AccountId; use test_log::test; +use tokio::sync::RwLock; +use url::Url; pub mod nightly; @@ -226,6 +233,70 @@ async fn test_triples_persistence_for_deletion() -> anyhow::Result<()> { Ok(()) } +#[test(tokio::test)] +async fn test_presignature_persistence() -> anyhow::Result<()> { + let docker_client = DockerClient::default(); + let docker_network = "test-presignature-persistence"; + docker_client.create_network(docker_network).await?; + let redis = containers::Redis::run(&docker_client, docker_network).await?; + let redis_url = Url::parse(redis.local_address.as_str())?; + let presignature_storage: LockPresignatureStorageBox = + Arc::new(RwLock::new(storage::presignature_storage::init(redis_url))); + let mut presignature_manager = PresignatureManager::new( + Participant::from(0), + 5, + 123, + &AccountId::from_str("test.near").unwrap(), + presignature_storage, + ); + + let presignature = Presignature::default(); + + // Check that the storage is empty at the start + assert!(!presignature_manager.contains(&presignature.id).await); + assert!(!presignature_manager.contains_mine(&presignature.id).await); + assert_eq!(presignature_manager.count_all().await, 0); + assert_eq!(presignature_manager.count_mine().await, 0); + assert!(presignature_manager.is_empty().await); + assert_eq!(presignature_manager.count_potential().await, 0); + + presignature_manager.insert(presignature.clone()).await; + + // Check that the storage contains the foreign presignature + assert!(presignature_manager.contains(&presignature.id).await); + assert!(!presignature_manager.contains_mine(&presignature.id).await); + assert_eq!(presignature_manager.count_all().await, 1); + assert_eq!(presignature_manager.count_mine().await, 0); + assert_eq!(presignature_manager.count_potential().await, 1); + + // Take presignature and check that it is removed from the storage + presignature_manager.take(presignature.id).await.unwrap(); + assert!(!presignature_manager.contains(&presignature.id).await); + assert!(!presignature_manager.contains_mine(&presignature.id).await); + assert_eq!(presignature_manager.count_all().await, 0); + assert_eq!(presignature_manager.count_mine().await, 0); + assert_eq!(presignature_manager.count_potential().await, 0); + + // Add mine presignature and check that it is in the storage + presignature_manager.insert_mine(presignature.clone()).await; + assert!(presignature_manager.contains(&presignature.id).await); + assert!(presignature_manager.contains_mine(&presignature.id).await); + assert_eq!(presignature_manager.count_all().await, 1); + assert_eq!(presignature_manager.count_mine().await, 1); + assert_eq!(presignature_manager.count_potential().await, 1); + + // Take mine presignature and check that it is removed from the storage + presignature_manager.take_mine().await.unwrap(); + assert!(!presignature_manager.contains(&presignature.id).await); + assert!(!presignature_manager.contains_mine(&presignature.id).await); + assert_eq!(presignature_manager.count_all().await, 0); + assert_eq!(presignature_manager.count_mine().await, 0); + assert!(presignature_manager.is_empty().await); + assert_eq!(presignature_manager.count_potential().await, 0); + + Ok(()) +} + #[test(tokio::test)] async fn test_latest_block_height() -> anyhow::Result<()> { with_multichain_nodes(MultichainConfig::default(), |ctx| {