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

@@ -23,7 +23,6 @@ use serde_json::{Value, json};
use monero_serai::{ use monero_serai::{
io::*, io::*,
Protocol,
transaction::{Input, Timelock, Transaction}, transaction::{Input, Timelock, Transaction},
block::Block, block::Block,
}; };
@@ -154,9 +153,6 @@ pub enum RpcError {
/// The node is invalid per the expected protocol. /// The node is invalid per the expected protocol.
#[cfg_attr(feature = "std", error("invalid node ({0})"))] #[cfg_attr(feature = "std", error("invalid node ({0})"))]
InvalidNode(String), InvalidNode(String),
/// The node is running an unsupported protocol.
#[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))]
UnsupportedProtocol(usize),
/// Requested transactions weren't found. /// Requested transactions weren't found.
#[cfg_attr(feature = "std", error("transactions not found"))] #[cfg_attr(feature = "std", error("transactions not found"))]
TransactionsNotFound(Vec<[u8; 32]>), TransactionsNotFound(Vec<[u8; 32]>),
@@ -210,42 +206,6 @@ fn read_epee_vi<R: io::Read>(reader: &mut R) -> io::Result<u64> {
Ok(vi) Ok(vi)
} }
async fn get_fee_rate_v14(rpc: &impl Rpc, priority: FeePriority) -> Result<FeeRate, RpcError> {
#[derive(Deserialize, Debug)]
struct FeeResponseV14 {
status: String,
fee: u64,
quantization_mask: u64,
}
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
// src/wallet/wallet2.cpp#L7569-L7584
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
// src/wallet/wallet2.cpp#L7660-L7661
let priority_idx =
usize::try_from(if priority.fee_priority() == 0 { 1 } else { priority.fee_priority() - 1 })
.map_err(|_| RpcError::InvalidPriority)?;
let multipliers = [1, 5, 25, 1000];
if priority_idx >= multipliers.len() {
// though not an RPC error, it seems sensible to treat as such
Err(RpcError::InvalidPriority)?;
}
let fee_multiplier = multipliers[priority_idx];
let res: FeeResponseV14 = rpc
.json_rpc_call(
"get_fee_estimate",
Some(json!({ "grace_blocks": GRACE_BLOCKS_FOR_FEE_ESTIMATE })),
)
.await?;
if res.status != "OK" {
Err(RpcError::InvalidFee)?;
}
FeeRate::new(res.fee * fee_multiplier, res.quantization_mask)
}
/// An RPC connection to a Monero daemon. /// An RPC connection to a Monero daemon.
/// ///
/// This is abstract such that users can use an HTTP library (which being their choice), a /// This is abstract such that users can use an HTTP library (which being their choice), a
@@ -305,10 +265,12 @@ pub trait Rpc: Sync + Clone + Debug {
} }
/// Get the active blockchain protocol version. /// Get the active blockchain protocol version.
async fn get_protocol(&self) -> Result<Protocol, RpcError> { ///
/// This is specifically the major version within the most recent block header.
async fn get_protocol(&self) -> Result<u8, RpcError> {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct ProtocolResponse { struct ProtocolResponse {
major_version: usize, major_version: u8,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@@ -317,16 +279,11 @@ pub trait Rpc: Sync + Clone + Debug {
} }
Ok( Ok(
match self self
.json_rpc_call::<LastHeaderResponse>("get_last_block_header", None) .json_rpc_call::<LastHeaderResponse>("get_last_block_header", None)
.await? .await?
.block_header .block_header
.major_version .major_version,
{
13 | 14 => Protocol::v14,
15 | 16 => Protocol::v16,
protocol => Err(RpcError::UnsupportedProtocol(protocol))?,
},
) )
} }
@@ -789,28 +746,27 @@ pub trait Rpc: Sync + Clone + Debug {
/// ///
/// This may be manipulated to unsafe levels and MUST be sanity checked. /// This may be manipulated to unsafe levels and MUST be sanity checked.
// TODO: Take a sanity check argument // TODO: Take a sanity check argument
async fn get_fee_rate( async fn get_fee_rate(&self, priority: FeePriority) -> Result<FeeRate, RpcError> {
&self, #[derive(Deserialize, Debug)]
protocol: Protocol, struct FeeResponse {
priority: FeePriority, status: String,
) -> Result<FeeRate, RpcError> { fees: Option<Vec<u64>>,
// TODO: Implement wallet2's adjust_priority which by default automatically uses a lower fee: u64,
// priority than provided depending on the backlog in the pool quantization_mask: u64,
if protocol.v16_fee() { }
#[derive(Deserialize, Debug)]
struct FeeResponse {
status: String,
fees: Vec<u64>,
quantization_mask: u64,
}
let res: FeeResponse = self let res: FeeResponse = self
.json_rpc_call( .json_rpc_call(
"get_fee_estimate", "get_fee_estimate",
Some(json!({ "grace_blocks": GRACE_BLOCKS_FOR_FEE_ESTIMATE })), Some(json!({ "grace_blocks": GRACE_BLOCKS_FOR_FEE_ESTIMATE })),
) )
.await?; .await?;
if res.status != "OK" {
Err(RpcError::InvalidFee)?;
}
if let Some(fees) = res.fees {
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/ // https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
// src/wallet/wallet2.cpp#L7615-L7620 // src/wallet/wallet2.cpp#L7615-L7620
let priority_idx = usize::try_from(if priority.fee_priority() >= 4 { let priority_idx = usize::try_from(if priority.fee_priority() >= 4 {
@@ -820,15 +776,27 @@ pub trait Rpc: Sync + Clone + Debug {
}) })
.map_err(|_| RpcError::InvalidPriority)?; .map_err(|_| RpcError::InvalidPriority)?;
if res.status != "OK" { if priority_idx >= fees.len() {
Err(RpcError::InvalidFee)
} else if priority_idx >= res.fees.len() {
Err(RpcError::InvalidPriority) Err(RpcError::InvalidPriority)
} else { } else {
FeeRate::new(res.fees[priority_idx], res.quantization_mask) FeeRate::new(fees[priority_idx], res.quantization_mask)
} }
} else { } else {
get_fee_rate_v14(self, priority).await // https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
// src/wallet/wallet2.cpp#L7569-L7584
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
// src/wallet/wallet2.cpp#L7660-L7661
let priority_idx =
usize::try_from(if priority.fee_priority() == 0 { 1 } else { priority.fee_priority() - 1 })
.map_err(|_| RpcError::InvalidPriority)?;
let multipliers = [1, 5, 25, 1000];
if priority_idx >= multipliers.len() {
// though not an RPC error, it seems sensible to treat as such
Err(RpcError::InvalidPriority)?;
}
let fee_multiplier = multipliers[priority_idx];
FeeRate::new(res.fee * fee_multiplier, res.quantization_mask)
} }
} }

View File

@@ -1,26 +1,19 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
// #![deny(missing_docs)] // TODO
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
use std_shims::io as stdio;
use zeroize::Zeroize;
pub use monero_io as io; pub use monero_io as io;
pub use monero_generators as generators; pub use monero_generators as generators;
pub use monero_primitives as primitives; pub use monero_primitives as primitives;
mod merkle; mod merkle;
use monero_io as serialize;
use serialize::{read_byte, read_u16};
/// Ring Signature structs and functionality. /// Ring Signature structs and functionality.
pub mod ring_signatures; pub mod ring_signatures;
/// RingCT structs and functionality. /// RingCT structs and functionality.
pub mod ringct; pub mod ringct;
use ringct::RctType;
/// Transaction structs. /// Transaction structs.
pub mod transaction; pub mod transaction;
@@ -30,123 +23,3 @@ pub mod block;
pub const DEFAULT_LOCK_WINDOW: usize = 10; pub const DEFAULT_LOCK_WINDOW: usize = 10;
pub const COINBASE_LOCK_WINDOW: usize = 60; pub const COINBASE_LOCK_WINDOW: usize = 60;
pub const BLOCK_TIME: usize = 120; pub const BLOCK_TIME: usize = 120;
/// 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 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: stdio::Write>(&self, w: &mut W) -> stdio::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: stdio::Read>(r: &mut R) -> stdio::Result<Protocol> {
Ok(match read_byte(r)? {
// Monero protocol
0 => match read_byte(r)? {
14 => Protocol::v14,
16 => Protocol::v16,
_ => Err(stdio::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(stdio::Error::other("invalid bool serialization"))?,
},
optimal_rct_type: RctType::from_byte(read_byte(r)?)
.ok_or_else(|| stdio::Error::other("invalid RctType serialization"))?,
view_tags: match read_byte(r)? {
0 => false,
1 => true,
_ => Err(stdio::Error::other("invalid bool serialization"))?,
},
v16_fee: match read_byte(r)? {
0 => false,
1 => true,
_ => Err(stdio::Error::other("invalid bool serialization"))?,
},
},
_ => Err(stdio::Error::other("unrecognized custom protocol serialization"))?,
},
_ => Err(stdio::Error::other("unrecognized protocol serialization"))?,
})
}
}

View File

@@ -17,7 +17,6 @@ use crate::{
io::*, io::*,
generators::hash_to_point, generators::hash_to_point,
ringct::{mlsag::Mlsag, clsag::Clsag, borromean::BorromeanRange, bulletproofs::Bulletproof}, ringct::{mlsag::Mlsag, clsag::Clsag, borromean::BorromeanRange, bulletproofs::Bulletproof},
Protocol,
}; };
/// Generate a key image for a given key. Defined as `x * hash_to_point(xG)`. /// Generate a key image for a given key. Defined as `x * hash_to_point(xG)`.
@@ -227,10 +226,13 @@ pub enum RctPrunable {
} }
impl RctPrunable { impl RctPrunable {
pub fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize) -> usize { #[rustfmt::skip]
pub fn fee_weight(bp_plus: bool, ring_len: usize, inputs: usize, outputs: usize) -> usize {
// 1 byte for number of BPs (technically a VarInt, yet there's always just zero or one) // 1 byte for number of BPs (technically a VarInt, yet there's always just zero or one)
1 + Bulletproof::fee_weight(protocol.bp_plus(), outputs) + 1 +
(inputs * (Clsag::fee_weight(protocol.ring_len()) + 32)) Bulletproof::fee_weight(bp_plus, outputs) +
// There's both the CLSAG and the pseudo-out
(inputs * (Clsag::fee_weight(ring_len) + 32))
} }
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> { pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
@@ -383,8 +385,14 @@ impl RctSignatures {
} }
} }
pub fn fee_weight(protocol: Protocol, inputs: usize, outputs: usize, fee: u64) -> usize { pub fn fee_weight(
RctBase::fee_weight(outputs, fee) + RctPrunable::fee_weight(protocol, inputs, outputs) bp_plus: bool,
ring_len: usize,
inputs: usize,
outputs: usize,
fee: u64,
) -> usize {
RctBase::fee_weight(outputs, fee) + RctPrunable::fee_weight(bp_plus, ring_len, inputs, outputs)
} }
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {

View File

@@ -13,7 +13,6 @@ use crate::{
primitives::keccak256, primitives::keccak256,
ring_signatures::RingSignature, ring_signatures::RingSignature,
ringct::{bulletproofs::Bulletproof, RctType, RctBase, RctPrunable, RctSignatures}, ringct::{bulletproofs::Bulletproof, RctType, RctBase, RctPrunable, RctSignatures},
Protocol,
}; };
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
@@ -254,15 +253,19 @@ pub struct Transaction {
} }
impl Transaction { impl Transaction {
// TODO: Replace ring_len, decoy_weights for &[&[usize]], where the inner buf is the decoy
// offsets
pub fn fee_weight( pub fn fee_weight(
protocol: Protocol, view_tags: bool,
bp_plus: bool,
ring_len: usize,
decoy_weights: &[usize], decoy_weights: &[usize],
outputs: usize, outputs: usize,
extra: usize, extra: usize,
fee: u64, fee: u64,
) -> usize { ) -> usize {
TransactionPrefix::fee_weight(decoy_weights, outputs, protocol.view_tags(), extra) + TransactionPrefix::fee_weight(decoy_weights, outputs, view_tags, extra) +
RctSignatures::fee_weight(protocol, decoy_weights.len(), outputs, fee) RctSignatures::fee_weight(bp_plus, ring_len, decoy_weights.len(), outputs, fee)
} }
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> { pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {

View File

@@ -4,7 +4,10 @@
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
use core::ops::Deref; use core::ops::Deref;
use std_shims::collections::{HashSet, HashMap}; use std_shims::{
io,
collections::{HashSet, HashMap},
};
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing}; use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
@@ -15,9 +18,9 @@ use curve25519_dalek::{
}; };
use monero_serai::{ use monero_serai::{
io::write_varint, io::{read_byte, read_u16, write_varint},
primitives::{Commitment, keccak256, keccak256_to_scalar}, primitives::{Commitment, keccak256, keccak256_to_scalar},
ringct::EncryptedAmount, ringct::{RctType, EncryptedAmount},
transaction::Input, transaction::Input,
}; };
pub use monero_serai as monero; pub use monero_serai as monero;
@@ -58,6 +61,137 @@ pub use send::TransactionMachine;
#[cfg(test)] #[cfg(test)]
mod tests; 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 { fn key_image_sort(x: &EdwardsPoint, y: &EdwardsPoint) -> core::cmp::Ordering {
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse() 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 zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use monero_serai::Protocol;
use crate::{ use crate::{
address::MoneroAddress, FeeRate, SpendableOutput, Change, Decoys, SignableTransaction, Protocol, address::MoneroAddress, FeeRate, SpendableOutput, Change, Decoys, SignableTransaction,
TransactionError, extra::MAX_ARBITRARY_DATA_SIZE, TransactionError, extra::MAX_ARBITRARY_DATA_SIZE,
}; };

View File

@@ -26,7 +26,6 @@ pub use monero_rpc::{FeePriority, FeeRate};
use monero_serai::{ use monero_serai::{
io::*, io::*,
primitives::{Commitment, keccak256}, primitives::{Commitment, keccak256},
Protocol,
ringct::{ ringct::{
generate_key_image, generate_key_image,
clsag::{ClsagError, ClsagContext, Clsag}, clsag::{ClsagError, ClsagContext, Clsag},
@@ -36,6 +35,7 @@ use monero_serai::{
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction}, transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
}; };
use crate::{ use crate::{
Protocol,
address::{Network, AddressSpec, MoneroAddress}, address::{Network, AddressSpec, MoneroAddress},
ViewPair, SpendableOutput, Decoys, PaymentId, ExtraField, Extra, key_image_sort, uniqueness, ViewPair, SpendableOutput, Decoys, PaymentId, ExtraField, Extra, key_image_sort, uniqueness,
shared_key, commitment_mask, compact_amount_encryption, shared_key, commitment_mask, compact_amount_encryption,
@@ -239,7 +239,15 @@ fn calculate_weight_and_fee(
let mut iters = 0; let mut iters = 0;
let max_iters = 5; let max_iters = 5;
while !done { 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); 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_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{ use monero_wallet::{
monero::{transaction::Transaction, Protocol, DEFAULT_LOCK_WINDOW}, monero::{transaction::Transaction, DEFAULT_LOCK_WINDOW},
rpc::{OutputResponse, Rpc}, rpc::{OutputResponse, Rpc},
SpendableOutput, SpendableOutput,
}; };

View File

@@ -109,9 +109,6 @@ pub async fn rpc() -> SimpleRequestRpc {
// Mine 40 blocks to ensure decoy availability // Mine 40 blocks to ensure decoy availability
rpc.generate_blocks(&addr, 40).await.unwrap(); rpc.generate_blocks(&addr, 40).await.unwrap();
// Make sure we recognize the protocol
rpc.get_protocol().await.unwrap();
rpc rpc
} }
@@ -171,6 +168,7 @@ macro_rules! test {
}; };
use monero_wallet::{ use monero_wallet::{
Protocol,
address::{Network, AddressSpec}, address::{Network, AddressSpec},
ViewPair, Scanner, Change, DecoySelection, Decoys, FeePriority, ViewPair, Scanner, Change, DecoySelection, Decoys, FeePriority,
SignableTransaction, SignableTransactionBuilder, SignableTransaction, SignableTransactionBuilder,
@@ -212,11 +210,11 @@ macro_rules! test {
let miner_tx = get_miner_tx_output(&rpc, &view).await; 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( let builder = SignableTransactionBuilder::new(
protocol, protocol,
rpc.get_fee_rate(protocol, FeePriority::Unimportant).await.unwrap(), rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
Change::new( Change::new(
&ViewPair::new( &ViewPair::new(
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE, &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_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{ use monero_wallet::{
monero::{transaction::Transaction, Protocol}, monero::transaction::Transaction, Protocol, rpc::Rpc, extra::Extra, address::SubaddressIndex,
rpc::Rpc,
extra::Extra,
address::SubaddressIndex,
ReceivedOutput, SpendableOutput, DecoySelection, Decoys, SignableTransactionBuilder, ReceivedOutput, SpendableOutput, DecoySelection, Decoys, SignableTransactionBuilder,
}; };
@@ -109,7 +106,7 @@ test!(
let mut builder = SignableTransactionBuilder::new( let mut builder = SignableTransactionBuilder::new(
protocol, protocol,
rpc.get_fee_rate(protocol, FeePriority::Unimportant).await.unwrap(), rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
Change::new(&change_view, false), Change::new(&change_view, false),
); );
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await; add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await;
@@ -293,7 +290,7 @@ test!(
let mut builder = SignableTransactionBuilder::new( let mut builder = SignableTransactionBuilder::new(
protocol, protocol,
rpc.get_fee_rate(protocol, FeePriority::Unimportant).await.unwrap(), rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
Change::fingerprintable(None), Change::fingerprintable(None),
); );
add_inputs(protocol, &rpc, vec![outputs.first().unwrap().clone()], &mut builder).await; 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 // TODO: Needs https://github.com/monero-project/monero/pull/9260
// let fee_rate = daemon_rpc // let fee_rate = daemon_rpc
// .get_fee_rate(daemon_rpc.get_protocol().await.unwrap(), FeePriority::Unimportant) // .get_fee_rate(FeePriority::Unimportant)
// .await // .await
// .unwrap(); // .unwrap();

View File

@@ -15,7 +15,8 @@ use frost::{curve::Ed25519, ThresholdKeys};
use monero_simple_request_rpc::SimpleRequestRpc; use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{ use monero_wallet::{
monero::{Protocol, ringct::RctType, transaction::Transaction, block::Block}, monero::{ringct::RctType, transaction::Transaction, block::Block},
Protocol,
rpc::{RpcError, Rpc}, rpc::{RpcError, Rpc},
ViewPair, Scanner, ViewPair, Scanner,
address::{Network as MoneroNetwork, SubaddressIndex, AddressSpec}, address::{Network as MoneroNetwork, SubaddressIndex, AddressSpec},
@@ -315,25 +316,14 @@ impl Monero {
let fee_rate = self.median_fee(&block_for_fee).await?; let fee_rate = self.median_fee(&block_for_fee).await?;
// Get the protocol for the specified block number // Get the protocol for the specified block number
// For now, this should just be v16, the latest deployed protocol, since there's no upcoming
// hard fork to be mindful of
let get_protocol = || Protocol::v16;
#[cfg(not(test))] #[cfg(not(test))]
let protocol = get_protocol(); let protocol = Protocol::try_from(block_for_fee.header.major_version)
.map_err(|()| NetworkError::ConnectionError)?;
// If this is a test, we won't be using a mainnet node and need a distinct protocol // If this is a test, we won't be using a mainnet node and need a distinct protocol
// determination // determination
// Just use whatever the node expects // Just use whatever the node expects
#[cfg(test)] #[cfg(test)]
let protocol = self.rpc.get_protocol().await.unwrap(); let protocol = Protocol::try_from(self.rpc.get_protocol().await.unwrap()).unwrap();
// Hedge against the above codegen failing by having an always included runtime check
if !cfg!(test) {
assert_eq!(protocol, get_protocol());
}
// Check a fork hasn't occurred which this processor hasn't been updated for
assert_eq!(protocol, self.rpc.get_protocol().await.map_err(map_rpc_err)?);
let spendable_outputs = inputs.iter().map(|input| input.0.clone()).collect::<Vec<_>>(); let spendable_outputs = inputs.iter().map(|input| input.0.clone()).collect::<Vec<_>>();
@@ -774,7 +764,7 @@ impl Network for Monero {
// The dust should always be sufficient for the fee // The dust should always be sufficient for the fee
let fee = Monero::DUST; let fee = Monero::DUST;
let protocol = self.rpc.get_protocol().await.unwrap(); let protocol = Protocol::try_from(self.rpc.get_protocol().await.unwrap()).unwrap();
let decoys = Decoys::fingerprintable_canonical_select( let decoys = Decoys::fingerprintable_canonical_select(
&mut OsRng, &mut OsRng,
@@ -795,7 +785,7 @@ impl Network for Monero {
vec![(address.into(), amount - fee)], vec![(address.into(), amount - fee)],
&Change::fingerprintable(Some(Self::test_address().into())), &Change::fingerprintable(Some(Self::test_address().into())),
vec![], vec![],
self.rpc.get_fee_rate(protocol, FeePriority::Unimportant).await.unwrap(), self.rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
) )
.unwrap() .unwrap()
.sign(&mut OsRng, &Zeroizing::new(Scalar::ONE.0)) .sign(&mut OsRng, &Zeroizing::new(Scalar::ONE.0))

View File

@@ -349,7 +349,8 @@ async fn mint_and_burn_test() {
{ {
use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, scalar::Scalar};
use monero_wallet::{ use monero_wallet::{
monero::{io::decompress_point, Protocol, transaction::Timelock}, monero::{io::decompress_point, transaction::Timelock},
Protocol,
rpc::Rpc, rpc::Rpc,
ViewPair, Scanner, DecoySelection, Decoys, Change, FeePriority, SignableTransaction, ViewPair, Scanner, DecoySelection, Decoys, Change, FeePriority, SignableTransaction,
address::{Network, AddressType, AddressMeta, MoneroAddress}, address::{Network, AddressType, AddressMeta, MoneroAddress},
@@ -397,7 +398,7 @@ async fn mint_and_burn_test() {
)], )],
&Change::new(&view_pair, false), &Change::new(&view_pair, false),
vec![Shorthand::transfer(None, serai_addr).encode()], vec![Shorthand::transfer(None, serai_addr).encode()],
rpc.get_fee_rate(Protocol::v16, FeePriority::Unimportant).await.unwrap(), rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
) )
.unwrap() .unwrap()
.sign(&mut OsRng, &Zeroizing::new(Scalar::ONE)) .sign(&mut OsRng, &Zeroizing::new(Scalar::ONE))

View File

@@ -438,7 +438,8 @@ impl Wallet {
use monero_simple_request_rpc::SimpleRequestRpc; use monero_simple_request_rpc::SimpleRequestRpc;
use monero_wallet::{ use monero_wallet::{
rpc::Rpc, rpc::Rpc,
monero::{Protocol, io::decompress_point}, monero::io::decompress_point,
Protocol,
address::{Network, AddressType, AddressMeta, Address}, address::{Network, AddressType, AddressMeta, Address},
SpendableOutput, DecoySelection, Decoys, Change, FeePriority, Scanner, SpendableOutput, DecoySelection, Decoys, Change, FeePriority, Scanner,
SignableTransaction, SignableTransaction,
@@ -492,7 +493,7 @@ impl Wallet {
vec![(to_addr, AMOUNT)], vec![(to_addr, AMOUNT)],
&Change::new(view_pair, false), &Change::new(view_pair, false),
data, data,
rpc.get_fee_rate(Protocol::v16, FeePriority::Unimportant).await.unwrap(), rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
) )
.unwrap() .unwrap()
.sign(&mut OsRng, spend_key) .sign(&mut OsRng, spend_key)