1use std::io::Write as _;
2
3use bitcoin::hashes::{sha256, Hash};
4use bitcoin::key::Keypair;
5use bitcoin::secp256k1::{self, schnorr, Message};
6
7use crate::{OffboardRequest, SignedVtxoRequest, Vtxo, VtxoId, SECP};
8use crate::encode::ProtocolEncoding;
9use crate::lightning::PaymentHash;
10
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct RoundAttemptChallenge([u8; 32]);
14
15impl RoundAttemptChallenge {
16 const CHALLENGE_MESSAGE_PREFIX: &'static [u8; 32] = b"Ark round input ownership proof ";
17
18 pub fn new(value: [u8; 32]) -> Self {
19 Self(value)
20 }
21
22 pub fn generate() -> Self {
23 Self(rand::random())
24 }
25
26 pub fn inner(&self) -> [u8; 32] {
27 self.0
28 }
29
30 fn as_signable_message(
34 &self,
35 vtxo_id: VtxoId,
36 vtxo_reqs: &[SignedVtxoRequest],
37 offboard_reqs: &[OffboardRequest],
38 ) -> Message {
39 let mut engine = sha256::Hash::engine();
40 engine.write_all(Self::CHALLENGE_MESSAGE_PREFIX).unwrap();
41 engine.write_all(&self.0).unwrap();
42 engine.write_all(&vtxo_id.to_bytes()).unwrap();
43
44 engine.write_all(&vtxo_reqs.len().to_be_bytes()).unwrap();
45 for req in vtxo_reqs {
46 engine.write_all(&req.vtxo.amount.to_sat().to_be_bytes()).unwrap();
47 req.vtxo.policy.encode(&mut engine).unwrap();
48 req.cosign_pubkey.encode(&mut engine).unwrap();
49 }
50
51 engine.write_all(&offboard_reqs.len().to_be_bytes()).unwrap();
52 for req in offboard_reqs {
53 req.to_txout().encode(&mut engine).unwrap();
54 }
55 let hash = sha256::Hash::from_engine(engine).to_byte_array();
56 Message::from_digest(hash)
57 }
58
59 pub fn sign_with(
60 &self,
61 vtxo_id: VtxoId,
62 vtxo_reqs: &[SignedVtxoRequest],
63 offboard_reqs: &[OffboardRequest],
64 vtxo_keypair: Keypair,
65 ) -> schnorr::Signature {
66 let msg = self.as_signable_message(vtxo_id, vtxo_reqs, offboard_reqs);
67 SECP.sign_schnorr_with_aux_rand(&msg, &vtxo_keypair, &rand::random())
68 }
69
70 pub fn verify_input_vtxo_sig(
71 &self,
72 vtxo: &Vtxo,
73 vtxo_reqs: &[SignedVtxoRequest],
74 offboard_reqs: &[OffboardRequest],
75 sig: &schnorr::Signature,
76 ) -> Result<(), secp256k1::Error> {
77 let msg = self.as_signable_message(vtxo.id(), vtxo_reqs, offboard_reqs);
78 SECP.verify_schnorr( sig, &msg, &vtxo.user_pubkey().x_only_public_key().0)
79 }
80}
81
82#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
88pub struct LightningReceiveChallenge(PaymentHash);
89
90impl LightningReceiveChallenge {
91 const CHALLENGE_MESSAGE_PREFIX: &'static [u8; 32] = b"Lightning receive VTXO challenge";
92
93 pub fn new(value: PaymentHash) -> Self {
94 Self(value)
95 }
96
97 fn as_signable_message(&self, vtxo_id: VtxoId) -> Message {
101 let mut engine = sha256::Hash::engine();
102 engine.write_all(Self::CHALLENGE_MESSAGE_PREFIX).unwrap();
103 engine.write_all(&self.0.to_byte_array()).unwrap();
104 engine.write_all(&vtxo_id.to_bytes()).unwrap();
105
106 let hash = sha256::Hash::from_engine(engine).to_byte_array();
107 Message::from_digest(hash)
108 }
109
110 pub fn sign_with(
111 &self,
112 vtxo_id: VtxoId,
113 vtxo_keypair: Keypair,
114 ) -> schnorr::Signature {
115 SECP.sign_schnorr_with_aux_rand(
116 &Self::as_signable_message(self, vtxo_id),
117 &vtxo_keypair,
118 &rand::random()
119 )
120 }
121
122 pub fn verify_input_vtxo_sig(
123 &self,
124 vtxo: &Vtxo,
125 sig: &schnorr::Signature,
126 ) -> Result<(), secp256k1::Error> {
127 SECP.verify_schnorr(
128 sig,
129 &Self::as_signable_message(self, vtxo.id()),
130 &vtxo.user_pubkey().x_only_public_key().0,
131 )
132 }
133}
134
135#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
149pub struct VtxoStatusChallenge;
150
151impl VtxoStatusChallenge {
152 const CHALLENGE_MESSAGE_PREFIX: &'static [u8; 32] = b"Ark VTXO status query challenge ";
153
154 pub fn new() -> Self {
155 Self
156 }
157
158 fn as_signable_message(&self, vtxo_id: VtxoId) -> Message {
159 let mut engine = sha256::Hash::engine();
160 engine.write_all(Self::CHALLENGE_MESSAGE_PREFIX).unwrap();
161 engine.write_all(&vtxo_id.to_bytes()).unwrap();
162
163 let hash = sha256::Hash::from_engine(engine).to_byte_array();
164 Message::from_digest(hash)
165 }
166
167 pub fn sign_with(
168 &self,
169 vtxo_id: VtxoId,
170 vtxo_keypair: Keypair,
171 ) -> schnorr::Signature {
172 SECP.sign_schnorr_with_aux_rand(
173 &Self::as_signable_message(self, vtxo_id),
174 &vtxo_keypair,
175 &rand::random(),
176 )
177 }
178
179 pub fn verify_input_vtxo_sig(
180 &self,
181 vtxo: &Vtxo,
182 sig: &schnorr::Signature,
183 ) -> Result<(), secp256k1::Error> {
184 SECP.verify_schnorr(
185 sig,
186 &Self::as_signable_message(self, vtxo.id()),
187 &vtxo.user_pubkey().x_only_public_key().0,
188 )
189 }
190}