1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::{connect_lightwalletd, CompactTxStreamerClient, DbAdapter};
use crate::fountain::FountainCodes;
use crate::mempool::MemPool;
use anyhow::anyhow;
use lazy_static::lazy_static;
use lazycell::AtomicLazyCell;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::{Arc, Mutex, MutexGuard};
use tonic::transport::Channel;
use zcash_params::coin::{get_coin_chain, CoinChain, CoinType};
use zcash_params::{OUTPUT_PARAMS, SPEND_PARAMS};
use zcash_proofs::prover::LocalTxProver;

lazy_static! {
    pub static ref COIN_CONFIG: [Mutex<CoinConfig>; 3] = [
        Mutex::new(CoinConfig::new(0, CoinType::Zcash)),
        Mutex::new(CoinConfig::new(1, CoinType::Ycash)),
        Mutex::new(CoinConfig::new(2, CoinType::PirateChain)),
    ];
    pub static ref PROVER: AtomicLazyCell<LocalTxProver> = AtomicLazyCell::new();
    pub static ref RAPTORQ: Mutex<FountainCodes> = Mutex::new(FountainCodes::new());
}

pub static ACTIVE_COIN: AtomicU8 = AtomicU8::new(0);

/// Set the active coin
pub fn set_active(active: u8) {
    ACTIVE_COIN.store(active, Ordering::Release);
}

/// Set the active account for a given coin
pub fn set_active_account(coin: u8, id: u32) {
    let mempool = {
        let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
        c.id_account = id;
        c.mempool.clone()
    };
    let mut mempool = mempool.lock().unwrap();
    let _ = mempool.clear();
}

/// Set the lightwalletd url for a given coin
pub fn set_coin_lwd_url(coin: u8, lwd_url: &str) {
    let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
    c.lwd_url = Some(lwd_url.to_string());
}

/// Get the URL of the lightwalletd server for a given coin
pub fn get_coin_lwd_url(coin: u8) -> String {
    let c = COIN_CONFIG[coin as usize].lock().unwrap();
    c.lwd_url.clone().unwrap_or_default()
}

/// Initialize a coin with a database path
pub fn init_coin(coin: u8, db_path: &str) -> anyhow::Result<()> {
    let mut c = COIN_CONFIG[coin as usize].lock().unwrap();
    c.set_db_path(db_path)?;
    Ok(())
}

#[derive(Clone)]
pub struct CoinConfig {
    pub coin: u8,
    pub coin_type: CoinType,
    pub id_account: u32,
    pub height: u32,
    pub lwd_url: Option<String>,
    pub db_path: Option<String>,
    pub mempool: Arc<Mutex<MemPool>>,
    pub db: Option<Arc<Mutex<DbAdapter>>>,
    pub chain: &'static (dyn CoinChain + Send),
}

impl CoinConfig {
    pub fn new(coin: u8, coin_type: CoinType) -> Self {
        let chain = get_coin_chain(coin_type);
        CoinConfig {
            coin,
            coin_type,
            id_account: 0,
            height: 0,
            lwd_url: None,
            db_path: None,
            db: None,
            mempool: Arc::new(Mutex::new(MemPool::new(coin))),
            chain,
        }
    }

    pub fn set_db_path(&mut self, db_path: &str) -> anyhow::Result<()> {
        self.db_path = Some(db_path.to_string());
        let mut db = DbAdapter::new(self.coin_type, db_path)?;
        db.init_db()?;
        self.db = Some(Arc::new(Mutex::new(db)));
        Ok(())
    }

    pub fn get(coin: u8) -> CoinConfig {
        let c = COIN_CONFIG[coin as usize].lock().unwrap();
        c.clone()
    }

    pub fn get_active() -> CoinConfig {
        let coin = ACTIVE_COIN.load(Ordering::Acquire) as usize;
        let c = COIN_CONFIG[coin].lock().unwrap();
        c.clone()
    }

    pub fn set_height(height: u32) {
        let coin = ACTIVE_COIN.load(Ordering::Acquire) as usize;
        let mut c = COIN_CONFIG[coin].lock().unwrap();
        c.height = height;
    }

    pub fn mempool(&self) -> MutexGuard<MemPool> {
        self.mempool.lock().unwrap()
    }

    pub fn db(&self) -> anyhow::Result<MutexGuard<DbAdapter>> {
        let db = self.db.as_ref().unwrap();
        let db = db.lock().unwrap();
        Ok(db)
    }

    pub async fn connect_lwd(&self) -> anyhow::Result<CompactTxStreamerClient<Channel>> {
        if let Some(lwd_url) = &self.lwd_url {
            connect_lightwalletd(lwd_url).await
        } else {
            Err(anyhow!("LWD URL Not set"))
        }
    }
}

pub fn get_prover() -> &'static LocalTxProver {
    if !PROVER.filled() {
        let _ = PROVER.fill(LocalTxProver::from_bytes(SPEND_PARAMS, OUTPUT_PARAMS));
    }
    PROVER.borrow().unwrap()
}