fedimint_mintv2_client/
issuance.rs1use 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
48pub 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
75pub fn check_nonce(secret: &OutputSecret, nonce_hash: hash160::Hash) -> bool {
78 blinded_message(secret).consensus_hash::<hash160::Hash>() == nonce_hash
79}
80
81pub 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}