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
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ tracing-subscriber = { version = "0.3.19", default-features = false, features =

# Crates used only for testing
fake = { version = "3.1.0", default-features = false, features = ["derive", "time"] }
madhouse = { git = "https://github.com/stacks-network/madhouse-rs.git", tag = "0.2.1", default-features = false }
mockall = { version = "0.13.1", default-features = false }
mockito = { version = "1.6.1", default-features = false }
more-asserts = { version = "0.3.1", default-features = false }
Expand Down
2 changes: 2 additions & 0 deletions signer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,10 @@ tonic-build.workspace = true

[dev-dependencies]
bitcoincore-rpc.workspace = true
madhouse.workspace = true
mockito.workspace = true
more-asserts.workspace = true
proptest.workspace = true
ripemd.workspace = true
sbtc = { workspace = true, features = ["testing"] }
# We need this so that we have access to "testing" feature code in our
Expand Down
40 changes: 40 additions & 0 deletions signer/tests/integration/commands/context.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::sync::Arc;

use madhouse::{State, TestContext};
use signer::{
storage::{model::EncryptedDkgShares, postgres::PgStore},
testing::storage::new_test_database,
};
use tokio::runtime::Runtime;

#[derive(Clone, Debug)]
pub struct Ctx {
db: PgStore,
runtime: Arc<Runtime>,
}

impl Ctx {
pub fn new() -> Self {
let runtime = Arc::new(Runtime::new().unwrap());
let db = runtime.block_on(async { new_test_database().await });

Self { db, runtime }
}

pub fn db(&self) -> &PgStore {
&self.db
}

pub fn runtime_handle(&self) -> Arc<Runtime> {
self.runtime.clone()
}
}

impl TestContext for Ctx {}

#[derive(Debug, Default)]
pub struct TestState {
pub shares: Option<EncryptedDkgShares>,
}

impl State for TestState {}
91 changes: 91 additions & 0 deletions signer/tests/integration/commands/dkg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::sync::Arc;

use fake::Fake as _;
use fake::Faker;
use madhouse::{Command, CommandWrapper};
use proptest::prelude::Just;
use proptest::prelude::Strategy;
use proptest::prelude::any;
use rand::SeedableRng;
use secp256k1::{Keypair, rand::rngs::StdRng};
use signer::storage::DbWrite as _;
use signer::{
keys::PublicKey,
storage::model::{DkgSharesStatus, EncryptedDkgShares},
};

use super::{Ctx, TestState};

pub struct CreateFailedDkgShares {
seed: u64,
}

impl CreateFailedDkgShares {
fn new(seed: u64) -> Self {
Self { seed }
}
}

impl Command<TestState, Ctx> for CreateFailedDkgShares {
fn check(&self, _state: &TestState) -> bool {
true
}

fn apply(&self, state: &mut TestState) {
let mut rng = StdRng::seed_from_u64(self.seed);
let aggregate_key: PublicKey = Keypair::new_global(&mut rng).public_key().into();
let shares = EncryptedDkgShares {
aggregate_key,
dkg_shares_status: DkgSharesStatus::Failed,
started_at_bitcoin_block_height: 0u64.into(),
..Faker.fake_with_rng(&mut rng)
};

state.shares = Some(shares);
}

fn label(&self) -> String {
"CREATE_FAILED_DKG_SHARES".to_string()
}

fn build(_ctx: Arc<Ctx>) -> impl Strategy<Value = CommandWrapper<TestState, Ctx>> {
any::<u64>().prop_map(|seed| CommandWrapper::new(CreateFailedDkgShares::new(seed)))
}
}

pub struct WriteDkgShares {
ctx: Arc<Ctx>,
}

impl WriteDkgShares {
pub fn new(ctx: Arc<Ctx>) -> Self {
Self { ctx }
}
}

impl Command<TestState, Ctx> for WriteDkgShares {
fn check(&self, state: &TestState) -> bool {
state.shares.is_some()
}

fn apply(&self, state: &mut TestState) {
let shares = state.shares.as_ref().unwrap();

self.ctx.runtime_handle().block_on(async {
match self.ctx.db().write_encrypted_dkg_shares(shares).await {
Ok(db_result) => db_result,
Err(e) => {
panic!("Failed to write DKG shares: {}", e);
}
};
});
}

fn label(&self) -> String {
"WRITE_DKG_SHARES".to_string()
}

fn build(ctx: Arc<Ctx>) -> impl Strategy<Value = CommandWrapper<TestState, Ctx>> {
Just(CommandWrapper::new(WriteDkgShares::new(ctx.clone())))
}
}
7 changes: 7 additions & 0 deletions signer/tests/integration/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod context;
mod dkg;
mod test_cases;

pub use context::{Ctx, TestState};
pub use dkg::{CreateFailedDkgShares, WriteDkgShares};
pub use test_cases::VerifyDkgVerificationFailed;
52 changes: 52 additions & 0 deletions signer/tests/integration/commands/test_cases.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::sync::Arc;

use madhouse::{Command, CommandWrapper};
use proptest::prelude::{Just, Strategy};
use signer::{error::Error, keys::PublicKeyXOnly, storage::model::DkgSharesStatus};

use crate::transaction_signer::validate_dkg_verification_message::TestParams;

use super::{Ctx, TestState};

pub struct VerifyDkgVerificationFailed {
ctx: Arc<Ctx>,
}

impl VerifyDkgVerificationFailed {
pub fn new(ctx: Arc<Ctx>) -> Self {
Self { ctx }
}
}

impl Command<TestState, Ctx> for VerifyDkgVerificationFailed {
fn check(&self, state: &TestState) -> bool {
state.shares.is_some()
&& state.shares.as_ref().unwrap().dkg_shares_status == DkgSharesStatus::Failed
}

fn apply(&self, state: &mut TestState) {
let aggregate_key = state.shares.as_ref().unwrap().aggregate_key.clone();
let aggregate_key_x_only: PublicKeyXOnly = aggregate_key.into();
let params = TestParams::new(aggregate_key_x_only);

let result = self
.ctx
.runtime_handle()
.block_on(async { params.execute(self.ctx.db()).await.unwrap_err() });

assert!(matches!(
result,
Error::DkgVerificationFailed(key) if aggregate_key_x_only == key
))
}

fn label(&self) -> String {
"VERIFY_DKG_VERIFICATION_FAILED".to_string()
}

fn build(ctx: Arc<Ctx>) -> impl Strategy<Value = CommandWrapper<TestState, Ctx>> {
Just(CommandWrapper::new(VerifyDkgVerificationFailed::new(
ctx.clone(),
)))
}
}
1 change: 1 addition & 0 deletions signer/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod bitcoin_client;
mod bitcoin_rpc;
mod bitcoin_validation;
mod block_observer;
mod commands;
mod communication;
mod complete_deposit;
mod contracts;
Expand Down
29 changes: 25 additions & 4 deletions signer/tests/integration/transaction_signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ async fn max_one_state_machine_per_bitcoin_block_hash_for_dkg() {
/// [`MockedTxSigner::validate_dkg_verification_message`] function. See
/// [`MockedTxSigner`] for information on the validations that these tests
/// are asserting.
mod validate_dkg_verification_message {
pub mod validate_dkg_verification_message {
use secp256k1::Keypair;

use signer::{
Expand All @@ -733,7 +733,8 @@ mod validate_dkg_verification_message {

/// Helper struct for testing
/// [`MockedTxSigner::validate_dkg_verification_message`].
struct TestParams {
#[derive(Debug)]
pub struct TestParams {
pub new_aggregate_key: PublicKeyXOnly,
pub dkg_verification_window: u16,
pub bitcoin_chain_tip: BitcoinBlockRef,
Expand All @@ -756,15 +757,15 @@ mod validate_dkg_verification_message {
}

impl TestParams {
fn new(new_aggregate_key: PublicKeyXOnly) -> Self {
pub fn new(new_aggregate_key: PublicKeyXOnly) -> Self {
Self {
new_aggregate_key,
..Self::default()
}
}
/// Executes [`MockedTxSigner::validate_dkg_verification_message`] with
/// the values in this [`TestParams`] instance.
async fn execute(&self, db: &PgStore) -> Result<(), Error> {
pub async fn execute(&self, db: &PgStore) -> Result<(), Error> {
MockedTxSigner::validate_dkg_verification_message::<PgStore>(
&db,
&self.new_aggregate_key,
Expand Down Expand Up @@ -850,6 +851,26 @@ mod validate_dkg_verification_message {
))
}

use std::sync::Arc;

use crate::commands::{
CreateFailedDkgShares, Ctx, VerifyDkgVerificationFailed, WriteDkgShares,
};
use madhouse::prelude::*;
use proptest::prelude::*;

#[test]
fn latest_key_in_failed_state_scenario() {
let test_context = Arc::new(Ctx::new());

scenario![
test_context,
CreateFailedDkgShares,
WriteDkgShares,
VerifyDkgVerificationFailed,
]
}

#[tokio::test]
async fn verification_window_elapsed() {
let db = testing::storage::new_test_database().await;
Expand Down
16 changes: 0 additions & 16 deletions supply-chain/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ criteria = "safe-to-deploy"
version = "1.1.3"
criteria = "safe-to-deploy"

[[exemptions.android-tzdata]]
version = "0.1.1"
criteria = "safe-to-deploy"

[[exemptions.anstyle]]
version = "1.0.10"
criteria = "safe-to-deploy"
Expand Down Expand Up @@ -525,10 +521,6 @@ criteria = "safe-to-deploy"
version = "0.1.9"
criteria = "safe-to-deploy"

[[exemptions.fastrand]]
version = "2.3.0"
criteria = "safe-to-deploy"

[[exemptions.finl_unicode]]
version = "1.2.0"
criteria = "safe-to-deploy"
Expand Down Expand Up @@ -1429,10 +1421,6 @@ criteria = "safe-to-deploy"
version = "0.2.3"
criteria = "safe-to-deploy"

[[exemptions.rustc_version]]
version = "0.4.0"
criteria = "safe-to-deploy"

[[exemptions.rustfmt-wrapper]]
version = "0.2.1"
criteria = "safe-to-deploy"
Expand Down Expand Up @@ -1817,10 +1805,6 @@ criteria = "safe-to-deploy"
version = "0.1.2"
criteria = "safe-to-deploy"

[[exemptions.tinyvec_macros]]
version = "0.1.1"
criteria = "safe-to-deploy"

[[exemptions.tokio]]
version = "1.43.0"
criteria = "safe-to-deploy"
Expand Down
Loading