mirror of
https://github.com/serai-dex/serai.git
synced 2024-10-30 00:47:43 +00:00
Differentiate Rpc from DecoyRpc
Enables using a locally backed decoy DB.
This commit is contained in:
parent
ed662568e2
commit
774424b70b
4 changed files with 202 additions and 148 deletions
|
@ -3,7 +3,6 @@ use monero_address::{Network, MoneroAddress};
|
||||||
// monero-rpc doesn't include a transport
|
// monero-rpc doesn't include a transport
|
||||||
// We can't include the simple-request crate there as then we'd have a cyclical dependency
|
// We can't include the simple-request crate there as then we'd have a cyclical dependency
|
||||||
// Accordingly, we test monero-rpc here (implicitly testing the simple-request transport)
|
// Accordingly, we test monero-rpc here (implicitly testing the simple-request transport)
|
||||||
use monero_rpc::*;
|
|
||||||
use monero_simple_request_rpc::*;
|
use monero_simple_request_rpc::*;
|
||||||
|
|
||||||
const ADDRESS: &str =
|
const ADDRESS: &str =
|
||||||
|
@ -11,6 +10,8 @@ const ADDRESS: &str =
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_rpc() {
|
async fn test_rpc() {
|
||||||
|
use monero_rpc::Rpc;
|
||||||
|
|
||||||
let rpc =
|
let rpc =
|
||||||
SimpleRequestRpc::new("http://serai:seraidex@127.0.0.1:18081".to_string()).await.unwrap();
|
SimpleRequestRpc::new("http://serai:seraidex@127.0.0.1:18081".to_string()).await.unwrap();
|
||||||
|
|
||||||
|
@ -52,17 +53,35 @@ async fn test_rpc() {
|
||||||
}
|
}
|
||||||
assert_eq!(blocks, actual_blocks);
|
assert_eq!(blocks, actual_blocks);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_decoy_rpc() {
|
||||||
|
use monero_rpc::{Rpc, DecoyRpc};
|
||||||
|
|
||||||
|
let rpc =
|
||||||
|
SimpleRequestRpc::new("http://serai:seraidex@127.0.0.1:18081".to_string()).await.unwrap();
|
||||||
|
|
||||||
// Test get_output_distribution
|
// Test get_output_distribution
|
||||||
// It's documented to take two inclusive block numbers
|
// It's documented to take two inclusive block numbers
|
||||||
{
|
{
|
||||||
let height = rpc.get_height().await.unwrap();
|
let distribution_len = rpc.get_output_distribution_len().await.unwrap();
|
||||||
|
assert_eq!(distribution_len, rpc.get_height().await.unwrap());
|
||||||
|
|
||||||
rpc.get_output_distribution(0 ..= height).await.unwrap_err();
|
rpc.get_output_distribution(0 ..= distribution_len).await.unwrap_err();
|
||||||
assert_eq!(rpc.get_output_distribution(0 .. height).await.unwrap().len(), height);
|
assert_eq!(
|
||||||
|
rpc.get_output_distribution(0 .. distribution_len).await.unwrap().len(),
|
||||||
|
distribution_len
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(rpc.get_output_distribution(0 .. (height - 1)).await.unwrap().len(), height - 1);
|
assert_eq!(
|
||||||
assert_eq!(rpc.get_output_distribution(1 .. height).await.unwrap().len(), height - 1);
|
rpc.get_output_distribution(0 .. (distribution_len - 1)).await.unwrap().len(),
|
||||||
|
distribution_len - 1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
rpc.get_output_distribution(1 .. distribution_len).await.unwrap().len(),
|
||||||
|
distribution_len - 1
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(rpc.get_output_distribution(0 ..= 0).await.unwrap().len(), 1);
|
assert_eq!(rpc.get_output_distribution(0 ..= 0).await.unwrap().len(), 1);
|
||||||
assert_eq!(rpc.get_output_distribution(0 ..= 1).await.unwrap().len(), 2);
|
assert_eq!(rpc.get_output_distribution(0 ..= 1).await.unwrap().len(), 2);
|
||||||
|
|
|
@ -492,6 +492,129 @@ pub trait Rpc: Sync + Clone + Debug {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the currently estimated fee rate from the node.
|
||||||
|
///
|
||||||
|
/// This may be manipulated to unsafe levels and MUST be sanity checked.
|
||||||
|
///
|
||||||
|
/// This MUST NOT be expected to be deterministic in any way.
|
||||||
|
// TODO: Take a sanity check argument
|
||||||
|
async fn get_fee_rate(&self, priority: FeePriority) -> Result<FeeRate, RpcError> {
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct FeeResponse {
|
||||||
|
status: String,
|
||||||
|
fees: Option<Vec<u64>>,
|
||||||
|
fee: u64,
|
||||||
|
quantization_mask: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let res: FeeResponse = self
|
||||||
|
.json_rpc_call(
|
||||||
|
"get_fee_estimate",
|
||||||
|
Some(json!({ "grace_blocks": GRACE_BLOCKS_FOR_FEE_ESTIMATE })),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if res.status != "OK" {
|
||||||
|
Err(RpcError::InvalidFee)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(fees) = res.fees {
|
||||||
|
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
|
||||||
|
// src/wallet/wallet2.cpp#L7615-L7620
|
||||||
|
let priority_idx = usize::try_from(if priority.fee_priority() >= 4 {
|
||||||
|
3
|
||||||
|
} else {
|
||||||
|
priority.fee_priority().saturating_sub(1)
|
||||||
|
})
|
||||||
|
.map_err(|_| RpcError::InvalidPriority)?;
|
||||||
|
|
||||||
|
if priority_idx >= fees.len() {
|
||||||
|
Err(RpcError::InvalidPriority)
|
||||||
|
} else {
|
||||||
|
FeeRate::new(fees[priority_idx], res.quantization_mask)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Publish a transaction.
|
||||||
|
async fn publish_transaction(&self, tx: &Transaction) -> Result<(), RpcError> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct SendRawResponse {
|
||||||
|
status: String,
|
||||||
|
double_spend: bool,
|
||||||
|
fee_too_low: bool,
|
||||||
|
invalid_input: bool,
|
||||||
|
invalid_output: bool,
|
||||||
|
low_mixin: bool,
|
||||||
|
not_relayed: bool,
|
||||||
|
overspend: bool,
|
||||||
|
too_big: bool,
|
||||||
|
too_few_outputs: bool,
|
||||||
|
reason: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
let res: SendRawResponse = self
|
||||||
|
.rpc_call(
|
||||||
|
"send_raw_transaction",
|
||||||
|
Some(json!({ "tx_as_hex": hex::encode(tx.serialize()), "do_sanity_checks": false })),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if res.status != "OK" {
|
||||||
|
Err(RpcError::InvalidTransaction(tx.hash()))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate blocks, with the specified address receiving the block reward.
|
||||||
|
///
|
||||||
|
/// Returns the hashes of the generated blocks and the last block's number.
|
||||||
|
async fn generate_blocks<const ADDR_BYTES: u128>(
|
||||||
|
&self,
|
||||||
|
address: &Address<ADDR_BYTES>,
|
||||||
|
block_count: usize,
|
||||||
|
) -> Result<(Vec<[u8; 32]>, usize), RpcError> {
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct BlocksResponse {
|
||||||
|
blocks: Vec<String>,
|
||||||
|
height: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = self
|
||||||
|
.json_rpc_call::<BlocksResponse>(
|
||||||
|
"generateblocks",
|
||||||
|
Some(json!({
|
||||||
|
"wallet_address": address.to_string(),
|
||||||
|
"amount_of_blocks": block_count
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut blocks = Vec::with_capacity(res.blocks.len());
|
||||||
|
for block in res.blocks {
|
||||||
|
blocks.push(hash_hex(&block)?);
|
||||||
|
}
|
||||||
|
Ok((blocks, res.height))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the output indexes of the specified transaction.
|
/// Get the output indexes of the specified transaction.
|
||||||
async fn get_o_indexes(&self, hash: [u8; 32]) -> Result<Vec<u64>, RpcError> {
|
async fn get_o_indexes(&self, hash: [u8; 32]) -> Result<Vec<u64>, RpcError> {
|
||||||
// Given the immaturity of Rust epee libraries, this is a homegrown one which is only validated
|
// Given the immaturity of Rust epee libraries, this is a homegrown one which is only validated
|
||||||
|
@ -673,10 +796,57 @@ pub trait Rpc: Sync + Clone + Debug {
|
||||||
})()
|
})()
|
||||||
.map_err(|e| RpcError::InvalidNode(format!("invalid binary response: {e:?}")))
|
.map_err(|e| RpcError::InvalidNode(format!("invalid binary response: {e:?}")))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for any object which can be used to select decoys.
|
||||||
|
///
|
||||||
|
/// An implementation is provided for any satisfier of `Rpc`. The benefit of this trait is the
|
||||||
|
/// ability to select decoys off of a locally stored output distribution, preventing potential
|
||||||
|
/// attacks a remote node can perform.
|
||||||
|
#[async_trait]
|
||||||
|
pub trait DecoyRpc: Sync + Clone + Debug {
|
||||||
|
/// Get the length of the output distribution.
|
||||||
|
///
|
||||||
|
/// This is equivalent to the hight of the blockchain it's for. This is intended to be cheaper
|
||||||
|
/// than fetching the entire output distribution.
|
||||||
|
async fn get_output_distribution_len(&self) -> Result<usize, RpcError>;
|
||||||
|
|
||||||
/// Get the output distribution.
|
/// Get the output distribution.
|
||||||
///
|
///
|
||||||
/// `range` is in terms of block numbers.
|
/// `range` is in terms of block numbers.
|
||||||
|
async fn get_output_distribution(
|
||||||
|
&self,
|
||||||
|
range: impl Send + RangeBounds<usize>,
|
||||||
|
) -> Result<Vec<u64>, RpcError>;
|
||||||
|
|
||||||
|
/// Get the specified outputs from the RingCT (zero-amount) pool.
|
||||||
|
async fn get_outs(&self, indexes: &[u64]) -> Result<Vec<OutputResponse>, RpcError>;
|
||||||
|
|
||||||
|
/// Get the specified outputs from the RingCT (zero-amount) pool, but only return them if their
|
||||||
|
/// timelock has been satisfied.
|
||||||
|
///
|
||||||
|
/// The timelock being satisfied is distinct from being free of the 10-block lock applied to all
|
||||||
|
/// Monero transactions.
|
||||||
|
///
|
||||||
|
/// The node is trusted for if the output is unlocked unless `fingerprintable_canonical` is set
|
||||||
|
/// to true. If `fingerprintable_canonical` is set to true, the node's local view isn't used, yet
|
||||||
|
/// the transaction's timelock is checked to be unlocked at the specified `height`. This offers a
|
||||||
|
/// canonical decoy selection, yet is fingerprintable as time-based timelocks aren't evaluated
|
||||||
|
/// (and considered locked, preventing their selection).
|
||||||
|
async fn get_unlocked_outputs(
|
||||||
|
&self,
|
||||||
|
indexes: &[u64],
|
||||||
|
height: usize,
|
||||||
|
fingerprintable_canonical: bool,
|
||||||
|
) -> Result<Vec<Option<[EdwardsPoint; 2]>>, RpcError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<R: Rpc> DecoyRpc for R {
|
||||||
|
async fn get_output_distribution_len(&self) -> Result<usize, RpcError> {
|
||||||
|
<Self as Rpc>::get_height(self).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_output_distribution(
|
async fn get_output_distribution(
|
||||||
&self,
|
&self,
|
||||||
range: impl Send + RangeBounds<usize>,
|
range: impl Send + RangeBounds<usize>,
|
||||||
|
@ -743,7 +913,6 @@ pub trait Rpc: Sync + Clone + Debug {
|
||||||
Ok(distribution)
|
Ok(distribution)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the specified outputs from the RingCT (zero-amount) pool.
|
|
||||||
async fn get_outs(&self, indexes: &[u64]) -> Result<Vec<OutputResponse>, RpcError> {
|
async fn get_outs(&self, indexes: &[u64]) -> Result<Vec<OutputResponse>, RpcError> {
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct OutsResponse {
|
struct OutsResponse {
|
||||||
|
@ -771,17 +940,6 @@ pub trait Rpc: Sync + Clone + Debug {
|
||||||
Ok(res.outs)
|
Ok(res.outs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the specified outputs from the RingCT (zero-amount) pool, but only return them if their
|
|
||||||
/// timelock has been satisfied.
|
|
||||||
///
|
|
||||||
/// The timelock being satisfied is distinct from being free of the 10-block lock applied to all
|
|
||||||
/// Monero transactions.
|
|
||||||
///
|
|
||||||
/// The node is trusted for if the output is unlocked unless `fingerprintable_canonical` is set
|
|
||||||
/// to true. If `fingerprintable_canonical` is set to true, the node's local view isn't used, yet
|
|
||||||
/// the transaction's timelock is checked to be unlocked at the specified `height`. This offers a
|
|
||||||
/// canonical decoy selection, yet is fingerprintable as time-based timelocks aren't evaluated
|
|
||||||
/// (and considered locked, preventing their selection).
|
|
||||||
async fn get_unlocked_outputs(
|
async fn get_unlocked_outputs(
|
||||||
&self,
|
&self,
|
||||||
indexes: &[u64],
|
indexes: &[u64],
|
||||||
|
@ -830,127 +988,4 @@ pub trait Rpc: Sync + Clone + Debug {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the currently estimated fee rate from the node.
|
|
||||||
///
|
|
||||||
/// This may be manipulated to unsafe levels and MUST be sanity checked.
|
|
||||||
///
|
|
||||||
/// This MUST NOT be expected to be deterministic in any way.
|
|
||||||
// TODO: Take a sanity check argument
|
|
||||||
async fn get_fee_rate(&self, priority: FeePriority) -> Result<FeeRate, RpcError> {
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct FeeResponse {
|
|
||||||
status: String,
|
|
||||||
fees: Option<Vec<u64>>,
|
|
||||||
fee: u64,
|
|
||||||
quantization_mask: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
let res: FeeResponse = self
|
|
||||||
.json_rpc_call(
|
|
||||||
"get_fee_estimate",
|
|
||||||
Some(json!({ "grace_blocks": GRACE_BLOCKS_FOR_FEE_ESTIMATE })),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if res.status != "OK" {
|
|
||||||
Err(RpcError::InvalidFee)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(fees) = res.fees {
|
|
||||||
// https://github.com/monero-project/monero/blob/94e67bf96bbc010241f29ada6abc89f49a81759c/
|
|
||||||
// src/wallet/wallet2.cpp#L7615-L7620
|
|
||||||
let priority_idx = usize::try_from(if priority.fee_priority() >= 4 {
|
|
||||||
3
|
|
||||||
} else {
|
|
||||||
priority.fee_priority().saturating_sub(1)
|
|
||||||
})
|
|
||||||
.map_err(|_| RpcError::InvalidPriority)?;
|
|
||||||
|
|
||||||
if priority_idx >= fees.len() {
|
|
||||||
Err(RpcError::InvalidPriority)
|
|
||||||
} else {
|
|
||||||
FeeRate::new(fees[priority_idx], res.quantization_mask)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Publish a transaction.
|
|
||||||
async fn publish_transaction(&self, tx: &Transaction) -> Result<(), RpcError> {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct SendRawResponse {
|
|
||||||
status: String,
|
|
||||||
double_spend: bool,
|
|
||||||
fee_too_low: bool,
|
|
||||||
invalid_input: bool,
|
|
||||||
invalid_output: bool,
|
|
||||||
low_mixin: bool,
|
|
||||||
not_relayed: bool,
|
|
||||||
overspend: bool,
|
|
||||||
too_big: bool,
|
|
||||||
too_few_outputs: bool,
|
|
||||||
reason: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
let res: SendRawResponse = self
|
|
||||||
.rpc_call(
|
|
||||||
"send_raw_transaction",
|
|
||||||
Some(json!({ "tx_as_hex": hex::encode(tx.serialize()), "do_sanity_checks": false })),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if res.status != "OK" {
|
|
||||||
Err(RpcError::InvalidTransaction(tx.hash()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate blocks, with the specified address receiving the block reward.
|
|
||||||
///
|
|
||||||
/// Returns the hashes of the generated blocks and the last block's number.
|
|
||||||
async fn generate_blocks<const ADDR_BYTES: u128>(
|
|
||||||
&self,
|
|
||||||
address: &Address<ADDR_BYTES>,
|
|
||||||
block_count: usize,
|
|
||||||
) -> Result<(Vec<[u8; 32]>, usize), RpcError> {
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct BlocksResponse {
|
|
||||||
blocks: Vec<String>,
|
|
||||||
height: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = self
|
|
||||||
.json_rpc_call::<BlocksResponse>(
|
|
||||||
"generateblocks",
|
|
||||||
Some(json!({
|
|
||||||
"wallet_address": address.to_string(),
|
|
||||||
"amount_of_blocks": block_count
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut blocks = Vec::with_capacity(res.blocks.len());
|
|
||||||
for block in res.blocks {
|
|
||||||
blocks.push(hash_hex(&block)?);
|
|
||||||
}
|
|
||||||
Ok((blocks, res.height))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use curve25519_dalek::{Scalar, EdwardsPoint};
|
||||||
use crate::{
|
use crate::{
|
||||||
DEFAULT_LOCK_WINDOW, COINBASE_LOCK_WINDOW, BLOCK_TIME,
|
DEFAULT_LOCK_WINDOW, COINBASE_LOCK_WINDOW, BLOCK_TIME,
|
||||||
primitives::{Commitment, Decoys},
|
primitives::{Commitment, Decoys},
|
||||||
rpc::{RpcError, Rpc},
|
rpc::{RpcError, DecoyRpc},
|
||||||
output::OutputData,
|
output::OutputData,
|
||||||
WalletOutput,
|
WalletOutput,
|
||||||
};
|
};
|
||||||
|
@ -24,7 +24,7 @@ const TIP_APPLICATION: f64 = (DEFAULT_LOCK_WINDOW * BLOCK_TIME) as f64;
|
||||||
|
|
||||||
async fn select_n(
|
async fn select_n(
|
||||||
rng: &mut (impl RngCore + CryptoRng),
|
rng: &mut (impl RngCore + CryptoRng),
|
||||||
rpc: &impl Rpc,
|
rpc: &impl DecoyRpc,
|
||||||
height: usize,
|
height: usize,
|
||||||
real_output: u64,
|
real_output: u64,
|
||||||
ring_len: usize,
|
ring_len: usize,
|
||||||
|
@ -33,7 +33,7 @@ async fn select_n(
|
||||||
if height < DEFAULT_LOCK_WINDOW {
|
if height < DEFAULT_LOCK_WINDOW {
|
||||||
Err(RpcError::InternalError("not enough blocks to select decoys".to_string()))?;
|
Err(RpcError::InternalError("not enough blocks to select decoys".to_string()))?;
|
||||||
}
|
}
|
||||||
if height > rpc.get_height().await? {
|
if height > rpc.get_output_distribution_len().await? {
|
||||||
Err(RpcError::InternalError(
|
Err(RpcError::InternalError(
|
||||||
"decoys being requested from blocks this node doesn't have".to_string(),
|
"decoys being requested from blocks this node doesn't have".to_string(),
|
||||||
))?;
|
))?;
|
||||||
|
@ -164,7 +164,7 @@ async fn select_n(
|
||||||
|
|
||||||
async fn select_decoys<R: RngCore + CryptoRng>(
|
async fn select_decoys<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
rpc: &impl Rpc,
|
rpc: &impl DecoyRpc,
|
||||||
ring_len: usize,
|
ring_len: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
input: &WalletOutput,
|
input: &WalletOutput,
|
||||||
|
@ -230,7 +230,7 @@ impl OutputWithDecoys {
|
||||||
/// Select decoys for this output.
|
/// Select decoys for this output.
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
rng: &mut (impl Send + Sync + RngCore + CryptoRng),
|
rng: &mut (impl Send + Sync + RngCore + CryptoRng),
|
||||||
rpc: &impl Rpc,
|
rpc: &impl DecoyRpc,
|
||||||
ring_len: usize,
|
ring_len: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
output: WalletOutput,
|
output: WalletOutput,
|
||||||
|
@ -249,7 +249,7 @@ impl OutputWithDecoys {
|
||||||
/// methodology.
|
/// methodology.
|
||||||
pub async fn fingerprintable_deterministic_new(
|
pub async fn fingerprintable_deterministic_new(
|
||||||
rng: &mut (impl Send + Sync + RngCore + CryptoRng),
|
rng: &mut (impl Send + Sync + RngCore + CryptoRng),
|
||||||
rpc: &impl Rpc,
|
rpc: &impl DecoyRpc,
|
||||||
ring_len: usize,
|
ring_len: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
output: WalletOutput,
|
output: WalletOutput,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use monero_simple_request_rpc::SimpleRequestRpc;
|
||||||
use monero_wallet::{
|
use monero_wallet::{
|
||||||
DEFAULT_LOCK_WINDOW,
|
DEFAULT_LOCK_WINDOW,
|
||||||
transaction::Transaction,
|
transaction::Transaction,
|
||||||
rpc::{OutputResponse, Rpc},
|
rpc::{OutputResponse, Rpc, DecoyRpc},
|
||||||
WalletOutput,
|
WalletOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue