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::*, primitives::Commitment, transaction::Timelock, address::SubaddressIndex, extra::PaymentId,
13};
14
15#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
19pub(crate) struct AbsoluteId {
20 pub(crate) transaction: [u8; 32],
21 pub(crate) index_in_transaction: u64,
22}
23
24impl core::fmt::Debug for AbsoluteId {
25 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
26 fmt
27 .debug_struct("AbsoluteId")
28 .field("transaction", &hex::encode(self.transaction))
29 .field("index_in_transaction", &self.index_in_transaction)
30 .finish()
31 }
32}
33
34impl AbsoluteId {
35 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
40 w.write_all(&self.transaction)?;
41 w.write_all(&self.index_in_transaction.to_le_bytes())
42 }
43
44 fn read<R: Read>(r: &mut R) -> io::Result<AbsoluteId> {
49 Ok(AbsoluteId { transaction: read_bytes(r)?, index_in_transaction: read_u64(r)? })
50 }
51}
52
53#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
57pub(crate) struct RelativeId {
58 pub(crate) index_on_blockchain: u64,
59}
60
61impl core::fmt::Debug for RelativeId {
62 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
63 fmt.debug_struct("RelativeId").field("index_on_blockchain", &self.index_on_blockchain).finish()
64 }
65}
66
67impl RelativeId {
68 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
73 w.write_all(&self.index_on_blockchain.to_le_bytes())
74 }
75
76 fn read<R: Read>(r: &mut R) -> io::Result<Self> {
81 Ok(RelativeId { index_on_blockchain: read_u64(r)? })
82 }
83}
84
85#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
87pub(crate) struct OutputData {
88 pub(crate) key: EdwardsPoint,
89 pub(crate) key_offset: Scalar,
90 pub(crate) commitment: Commitment,
91}
92
93impl core::fmt::Debug for OutputData {
94 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
95 fmt
96 .debug_struct("OutputData")
97 .field("key", &hex::encode(self.key.compress().0))
98 .field("key_offset", &hex::encode(self.key_offset.to_bytes()))
99 .field("commitment", &self.commitment)
100 .finish()
101 }
102}
103
104impl OutputData {
105 pub(crate) fn key(&self) -> EdwardsPoint {
107 self.key
108 }
109
110 pub(crate) fn key_offset(&self) -> Scalar {
113 self.key_offset
114 }
115
116 pub(crate) fn commitment(&self) -> &Commitment {
118 &self.commitment
119 }
120
121 pub(crate) fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
126 w.write_all(&self.key.compress().to_bytes())?;
127 w.write_all(&self.key_offset.to_bytes())?;
128 self.commitment.write(w)
129 }
130
131 pub(crate) fn read<R: Read>(r: &mut R) -> io::Result<OutputData> {
145 Ok(OutputData {
146 key: read_point(r)?,
147 key_offset: read_scalar(r)?,
148 commitment: Commitment::read(r)?,
149 })
150 }
151}
152
153#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
155pub(crate) struct Metadata {
156 pub(crate) additional_timelock: Timelock,
157 pub(crate) subaddress: Option<SubaddressIndex>,
158 pub(crate) payment_id: Option<PaymentId>,
159 pub(crate) arbitrary_data: Vec<Vec<u8>>,
160}
161
162impl core::fmt::Debug for Metadata {
163 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
164 fmt
165 .debug_struct("Metadata")
166 .field("additional_timelock", &self.additional_timelock)
167 .field("subaddress", &self.subaddress)
168 .field("payment_id", &self.payment_id)
169 .field("arbitrary_data", &self.arbitrary_data.iter().map(hex::encode).collect::<Vec<_>>())
170 .finish()
171 }
172}
173
174impl Metadata {
175 fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
180 self.additional_timelock.write(w)?;
181
182 if let Some(subaddress) = self.subaddress {
183 w.write_all(&[1])?;
184 w.write_all(&subaddress.account().to_le_bytes())?;
185 w.write_all(&subaddress.address().to_le_bytes())?;
186 } else {
187 w.write_all(&[0])?;
188 }
189
190 if let Some(payment_id) = self.payment_id {
191 w.write_all(&[1])?;
192 payment_id.write(w)?;
193 } else {
194 w.write_all(&[0])?;
195 }
196
197 w.write_all(
198 &u64::try_from(self.arbitrary_data.len())
199 .expect("amount of arbitrary data chunks exceeded u64::MAX")
200 .to_le_bytes(),
201 )?;
202 for part in &self.arbitrary_data {
203 w.write_all(&[
206 u8::try_from(part.len()).expect("piece of arbitrary data exceeded max length of u8::MAX")
207 ])?;
208 w.write_all(part)?;
209 }
210 Ok(())
211 }
212
213 fn read<R: Read>(r: &mut R) -> io::Result<Metadata> {
218 let additional_timelock = Timelock::read(r)?;
219
220 let subaddress = match read_byte(r)? {
221 0 => None,
222 1 => Some(
223 SubaddressIndex::new(read_u32(r)?, read_u32(r)?)
224 .ok_or_else(|| io::Error::other("invalid subaddress in metadata"))?,
225 ),
226 _ => Err(io::Error::other("invalid subaddress is_some boolean in metadata"))?,
227 };
228
229 Ok(Metadata {
230 additional_timelock,
231 subaddress,
232 payment_id: if read_byte(r)? == 1 { PaymentId::read(r).ok() } else { None },
233 arbitrary_data: {
234 let mut data = vec![];
235 for _ in 0 .. read_u64(r)? {
236 let len = read_byte(r)?;
237 data.push(read_raw_vec(read_byte, usize::from(len), r)?);
238 }
239 data
240 },
241 })
242 }
243}
244
245#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
253pub struct WalletOutput {
254 pub(crate) absolute_id: AbsoluteId,
256 pub(crate) relative_id: RelativeId,
258 pub(crate) data: OutputData,
260 pub(crate) metadata: Metadata,
262}
263
264impl WalletOutput {
265 pub fn transaction(&self) -> [u8; 32] {
267 self.absolute_id.transaction
268 }
269
270 pub fn index_in_transaction(&self) -> u64 {
272 self.absolute_id.index_in_transaction
273 }
274
275 pub fn index_on_blockchain(&self) -> u64 {
277 self.relative_id.index_on_blockchain
278 }
279
280 pub fn key(&self) -> EdwardsPoint {
282 self.data.key()
283 }
284
285 pub fn key_offset(&self) -> Scalar {
288 self.data.key_offset()
289 }
290
291 pub fn commitment(&self) -> &Commitment {
293 self.data.commitment()
294 }
295
296 pub fn additional_timelock(&self) -> Timelock {
302 self.metadata.additional_timelock
303 }
304
305 pub fn subaddress(&self) -> Option<SubaddressIndex> {
307 self.metadata.subaddress
308 }
309
310 pub fn payment_id(&self) -> Option<PaymentId> {
335 self.metadata.payment_id
336 }
337
338 pub fn arbitrary_data(&self) -> &[Vec<u8>] {
340 &self.metadata.arbitrary_data
341 }
342
343 pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
348 self.absolute_id.write(w)?;
349 self.relative_id.write(w)?;
350 self.data.write(w)?;
351 self.metadata.write(w)
352 }
353
354 pub fn serialize(&self) -> Vec<u8> {
359 let mut serialized = Vec::with_capacity(128);
360 self.write(&mut serialized).expect("write failed but <Vec as io::Write> doesn't fail");
361 serialized
362 }
363
364 pub fn read<R: Read>(r: &mut R) -> io::Result<WalletOutput> {
369 Ok(WalletOutput {
370 absolute_id: AbsoluteId::read(r)?,
371 relative_id: RelativeId::read(r)?,
372 data: OutputData::read(r)?,
373 metadata: Metadata::read(r)?,
374 })
375 }
376}