1pub use crate::attestations::{Challenge, RoundAttemptAttestation};
2
3use std::fmt;
4use std::str::FromStr;
5
6use bitcoin::{Transaction, Txid};
7use bitcoin::hashes::Hash as _;
8use bitcoin::hex::DisplayHex;
9use bitcoin::secp256k1::schnorr;
10
11use crate::musig;
12use crate::tree::signed::VtxoTreeSpec;
13
14pub const ROUND_TX_VTXO_TREE_VOUT: u32 = 0;
16
17#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
24pub struct RoundSeq(u64);
25
26impl RoundSeq {
27 pub const fn new(seq: u64) -> Self {
28 Self(seq)
29 }
30
31 pub fn increment(&mut self) {
32 self.0 += 1;
33 }
34
35 pub fn inner(&self) -> u64 {
36 self.0
37 }
38}
39
40impl From<u64> for RoundSeq {
41 fn from(v: u64) -> Self {
42 Self::new(v)
43 }
44}
45
46impl From<RoundSeq> for u64 {
47 fn from(v: RoundSeq) -> u64 {
48 v.0
49 }
50}
51
52impl fmt::Display for RoundSeq {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) }
54}
55
56impl fmt::Debug for RoundSeq {
57 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
64pub struct RoundId(Txid);
65
66impl RoundId {
67 pub const fn new(txid: Txid) -> RoundId {
69 RoundId(txid)
70 }
71
72 pub fn from_slice(bytes: &[u8]) -> Result<RoundId, bitcoin::hashes::FromSliceError> {
73 Txid::from_slice(bytes).map(RoundId::new)
74 }
75
76 pub fn as_round_txid(&self) -> Txid {
77 self.0
78 }
79}
80
81impl From<Txid> for RoundId {
82 fn from(txid: Txid) -> RoundId {
83 RoundId::new(txid)
84 }
85}
86
87impl std::ops::Deref for RoundId {
88 type Target = Txid;
89 fn deref(&self) -> &Self::Target {
90 &self.0
91 }
92}
93
94impl fmt::Display for RoundId {
95 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96 write!(f, "{}", self.0)
97 }
98}
99
100impl FromStr for RoundId {
101 type Err = bitcoin::hashes::hex::HexToArrayError;
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 Txid::from_str(s).map(RoundId::new)
104 }
105}
106
107impl serde::Serialize for RoundId {
108 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
109 if s.is_human_readable() {
110 s.collect_str(self)
111 } else {
112 s.serialize_bytes(self.as_ref())
113 }
114 }
115}
116
117impl<'de> serde::Deserialize<'de> for RoundId {
118 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
119 struct Visitor;
120 impl<'de> serde::de::Visitor<'de> for Visitor {
121 type Value = RoundId;
122 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(f, "a RoundId, which is a Txid")
124 }
125 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
126 RoundId::from_slice(v).map_err(serde::de::Error::custom)
127 }
128 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
129 RoundId::from_str(v).map_err(serde::de::Error::custom)
130 }
131 }
132 if d.is_human_readable() {
133 d.deserialize_str(Visitor)
134 } else {
135 d.deserialize_bytes(Visitor)
136 }
137 }
138}
139
140#[derive(Debug, Clone)]
141pub struct RoundAttempt {
142 pub round_seq: RoundSeq,
143 pub attempt_seq: usize,
144 pub challenge: Challenge,
145}
146
147#[derive(Debug, Clone)]
148pub struct VtxoProposal {
149 pub round_seq: RoundSeq,
150 pub attempt_seq: usize,
151 pub unsigned_round_tx: Transaction,
152 pub vtxos_spec: VtxoTreeSpec,
153 pub cosign_agg_nonces: Vec<musig::AggregatedNonce>,
154}
155
156#[derive(Debug, Clone)]
157pub struct RoundFinished {
158 pub round_seq: RoundSeq,
159 pub attempt_seq: usize,
160 pub cosign_sigs: Vec<schnorr::Signature>,
161 pub signed_round_tx: Transaction,
162}
163
164#[derive(Debug, Clone)]
165pub struct RoundFailed {
166 pub round_seq: RoundSeq,
167}
168
169#[derive(Debug, Clone)]
170pub enum RoundEvent {
171 Attempt(RoundAttempt),
172 VtxoProposal(VtxoProposal),
173 Finished(RoundFinished),
174 Failed(RoundFailed),
175}
176
177impl RoundEvent {
178 pub fn kind(&self) -> &'static str {
180 match self {
181 Self::Attempt(_) => "RoundAttempt",
182 Self::VtxoProposal { .. } => "VtxoProposal",
183 Self::Finished { .. } => "Finished",
184 Self::Failed { .. } => "Failed",
185 }
186 }
187
188 pub fn round_seq(&self) -> RoundSeq {
189 match self {
190 Self::Attempt(e) => e.round_seq,
191 Self::VtxoProposal(e) => e.round_seq,
192 Self::Finished(e) => e.round_seq,
193 Self::Failed(e) => e.round_seq,
194 }
195 }
196
197 pub fn attempt_seq(&self) -> usize {
198 match self {
199 Self::Attempt(e) => e.attempt_seq,
200 Self::VtxoProposal(e) => e.attempt_seq,
201 Self::Finished(e) => e.attempt_seq,
202 Self::Failed(_) => 0,
203 }
204 }
205}
206
207impl fmt::Display for RoundEvent {
209 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210 match self {
211 Self::Attempt(RoundAttempt { round_seq, attempt_seq, challenge }) => {
212 f.debug_struct("RoundAttempt")
213 .field("round_seq", round_seq)
214 .field("attempt_seq", attempt_seq)
215 .field("challenge", &challenge.inner().as_hex())
216 .finish()
217 },
218 Self::VtxoProposal(VtxoProposal { round_seq, attempt_seq, unsigned_round_tx, .. }) => {
219 f.debug_struct("VtxoProposal")
220 .field("round_seq", round_seq)
221 .field("attempt_seq", attempt_seq)
222 .field("unsigned_round_txid", &unsigned_round_tx.compute_txid())
223 .finish()
224 },
225 Self::Finished(RoundFinished {
226 round_seq, attempt_seq, signed_round_tx, ..
227 }) => {
228 f.debug_struct("Finished")
229 .field("round_seq", round_seq)
230 .field("attempt_seq", attempt_seq)
231 .field("signed_round_txid", &signed_round_tx.compute_txid())
232 .finish()
233 },
234 Self::Failed(RoundFailed { round_seq }) => {
235 f.debug_struct("Failed")
236 .field("round_seq", round_seq)
237 .finish()
238 },
239 }
240 }
241}