Mempool
This commit is contained in:
parent
74a6310784
commit
ac71c40218
25
binding.h
25
binding.h
|
@ -13,6 +13,11 @@ typedef char bool;
|
||||||
#endif
|
#endif
|
||||||
typedef void *DartPostCObjectFnType;
|
typedef void *DartPostCObjectFnType;
|
||||||
|
|
||||||
|
typedef struct CResult {
|
||||||
|
char value;
|
||||||
|
char *error;
|
||||||
|
} CResult;
|
||||||
|
|
||||||
#define QR_DATA_SIZE 256
|
#define QR_DATA_SIZE 256
|
||||||
|
|
||||||
#define MAX_ATTEMPTS 10
|
#define MAX_ATTEMPTS 10
|
||||||
|
@ -34,21 +39,11 @@ typedef struct CResult_u8 {
|
||||||
char *error;
|
char *error;
|
||||||
} CResult_u8;
|
} CResult_u8;
|
||||||
|
|
||||||
typedef struct CResult_i64 {
|
|
||||||
int64_t value;
|
|
||||||
char *error;
|
|
||||||
} CResult_i64;
|
|
||||||
|
|
||||||
typedef struct CResult_u64 {
|
typedef struct CResult_u64 {
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
char *error;
|
char *error;
|
||||||
} CResult_u64;
|
} CResult_u64;
|
||||||
|
|
||||||
typedef struct CResult {
|
|
||||||
char value; // dummy
|
|
||||||
char *error;
|
|
||||||
} CResult;
|
|
||||||
|
|
||||||
void dummy_export(void);
|
void dummy_export(void);
|
||||||
|
|
||||||
void dart_post_cobject(DartPostCObjectFnType ptr);
|
void dart_post_cobject(DartPostCObjectFnType ptr);
|
||||||
|
@ -69,6 +64,10 @@ char *get_lwd_url(uint8_t coin);
|
||||||
|
|
||||||
void reset_app(void);
|
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);
|
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);
|
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);
|
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_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);
|
struct CResult_____c_char shield_taddr(uint8_t coin, uint32_t account, uint32_t confirmations);
|
||||||
|
|
|
@ -12,3 +12,8 @@ typedef char bool;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
typedef void *DartPostCObjectFnType;
|
typedef void *DartPostCObjectFnType;
|
||||||
|
|
||||||
|
typedef struct CResult {
|
||||||
|
char value;
|
||||||
|
char *error;
|
||||||
|
} CResult;
|
||||||
|
|
|
@ -2,7 +2,6 @@ pub mod account;
|
||||||
pub mod contact;
|
pub mod contact;
|
||||||
pub mod fullbackup;
|
pub mod fullbackup;
|
||||||
pub mod historical_prices;
|
pub mod historical_prices;
|
||||||
pub mod mempool;
|
|
||||||
pub mod message;
|
pub mod message;
|
||||||
pub mod recipient;
|
pub mod recipient;
|
||||||
// pub mod payment;
|
// 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::note_selection::TransactionReport;
|
||||||
use crate::{ChainError, TransactionPlan, Tx};
|
use crate::{ChainError, TransactionPlan, Tx};
|
||||||
use allo_isolate::{ffi, IntoDart};
|
use allo_isolate::{ffi, IntoDart};
|
||||||
|
@ -6,7 +6,6 @@ use android_logger::Config;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::Level;
|
use log::Level;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
@ -17,10 +16,6 @@ static mut POST_COBJ: Option<ffi::DartPostCObjectFnType> = None;
|
||||||
|
|
||||||
const MAX_COINS: u8 = 3;
|
const MAX_COINS: u8 = 3;
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref LAST_ERROR: Mutex<RefCell<String>> = Mutex::new(RefCell::new(String::new()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn dummy_export() {}
|
pub unsafe extern "C" fn dummy_export() {}
|
||||||
|
|
||||||
|
@ -121,8 +116,9 @@ pub unsafe extern "C" fn set_active(active: u8) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn set_active_account(coin: u8, id: u32) {
|
#[tokio::main]
|
||||||
crate::coinconfig::set_active_account(coin, id);
|
pub async unsafe extern "C" fn set_active_account(coin: u8, id: u32) {
|
||||||
|
crate::coinconfig::set_active_account(coin, id).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
@ -148,6 +144,31 @@ pub unsafe extern "C" fn reset_app() {
|
||||||
log_error(res())
|
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]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn new_account(
|
pub unsafe extern "C" fn new_account(
|
||||||
coin: u8,
|
coin: u8,
|
||||||
|
@ -261,8 +282,6 @@ pub async unsafe extern "C" fn warp(
|
||||||
.await;
|
.await;
|
||||||
log::info!("Sync finished");
|
log::info!("Sync finished");
|
||||||
|
|
||||||
crate::api::mempool::scan().await?;
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Ok(0),
|
Ok(_) => Ok(0),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -374,27 +393,6 @@ pub async unsafe extern "C" fn rescan_from(height: u32) {
|
||||||
log_error(res)
|
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]
|
#[tokio::main]
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub async unsafe extern "C" fn get_taddr_balance(coin: u8, id_account: u32) -> CResult<u64> {
|
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,
|
pub output_index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn to_output_description(co: &CompactSaplingOutput) -> CompactOutputDescription {
|
pub fn to_output_description(co: &CompactSaplingOutput) -> CompactOutputDescription {
|
||||||
let cmu: [u8; 32] = co.cmu.clone().try_into().unwrap();
|
let cmu: [u8; 32] = co.cmu.clone().try_into().unwrap();
|
||||||
let cmu = bls12_381::Scalar::from_repr(cmu).unwrap();
|
let cmu = bls12_381::Scalar::from_repr(cmu).unwrap();
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::fountain::FountainCodes;
|
use crate::fountain::FountainCodes;
|
||||||
use crate::mempool::MemPool;
|
use crate::mempool::{MemPool, MemPoolRunner};
|
||||||
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter};
|
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter};
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -19,6 +19,8 @@ lazy_static! {
|
||||||
];
|
];
|
||||||
pub static ref PROVER: AtomicLazyCell<LocalTxProver> = AtomicLazyCell::new();
|
pub static ref PROVER: AtomicLazyCell<LocalTxProver> = AtomicLazyCell::new();
|
||||||
pub static ref RAPTORQ: Mutex<FountainCodes> = Mutex::new(FountainCodes::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);
|
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
|
/// Set the active account for a given coin
|
||||||
pub fn set_active_account(coin: u8, id: u32) {
|
pub async fn set_active_account(coin: u8, id: u32) {
|
||||||
let mempool = {
|
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
||||||
let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
|
c.id_account = id;
|
||||||
c.id_account = id;
|
let mempool = MEMPOOL.borrow().unwrap();
|
||||||
c.mempool.clone()
|
mempool.set_active(coin, id).await;
|
||||||
};
|
|
||||||
let mut mempool = mempool.lock().unwrap();
|
|
||||||
let _ = mempool.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the lightwalletd url for a given coin
|
/// Set the lightwalletd url for a given coin
|
||||||
|
@ -77,7 +76,6 @@ pub struct CoinConfig {
|
||||||
pub height: u32,
|
pub height: u32,
|
||||||
pub lwd_url: Option<String>,
|
pub lwd_url: Option<String>,
|
||||||
pub db_path: Option<String>,
|
pub db_path: Option<String>,
|
||||||
pub mempool: Arc<Mutex<MemPool>>,
|
|
||||||
pub db: Option<Arc<Mutex<DbAdapter>>>,
|
pub db: Option<Arc<Mutex<DbAdapter>>>,
|
||||||
pub chain: &'static (dyn CoinChain + Send),
|
pub chain: &'static (dyn CoinChain + Send),
|
||||||
}
|
}
|
||||||
|
@ -93,7 +91,6 @@ impl CoinConfig {
|
||||||
lwd_url: None,
|
lwd_url: None,
|
||||||
db_path: None,
|
db_path: None,
|
||||||
db: None,
|
db: None,
|
||||||
mempool: Arc::new(Mutex::new(MemPool::new(coin))),
|
|
||||||
chain,
|
chain,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,10 +134,6 @@ impl CoinConfig {
|
||||||
c.height = height;
|
c.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mempool(&self) -> MutexGuard<MemPool> {
|
|
||||||
self.mempool.lock().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn db(&self) -> anyhow::Result<MutexGuard<DbAdapter>> {
|
pub fn db(&self) -> anyhow::Result<MutexGuard<DbAdapter>> {
|
||||||
let db = self.db.as_ref().unwrap();
|
let db = self.db.as_ref().unwrap();
|
||||||
let db = db.lock().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::orchard::{derive_orchard_keys, OrchardKeyBytes, OrchardViewKey};
|
||||||
use crate::prices::Quote;
|
use crate::prices::Quote;
|
||||||
use crate::sapling::SaplingViewKey;
|
use crate::sapling::SaplingViewKey;
|
||||||
use crate::sync;
|
|
||||||
use crate::sync::tree::{CTree, TreeCheckpoint};
|
use crate::sync::tree::{CTree, TreeCheckpoint};
|
||||||
use crate::taddr::{derive_tkeys, TBalance};
|
use crate::taddr::{derive_tkeys, TBalance};
|
||||||
use crate::transaction::{GetTransactionDetailRequest, TransactionDetails};
|
use crate::transaction::{GetTransactionDetailRequest, TransactionDetails};
|
||||||
use crate::unified::UnifiedAddressType;
|
use crate::unified::UnifiedAddressType;
|
||||||
|
use crate::{sync, Hash};
|
||||||
use orchard::keys::FullViewingKey;
|
use orchard::keys::FullViewingKey;
|
||||||
use rusqlite::Error::QueryReturnedNoRows;
|
use rusqlite::Error::QueryReturnedNoRows;
|
||||||
use rusqlite::{params, Connection, OptionalExtension, Transaction};
|
use rusqlite::{params, Connection, OptionalExtension, Transaction};
|
||||||
|
@ -555,7 +555,7 @@ impl DbAdapter {
|
||||||
&self,
|
&self,
|
||||||
account: u32,
|
account: u32,
|
||||||
unspent_only: bool,
|
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();
|
let mut sql = "SELECT value, nf FROM received_notes WHERE account = ?1".to_string();
|
||||||
if unspent_only {
|
if unspent_only {
|
||||||
sql += " AND (spent IS NULL OR spent = 0)";
|
sql += " AND (spent IS NULL OR spent = 0)";
|
||||||
|
@ -564,9 +564,9 @@ impl DbAdapter {
|
||||||
let nfs_res = statement.query_map(params![account], |row| {
|
let nfs_res = statement.query_map(params![account], |row| {
|
||||||
let amount: i64 = row.get(0)?;
|
let amount: i64 = row.get(0)?;
|
||||||
let nf: Vec<u8> = row.get(1)?;
|
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 {
|
for n in nfs_res {
|
||||||
let n = n?;
|
let n = n?;
|
||||||
nfs.insert(n.1, n.0 as u64);
|
nfs.insert(n.1, n.0 as u64);
|
||||||
|
@ -1369,8 +1369,7 @@ pub struct AccountData {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::db::{DbAdapter, ReceivedNote, DEFAULT_DB_PATH};
|
use crate::db::{DbAdapter, DEFAULT_DB_PATH};
|
||||||
use crate::sync::{CTree, Witness};
|
|
||||||
use zcash_params::coin::CoinType;
|
use zcash_params::coin::CoinType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
306
src/mempool.rs
306
src/mempool.rs
|
@ -1,116 +1,228 @@
|
||||||
use crate::chain::to_output_description;
|
use crate::{AccountData, Empty, Hash, RawTransaction};
|
||||||
use crate::{CompactTx, CompactTxStreamerClient, Exclude};
|
use orchard::keys::{FullViewingKey, IncomingViewingKey, Scope};
|
||||||
|
use orchard::note_encryption::OrchardDomain;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tonic::transport::Channel;
|
use tokio::runtime::Runtime;
|
||||||
|
use tokio::sync::mpsc::{Receiver, Sender};
|
||||||
use tonic::Request;
|
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 crate::coinconfig::CoinConfig;
|
||||||
use zcash_primitives::consensus::BlockHeight;
|
use zcash_primitives::consensus::{BlockHeight, Network, NetworkUpgrade, Parameters};
|
||||||
use zcash_primitives::sapling::note_encryption::{
|
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::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 {
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
pub enum MemPoolMsg {
|
||||||
balance: i64, // negative if spent
|
Active(u8, u32),
|
||||||
exclude_len: u8,
|
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 {
|
pub struct MemPool {
|
||||||
coin: u8,
|
tx_mesg: Sender<MemPoolMsg>,
|
||||||
transactions: HashMap<Vec<u8>, MemPoolTransacton>,
|
|
||||||
nfs: HashMap<Vec<u8>, u64>,
|
|
||||||
balance: i64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemPool {
|
impl MemPool {
|
||||||
pub fn new(coin: u8) -> MemPool {
|
pub fn new(tx_mesg: Sender<MemPoolMsg>) -> Self {
|
||||||
MemPool {
|
MemPool { tx_mesg }
|
||||||
coin,
|
|
||||||
transactions: HashMap::new(),
|
|
||||||
nfs: HashMap::new(),
|
|
||||||
balance: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_unconfirmed_balance(&self) -> i64 {
|
pub async fn set_active(&self, coin: u8, id_account: u32) {
|
||||||
self.balance
|
let _ = self
|
||||||
}
|
.tx_mesg
|
||||||
|
.send(MemPoolMsg::Active(coin, id_account))
|
||||||
pub fn clear(&mut self) -> anyhow::Result<()> {
|
.await;
|
||||||
let c = CoinConfig::get(self.coin);
|
}
|
||||||
self.nfs = c.db()?.get_nullifier_amounts(c.id_account, true)?;
|
}
|
||||||
self.transactions.clear();
|
|
||||||
self.balance = 0;
|
pub struct MemPoolRunner {
|
||||||
Ok(())
|
runtime: Runtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update(
|
impl MemPoolRunner {
|
||||||
&mut self,
|
pub fn new() -> Self {
|
||||||
client: &mut CompactTxStreamerClient<Channel>,
|
MemPoolRunner {
|
||||||
height: u32,
|
runtime: Runtime::new().unwrap(),
|
||||||
ivk: &SaplingIvk,
|
}
|
||||||
) -> anyhow::Result<()> {
|
}
|
||||||
let filter: Vec<_> = self
|
|
||||||
.transactions
|
pub async fn run<F: Fn(i64) + Send + Sync + 'static>(&mut self, f: F) -> MemPool {
|
||||||
.iter()
|
let (tx_mesg, rx_mesg) = tokio::sync::mpsc::channel::<MemPoolMsg>(8);
|
||||||
.map(|(hash, tx)| {
|
let mempool = MemPool::new(tx_mesg.clone());
|
||||||
let mut hash = hash.clone();
|
self.runtime.spawn(async move {
|
||||||
hash.truncate(tx.exclude_len as usize);
|
run_mempool_loop(tx_mesg, rx_mesg, f).await.unwrap();
|
||||||
hash
|
});
|
||||||
})
|
mempool
|
||||||
.collect();
|
}
|
||||||
|
}
|
||||||
let exclude = Exclude { txid: filter };
|
|
||||||
let mut txs = client
|
pub async fn run_mempool_loop<F: Fn(i64) + Send + Sync + 'static>(
|
||||||
.get_mempool_tx(Request::new(exclude))
|
tx_mesg: Sender<MemPoolMsg>,
|
||||||
.await?
|
mut rx_mesg: Receiver<MemPoolMsg>,
|
||||||
.into_inner();
|
f: F,
|
||||||
while let Some(tx) = txs.message().await? {
|
) -> anyhow::Result<()> {
|
||||||
match self.transactions.get_mut(&*tx.hash) {
|
log::info!("MEMPOOL run");
|
||||||
Some(tx) => {
|
let mut active_coin = 0;
|
||||||
tx.exclude_len += 1; // server sent us the same tx: make the filter more specific
|
let mut active_account = 0;
|
||||||
}
|
while let Some(message) = rx_mesg.recv().await {
|
||||||
None => {
|
match message {
|
||||||
let balance = self.scan_transaction(height, &tx, ivk);
|
MemPoolMsg::Active(coin, id_account) => {
|
||||||
let mempool_tx = MemPoolTransacton {
|
if coin != active_coin || id_account != active_account {
|
||||||
balance,
|
active_coin = coin;
|
||||||
exclude_len: DEFAULT_EXCLUDE_LEN,
|
active_account = id_account;
|
||||||
};
|
let _ = tx_mesg.send(MemPoolMsg::Subscribe(coin, id_account)).await;
|
||||||
self.balance += balance;
|
}
|
||||||
self.transactions.insert(tx.hash.clone(), mempool_tx);
|
}
|
||||||
}
|
MemPoolMsg::Subscribe(coin, id_account) => {
|
||||||
}
|
let mempool_handler = MemPoolHandler::new(coin, id_account, tx_mesg.clone());
|
||||||
}
|
mempool_handler.subscribe().await?;
|
||||||
|
}
|
||||||
Ok(())
|
MemPoolMsg::Balance(coin, id_account, balance) => {
|
||||||
}
|
if coin == active_coin && id_account == active_account {
|
||||||
|
f(balance);
|
||||||
fn scan_transaction(&self, height: u32, tx: &CompactTx, ivk: &SaplingIvk) -> i64 {
|
}
|
||||||
let c = CoinConfig::get_active();
|
}
|
||||||
let mut balance = 0i64;
|
MemPoolMsg::Close(coin, id_account) => {
|
||||||
for cs in tx.spends.iter() {
|
if coin == active_coin && id_account == active_account {
|
||||||
if let Some(&value) = self.nfs.get(&*cs.nf) {
|
let _ = tx_mesg.send(MemPoolMsg::Subscribe(coin, id_account)).await;
|
||||||
// nf recognized -> value is spent
|
}
|
||||||
balance -= value as i64;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let pivk = PreparedIncomingViewingKey::new(ivk);
|
Ok(())
|
||||||
for co in tx.outputs.iter() {
|
}
|
||||||
let od = to_output_description(co);
|
|
||||||
if let Some((note, _)) = try_sapling_compact_note_decryption(
|
impl MemPoolImpl {
|
||||||
c.chain.network(),
|
pub fn new(
|
||||||
BlockHeight::from_u32(height),
|
network: &Network,
|
||||||
&pivk,
|
nfs: HashMap<Hash, u64>,
|
||||||
&od,
|
sivk: SaplingIvk,
|
||||||
) {
|
oivk: Option<IncomingViewingKey>,
|
||||||
balance += note.value as i64; // value is incoming
|
) -> Self {
|
||||||
}
|
let pivk = PreparedIncomingViewingKey::new(&sivk);
|
||||||
}
|
MemPoolImpl {
|
||||||
|
network: network.clone(),
|
||||||
balance
|
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>(
|
let tx_plan = build_tx_plan::<FeeZIP327>(
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
&[Hash::default(); 2],
|
&Hash::default(),
|
||||||
&utxos,
|
&utxos,
|
||||||
&orders,
|
&orders,
|
||||||
&TransactionBuilderConfig {
|
&TransactionBuilderConfig {
|
||||||
|
|
|
@ -108,44 +108,3 @@ impl<N: Parameters> TrialDecrypter<N, OrchardDomain, OrchardViewKey, DecryptedOr
|
||||||
vtx.actions.iter().map(|co| co.into()).collect()
|
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