This commit is contained in:
Hanh 2022-11-16 11:51:52 +08:00
parent 5ddacf524c
commit 4e553b42a6
14 changed files with 104 additions and 48 deletions

View File

@ -15,6 +15,8 @@ typedef void *DartPostCObjectFnType;
#define QR_DATA_SIZE 256
#define MAX_ATTEMPTS 10
#define N 200000
typedef struct CResult_u32 {
@ -102,7 +104,7 @@ int64_t get_mempool_balance(void);
struct CResult_u64 get_taddr_balance(uint8_t coin, uint32_t id_account);
struct CResult_____c_char shield_taddr(uint8_t coin, uint32_t account);
struct CResult_____c_char shield_taddr(uint8_t coin, uint32_t account, uint32_t confirmations);
void scan_transparent_accounts(uint32_t gap_limit);

View File

@ -55,6 +55,7 @@ async fn save_contacts_tx(memos: &[Memo], anchor_offset: u32) -> anyhow::Result<
c.id_account,
last_height,
&recipients,
1,
anchor_offset,
)
.await?;

View File

@ -390,8 +390,12 @@ pub async unsafe extern "C" fn get_taddr_balance(coin: u8, id_account: u32) -> C
#[tokio::main]
#[no_mangle]
pub async unsafe extern "C" fn shield_taddr(coin: u8, account: u32) -> CResult<*mut c_char> {
let res = crate::api::payment_v2::shield_taddr(coin, account).await;
pub async unsafe extern "C" fn shield_taddr(
coin: u8,
account: u32,
confirmations: u32,
) -> CResult<*mut c_char> {
let res = crate::api::payment_v2::shield_taddr(coin, account, confirmations).await;
to_cresult_str(res)
}
@ -419,6 +423,7 @@ pub async unsafe extern "C" fn prepare_multi_payment(
account,
last_height,
&recipients,
0,
anchor_offset,
)
.await?;

View File

@ -4,12 +4,15 @@ use crate::api::sync::get_latest_height;
pub use crate::broadcast_tx;
use crate::note_selection::{FeeFlat, FeeZIP327, Order, TransactionReport};
use crate::{
build_tx, fetch_utxos, get_secret_keys, AccountData, CoinConfig, TransactionBuilderConfig,
TransactionPlan, TxBuilderContext,
build_tx, fetch_utxos, get_secret_keys, note_selection, AccountData, CoinConfig, DbAdapter,
TransactionBuilderConfig, TransactionBuilderError, TransactionPlan, TxBuilderContext,
MAX_ATTEMPTS,
};
use rand::rngs::OsRng;
use std::cmp::min;
use zcash_primitives::memo::MemoBytes;
use std::slice;
use std::str::FromStr;
use zcash_primitives::memo::{Memo, MemoBytes, TextMemo};
use zcash_primitives::transaction::builder::Progress;
type PaymentProgressCallback = Box<dyn Fn(Progress) + Send + Sync>;
@ -19,16 +22,15 @@ pub async fn build_tx_plan(
account: u32,
last_height: u32,
recipients: &[RecipientMemo],
excluded_flags: u8,
confirmations: u32,
) -> anyhow::Result<TransactionPlan> {
) -> note_selection::Result<TransactionPlan> {
let c = CoinConfig::get(coin);
let (fvk, checkpoint_height) = {
let db = c.db()?;
let AccountData { fvk, .. } = db.get_account_info(account)?;
let anchor_height = last_height.saturating_sub(confirmations);
let checkpoint_height = db
.get_checkpoint_height(anchor_height)?
.unwrap_or_else(|| db.sapling_activation_height()); // get the latest checkpoint before the requested anchor height
let checkpoint_height = get_checkpoint_height(&db, last_height, confirmations)?;
(fvk, checkpoint_height)
};
let change_address = get_unified_address(coin, account, true, true, true)?;
@ -52,9 +54,7 @@ pub async fn build_tx_plan(
id_order += 1;
}
}
let utxos = fetch_utxos(coin, account, checkpoint_height, true).await?;
log::info!("UTXO: {:?}", utxos);
let utxos = fetch_utxos(coin, account, checkpoint_height, excluded_flags).await?;
let config = TransactionBuilderConfig::new(&change_address);
let tx_plan = crate::note_selection::build_tx_plan::<FeeFlat>(
@ -101,12 +101,58 @@ pub async fn sign_and_broadcast(
Ok(txid)
}
pub async fn build_max_tx(
coin: u8,
account: u32,
last_height: u32,
recipient: &RecipientMemo, // amount & max_amount per note are ignored
excluded_flags: u8,
confirmations: u32,
) -> note_selection::Result<TransactionPlan> {
let mut recipient = recipient.clone();
let checkpoint_height = {
let c = CoinConfig::get(coin);
let db = c.db()?;
get_checkpoint_height(&db, last_height, confirmations)?
};
let utxos = fetch_utxos(coin, account, checkpoint_height, excluded_flags).await?;
let available_funds: u64 = utxos.iter().map(|n| n.amount).sum();
recipient.amount = available_funds;
for _ in 0..MAX_ATTEMPTS {
// this will fail at least once because of the fees
let result = build_tx_plan(
coin,
account,
last_height,
slice::from_ref(&recipient),
excluded_flags,
confirmations,
)
.await;
match result {
Err(TransactionBuilderError::NotEnoughFunds(missing)) => {
recipient.amount -= missing; // reduce the amount and retry
}
_ => return result,
}
}
Err(TransactionBuilderError::TxTooComplex)
}
/// Make a transaction that shields the transparent balance
pub async fn shield_taddr(coin: u8, account: u32) -> anyhow::Result<String> {
// let last_height = get_latest_height().await?;
// let tx_id = build_sign_send_multi_payment(coin, account, last_height, &[], 0, Box::new(|_| {})).await?;
// Ok(tx_id)
todo!()
pub async fn shield_taddr(coin: u8, account: u32, confirmations: u32) -> anyhow::Result<String> {
let address = get_unified_address(coin, account, false, true, true)?; // get our own unified address
let recipient = RecipientMemo {
address,
amount: 0,
memo: Memo::from_str("Shield Transparent Balance")?,
max_amount_per_note: 0,
};
let last_height = get_latest_height().await?;
let tx_plan = build_max_tx(coin, account, last_height, &recipient, 2, confirmations).await?;
let tx_id = sign_and_broadcast(coin, account, &tx_plan).await?;
log::info!("TXID: {}", tx_id);
Ok(tx_id)
}
fn mark_spent(coin: u8, ids: &[u32]) -> anyhow::Result<()> {
@ -115,3 +161,15 @@ fn mark_spent(coin: u8, ids: &[u32]) -> anyhow::Result<()> {
db.tx_mark_spend(ids)?;
Ok(())
}
fn get_checkpoint_height(
db: &DbAdapter,
last_height: u32,
confirmations: u32,
) -> anyhow::Result<u32> {
let anchor_height = last_height.saturating_sub(confirmations);
let checkpoint_height = db
.get_checkpoint_height(anchor_height)?
.unwrap_or_else(|| db.sapling_activation_height()); // get the latest checkpoint before the requested anchor height
Ok(checkpoint_height)
}

View File

@ -20,6 +20,7 @@ pub struct RecipientShort {
pub amount: u64,
}
#[derive(Clone)]
pub struct RecipientMemo {
pub address: String,
pub amount: u64,

View File

@ -130,7 +130,7 @@ pub use zip32::KeyPack;
pub use note_selection::{
build_tx, build_tx_plan, fetch_utxos, get_secret_keys, TransactionBuilderConfig,
TransactionPlan, TxBuilderContext,
TransactionBuilderError, TransactionPlan, TxBuilderContext, MAX_ATTEMPTS,
};
pub use unified::{decode_unified_address, get_unified_address};

View File

@ -32,6 +32,8 @@ pub enum Error {
#[error(transparent)]
Reqwest(#[from] reqwest::Error),
#[error(transparent)]
TxBuilder(#[from] warp_api_ffi::TransactionBuilderError),
#[error(transparent)]
Other(#[from] anyhow::Error),
}
@ -252,6 +254,7 @@ pub async fn create_offline_tx(payment: Json<Payment>) -> Result<Json<Transactio
c.id_account,
latest,
&recipients,
0,
payment.confirmations,
)
.await?;
@ -294,6 +297,7 @@ pub async fn pay(payment: Json<Payment>, config: &State<Config>) -> Result<Strin
c.id_account,
latest,
&recipients,
0,
payment.confirmations,
)
.await?;
@ -330,6 +334,7 @@ pub async fn get_tx_plan(
account,
last_height,
&recipients,
0,
confirmations,
)
.await?;

View File

@ -17,8 +17,8 @@ use zcash_primitives::memo::Memo;
#[derive(Error, Debug)]
pub enum TransactionBuilderError {
#[error("Not enough funds")]
NotEnoughFunds,
#[error("Not enough funds: Missing {0} zats")]
NotEnoughFunds(u64),
#[error("Tx too complex")]
TxTooComplex,
#[error(transparent)]
@ -35,7 +35,7 @@ mod types;
mod ua;
mod utxo;
const MAX_ATTEMPTS: usize = 10;
pub const MAX_ATTEMPTS: usize = 10;
pub fn recipients_to_orders(recipients: &[Recipient]) -> Result<Vec<Order>> {
let orders: Result<Vec<_>> = recipients

View File

@ -100,7 +100,6 @@ pub fn build_tx(
let mut has_orchard = false;
let mut builder = Builder::new(*network, BlockHeight::from_u32(plan.height));
log::info!("ANCHOR {}", hex::encode(&plan.orchard_anchor));
let anchor: Anchor = orchard::tree::MerkleHashOrchard::from_bytes(&plan.orchard_anchor)
.unwrap()
.into();
@ -328,7 +327,7 @@ async fn dummy_test() {
let context = TxBuilderContext::from_height(0, height).unwrap();
log::info!("Getting available notes");
let utxos = fetch_utxos(0, 1, height, true, 0).await.unwrap();
let utxos = fetch_utxos(0, 1, height).await.unwrap();
// let notes: Vec<_> = utxos.into_iter().filter(|utxo| utxo.source.pool() == Pool::Orchard).collect();
log::info!("Preparing outputs");
@ -348,12 +347,11 @@ async fn dummy_test() {
log::info!("Building tx plan");
let tx_plan =
build_tx_plan::<FeeFlat>("", 0, &[Hash::default(); 2], &utxos, &mut orders, &config)
.unwrap();
build_tx_plan::<FeeFlat>("", 0, &Hash::default(), &utxos, &mut orders, &config).unwrap();
log::info!("Plan: {}", serde_json::to_string(&tx_plan).unwrap());
log::info!("Building tx");
let tx = build_tx(c.chain.network(), &keys, &tx_plan, context, OsRng).unwrap();
let tx = build_tx(c.chain.network(), &keys, &tx_plan, OsRng).unwrap();
println!("{}", hex::encode(&tx));
}

View File

@ -101,7 +101,7 @@ pub fn allocate_funds(
let mut t2 = sum - smax - omax;
if t2 > 0 {
if t2 > tmax {
return Err(TransactionBuilderError::NotEnoughFunds);
return Err(TransactionBuilderError::NotEnoughFunds((t2 - tmax) as u64));
}
// Not enough shielded notes. Use them all before using transparent notes
s2 = smax;

View File

@ -596,7 +596,7 @@ fn test_neg_ua2ua() {
},
&PoolAllocation([10, 10, 10]),
);
assert_matches!(r, Err(NotEnoughFunds))
assert_matches!(r, Err(NotEnoughFunds(_)))
}
#[test]

View File

@ -6,16 +6,18 @@ pub async fn fetch_utxos(
coin: u8,
account: u32,
checkpoint_height: u32,
use_transparent_inputs: bool,
excluded_flags: u8,
) -> anyhow::Result<Vec<UTXO>> {
let mut utxos = vec![];
if use_transparent_inputs {
if excluded_flags & 1 == 0 {
utxos.extend(get_transparent_utxos(coin, account).await?);
}
let coin = CoinConfig::get(coin);
let db = coin.db.as_ref().unwrap();
let db = db.lock().unwrap();
utxos.extend(db.get_unspent_received_notes(account, checkpoint_height)?);
if excluded_flags & 2 == 0 {
utxos.extend(db.get_unspent_received_notes(account, checkpoint_height)?);
}
Ok(utxos)
}

View File

@ -16,7 +16,6 @@ pub const MERKLE_CRH_PERSONALIZATION: &str = "z.cash:Orchard-MerkleCRH";
lazy_static! {
pub static ref ORCHARD_ROOTS: Vec<Hash> = {
log::info!("Initialize Orchard Hasher");
let h = OrchardHasher::new();
h.empty_roots(32)
};

View File

@ -85,32 +85,24 @@ impl<
}
pub fn process(&mut self, blocks: &[CompactBlock]) -> Result<usize> {
log::info!("=");
if blocks.is_empty() {
return Ok(0);
}
log::info!("+1");
let decrypter = self.decrypter.clone();
log::info!("+2");
let decrypted_blocks: Vec<_> = blocks
.par_iter()
.map(|b| decrypter.decrypt_notes(b, &self.vks))
.collect();
log::info!("+");
let count_outputs: usize = decrypted_blocks
.iter()
.map(|b| b.count_outputs)
.sum::<u32>() as usize;
log::info!("+");
let mut db = self.db.build()?;
log::info!("+");
self.warper.initialize(&self.tree, &self.witnesses);
log::info!("+");
let db_tx = db.begin_transaction()?;
// Detect new received notes
log::info!("+");
let mut new_witnesses = vec![];
for decb in decrypted_blocks.iter() {
for dectx in decb.txs.iter() {
@ -151,7 +143,6 @@ impl<
}
self.note_position += decb.count_outputs as usize;
}
log::info!("+");
// Detect spends and collect note commitments
let mut new_cmx = vec![];
@ -181,24 +172,18 @@ impl<
}
// Run blocks through warp sync
log::info!("+");
self.warper.add_nodes(&mut new_cmx, &new_witnesses);
log::info!("+");
let (updated_tree, updated_witnesses) = self.warper.finalize();
// Store witnesses
log::info!("+");
for w in updated_witnesses.iter() {
DbAdapter::store_witness(w, height, w.id_note, &db_tx, &self.shielded_pool)?;
}
log::info!("+");
DbAdapter::store_tree(height, &updated_tree, &db_tx, &self.shielded_pool)?;
log::info!("+");
self.tree = updated_tree;
self.witnesses = updated_witnesses;
db_tx.commit()?;
log::info!("+");
Ok(count_outputs * self.vks.len())
}
}