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(
coin: u8,
account: u32,
tx: *mut c_char,
tx_plan: *mut c_char,
_port: i64,
) -> CResult<*mut c_char> {
from_c_str!(tx);
from_c_str!(tx_plan);
let res = async {
let tx: TransactionPlan = serde_json::from_str(&tx)?;
let raw_tx = crate::api::payment_v2::sign_plan(coin, account, &tx)?;
let tx_plan: TransactionPlan = serde_json::from_str(&tx_plan)?;
let raw_tx = crate::api::payment_v2::sign_plan(coin, account, &tx_plan)?;
let tx_str = base64::encode(&raw_tx);
Ok::<_, anyhow::Error>(tx_str)
};

View File

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

View File

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

View File

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

View File

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

View File

@ -3,12 +3,12 @@ use super::types::*;
use super::TransactionBuilderError::NotEnoughFunds;
use crate::note_selection::build_tx_plan;
use crate::note_selection::fee::{FeeCalculator, FeeZIP327};
use crate::note_selection::optimize::{outputs_for_change, select_inputs};
use crate::note_selection::ua::decode;
use crate::note_selection::optimize::select_inputs;
use crate::Hash;
use assert_matches::assert_matches;
use serde::Serialize;
use serde_json::Value;
use zcash_primitives::consensus::Network;
use zcash_primitives::memo::MemoBytes;
macro_rules! utxo {
@ -689,14 +689,6 @@ fn test_select_utxo() {
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]
fn test_fees() {
let _ = env_logger::try_init();
@ -733,6 +725,7 @@ fn test_tx_plan() {
tso!(7, 70),
];
let tx_plan = build_tx_plan::<FeeZIP327>(
&Network::MainNetwork,
"",
0,
&Hash::default(),

View File

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

View File

@ -1,41 +1,53 @@
use super::types::*;
use zcash_address::unified::{Container, Receiver};
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 address = ZcashAddress::try_from_encoded(address)?;
match address.kind {
AddressKind::Sprout(_) => {}
AddressKind::Sapling(data) => {
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);
if let Ok(data) = decode_payment_address(network.hrp_sapling_payment_address(), address) {
let destination = Destination::Sapling(data.to_bytes());
destinations[Pool::Sapling as usize] = Some(destination);
}
else if let Ok(Some(TransparentAddress::PublicKey(data))) = decode_transparent_address(&network.b58_pubkey_address_prefix(), &network.b58_script_address_prefix(), address) {
let destination = Destination::Transparent(data);
destinations[Pool::Transparent as usize] = Some(destination);
}
else if let Ok(address) = ZcashAddress::try_from_encoded(address) { // ZcashAddress only supports Zcash
match address.kind {
AddressKind::Sprout(_) => {}
AddressKind::Sapling(data) => {
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)

View File

@ -1,3 +1,4 @@
use anyhow::anyhow;
use crate::DbAdapter;
use chrono::NaiveDateTime;
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 price = p[1].as_f64().ok_or_else(json_error)?;
// 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();
if timestamp != prev_timestamp {
let quote = Quote { timestamp, price };