Move Protocol to monero-wallet

This commit is contained in:
Luke Parker
2024-06-21 14:14:15 -04:00
parent 6f61861d4b
commit b5b9d4a871
14 changed files with 233 additions and 253 deletions

View File

@@ -4,7 +4,10 @@
#![cfg_attr(not(feature = "std"), no_std)]
use core::ops::Deref;
use std_shims::collections::{HashSet, HashMap};
use std_shims::{
io,
collections::{HashSet, HashMap},
};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
@@ -15,9 +18,9 @@ use curve25519_dalek::{
};
use monero_serai::{
io::write_varint,
io::{read_byte, read_u16, write_varint},
primitives::{Commitment, keccak256, keccak256_to_scalar},
ringct::EncryptedAmount,
ringct::{RctType, EncryptedAmount},
transaction::Input,
};
pub use monero_serai as monero;
@@ -58,6 +61,137 @@ pub use send::TransactionMachine;
#[cfg(test)]
mod tests;
/// Monero protocol version.
///
/// v15 is omitted as v15 was simply v14 and v16 being active at the same time, with regards to the
/// transactions supported. Accordingly, v16 should be used during v15.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
#[allow(non_camel_case_types)]
pub enum Protocol {
v14,
v16,
Custom {
ring_len: usize,
bp_plus: bool,
optimal_rct_type: RctType,
view_tags: bool,
v16_fee: bool,
},
}
impl TryFrom<u8> for Protocol {
type Error = ();
fn try_from(version: u8) -> Result<Self, ()> {
Ok(match version {
14 => Protocol::v14, // TODO: 13 | 14?
15 | 16 => Protocol::v16,
_ => Err(())?,
})
}
}
impl Protocol {
/// Amount of ring members under this protocol version.
pub fn ring_len(&self) -> usize {
match self {
Protocol::v14 => 11,
Protocol::v16 => 16,
Protocol::Custom { ring_len, .. } => *ring_len,
}
}
/// Whether or not the specified version uses Bulletproofs or Bulletproofs+.
///
/// This method will likely be reworked when versions not using Bulletproofs at all are added.
pub fn bp_plus(&self) -> bool {
match self {
Protocol::v14 => false,
Protocol::v16 => true,
Protocol::Custom { bp_plus, .. } => *bp_plus,
}
}
// TODO: Make this an Option when we support pre-RCT protocols
pub fn optimal_rct_type(&self) -> RctType {
match self {
Protocol::v14 => RctType::Clsag,
Protocol::v16 => RctType::BulletproofsPlus,
Protocol::Custom { optimal_rct_type, .. } => *optimal_rct_type,
}
}
/// Whether or not the specified version uses view tags.
pub fn view_tags(&self) -> bool {
match self {
Protocol::v14 => false,
Protocol::v16 => true,
Protocol::Custom { view_tags, .. } => *view_tags,
}
}
/// Whether or not the specified version uses the fee algorithm from Monero
/// hard fork version 16 (released in v18 binaries).
pub fn v16_fee(&self) -> bool {
match self {
Protocol::v14 => false,
Protocol::v16 => true,
Protocol::Custom { v16_fee, .. } => *v16_fee,
}
}
pub fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
match self {
Protocol::v14 => w.write_all(&[0, 14]),
Protocol::v16 => w.write_all(&[0, 16]),
Protocol::Custom { ring_len, bp_plus, optimal_rct_type, view_tags, v16_fee } => {
// Custom, version 0
w.write_all(&[1, 0])?;
w.write_all(&u16::try_from(*ring_len).unwrap().to_le_bytes())?;
w.write_all(&[u8::from(*bp_plus)])?;
w.write_all(&[optimal_rct_type.to_byte()])?;
w.write_all(&[u8::from(*view_tags)])?;
w.write_all(&[u8::from(*v16_fee)])
}
}
}
pub fn read<R: io::Read>(r: &mut R) -> io::Result<Protocol> {
Ok(match read_byte(r)? {
// Monero protocol
0 => match read_byte(r)? {
14 => Protocol::v14,
16 => Protocol::v16,
_ => Err(io::Error::other("unrecognized monero protocol"))?,
},
// Custom
1 => match read_byte(r)? {
0 => Protocol::Custom {
ring_len: read_u16(r)?.into(),
bp_plus: match read_byte(r)? {
0 => false,
1 => true,
_ => Err(io::Error::other("invalid bool serialization"))?,
},
optimal_rct_type: RctType::from_byte(read_byte(r)?)
.ok_or_else(|| io::Error::other("invalid RctType serialization"))?,
view_tags: match read_byte(r)? {
0 => false,
1 => true,
_ => Err(io::Error::other("invalid bool serialization"))?,
},
v16_fee: match read_byte(r)? {
0 => false,
1 => true,
_ => Err(io::Error::other("invalid bool serialization"))?,
},
},
_ => Err(io::Error::other("unrecognized custom protocol serialization"))?,
},
_ => Err(io::Error::other("unrecognized protocol serialization"))?,
})
}
}
fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> core::cmp::Ordering {
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse()
}

View File

@@ -2,9 +2,8 @@ use std::sync::{Arc, RwLock};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use monero_serai::Protocol;
use crate::{
address::MoneroAddress, FeeRate, SpendableOutput, Change, Decoys, SignableTransaction,
Protocol, address::MoneroAddress, FeeRate, SpendableOutput, Change, Decoys, SignableTransaction,
TransactionError, extra::MAX_ARBITRARY_DATA_SIZE,
};

View File

@@ -26,7 +26,6 @@ pub use monero_rpc::{FeePriority, FeeRate};
use monero_serai::{
io::*,
primitives::{Commitment, keccak256},
Protocol,
ringct::{
generate_key_image,
clsag::{ClsagError, ClsagContext, Clsag},
@@ -36,6 +35,7 @@ use monero_serai::{
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
};
use crate::{
Protocol,
address::{Network, AddressSpec, MoneroAddress},
ViewPair, SpendableOutput, Decoys, PaymentId, ExtraField, Extra, key_image_sort, uniqueness,
shared_key, commitment_mask, compact_amount_encryption,
@@ -239,7 +239,15 @@ fn calculate_weight_and_fee(
let mut iters = 0;
let max_iters = 5;
while !done {
weight = Transaction::fee_weight(protocol, decoy_weights, n_outputs, extra, fee);
weight = Transaction::fee_weight(
protocol.view_tags(),
protocol.bp_plus(),
protocol.ring_len(),
decoy_weights,
n_outputs,
extra,
fee,
);
let fee_calculated_from_weight = fee_rate.calculate_fee_from_weight(weight);

View File

@@ -1,6 +1,6 @@
use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{
monero::{transaction::Transaction, Protocol, DEFAULT_LOCK_WINDOW},
monero::{transaction::Transaction, DEFAULT_LOCK_WINDOW},
rpc::{OutputResponse, Rpc},
SpendableOutput,
};

View File

@@ -109,9 +109,6 @@ pub async fn rpc() -> SimpleRequestRpc {
// Mine 40 blocks to ensure decoy availability
rpc.generate_blocks(&addr, 40).await.unwrap();
// Make sure we recognize the protocol
rpc.get_protocol().await.unwrap();
rpc
}
@@ -171,6 +168,7 @@ macro_rules! test {
};
use monero_wallet::{
Protocol,
address::{Network, AddressSpec},
ViewPair, Scanner, Change, DecoySelection, Decoys, FeePriority,
SignableTransaction, SignableTransactionBuilder,
@@ -212,11 +210,11 @@ macro_rules! test {
let miner_tx = get_miner_tx_output(&rpc, &view).await;
let protocol = rpc.get_protocol().await.unwrap();
let protocol = Protocol::try_from(rpc.get_protocol().await.unwrap()).unwrap();
let builder = SignableTransactionBuilder::new(
protocol,
rpc.get_fee_rate(protocol, FeePriority::Unimportant).await.unwrap(),
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
Change::new(
&ViewPair::new(
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,

View File

@@ -2,10 +2,7 @@ use rand_core::OsRng;
use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{
monero::{transaction::Transaction, Protocol},
rpc::Rpc,
extra::Extra,
address::SubaddressIndex,
monero::transaction::Transaction, Protocol, rpc::Rpc, extra::Extra, address::SubaddressIndex,
ReceivedOutput, SpendableOutput, DecoySelection, Decoys, SignableTransactionBuilder,
};
@@ -109,7 +106,7 @@ test!(
let mut builder = SignableTransactionBuilder::new(
protocol,
rpc.get_fee_rate(protocol, FeePriority::Unimportant).await.unwrap(),
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
Change::new(&change_view, false),
);
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
@@ -293,7 +290,7 @@ test!(
let mut builder = SignableTransactionBuilder::new(
protocol,
rpc.get_fee_rate(protocol, FeePriority::Unimportant).await.unwrap(),
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
Change::fingerprintable(None),
);
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;

View File

@@ -92,7 +92,7 @@ async fn from_wallet_rpc_to_self(spec: AddressSpec) {
// TODO: Needs https://github.com/monero-project/monero/pull/9260
// let fee_rate = daemon_rpc
// .get_fee_rate(daemon_rpc.get_protocol().await.unwrap(), FeePriority::Unimportant)
// .get_fee_rate(FeePriority::Unimportant)
// .await
// .unwrap();