1use std_shims::{
2 vec,
3 vec::Vec,
4 io::{self, Read, Write},
5};
6
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9use curve25519_dalek::{Scalar, edwards::EdwardsPoint};
10
11use crate::{
12 io::*,
13 primitives::Commitment,
14 transaction::Timelock,
15 address::SubaddressIndex,
16 extra::{MAX_ARBITRARY_DATA_SIZE, MAX_EXTRA_SIZE_BY_RELAY_RULE, PaymentId},
17};
18
19#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
23pub(crate) struct AbsoluteId {
24 pub(crate) transaction: [u8; 32],
25 pub(crate) index_in_transaction: u64,
26}
27
28impl core::fmt::Debug for AbsoluteId {
29 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
30 fmt
31 .debug_struct("AbsoluteId")
32 .field("transaction", &hex::encode(self.transaction))
33 .field("index_in_transaction", &self.index_in_transaction)
34 .finish()
35 }
36}
37
38impl AbsoluteId {
39 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
44 w.write_all(&self.transaction)?;
45 w.write_all(&self.index_in_transaction.to_le_bytes())
46 }
47
48 fn read<R: Read>(r: &mut R) -> io::Result<AbsoluteId> {
53 Ok(AbsoluteId { transaction: read_bytes(r)?, index_in_transaction: read_u64(r)? })
54 }
55}
56
57#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
61pub(crate) struct RelativeId {
62 pub(crate) index_on_blockchain: u64,
63}
64
65impl core::fmt::Debug for RelativeId {
66 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
67 fmt.debug_struct("RelativeId").field("index_on_blockchain", &self.index_on_blockchain).finish()
68 }
69}
70
71impl RelativeId {
72 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
77 w.write_all(&self.index_on_blockchain.to_le_bytes())
78 }
79
80 fn read<R: Read>(r: &mut R) -> io::Result<Self> {
85 Ok(RelativeId { index_on_blockchain: read_u64(r)? })
86 }
87}
88
89#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
91pub(crate) struct OutputData {
92 pub(crate) key: EdwardsPoint,
93 pub(crate) key_offset: Scalar,
94 pub(crate) commitment: Commitment,
95}
96
97impl core::fmt::Debug for OutputData {
98 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
99 fmt
100 .debug_struct("OutputData")
101 .field("key", &hex::encode(self.key.compress().0))
102 .field("commitment", &self.commitment)
103 .finish_non_exhaustive()
104 }
105}
106
107impl OutputData {
108 pub(crate) fn key(&self) -> EdwardsPoint {
110 self.key
111 }
112
113 pub(crate) fn key_offset(&self) -> Scalar {
116 self.key_offset
117 }
118
119 pub(crate) fn commitment(&self) -> &Commitment {
121 &self.commitment
122 }
123
124 pub(crate) fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
129 w.write_all(&self.key.compress().to_bytes())?;
130 w.write_all(&self.key_offset.to_bytes())?;
131 self.commitment.write(w)
132 }
133
134 pub(crate) fn read<R: Read>(r: &mut R) -> io::Result<OutputData> {
148 Ok(OutputData {
149 key: read_point(r)?,
150 key_offset: read_scalar(r)?,
151 commitment: Commitment::read(r)?,
152 })
153 }
154}
155
156#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
158pub(crate) struct Metadata {
159 pub(crate) additional_timelock: Timelock,
160 pub(crate) subaddress: Option<SubaddressIndex>,
161 pub(crate) payment_id: Option<PaymentId>,
162 pub(crate) arbitrary_data: Vec<Vec<u8>>,
163}
164
165impl core::fmt::Debug for Metadata {
166 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
167 fmt
168 .debug_struct("Metadata")
169 .field("additional_timelock", &self.additional_timelock)
170 .field("subaddress", &self.subaddress)
171 .field("payment_id", &self.payment_id)
172 .field("arbitrary_data", &self.arbitrary_data.iter().map(hex::encode).collect::<Vec<_>>())
173 .finish()
174 }
175}
176
177impl Metadata {
178 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
183 self.additional_timelock.write(w)?;
184
185 if let Some(subaddress) = self.subaddress {
186 w.write_all(&[1])?;
187 w.write_all(&subaddress.account().to_le_bytes())?;
188 w.write_all(&subaddress.address().to_le_bytes())?;
189 } else {
190 w.write_all(&[0])?;
191 }
192
193 if let Some(payment_id) = self.payment_id {
194 w.write_all(&[1])?;
195 payment_id.write(w)?;
196 } else {
197 w.write_all(&[0])?;
198 }
199
200 VarInt::write(&self.arbitrary_data.len(), w)?;
201 for part in &self.arbitrary_data {
202 const _ASSERT_MAX_ARBITRARY_DATA_SIZE_FITS_WITHIN_U8: [();
203 (u8::MAX as usize) - MAX_ARBITRARY_DATA_SIZE] = [(); _];
204 w.write_all(&[
205 u8::try_from(part.len()).expect("piece of arbitrary data exceeded max length of u8::MAX")
206 ])?;
207 w.write_all(part)?;
208 }
209 Ok(())
210 }
211
212 fn read<R: Read>(r: &mut R) -> io::Result<Metadata> {
217 let additional_timelock = Timelock::read(r)?;
218
219 let subaddress = match read_byte(r)? {
220 0 => None,
221 1 => Some(
222 SubaddressIndex::new(read_u32(r)?, read_u32(r)?)
223 .ok_or_else(|| io::Error::other("invalid subaddress in metadata"))?,
224 ),
225 _ => Err(io::Error::other("invalid subaddress is_some boolean in metadata"))?,
226 };
227
228 Ok(Metadata {
229 additional_timelock,
230 subaddress,
231 payment_id: if read_byte(r)? == 1 { PaymentId::read(r).ok() } else { None },
232 arbitrary_data: {
237 let mut data = vec![];
238 let mut total_len = 0usize;
239 for _ in 0 .. <usize as VarInt>::read(r)? {
240 let len = read_byte(r)?;
241 let chunk = read_raw_vec(read_byte, usize::from(len), r)?;
242 total_len = total_len.wrapping_add(chunk.len());
243 if total_len > MAX_EXTRA_SIZE_BY_RELAY_RULE {
244 Err(io::Error::other("amount of arbitrary data exceeded amount allowed by policy"))?;
245 }
246 data.push(chunk);
247 }
248 data
249 },
250 })
251 }
252}
253
254#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
262pub struct WalletOutput {
263 pub(crate) absolute_id: AbsoluteId,
265 pub(crate) relative_id: RelativeId,
267 pub(crate) data: OutputData,
269 pub(crate) metadata: Metadata,
271}
272
273impl WalletOutput {
274 pub fn transaction(&self) -> [u8; 32] {
276 self.absolute_id.transaction
277 }
278
279 pub fn index_in_transaction(&self) -> u64 {
281 self.absolute_id.index_in_transaction
282 }
283
284 pub fn index_on_blockchain(&self) -> u64 {
286 self.relative_id.index_on_blockchain
287 }
288
289 pub fn key(&self) -> EdwardsPoint {
291 self.data.key()
292 }
293
294 pub fn key_offset(&self) -> Scalar {
297 self.data.key_offset()
298 }
299
300 pub fn commitment(&self) -> &Commitment {
302 self.data.commitment()
303 }
304
305 pub fn additional_timelock(&self) -> Timelock {
311 self.metadata.additional_timelock
312 }
313
314 pub fn subaddress(&self) -> Option<SubaddressIndex> {
316 self.metadata.subaddress
317 }
318
319 pub fn payment_id(&self) -> Option<PaymentId> {
344 self.metadata.payment_id
345 }
346
347 pub fn arbitrary_data(&self) -> &[Vec<u8>] {
349 &self.metadata.arbitrary_data
350 }
351
352 pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
357 self.absolute_id.write(w)?;
358 self.relative_id.write(w)?;
359 self.data.write(w)?;
360 self.metadata.write(w)
361 }
362
363 pub fn serialize(&self) -> Vec<u8> {
368 let mut serialized = Vec::with_capacity(128);
369 self.write(&mut serialized).expect("write failed but <Vec as io::Write> doesn't fail");
370 serialized
371 }
372
373 pub fn read<R: Read>(r: &mut R) -> io::Result<WalletOutput> {
378 Ok(WalletOutput {
379 absolute_id: AbsoluteId::read(r)?,
380 relative_id: RelativeId::read(r)?,
381 data: OutputData::read(r)?,
382 metadata: Metadata::read(r)?,
383 })
384 }
385}