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(
32 &self,
33 vtxo_id: VtxoId,
34 vtxo_reqs: &[SignedVtxoRequest],
35 offboard_reqs: &[OffboardRequest],
36 ) -> Message {
37 let mut engine = sha256::Hash::engine();
38 engine.write_all(Self::CHALLENGE_MESSAGE_PREFIX).unwrap();
39 engine.write_all(&self.0).unwrap();
40 engine.write_all(&vtxo_id.to_bytes()).unwrap();
41
42 engine.write_all(&vtxo_reqs.len().to_be_bytes()).unwrap();
43 for req in vtxo_reqs {
44 engine.write_all(&req.vtxo.amount.to_sat().to_be_bytes()).unwrap();
45 req.vtxo.policy.encode(&mut engine).unwrap();
46 req.cosign_pubkey.encode(&mut engine).unwrap();
47 }
48
49 engine.write_all(&offboard_reqs.len().to_be_bytes()).unwrap();
50 for req in offboard_reqs {
51 req.to_txout().encode(&mut engine).unwrap();
52 }
53 let hash = sha256::Hash::from_engine(engine).to_byte_array();
54 Message::from_digest(hash)
55 }
56
57 pub fn sign_with(
58 &self,
59 vtxo_id: VtxoId,
60 vtxo_reqs: &[SignedVtxoRequest],
61 offboard_reqs: &[OffboardRequest],
62 vtxo_keypair: &Keypair,
63 ) -> schnorr::Signature {
64 let msg = self.as_signable_message(vtxo_id, vtxo_reqs, offboard_reqs);
65 SECP.sign_schnorr_with_aux_rand(&msg, &vtxo_keypair, &rand::random())
66 }
67
68 pub fn verify_input_vtxo_sig(
69 &self,
70 vtxo: &Vtxo,
71 vtxo_reqs: &[SignedVtxoRequest],
72 offboard_reqs: &[OffboardRequest],
73 sig: &schnorr::Signature,
74 ) -> Result<(), secp256k1::Error> {
75 let msg = self.as_signable_message(vtxo.id(), vtxo_reqs, offboard_reqs);
76 SECP.verify_schnorr( sig, &msg, &vtxo.user_pubkey().x_only_public_key().0)
77 }
78}
79
80#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
86pub struct LightningReceiveChallenge(PaymentHash);
87
88impl LightningReceiveChallenge {
89 const CHALLENGE_MESSAGE_PREFIX: &'static [u8; 32] = b"Lightning receive VTXO challenge";
90
91 pub fn new(value: PaymentHash) -> Self {
92 Self(value)
93 }
94
95 fn as_signable_message(&self, vtxo_id: VtxoId) -> Message {
99 let mut engine = sha256::Hash::engine();
100 engine.write_all(Self::CHALLENGE_MESSAGE_PREFIX).unwrap();
101 engine.write_all(&self.0.to_byte_array()).unwrap();
102 engine.write_all(&vtxo_id.to_bytes()).unwrap();
103
104 let hash = sha256::Hash::from_engine(engine).to_byte_array();
105 Message::from_digest(hash)
106 }
107
108 pub fn sign_with(
109 &self,
110 vtxo_id: VtxoId,
111 vtxo_keypair: &Keypair,
112 ) -> schnorr::Signature {
113 SECP.sign_schnorr_with_aux_rand(
114 &Self::as_signable_message(self, vtxo_id),
115 &vtxo_keypair,
116 &rand::random()
117 )
118 }
119
120 pub fn verify_input_vtxo_sig(
121 &self,
122 vtxo: &Vtxo,
123 sig: &schnorr::Signature,
124 ) -> Result<(), secp256k1::Error> {
125 SECP.verify_schnorr(
126 sig,
127 &Self::as_signable_message(self, vtxo.id()),
128 &vtxo.user_pubkey().x_only_public_key().0,
129 )
130 }
131}
132
133#[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
147pub struct VtxoStatusChallenge;
148
149impl VtxoStatusChallenge {
150 const CHALLENGE_MESSAGE_PREFIX: &'static [u8; 32] = b"Ark VTXO status query challenge ";
151
152 pub fn new() -> Self {
153 Self
154 }
155
156 fn as_signable_message(&self, vtxo_id: VtxoId) -> Message {
157 let mut engine = sha256::Hash::engine();
158 engine.write_all(Self::CHALLENGE_MESSAGE_PREFIX).unwrap();
159 engine.write_all(&vtxo_id.to_bytes()).unwrap();
160
161 let hash = sha256::Hash::from_engine(engine).to_byte_array();
162 Message::from_digest(hash)
163 }
164
165 pub fn sign_with(
166 &self,
167 vtxo_id: VtxoId,
168 vtxo_keypair: &Keypair,
169 ) -> schnorr::Signature {
170 SECP.sign_schnorr_with_aux_rand(
171 &Self::as_signable_message(self, vtxo_id),
172 &vtxo_keypair,
173 &rand::random(),
174 )
175 }
176
177 pub fn verify_input_vtxo_sig(
178 &self,
179 vtxo: &Vtxo,
180 sig: &schnorr::Signature,
181 ) -> Result<(), secp256k1::Error> {
182 SECP.verify_schnorr(
183 sig,
184 &Self::as_signable_message(self, vtxo.id()),
185 &vtxo.user_pubkey().x_only_public_key().0,
186 )
187 }
188}