Skip to content

Commit

Permalink
Finish documenting monero-serai
Browse files Browse the repository at this point in the history
  • Loading branch information
kayabaNerve committed Jul 4, 2024
1 parent 59ca9f7 commit da2ab12
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 32 deletions.
14 changes: 13 additions & 1 deletion coins/monero/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,28 @@ impl FeeRate {
}

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

/// Serialize the FeeRate 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 res = Vec::with_capacity(16);
self.write(&mut res).unwrap();
res
}

/// Read a FeeRate.
///
/// This is not a Monero protocol defined struct, and this is accordingly not a Monero protocol
/// defined serialization.
pub fn read(r: &mut impl io::Read) -> io::Result<FeeRate> {
Ok(FeeRate { per_weight: read_u64(r)?, mask: read_u64(r)? })
}
Expand Down Expand Up @@ -486,7 +495,10 @@ pub trait Rpc: Sync + Clone + Debug {
&self,
number: usize,
) -> Result<Vec<Transaction>, RpcError> {
self.get_block_transactions(self.get_block_hash(number).await?).await
let block = self.get_block_by_number(number).await?;
let mut res = vec![block.miner_transaction];
res.extend(self.get_transactions(&block.transactions).await?);
Ok(res)
}

/// Get the output indexes of the specified transaction.
Expand Down
23 changes: 13 additions & 10 deletions coins/monero/wallet/src/decoys.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// TODO: Clean this

use std_shims::{vec::Vec, collections::HashSet};

use zeroize::Zeroize;
Expand Down Expand Up @@ -277,9 +279,12 @@ async fn select_decoys<R: RngCore + CryptoRng>(
pub use monero_serai::primitives::Decoys;

// TODO: Remove this trait
/// TODO: Document
#[cfg(feature = "std")]
#[async_trait::async_trait]
pub trait DecoySelection {
/// Select decoys using the same distribution as Monero. Relies on the monerod RPC
/// response for an output's unlocked status, minimizing trips to the daemon.
async fn select<R: Send + Sync + RngCore + CryptoRng>(
rng: &mut R,
rpc: &impl Rpc,
Expand All @@ -288,6 +293,14 @@ pub trait DecoySelection {
inputs: &[WalletOutput],
) -> Result<Vec<Decoys>, RpcError>;

/// If no reorg has occurred and an honest RPC, any caller who passes the same height to this
/// function will use the same distribution to select decoys. It is fingerprintable
/// because a caller using this will not be able to select decoys that are timelocked
/// with a timestamp. Any transaction which includes timestamp timelocked decoys in its
/// rings could not be constructed using this function.
///
/// TODO: upstream change to monerod get_outs RPC to accept a height param for checking
/// output's unlocked status and remove all usage of fingerprintable_canonical
async fn fingerprintable_canonical_select<R: Send + Sync + RngCore + CryptoRng>(
rng: &mut R,
rpc: &impl Rpc,
Expand All @@ -300,8 +313,6 @@ pub trait DecoySelection {
#[cfg(feature = "std")]
#[async_trait::async_trait]
impl DecoySelection for Decoys {
/// Select decoys using the same distribution as Monero. Relies on the monerod RPC
/// response for an output's unlocked status, minimizing trips to the daemon.
async fn select<R: Send + Sync + RngCore + CryptoRng>(
rng: &mut R,
rpc: &impl Rpc,
Expand All @@ -312,14 +323,6 @@ impl DecoySelection for Decoys {
select_decoys(rng, rpc, ring_len, height, inputs, false).await
}

/// If no reorg has occurred and an honest RPC, any caller who passes the same height to this
/// function will use the same distribution to select decoys. It is fingerprintable
/// because a caller using this will not be able to select decoys that are timelocked
/// with a timestamp. Any transaction which includes timestamp timelocked decoys in its
/// rings could not be constructed using this function.
///
/// TODO: upstream change to monerod get_outs RPC to accept a height param for checking
/// output's unlocked status and remove all usage of fingerprintable_canonical
async fn fingerprintable_canonical_select<R: Send + Sync + RngCore + CryptoRng>(
rng: &mut R,
rpc: &impl Rpc,
Expand Down
70 changes: 64 additions & 6 deletions coins/monero/wallet/src/extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,26 @@ use curve25519_dalek::edwards::EdwardsPoint;

use monero_serai::io::*;

pub const MAX_TX_EXTRA_PADDING_COUNT: usize = 255;
pub const MAX_TX_EXTRA_NONCE_SIZE: usize = 255;
pub(crate) const MAX_TX_EXTRA_PADDING_COUNT: usize = 255;
const MAX_TX_EXTRA_NONCE_SIZE: usize = 255;

pub const PAYMENT_ID_MARKER: u8 = 0;
pub const ENCRYPTED_PAYMENT_ID_MARKER: u8 = 1;
const PAYMENT_ID_MARKER: u8 = 0;
const ENCRYPTED_PAYMENT_ID_MARKER: u8 = 1;
// Used as it's the highest value not interpretable as a continued VarInt
pub const ARBITRARY_DATA_MARKER: u8 = 127;
pub(crate) const ARBITRARY_DATA_MARKER: u8 = 127;

/// The max amount of data which will fit within a blob of arbitrary data.
// 1 byte is used for the marker
pub const MAX_ARBITRARY_DATA_SIZE: usize = MAX_TX_EXTRA_NONCE_SIZE - 1;

/// A Payment ID.
///
/// This is a legacy method of identifying why Monero was sent to the receiver.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub enum PaymentId {
/// A deprecated form of payment ID which is no longer supported.
Unencrypted([u8; 32]),
/// An encrypted payment ID.
Encrypted([u8; 8]),
}

Expand All @@ -42,6 +48,7 @@ impl BitXor<[u8; 8]> for PaymentId {
}

impl PaymentId {
/// Write the PaymentId.
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
PaymentId::Unencrypted(id) => {
Expand All @@ -56,6 +63,14 @@ impl PaymentId {
Ok(())
}

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

/// Read a PaymentId.
pub fn read<R: Read>(r: &mut R) -> io::Result<PaymentId> {
Ok(match read_byte(r)? {
0 => PaymentId::Unencrypted(read_bytes(r)?),
Expand All @@ -65,18 +80,39 @@ impl PaymentId {
}
}

// Doesn't bother with padding nor MinerGate
/// A field within the TX extra.
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub enum ExtraField {
/// Padding.
///
/// This is a block of zeroes within the TX extra.
Padding(usize),
/// The transaction key.
///
/// This is a commitment to the randomness used for deriving outputs.
PublicKey(EdwardsPoint),
/// The nonce field.
///
/// This is used for data, such as payment IDs.
Nonce(Vec<u8>),
/// The field for merge-mining.
///
/// This is used within miner transactions who are merge-mining Monero to specify the foreign
/// block they mined.
MergeMining(usize, [u8; 32]),
/// The additional transaction keys.
///
/// These are the per-output commitments to the randomness used for deriving outputs.
PublicKeys(Vec<EdwardsPoint>),
/// The 'mysterious' Minergate tag.
///
/// This was used by a closed source entity without documentation. Support for parsing it was
/// added to reduce extra which couldn't be decoded.
MysteriousMinergate(Vec<u8>),
}

impl ExtraField {
/// Write the ExtraField.
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
match self {
ExtraField::Padding(size) => {
Expand Down Expand Up @@ -110,6 +146,14 @@ impl ExtraField {
Ok(())
}

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

/// Read an ExtraField.
pub fn read<R: BufRead>(r: &mut R) -> io::Result<ExtraField> {
Ok(match read_byte(r)? {
0 => ExtraField::Padding({
Expand Down Expand Up @@ -151,9 +195,15 @@ impl ExtraField {
}
}

/// The result of decoding a transaction's extra field.
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
pub struct Extra(pub(crate) Vec<ExtraField>);
impl Extra {
/// The keys within this extra.
///
/// This returns all keys specified with `PublicKey` and the first set of keys specified with
/// `PublicKeys`, so long as they're well-formed.
// TODO: Cite this
pub fn keys(&self) -> Option<(Vec<EdwardsPoint>, Option<Vec<EdwardsPoint>>)> {
let mut keys = vec![];
let mut additional = None;
Expand All @@ -174,6 +224,8 @@ impl Extra {
}
}

/// The payment ID embedded within this extra.
// TODO: Monero distinguishes encrypted/unencrypted payment ID retrieval
pub fn payment_id(&self) -> Option<PaymentId> {
for field in &self.0 {
if let ExtraField::Nonce(data) = field {
Expand All @@ -183,6 +235,9 @@ impl Extra {
None
}

/// The arbitrary data within this extra.
///
/// This uses a marker custom to monero-wallet.
pub fn data(&self) -> Vec<Vec<u8>> {
let mut res = vec![];
for field in &self.0 {
Expand All @@ -208,20 +263,23 @@ impl Extra {
self.0.push(field);
}

/// Write the Extra.
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
for field in &self.0 {
field.write(w)?;
}
Ok(())
}

/// Serialize the Extra to a `Vec<u8>`.
pub fn serialize(&self) -> Vec<u8> {
let mut buf = vec![];
self.write(&mut buf).unwrap();
buf
}

// TODO: Is this supposed to silently drop trailing gibberish?
/// Read an `Extra`.
#[allow(clippy::unnecessary_wraps)]
pub fn read<R: BufRead>(r: &mut R) -> io::Result<Extra> {
let mut res = Extra(vec![]);
Expand Down
4 changes: 3 additions & 1 deletion coins/monero/wallet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
// #![deny(missing_docs)] // TODO
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]

use zeroize::{Zeroize, Zeroizing};
Expand All @@ -23,6 +23,7 @@ pub use monero_address as address;
mod view_pair;
pub use view_pair::{ViewPair, GuaranteedViewPair};

/// Structures and functionality for working with transactions' extra fields.
pub mod extra;
pub(crate) use extra::{PaymentId, Extra};

Expand All @@ -37,6 +38,7 @@ mod decoys;
#[cfg(not(feature = "std"))]
mod decoys {
pub use monero_serai::primitives::Decoys;
/// TODO: Document/remove
pub trait DecoySelection {}
}
pub use decoys::{DecoySelection, Decoys};
Expand Down
Loading

0 comments on commit da2ab12

Please sign in to comment.