1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
use std_shims::{
  vec,
  vec::Vec,
  io::{self, Read, Write},
};

use zeroize::{Zeroize, ZeroizeOnDrop};

use curve25519_dalek::{Scalar, edwards::EdwardsPoint};

use crate::{
  io::*, primitives::Commitment, transaction::Timelock, address::SubaddressIndex, extra::PaymentId,
};

/// An absolute output ID, defined as its transaction hash and output index.
///
/// This is not the output's key as multiple outputs may share an output key.
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub(crate) struct AbsoluteId {
  pub(crate) transaction: [u8; 32],
  pub(crate) index_in_transaction: u32,
}

impl core::fmt::Debug for AbsoluteId {
  fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
    fmt
      .debug_struct("AbsoluteId")
      .field("transaction", &hex::encode(self.transaction))
      .field("index_in_transaction", &self.index_in_transaction)
      .finish()
  }
}

impl AbsoluteId {
  /// Write the AbsoluteId.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
    w.write_all(&self.transaction)?;
    w.write_all(&self.index_in_transaction.to_le_bytes())
  }

  /// Read an AbsoluteId.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  fn read<R: Read>(r: &mut R) -> io::Result<AbsoluteId> {
    Ok(AbsoluteId { transaction: read_bytes(r)?, index_in_transaction: read_u32(r)? })
  }
}

/// An output's relative ID.
///
/// This is defined as the output's index on the blockchain.
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub(crate) struct RelativeId {
  pub(crate) index_on_blockchain: u64,
}

impl core::fmt::Debug for RelativeId {
  fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
    fmt.debug_struct("RelativeId").field("index_on_blockchain", &self.index_on_blockchain).finish()
  }
}

impl RelativeId {
  /// Write the RelativeId.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
    w.write_all(&self.index_on_blockchain.to_le_bytes())
  }

  /// Read an RelativeId.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  fn read<R: Read>(r: &mut R) -> io::Result<Self> {
    Ok(RelativeId { index_on_blockchain: read_u64(r)? })
  }
}

/// The data within an output, as necessary to spend the output.
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub(crate) struct OutputData {
  pub(crate) key: EdwardsPoint,
  pub(crate) key_offset: Scalar,
  pub(crate) commitment: Commitment,
}

impl core::fmt::Debug for OutputData {
  fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
    fmt
      .debug_struct("OutputData")
      .field("key", &hex::encode(self.key.compress().0))
      .field("key_offset", &hex::encode(self.key_offset.to_bytes()))
      .field("commitment", &self.commitment)
      .finish()
  }
}

impl OutputData {
  /// The key this output may be spent by.
  pub(crate) fn key(&self) -> EdwardsPoint {
    self.key
  }

  /// The scalar to add to the private spend key for it to be the discrete logarithm of this
  /// output's key.
  pub(crate) fn key_offset(&self) -> Scalar {
    self.key_offset
  }

  /// The commitment this output created.
  pub(crate) fn commitment(&self) -> &Commitment {
    &self.commitment
  }

  /// Write the OutputData.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  pub(crate) fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
    w.write_all(&self.key.compress().to_bytes())?;
    w.write_all(&self.key_offset.to_bytes())?;
    self.commitment.write(w)
  }

  /*
  /// Serialize the OutputData to a `Vec<u8>`.
  pub fn serialize(&self) -> Vec<u8> {
    let mut res = Vec::with_capacity(32 + 32 + 40);
    self.write(&mut res).unwrap();
    res
  }
  */

  /// Read an OutputData.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  pub(crate) fn read<R: Read>(r: &mut R) -> io::Result<OutputData> {
    Ok(OutputData {
      key: read_point(r)?,
      key_offset: read_scalar(r)?,
      commitment: Commitment::read(r)?,
    })
  }
}

/// The metadata for an output.
#[derive(Clone, PartialEq, Eq, Zeroize, ZeroizeOnDrop)]
pub(crate) struct Metadata {
  pub(crate) additional_timelock: Timelock,
  pub(crate) subaddress: Option<SubaddressIndex>,
  pub(crate) payment_id: Option<PaymentId>,
  pub(crate) arbitrary_data: Vec<Vec<u8>>,
}

impl core::fmt::Debug for Metadata {
  fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
    fmt
      .debug_struct("Metadata")
      .field("additional_timelock", &self.additional_timelock)
      .field("subaddress", &self.subaddress)
      .field("payment_id", &self.payment_id)
      .field("arbitrary_data", &self.arbitrary_data.iter().map(hex::encode).collect::<Vec<_>>())
      .finish()
  }
}

impl Metadata {
  /// Write the Metadata.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
    self.additional_timelock.write(w)?;

    if let Some(subaddress) = self.subaddress {
      w.write_all(&[1])?;
      w.write_all(&subaddress.account().to_le_bytes())?;
      w.write_all(&subaddress.address().to_le_bytes())?;
    } else {
      w.write_all(&[0])?;
    }

    if let Some(payment_id) = self.payment_id {
      w.write_all(&[1])?;
      payment_id.write(w)?;
    } else {
      w.write_all(&[0])?;
    }

    w.write_all(&u32::try_from(self.arbitrary_data.len()).unwrap().to_le_bytes())?;
    for part in &self.arbitrary_data {
      w.write_all(&[u8::try_from(part.len()).unwrap()])?;
      w.write_all(part)?;
    }
    Ok(())
  }

  /// Read a Metadata.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  fn read<R: Read>(r: &mut R) -> io::Result<Metadata> {
    let additional_timelock = Timelock::read(r)?;

    let subaddress = match read_byte(r)? {
      0 => None,
      1 => Some(
        SubaddressIndex::new(read_u32(r)?, read_u32(r)?)
          .ok_or_else(|| io::Error::other("invalid subaddress in metadata"))?,
      ),
      _ => Err(io::Error::other("invalid subaddress is_some boolean in metadata"))?,
    };

    Ok(Metadata {
      additional_timelock,
      subaddress,
      payment_id: if read_byte(r)? == 1 { PaymentId::read(r).ok() } else { None },
      arbitrary_data: {
        let mut data = vec![];
        for _ in 0 .. read_u32(r)? {
          let len = read_byte(r)?;
          data.push(read_raw_vec(read_byte, usize::from(len), r)?);
        }
        data
      },
    })
  }
}

/// A scanned output and all associated data.
///
/// This struct contains all data necessary to spend this output, or handle it as a payment.
///
/// This struct is bound to a specific instance of the blockchain. If the blockchain reorganizes
/// the block this struct is bound to, it MUST be discarded. If any outputs are mutual to both
/// blockchains, scanning the new blockchain will yield those outputs again.
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
pub struct WalletOutput {
  /// The absolute ID for this transaction.
  pub(crate) absolute_id: AbsoluteId,
  /// The ID for this transaction, relative to the blockchain.
  pub(crate) relative_id: RelativeId,
  /// The output's data.
  pub(crate) data: OutputData,
  /// Associated metadata relevant for handling it as a payment.
  pub(crate) metadata: Metadata,
}

impl WalletOutput {
  /// The hash of the transaction which created this output.
  pub fn transaction(&self) -> [u8; 32] {
    self.absolute_id.transaction
  }

  /// The index of the output within the transaction.
  pub fn index_in_transaction(&self) -> u32 {
    self.absolute_id.index_in_transaction
  }

  /// The index of the output on the blockchain.
  pub fn index_on_blockchain(&self) -> u64 {
    self.relative_id.index_on_blockchain
  }

  /// The key this output may be spent by.
  pub fn key(&self) -> EdwardsPoint {
    self.data.key()
  }

  /// The scalar to add to the private spend key for it to be the discrete logarithm of this
  /// output's key.
  pub fn key_offset(&self) -> Scalar {
    self.data.key_offset()
  }

  /// The commitment this output created.
  pub fn commitment(&self) -> &Commitment {
    self.data.commitment()
  }

  /// The additional timelock this output is subject to.
  ///
  /// All outputs are subject to the '10-block lock', a 10-block window after their inclusion
  /// on-chain during which they cannot be spent. Outputs may be additionally timelocked. This
  /// function only returns the additional timelock.
  pub fn additional_timelock(&self) -> Timelock {
    self.metadata.additional_timelock
  }

  /// The index of the subaddress this output was identified as sent to.
  pub fn subaddress(&self) -> Option<SubaddressIndex> {
    self.metadata.subaddress
  }

  /// The payment ID included with this output.
  ///
  /// This field may be `Some` even if wallet2 would not return a payment ID. wallet2 will only
  /// decrypt a payment ID if either:
  ///
  /// A) The transaction wasn't made by the wallet (via checking if any key images are recognized)
  /// B) For the highest-indexed input with a recognized key image, it spends an output with
  ///    subaddress account `(a, _)` which is distinct from this output's subaddress account
  ///
  /// Neither of these cases are handled by `monero-wallet` as scanning doesn't have the context
  /// of key images.
  //
  // Identification of the subaddress account for the highest-indexed input with a recognized key
  // image:
  //   https://github.com/monero-project/monero/blob/a1dc85c5373a30f14aaf7dcfdd95f5a7375d3623
  //     /src/wallet/wallet2.cpp/#L2637-L2670
  //
  // Removal of 'transfers' received to this account:
  //   https://github.com/monero-project/monero/blob/a1dc85c5373a30f14aaf7dcfdd95f5a7375d3623
  //     /src/wallet/wallet2.cpp/#L2782-L2794
  //
  // Payment IDs only being decrypted for the remaining transfers:
  //   https://github.com/monero-project/monero/blob/a1dc85c5373a30f14aaf7dcfdd95f5a7375d3623
  //     /src/wallet/wallet2.cpp/#L2796-L2844
  pub fn payment_id(&self) -> Option<PaymentId> {
    self.metadata.payment_id
  }

  /// The arbitrary data from the `extra` field of the transaction which created this output.
  pub fn arbitrary_data(&self) -> &[Vec<u8>] {
    &self.metadata.arbitrary_data
  }

  /// Write the WalletOutput.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
    self.absolute_id.write(w)?;
    self.relative_id.write(w)?;
    self.data.write(w)?;
    self.metadata.write(w)
  }

  /// Serialize the WalletOutput to a `Vec<u8>`.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  pub fn serialize(&self) -> Vec<u8> {
    let mut serialized = Vec::with_capacity(128);
    self.write(&mut serialized).unwrap();
    serialized
  }

  /// Read a WalletOutput.
  ///
  /// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
  /// defined serialization.
  pub fn read<R: Read>(r: &mut R) -> io::Result<WalletOutput> {
    Ok(WalletOutput {
      absolute_id: AbsoluteId::read(r)?,
      relative_id: RelativeId::read(r)?,
      data: OutputData::read(r)?,
      metadata: Metadata::read(r)?,
    })
  }
}