Mempool
This commit is contained in:
parent
74a6310784
commit
ac71c40218
25
binding.h
25
binding.h
|
@ -13,6 +13,11 @@ typedef char bool;
|
|||
#endif
|
||||
typedef void *DartPostCObjectFnType;
|
||||
|
||||
typedef struct CResult {
|
||||
char value;
|
||||
char *error;
|
||||
} CResult;
|
||||
|
||||
#define QR_DATA_SIZE 256
|
||||
|
||||
#define MAX_ATTEMPTS 10
|
||||
|
@ -34,21 +39,11 @@ typedef struct CResult_u8 {
|
|||
char *error;
|
||||
} CResult_u8;
|
||||
|
||||
typedef struct CResult_i64 {
|
||||
int64_t value;
|
||||
char *error;
|
||||
} CResult_i64;
|
||||
|
||||
typedef struct CResult_u64 {
|
||||
uint64_t value;
|
||||
char *error;
|
||||
} CResult_u64;
|
||||
|
||||
typedef struct CResult {
|
||||
char value; // dummy
|
||||
char *error;
|
||||
} CResult;
|
||||
|
||||
void dummy_export(void);
|
||||
|
||||
void dart_post_cobject(DartPostCObjectFnType ptr);
|
||||
|
@ -69,6 +64,10 @@ char *get_lwd_url(uint8_t coin);
|
|||
|
||||
void reset_app(void);
|
||||
|
||||
void mempool_run(int64_t port);
|
||||
|
||||
void mempool_set_active(uint8_t coin, uint32_t id_account);
|
||||
|
||||
struct CResult_u32 new_account(uint8_t coin, char *name, char *data, int32_t index);
|
||||
|
||||
void new_sub_account(char *name, int32_t index, uint32_t count);
|
||||
|
@ -105,12 +104,6 @@ struct CResult_u32 rewind_to(uint32_t height);
|
|||
|
||||
void rescan_from(uint32_t height);
|
||||
|
||||
struct CResult_i64 mempool_sync(void);
|
||||
|
||||
void mempool_reset(void);
|
||||
|
||||
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, uint32_t confirmations);
|
||||
|
|
|
@ -12,3 +12,8 @@ typedef char bool;
|
|||
#endif
|
||||
#endif
|
||||
typedef void *DartPostCObjectFnType;
|
||||
|
||||
typedef struct CResult {
|
||||
char value;
|
||||
char *error;
|
||||
} CResult;
|
||||
|
|
|
@ -2,7 +2,6 @@ pub mod account;
|
|||
pub mod contact;
|
||||
pub mod fullbackup;
|
||||
pub mod historical_prices;
|
||||
pub mod mempool;
|
||||
pub mod message;
|
||||
pub mod recipient;
|
||||
// pub mod payment;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::coinconfig::{init_coin, migrate_coin, CoinConfig};
|
||||
use crate::coinconfig::{init_coin, migrate_coin, CoinConfig, MEMPOOL, MEMPOOL_RUNNER};
|
||||
use crate::note_selection::TransactionReport;
|
||||
use crate::{ChainError, TransactionPlan, Tx};
|
||||
use allo_isolate::{ffi, IntoDart};
|
||||
|
@ -6,7 +6,6 @@ use android_logger::Config;
|
|||
use anyhow::anyhow;
|
||||
use lazy_static::lazy_static;
|
||||
use log::Level;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
use std::sync::Mutex;
|
||||
|
@ -17,10 +16,6 @@ static mut POST_COBJ: Option<ffi::DartPostCObjectFnType> = None;
|
|||
|
||||
const MAX_COINS: u8 = 3;
|
||||
|
||||
lazy_static! {
|
||||
static ref LAST_ERROR: Mutex<RefCell<String>> = Mutex::new(RefCell::new(String::new()));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dummy_export() {}
|
||||
|
||||
|
@ -121,8 +116,9 @@ pub unsafe extern "C" fn set_active(active: u8) {
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn set_active_account(coin: u8, id: u32) {
|
||||
crate::coinconfig::set_active_account(coin, id);
|
||||
#[tokio::main]
|
||||
pub async unsafe extern "C" fn set_active_account(coin: u8, id: u32) {
|
||||
crate::coinconfig::set_active_account(coin, id).await;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -148,6 +144,31 @@ pub unsafe extern "C" fn reset_app() {
|
|||
log_error(res())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[tokio::main]
|
||||
pub async unsafe extern "C" fn mempool_run(port: i64) {
|
||||
let mut mempool_runner = MEMPOOL_RUNNER.lock().unwrap();
|
||||
let mempool = mempool_runner
|
||||
.run(move |balance: i64| {
|
||||
let mut balance = balance.into_dart();
|
||||
if port != 0 {
|
||||
if let Some(p) = POST_COBJ {
|
||||
p(port, &mut balance);
|
||||
}
|
||||
}
|
||||
})
|
||||
.await;
|
||||
let _ = MEMPOOL.fill(mempool);
|
||||
log::info!("end mempool_start");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[tokio::main]
|
||||
pub async unsafe extern "C" fn mempool_set_active(coin: u8, id_account: u32) {
|
||||
let mempool = MEMPOOL.borrow().unwrap();
|
||||
mempool.set_active(coin, id_account).await;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn new_account(
|
||||
coin: u8,
|
||||
|
@ -261,8 +282,6 @@ pub async unsafe extern "C" fn warp(
|
|||
.await;
|
||||
log::info!("Sync finished");
|
||||
|
||||
crate::api::mempool::scan().await?;
|
||||
|
||||
match result {
|
||||
Ok(_) => Ok(0),
|
||||
Err(err) => {
|
||||
|
@ -374,27 +393,6 @@ pub async unsafe extern "C" fn rescan_from(height: u32) {
|
|||
log_error(res)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[no_mangle]
|
||||
pub async unsafe extern "C" fn mempool_sync() -> CResult<i64> {
|
||||
let res = crate::api::mempool::scan().await;
|
||||
to_cresult(res)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn mempool_reset() {
|
||||
let c = CoinConfig::get_active();
|
||||
let mut mempool = c.mempool.lock().unwrap();
|
||||
log_error(mempool.clear());
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_mempool_balance() -> i64 {
|
||||
let c = CoinConfig::get_active();
|
||||
let mempool = c.mempool.lock().unwrap();
|
||||
mempool.get_unconfirmed_balance()
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[no_mangle]
|
||||
pub async unsafe extern "C" fn get_taddr_balance(coin: u8, id_account: u32) -> CResult<u64> {
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
//! Access to server mempool
|
||||
|
||||
use crate::api::sync::get_latest_height;
|
||||
use anyhow::anyhow;
|
||||
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
||||
use zcash_primitives::consensus::Parameters;
|
||||
|
||||
use crate::coinconfig::CoinConfig;
|
||||
use crate::db::AccountData;
|
||||
|
||||
/// Scan the mempool and return the unconfirmed balance
|
||||
pub async fn scan() -> anyhow::Result<i64> {
|
||||
let c = CoinConfig::get_active();
|
||||
let AccountData { fvk, .. } = c.db()?.get_account_info(c.id_account)?;
|
||||
let height = get_latest_height().await?;
|
||||
let mut mempool = c.mempool.lock().unwrap();
|
||||
let current_height = c.height;
|
||||
if height != current_height {
|
||||
CoinConfig::set_height(height);
|
||||
mempool.clear()?;
|
||||
}
|
||||
let fvk = decode_extended_full_viewing_key(
|
||||
c.chain.network().hrp_sapling_extended_full_viewing_key(),
|
||||
&fvk,
|
||||
)
|
||||
.map_err(|_| anyhow!("Decode error"))?;
|
||||
let mut client = c.connect_lwd().await?;
|
||||
mempool
|
||||
.update(&mut client, height, &fvk.fvk.vk.ivk())
|
||||
.await?;
|
||||
|
||||
Ok(mempool.get_unconfirmed_balance())
|
||||
}
|
|
@ -286,6 +286,7 @@ pub struct DecryptedNote {
|
|||
pub output_index: usize,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn to_output_description(co: &CompactSaplingOutput) -> CompactOutputDescription {
|
||||
let cmu: [u8; 32] = co.cmu.clone().try_into().unwrap();
|
||||
let cmu = bls12_381::Scalar::from_repr(cmu).unwrap();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::fountain::FountainCodes;
|
||||
use crate::mempool::MemPool;
|
||||
use crate::mempool::{MemPool, MemPoolRunner};
|
||||
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter};
|
||||
use anyhow::anyhow;
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -19,6 +19,8 @@ lazy_static! {
|
|||
];
|
||||
pub static ref PROVER: AtomicLazyCell<LocalTxProver> = AtomicLazyCell::new();
|
||||
pub static ref RAPTORQ: Mutex<FountainCodes> = Mutex::new(FountainCodes::new());
|
||||
pub static ref MEMPOOL: AtomicLazyCell<MemPool> = AtomicLazyCell::new();
|
||||
pub static ref MEMPOOL_RUNNER: Mutex<MemPoolRunner> = Mutex::new(MemPoolRunner::new());
|
||||
}
|
||||
|
||||
pub static ACTIVE_COIN: AtomicU8 = AtomicU8::new(0);
|
||||
|
@ -29,14 +31,11 @@ pub fn set_active(active: u8) {
|
|||
}
|
||||
|
||||
/// Set the active account for a given coin
|
||||
pub fn set_active_account(coin: u8, id: u32) {
|
||||
let mempool = {
|
||||
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||
c.id_account = id;
|
||||
c.mempool.clone()
|
||||
};
|
||||
let mut mempool = mempool.lock().unwrap();
|
||||
let _ = mempool.clear();
|
||||
pub async fn set_active_account(coin: u8, id: u32) {
|
||||
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||
c.id_account = id;
|
||||
let mempool = MEMPOOL.borrow().unwrap();
|
||||
mempool.set_active(coin, id).await;
|
||||
}
|
||||
|
||||
/// Set the lightwalletd url for a given coin
|
||||
|
@ -77,7 +76,6 @@ pub struct CoinConfig {
|
|||
pub height: u32,
|
||||
pub lwd_url: Option<String>,
|
||||
pub db_path: Option<String>,
|
||||
pub mempool: Arc<Mutex<MemPool>>,
|
||||
pub db: Option<Arc<Mutex<DbAdapter>>>,
|
||||
pub chain: &'static (dyn CoinChain + Send),
|
||||
}
|
||||
|
@ -93,7 +91,6 @@ impl CoinConfig {
|
|||
lwd_url: None,
|
||||
db_path: None,
|
||||
db: None,
|
||||
mempool: Arc::new(Mutex::new(MemPool::new(coin))),
|
||||
chain,
|
||||
}
|
||||
}
|
||||
|
@ -137,10 +134,6 @@ impl CoinConfig {
|
|||
c.height = height;
|
||||
}
|
||||
|
||||
pub fn mempool(&self) -> MutexGuard<MemPool> {
|
||||
self.mempool.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn db(&self) -> anyhow::Result<MutexGuard<DbAdapter>> {
|
||||
let db = self.db.as_ref().unwrap();
|
||||
let db = db.lock().unwrap();
|
||||
|
|
11
src/db.rs
11
src/db.rs
|
@ -4,11 +4,11 @@ use crate::note_selection::{Source, UTXO};
|
|||
use crate::orchard::{derive_orchard_keys, OrchardKeyBytes, OrchardViewKey};
|
||||
use crate::prices::Quote;
|
||||
use crate::sapling::SaplingViewKey;
|
||||
use crate::sync;
|
||||
use crate::sync::tree::{CTree, TreeCheckpoint};
|
||||
use crate::taddr::{derive_tkeys, TBalance};
|
||||
use crate::transaction::{GetTransactionDetailRequest, TransactionDetails};
|
||||
use crate::unified::UnifiedAddressType;
|
||||
use crate::{sync, Hash};
|
||||
use orchard::keys::FullViewingKey;
|
||||
use rusqlite::Error::QueryReturnedNoRows;
|
||||
use rusqlite::{params, Connection, OptionalExtension, Transaction};
|
||||
|
@ -555,7 +555,7 @@ impl DbAdapter {
|
|||
&self,
|
||||
account: u32,
|
||||
unspent_only: bool,
|
||||
) -> anyhow::Result<HashMap<Vec<u8>, u64>> {
|
||||
) -> anyhow::Result<HashMap<Hash, u64>> {
|
||||
let mut sql = "SELECT value, nf FROM received_notes WHERE account = ?1".to_string();
|
||||
if unspent_only {
|
||||
sql += " AND (spent IS NULL OR spent = 0)";
|
||||
|
@ -564,9 +564,9 @@ impl DbAdapter {
|
|||
let nfs_res = statement.query_map(params![account], |row| {
|
||||
let amount: i64 = row.get(0)?;
|
||||
let nf: Vec<u8> = row.get(1)?;
|
||||
Ok((amount, nf))
|
||||
Ok((amount, nf.try_into().unwrap()))
|
||||
})?;
|
||||
let mut nfs: HashMap<Vec<u8>, u64> = HashMap::new();
|
||||
let mut nfs: HashMap<Hash, u64> = HashMap::new();
|
||||
for n in nfs_res {
|
||||
let n = n?;
|
||||
nfs.insert(n.1, n.0 as u64);
|
||||
|
@ -1369,8 +1369,7 @@ pub struct AccountData {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH};
|
||||
use crate::sync::{CTree, Witness};
|
||||
use crate::db::{DbAdapter, DEFAULT_DB_PATH};
|
||||
use zcash_params::coin::CoinType;
|
||||
|
||||
#[test]
|
||||
|
|
306
src/mempool.rs
306
src/mempool.rs
|
@ -1,116 +1,228 @@
|
|||
use crate::chain::to_output_description;
|
||||
use crate::{CompactTx, CompactTxStreamerClient, Exclude};
|
||||
use crate::{AccountData, Empty, Hash, RawTransaction};
|
||||
use orchard::keys::{FullViewingKey, IncomingViewingKey, Scope};
|
||||
use orchard::note_encryption::OrchardDomain;
|
||||
use std::collections::HashMap;
|
||||
use tonic::transport::Channel;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::sync::mpsc::{Receiver, Sender};
|
||||
use tonic::Request;
|
||||
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
||||
use zcash_note_encryption::try_note_decryption;
|
||||
|
||||
use crate::coinconfig::CoinConfig;
|
||||
use zcash_primitives::consensus::BlockHeight;
|
||||
use zcash_primitives::consensus::{BlockHeight, Network, NetworkUpgrade, Parameters};
|
||||
use zcash_primitives::sapling::note_encryption::{
|
||||
try_sapling_compact_note_decryption, PreparedIncomingViewingKey,
|
||||
try_sapling_note_decryption, PreparedIncomingViewingKey,
|
||||
};
|
||||
use zcash_primitives::sapling::SaplingIvk;
|
||||
use zcash_primitives::transaction::Transaction;
|
||||
|
||||
const DEFAULT_EXCLUDE_LEN: u8 = 1;
|
||||
struct MemPoolImpl {
|
||||
network: Network,
|
||||
nfs: HashMap<Hash, u64>,
|
||||
balance: i64,
|
||||
pivk: PreparedIncomingViewingKey,
|
||||
oivk: Option<IncomingViewingKey>,
|
||||
}
|
||||
|
||||
struct MemPoolTransacton {
|
||||
#[allow(dead_code)]
|
||||
balance: i64, // negative if spent
|
||||
exclude_len: u8,
|
||||
#[derive(Debug)]
|
||||
pub enum MemPoolMsg {
|
||||
Active(u8, u32),
|
||||
Subscribe(u8, u32),
|
||||
Balance(u8, u32, i64),
|
||||
Close(u8, u32),
|
||||
}
|
||||
|
||||
struct MemPoolHandler {
|
||||
coin: u8,
|
||||
id_account: u32,
|
||||
tx_mesg: Sender<MemPoolMsg>,
|
||||
}
|
||||
|
||||
impl MemPoolHandler {
|
||||
pub fn new(coin: u8, id_account: u32, tx_mesg: Sender<MemPoolMsg>) -> Self {
|
||||
MemPoolHandler {
|
||||
coin,
|
||||
id_account,
|
||||
tx_mesg,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn subscribe(&self) -> anyhow::Result<()> {
|
||||
let tx_mesg = self.tx_mesg.clone();
|
||||
let coin = self.coin;
|
||||
let id_account = self.id_account;
|
||||
let c = CoinConfig::get(self.coin);
|
||||
let mut client = c.connect_lwd().await?;
|
||||
let (nfs, sapling_ivk, orchard_ivk) = {
|
||||
let db = c.db()?;
|
||||
let nfs = db.get_nullifier_amounts(id_account, true)?;
|
||||
let network = c.chain.network();
|
||||
let AccountData { fvk, .. } = db.get_account_info(id_account)?;
|
||||
let fvk = decode_extended_full_viewing_key(
|
||||
network.hrp_sapling_extended_full_viewing_key(),
|
||||
&fvk,
|
||||
)
|
||||
.unwrap();
|
||||
let sapling_ivk = fvk.fvk.vk.ivk();
|
||||
let orchard_ivk = db.get_orchard(id_account)?.map(|k| {
|
||||
let fvk = FullViewingKey::from_bytes(&k.fvk).unwrap();
|
||||
fvk.to_ivk(Scope::External)
|
||||
});
|
||||
(nfs, sapling_ivk, orchard_ivk)
|
||||
};
|
||||
let mut mempool_impl = MemPoolImpl::new(c.chain.network(), nfs, sapling_ivk, orchard_ivk);
|
||||
let mut mempool_stream = client
|
||||
.get_mempool_stream(Request::new(Empty {}))
|
||||
.await?
|
||||
.into_inner();
|
||||
tokio::spawn(async move {
|
||||
while let Some(raw_tx) = mempool_stream.message().await? {
|
||||
let balance = mempool_impl.scan_transaction(&raw_tx)?;
|
||||
let _ = tx_mesg
|
||||
.send(MemPoolMsg::Balance(coin, id_account, balance))
|
||||
.await;
|
||||
}
|
||||
let _ = tx_mesg.send(MemPoolMsg::Close(coin, id_account)).await;
|
||||
Ok::<_, anyhow::Error>(())
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemPool {
|
||||
coin: u8,
|
||||
transactions: HashMap<Vec<u8>, MemPoolTransacton>,
|
||||
nfs: HashMap<Vec<u8>, u64>,
|
||||
balance: i64,
|
||||
tx_mesg: Sender<MemPoolMsg>,
|
||||
}
|
||||
|
||||
impl MemPool {
|
||||
pub fn new(coin: u8) -> MemPool {
|
||||
MemPool {
|
||||
coin,
|
||||
transactions: HashMap::new(),
|
||||
nfs: HashMap::new(),
|
||||
balance: 0,
|
||||
}
|
||||
pub fn new(tx_mesg: Sender<MemPoolMsg>) -> Self {
|
||||
MemPool { tx_mesg }
|
||||
}
|
||||
|
||||
pub fn get_unconfirmed_balance(&self) -> i64 {
|
||||
self.balance
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) -> anyhow::Result<()> {
|
||||
let c = CoinConfig::get(self.coin);
|
||||
self.nfs = c.db()?.get_nullifier_amounts(c.id_account, true)?;
|
||||
self.transactions.clear();
|
||||
self.balance = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update(
|
||||
&mut self,
|
||||
client: &mut CompactTxStreamerClient<Channel>,
|
||||
height: u32,
|
||||
ivk: &SaplingIvk,
|
||||
) -> anyhow::Result<()> {
|
||||
let filter: Vec<_> = self
|
||||
.transactions
|
||||
.iter()
|
||||
.map(|(hash, tx)| {
|
||||
let mut hash = hash.clone();
|
||||
hash.truncate(tx.exclude_len as usize);
|
||||
hash
|
||||
})
|
||||
.collect();
|
||||
|
||||
let exclude = Exclude { txid: filter };
|
||||
let mut txs = client
|
||||
.get_mempool_tx(Request::new(exclude))
|
||||
.await?
|
||||
.into_inner();
|
||||
while let Some(tx) = txs.message().await? {
|
||||
match self.transactions.get_mut(&*tx.hash) {
|
||||
Some(tx) => {
|
||||
tx.exclude_len += 1; // server sent us the same tx: make the filter more specific
|
||||
}
|
||||
None => {
|
||||
let balance = self.scan_transaction(height, &tx, ivk);
|
||||
let mempool_tx = MemPoolTransacton {
|
||||
balance,
|
||||
exclude_len: DEFAULT_EXCLUDE_LEN,
|
||||
};
|
||||
self.balance += balance;
|
||||
self.transactions.insert(tx.hash.clone(), mempool_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn scan_transaction(&self, height: u32, tx: &CompactTx, ivk: &SaplingIvk) -> i64 {
|
||||
let c = CoinConfig::get_active();
|
||||
let mut balance = 0i64;
|
||||
for cs in tx.spends.iter() {
|
||||
if let Some(&value) = self.nfs.get(&*cs.nf) {
|
||||
// nf recognized -> value is spent
|
||||
balance -= value as i64;
|
||||
}
|
||||
}
|
||||
let pivk = PreparedIncomingViewingKey::new(ivk);
|
||||
for co in tx.outputs.iter() {
|
||||
let od = to_output_description(co);
|
||||
if let Some((note, _)) = try_sapling_compact_note_decryption(
|
||||
c.chain.network(),
|
||||
BlockHeight::from_u32(height),
|
||||
&pivk,
|
||||
&od,
|
||||
) {
|
||||
balance += note.value as i64; // value is incoming
|
||||
}
|
||||
}
|
||||
|
||||
balance
|
||||
pub async fn set_active(&self, coin: u8, id_account: u32) {
|
||||
let _ = self
|
||||
.tx_mesg
|
||||
.send(MemPoolMsg::Active(coin, id_account))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MemPoolRunner {
|
||||
runtime: Runtime,
|
||||
}
|
||||
|
||||
impl MemPoolRunner {
|
||||
pub fn new() -> Self {
|
||||
MemPoolRunner {
|
||||
runtime: Runtime::new().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run<F: Fn(i64) + Send + Sync + 'static>(&mut self, f: F) -> MemPool {
|
||||
let (tx_mesg, rx_mesg) = tokio::sync::mpsc::channel::<MemPoolMsg>(8);
|
||||
let mempool = MemPool::new(tx_mesg.clone());
|
||||
self.runtime.spawn(async move {
|
||||
run_mempool_loop(tx_mesg, rx_mesg, f).await.unwrap();
|
||||
});
|
||||
mempool
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_mempool_loop<F: Fn(i64) + Send + Sync + 'static>(
|
||||
tx_mesg: Sender<MemPoolMsg>,
|
||||
mut rx_mesg: Receiver<MemPoolMsg>,
|
||||
f: F,
|
||||
) -> anyhow::Result<()> {
|
||||
log::info!("MEMPOOL run");
|
||||
let mut active_coin = 0;
|
||||
let mut active_account = 0;
|
||||
while let Some(message) = rx_mesg.recv().await {
|
||||
match message {
|
||||
MemPoolMsg::Active(coin, id_account) => {
|
||||
if coin != active_coin || id_account != active_account {
|
||||
active_coin = coin;
|
||||
active_account = id_account;
|
||||
let _ = tx_mesg.send(MemPoolMsg::Subscribe(coin, id_account)).await;
|
||||
}
|
||||
}
|
||||
MemPoolMsg::Subscribe(coin, id_account) => {
|
||||
let mempool_handler = MemPoolHandler::new(coin, id_account, tx_mesg.clone());
|
||||
mempool_handler.subscribe().await?;
|
||||
}
|
||||
MemPoolMsg::Balance(coin, id_account, balance) => {
|
||||
if coin == active_coin && id_account == active_account {
|
||||
f(balance);
|
||||
}
|
||||
}
|
||||
MemPoolMsg::Close(coin, id_account) => {
|
||||
if coin == active_coin && id_account == active_account {
|
||||
let _ = tx_mesg.send(MemPoolMsg::Subscribe(coin, id_account)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl MemPoolImpl {
|
||||
pub fn new(
|
||||
network: &Network,
|
||||
nfs: HashMap<Hash, u64>,
|
||||
sivk: SaplingIvk,
|
||||
oivk: Option<IncomingViewingKey>,
|
||||
) -> Self {
|
||||
let pivk = PreparedIncomingViewingKey::new(&sivk);
|
||||
MemPoolImpl {
|
||||
network: network.clone(),
|
||||
nfs,
|
||||
balance: 0,
|
||||
pivk,
|
||||
oivk,
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_transaction(&mut self, tx: &RawTransaction) -> anyhow::Result<i64> {
|
||||
let height = tx.height as u32;
|
||||
let mut balance = 0i64;
|
||||
let consensus_branch_id = self.network.branch_id(NetworkUpgrade::Nu5);
|
||||
let tx = Transaction::read(&tx.data[..], consensus_branch_id)?;
|
||||
log::info!("Mempool TXID {}", tx.txid());
|
||||
if let Some(sapling_bundle) = tx.sapling_bundle() {
|
||||
for cs in sapling_bundle.shielded_spends.iter() {
|
||||
let nf = cs.nullifier.0;
|
||||
if let Some(&value) = self.nfs.get(&nf) {
|
||||
// nf recognized -> value is spent
|
||||
balance -= value as i64;
|
||||
}
|
||||
}
|
||||
for co in sapling_bundle.shielded_outputs.iter() {
|
||||
// let od = to_output_description(co);
|
||||
if let Some((note, _, _)) = try_sapling_note_decryption(
|
||||
&self.network,
|
||||
BlockHeight::from_u32(height),
|
||||
&self.pivk,
|
||||
co,
|
||||
) {
|
||||
balance += note.value as i64; // value is incoming
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(orchard_bundle) = tx.orchard_bundle() {
|
||||
if let Some(ref oivk) = self.oivk {
|
||||
for a in orchard_bundle.actions().iter() {
|
||||
let nf = a.nullifier().to_bytes();
|
||||
if let Some(&value) = self.nfs.get(&nf) {
|
||||
// nf recognized -> value is spent
|
||||
balance -= value as i64;
|
||||
}
|
||||
let domain = OrchardDomain::for_action(a);
|
||||
if let Some((note, _, _)) = try_note_decryption(&domain, oivk, a) {
|
||||
balance += note.value().inner() as i64; // value is incoming
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.balance += balance;
|
||||
Ok(self.balance)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -735,7 +735,7 @@ fn test_tx_plan() {
|
|||
let tx_plan = build_tx_plan::<FeeZIP327>(
|
||||
"",
|
||||
0,
|
||||
&[Hash::default(); 2],
|
||||
&Hash::default(),
|
||||
&utxos,
|
||||
&orders,
|
||||
&TransactionBuilderConfig {
|
||||
|
|
|
@ -108,44 +108,3 @@ impl<N: Parameters> TrialDecrypter<N, OrchardDomain, OrchardViewKey, DecryptedOr
|
|||
vtx.actions.iter().map(|co| co.into()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_decrypt() -> anyhow::Result<()> {
|
||||
// let mut nullifier = hex::decode("951ab285b0f4df3ff24f24470dbb8bafa3b5caeeb204fc4465f7ea9c3d5a980a").unwrap();
|
||||
// let mut epk = hex::decode("182d698c3bb8b168d5f9420f1c2e32d94b4dbc0826181c1783ea47fedd31b710").unwrap();
|
||||
// let mut cmx = hex::decode("df45e00eb39e4c281e2804a366d3010b7f663724472d12637e0a749e6ce22719").unwrap();
|
||||
// let ciphertext = hex::decode("d9bc6ee09b0afde5dd69bfdf4b667a38da3e1084e84eb6752d54800b9f5110203b60496ab5313dba3f2acb9ef30bcaf68fbfcc59").unwrap();
|
||||
|
||||
let nullifier =
|
||||
hex::decode("ea1b97cc83d326db4130433022f68dd32a0bc707448b19b0980e4e6404412b29").unwrap();
|
||||
let epk =
|
||||
hex::decode("e2f666e905666f29bb678c694602b2768bea655c0f2b18f9c342ad8b64b18c0c").unwrap();
|
||||
let cmx =
|
||||
hex::decode("4a95dbf0d1d0cac1376a0b8fb0fc2ed2843d0e2670dd976a63386b293f30de25").unwrap();
|
||||
let ciphertext = hex::decode("73640095a90bb03d14f687d6acf4822618a3def1da3b71a588da1c68e25042f7c9aa759778e73aa2bb39d1061e51c1e8cf5e0bce").unwrap();
|
||||
|
||||
let db_builder = DbAdapterBuilder {
|
||||
coin_type: CoinType::Zcash,
|
||||
db_path: "./zec.db".to_string(),
|
||||
};
|
||||
let db = db_builder.build()?;
|
||||
let keys = db.get_orchard_fvks()?.first().unwrap().clone();
|
||||
let fvk = keys.fvk;
|
||||
|
||||
let output = CompactOutputBytes {
|
||||
nullifier: nullifier.clone().try_into().unwrap(),
|
||||
epk: epk.try_into().unwrap(),
|
||||
cmx: cmx.try_into().unwrap(),
|
||||
ciphertext: ciphertext.try_into().unwrap(),
|
||||
};
|
||||
let domain = OrchardDomain::for_nullifier(
|
||||
orchard::note::Nullifier::from_bytes(&nullifier.try_into().unwrap()).unwrap(),
|
||||
);
|
||||
let r = zcash_note_encryption::try_compact_note_decryption(
|
||||
&domain,
|
||||
&fvk.to_ivk(Scope::External),
|
||||
&output,
|
||||
);
|
||||
println!("{:?}", r);
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue