zcash-sync/src/note_selection/fill.rs

152 lines
5.9 KiB
Rust

use std::cmp::min;
use zcash_address::{AddressKind, ZcashAddress};
use zcash_address::unified::{Container, Receiver};
use zcash_primitives::memo::MemoBytes;
use crate::note_selection::types::{PrivacyPolicy, Fill, Execution, Order, Pool, PoolAllocation, Destination, PoolPrecedence};
/// Decode address and return it as an order
///
pub fn decode(id: u32, address: &str, amount: u64, memo: MemoBytes) -> anyhow::Result<Order> {
let address = ZcashAddress::try_from_encoded(address)?;
let mut order = Order::default();
order.id = id;
match address.kind {
AddressKind::Sprout(_) => {}
AddressKind::Sapling(data) => {
let destination = Destination::Sapling(data);
order.destinations[Pool::Sapling as usize] = Some(destination);
}
AddressKind::Unified(unified_address) => {
for address in unified_address.items() {
match address {
Receiver::Orchard(data) => {
let destination = Destination::Orchard(data);
order.destinations[Pool::Orchard as usize] = Some(destination);
}
Receiver::Sapling(data) => {
let destination = Destination::Sapling(data);
order.destinations[Pool::Sapling as usize] = Some(destination);
}
Receiver::P2pkh(data) => {
let destination = Destination::Transparent(data);
order.destinations[Pool::Transparent as usize] = Some(destination);
}
Receiver::P2sh(_) => {}
Receiver::Unknown { .. } => {}
}
}
}
AddressKind::P2pkh(data) => {
let destination = Destination::Transparent(data);
order.destinations[Pool::Transparent as usize] = Some(destination);
}
AddressKind::P2sh(_) => {}
}
order.amount = amount;
order.memo = memo;
Ok(order)
}
pub fn execute_orders(orders: &mut [Order], initial_pool: &PoolAllocation, use_transparent: bool, use_shielded: bool,
privacy_policy: PrivacyPolicy, precedence: &PoolPrecedence) -> anyhow::Result<Execution> {
let mut allocation: PoolAllocation = PoolAllocation::default();
let mut fills = vec![];
for order in orders.iter_mut() {
let order_precedence = if order.is_fee { precedence } else { order.priority.to_pool_precedence() };
// log::info!("Order {:?}", order);
// Direct Shielded Fill - s2s, o2o
// t2t only for fees
if use_shielded {
for &pool in order_precedence {
if pool == Pool::Transparent && !order.is_fee { continue }
if order.destinations[pool as usize].is_some() {
fill_order(pool, pool, order, initial_pool, &mut allocation, &mut fills);
}
}
}
if privacy_policy != PrivacyPolicy::SamePoolOnly {
// Indirect Shielded - z2z: s2o, o2s
for &pool in order_precedence {
if order.destinations[pool as usize].is_none() { continue }
if !use_shielded { continue }
if let Some(from_pool) = pool.other_shielded() {
fill_order(from_pool, pool, order, initial_pool, &mut allocation, &mut fills);
}
}
if privacy_policy == PrivacyPolicy::AnyPool {
// Other - s2t, o2t, t2s, t2o
for &pool in order_precedence {
if order.destinations[pool as usize].is_none() { continue }
match pool {
Pool::Transparent if use_shielded => {
for &from_pool in precedence {
if from_pool != Pool::Transparent {
fill_order(from_pool, pool, order, initial_pool, &mut allocation, &mut fills);
}
}
}
Pool::Sapling | Pool::Orchard if use_transparent => {
fill_order(Pool::Transparent, pool, order, initial_pool, &mut allocation, &mut fills);
}
_ => {}
};
}
// t2t
if use_transparent && order.destinations[Pool::Transparent as usize].is_some() {
fill_order(Pool::Transparent, Pool::Transparent, order, initial_pool, &mut allocation, &mut fills);
}
}
}
}
let execution = Execution {
allocation,
fills,
};
Ok(execution)
}
fn fill_order(from: Pool, to: Pool, order: &mut Order, initial_pool: &PoolAllocation,
fills: &mut PoolAllocation, executions: &mut Vec<Fill>) {
let from = from as usize;
let to = to as usize;
let destination = order.destinations[to].as_ref().unwrap(); // Checked by caller
let order_remaining = order.amount - order.filled;
let pool_remaining = initial_pool.0[from] - fills.0[from];
let amount = min(pool_remaining, order_remaining);
order.filled += amount;
fills.0[from] += amount;
if amount > 0 {
let execution = Fill {
id_order: order.id,
destination: destination.clone(),
amount,
memo: order.memo.clone(),
is_fee: order.is_fee,
};
log::debug!("{:?}", execution);
executions.push(execution);
}
assert!(order.amount == order.filled || initial_pool.0[from] == fills.0[from]); // fill must be to the max
}
impl Pool {
fn other_shielded(&self) -> Option<Self> {
match self {
Pool::Transparent => None,
Pool::Sapling => Some(Pool::Orchard),
Pool::Orchard => Some(Pool::Sapling),
}
}
}
#[cfg(test)]
mod tests {
}