use assert_matches::assert_matches;
use fedimint_core::config::{ClientModuleConfig, ServerModuleConfig};
use fedimint_core::db::Database;
use fedimint_core::db::mem_impl::MemDatabase;
use fedimint_core::module::ModuleConsensusVersion;
use fedimint_core::module::registry::ModuleRegistry;
use fedimint_core::{Amount, BitcoinHash, InPoint, PeerId, TransactionId, secp256k1};
use fedimint_mint_common::config::FeeConsensus;
use fedimint_mint_common::{MintInput, Nonce, Note};
use fedimint_server_core::{ConfigGenModuleArgs, ServerModule, ServerModuleInit};
use tbs::blind_message;
use crate::{Mint, MintConfig, MintConfigConsensus, MintConfigPrivate, MintInit};
const MINTS: u16 = 5;
fn build_configs() -> (Vec<ServerModuleConfig>, ClientModuleConfig) {
let peers = (0..MINTS).map(PeerId::from).collect::<Vec<_>>();
let args = ConfigGenModuleArgs {
network: bitcoin::Network::Regtest,
disable_base_fees: false,
};
let mint_cfg = MintInit.trusted_dealer_gen(&peers, &args);
let client_cfg = ClientModuleConfig::from_typed(
0,
MintInit::kind(),
ModuleConsensusVersion::new(0, 0),
MintInit
.get_client_config(&mint_cfg[&PeerId::from(0)].consensus)
.unwrap(),
)
.unwrap();
(mint_cfg.into_values().collect(), client_cfg)
}
#[test_log::test]
#[should_panic(expected = "Own key not found among pub keys.")]
fn test_new_panic_without_own_pub_key() {
let (mint_server_cfg1, _) = build_configs();
let (mint_server_cfg2, _) = build_configs();
Mint::new(MintConfig {
consensus: MintConfigConsensus {
peer_tbs_pks: mint_server_cfg2[0]
.to_typed::<MintConfig>()
.unwrap()
.consensus
.peer_tbs_pks,
fee_consensus: FeeConsensus::new(1000).expect("Relative fee is within range"),
max_notes_per_denomination: 0,
},
private: MintConfigPrivate {
tbs_sks: mint_server_cfg1[0]
.to_typed::<MintConfig>()
.unwrap()
.private
.tbs_sks,
},
});
}
fn issue_note(
server_cfgs: &[ServerModuleConfig],
denomination: Amount,
) -> (secp256k1::Keypair, Note) {
let note_key = secp256k1::Keypair::new(secp256k1::SECP256K1, &mut rand::thread_rng());
let nonce = Nonce(note_key.public_key());
let message = nonce.to_message();
let blinding_key = tbs::BlindingKey::random();
let blind_msg = blind_message(message, blinding_key);
let bsig_shares = (0_u64..)
.zip(server_cfgs.iter().map(|cfg| {
let sks = *cfg
.to_typed::<MintConfig>()
.unwrap()
.private
.tbs_sks
.get(denomination)
.expect("Mint cannot issue a note of this denomination");
tbs::sign_message(blind_msg, sks)
}))
.take(server_cfgs.len() - ((server_cfgs.len() - 1) / 3))
.collect();
let blind_signature = tbs::aggregate_signature_shares(&bsig_shares);
let signature = tbs::unblind_signature(blinding_key, blind_signature);
(note_key, Note { nonce, signature })
}
#[test_log::test(tokio::test)]
async fn test_detect_double_spends() {
let (mint_server_cfg, _) = build_configs();
let mint = Mint::new(mint_server_cfg[0].to_typed().unwrap());
let (_, tiered) = mint
.cfg
.consensus
.peer_tbs_pks
.first_key_value()
.expect("mint has peers");
let highest_denomination = *tiered.max_tier();
let (_, note) = issue_note(&mint_server_cfg, highest_denomination);
let db = Database::new(MemDatabase::new(), ModuleRegistry::default());
let input = MintInput::new_v0(highest_denomination, note);
let mut dbtx = db.begin_transaction_nc().await;
mint.process_input(
&mut dbtx.to_ref_with_prefix_module_id(42).0.into_nc(),
&input,
InPoint {
txid: TransactionId::all_zeros(),
in_idx: 0,
},
)
.await
.expect("Spend of valid e-cash works");
assert_matches!(
mint.process_input(
&mut dbtx.to_ref_with_prefix_module_id(42).0.into_nc(),
&input,
InPoint {
txid: TransactionId::all_zeros(),
in_idx: 0
},
)
.await,
Err(_)
);
}