Remove old fullbackup system
This commit is contained in:
parent
56f4f5e73e
commit
d0550c8908
|
@ -146,12 +146,6 @@ struct CResult_____c_char make_payment_uri(char *address, uint64_t amount, char
|
|||
|
||||
struct CResult_____c_char parse_payment_uri(char *uri);
|
||||
|
||||
struct CResult_____c_char generate_random_enc_key(void);
|
||||
|
||||
struct CResult_____c_char get_full_backup(char *key);
|
||||
|
||||
void restore_full_backup(char *key, char *backup);
|
||||
|
||||
struct CResult_____c_char generate_key(void);
|
||||
|
||||
struct CResult_u8 zip_backup(char *key, char *dst_dir);
|
||||
|
@ -184,5 +178,3 @@ bool has_metal(void);
|
|||
bool has_gpu(void);
|
||||
|
||||
void use_gpu(bool v);
|
||||
|
||||
void import_sync_file(uint8_t coin, char *path);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
pub mod account;
|
||||
pub mod contact;
|
||||
pub mod fullbackup;
|
||||
pub mod historical_prices;
|
||||
pub mod message;
|
||||
pub mod recipient;
|
||||
|
|
|
@ -9,15 +9,13 @@ use crate::orchard::OrchardKeyBytes;
|
|||
use crate::taddr::{derive_taddr, derive_tkeys};
|
||||
use crate::unified::UnifiedAddressType;
|
||||
use crate::zip32::derive_zip32;
|
||||
use crate::{AccountInfo, KeyPack};
|
||||
use crate::KeyPack;
|
||||
use anyhow::anyhow;
|
||||
use bip39::{Language, Mnemonic};
|
||||
use orchard::keys::{FullViewingKey, Scope};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use serde::Serialize;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use zcash_address::unified::{Address as UA, Receiver};
|
||||
use zcash_address::{ToAddress, ZcashAddress};
|
||||
use zcash_client_backend::encoding::{decode_extended_full_viewing_key, encode_payment_address};
|
||||
|
@ -357,20 +355,6 @@ pub fn derive_keys(
|
|||
derive_zip32(c.chain.network(), &seed, account, external, address)
|
||||
}
|
||||
|
||||
/// Import synchronization data obtained from external source
|
||||
/// # Arguments
|
||||
/// * `coin`: 0 for zcash, 1 for ycash
|
||||
/// * `file`: file that contains the synchronization data
|
||||
pub async fn import_sync_data(coin: u8, file: &str) -> anyhow::Result<()> {
|
||||
let c = CoinConfig::get(coin);
|
||||
let mut db = c.db()?;
|
||||
let file = File::open(file)?;
|
||||
let file = BufReader::new(file);
|
||||
let account_info: AccountInfo = serde_json::from_reader(file)?;
|
||||
db.import_from_syncdata(&account_info)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the Unified address
|
||||
/// # Arguments
|
||||
/// * `coin`: 0 for zcash, 1 for ycash
|
||||
|
|
|
@ -620,40 +620,6 @@ pub unsafe extern "C" fn parse_payment_uri(uri: *mut c_char) -> CResult<*mut c_c
|
|||
to_cresult_str(payment_json())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn generate_random_enc_key() -> CResult<*mut c_char> {
|
||||
to_cresult_str(crate::api::fullbackup::generate_random_enc_key())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn get_full_backup(key: *mut c_char) -> CResult<*mut c_char> {
|
||||
from_c_str!(key);
|
||||
let res = || {
|
||||
let mut accounts = vec![];
|
||||
for coin in 0..MAX_COINS {
|
||||
accounts.extend(crate::api::fullbackup::get_full_backup(coin)?);
|
||||
}
|
||||
|
||||
let backup = crate::api::fullbackup::encrypt_backup(&accounts, &key)?;
|
||||
Ok(backup)
|
||||
};
|
||||
to_cresult_str(res())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn restore_full_backup(key: *mut c_char, backup: *mut c_char) {
|
||||
from_c_str!(key);
|
||||
from_c_str!(backup);
|
||||
let res = || {
|
||||
let accounts = crate::api::fullbackup::decrypt_backup(&key, &backup)?;
|
||||
for coin in 0..MAX_COINS {
|
||||
crate::api::fullbackup::restore_full_backup(coin, &accounts)?;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
log_error(res())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn generate_key() -> CResult<*mut c_char> {
|
||||
let res = || {
|
||||
|
@ -796,11 +762,3 @@ pub unsafe extern "C" fn has_gpu() -> bool {
|
|||
pub unsafe extern "C" fn use_gpu(v: bool) {
|
||||
crate::gpu::use_gpu(v)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
#[no_mangle]
|
||||
pub async unsafe extern "C" fn import_sync_file(coin: u8, path: *mut c_char) {
|
||||
from_c_str!(path);
|
||||
let res = crate::api::account::import_sync_data(coin, &path).await;
|
||||
log_error(res)
|
||||
}
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
//! Save/Load account data as JSON
|
||||
use crate::coinconfig::CoinConfig;
|
||||
use crate::db::AccountBackup;
|
||||
use bech32::{FromBase32, ToBase32, Variant};
|
||||
use chacha20poly1305::aead::{Aead, NewAead};
|
||||
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
|
||||
const NONCE: &[u8; 12] = b"unique nonce";
|
||||
|
||||
/// Return backup data of every account for a given coin
|
||||
/// # Argument
|
||||
/// * `coin`: 0 for zcash, 1 for ycash
|
||||
pub fn get_full_backup(coin: u8) -> anyhow::Result<Vec<AccountBackup>> {
|
||||
let c = CoinConfig::get(coin);
|
||||
let db = c.db()?;
|
||||
db.get_full_backup(coin)
|
||||
}
|
||||
|
||||
/// Import backup data for a given coin
|
||||
/// # Argument
|
||||
/// * `coin`: 0 for zcash, 1 for ycash
|
||||
/// * `accounts`: list of backups
|
||||
pub fn restore_full_backup(coin: u8, accounts: &[AccountBackup]) -> anyhow::Result<()> {
|
||||
let c = CoinConfig::get(coin);
|
||||
let db = c.db()?;
|
||||
db.restore_full_backup(accounts)
|
||||
}
|
||||
|
||||
/// Encrypt a list of account backups
|
||||
/// # Argument
|
||||
/// * `accounts`: list of backups
|
||||
/// * `key`: encryption key
|
||||
pub fn encrypt_backup(accounts: &[AccountBackup], key: &str) -> anyhow::Result<String> {
|
||||
let accounts_bin = bincode::serialize(&accounts)?;
|
||||
let backup = if !key.is_empty() {
|
||||
let (hrp, key, _) = bech32::decode(key)?;
|
||||
if hrp != "zwk" {
|
||||
anyhow::bail!("Invalid backup key")
|
||||
}
|
||||
let key = Vec::<u8>::from_base32(&key)?;
|
||||
let key = Key::from_slice(&key);
|
||||
|
||||
let cipher = ChaCha20Poly1305::new(key);
|
||||
// nonce is constant because we always use a different key!
|
||||
let cipher_text = cipher
|
||||
.encrypt(Nonce::from_slice(NONCE), &*accounts_bin)
|
||||
.map_err(|_e| anyhow::anyhow!("Failed to encrypt backup"))?;
|
||||
base64::encode(cipher_text)
|
||||
} else {
|
||||
base64::encode(accounts_bin)
|
||||
};
|
||||
Ok(backup)
|
||||
}
|
||||
|
||||
/// Decrypt a list of account backups
|
||||
/// # Argument
|
||||
/// * `key`: encryption key
|
||||
/// * `backup`: encrypted backup
|
||||
pub fn decrypt_backup(key: &str, backup: &str) -> anyhow::Result<Vec<AccountBackup>> {
|
||||
let backup = if !key.is_empty() {
|
||||
let (hrp, key, _) = bech32::decode(key)?;
|
||||
if hrp != "zwk" {
|
||||
anyhow::bail!("Not a valid decryption key");
|
||||
}
|
||||
let key = Vec::<u8>::from_base32(&key)?;
|
||||
let key = Key::from_slice(&key);
|
||||
|
||||
let cipher = ChaCha20Poly1305::new(key);
|
||||
let backup = base64::decode(backup)?;
|
||||
cipher
|
||||
.decrypt(Nonce::from_slice(NONCE), &*backup)
|
||||
.map_err(|_e| anyhow::anyhow!("Failed to decrypt backup"))?
|
||||
} else {
|
||||
base64::decode(backup)?
|
||||
};
|
||||
|
||||
let accounts: Vec<AccountBackup> = bincode::deserialize(&backup)?;
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
/// Generate a random encryption key
|
||||
pub fn generate_random_enc_key() -> anyhow::Result<String> {
|
||||
let mut key = [0u8; 32];
|
||||
OsRng.fill_bytes(&mut key);
|
||||
let key = bech32::encode("zwk", key.to_base32(), Variant::Bech32)?;
|
||||
Ok(key)
|
||||
}
|
202
src/db.rs
202
src/db.rs
|
@ -12,14 +12,13 @@ use crate::{sync, BlockId, CompactTxStreamerClient, Hash};
|
|||
use orchard::keys::FullViewingKey;
|
||||
use rusqlite::Error::QueryReturnedNoRows;
|
||||
use rusqlite::{params, Connection, OpenFlags, OptionalExtension, Transaction};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use tonic::transport::Channel;
|
||||
use tonic::Request;
|
||||
use zcash_client_backend::encoding::decode_extended_full_viewing_key;
|
||||
use zcash_params::coin::{get_coin_chain, get_coin_id, CoinType};
|
||||
use zcash_params::coin::{get_coin_chain, CoinType};
|
||||
use zcash_primitives::consensus::{Network, NetworkUpgrade, Parameters};
|
||||
use zcash_primitives::merkle_tree::IncrementalWitness;
|
||||
use zcash_primitives::sapling::{Diversifier, Node, Note, SaplingIvk};
|
||||
|
@ -80,22 +79,6 @@ pub struct AccountViewKey {
|
|||
pub viewonly: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AccountBackup {
|
||||
pub version: u8,
|
||||
pub coin: u8,
|
||||
pub name: String,
|
||||
pub seed: Option<String>,
|
||||
pub index: u32,
|
||||
pub z_sk: Option<String>,
|
||||
pub ivk: String,
|
||||
pub z_addr: String,
|
||||
pub t_sk: Option<String>,
|
||||
pub t_addr: Option<String>,
|
||||
pub o_sk: Option<String>,
|
||||
pub o_fvk: Option<String>,
|
||||
}
|
||||
|
||||
pub fn wrap_query_no_rows(name: &'static str) -> impl Fn(rusqlite::Error) -> anyhow::Error {
|
||||
move |err: rusqlite::Error| match err {
|
||||
QueryReturnedNoRows => anyhow::anyhow!("Query {} returned no rows", name),
|
||||
|
@ -1087,85 +1070,6 @@ impl DbAdapter {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_full_backup(&self, coin: u8) -> anyhow::Result<Vec<AccountBackup>> {
|
||||
let mut statement = self.connection.prepare(
|
||||
"SELECT name, seed, aindex, a.sk AS z_sk, ivk, a.address AS z_addr, t.sk as t_sk, t.address AS t_addr, \
|
||||
o.sk as o_sk, o.fvk as o_fvk \
|
||||
FROM accounts a LEFT JOIN taddrs t ON a.id_account = t.account LEFT JOIN orchard_addrs o ON a.id_account = o.account")?;
|
||||
let rows = statement.query_map([], |r| {
|
||||
let name: String = r.get(0)?;
|
||||
let seed: Option<String> = r.get(1)?;
|
||||
let index: u32 = r.get(2)?;
|
||||
let z_sk: Option<String> = r.get(3)?;
|
||||
let ivk: String = r.get(4)?;
|
||||
let z_addr: String = r.get(5)?;
|
||||
let t_sk: Option<String> = r.get(6)?;
|
||||
let t_addr: Option<String> = r.get(7)?;
|
||||
let o_sk: Option<Vec<u8>> = r.get(8)?;
|
||||
let o_fvk: Option<Vec<u8>> = r.get(9)?;
|
||||
let o_sk: Option<String> = o_sk.map(|v| hex::encode(v));
|
||||
let o_fvk: Option<String> = o_fvk.map(|v| hex::encode(v));
|
||||
Ok(AccountBackup {
|
||||
version: 2,
|
||||
coin,
|
||||
name,
|
||||
seed,
|
||||
index,
|
||||
z_sk,
|
||||
ivk,
|
||||
z_addr,
|
||||
t_sk,
|
||||
t_addr,
|
||||
o_sk,
|
||||
o_fvk,
|
||||
})
|
||||
})?;
|
||||
let mut accounts: Vec<AccountBackup> = vec![];
|
||||
for r in rows {
|
||||
accounts.push(r?);
|
||||
}
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
pub fn restore_full_backup(&self, accounts: &[AccountBackup]) -> anyhow::Result<()> {
|
||||
let coin = get_coin_id(self.coin_type);
|
||||
for a in accounts {
|
||||
if a.version != 2 {
|
||||
anyhow::bail!("Unsupported backup version")
|
||||
}
|
||||
log::info!("{} {} {}", a.name, a.coin, coin);
|
||||
if a.coin == coin {
|
||||
let do_insert = || {
|
||||
self.connection.execute("INSERT INTO accounts(name, seed, aindex, sk, ivk, address) VALUES (?1,?2,?3,?4,?5,?6)",
|
||||
params![a.name, a.seed, a.index, a.z_sk, a.ivk, a.z_addr])?;
|
||||
let id_account = self.connection.last_insert_rowid() as u32;
|
||||
if let Some(t_addr) = &a.t_addr {
|
||||
self.connection.execute(
|
||||
"INSERT INTO taddrs(account, sk, address) VALUES (?1,?2,?3)",
|
||||
params![id_account, a.t_sk, t_addr],
|
||||
)?;
|
||||
}
|
||||
if let Some(o_fvk) = &a.o_fvk {
|
||||
self.connection.execute(
|
||||
"INSERT INTO orchard_addrs(account, sk, fvk) VALUES (?1,?2,?3)",
|
||||
params![
|
||||
id_account,
|
||||
a.o_sk.as_ref().map(|v| hex::decode(&v).unwrap()),
|
||||
hex::decode(&o_fvk).unwrap()
|
||||
],
|
||||
)?;
|
||||
}
|
||||
Ok::<_, anyhow::Error>(())
|
||||
};
|
||||
if let Err(e) = do_insert() {
|
||||
log::info!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_message(&self, account: u32, message: &ZMessage) -> anyhow::Result<()> {
|
||||
self.connection.execute("INSERT INTO messages(account, id_tx, sender, recipient, subject, body, timestamp, height, incoming, read) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10)",
|
||||
params![account, message.id_tx, message.sender, message.recipient, message.subject, message.body, message.timestamp, message.height, message.incoming, false])?;
|
||||
|
@ -1279,65 +1183,6 @@ impl DbAdapter {
|
|||
Ok(reqs)
|
||||
}
|
||||
|
||||
pub fn import_from_syncdata(&mut self, account_info: &AccountInfo) -> anyhow::Result<Vec<u32>> {
|
||||
// get id_account from fvk
|
||||
// truncate received_notes, sapling_witnesses for account
|
||||
// add new received_notes, sapling_witnesses
|
||||
// add block
|
||||
let id_account = self
|
||||
.connection
|
||||
.query_row(
|
||||
"SELECT id_account FROM accounts WHERE ivk = ?1",
|
||||
params![&account_info.fvk],
|
||||
|row| {
|
||||
let id_account: u32 = row.get(0)?;
|
||||
Ok(id_account)
|
||||
},
|
||||
)
|
||||
.map_err(wrap_query_no_rows("import_from_syncdata/id_account"))?;
|
||||
self.connection.execute(
|
||||
"DELETE FROM received_notes WHERE account = ?1",
|
||||
params![id_account],
|
||||
)?;
|
||||
self.connection.execute(
|
||||
"DELETE FROM transactions WHERE account = ?1",
|
||||
params![id_account],
|
||||
)?;
|
||||
self.connection.execute(
|
||||
"DELETE FROM messages WHERE account = ?1",
|
||||
params![id_account],
|
||||
)?;
|
||||
let mut ids = vec![];
|
||||
for tx in account_info.txs.iter() {
|
||||
let mut tx_hash = hex::decode(&tx.hash)?;
|
||||
tx_hash.reverse();
|
||||
self.connection.execute("INSERT INTO transactions(account,txid,height,timestamp,value,address,memo,tx_index) VALUES (?1,?2,?3,?4,?5,'','',?6)",
|
||||
params![id_account, &tx_hash, tx.height, tx.timestamp, tx.value, tx.index])?;
|
||||
let id_tx = self.connection.last_insert_rowid() as u32;
|
||||
ids.push(id_tx);
|
||||
for n in tx.notes.iter() {
|
||||
let spent = if n.spent == 0 { None } else { Some(n.spent) };
|
||||
self.connection.execute("INSERT INTO received_notes(account,position,tx,height,output_index,diversifier,value,rcm,nf,spent,excluded) \
|
||||
VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11)",
|
||||
params![id_account, n.position as i64, id_tx, n.height, n.output_index, hex::decode(&n.diversifier)?, n.value as i64,
|
||||
hex::decode(&n.rcm)?, &n.nf, spent, false])?;
|
||||
let id_note = self.connection.last_insert_rowid() as u32;
|
||||
self.connection.execute(
|
||||
"INSERT INTO sapling_witnesses(note,height,witness) VALUES (?1,?2,?3) ON CONFLICT DO NOTHING",
|
||||
params![
|
||||
id_note,
|
||||
account_info.height,
|
||||
hex::decode(&n.witness).unwrap()
|
||||
],
|
||||
)?;
|
||||
}
|
||||
}
|
||||
self.connection.execute("INSERT INTO blocks(height,hash,timestamp,sapling_tree) VALUES (?1,?2,?3,?4) ON CONFLICT(height) DO NOTHING",
|
||||
params![account_info.height, hex::decode(&account_info.hash)?, account_info.timestamp, hex::decode(&account_info.sapling_tree)?])?;
|
||||
self.trim_to_height(account_info.height)?;
|
||||
Ok(ids)
|
||||
}
|
||||
|
||||
fn network(&self) -> &'static Network {
|
||||
let chain = get_coin_chain(self.coin_type);
|
||||
chain.network()
|
||||
|
@ -1385,49 +1230,6 @@ pub struct AccountRec {
|
|||
address: String,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct PlainNote {
|
||||
pub height: u32,
|
||||
pub value: u64,
|
||||
#[serde_as(as = "serde_with::hex::Hex")]
|
||||
pub nf: Vec<u8>,
|
||||
pub position: u64,
|
||||
pub tx_index: u32,
|
||||
pub output_index: u32,
|
||||
pub spent: u32,
|
||||
pub witness: String,
|
||||
pub rcm: String,
|
||||
pub diversifier: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct TxInfo {
|
||||
pub height: u32,
|
||||
pub timestamp: u32,
|
||||
pub index: i64,
|
||||
pub hash: String,
|
||||
pub value: i64,
|
||||
pub notes: Vec<PlainNote>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct OutputInfo {
|
||||
pub tx_index: u32,
|
||||
pub index: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct AccountInfo {
|
||||
pub fvk: String,
|
||||
pub height: u32,
|
||||
pub balance: u64,
|
||||
pub txs: Vec<TxInfo>,
|
||||
pub hash: String,
|
||||
pub timestamp: u32,
|
||||
pub sapling_tree: String,
|
||||
}
|
||||
|
||||
pub struct AccountData {
|
||||
pub name: String,
|
||||
pub seed: Option<String>,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use age::secrecy::ExposeSecret;
|
||||
use serde::Serialize;
|
||||
use anyhow::anyhow;
|
||||
use rusqlite::backup::Backup;
|
||||
use rusqlite::Connection;
|
||||
use serde::Serialize;
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -43,8 +43,7 @@ impl FullEncryptedBackup {
|
|||
|
||||
pub fn close(&self, pk: &str) -> anyhow::Result<()> {
|
||||
let data = self.make_zip()?;
|
||||
let pubkey =
|
||||
age::x25519::Recipient::from_str(pk).map_err(|e| anyhow!(e.to_string()))?;
|
||||
let pubkey = age::x25519::Recipient::from_str(pk).map_err(|e| anyhow!(e.to_string()))?;
|
||||
|
||||
let mut encrypted_file = File::create(self.tmp_dir.join(YWALLET_BAK))?;
|
||||
let encryptor = age::Encryptor::with_recipients(vec![Box::new(pubkey)]).unwrap();
|
||||
|
|
|
@ -119,7 +119,7 @@ pub use crate::chain::{connect_lightwalletd, get_best_server, ChainError};
|
|||
pub use crate::coinconfig::{
|
||||
init_coin, set_active, set_active_account, set_coin_lwd_url, CoinConfig, COIN_CONFIG,
|
||||
};
|
||||
pub use crate::db::{AccountData, AccountInfo, AccountRec, DbAdapter, DbAdapterBuilder, TxRec};
|
||||
pub use crate::db::{AccountData, AccountRec, DbAdapter, DbAdapterBuilder, TxRec};
|
||||
pub use crate::fountain::{FountainCodes, RaptorQDrops};
|
||||
// pub use crate::key::KeyHelpers;
|
||||
pub use crate::lw_rpc::compact_tx_streamer_client::CompactTxStreamerClient;
|
||||
|
|
|
@ -175,7 +175,7 @@ pub async fn run_mempool_loop<F: Fn(i64) + Send + Sync + 'static>(
|
|||
}) = active
|
||||
{
|
||||
if coin != active_coin || id_account != active_account {
|
||||
tx_shutdown.send(())?; // Close current connection
|
||||
let _ = tx_shutdown.send(()); // Close current connection
|
||||
let _ = tx_mesg.send(MemPoolMsg::Subscribe(coin, id_account)).await;
|
||||
} else {
|
||||
// same active account, just put it back
|
||||
|
@ -194,7 +194,7 @@ pub async fn run_mempool_loop<F: Fn(i64) + Send + Sync + 'static>(
|
|||
account: id_account,
|
||||
tx_shutdown,
|
||||
});
|
||||
MemPoolHandler::run(mempool_handler, rx_shutdown)?;
|
||||
let _ = MemPoolHandler::run(mempool_handler, rx_shutdown);
|
||||
}
|
||||
}
|
||||
MemPoolMsg::Balance(coin, id_account, balance) => {
|
||||
|
|
Loading…
Reference in New Issue