Skip to main content

fedimint_mintv2_client/
issuance.rs

1use bitcoin_hashes::{Hash, hash160, sha256};
2use fedimint_core::encoding::{Decodable, Encodable};
3use fedimint_core::secp256k1::rand::Rng;
4use fedimint_core::secp256k1::{Keypair, PublicKey, SECP256K1};
5use fedimint_derive_secret::{ChildId, DerivableSecret};
6use fedimint_mintv2_common::{Denomination, MintOutput, nonce_message};
7use tbs::{BlindedMessage, BlindedSignature, BlindingKey, blind_message, unblind_signature};
8
9use crate::{SpendableNote, thread_rng};
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Encodable, Decodable)]
12pub struct NoteIssuanceRequest {
13    pub denomination: Denomination,
14    pub tweak: [u8; 16],
15    pub keypair: Keypair,
16    pub blinding_key: BlindingKey,
17}
18
19impl NoteIssuanceRequest {
20    pub fn new(denomination: Denomination, tweak: [u8; 16], root_secret: &DerivableSecret) -> Self {
21        let secret = output_secret(denomination, tweak, root_secret);
22
23        Self {
24            denomination,
25            tweak,
26            keypair: keypair(&secret),
27            blinding_key: blinding_key(&secret),
28        }
29    }
30
31    pub fn output(&self) -> MintOutput {
32        MintOutput::new_v0(self.denomination, self.blinded_message(), self.tweak)
33    }
34
35    pub fn finalize(&self, signature: BlindedSignature) -> SpendableNote {
36        SpendableNote {
37            denomination: self.denomination,
38            keypair: self.keypair,
39            signature: unblind_signature(self.blinding_key, signature),
40        }
41    }
42
43    pub fn blinded_message(&self) -> BlindedMessage {
44        blind_message(nonce_message(self.keypair.public_key()), self.blinding_key)
45    }
46}
47
48// ============ Grinding Functions ============
49
50pub fn tweak_filter(root_secret: &DerivableSecret) -> [u8; 32] {
51    root_secret.to_random_bytes()
52}
53
54pub fn grind_tweak(root_secret: &DerivableSecret) -> [u8; 16] {
55    let filter = tweak_filter(root_secret);
56
57    loop {
58        let tweak = thread_rng().r#gen();
59
60        if check_tweak(tweak, filter) {
61            return tweak;
62        }
63    }
64}
65
66pub fn check_tweak(tweak: [u8; 16], seed: [u8; 32]) -> bool {
67    (tweak, seed)
68        .consensus_hash::<sha256::Hash>()
69        .to_byte_array()
70        .iter()
71        .take(2)
72        .all(|b| *b == 0)
73}
74
75// ============ Validation Functions ============
76
77pub fn check_nonce(secret: &OutputSecret, nonce_hash: hash160::Hash) -> bool {
78    blinded_message(secret).consensus_hash::<hash160::Hash>() == nonce_hash
79}
80
81// ============ Core Crypto Functions ============
82
83pub struct OutputSecret(DerivableSecret);
84
85pub fn output_secret(
86    denomination: Denomination,
87    tweak: [u8; 16],
88    root: &DerivableSecret,
89) -> OutputSecret {
90    OutputSecret(
91        root.child_key(ChildId(u64::from(denomination.0)))
92            .tweak(&tweak),
93    )
94}
95
96fn keypair(secret: &OutputSecret) -> Keypair {
97    secret.0.clone().to_secp_key(SECP256K1)
98}
99
100pub fn nonce(secret: &OutputSecret) -> PublicKey {
101    keypair(secret).public_key()
102}
103
104fn blinding_key(secret: &OutputSecret) -> BlindingKey {
105    BlindingKey(secret.0.to_bls12_381_key())
106}
107
108pub fn blinded_message(secret: &OutputSecret) -> BlindedMessage {
109    blind_message(nonce_message(nonce(secret)), blinding_key(secret))
110}