Browse Source

bin: Remove legacy cashierd code.

parazyd 3 years ago
parent
commit
c0925ebda9

+ 0 - 86
bin/cashierd/Cargo.toml

@@ -1,86 +0,0 @@
-[package]
-name = "cashierd"
-version = "0.4.1"
-homepage = "https://dark.fi"
-description = "cashier daemon for DarkFi"
-authors = ["Dyne.org foundation <foundation@dyne.org>"]
-repository = "https://github.com/darkrenaissance/darkfi"
-license = "AGPL-3.0-only"
-edition = "2021"
-
-[dependencies.darkfi]
-path = "../../"
-features = ["wallet", "node", "rpc"]
-
-[dependencies]
-# Async
-smol = "1.3.0"
-futures = "0.3.28"
-async-std = "1.12.0"
-async-trait = "0.1.68"
-async-channel = "1.8.0"
-async-executor = "1.5.1"
-easy-parallel = "3.3.0"
-
-# Crypto
-rand = "0.8.5"
-
-# Misc
-clap = {version = "4.3.3", features = ["derive"]}
-log = "0.4.19"
-num_cpus = "1.15.0"
-simplelog = "0.12.1"
-thiserror = "1.0.40"
-url = "2.4.0"
-
-# Encoding and parsing
-serde = {version = "1.0.164", features = ["derive"]}
-serde_json = "1.0.96"
-
-# Bitcoin bridge dependencies
-bdk = {version = "0.28.0", optional = true}
-anyhow = {version = "1.0.71", optional = true}
-bitcoin = {version = "0.30.0", optional = true}
-secp256k1 = {version = "0.27.0", default-features = false, features = ["rand-std"], optional = true}
-
-# Ethereum bridge dependencies
-hex = {version = "0.4.3", optional = true}
-hash-db = {version = "0.16.0", optional = true}
-lazy_static = {version = "1.4.0", optional = true}
-keccak-hasher = {version = "0.16.0", optional = true}
-num-bigint = {version = "0.4.3", features = ["rand", "serde"], optional = true}
-
-# Solana bridge dependencies
-native-tls = {version = "0.2.11", optional = true}
-async-native-tls = {version = "0.5.0", optional = true}
-solana-client = {version = "1.16.0", optional = true}
-solana-sdk = {version = "1.16.0", optional = true}
-spl-associated-token-account = {version = "1.1.3", features = ["no-entrypoint"], optional = true}
-spl-token = {version = "3.5.0", features = ["no-entrypoint"], optional = true}
-tungstenite = {version = "0.19.0", optional = true}
-
-[features]
-btc = [
-    "anyhow",
-    "bdk",
-    "bitcoin",
-    "secp256k1",
-]
-
-eth = [
-    "num-bigint",
-    "keccak-hasher",
-    "hash-db",
-    "lazy_static",
-    "hex",
-]
-
-sol = [
-    "async-native-tls",
-    "native-tls",
-    "solana-client",
-    "solana-sdk",
-    "spl-associated-token-account",
-    "spl-token",
-    "tungstenite",
-]

+ 0 - 65
bin/cashierd/cashierd_config.toml

@@ -1,65 +0,0 @@
-## cashierd configuration file
-##
-## Please make sure you go through all the settings so you can configre
-## your daemon properly.
-
-# The DNS name of the cashier (can also be an IP, or a .onion address)
-dns_addr = "testnet.cashier.dark.fi"
-
-# The endpoint where cashierd will bind its RPC socket
-rpc_listen_address = "127.0.0.1:9000"
-
-# Whether to listen with TLS or plain TCP
-serve_tls = false
-
-# Path to DER-formatted PKCS#12 archive. (Unused if serve_tls=false)
-# This can be created using openssl:
-# openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem
-tls_identity_path = "~/.config/darkfi/cashierd_identity.pfx"
-
-# Password for the created TLS identity. (Unused if serve_tls=false)
-tls_identity_password = "FOOBAR"
-
-# The endpoint to a gatewayd protocol API
-gateway_protocol_url = "tcp://testnet.gateway-protocol.dark.fi:3333"
-
-# The endpoint to a gatewayd publisher API
-gateway_publisher_url = "tcp://testnet.gateway-publish.dark.fi:4444"
-
-# Path to cashierd wallet
-cashier_wallet_path = "~/.config/darkfi/cashier_wallet.db"
-
-# Password for cashierd wallet
-cashier_wallet_password = "TEST_PASSWORD"
-
-# Path to client wallet
-client_wallet_path = "~/.config/darkfi/cashier_client_wallet.db"
-
-# Password for client wallet
-client_wallet_password = "TEST_PASSWORD"
-
-# Path to database
-database_path = "~/.config/darkfi/cashier_database.db"
-
-# Geth IPC endpoint 
-geth_socket= "~/.ethereum/ropsten/geth.ipc"
-
-# Geth passphrase 
-geth_passphrase= "TEST_PASS"
-
-# The configured networks to use.
-[[networks]]
-name = "sol"
-blockchain = "devnet"
-# The path to a secret key (can be created with solana-keygen new --no-bip39-passphrase)
-keypair = ""
-
-[[networks]]
-name = "btc"
-blockchain = "testnet"
-keypair = ""
-
-[[networks]]
-name = "eth"
-blockchain = "ropsten"
-keypair = ""

+ 0 - 103
bin/cashierd/example/eth.rs

@@ -1,103 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use num_bigint::BigUint;
-use simplelog::{ColorChoice, LevelFilter, TermLogger, TerminalMode};
-
-use darkfi::{
-    service::eth::{erc20_transfer_data, EthClient, EthTx},
-    util::{decode_base10, encode_base10},
-    Result,
-};
-
-#[async_std::main]
-async fn main() -> Result<()> {
-    TermLogger::init(
-        LevelFilter::Trace,
-        simplelog::Config::default(),
-        TerminalMode::Mixed,
-        ColorChoice::Auto,
-    )?;
-
-    let acc = "0x113b6648f34f4d0340d04ff171cbcf0b49d47827".to_string();
-    let key = "67cbb73cb293eea5fa2a7025d5479dbd50319010c03fd8821917ad0d9d53276c".to_string();
-
-    let mut eth = EthClient::new("", "/home/parazyd/.ethereum/ropsten/geth.ipc", "foobar");
-
-    eth.main_keypair.private_key = key;
-    eth.main_keypair.public_key = acc.clone();
-
-    //let key = generate_privkey();
-    //let passphrase = "foobar".to_string();
-    //let rep = eth.import_privkey(&key, &passphrase).await?;
-    //println!("{:#?}", rep);
-
-    let passphrase = "foobar".to_string();
-
-    // Recipient address
-    let dest = "0xcD640A363305c21255c58Ba9C8c1C508e6997a12".to_string();
-
-    // Latest known block, used to calculate present balance.
-    let block = eth.block_number().await?;
-    let block = block.as_str().unwrap();
-
-    // Native ETH balance
-    let hexbalance = eth.get_eth_balance(&acc, block).await?;
-    let hexbalance = hexbalance.as_str().unwrap().trim_start_matches("0x");
-    let balance = BigUint::parse_bytes(hexbalance.as_bytes(), 16).unwrap();
-    println!("{}", encode_base10(balance, 18));
-
-    /*
-    // Transfer native ETH
-    let tx = EthTx::new(
-    &acc,
-    &dest,
-    None,
-    None,
-    Some(decode_base10("0.051", 18, true)?),
-    None,
-    None,
-    );
-
-    let rep = eth.send_transaction(&tx, &passphrase).await?;
-    println!("TXID: {}", rep.as_str().unwrap());
-    */
-
-    // ERC20 Token balance
-    let mint = "0xad6d458402f60fd3bd25163575031acdce07538d"; // Ropsten DAI (get on Uniswap)
-    let hexbalance = eth.get_erc20_balance(&acc, mint).await?;
-    let hexbalance = hexbalance.as_str().unwrap().trim_start_matches("0x");
-    let balance = BigUint::parse_bytes(hexbalance.as_bytes(), 16).unwrap();
-    println!("{}", encode_base10(balance, 18));
-
-    // Transfer ERC20 token
-    let tx = EthTx::new(
-        &acc,
-        mint,
-        None,
-        None,
-        None,
-        Some(erc20_transfer_data(&dest, decode_base10("1", 18, true)?)),
-        None,
-    );
-
-    let rep = eth.send_transaction(&tx, &passphrase).await?;
-    println!("TXID: {}", rep.as_str().unwrap());
-
-    Ok(())
-}

+ 0 - 63
bin/cashierd/src/error.rs

@@ -1,63 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-#[derive(Debug, Clone, thiserror::Error)]
-pub enum Error {
-    /// Service
-    #[error("Services Error: `{0}`")]
-    ServicesError(&'static str),
-    #[error("Client failed: `{0}`")]
-    ClientFailed(String),
-    #[cfg(feature = "btc")]
-    #[error(transparent)]
-    BtcFailed(#[from] crate::service::BtcFailed),
-    #[cfg(feature = "sol")]
-    #[error("Sol client failed: `{0}`")]
-    SolFailed(String),
-    #[cfg(feature = "eth")]
-    #[error(transparent)]
-    EthFailed(#[from] crate::service::EthFailed),
-    #[error("BridgeError Error: `{0}`")]
-    BridgeError(String),
-
-    #[error("Async_channel sender error")]
-    AsyncChannelSenderError,
-    #[error(transparent)]
-    AsyncChannelReceiverError(#[from] async_channel::RecvError),
-}
-
-#[cfg(feature = "sol")]
-impl From<crate::service::SolFailed> for Error {
-    fn from(err: crate::service::SolFailed) -> Error {
-        Error::SolFailed(err.to_string())
-    }
-}
-
-impl From<darkfi::Error> for Error {
-    fn from(err: darkfi::Error) -> Error {
-        Error::ClientFailed(err.to_string())
-    }
-}
-
-impl<T> From<async_channel::SendError<T>> for Error {
-    fn from(_err: async_channel::SendError<T>) -> Error {
-        Error::AsyncChannelSenderError
-    }
-}

+ 0 - 20
bin/cashierd/src/lib.rs

@@ -1,20 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-pub mod error;
-pub mod service;

+ 0 - 821
bin/cashierd/src/main.rs

@@ -1,821 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::{net::SocketAddr, path::PathBuf, str::FromStr};
-
-use async_executor::Executor;
-use async_std::sync::{Arc, Mutex};
-use async_trait::async_trait;
-use clap::{IntoApp, Parser};
-use easy_parallel::Parallel;
-use log::{debug, info};
-use rand::rngs::OsRng;
-use serde::{Deserialize, Serialize};
-use serde_json::{json, Value};
-use simplelog::{ColorChoice, TermLogger, TerminalMode};
-
-use darkfi::{
-    blockchain::{rocks::columns, Rocks, RocksColumn},
-    crypto::{
-        address::Address,
-        keypair::{PublicKey, SecretKey},
-        proof::VerifyingKey,
-        token_id::generate_id2,
-        types::DrkTokenId,
-    },
-    node::{client::Client, state::State},
-    rpc::{
-        jsonrpc::{error as jsonerr, response as jsonresp, ErrorCode::*, JsonRequest, JsonResult},
-        rpcserver::{listen_and_serve, RequestHandler, RpcServerConfig},
-    },
-    util::{
-        cli::{log_config, spawn_config, Config},
-        expand_path, join_config_path,
-        parse::truncate,
-        serial::serialize,
-        NetworkName,
-    },
-    wallet::{cashierdb::CashierDb, walletdb::WalletDb},
-    zk::circuit::{MintContract, SpendContract},
-    Error, Result,
-};
-
-use cashierd::service::{bridge, bridge::Bridge};
-
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct FeatureNetwork {
-    /// Network name
-    pub name: String,
-    /// Blockchain (mainnet/testnet/etc.)
-    pub blockchain: String,
-    /// Keypair
-    pub keypair: String,
-}
-
-#[derive(Clone, Serialize, Deserialize, Debug)]
-pub struct CashierdConfig {
-    /// The DNS name of the cashier (can also be an IP, or a .onion address)
-    pub dns_addr: String,
-    /// The endpoint where cashierd will bind its RPC socket
-    pub rpc_listen_address: SocketAddr,
-    /// Whether to listen with TLS or plain TCP
-    pub serve_tls: bool,
-    /// Path to DER-formatted PKCS#12 archive. (Unused if serve_tls=false)
-    pub tls_identity_path: String,
-    /// Password for the TLS identity. (Unused if serve_tls=false)
-    pub tls_identity_password: String,
-    /// The endpoint to a gatewayd protocol API
-    pub gateway_protocol_url: String,
-    /// The endpoint to a gatewayd publisher API
-    pub gateway_publisher_url: String,
-    /// Path to cashierd wallet
-    pub cashier_wallet_path: String,
-    /// Password for cashierd wallet
-    pub cashier_wallet_password: String,
-    /// Path to client wallet
-    pub client_wallet_path: String,
-    /// Password for client wallet
-    pub client_wallet_password: String,
-    /// Path to database
-    pub database_path: String,
-    /// Geth IPC endpoint
-    pub geth_socket: String,
-    /// Geth passphrase
-    pub geth_passphrase: String,
-    /// The configured networks to use
-    pub networks: Vec<FeatureNetwork>,
-}
-
-/// Cashierd cli
-#[derive(Parser)]
-#[clap(name = "cashierd")]
-pub struct CliCashierd {
-    /// Sets a custom config file
-    #[clap(short, long)]
-    pub config: Option<String>,
-    /// Get Cashier Public key
-    #[clap(short, long)]
-    pub address: bool,
-    /// Increase verbosity
-    #[clap(short, parse(from_occurrences))]
-    pub verbose: u8,
-    /// Refresh the wallet and slabstore
-    #[clap(short, long)]
-    pub refresh: bool,
-}
-
-const CONFIG_FILE_CONTENTS: &[u8] = include_bytes!("../cashierd_config.toml");
-
-fn handle_bridge_error(error_code: u32) -> Result<()> {
-    match error_code {
-        1 => Err(Error::CashierError("Not Supported Client".into())),
-        2 => Err(Error::CashierError("Unable to watch the deposit address".into())),
-        3 => Err(Error::CashierError("Unable to send the token".into())),
-        _ => Err(Error::CashierError("Unknown error_code".into())),
-    }
-}
-
-#[derive(Clone, Debug)]
-pub struct Network {
-    pub name: NetworkName,
-    pub blockchain: String,
-    pub keypair: String,
-}
-
-struct Cashierd {
-    bridge: Arc<Bridge>,
-    cashier_wallet: Arc<CashierDb>,
-    networks: Vec<Network>,
-    public_key: Address,
-    config: CashierdConfig,
-}
-
-#[async_trait]
-impl RequestHandler for Cashierd {
-    async fn handle_request(&self, req: JsonRequest, executor: Arc<Executor<'_>>) -> JsonResult {
-        if req.params.as_array().is_none() {
-            return JsonResult::Err(jsonerr(InvalidParams, None, req.id))
-        }
-
-        debug!(target: "RPC", "--> {}", serde_json::to_string(&req).unwrap());
-
-        match req.method.as_str() {
-            Some("deposit") => return self.deposit(req.id, req.params, executor).await,
-            Some("withdraw") => return self.withdraw(req.id, req.params).await,
-            Some("features") => return self.features(req.id, req.params).await,
-            Some(_) => {}
-            None => {}
-        };
-
-        return JsonResult::Err(jsonerr(MethodNotFound, None, req.id))
-    }
-}
-
-impl Cashierd {
-    async fn new(config: CashierdConfig, public_key: Address) -> Result<Self> {
-        debug!(target: "CASHIER DAEMON", "Initialize");
-
-        let wallet_path =
-            format!("sqlite://{}", expand_path(&config.cashier_wallet_path)?.to_str().unwrap());
-
-        let cashier_wallet = CashierDb::new(&wallet_path, &config.cashier_wallet_password).await?;
-
-        let mut networks = Vec::new();
-
-        for network in config.clone().networks {
-            networks.push(Network {
-                name: NetworkName::from_str(&network.name)?,
-                blockchain: network.blockchain,
-                keypair: network.keypair,
-            });
-        }
-
-        let bridge = bridge::Bridge::new();
-
-        Ok(Self { bridge, cashier_wallet, networks, public_key, config })
-    }
-
-    async fn start(
-        &mut self,
-        mut client: Client,
-        state: Arc<Mutex<State>>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<(smol::Task<Result<()>>, smol::Task<Result<()>>)> {
-        self.cashier_wallet.init_db().await?;
-
-        for network in self.networks.iter() {
-            match network.name {
-                #[cfg(feature = "sol")]
-                NetworkName::Solana => {
-                    debug!(target: "CASHIER DAEMON", "Adding solana network");
-                    use cashierd::service::SolClient;
-
-                    let _bridge = self.bridge.clone();
-
-                    let sol_client = SolClient::new(
-                        self.cashier_wallet.clone(),
-                        &network.blockchain,
-                        &network.keypair,
-                    )
-                    .await?;
-
-                    _bridge.add_clients(NetworkName::Solana, sol_client).await?;
-                }
-
-                #[cfg(feature = "eth")]
-                NetworkName::Ethereum => {
-                    debug!(target: "CASHIER DAEMON", "Adding ethereum network");
-
-                    use cashierd::service::EthClient;
-
-                    let _bridge = self.bridge.clone();
-
-                    let passphrase = self.config.geth_passphrase.clone();
-
-                    let mut eth_client = EthClient::new(
-                        &network.blockchain,
-                        expand_path(&self.config.geth_socket)?.to_str().unwrap(),
-                        &passphrase,
-                    );
-
-                    eth_client.setup_keypair(self.cashier_wallet.clone(), &network.keypair).await?;
-
-                    _bridge.add_clients(NetworkName::Ethereum, Arc::new(eth_client)).await?;
-                }
-
-                #[cfg(feature = "btc")]
-                NetworkName::Bitcoin => {
-                    debug!(target: "CASHIER DAEMON", "Adding bitcoin network");
-                    use cashierd::service::btc::BtcClient;
-
-                    let _bridge = self.bridge.clone();
-
-                    let btc_client = BtcClient::new(
-                        self.cashier_wallet.clone(),
-                        &network.blockchain,
-                        &network.keypair,
-                    )
-                    .await?;
-
-                    _bridge.add_clients(NetworkName::Bitcoin, btc_client).await?;
-                }
-                _ => {}
-            }
-        }
-
-        client.start().await?;
-
-        let (notify, recv_coin) = async_channel::unbounded::<(PublicKey, u64)>();
-
-        client
-            .connect_to_subscriber_from_cashier(
-                state.clone(),
-                self.cashier_wallet.clone(),
-                notify.clone(),
-                executor.clone(),
-            )
-            .await?;
-
-        let cashier_wallet = self.cashier_wallet.clone();
-        let bridge = self.bridge.clone();
-        let ex = executor.clone();
-        let listen_for_receiving_coins_task: smol::Task<Result<()>> = executor.spawn(async move {
-            let ex2 = ex.clone();
-            loop {
-                Self::listen_for_receiving_coins(
-                    bridge.clone(),
-                    cashier_wallet.clone(),
-                    recv_coin.clone(),
-                    ex2.clone(),
-                )
-                .await?;
-            }
-        });
-
-        let bridge2 = self.bridge.clone();
-        let listen_for_notification_from_bridge_task: smol::Task<Result<()>> =
-            executor.spawn(async move {
-                while let Some(token_notification) = bridge2.clone().listen().await {
-                    debug!(target: "CASHIER DAEMON", "Received notification from bridge");
-
-                    let token_notification = token_notification?;
-
-                    let received_balance = truncate(
-                        token_notification.received_balance,
-                        8,
-                        token_notification.decimals,
-                    )?;
-
-                    client
-                        .send(
-                            token_notification.drk_pub_key,
-                            received_balance,
-                            token_notification.token_id,
-                            true,
-                            state.clone(),
-                        )
-                        .await?;
-                }
-                Ok(())
-            });
-
-        Ok((listen_for_receiving_coins_task, listen_for_notification_from_bridge_task))
-    }
-
-    async fn listen_for_receiving_coins(
-        bridge: Arc<Bridge>,
-        cashier_wallet: Arc<CashierDb>,
-        recv_coin: async_channel::Receiver<(PublicKey, u64)>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<()> {
-        // received drk coin
-        let (drk_pub_key, amount) = recv_coin.recv().await?;
-
-        debug!(target: "CASHIER DAEMON", "Receive coin with amount: {}", amount);
-
-        // get public key, and token_id of the token
-        let token =
-            cashier_wallet.get_withdraw_token_public_key_by_dkey_public(&drk_pub_key).await?;
-
-        // send a request to bridge to send equivalent amount of
-        // received drk coin to token publickey
-        if let Some(withdraw_token) = token {
-            let bridge_subscribtion = bridge
-                .subscribe(drk_pub_key, Some(withdraw_token.mint_address), executor.clone())
-                .await;
-
-            // send a request to the bridge to send amount of token
-            // equivalent to the received drk
-            bridge_subscribtion
-                .sender
-                .send(bridge::BridgeRequests {
-                    network: withdraw_token.network.clone(),
-                    payload: bridge::BridgeRequestsPayload::Send(
-                        withdraw_token.token_public_key.clone(),
-                        amount,
-                    ),
-                })
-                .await?;
-
-            // receive a response
-            let res = bridge_subscribtion.receiver.recv().await?;
-
-            // check the response's error
-            let error_code = res.error as u32;
-
-            if error_code != 0 {
-                return handle_bridge_error(error_code)
-            }
-
-            match res.payload {
-                bridge::BridgeResponsePayload::Send => {
-                    cashier_wallet
-                        .confirm_withdraw_key_record(
-                            &withdraw_token.token_public_key,
-                            &withdraw_token.network,
-                        )
-                        .await?;
-                }
-                _ => {
-                    return Err(Error::CashierError(
-                        "Receive unknown value from Subscription".into(),
-                    ))
-                }
-            }
-        }
-
-        Ok(())
-    }
-
-    fn check_token_id(network: &NetworkName, _token_id: &str) -> Result<Option<String>> {
-        match network {
-            #[cfg(feature = "sol")]
-            NetworkName::Solana => {
-                use cashierd::service::sol::SOL_NATIVE_TOKEN_ID;
-                if _token_id != SOL_NATIVE_TOKEN_ID {
-                    return Ok(Some(_token_id.to_string()))
-                }
-                Ok(None)
-            }
-            #[cfg(feature = "eth")]
-            NetworkName::Ethereum => {
-                use cashierd::service::eth::ETH_NATIVE_TOKEN_ID;
-                if _token_id != ETH_NATIVE_TOKEN_ID {
-                    return Ok(Some(_token_id.to_string()))
-                }
-                Ok(None)
-            }
-            #[cfg(feature = "btc")]
-            NetworkName::Bitcoin => Ok(None),
-            _ => Err(Error::NotSupportedNetwork),
-        }
-    }
-
-    // RPCAPI:
-    // Executes a deposit request given `network` and `token_id`.
-    // Returns the address where the deposit shall be transferred to.
-    // --> {"jsonrpc": "2.0", "method": "deposit", "params": ["network", "token", "publickey"], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL", "id": 1}
-    async fn deposit(&self, id: Value, params: Value, executor: Arc<Executor<'_>>) -> JsonResult {
-        info!(target: "CASHIER DAEMON", "Received deposit request");
-
-        let args: &Vec<serde_json::Value> = params.as_array().unwrap();
-
-        if args.len() != 3 {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id))
-        }
-
-        let network: NetworkName;
-        let mut mint_address: &str;
-        let drk_pub_key: &str;
-
-        match (args[0].as_str(), args[1].as_str(), args[2].as_str()) {
-            (Some(n), Some(m), Some(d)) => {
-                if NetworkName::from_str(n).is_err() {
-                    return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id))
-                }
-                network = NetworkName::from_str(n).unwrap();
-                mint_address = m;
-                drk_pub_key = d;
-            }
-            (None, _, _) => return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id)),
-            (_, None, _) => return JsonResult::Err(jsonerr(InvalidTokenIdParam, None, id)),
-            (_, _, None) => return JsonResult::Err(jsonerr(InvalidAddressParam, None, id)),
-        }
-
-        // Check if the features list contains this network
-        if !self.networks.iter().any(|net| net.name == network) {
-            return JsonResult::Err(jsonerr(
-                InvalidParams,
-                Some(format!("Cashier doesn't support this network: {}", network)),
-                id,
-            ))
-        }
-
-        let result: Result<String> = async {
-            let token_id = generate_id2(mint_address, &network)?;
-
-            let mint_address_opt = Self::check_token_id(&network, mint_address)?;
-
-            if mint_address_opt.is_none() {
-                mint_address = "";
-            }
-            let drk_pub_key = Address::from_str(drk_pub_key)?;
-            let drk_pub_key: PublicKey = PublicKey::try_from(drk_pub_key)?;
-
-            // check if the drk public key already exist
-            let check = self
-                .cashier_wallet
-                .get_deposit_token_keys_by_dkey_public(&drk_pub_key, &network)
-                .await?;
-
-            // start new subscription from the bridge and then cashierd will
-            // send a request to the bridge to generate keypair for the desired token
-            // and start watch this token's keypair
-            // once a bridge receive an update for this token's address
-            // cashierd will get notification from bridge.listen() function
-            //
-            // The "if statement" check from the cashierdb if the node's drk_pub_key already exist
-            // in this case it will not generate new keypair but it will
-            // retrieve the old generated keypair
-            //
-            // Once receive a response from the bridge, the cashierd then save a deposit
-            // record in cashierdb with the network name and token id
-
-            let bridge = self.bridge.clone();
-            let bridge_subscribtion =
-                bridge.subscribe(drk_pub_key, mint_address_opt, executor).await;
-
-            if check.is_empty() {
-                bridge_subscribtion
-                    .sender
-                    .send(bridge::BridgeRequests {
-                        network: network.clone(),
-                        payload: bridge::BridgeRequestsPayload::Watch(None),
-                    })
-                    .await?;
-            } else {
-                let keypair = check[0].clone();
-                bridge_subscribtion
-                    .sender
-                    .send(bridge::BridgeRequests {
-                        network: network.clone(),
-                        payload: bridge::BridgeRequestsPayload::Watch(Some(keypair)),
-                    })
-                    .await?;
-            }
-
-            let bridge_res = bridge_subscribtion.receiver.recv().await?;
-
-            let error_code = bridge_res.error as u32;
-
-            if error_code != 0 {
-                return handle_bridge_error(error_code).map(|_| String::new())
-            }
-
-            match bridge_res.payload {
-                bridge::BridgeResponsePayload::Watch(token_key) => {
-                    // add pairings to db
-                    self.cashier_wallet
-                        .put_deposit_keys(
-                            &drk_pub_key,
-                            &token_key.private_key,
-                            &serialize(&token_key.public_key),
-                            &network,
-                            &token_id,
-                            mint_address.into(),
-                        )
-                        .await?;
-
-                    Ok(token_key.public_key)
-                }
-                bridge::BridgeResponsePayload::Address(token_pub) => Ok(token_pub),
-                _ => Err(Error::CashierError("Receive unknown value from Subscription".into())),
-            }
-        }
-        .await;
-
-        match result {
-            Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(id))),
-            Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
-        }
-    }
-
-    // RPCAPI:
-    // Executes a withdraw request given `network`, `token_id`, `publickey`
-    // and `amount`. `publickey` is supposed to correspond to `network`.
-    // Returns the transaction ID of the processed withdraw.
-    // --> {"jsonrpc": "2.0", "method": "withdraw", "params": ["network", "token", "publickey", "amount"], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": "txID", "id": 1}
-    async fn withdraw(&self, id: Value, params: Value) -> JsonResult {
-        info!(target: "CASHIER DAEMON", "Received withdraw request");
-
-        let args: &Vec<serde_json::Value> = params.as_array().unwrap();
-
-        if args.len() != 4 {
-            return JsonResult::Err(jsonerr(InvalidParams, None, id))
-        }
-
-        let network: NetworkName;
-        let mut mint_address: &str;
-        let address: &str;
-
-        match (args[0].as_str(), args[1].as_str(), args[2].as_str()) {
-            (Some(n), Some(m), Some(a)) => {
-                if NetworkName::from_str(n).is_err() {
-                    return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id))
-                }
-                network = NetworkName::from_str(n).unwrap();
-                mint_address = m;
-                address = a;
-            }
-            (None, _, _) => return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id)),
-            (_, None, _) => return JsonResult::Err(jsonerr(InvalidTokenIdParam, None, id)),
-            (_, _, None) => return JsonResult::Err(jsonerr(InvalidAddressParam, None, id)),
-        }
-
-        // Check if the features list contains this network
-        if !self.networks.iter().any(|net| net.name == network) {
-            return JsonResult::Err(jsonerr(
-                InvalidParams,
-                Some(format!("Cashier doesn't support this network: {}", network)),
-                id,
-            ))
-        }
-
-        let result: Result<String> = async {
-            let token_id: DrkTokenId = generate_id2(mint_address, &network)?;
-
-            let mint_address_opt = Self::check_token_id(&network, mint_address)?;
-
-            if mint_address_opt.is_none() {
-                // empty string
-                mint_address = "";
-            }
-
-            let address = serialize(&address.to_string());
-
-            let cashier_public: PublicKey;
-
-            if let Some(addr) = self
-                .cashier_wallet
-                .get_withdraw_keys_by_token_public_key(&address, &network)
-                .await?
-            {
-                cashier_public = addr.public;
-            } else {
-                let cashier_secret = SecretKey::random(&mut OsRng);
-                cashier_public = PublicKey::from_secret(cashier_secret);
-
-                self.cashier_wallet
-                    .put_withdraw_keys(
-                        &address,
-                        &cashier_public,
-                        &cashier_secret,
-                        &network,
-                        &token_id,
-                        mint_address.into(),
-                    )
-                    .await?;
-            }
-
-            let cashier_public_str = Address::from(cashier_public).to_string();
-            Ok(cashier_public_str)
-        }
-        .await;
-
-        match result {
-            Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(id))),
-            Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
-        }
-    }
-
-    // RPCAPI:
-    // Returns supported cashier features, like network, listening ports, etc.
-    // --> {"jsonrpc": "2.0", "method": "features", "params": [], "id": 1}
-    // <-- {"jsonrpc": "2.0", "result": {"network": ["btc", "sol"]}, "id": 1}
-    async fn features(&self, id: Value, _params: Value) -> JsonResult {
-        let tcp_port: Option<u16>;
-        let tls_port: Option<u16>;
-        let onionaddr: Option<String>;
-        let dnsaddr: Option<String>;
-
-        if self.config.serve_tls {
-            tls_port = Some(self.config.rpc_listen_address.port());
-            tcp_port = None;
-        } else {
-            tcp_port = Some(self.config.rpc_listen_address.port());
-            tls_port = None;
-        }
-
-        if self.config.dns_addr.ends_with(".onion") {
-            onionaddr = Some(self.config.dns_addr.clone());
-            dnsaddr = None;
-        } else {
-            dnsaddr = Some(self.config.dns_addr.clone());
-            onionaddr = None;
-        }
-
-        let mut resp: serde_json::Value = json!(
-        {
-            "server_version": env!("CARGO_PKG_VERSION"),
-            "protocol_version": "1.0",
-            "public_key": self.public_key.to_string(),
-            "networks": [],
-            "hosts": {
-                "tcp_port": tcp_port,
-                "tls_port": tls_port,
-                "onion_addr": onionaddr,
-                "dns_addr": dnsaddr,
-            }
-        }
-        );
-
-        for network in self.networks.iter() {
-            resp.as_object_mut().unwrap()["networks"].as_array_mut().unwrap().push(json!(
-                    {
-                        network.name.to_string().to_lowercase():
-                        {"chain": network.blockchain.to_lowercase()}
-                    }
-            ));
-        }
-
-        JsonResult::Resp(jsonresp(resp, id))
-    }
-}
-
-async fn start(
-    executor: Arc<Executor<'_>>,
-    config: &CashierdConfig,
-    get_address_flag: bool,
-) -> Result<()> {
-    let client_wallet_path =
-        format!("sqlite://{}", expand_path(&config.client_wallet_path)?.to_str().unwrap());
-
-    let client_wallet = WalletDb::new(&client_wallet_path, &config.client_wallet_password).await?;
-
-    let rocks = Rocks::new(expand_path(&config.database_path.clone())?.as_path())?;
-
-    info!("Building verifying key for the mint contract...");
-    let mint_vk = VerifyingKey::build(11, &MintContract::default());
-    info!("Building verifying key for the spend contract...");
-    let spend_vk = VerifyingKey::build(11, &SpendContract::default());
-
-    // new Client
-    let gateway_urls =
-        (config.gateway_protocol_url.parse()?, config.gateway_publisher_url.parse()?);
-    let client = Client::new(rocks.clone(), gateway_urls, client_wallet.clone()).await?;
-
-    let tree = client.get_tree().await?;
-    let merkle_roots = RocksColumn::<columns::MerkleRoots>::new(rocks.clone());
-    let nullifiers = RocksColumn::<columns::Nullifiers>::new(rocks);
-
-    // get cashier public key
-    let cashier_public = client.main_keypair.public;
-
-    // new Cashier daemon
-    let mut cashierd = Cashierd::new(config.clone(), Address::from(cashier_public)).await?;
-
-    // this will print the cashier public key and exit
-    if get_address_flag {
-        info!("Public Key: {}", cashierd.public_key);
-        return Ok(())
-    };
-
-    // new State
-    let public_keys = vec![cashier_public];
-    let state = Arc::new(Mutex::new(State {
-        tree,
-        merkle_roots,
-        nullifiers,
-        public_keys,
-        mint_vk,
-        spend_vk,
-    }));
-
-    // start cashier
-    let (t1, t2) = cashierd.start(client, state, executor.clone()).await?;
-
-    // config for rpc
-    let cfg = RpcServerConfig {
-        socket_addr: config.rpc_listen_address,
-        use_tls: config.serve_tls,
-        identity_path: expand_path(&config.clone().tls_identity_path)?,
-        identity_pass: config.tls_identity_password.clone(),
-    };
-
-    // listen and serve RPC
-    listen_and_serve(cfg, Arc::new(cashierd), executor).await?;
-
-    t1.cancel().await;
-    t2.cancel().await;
-
-    Ok(())
-}
-
-#[async_std::main]
-async fn main() -> Result<()> {
-    let args = CliCashierd::parse();
-    let matches = CliCashierd::command().get_matches();
-
-    let config_path = if args.config.is_some() {
-        expand_path(&args.config.unwrap())?
-    } else {
-        join_config_path(&PathBuf::from("cashierd.toml"))?
-    };
-
-    // Spawn config file if it's not in place already.
-    spawn_config(&config_path, CONFIG_FILE_CONTENTS)?;
-
-    let verbosity_level = matches.occurrences_of("verbose");
-
-    let (lvl, conf) = log_config(verbosity_level)?;
-
-    TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
-
-    let config: CashierdConfig = Config::<CashierdConfig>::load(config_path)?;
-
-    if args.refresh {
-        info!(target: "CASHIER DAEMON", "Refresh the wallet and the database");
-
-        // refresh cashier's client wallet
-        let client_wallet_path =
-            format!("sqlite://{}", expand_path(&config.client_wallet_path)?.to_str().unwrap());
-        let client_wallet =
-            WalletDb::new(&client_wallet_path, &config.client_wallet_password).await?;
-        client_wallet.remove_own_coins().await?;
-
-        // refresh cashier wallet
-        let wallet_path =
-            format!("sqlite://{}", expand_path(&config.cashier_wallet_path)?.to_str().unwrap());
-        let wallet = CashierDb::new(&wallet_path, &config.cashier_wallet_password).await?;
-        wallet.remove_withdraw_and_deposit_keys().await?;
-
-        // refresh rocks database
-        if let Some(path) = expand_path(&config.database_path)?.to_str() {
-            info!(target: "CASHIER DAEMON", "Remove database: {}", path);
-            std::fs::remove_dir_all(path)?;
-        }
-
-        info!("Wallet updated successfully.");
-        return Ok(())
-    }
-
-    let get_address_flag = args.address;
-
-    let ex = Arc::new(Executor::new());
-    let (signal, shutdown) = async_channel::unbounded::<()>();
-
-    let ex2 = ex.clone();
-
-    let nthreads = num_cpus::get();
-    debug!(target: "CASHIER DAEMON", "Run {} executor threads", nthreads);
-
-    let (_, result) = Parallel::new()
-        .each(0..nthreads, |_| smol::future::block_on(ex.run(shutdown.recv())))
-        // Run the main future on the current thread.
-        .finish(|| {
-            smol::future::block_on(async move {
-                start(ex2, &config, get_address_flag).await?;
-                drop(signal);
-                Ok::<(), darkfi::Error>(())
-            })
-        });
-
-    result
-}

+ 0 - 280
bin/cashierd/src/service/bridge.rs

@@ -1,280 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-use std::collections::HashMap;
-
-use async_executor::Executor;
-use async_std::sync::{Arc, Mutex};
-use async_trait::async_trait;
-use futures::stream::{FuturesUnordered, StreamExt};
-use log::{debug, error};
-
-use darkfi::{
-    crypto::{keypair::PublicKey, types::*},
-    util::NetworkName,
-    wallet::cashierdb::TokenKey,
-    Error, Result,
-};
-
-pub struct BridgeRequests {
-    pub network: NetworkName,
-    pub payload: BridgeRequestsPayload,
-}
-
-pub struct BridgeResponse {
-    pub error: BridgeResponseError,
-    pub payload: BridgeResponsePayload,
-}
-
-pub enum BridgeRequestsPayload {
-    Send(Vec<u8>, u64),      // send (address, amount)
-    Watch(Option<TokenKey>), // if already has a keypair
-}
-
-pub enum BridgeResponsePayload {
-    Watch(TokenSubscribtion),
-    Address(String),
-    Send,
-    Empty,
-}
-
-#[repr(u8)]
-pub enum BridgeResponseError {
-    NoError,
-    NotSupportedClient,
-    BridgeWatchSubscribtionError,
-    BridgeSendSubscribtionError,
-}
-
-pub struct BridgeSubscribtion {
-    pub sender: async_channel::Sender<BridgeRequests>,
-    pub receiver: async_channel::Receiver<BridgeResponse>,
-}
-
-#[derive(Debug)]
-pub struct TokenSubscribtion {
-    pub private_key: Vec<u8>,
-    pub public_key: String,
-}
-
-#[derive(Debug)]
-pub struct TokenNotification {
-    pub network: NetworkName,
-    pub token_id: DrkTokenId,
-    pub drk_pub_key: PublicKey,
-    pub received_balance: u64,
-    pub decimals: u16,
-}
-
-pub struct Bridge {
-    clients: Mutex<HashMap<NetworkName, Arc<dyn NetworkClient + Send + Sync>>>,
-    notifiers: FuturesUnordered<async_channel::Receiver<TokenNotification>>,
-}
-
-impl Bridge {
-    pub fn new() -> Arc<Self> {
-        Arc::new(Self { clients: Mutex::new(HashMap::new()), notifiers: FuturesUnordered::new() })
-    }
-
-    pub async fn add_clients(
-        self: Arc<Self>,
-        network: NetworkName,
-        client: Arc<dyn NetworkClient + Send + Sync>,
-    ) -> Result<()> {
-        debug!(target: "BRIDGE", "Adding new client");
-
-        let client2 = client.clone();
-        let notifier = client2.get_notifier().await?;
-
-        if !notifier.is_closed() {
-            self.notifiers.push(notifier);
-        }
-
-        self.clients.lock().await.insert(network, client.clone());
-
-        Ok(())
-    }
-
-    pub async fn listen(self: Arc<Self>) -> Option<Result<TokenNotification>> {
-        if !self.notifiers.is_empty() {
-            debug!(target: "BRIDGE", "Start listening for new notifications");
-            let notification = self
-                .notifiers
-                .iter()
-                .map(|n| n.recv())
-                .collect::<FuturesUnordered<async_channel::Recv<TokenNotification>>>()
-                .next()
-                .await
-                .map(|o| o.map_err(Error::from));
-
-            debug!(target: "BRIDGE", "Stop listening for new notifications");
-
-            notification
-        } else {
-            None
-        }
-    }
-
-    pub async fn subscribe(
-        self: Arc<Self>,
-        drk_pub_key: PublicKey,
-        mint: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> BridgeSubscribtion {
-        debug!(target: "BRIDGE", "Start new subscription");
-        let (sender, req) = async_channel::unbounded();
-        let (rep, receiver) = async_channel::unbounded();
-
-        executor
-            .spawn(self.listen_for_new_subscription(req, rep, drk_pub_key, mint, executor.clone()))
-            .detach();
-
-        BridgeSubscribtion { sender, receiver }
-    }
-
-    async fn listen_for_new_subscription(
-        self: Arc<Self>,
-        req: async_channel::Receiver<BridgeRequests>,
-        rep: async_channel::Sender<BridgeResponse>,
-        drk_pub_key: PublicKey,
-        mint: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<()> {
-        debug!(target: "BRIDGE", "Listen for new subscriptions");
-        let req = req.recv().await?;
-
-        let network = req.network;
-
-        if !self.clients.lock().await.contains_key(&network) {
-            let res = BridgeResponse {
-                error: BridgeResponseError::NotSupportedClient,
-                payload: BridgeResponsePayload::Empty,
-            };
-            rep.send(res).await?;
-            return Ok(())
-        }
-
-        let mut mint_address: Option<String> = mint.clone();
-
-        if mint.is_some() && mint.unwrap().is_empty() {
-            mint_address = None;
-        }
-
-        let client: Arc<dyn NetworkClient + Send + Sync>;
-        // avoid deadlock
-        {
-            let c = &self.clients.lock().await[&network];
-            client = c.clone();
-        }
-
-        let res: BridgeResponse;
-
-        match req.payload {
-            BridgeRequestsPayload::Watch(val) => match val {
-                Some(token_key) => {
-                    let pub_key = client
-                        .subscribe_with_keypair(
-                            token_key.secret_key,
-                            token_key.public_key,
-                            drk_pub_key,
-                            mint_address,
-                            executor,
-                        )
-                        .await;
-
-                    if pub_key.is_err() {
-                        error!(target: "BRIDGE", "{}", pub_key.unwrap_err().to_string());
-                        res = BridgeResponse {
-                            error: BridgeResponseError::BridgeWatchSubscribtionError,
-                            payload: BridgeResponsePayload::Empty,
-                        };
-                    } else {
-                        res = BridgeResponse {
-                            error: BridgeResponseError::NoError,
-                            payload: BridgeResponsePayload::Address(pub_key?),
-                        };
-                    }
-                }
-                None => {
-                    let sub = client.subscribe(drk_pub_key, mint_address, executor).await;
-                    if sub.is_err() {
-                        error!(target: "BRIDGE", "{}", sub.unwrap_err().to_string());
-                        res = BridgeResponse {
-                            error: BridgeResponseError::BridgeWatchSubscribtionError,
-                            payload: BridgeResponsePayload::Empty,
-                        };
-                    } else {
-                        let sub = sub?;
-                        res = BridgeResponse {
-                            error: BridgeResponseError::NoError,
-                            payload: BridgeResponsePayload::Watch(sub),
-                        };
-                    }
-                }
-            },
-            BridgeRequestsPayload::Send(addr, amount) => {
-                let result = client.send(addr, mint_address, amount).await;
-
-                if result.is_err() {
-                    error!(target: "BRIDGE", "{}", result.unwrap_err().to_string());
-                    res = BridgeResponse {
-                        error: BridgeResponseError::BridgeSendSubscribtionError,
-                        payload: BridgeResponsePayload::Empty,
-                    };
-                } else {
-                    res = BridgeResponse {
-                        error: BridgeResponseError::NoError,
-                        payload: BridgeResponsePayload::Send,
-                    };
-                }
-            }
-        }
-
-        rep.send(res).await?;
-
-        Ok(())
-    }
-}
-
-#[async_trait]
-pub trait NetworkClient {
-    async fn subscribe(
-        self: Arc<Self>,
-        drk_pub_key: PublicKey,
-        mint: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<TokenSubscribtion>;
-
-    // should check if the keypair in not already subscribed
-    async fn subscribe_with_keypair(
-        self: Arc<Self>,
-        private_key: Vec<u8>,
-        public_key: Vec<u8>,
-        drk_pub_key: PublicKey,
-        mint: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<String>;
-
-    async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>>;
-
-    async fn send(
-        self: Arc<Self>,
-        address: Vec<u8>,
-        mint: Option<String>,
-        amount: u64,
-    ) -> Result<()>;
-}

+ 0 - 967
bin/cashierd/src/service/btc.rs

@@ -1,967 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-// TODO: This module needs cleanup related to PublicKey/SecretKey types.
-
-use std::{
-    cmp::max,
-    collections::BTreeMap,
-    convert::{From, TryFrom, TryInto},
-    fmt,
-    ops::Add,
-    str::FromStr,
-    time::{Duration, Instant},
-};
-
-use anyhow::Context;
-use async_executor::Executor;
-use async_std::sync::{Arc, Mutex};
-use async_trait::async_trait;
-
-use bdk::electrum_client::{
-    Client as ElectrumClient, ElectrumApi, GetBalanceRes, GetHistoryRes, HeaderNotification,
-};
-use bitcoin::{
-    blockdata::{
-        script::{Builder, Script},
-        transaction::{OutPoint, SigHashType, Transaction, TxIn, TxOut},
-    },
-    consensus::encode::serialize_hex,
-    hash_types::PubkeyHash as BtcPubKeyHash,
-    network::constants::Network,
-    util::{
-        address::Address,
-        ecdsa::{PrivateKey as BtcPrivKey, PublicKey as BtcPubKey},
-        psbt::serialize::Serialize,
-    },
-};
-use log::*;
-use secp256k1::{
-    constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE},
-    key::{PublicKey, SecretKey},
-    rand::rngs::OsRng,
-    All, Message as BtcMessage, Secp256k1,
-};
-
-use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
-use darkfi::{
-    crypto::{keypair::PublicKey as DrkPublicKey, token_id::generate_id2},
-    util::{
-        expand_path, load_keypair_to_str,
-        serial::{deserialize, serialize, Decodable, Encodable},
-        NetworkName,
-    },
-    wallet::cashierdb::{CashierDb, TokenKey},
-    Error, Result,
-};
-
-// Swap out these types for any future non bitcoin-rs types
-pub type PubAddress = Address;
-pub type PubKey = BtcPubKey;
-pub type PrivKey = BtcPrivKey;
-
-const KEYPAIR_LENGTH: usize = SECRET_KEY_SIZE + PUBLIC_KEY_SIZE;
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
-pub struct BlockHeight(u32);
-
-impl From<BlockHeight> for u32 {
-    fn from(height: BlockHeight) -> Self {
-        height.0
-    }
-}
-
-impl TryFrom<HeaderNotification> for BlockHeight {
-    type Error = BtcFailed;
-    fn try_from(value: HeaderNotification) -> BtcResult<Self> {
-        Ok(Self(value.height.try_into().context("Failed to fit usize into u32")?))
-    }
-}
-
-impl Add<u32> for BlockHeight {
-    type Output = BlockHeight;
-    fn add(self, rhs: u32) -> Self::Output {
-        BlockHeight(self.0 + rhs)
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub enum ExpiredTimelocks {
-    None,
-    Cancel,
-    Punish,
-}
-#[derive(Clone, Debug, PartialEq)]
-pub struct Keypair {
-    secret: SecretKey,
-    public: PublicKey,
-    context: Secp256k1<All>,
-}
-
-impl Keypair {
-    pub fn new() -> Self {
-        let secp = Secp256k1::new();
-        let mut rng = OsRng::new().expect("OsRng");
-
-        let (secret, public) = secp.generate_keypair(&mut rng);
-        Self { secret, public, context: secp }
-    }
-
-    pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
-        let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
-
-        bytes[..SECRET_KEY_SIZE].copy_from_slice(self.secret.as_ref());
-        bytes[SECRET_KEY_SIZE..].copy_from_slice(&self.public.serialize());
-
-        bytes
-    }
-
-    pub fn from_bytes(bytes: &[u8]) -> BtcResult<Keypair> {
-        if bytes.len() != KEYPAIR_LENGTH {
-            return Err(BtcFailed::KeypairError("Not right size".to_string()))
-        }
-        let secp = Secp256k1::new();
-
-        let secret = SecretKey::from_slice(&bytes[..SECRET_KEY_SIZE])?;
-        let public = PublicKey::from_slice(&bytes[SECRET_KEY_SIZE..])?;
-
-        Ok(Keypair { secret, public, context: secp })
-    }
-    fn secret(&self) -> SecretKey {
-        self.secret
-    }
-    pub fn pubkey(&self) -> PublicKey {
-        self.public
-    }
-    pub fn as_tuple(&self) -> (SecretKey, PublicKey) {
-        (self.secret, self.public)
-    }
-}
-
-impl Default for Keypair {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-#[derive(Clone)]
-pub struct Account {
-    keypair: Arc<Keypair>,
-    btc_privkey: BtcPrivKey,
-    pub btc_pubkey: BtcPubKey,
-    pub address: Address,
-    pub script_pubkey: Script,
-    pub network: Network,
-}
-
-impl Account {
-    pub fn new(keypair: &Keypair, network: Network) -> Self {
-        let (secret_key, _public_key) = keypair.as_tuple();
-
-        let btc_privkey = BtcPrivKey::new(secret_key, network);
-        let btc_pubkey = btc_privkey.public_key(&keypair.context);
-        let address = Account::derive_btc_address(btc_pubkey, network);
-        let script_pubkey = address.script_pubkey();
-
-        Self {
-            keypair: Arc::new(keypair.clone()),
-            btc_privkey,
-            btc_pubkey,
-            address,
-            script_pubkey,
-            network,
-        }
-    }
-    pub fn priv_from_secret(keypair: &Keypair, network: Network) -> BtcPrivKey {
-        BtcPrivKey::new(keypair.secret(), network)
-    }
-    pub fn btcpub_from_keypair(keypair: &Keypair) -> BtcPubKey {
-        BtcPubKey::new(keypair.public)
-    }
-    pub fn btc_privkey(&self) -> &BtcPrivKey {
-        &self.btc_privkey
-    }
-    pub fn btc_pubkey(&self) -> &BtcPubKey {
-        &self.btc_pubkey
-    }
-    pub fn btc_pubkey_hash(&self) -> BtcPubKeyHash {
-        self.btc_pubkey.pubkey_hash()
-    }
-    pub fn derive_btc_script_pubkey(pubkey: PublicKey, network: Network) -> Script {
-        let btc_pubkey = BtcPubKey::new(pubkey);
-        let address = Address::p2pkh(&btc_pubkey, network);
-        address.script_pubkey()
-    }
-    pub fn derive_btc_pubkey(pubkey: PublicKey) -> BtcPubKey {
-        BtcPubKey::new(pubkey)
-    }
-    pub fn derive_btc_address(btc_pubkey: BtcPubKey, network: Network) -> Address {
-        Address::p2pkh(&btc_pubkey, network)
-    }
-    pub fn derive_script(btc_pubkey_hash: BtcPubKeyHash) -> Script {
-        Script::new_p2pkh(&btc_pubkey_hash)
-    }
-}
-fn print_status_change(
-    script: &Script,
-    old: Option<ScriptStatus>,
-    new: ScriptStatus,
-) -> ScriptStatus {
-    match (old, new) {
-        (None, new_status) => {
-            debug!(target: "BTC BRIDGE", "Found relevant script: {:?}, Status: {:?}", script, new_status);
-        }
-        (Some(old_status), new_status) if old_status != new_status => {
-            debug!(target: "BTC BRIDGE", "Script status changed: {:?}, to {} from {}", script, new_status, old_status);
-        }
-        _ => {}
-    }
-
-    new
-}
-fn sync_interval(avg_block_time: Duration) -> Duration {
-    max(avg_block_time / 10, Duration::from_secs(1))
-}
-pub struct Client {
-    electrum: ElectrumClient,
-    subscriptions: Vec<Script>,
-    latest_block_height: BlockHeight,
-    last_sync: Instant,
-    sync_interval: Duration,
-    script_history: BTreeMap<Script, Vec<GetHistoryRes>>,
-}
-impl Client {
-    pub fn new(electrum_url: &str) -> BtcResult<Self> {
-        let config = bdk::electrum_client::ConfigBuilder::default().retry(5).build();
-        let _client = ElectrumClient::from_config(electrum_url, config)?;
-
-        let electrum = ElectrumClient::new(electrum_url)
-            .map_err(|err| darkfi::Error::from(super::BtcFailed::from(err)))?;
-
-        let latest_block = electrum.block_headers_subscribe()?;
-
-        //testnet avg block time
-        let interval = sync_interval(Duration::from_secs(300));
-
-        Ok(Self {
-            electrum,
-            subscriptions: Vec::new(),
-            latest_block_height: BlockHeight::try_from(latest_block)?,
-            last_sync: Instant::now(),
-            sync_interval: interval,
-            script_history: Default::default(),
-        })
-    }
-    fn update_state(&mut self) -> Result<()> {
-        let now = Instant::now();
-        if now < self.last_sync + self.sync_interval {
-            return Ok(())
-        }
-
-        self.last_sync = now;
-        self.update_latest_block()?;
-        self.update_script_histories()?;
-
-        Ok(())
-    }
-    fn update_latest_block(&mut self) -> BtcResult<()> {
-        let latest_block = self.electrum.block_headers_subscribe()?;
-        let latest_block_height = BlockHeight::try_from(latest_block)?;
-
-        if latest_block_height > self.latest_block_height {
-            debug!( target: "BTC BRIDGE", "{} {}",
-                u32::from(latest_block_height),
-                "Got notification for new block"
-            );
-            self.latest_block_height = latest_block_height;
-        }
-
-        Ok(())
-    }
-
-    fn update_script_histories(&mut self) -> BtcResult<()> {
-        let histories = self.electrum.batch_script_get_history(self.script_history.keys())?;
-
-        if histories.len() != self.script_history.len() {
-            debug!(
-                "Expected {} history entries, received {}",
-                self.script_history.len(),
-                histories.len()
-            );
-        }
-
-        let scripts = self.script_history.keys().cloned();
-        let histories = histories.into_iter();
-
-        self.script_history = scripts.zip(histories).collect::<BTreeMap<_, _>>();
-
-        Ok(())
-    }
-
-    pub fn status_of_script(&mut self, script: Script) -> BtcResult<ScriptStatus> {
-        if !self.script_history.contains_key(&script) {
-            self.script_history.insert(script.clone(), vec![]);
-        }
-        self.update_state()?;
-
-        let history = self.script_history.entry(script).or_default();
-
-        match history.as_slice() {
-            [] => Ok(ScriptStatus::Unseen),
-            [_remaining @ .., last] => {
-                if last.height <= 0 {
-                    Ok(ScriptStatus::InMempool)
-                } else {
-                    Ok(ScriptStatus::Confirmed(Confirmed::from_inclusion_and_latest_block(
-                        last.height as u32,
-                        u32::from(self.latest_block_height),
-                    )))
-                }
-            }
-        }
-    }
-}
-pub struct BtcClient {
-    main_account: Account,
-    client: Arc<Mutex<Client>>,
-    notify_channel:
-        (async_channel::Sender<TokenNotification>, async_channel::Receiver<TokenNotification>),
-    network: Network,
-}
-impl BtcClient {
-    pub async fn new(
-        cashier_wallet: Arc<CashierDb>,
-        network: &str,
-        keypair_path: &str,
-    ) -> Result<Arc<Self>> {
-        let main_keypair: Keypair;
-
-        let main_keypairs = cashier_wallet.get_main_keys(&NetworkName::Bitcoin).await?;
-
-        if keypair_path.is_empty() {
-            if main_keypairs.is_empty() {
-                main_keypair = Keypair::new();
-                cashier_wallet
-                    .put_main_keys(
-                        &TokenKey {
-                            secret_key: serialize(&main_keypair),
-                            public_key: serialize(&SecPublicKey(main_keypair.pubkey())),
-                        },
-                        &NetworkName::Bitcoin,
-                    )
-                    .await?;
-            } else {
-                main_keypair = deserialize(&main_keypairs[main_keypairs.len() - 1].secret_key)?;
-            }
-        } else {
-            let keypair_str = load_keypair_to_str(expand_path(keypair_path)?)?;
-            let keypair_bytes: Vec<u8> = serde_json::from_str(&keypair_str)?;
-            main_keypair = Keypair::from_bytes(&keypair_bytes)
-                .map_err(|e| BtcFailed::DecodeAndEncodeError(e.to_string()))?;
-        }
-
-        let notify_channel = async_channel::unbounded();
-
-        let (network, url) = match network {
-            "mainnet" => (Network::Bitcoin, "ssl://electrum.blockstream.info:50002"),
-            "testnet" => (Network::Testnet, "ssl://electrum.blockstream.info:60002"),
-            _ => return Err(Error::UnsupportedCoinNetwork),
-        };
-
-        let main_account = Account::new(&main_keypair, network);
-
-        info!(target: "BTC BRIDGE", "Main BTC Address: {}", main_account.address.to_string());
-
-        Ok(Arc::new(Self {
-            main_account,
-            client: Arc::new(Mutex::new(Client::new(url)?)),
-            notify_channel,
-            network,
-        }))
-    }
-
-    async fn handle_subscribe_request(
-        self: Arc<Self>,
-        btc_keys: Account,
-        drk_pub_key: DrkPublicKey,
-    ) -> BtcResult<()> {
-        let client = self.client.clone();
-
-        let keys_clone = btc_keys.clone();
-        let script = keys_clone.script_pubkey;
-
-        if client.lock().await.subscriptions.contains(&script) {
-            return Ok(())
-        } else {
-            client.lock().await.subscriptions.push(script.clone());
-        }
-        //Fetch any current balance
-        let prev_balance = client.lock().await.electrum.script_get_balance(&script)?;
-        let mut last_status = None;
-
-        loop {
-            async_std::task::sleep(Duration::from_secs(5)).await;
-            let new_status = match client.lock().await.status_of_script(script.clone()) {
-                Ok(new_status) => new_status,
-                Err(error) => {
-                    debug!(target: "BTC BRIDGE", "Failed to get status of script: {:#}", error);
-                    return Err(BtcFailed::BtcError("Failed to get status of script".to_string()))
-                }
-            };
-
-            last_status = Some(print_status_change(&script, last_status, new_status));
-
-            match new_status {
-                ScriptStatus::Unseen => continue,
-                ScriptStatus::InMempool => break,
-                ScriptStatus::Confirmed(inner) => {
-                    //Only break when confirmations happen
-                    let confirmations = inner.confirmations();
-                    if confirmations > 1 {
-                        break
-                    }
-                }
-            }
-        }
-
-        let index = &mut client.lock().await.subscriptions.iter().position(|p| p == &script);
-
-        if let Some(ind) = index {
-            trace!(target: "BTC BRIDGE", "Removing subscription from list");
-            let _ = &mut client.lock().await.subscriptions.remove(*ind);
-        }
-
-        let cur_balance: GetBalanceRes =
-            client.lock().await.electrum.script_get_balance(&script)?;
-
-        let send_notification = self.notify_channel.0.clone();
-        //FIXME: dev
-        if cur_balance.unconfirmed < prev_balance.unconfirmed {
-            return Err(BtcFailed::Notification("New balance is less than previous balance".into()))
-        }
-        //Just check unconfirmed for now
-        let amnt = cur_balance.confirmed - prev_balance.confirmed;
-        let ui_amnt = amnt;
-        send_notification
-            .send(TokenNotification {
-                network: NetworkName::Bitcoin,
-                // is btc an acceptable token name?
-                token_id: generate_id2(
-                    "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa",
-                    &NetworkName::Bitcoin,
-                )?,
-                drk_pub_key,
-                received_balance: amnt as u64,
-                decimals: 8,
-            })
-            .await
-            .map_err(Error::from)?;
-
-        info!(target: "BTC BRIDGE", "Received {} btc", ui_amnt);
-        let _ = self.send_btc_to_main_wallet(amnt as u64, btc_keys).await;
-
-        Ok(())
-    }
-
-    async fn send_btc_to_main_wallet(
-        self: Arc<Self>,
-        amount: u64,
-        btc_keys: Account,
-    ) -> BtcResult<()> {
-        info!(target: "BTC BRIDGE", "Sending {} BTC to main wallet", amount);
-        let client = self.client.lock().await;
-        let electrum = &client.electrum;
-        let keys_clone = btc_keys.clone();
-        let script = keys_clone.script_pubkey;
-        let utxo = electrum.script_list_unspent(&script)?;
-
-        let mut inputs = Vec::new();
-        let mut amounts: u64 = 0;
-        for tx in utxo {
-            let tx_in = TxIn {
-                previous_output: OutPoint { txid: tx.tx_hash, vout: tx.tx_pos as u32 },
-                sequence: 0xffffffff,
-                witness: Vec::new(),
-                script_sig: Script::new(),
-            };
-            inputs.push(tx_in);
-            amounts += tx.value;
-        }
-        let main_script_pubkey = self.main_account.script_pubkey.clone();
-
-        //TODO: Change to PSBT
-        let transaction = Transaction {
-            input: inputs.clone(),
-            output: vec![TxOut { script_pubkey: main_script_pubkey.clone(), value: amounts }],
-            lock_time: 0,
-            version: 2,
-        };
-
-        let tx_size = transaction.get_size();
-
-        let fee_per_kb = electrum.estimate_fee(1)?;
-        let _fee = tx_size as f64 * fee_per_kb * 100000_f64;
-
-        let transaction = Transaction {
-            input: inputs,
-            output: vec![TxOut {
-                script_pubkey: main_script_pubkey,
-                // TODO: calculate fee properly above
-                value: amounts - 400,
-            }],
-            lock_time: 0,
-            version: 2,
-        };
-
-        let _txid = transaction.txid();
-
-        let signed_tx = sign_transaction(
-            transaction,
-            script,
-            btc_keys.keypair.secret,
-            btc_keys.btc_pubkey,
-            &btc_keys.keypair.context,
-        )?;
-
-        let _txid = signed_tx.txid();
-        let signed_tx = BtcTransaction(signed_tx);
-        let _serialized_tx = serialize(&signed_tx);
-
-        info!(target: "BTC BRIDGE", "Signed tx: {:?}",
-            serialize_hex(&signed_tx.0));
-
-        let txid = electrum.transaction_broadcast_raw(&signed_tx.0.serialize().to_vec())?;
-
-        info!(target: "BTC BRIDGE", "Sent {} satoshi to main wallet, txid: {}", amount, txid);
-        Ok(())
-    }
-}
-
-#[async_trait]
-impl NetworkClient for BtcClient {
-    async fn subscribe(
-        self: Arc<Self>,
-        drk_pub_key: DrkPublicKey,
-        _mint: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<TokenSubscribtion> {
-        // Generate bitcoin keys
-        let keypair = Keypair::new();
-        let btc_keys = Account::new(&keypair, self.network);
-        let private_key = serialize(&keypair);
-        let public_key = btc_keys.address.to_string();
-
-        // start scheduler for checking balance
-        trace!(target: "BRIDGE BITCOIN", "Subscribing for deposit");
-
-        executor
-            .spawn(async move {
-                let result = self.handle_subscribe_request(btc_keys, drk_pub_key).await;
-                if let Err(e) = result {
-                    error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
-                }
-            })
-            .detach();
-
-        Ok(TokenSubscribtion { private_key, public_key })
-    }
-
-    async fn subscribe_with_keypair(
-        self: Arc<Self>,
-        private_key: Vec<u8>,
-        _public_key: Vec<u8>,
-        drk_pub_key: DrkPublicKey,
-        _mint: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<String> {
-        let keypair: Keypair = deserialize(&private_key)?;
-        let btc_keys = Account::new(&keypair, self.network);
-        let public_key = btc_keys.address.to_string();
-
-        executor
-            .spawn(async move {
-                let result = self.handle_subscribe_request(btc_keys, drk_pub_key).await;
-                if let Err(e) = result {
-                    error!(target: "BTC BRIDGE SUBSCRIPTION","{}", e.to_string());
-                }
-            })
-            .detach();
-
-        Ok(public_key)
-    }
-
-    async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>> {
-        Ok(self.notify_channel.1.clone())
-    }
-
-    async fn send(
-        self: Arc<Self>,
-        address: Vec<u8>,
-        _mint: Option<String>,
-        amount: u64,
-    ) -> Result<()> {
-        // address is not a btc address, so derive the btc address
-        let electrum = &self.client.lock().await.electrum;
-        let public_key = deserialize::<SecPublicKey>(&address)?.0;
-        let script_pubkey = Account::derive_btc_script_pubkey(public_key, self.network);
-
-        let main_script_pubkey = &self.main_account.script_pubkey;
-
-        let main_utxo = electrum
-            .script_list_unspent(main_script_pubkey)
-            .map_err(|e| Error::from(BtcFailed::from(e)))?;
-
-        let transaction = Transaction {
-            input: vec![TxIn {
-                previous_output: OutPoint {
-                    txid: main_utxo[0].tx_hash,
-                    vout: main_utxo[0].tx_pos as u32,
-                },
-                sequence: 0xffffffff,
-                witness: Vec::new(),
-                script_sig: Script::new(),
-            }],
-            output: vec![TxOut {
-                script_pubkey: script_pubkey.clone(),
-                // TODO: Calculate fees
-                value: amount - 300,
-            }],
-            lock_time: 0,
-            version: 2,
-        };
-
-        let signed_tx = sign_transaction(
-            transaction,
-            script_pubkey,
-            self.main_account.keypair.secret,
-            self.main_account.btc_pubkey,
-            &self.main_account.keypair.context,
-        )?;
-
-        let txid = electrum
-            .transaction_broadcast_raw(&signed_tx.serialize().to_vec())
-            .map_err(|e| Error::from(BtcFailed::from(e)))?;
-
-        info!(target: "BTC BRIDGE", "Sent {} satoshi to external wallet, txid: {}", amount, txid);
-        Ok(())
-    }
-}
-
-pub fn sign_transaction(
-    tx: Transaction,
-    script_pubkey: Script,
-    priv_key: SecretKey,
-    pub_key: BtcPubKey,
-    curve: &Secp256k1<All>,
-) -> BtcResult<Transaction> {
-    let mut signed_inputs: Vec<TxIn> = Vec::new();
-
-    for (i, unsigned_input) in tx.input.iter().enumerate() {
-        let sighash = tx.signature_hash(i, &script_pubkey, SigHashType::All as u32);
-
-        let msg = BtcMessage::from_slice(sighash.as_ref())?;
-
-        let signature = curve.sign(&msg, &priv_key);
-        let byte_signature = &signature.serialize_der();
-        let mut with_hashtype = byte_signature.to_vec();
-        with_hashtype.push(SigHashType::All as u8);
-
-        let redeem_script =
-            Builder::new().push_slice(with_hashtype.as_slice()).push_key(&pub_key).into_script();
-        signed_inputs.push(TxIn {
-            previous_output: unsigned_input.previous_output,
-            script_sig: redeem_script,
-            sequence: unsigned_input.sequence,
-            witness: unsigned_input.witness.clone(),
-        });
-    }
-
-    Ok(Transaction {
-        version: tx.version,
-        lock_time: tx.lock_time,
-        input: signed_inputs,
-        output: tx.output,
-    })
-}
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub enum ScriptStatus {
-    Unseen,
-    InMempool,
-    Confirmed(Confirmed),
-}
-
-impl ScriptStatus {
-    pub fn from_confirmations(confirmations: u32) -> Self {
-        match confirmations {
-            0 => Self::InMempool,
-            confirmations => Self::Confirmed(Confirmed::new(confirmations - 1)),
-        }
-    }
-}
-
-#[derive(Debug, Copy, Clone, PartialEq)]
-pub struct Confirmed {
-    depth: u32,
-}
-
-impl Confirmed {
-    pub fn new(depth: u32) -> Self {
-        Self { depth }
-    }
-    pub fn from_inclusion_and_latest_block(inclusion_height: u32, latest_block: u32) -> Self {
-        let depth = latest_block.saturating_sub(inclusion_height);
-
-        Self { depth }
-    }
-
-    pub fn confirmations(&self) -> u32 {
-        self.depth + 1
-    }
-
-    pub fn meets_target<T>(&self, target: T) -> bool
-    where
-        u32: PartialOrd<T>,
-    {
-        self.confirmations() >= target
-    }
-}
-
-impl ScriptStatus {
-    // Check if the script has any confirmations.
-    pub fn is_confirmed(&self) -> bool {
-        matches!(self, ScriptStatus::Confirmed(_))
-    }
-    // Check if the script has met the given confirmation target.
-    pub fn is_confirmed_with<T>(&self, target: T) -> bool
-    where
-        u32: PartialOrd<T>,
-    {
-        match self {
-            ScriptStatus::Confirmed(inner) => inner.meets_target(target),
-            _ => false,
-        }
-    }
-
-    pub fn has_been_seen(&self) -> bool {
-        matches!(self, ScriptStatus::InMempool | ScriptStatus::Confirmed(_))
-    }
-}
-
-impl fmt::Display for ScriptStatus {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            ScriptStatus::Unseen => write!(f, "unseen"),
-            ScriptStatus::InMempool => write!(f, "in mempool"),
-            ScriptStatus::Confirmed(inner) => {
-                write!(f, "confirmed with {} blocks", inner.confirmations())
-            }
-        }
-    }
-}
-
-// Aliases
-pub struct BtcTransaction(bitcoin::Transaction);
-pub struct BtcAddress(bitcoin::Address);
-pub struct BtcPublicKey(bitcoin::PublicKey);
-pub struct BtcPrivateKey(bitcoin::PrivateKey);
-pub struct SecPublicKey(secp256k1::PublicKey);
-
-impl Encodable for BtcTransaction {
-    fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
-        let tx = self.0.serialize();
-        let len = tx.encode(s)?;
-        Ok(len)
-    }
-}
-impl Encodable for BtcAddress {
-    fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
-        let addr = self.0.to_string();
-        let len = addr.encode(s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for BtcAddress {
-    fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
-        let addr: String = Decodable::decode(&mut d)?;
-        let addr = bitcoin::Address::from_str(&addr)
-            .map_err(|err| darkfi::Error::from(BtcFailed::from(err)))?;
-        Ok(BtcAddress(addr))
-    }
-}
-
-impl Encodable for BtcPublicKey {
-    fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
-        let key = self.0.to_bytes();
-        let len = key.encode(s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for BtcPublicKey {
-    fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
-        let key: Vec<u8> = Decodable::decode(&mut d)?;
-        let key = bitcoin::PublicKey::from_slice(&key)
-            .map_err(|err| darkfi::Error::from(BtcFailed::from(err)))?;
-        Ok(BtcPublicKey(key))
-    }
-}
-
-impl Encodable for BtcPrivateKey {
-    fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
-        let key: String = self.0.to_string();
-        let len = key.encode(s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for BtcPrivateKey {
-    fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
-        let key: String = Decodable::decode(&mut d)?;
-        let key = bitcoin::PrivateKey::from_str(&key)
-            .map_err(|err| darkfi::Error::from(BtcFailed::from(err)))?;
-        Ok(BtcPrivateKey(key))
-    }
-}
-impl Encodable for SecPublicKey {
-    fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
-        let key: Vec<u8> = self.0.serialize().to_vec();
-        let len = key.encode(s)?;
-        Ok(len)
-    }
-}
-impl Decodable for SecPublicKey {
-    fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
-        let key: Vec<u8> = Decodable::decode(&mut d)?;
-        let key = secp256k1::PublicKey::from_slice(&key)
-            .map_err(|err| darkfi::Error::from(BtcFailed::from(err)))?;
-        Ok(SecPublicKey(key))
-    }
-}
-// TODO: add secret + public keys together for Encodable
-impl Encodable for Keypair {
-    fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
-        let key: Vec<u8> = self.to_bytes().to_vec();
-        let len = key.encode(s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for Keypair {
-    fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
-        let key: Vec<u8> = Decodable::decode(&mut d)?;
-        let key = Keypair::from_bytes(key.as_slice()).map_err(|_| {
-            darkfi::Error::from(BtcFailed::DecodeAndEncodeError("load keypair from slice".into()))
-        })?;
-        Ok(key)
-    }
-}
-
-#[derive(Debug, Clone, thiserror::Error)]
-pub enum BtcFailed {
-    #[error("There is no enough value {0}")]
-    NotEnoughValue(u64),
-    #[error("could not parse BTC address: {0}")]
-    BadBtcAddress(String),
-    #[error("Unable to create Electrum Client: {0}")]
-    ElectrumError(String),
-    #[error("BtcFailed: {0}")]
-    BtcError(String),
-    #[error("Decode and decode keys error: {0}")]
-    DecodeAndEncodeError(String),
-    #[error("Keypair error from Secp256k1:  {0}")]
-    KeypairError(String),
-    #[error("Received Notification Error: {0}")]
-    Notification(String),
-}
-
-impl From<darkfi::error::Error> for BtcFailed {
-    fn from(err: darkfi::error::Error) -> BtcFailed {
-        BtcFailed::BtcError(err.to_string())
-    }
-}
-impl From<secp256k1::Error> for BtcFailed {
-    fn from(err: secp256k1::Error) -> BtcFailed {
-        BtcFailed::KeypairError(err.to_string())
-    }
-}
-impl From<bitcoin::util::address::Error> for BtcFailed {
-    fn from(err: bitcoin::util::address::Error) -> BtcFailed {
-        BtcFailed::BadBtcAddress(err.to_string())
-    }
-}
-impl From<bdk::electrum_client::Error> for BtcFailed {
-    fn from(err: bdk::electrum_client::Error) -> BtcFailed {
-        BtcFailed::ElectrumError(err.to_string())
-    }
-}
-
-impl From<bitcoin::util::key::Error> for BtcFailed {
-    fn from(err: bitcoin::util::key::Error) -> BtcFailed {
-        BtcFailed::DecodeAndEncodeError(err.to_string())
-    }
-}
-impl From<anyhow::Error> for BtcFailed {
-    fn from(err: anyhow::Error) -> BtcFailed {
-        BtcFailed::DecodeAndEncodeError(err.to_string())
-    }
-}
-
-impl From<BtcFailed> for Error {
-    fn from(error: BtcFailed) -> Self {
-        Error::CashierError(error.to_string())
-    }
-}
-
-pub type BtcResult<T> = std::result::Result<T, BtcFailed>;
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use darkfi::util::serial::{deserialize, serialize};
-    use secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE};
-    use std::str::FromStr;
-
-    const KEYPAIR_LENGTH: usize = SECRET_KEY_SIZE + PUBLIC_KEY_SIZE;
-
-    #[test]
-    pub fn test_serialize_btc_address() -> super::BtcResult<()> {
-        let btc_addr =
-            bitcoin::Address::from_str(&String::from("mxVFsFW5N4mu1HPkxPttorvocvzeZ7KZyk"))?;
-
-        let btc_addr = BtcAddress(btc_addr);
-
-        let btc_ser = serialize(&btc_addr);
-        let btc_dser = deserialize::<BtcAddress>(&btc_ser)?.0;
-
-        assert_eq!(btc_addr.0, btc_dser);
-
-        Ok(())
-    }
-
-    #[test]
-    pub fn test_serialize_and_deserialize_keypair() -> super::BtcResult<()> {
-        let keypair = Keypair::new();
-
-        let bytes: [u8; KEYPAIR_LENGTH] = keypair.to_bytes();
-        let keys = Keypair::from_bytes(&bytes)?;
-
-        assert_eq!(keypair, keys);
-
-        Ok(())
-    }
-}

+ 0 - 589
bin/cashierd/src/service/eth.rs

@@ -1,589 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::convert::TryInto;
-
-use async_executor::Executor;
-use async_std::sync::{Arc, Mutex};
-use async_trait::async_trait;
-use hash_db::Hasher;
-use keccak_hasher::KeccakHasher;
-use lazy_static::lazy_static;
-use log::{debug, error, info, trace};
-use num_bigint::{BigUint, RandBigInt};
-use serde::{Deserialize, Serialize};
-use serde_json::{json, Value};
-use url::Url;
-
-use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
-
-use darkfi::{
-    crypto::{keypair::PublicKey, token_id::generate_id2},
-    rpc::{jsonrpc, jsonrpc::JsonResult},
-    util::{
-        parse::truncate,
-        serial::{deserialize, serialize, Decodable, Encodable},
-        sleep, NetworkName,
-    },
-    wallet::cashierdb::{CashierDb, TokenKey},
-    Error, Result,
-};
-
-pub const ETH_NATIVE_TOKEN_ID: &str = "0x0000000000000000000000000000000000000000";
-
-#[derive(Clone, Debug)]
-pub struct Keypair {
-    pub private_key: String,
-    pub public_key: String,
-}
-
-impl Encodable for Keypair {
-    fn encode<S: std::io::Write>(&self, mut s: S) -> Result<usize> {
-        let mut len = 0;
-        len += self.private_key.encode(&mut s)?;
-        len += self.public_key.encode(&mut s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for Keypair {
-    fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
-        Ok(Self { private_key: Decodable::decode(&mut d)?, public_key: Decodable::decode(&mut d)? })
-    }
-}
-
-// An ERC-20 token transfer transaction's data is as follows:
-//
-// 1. The first 4 bytes of the keccak256 hash of "transfer(address,uint256)".
-// 2. The address of the recipient, left-zero-padded to be 32 bytes.
-// 3. The amount to be transferred: amount * 10^decimals
-
-// This is the entire ERC20 ABI
-lazy_static! {
-    static ref ERC20_NAME_METHOD: [u8; 4] = {
-        let method = b"name()";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_APPROVE_METHOD: [u8; 4] = {
-        let method = b"approve(address,uint256)";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_TOTALSUPPLY_METHOD: [u8; 4] = {
-        let method = b"totalSupply()";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_TRANSFERFROM_METHOD: [u8; 4] = {
-        let method = b"transferFrom(address,address,uint256)";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_DECIMALS_METHOD: [u8; 4] = {
-        let method = b"decimals()";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_VERSION_METHOD: [u8; 4] = {
-        let method = b"version()";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_BALANCEOF_METHOD: [u8; 4] = {
-        let method = b"balanceOf(address)";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_SYMBOL_METHOD: [u8; 4] = {
-        let method = b"symbol()";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_TRANSFER_METHOD: [u8; 4] = {
-        let method = b"transfer(address,uint256)";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_APPROVEANDCALL_METHOD: [u8; 4] = {
-        let method = b"approveAndCall(address,uint256,bytes)";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-    static ref ERC20_ALLOWANCE_METHOD: [u8; 4] = {
-        let method = b"allowance(address,address)";
-        KeccakHasher::hash(method)[0..4].try_into().expect("nope")
-    };
-}
-
-pub fn erc20_transfer_data(recipient: &str, amount: BigUint) -> String {
-    let rec = recipient.trim_start_matches("0x");
-    let rec_padded = format!("{:0>64}", rec);
-
-    let amnt_bytes = amount.to_bytes_be();
-    let amnt_hex = hex::encode(amnt_bytes);
-    let amnt_hex_padded = format!("{:0>64}", amnt_hex);
-
-    format!("0x{}{}{}", hex::encode(*ERC20_TRANSFER_METHOD), rec_padded, amnt_hex_padded)
-}
-
-pub fn erc20_balanceof_data(account: &str) -> String {
-    let acc = account.trim_start_matches("0x");
-    let acc_padded = format!("{:0>64}", acc);
-
-    format!("0x{}{}", hex::encode(*ERC20_BALANCEOF_METHOD), acc_padded)
-}
-
-fn to_eth_hex(val: BigUint) -> String {
-    let bytes = val.to_bytes_be();
-    let h = hex::encode(bytes);
-    format!("0x{}", h.trim_start_matches('0'))
-}
-
-/// Generate a 256-bit ETH private key.
-pub fn generate_privkey() -> String {
-    let mut rng = rand::thread_rng();
-    let token = rng.gen_bigint(256);
-    let token_bytes = token.to_bytes_le().1;
-    let key = KeccakHasher::hash(&token_bytes);
-    hex::encode(key)
-}
-
-#[allow(non_snake_case)]
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct EthTx {
-    pub from: String,
-
-    pub to: String,
-
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub gas: Option<String>,
-
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub gasPrice: Option<String>,
-
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub value: Option<String>,
-
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub data: Option<String>,
-
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub nonce: Option<String>,
-}
-
-impl EthTx {
-    pub fn new(
-        from: &str,
-        to: &str,
-        gas: Option<BigUint>,
-        gas_price: Option<BigUint>,
-        value: Option<BigUint>,
-        data: Option<String>,
-        nonce: Option<String>,
-    ) -> Self {
-        let gas_hex = gas.map(to_eth_hex);
-        let gasprice_hex = gas_price.map(to_eth_hex);
-        let value_hex = value.map(to_eth_hex);
-
-        EthTx {
-            from: from.to_string(),
-            to: to.to_string(),
-            gas: gas_hex,
-            gasPrice: gasprice_hex,
-            value: value_hex,
-            data,
-            nonce,
-        }
-    }
-}
-
-// JSON-RPC interface to Geth.
-// https://eth.wiki/json-rpc/API
-// https://geth.ethereum.org/docs/rpc/
-//
-// geth can be started with: $ geth --ropsten --syncmode light
-// It should then show an Unix socket endpoint like so:
-// INFO [10-25|19:47:32.845] IPC endpoint opened: url=/home/x/.ethereum/ropsten/geth.ipc
-//
-pub struct EthClient {
-    pub main_keypair: Keypair,
-    passphrase: String,
-    socket_path: String,
-    subscriptions: Arc<Mutex<Vec<String>>>,
-    notify_channel:
-        (async_channel::Sender<TokenNotification>, async_channel::Receiver<TokenNotification>),
-}
-
-impl EthClient {
-    pub fn new(_network: &str, socket_path: &str, passphrase: &str) -> Self {
-        let notify_channel = async_channel::unbounded();
-
-        let subscriptions = Arc::new(Mutex::new(Vec::new()));
-
-        let main_keypair = Keypair { public_key: "".into(), private_key: "".into() };
-
-        Self {
-            main_keypair,
-            passphrase: passphrase.into(),
-            socket_path: socket_path.into(),
-            subscriptions,
-            notify_channel,
-        }
-    }
-
-    pub async fn setup_keypair(
-        &mut self,
-        cashier_wallet: Arc<CashierDb>,
-        _keypair_path: &str,
-    ) -> Result<()> {
-        let main_keypair: Keypair;
-
-        let main_keypairs = cashier_wallet.get_main_keys(&NetworkName::Ethereum).await?;
-
-        if main_keypairs.is_empty() {
-            let main_private_key = generate_privkey();
-            let main_public_key =
-                self.import_privkey(&main_private_key).await?.as_str().unwrap().to_string();
-
-            cashier_wallet
-                .put_main_keys(
-                    &TokenKey {
-                        secret_key: serialize(&main_private_key),
-                        public_key: serialize(&main_public_key),
-                    },
-                    &NetworkName::Ethereum,
-                )
-                .await?;
-
-            main_keypair = Keypair { private_key: main_private_key, public_key: main_public_key };
-        } else {
-            let last_keypair = &main_keypairs[main_keypairs.len() - 1];
-
-            main_keypair = Keypair {
-                private_key: deserialize(&last_keypair.secret_key)?,
-                public_key: deserialize(&last_keypair.public_key)?,
-            }
-        }
-
-        self.main_keypair = main_keypair;
-
-        Ok(())
-    }
-
-    async fn send_eth_to_main_wallet(&self, acc: &str, amount: BigUint) -> Result<()> {
-        info!(target: "ETH BRIDGE", "Sending eth to main wallet");
-
-        let tx =
-            EthTx::new(acc, &self.main_keypair.public_key, None, None, Some(amount), None, None);
-
-        self.send_transaction(&tx, &self.passphrase).await?;
-
-        Ok(())
-    }
-
-    async fn handle_subscribe_request(
-        self: Arc<Self>,
-        addr: String,
-        drk_pub_key: PublicKey,
-    ) -> Result<()> {
-        if self.subscriptions.lock().await.contains(&addr) {
-            return Ok(())
-        }
-
-        let decimals = 18;
-
-        let prev_balance = self.get_current_balance(&addr, None).await?;
-
-        let mut current_balance;
-
-        let iter_interval = 1;
-        let mut sub_iter = 0;
-
-        loop {
-            if sub_iter > 60 * 10 {
-                // 10 minutes
-                self.unsubscribe(&addr).await;
-                return Err(EthFailed::Custom("Deposit for expired".to_string()).into())
-            }
-
-            sub_iter += iter_interval;
-            sleep(iter_interval).await;
-
-            current_balance = self.get_current_balance(&addr, None).await?;
-
-            if current_balance != prev_balance {
-                break
-            }
-        }
-
-        let send_notification = self.notify_channel.0.clone();
-
-        self.unsubscribe(&addr).await;
-
-        if current_balance < prev_balance {
-            return Err(
-                EthFailed::Custom("New balance is less than previous balance".to_string()).into()
-            )
-        }
-
-        let received_balance = current_balance - prev_balance;
-
-        let received_balance_ui = received_balance.clone() / u64::pow(10, decimals as u32);
-
-        send_notification
-            .send(TokenNotification {
-                network: NetworkName::Ethereum,
-                token_id: generate_id2(ETH_NATIVE_TOKEN_ID, &NetworkName::Ethereum)?,
-                drk_pub_key,
-                // TODO FIX
-                received_balance: received_balance.to_u64_digits()[0],
-                decimals: decimals as u16,
-            })
-            .await
-            .map_err(Error::from)?;
-
-        self.send_eth_to_main_wallet(&addr, received_balance).await?;
-
-        info!(target: "ETH BRIDGE", "Received {} eth", received_balance_ui );
-
-        Ok(())
-    }
-
-    async fn unsubscribe(&self, pubkey: &str) {
-        let mut subscriptions = self.subscriptions.lock().await;
-        let index = subscriptions.iter().position(|p| p == pubkey);
-        if let Some(ind) = index {
-            trace!(target: "ETH BRIDGE", "Removing subscription from list");
-            subscriptions.remove(ind);
-        }
-    }
-
-    async fn request(&self, r: jsonrpc::JsonRequest) -> EthResult<Value> {
-        debug!(target: "ETH RPC", "--> {}", serde_json::to_string(&r)?);
-        let url = Url::parse(&format!("unix://{}", self.socket_path)).map_err(Error::from)?;
-        let reply: JsonResult =
-            match jsonrpc::send_request(&url, json!(r), None).await.map_err(EthFailed::from) {
-                Ok(v) => v,
-                Err(e) => return Err(e),
-            };
-
-        match reply {
-            JsonResult::Resp(r) => {
-                debug!(target: "ETH RPC", "<-- {}", serde_json::to_string(&r)?);
-                Ok(r.result)
-            }
-
-            JsonResult::Err(e) => {
-                debug!(target: "ETH RPC", "<-- {}", serde_json::to_string(&e)?);
-                Err(EthFailed::RpcError(e.error.message.to_string()))
-            }
-
-            JsonResult::Notif(n) => {
-                debug!(target: "ETH RPC", "<-- {}", serde_json::to_string(&n)?);
-                Err(EthFailed::RpcError("Unexpected reply".to_string()))
-            }
-        }
-    }
-
-    pub async fn import_privkey(&self, key: &str) -> EthResult<Value> {
-        let req = jsonrpc::request(json!("personal_importRawKey"), json!([key, self.passphrase]));
-        Ok(self.request(req).await?)
-    }
-
-    /*
-    pub async fn estimate_gas(&self, tx: &EthTx) -> Result<Value> {
-    let req = jsonrpc::request(json!("eth_estimateGas"), json!([tx]));
-    Ok(self.request(req).await?)
-    }
-    */
-
-    pub async fn block_number(&self) -> EthResult<Value> {
-        let req = jsonrpc::request(json!("eth_blockNumber"), json!([]));
-        Ok(self.request(req).await?)
-    }
-
-    pub async fn get_eth_balance(&self, acc: &str, block: &str) -> EthResult<Value> {
-        let req = jsonrpc::request(json!("eth_getBalance"), json!([acc, block]));
-        Ok(self.request(req).await?)
-    }
-
-    pub async fn get_erc20_balance(&self, acc: &str, mint: &str) -> EthResult<Value> {
-        let tx = EthTx::new(acc, mint, None, None, None, Some(erc20_balanceof_data(acc)), None);
-        let req = jsonrpc::request(json!("eth_call"), json!([tx, "latest"]));
-        Ok(self.request(req).await?)
-    }
-
-    pub async fn get_current_balance(&self, acc: &str, _mint: Option<&str>) -> EthResult<BigUint> {
-        // Latest known block, used to calculate present balance.
-        let block = self.block_number().await?;
-        let block = block.as_str().unwrap();
-
-        // Native ETH balance
-        let hexbalance = self.get_eth_balance(acc, block).await?;
-        let hexbalance = hexbalance.as_str().unwrap().trim_start_matches("0x");
-        let balance = BigUint::parse_bytes(hexbalance.as_bytes(), 16).unwrap();
-
-        Ok(balance)
-    }
-
-    pub async fn send_transaction(&self, tx: &EthTx, passphrase: &str) -> EthResult<Value> {
-        let req = jsonrpc::request(json!("personal_sendTransaction"), json!([tx, passphrase]));
-        Ok(self.request(req).await?)
-    }
-}
-
-#[async_trait]
-impl NetworkClient for EthClient {
-    async fn subscribe(
-        self: Arc<Self>,
-        drk_pub_key: PublicKey,
-        _mint_address: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<TokenSubscribtion> {
-        let private_key = generate_privkey();
-
-        let addr = self.import_privkey(&private_key).await?;
-
-        let address: String = if addr.as_str().is_some() {
-            addr.as_str().unwrap().to_string()
-        } else {
-            return Err(Error::from(EthFailed::ImportPrivateError))
-        };
-
-        let addr_cloned = address.clone();
-        executor
-            .spawn(async move {
-                let result = self.handle_subscribe_request(addr_cloned, drk_pub_key).await;
-                if let Err(e) = result {
-                    error!(target: "ETH BRIDGE SUBSCRIPTION","{}", e.to_string());
-                }
-            })
-            .detach();
-
-        let private_key: Vec<u8> = serialize(&private_key);
-
-        Ok(TokenSubscribtion { private_key, public_key: address })
-    }
-
-    async fn subscribe_with_keypair(
-        self: Arc<Self>,
-        _private_key: Vec<u8>,
-        public_key: Vec<u8>,
-        drk_pub_key: PublicKey,
-        _mint_address: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<String> {
-        let public_key: String = deserialize(&public_key)?;
-
-        let address = public_key.clone();
-        executor
-            .spawn(async move {
-                let result = self.handle_subscribe_request(address, drk_pub_key).await;
-                if let Err(e) = result {
-                    error!(target: "ETH BRIDGE SUBSCRIPTION","{}", e.to_string());
-                }
-            })
-            .detach();
-
-        Ok(public_key)
-    }
-
-    async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>> {
-        Ok(self.notify_channel.1.clone())
-    }
-
-    async fn send(
-        self: Arc<Self>,
-        address: Vec<u8>,
-        _mint: Option<String>,
-        amount: u64,
-    ) -> Result<()> {
-        // Recipient address
-        let dest: String = deserialize(&address)?;
-
-        let decimals = 18;
-
-        // reverse truncate
-        let amount = truncate(amount, decimals as u16, 8)?;
-
-        let tx = EthTx::new(
-            &self.main_keypair.public_key,
-            &dest,
-            None,
-            None,
-            Some(BigUint::from(amount)),
-            None,
-            None,
-        );
-
-        self.send_transaction(&tx, &self.passphrase).await?;
-
-        Ok(())
-    }
-}
-
-#[derive(Debug, Clone, thiserror::Error)]
-pub enum EthFailed {
-    #[error("There is no enough value {0}")]
-    NotEnoughValue(u64),
-    #[error("Main Account Has no enough value")]
-    MainAccountNotEnoughValue,
-    #[error("Bad Eth Address: {0}")]
-    BadEthAddress(String),
-    #[error("Decode and decode keys error: {0}")]
-    DecodeAndEncodeError(String),
-    #[error("Rpc Error: {0}")]
-    RpcError(String),
-    #[error("Eth client error: {0}")]
-    EthClientError(String),
-    #[error("Given mint is not valid: {0}")]
-    MintIsNotValid(String),
-    #[error("JsonError: {0}")]
-    JsonError(String),
-    #[error("Parse Error: {0}")]
-    ParseError(String),
-    #[error("Unable to derive address from private key")]
-    ImportPrivateError,
-    #[error("{0}")]
-    Custom(String),
-}
-
-impl From<darkfi::Error> for EthFailed {
-    fn from(err: darkfi::Error) -> EthFailed {
-        EthFailed::EthClientError(err.to_string())
-    }
-}
-impl From<serde_json::Error> for EthFailed {
-    fn from(err: serde_json::Error) -> EthFailed {
-        EthFailed::JsonError(err.to_string())
-    }
-}
-
-impl From<EthFailed> for Error {
-    fn from(error: EthFailed) -> Self {
-        Error::CashierError(error.to_string())
-    }
-}
-
-pub type EthResult<T> = std::result::Result<T, EthFailed>;
-
-#[allow(unused_imports)]
-mod tests {
-    use super::*;
-    use num_bigint::ToBigUint;
-    use std::str::FromStr;
-
-    #[test]
-    fn test_erc20_transfer_data() {
-        let recipient = "0x5b7b3b499fb69c40c365343cb0dc842fe8c23887";
-        let amnt = BigUint::from_str("34765403556934000640").unwrap();
-
-        assert_eq!(erc20_transfer_data(recipient, amnt), "0xa9059cbb0000000000000000000000005b7b3b499fb69c40c365343cb0dc842fe8c23887000000000000000000000000000000000000000000000001e27786570c272000");
-    }
-}

+ 0 - 34
bin/cashierd/src/service/mod.rs

@@ -1,34 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-pub mod bridge;
-
-#[cfg(feature = "btc")]
-pub mod btc;
-#[cfg(feature = "btc")]
-pub use btc::{Account, BtcFailed, BtcResult, Keypair, PubAddress};
-
-#[cfg(feature = "sol")]
-pub mod sol;
-#[cfg(feature = "sol")]
-pub use sol::{SolClient, SolFailed, SolResult};
-
-#[cfg(feature = "eth")]
-pub mod eth;
-#[cfg(feature = "eth")]
-pub use eth::{EthClient, EthFailed, EthResult};

+ 0 - 678
bin/cashierd/src/service/sol.rs

@@ -1,678 +0,0 @@
-/* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2023 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-use std::str::FromStr;
-
-use async_executor::Executor;
-use async_native_tls::TlsConnector;
-use async_std::sync::{Arc, Mutex};
-use async_trait::async_trait;
-use futures::{SinkExt, StreamExt};
-use log::{debug, error, info, trace, warn};
-use serde::Serialize;
-use serde_json::{json, Value};
-use solana_client::{blockhash_query::BlockhashQuery, rpc_client::RpcClient};
-use solana_sdk::{
-    native_token::{lamports_to_sol, sol_to_lamports},
-    program_pack::Pack,
-    pubkey::Pubkey,
-    signature::{Signature, Signer},
-    signer::keypair::Keypair,
-    system_instruction,
-    transaction::Transaction,
-};
-use spl_associated_token_account::{create_associated_token_account, get_associated_token_address};
-use tungstenite::Message;
-
-use super::bridge::{NetworkClient, TokenNotification, TokenSubscribtion};
-
-use darkfi::{
-    crypto::{keypair::PublicKey, token_id::generate_id2},
-    rpc::{jsonrpc, jsonrpc::JsonResult, websockets, websockets::WsStream},
-    util::{
-        expand_path, load_keypair_to_str,
-        parse::truncate,
-        serial::{deserialize, serialize, Decodable, Encodable},
-        sleep, NetworkName,
-    },
-    wallet::cashierdb::{CashierDb, TokenKey},
-    Error, Result,
-};
-
-pub const SOL_NATIVE_TOKEN_ID: &str = "So11111111111111111111111111111111111111112";
-
-struct SolKeypair(Keypair);
-struct SolPubkey(Pubkey);
-
-#[derive(Serialize)]
-struct SubscribeParams {
-    encoding: Value,
-    commitment: Value,
-}
-
-pub struct SolClient {
-    main_keypair: Keypair,
-    // Subscriptions vector of pubkey
-    subscriptions: Arc<Mutex<Vec<Pubkey>>>,
-    notify_channel:
-        (async_channel::Sender<TokenNotification>, async_channel::Receiver<TokenNotification>),
-    rpc_server: &'static str,
-    wss_server: &'static str,
-}
-
-impl SolClient {
-    pub async fn new(
-        cashier_wallet: Arc<CashierDb>,
-        network: &str,
-        keypair_path: &str,
-    ) -> Result<Arc<Self>> {
-        let notify_channel = async_channel::unbounded();
-
-        let main_keypair: SolKeypair;
-
-        let main_keypairs = cashier_wallet.get_main_keys(&NetworkName::Solana).await?;
-
-        if keypair_path.is_empty() {
-            if main_keypairs.is_empty() {
-                main_keypair = SolKeypair(Keypair::new());
-                cashier_wallet
-                    .put_main_keys(
-                        &TokenKey {
-                            secret_key: serialize(&main_keypair),
-                            public_key: serialize(&SolPubkey(main_keypair.0.pubkey())),
-                        },
-                        &NetworkName::Solana,
-                    )
-                    .await?;
-            } else {
-                main_keypair =
-                    deserialize::<SolKeypair>(&main_keypairs[main_keypairs.len() - 1].secret_key)?;
-            }
-        } else {
-            let keypair_str = load_keypair_to_str(expand_path(keypair_path)?)?;
-
-            let keypair_bytes: Vec<u8> = serde_json::from_str(&keypair_str)?;
-            main_keypair = SolKeypair(
-                Keypair::from_bytes(&keypair_bytes)
-                    .map_err(|e| SolFailed::Signature(e.to_string()))?,
-            );
-        }
-
-        info!(target: "SOL BRIDGE", "Main SOL wallet pubkey: {:?}", &main_keypair.0.pubkey());
-
-        let (rpc_server, wss_server) = match network {
-            "mainnet" => ("https://api.mainnet-beta.solana.com", "wss://api.devnet.solana.com"),
-            "devnet" => ("https://api.devnet.solana.com", "wss://api.devnet.solana.com"),
-            "testnet" => ("https://api.testnet.solana.com", "wss://api.testnet.solana.com"),
-            "localhost" => ("http://localhost:8899", "ws://localhost:8900"),
-            _ => return Err(Error::UnsupportedCoinNetwork),
-        };
-
-        Ok(Arc::new(Self {
-            main_keypair: main_keypair.0,
-            subscriptions: Arc::new(Mutex::new(Vec::new())),
-            notify_channel,
-            rpc_server,
-            wss_server,
-        }))
-    }
-
-    fn check_main_account_balance(&self, rpc: &RpcClient) -> SolResult<bool> {
-        let main_sol_balance =
-            rpc.get_balance(&self.main_keypair.pubkey()).map_err(SolFailed::from)?;
-
-        // 0.0001 is the maximum that could happen
-        let lamports_per_signature = sol_to_lamports(0.0001);
-        let required_funds = lamports_per_signature * 3;
-
-        Ok(main_sol_balance > required_funds)
-    }
-
-    async fn handle_subscribe_request(
-        self: Arc<Self>,
-        keypair: Keypair,
-        drk_pub_key: PublicKey,
-        mint: Option<Pubkey>,
-    ) -> SolResult<()> {
-        trace!(target: "SOL BRIDGE", "handle_subscribe_request()");
-
-        // Derive token pubkey if mint was provided.
-        let pubkey = if mint.is_some() {
-            get_associated_token_address(&keypair.pubkey(), &mint.unwrap())
-        } else {
-            keypair.pubkey()
-        };
-
-        if mint.is_some() {
-            debug!(target: "SOL BRIDGE", "Got subscribe request for SPL token");
-            debug!(target: "SOL BRIDGE", "Main wallet: {}", keypair.pubkey());
-            debug!(target: "SOL BRIDGE", "Associated token address: {}", pubkey);
-        } else {
-            debug!(target: "SOL BRIDGE", "Got subscribe request for native SOL");
-            debug!(target: "SOL BRIDGE", "Main wallet: {}", keypair.pubkey());
-        }
-
-        // Check if we're already subscribed
-        if self.subscriptions.lock().await.contains(&pubkey) {
-            return Ok(())
-        }
-
-        let rpc = RpcClient::new(self.rpc_server.to_string());
-
-        // Fetch the current balance.
-        let (prev_balance, decimals) = if mint.is_none() {
-            (rpc.get_balance(&pubkey).map_err(SolFailed::from)?, 9)
-        } else {
-            let mint = mint.unwrap();
-            match get_account_token_balance(&rpc, &pubkey, &mint) {
-                Ok(v) => v,
-                Err(_) => {
-                    let (exists, decimals) = account_is_initialized_mint(&rpc, &mint);
-                    if !exists {
-                        debug!("Could not figure out the number of decimals in SPL token");
-                        return Err(SolFailed::MintIsNotValid(mint.to_string()))
-                    }
-                    (0, decimals)
-                }
-            }
-        };
-
-        // WebSocket connection
-        let builder = native_tls::TlsConnector::builder();
-        let tls = TlsConnector::from(builder);
-        let (stream, _) = websockets::connect(self.wss_server, tls).await?;
-        let (mut write, mut read) = stream.split();
-
-        // Subscription request build
-        let sub_params =
-            SubscribeParams { encoding: json!("jsonParsed"), commitment: json!("finalized") };
-
-        let subscription = jsonrpc::request(
-            json!("accountSubscribe"),
-            json!([json!(pubkey.to_string()), json!(sub_params)]),
-        );
-
-        debug!(target: "SOLANA RPC", "--> {}", serde_json::to_string(&subscription)?);
-        write.send(Message::text(serde_json::to_string(&subscription)?)).await?;
-
-        // Subscription ID used for unsubscribing later.
-        let mut sub_id: i64 = 0;
-
-        // The balance we are going to receive from the JSONRPC notification
-        let cur_balance: u64;
-
-        let ping_payload: Vec<u8> = vec![42, 33, 31, 42];
-
-        let iter_interval = 1;
-        let mut sub_iter = 0;
-
-        loop {
-            let message = read
-                .next()
-                .await
-                .ok_or_else(|| Error::TungsteniteError("No more messages".to_string()))??;
-
-            if let Message::Pong(_) = message.clone() {
-                if sub_iter > 60 * 10 {
-                    // 10 minutes
-                    self.unsubscribe(&mut write, &pubkey, &sub_id).await?;
-                    return Err(SolFailed::RpcError(format!("Deposit for {:?} expired", pubkey)))
-                }
-                sub_iter += iter_interval;
-                sleep(iter_interval).await;
-                write.send(Message::Ping(ping_payload.clone())).await?;
-                continue
-            };
-
-            match serde_json::from_slice(&message.into_data())? {
-                JsonResult::Resp(r) => {
-                    // ACK
-                    debug!(target: "SOLANA RPC", "<-- {}", serde_json::to_string(&r)?);
-                    self.subscriptions.lock().await.push(pubkey);
-                    sub_id = r.result.as_i64().unwrap();
-
-                    // Start sending pings
-                    write.send(Message::Ping(ping_payload.clone())).await?;
-                }
-                JsonResult::Err(e) => {
-                    debug!(target: "SOLANA RPC", "<-- {}", serde_json::to_string(&e)?);
-
-                    self.unsubscribe(&mut write, &pubkey, &sub_id).await?;
-                    return Err(SolFailed::RpcError(e.error.message.to_string()))
-                }
-                JsonResult::Notif(n) => {
-                    // Account updated
-                    debug!(target: "SOLANA RPC", "Got WebSocket notification");
-                    let params = n.params["result"]["value"].clone();
-
-                    if mint.is_some() {
-                        cur_balance = params["data"]["parsed"]["info"]["tokenAmount"]["amount"]
-                            .as_str()
-                            .unwrap()
-                            .parse()
-                            .map_err(Error::from)?;
-                    } else {
-                        cur_balance = params["lamports"].as_u64().unwrap();
-                    }
-                    break
-                }
-            }
-        }
-
-        let send_notification = self.notify_channel.0.clone();
-
-        let self2 = self.clone();
-        self2.unsubscribe(&mut write, &pubkey, &sub_id).await?;
-
-        if cur_balance < prev_balance {
-            return Err(SolFailed::Notification("New balance is less than previous balance".into()))
-        }
-
-        let amnt = cur_balance - prev_balance;
-
-        if mint.is_some() {
-            let ui_amnt = amnt / u64::pow(10, decimals as u32);
-
-            send_notification
-                .send(TokenNotification {
-                    network: NetworkName::Solana,
-                    token_id: generate_id2(&mint.unwrap().to_string(), &NetworkName::Solana)?,
-                    drk_pub_key,
-                    received_balance: amnt,
-                    decimals: decimals as u16,
-                })
-                .await
-                .map_err(Error::from)?;
-
-            info!(target: "SOL BRIDGE", "Received {} {:?} tokens", ui_amnt, mint.unwrap());
-            let _ = self.send_tok_to_main_wallet(&rpc, &mint.unwrap(), amnt, decimals, &keypair)?;
-        } else {
-            let ui_amnt = lamports_to_sol(amnt);
-
-            send_notification
-                .send(TokenNotification {
-                    network: NetworkName::Solana,
-                    token_id: generate_id2(SOL_NATIVE_TOKEN_ID, &NetworkName::Solana)?,
-                    drk_pub_key,
-                    received_balance: amnt,
-                    decimals: decimals as u16,
-                })
-                .await
-                .map_err(Error::from)?;
-
-            info!(target: "SOL BRIDGE", "Received {} SOL", ui_amnt);
-            let _ = self.send_sol_to_main_wallet(&rpc, amnt, &keypair)?;
-        }
-
-        Ok(())
-    }
-
-    async fn unsubscribe(
-        self: Arc<Self>,
-        write: &mut futures::stream::SplitSink<WsStream, tungstenite::Message>,
-        pubkey: &Pubkey,
-        sub_id: &i64,
-    ) -> Result<()> {
-        {
-            let mut subscriptions = self.subscriptions.lock().await;
-            let index = subscriptions.iter().position(|p| p == pubkey);
-            if let Some(ind) = index {
-                trace!(target: "SOL BRIDGE", "Removing subscription from list");
-                subscriptions.remove(ind);
-            }
-        }
-
-        let unsubscription = jsonrpc::request(json!("accountUnsubscribe"), json!([sub_id]));
-
-        write.send(Message::text(serde_json::to_string(&unsubscription)?)).await?;
-
-        Ok(())
-    }
-
-    fn send_tok_to_main_wallet(
-        self: Arc<Self>,
-        rpc: &RpcClient,
-        mint: &Pubkey,
-        amount: u64,
-        decimals: u64,
-        keypair: &Keypair,
-    ) -> SolResult<Signature> {
-        debug!(target: "SOL BRIDGE", "Sending {} {:?} tokens to main wallet",
-            amount / u64::pow(10, decimals as u32), mint);
-
-        // The token account from our main wallet
-        let main_tok_pk = get_associated_token_address(&self.main_keypair.pubkey(), mint);
-        // The token account from the deposit wallet
-        let temp_tok_pk = get_associated_token_address(&keypair.pubkey(), mint);
-
-        let mut instructions = vec![];
-
-        match rpc.get_account_data(&main_tok_pk) {
-            Ok(v) => {
-                // This will fail in the event of unexpected data
-                // otherwise it's valid token data, and we consider account initialized.
-                spl_token::state::Account::unpack_from_slice(&v)?;
-            }
-            Err(_) => {
-                // Unitinialized, so we add a creation instruction
-                debug!("Main wallet token account is uninitialized. Adding init instruction.");
-                let init_ix = create_associated_token_account(
-                    &self.main_keypair.pubkey(), // fee payer
-                    &self.main_keypair.pubkey(), // wallet
-                    mint,
-                );
-                instructions.push(init_ix);
-            }
-        }
-
-        // Transfer tokens from the deposit wallet to the main wallet
-        let transfer_ix = spl_token::instruction::transfer_checked(
-            &spl_token::id(),
-            &temp_tok_pk,
-            mint,
-            &main_tok_pk,
-            &keypair.pubkey(),
-            &[],
-            amount,
-            decimals as u8,
-        )?;
-        instructions.push(transfer_ix);
-
-        // Close the account and reap the rent if there's no more tokens on it.
-        let (tok_balance, _) = get_account_token_balance(rpc, &temp_tok_pk, mint)?;
-        if tok_balance - amount == 0 {
-            debug!(target: "SOL BRIDGE", "Adding account close instruction because resulting balance is 0");
-            let close_ix = spl_token::instruction::close_account(
-                &spl_token::id(),
-                &temp_tok_pk,
-                &self.main_keypair.pubkey(),
-                &keypair.pubkey(),
-                &[],
-            )?;
-            instructions.push(close_ix);
-        }
-
-        let tx = Transaction::new_with_payer(&instructions, Some(&self.main_keypair.pubkey()));
-        let signature = sign_and_send_transaction(rpc, tx, vec![&self.main_keypair, keypair])?;
-
-        debug!(target: "SOL BRIDGE", "Sent tokens to main wallet: {}", signature);
-
-        Ok(signature)
-    }
-
-    fn send_sol_to_main_wallet(
-        self: Arc<Self>,
-        rpc: &RpcClient,
-        amount: u64,
-        keypair: &Keypair,
-    ) -> SolResult<Signature> {
-        debug!(target: "SOL BRIDGE", "Sending {} SOL to main wallet", lamports_to_sol(amount));
-
-        let ix =
-            system_instruction::transfer(&keypair.pubkey(), &self.main_keypair.pubkey(), amount);
-        let tx = Transaction::new_with_payer(&[ix], Some(&self.main_keypair.pubkey()));
-        let signature = sign_and_send_transaction(rpc, tx, vec![&self.main_keypair, keypair])?;
-
-        debug!(target: "SOL BRIDGE", "Sent {} SOL to main wallet: {}", lamports_to_sol(amount), signature);
-        Ok(signature)
-    }
-
-    fn check_mint_address(&self, mint_address: Option<String>) -> SolResult<Option<Pubkey>> {
-        if let Some(mint_addr) = mint_address {
-            let pubkey = match Pubkey::from_str(&mint_addr) {
-                Ok(v) => v,
-                Err(e) => return Err(SolFailed::BadSolAddress(e.to_string())),
-            };
-
-            let rpc = RpcClient::new(self.rpc_server.to_string());
-
-            if !account_is_initialized_mint(&rpc, &pubkey).0 {
-                return Err(SolFailed::MintIsNotValid(mint_addr))
-            }
-
-            Ok(Some(pubkey))
-        } else {
-            Ok(None)
-        }
-    }
-}
-
-#[async_trait]
-impl NetworkClient for SolClient {
-    async fn subscribe(
-        self: Arc<Self>,
-        drk_pub_key: PublicKey,
-        mint_address: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<TokenSubscribtion> {
-        let keypair = SolKeypair(Keypair::new());
-
-        let public_key = keypair.0.pubkey().to_string();
-        let private_key = serialize(&keypair);
-
-        let mint = self.check_mint_address(mint_address)?;
-
-        let rpc = RpcClient::new(self.rpc_server.to_string());
-
-        if !self.check_main_account_balance(&rpc)? {
-            warn!(target: "SOL BRIDGE", "Main account has no enough funds");
-            return Err(Error::from(SolFailed::MainAccountNotEnoughValue))
-        }
-
-        executor
-            .spawn(async move {
-                let result = self.handle_subscribe_request(keypair.0, drk_pub_key, mint).await;
-                if let Err(e) = result {
-                    error!(target: "SOL BRIDGE SUBSCRIPTION","{}", e.to_string());
-                }
-            })
-            .detach();
-
-        Ok(TokenSubscribtion { private_key, public_key })
-    }
-
-    // in solana case private key it's the same as keypair
-    async fn subscribe_with_keypair(
-        self: Arc<Self>,
-        private_key: Vec<u8>,
-        _public_key: Vec<u8>,
-        drk_pub_key: PublicKey,
-        mint_address: Option<String>,
-        executor: Arc<Executor<'_>>,
-    ) -> Result<String> {
-        let keypair: Keypair = deserialize::<SolKeypair>(&private_key)?.0;
-
-        let public_key = keypair.pubkey().to_string();
-
-        let mint = self.check_mint_address(mint_address)?;
-
-        let rpc = RpcClient::new(self.rpc_server.to_string());
-
-        if !self.check_main_account_balance(&rpc)? {
-            return Err(Error::from(SolFailed::MainAccountNotEnoughValue))
-        }
-
-        executor
-            .spawn(async move {
-                let result = self.handle_subscribe_request(keypair, drk_pub_key, mint).await;
-                if let Err(e) = result {
-                    error!(target: "SOL BRIDGE SUBSCRIPTION","{}", e.to_string());
-                }
-            })
-            .detach();
-
-        Ok(public_key)
-    }
-
-    async fn get_notifier(self: Arc<Self>) -> Result<async_channel::Receiver<TokenNotification>> {
-        Ok(self.notify_channel.1.clone())
-    }
-
-    async fn send(
-        self: Arc<Self>,
-        address: Vec<u8>,
-        mint: Option<String>,
-        amount: u64,
-    ) -> Result<()> {
-        debug!(target: "SOL BRIDGE", "start sending {} sol", lamports_to_sol(amount) );
-
-        let rpc = RpcClient::new(self.rpc_server.to_string());
-        let address: Pubkey = deserialize::<SolPubkey>(&address)?.0;
-
-        let mut decimals = 9;
-
-        if mint.is_some() {
-            let mint_address: Option<Pubkey> = self.check_mint_address(mint)?;
-            if let Some(mint_addr) = mint_address {
-                let tkn = rpc.get_token_supply(&mint_addr).map_err(SolFailed::from)?;
-                decimals = tkn.decimals;
-            };
-        }
-
-        // reverse truncate
-        let amount = truncate(amount, decimals as u16, 8)?;
-
-        let instruction =
-            system_instruction::transfer(&self.main_keypair.pubkey(), &address, amount);
-
-        let mut tx = Transaction::new_with_payer(&[instruction], Some(&self.main_keypair.pubkey()));
-        let bhq = BlockhashQuery::default();
-        match bhq.get_blockhash(&rpc, rpc.commitment()) {
-            Err(_) => panic!("Couldn't connect to RPC"),
-            Ok(v) => tx.sign(&[&self.main_keypair], v),
-        }
-
-        let _signature = rpc.send_and_confirm_transaction(&tx).map_err(SolFailed::from)?;
-
-        Ok(())
-    }
-}
-
-/// Gets account token balance for given mint.
-/// Returns: (amount, decimals)
-pub fn get_account_token_balance(
-    rpc: &RpcClient,
-    address: &Pubkey,
-    mint: &Pubkey,
-) -> SolResult<(u64, u64)> {
-    let mint_account = rpc.get_account(mint)?;
-    let token_account = rpc.get_account(address)?;
-    let mint_data = spl_token::state::Mint::unpack_from_slice(&mint_account.data)?;
-    let token_data = spl_token::state::Account::unpack_from_slice(&token_account.data)?;
-
-    Ok((token_data.amount, mint_data.decimals as u64))
-}
-
-/// Check if given account is a valid token mint
-pub fn account_is_initialized_mint(rpc: &RpcClient, mint: &Pubkey) -> (bool, u64) {
-    match rpc.get_token_supply(mint) {
-        Ok(v) => (true, v.decimals as u64),
-        Err(_) => (false, 0),
-    }
-}
-
-pub fn sign_and_send_transaction(
-    rpc: &RpcClient,
-    mut tx: Transaction,
-    signers: Vec<&Keypair>,
-) -> SolResult<Signature> {
-    let bhq = BlockhashQuery::default();
-    match bhq.get_blockhash(rpc, rpc.commitment()) {
-        Err(_) => return Err(SolFailed::RpcError("Couldn't connect to RPC".into())),
-        Ok(v) => tx.sign(&signers, v),
-    }
-
-    match rpc.send_and_confirm_transaction(&tx) {
-        Ok(s) => Ok(s),
-        Err(_) => Err(SolFailed::RpcError("Failed to send transaction".into())),
-    }
-}
-
-impl Encodable for SolKeypair {
-    fn encode<S: std::io::Write>(&self, s: S) -> darkfi::Result<usize> {
-        let key: Vec<u8> = self.0.to_bytes().to_vec();
-        let len = key.encode(s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for SolKeypair {
-    fn decode<D: std::io::Read>(mut d: D) -> darkfi::Result<Self> {
-        let key: Vec<u8> = Decodable::decode(&mut d)?;
-        let key = Keypair::from_bytes(key.as_slice())
-            .map_err(|_| darkfi::Error::DecodeError("SOL BRIDGE: load keypair from slice"))?;
-        Ok(SolKeypair(key))
-    }
-}
-
-impl Encodable for SolPubkey {
-    fn encode<S: std::io::Write>(&self, s: S) -> darkfi::Result<usize> {
-        let key = self.0.to_string();
-        let len = key.encode(s)?;
-        Ok(len)
-    }
-}
-
-impl Decodable for SolPubkey {
-    fn decode<D: std::io::Read>(mut d: D) -> darkfi::Result<Self> {
-        let key: String = Decodable::decode(&mut d)?;
-        let key = Pubkey::try_from(key.as_str())
-            .map_err(|_| darkfi::Error::DecodeError("SOL BRIDGE: load public key from slice"))?;
-        Ok(SolPubkey(key))
-    }
-}
-
-#[derive(Debug, thiserror::Error)]
-pub enum SolFailed {
-    #[error("There is no enough value `{0}`")]
-    NotEnoughValue(u64),
-    #[error("Main Account Has no enough value")]
-    MainAccountNotEnoughValue,
-    #[error("Bad Sol Address: `{0}`")]
-    BadSolAddress(String),
-    #[error("Decode and decode keys error: `{0}`")]
-    DecodeAndEncodeError(String),
-    #[error(transparent)]
-    WebSocketError(#[from] tungstenite::Error),
-    #[error("RpcError: `{0}`")]
-    RpcError(String),
-    #[error(transparent)]
-    SolClientError(#[from] solana_client::client_error::ClientError),
-    #[error("Received Notification Error: `{0}`")]
-    Notification(String),
-    #[error(transparent)]
-    ProgramError(#[from] solana_sdk::program_error::ProgramError),
-    #[error("Given mint is not valid: `{0}`")]
-    MintIsNotValid(String),
-    #[error(transparent)]
-    JsonError(#[from] serde_json::Error),
-    #[error(transparent)]
-    ParseError(#[from] solana_sdk::pubkey::ParsePubkeyError),
-    #[error("Signature Error: `{0}`")]
-    Signature(String),
-    #[error(transparent)]
-    Darkfi(#[from] darkfi::error::Error),
-}
-
-impl From<SolFailed> for Error {
-    fn from(error: SolFailed) -> Self {
-        Error::CashierError(error.to_string())
-    }
-}
-
-pub type SolResult<T> = std::result::Result<T, SolFailed>;