Fix ycash

This commit is contained in:
Hanh 2022-11-23 16:59:22 +08:00
parent d0550c8908
commit 9d7141ae40
9 changed files with 85 additions and 64 deletions

View File

@ -479,13 +479,13 @@ pub unsafe extern "C" fn transaction_report(coin: u8, plan: *mut c_char) -> CRes
pub async unsafe extern "C" fn sign( pub async unsafe extern "C" fn sign(
coin: u8, coin: u8,
account: u32, account: u32,
tx: *mut c_char, tx_plan: *mut c_char,
_port: i64, _port: i64,
) -> CResult<*mut c_char> { ) -> CResult<*mut c_char> {
from_c_str!(tx); from_c_str!(tx_plan);
let res = async { let res = async {
let tx: TransactionPlan = serde_json::from_str(&tx)?; let tx_plan: TransactionPlan = serde_json::from_str(&tx_plan)?;
let raw_tx = crate::api::payment_v2::sign_plan(coin, account, &tx)?; let raw_tx = crate::api::payment_v2::sign_plan(coin, account, &tx_plan)?;
let tx_str = base64::encode(&raw_tx); let tx_str = base64::encode(&raw_tx);
Ok::<_, anyhow::Error>(tx_str) Ok::<_, anyhow::Error>(tx_str)
}; };

View File

@ -27,6 +27,7 @@ pub async fn build_tx_plan(
confirmations: u32, confirmations: u32,
) -> note_selection::Result<TransactionPlan> { ) -> note_selection::Result<TransactionPlan> {
let c = CoinConfig::get(coin); let c = CoinConfig::get(coin);
let network = c.chain.network();
let (fvk, checkpoint_height) = { let (fvk, checkpoint_height) = {
let db = c.db()?; let db = c.db()?;
let AccountData { fvk, .. } = db.get_account_info(account)?; let AccountData { fvk, .. } = db.get_account_info(account)?;
@ -48,7 +49,7 @@ pub async fn build_tx_plan(
while amount > 0 { while amount > 0 {
let a = min(amount, max_amount_per_note); let a = min(amount, max_amount_per_note);
let memo_bytes: MemoBytes = r.memo.clone().into(); let memo_bytes: MemoBytes = r.memo.clone().into();
let order = Order::new(id_order, &r.address, a, memo_bytes); let order = Order::new(network, id_order, &r.address, a, memo_bytes);
orders.push(order); orders.push(order);
amount -= a; amount -= a;
id_order += 1; id_order += 1;
@ -58,6 +59,7 @@ pub async fn build_tx_plan(
let config = TransactionBuilderConfig::new(&change_address); let config = TransactionBuilderConfig::new(&change_address);
let tx_plan = crate::note_selection::build_tx_plan::<FeeFlat>( let tx_plan = crate::note_selection::build_tx_plan::<FeeFlat>(
network,
&fvk, &fvk,
checkpoint_height, checkpoint_height,
&context.orchard_anchor, &context.orchard_anchor,

View File

@ -11,6 +11,7 @@ pub use utxo::fetch_utxos;
use crate::api::recipient::Recipient; use crate::api::recipient::Recipient;
use thiserror::Error; use thiserror::Error;
use zcash_primitives::consensus::Network;
use ua::decode; use ua::decode;
use zcash_primitives::memo::Memo; use zcash_primitives::memo::Memo;
@ -37,12 +38,12 @@ mod utxo;
pub const MAX_ATTEMPTS: usize = 10; pub const MAX_ATTEMPTS: usize = 10;
#[allow(dead_code)] #[allow(dead_code)]
pub fn recipients_to_orders(recipients: &[Recipient]) -> Result<Vec<Order>> { pub fn recipients_to_orders(network: &Network, recipients: &[Recipient]) -> Result<Vec<Order>> {
let orders: Result<Vec<_>> = recipients let orders: Result<Vec<_>> = recipients
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, r)| { .map(|(i, r)| {
let destinations = decode(&r.address)?; let destinations = decode(network, &r.address)?;
Ok::<_, TransactionBuilderError>(Order { Ok::<_, TransactionBuilderError>(Order {
id: i as u32, id: i as u32,
destinations, destinations,

View File

@ -26,8 +26,7 @@ use zcash_primitives::sapling::prover::TxProver;
use zcash_primitives::sapling::{Diversifier, Node, PaymentAddress, Rseed}; use zcash_primitives::sapling::{Diversifier, Node, PaymentAddress, Rseed};
use zcash_primitives::transaction::builder::Builder; use zcash_primitives::transaction::builder::Builder;
use zcash_primitives::transaction::components::{Amount, OutPoint, TxOut}; use zcash_primitives::transaction::components::{Amount, OutPoint, TxOut};
use zcash_primitives::transaction::sighash::SignableInput; use zcash_primitives::transaction::sighash::{SignableInput, signature_hash};
use zcash_primitives::transaction::sighash_v5::v5_signature_hash;
use zcash_primitives::transaction::txid::TxIdDigester; use zcash_primitives::transaction::txid::TxIdDigester;
use zcash_primitives::transaction::{Transaction, TransactionData, TxVersion}; use zcash_primitives::transaction::{Transaction, TransactionData, TxVersion};
use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey}; use zcash_primitives::zip32::{ExtendedFullViewingKey, ExtendedSpendingKey};
@ -41,7 +40,7 @@ pub struct SecretKeys {
pub struct TxBuilderContext { pub struct TxBuilderContext {
pub height: u32, pub height: u32,
pub sapling_anchor: [u8; 32], pub sapling_anchor: [u8; 32],
pub orchard_anchor: [u8; 32], pub orchard_anchor: Option<[u8; 32]>,
} }
impl TxBuilderContext { impl TxBuilderContext {
@ -52,9 +51,13 @@ impl TxBuilderContext {
let TreeCheckpoint { tree, .. } = db.get_tree_by_name(height, "sapling")?; let TreeCheckpoint { tree, .. } = db.get_tree_by_name(height, "sapling")?;
let hasher = SaplingHasher {}; let hasher = SaplingHasher {};
let sapling_anchor = tree.root(32, &SAPLING_ROOTS, &hasher); let sapling_anchor = tree.root(32, &SAPLING_ROOTS, &hasher);
let TreeCheckpoint { tree, .. } = db.get_tree_by_name(height, "orchard")?;
let hasher = OrchardHasher::new(); let orchard_anchor = if c.chain.has_unified() {
let orchard_anchor = tree.root(32, &ORCHARD_ROOTS, &hasher); let TreeCheckpoint { tree, .. } = db.get_tree_by_name(height, "orchard")?;
let hasher = OrchardHasher::new();
Some(tree.root(32, &ORCHARD_ROOTS, &hasher))
}
else { None };
let context = TxBuilderContext { let context = TxBuilderContext {
height, height,
sapling_anchor, sapling_anchor,
@ -208,10 +211,13 @@ pub fn build_tx(
orchard_bundle = Some(orchard_builder.build(rng.clone()).unwrap()); orchard_bundle = Some(orchard_builder.build(rng.clone()).unwrap());
} }
let consensus_branch_id = BranchId::for_height(network, BlockHeight::from_u32(plan.height));
let version = TxVersion::suggested_for_branch(consensus_branch_id);
let unauthed_tx: TransactionData<zcash_primitives::transaction::Unauthorized> = let unauthed_tx: TransactionData<zcash_primitives::transaction::Unauthorized> =
TransactionData::from_parts( TransactionData::from_parts(
TxVersion::Zip225, version,
BranchId::Nu5, consensus_branch_id,
0, 0,
BlockHeight::from_u32(plan.height + EXPIRY_HEIGHT), BlockHeight::from_u32(plan.height + EXPIRY_HEIGHT),
transparent_bundle, transparent_bundle,
@ -221,8 +227,8 @@ pub fn build_tx(
); );
let txid_parts = unauthed_tx.digest(TxIdDigester); let txid_parts = unauthed_tx.digest(TxIdDigester);
let sig_hash = v5_signature_hash(&unauthed_tx, &SignableInput::Shielded, &txid_parts); let sig_hash = signature_hash(&unauthed_tx, &SignableInput::Shielded, &txid_parts);
let sig_hash: [u8; 32] = sig_hash.as_bytes().try_into().unwrap(); let sig_hash: [u8; 32] = sig_hash.as_ref().clone();
let transparent_bundle = unauthed_tx let transparent_bundle = unauthed_tx
.transparent_bundle() .transparent_bundle()
@ -252,8 +258,8 @@ pub fn build_tx(
let tx_data: TransactionData<zcash_primitives::transaction::Authorized> = let tx_data: TransactionData<zcash_primitives::transaction::Authorized> =
TransactionData::from_parts( TransactionData::from_parts(
TxVersion::Zip225, version,
BranchId::Nu5, consensus_branch_id,
0, 0,
BlockHeight::from_u32(plan.height + EXPIRY_HEIGHT), BlockHeight::from_u32(plan.height + EXPIRY_HEIGHT),
transparent_bundle, transparent_bundle,

View File

@ -1,3 +1,4 @@
use zcash_primitives::consensus::Network;
use super::{types::*, Result}; use super::{types::*, Result};
use crate::note_selection::fee::FeeCalculator; use crate::note_selection::fee::FeeCalculator;
use crate::note_selection::ua::decode; use crate::note_selection::ua::decode;
@ -287,15 +288,17 @@ pub fn outputs_for_change(
} }
pub fn build_tx_plan<F: FeeCalculator>( pub fn build_tx_plan<F: FeeCalculator>(
network: &Network,
fvk: &str, fvk: &str,
height: u32, height: u32,
orchard_anchor: &Hash, orchard_anchor: &Option<Hash>,
utxos: &[UTXO], utxos: &[UTXO],
orders: &[Order], orders: &[Order],
config: &TransactionBuilderConfig, config: &TransactionBuilderConfig,
) -> Result<TransactionPlan> { ) -> Result<TransactionPlan> {
let mut fee = 0; let mut fee = 0;
println!("build_tx_plan");
for _ in 0..MAX_ATTEMPTS { for _ in 0..MAX_ATTEMPTS {
let balances = sum_utxos(utxos)?; let balances = sum_utxos(utxos)?;
let (groups, amounts) = group_orders(&orders, fee)?; let (groups, amounts) = group_orders(&orders, fee)?;
@ -311,7 +314,7 @@ pub fn build_tx_plan<F: FeeCalculator>(
let mut fills = fill(&orders, &groups, &amounts, &allocation)?; let mut fills = fill(&orders, &groups, &amounts, &allocation)?;
let (notes, change) = select_inputs(&utxos, &allocation)?; let (notes, change) = select_inputs(&utxos, &allocation)?;
let change_destinations = decode(&config.change_address)?; let change_destinations = decode(network, &config.change_address)?;
let change_outputs = outputs_for_change(&change_destinations, &change)?; let change_outputs = outputs_for_change(&change_destinations, &change)?;
fills.extend(change_outputs); fills.extend(change_outputs);
@ -320,7 +323,7 @@ pub fn build_tx_plan<F: FeeCalculator>(
let tx_plan = TransactionPlan { let tx_plan = TransactionPlan {
fvk: fvk.to_string(), fvk: fvk.to_string(),
height, height,
orchard_anchor: orchard_anchor.clone(), orchard_anchor: orchard_anchor.unwrap_or(Hash::default()),
spends: notes, spends: notes,
outputs: fills, outputs: fills,
net_chg, net_chg,

View File

@ -3,12 +3,12 @@ use super::types::*;
use super::TransactionBuilderError::NotEnoughFunds; use super::TransactionBuilderError::NotEnoughFunds;
use crate::note_selection::build_tx_plan; use crate::note_selection::build_tx_plan;
use crate::note_selection::fee::{FeeCalculator, FeeZIP327}; use crate::note_selection::fee::{FeeCalculator, FeeZIP327};
use crate::note_selection::optimize::{outputs_for_change, select_inputs}; use crate::note_selection::optimize::select_inputs;
use crate::note_selection::ua::decode;
use crate::Hash; use crate::Hash;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use serde::Serialize; use serde::Serialize;
use serde_json::Value; use serde_json::Value;
use zcash_primitives::consensus::Network;
use zcash_primitives::memo::MemoBytes; use zcash_primitives::memo::MemoBytes;
macro_rules! utxo { macro_rules! utxo {
@ -689,14 +689,6 @@ fn test_select_utxo() {
const CHANGE_ADDRESS: &str = "u1pncsxa8jt7aq37r8uvhjrgt7sv8a665hdw44rqa28cd9t6qqmktzwktw772nlle6skkkxwmtzxaan3slntqev03g70tzpky3c58hfgvfjkcky255cwqgfuzdjcktfl7pjalt5sl33se75pmga09etn9dplr98eq2g8cgmvgvx6jx2a2xhy39x96c6rumvlyt35whml87r064qdzw30e"; const CHANGE_ADDRESS: &str = "u1pncsxa8jt7aq37r8uvhjrgt7sv8a665hdw44rqa28cd9t6qqmktzwktw772nlle6skkkxwmtzxaan3slntqev03g70tzpky3c58hfgvfjkcky255cwqgfuzdjcktfl7pjalt5sl33se75pmga09etn9dplr98eq2g8cgmvgvx6jx2a2xhy39x96c6rumvlyt35whml87r064qdzw30e";
#[test]
fn test_change_fills() {
let _ = env_logger::try_init();
let destinations = decode(CHANGE_ADDRESS).unwrap();
let outputs = outputs_for_change(&destinations, &&PoolAllocation([0, 10000, 10000])).unwrap();
log::info!("{:?}", outputs);
}
#[test] #[test]
fn test_fees() { fn test_fees() {
let _ = env_logger::try_init(); let _ = env_logger::try_init();
@ -733,6 +725,7 @@ fn test_tx_plan() {
tso!(7, 70), tso!(7, 70),
]; ];
let tx_plan = build_tx_plan::<FeeZIP327>( let tx_plan = build_tx_plan::<FeeZIP327>(
&Network::MainNetwork,
"", "",
0, 0,
&Hash::default(), &Hash::default(),

View File

@ -200,8 +200,8 @@ impl Destination {
} }
impl Order { impl Order {
pub fn new(id: u32, address: &str, amount: u64, memo: MemoBytes) -> Self { pub fn new(network: &Network, id: u32, address: &str, amount: u64, memo: MemoBytes) -> Self {
let destinations = decode(address).unwrap(); let destinations = decode(network, address).unwrap();
Order { Order {
id, id,
destinations, destinations,

View File

@ -1,41 +1,53 @@
use super::types::*; use super::types::*;
use zcash_address::unified::{Container, Receiver}; use zcash_address::unified::{Container, Receiver};
use zcash_address::{AddressKind, ZcashAddress}; use zcash_address::{AddressKind, ZcashAddress};
use zcash_client_backend::encoding::{decode_payment_address, decode_transparent_address};
use zcash_primitives::consensus::{Network, Parameters};
use zcash_primitives::legacy::TransparentAddress;
pub fn decode(address: &str) -> anyhow::Result<[Option<Destination>; 3]> { pub fn decode(network: &Network, address: &str) -> anyhow::Result<[Option<Destination>; 3]> {
let mut destinations: [Option<Destination>; 3] = [None; 3]; let mut destinations: [Option<Destination>; 3] = [None; 3];
let address = ZcashAddress::try_from_encoded(address)?; if let Ok(data) = decode_payment_address(network.hrp_sapling_payment_address(), address) {
match address.kind { let destination = Destination::Sapling(data.to_bytes());
AddressKind::Sprout(_) => {} destinations[Pool::Sapling as usize] = Some(destination);
AddressKind::Sapling(data) => { }
let destination = Destination::Sapling(data); else if let Ok(Some(TransparentAddress::PublicKey(data))) = decode_transparent_address(&network.b58_pubkey_address_prefix(), &network.b58_script_address_prefix(), address) {
destinations[Pool::Sapling as usize] = Some(destination); let destination = Destination::Transparent(data);
} destinations[Pool::Transparent as usize] = Some(destination);
AddressKind::Unified(unified_address) => { }
for address in unified_address.items() { else if let Ok(address) = ZcashAddress::try_from_encoded(address) { // ZcashAddress only supports Zcash
match address { match address.kind {
Receiver::Orchard(data) => { AddressKind::Sprout(_) => {}
let destination = Destination::Orchard(data); AddressKind::Sapling(data) => {
destinations[Pool::Orchard as usize] = Some(destination); let destination = Destination::Sapling(data);
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);
destinations[Pool::Orchard as usize] = Some(destination);
}
Receiver::Sapling(data) => {
let destination = Destination::Sapling(data);
destinations[Pool::Sapling as usize] = Some(destination);
}
Receiver::P2pkh(data) => {
let destination = Destination::Transparent(data);
destinations[Pool::Transparent as usize] = Some(destination);
}
Receiver::P2sh(_) => {}
Receiver::Unknown { .. } => {}
} }
Receiver::Sapling(data) => {
let destination = Destination::Sapling(data);
destinations[Pool::Sapling as usize] = Some(destination);
}
Receiver::P2pkh(data) => {
let destination = Destination::Transparent(data);
destinations[Pool::Transparent as usize] = Some(destination);
}
Receiver::P2sh(_) => {}
Receiver::Unknown { .. } => {}
} }
} }
AddressKind::P2pkh(data) => {
let destination = Destination::Transparent(data);
destinations[Pool::Transparent as usize] = Some(destination);
}
AddressKind::P2sh(_) => {}
} }
AddressKind::P2pkh(data) => {
let destination = Destination::Transparent(data);
destinations[Pool::Transparent as usize] = Some(destination);
}
AddressKind::P2sh(_) => {}
} }
Ok(destinations) Ok(destinations)

View File

@ -1,3 +1,4 @@
use anyhow::anyhow;
use crate::DbAdapter; use crate::DbAdapter;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
use zcash_params::coin::get_coin_chain; use zcash_params::coin::get_coin_chain;
@ -52,7 +53,10 @@ pub async fn fetch_historical_prices(
let ts = p[0].as_i64().ok_or_else(json_error)? / 1000; let ts = p[0].as_i64().ok_or_else(json_error)? / 1000;
let price = p[1].as_f64().ok_or_else(json_error)?; let price = p[1].as_f64().ok_or_else(json_error)?;
// rounded to daily // rounded to daily
let date = NaiveDateTime::from_timestamp(ts, 0).date().and_hms(0, 0, 0); let date = NaiveDateTime::from_timestamp_opt(ts, 0)
.ok_or(anyhow!("Invalid Date"))?
.date().and_hms_opt(0, 0, 0)
.ok_or(anyhow!("Invalid Date"))?;
let timestamp = date.timestamp(); let timestamp = date.timestamp();
if timestamp != prev_timestamp { if timestamp != prev_timestamp {
let quote = Quote { timestamp, price }; let quote = Quote { timestamp, price };