WIP
This commit is contained in:
parent
5ddacf524c
commit
4e553b42a6
|
@ -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);
|
||||
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ pub struct RecipientShort {
|
|||
pub amount: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RecipientMemo {
|
||||
pub address: String,
|
||||
pub amount: u64,
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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?;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -596,7 +596,7 @@ fn test_neg_ua2ua() {
|
|||
},
|
||||
&PoolAllocation([10, 10, 10]),
|
||||
);
|
||||
assert_matches!(r, Err(NotEnoughFunds))
|
||||
assert_matches!(r, Err(NotEnoughFunds(_)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
};
|
||||
|
|
15
src/sync.rs
15
src/sync.rs
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue