Просмотр исходного кода

Merge pull request #3 from darkrenaissance/bitcoin

Cashier with bitcoin
zkjanus 5 лет назад
Родитель
Сommit
20539f3f7f

+ 43 - 0
Cargo.lock

@@ -401,6 +401,12 @@ version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 
 
+[[package]]
+name = "bech32"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1"
+
 [[package]]
 [[package]]
 name = "bellman"
 name = "bellman"
 version = "0.8.1"
 version = "0.8.1"
@@ -446,6 +452,23 @@ dependencies = [
  "shlex",
  "shlex",
 ]
 ]
 
 
+[[package]]
+name = "bitcoin"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6742ec672d3f12506f4ac5c0d853926ff1f94e675f60ffd3224039972bf663f1"
+dependencies = [
+ "bech32",
+ "bitcoin_hashes",
+ "secp256k1",
+]
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ce18265ec2324ad075345d5814fbeed4f41f0a660055dc78840b74d19b874b1"
+
 [[package]]
 [[package]]
 name = "bitflags"
 name = "bitflags"
 version = "1.2.1"
 version = "1.2.1"
@@ -961,6 +984,7 @@ dependencies = [
  "async-trait",
  "async-trait",
  "bellman",
  "bellman",
  "bimap",
  "bimap",
+ "bitcoin",
  "bitvec",
  "bitvec",
  "blake2b_simd",
  "blake2b_simd",
  "blake2s_simd",
  "blake2s_simd",
@@ -992,6 +1016,7 @@ dependencies = [
  "regex",
  "regex",
  "rocksdb",
  "rocksdb",
  "rusqlite",
  "rusqlite",
+ "secp256k1",
  "serde",
  "serde",
  "serde_json",
  "serde_json",
  "sha2",
  "sha2",
@@ -2426,6 +2451,24 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 
+[[package]]
+name = "secp256k1"
+version = "0.20.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a"
+dependencies = [
+ "secp256k1-sys",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "827cb7cce42533829c792fc51b82fbf18b125b45a702ef2c8be77fce65463a7b"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 [[package]]
 name = "security-framework"
 name = "security-framework"
 version = "2.3.1"
 version = "2.3.1"

+ 9 - 2
Cargo.toml

@@ -15,7 +15,7 @@ bls12_381 = "0.3.1"
 jubjub = "0.5.1"
 jubjub = "0.5.1"
 
 
 zcash_primitives = "0.5.0"
 zcash_primitives = "0.5.0"
-zcash_proofs = "0.5.0" 
+zcash_proofs = "0.5.0"
 # zcash_primitives = { git = "https://github.com/zcash/librustzcash" }
 # zcash_primitives = { git = "https://github.com/zcash/librustzcash" }
 #zcash_proofs = { git = "https://github.com/zcash/librustzcash" }
 #zcash_proofs = { git = "https://github.com/zcash/librustzcash" }
 #zcash_proofs = { git = "https://github.com/narodnik/librustzcash" }
 #zcash_proofs = { git = "https://github.com/narodnik/librustzcash" }
@@ -77,6 +77,9 @@ bytes = "1.0.1"
 rocksdb = "0.16.0"
 rocksdb = "0.16.0"
 dirs = "3.0.2"
 dirs = "3.0.2"
 
 
+bitcoin = "0.26.2"
+secp256k1 = "0.20.3"
+
 [dependencies.rusqlite]
 [dependencies.rusqlite]
 version = "0.25.1"
 version = "0.25.1"
 features = ["bundled", "sqlcipher"]
 features = ["bundled", "sqlcipher"]
@@ -109,13 +112,17 @@ path = "src/bin/tx.rs"
 name = "gatewayd"
 name = "gatewayd"
 path = "src/bin/gatewayd.rs"
 path = "src/bin/gatewayd.rs"
 
 
+[[bin]]
+name = "cashierd"
+path = "src/bin/cashierd.rs"
+
 [[bin]]
 [[bin]]
 name = "darkfid"
 name = "darkfid"
 path = "src/bin/darkfid.rs"
 path = "src/bin/darkfid.rs"
+
 [[bin]]
 [[bin]]
 name = "drk"
 name = "drk"
 path = "src/bin/drk.rs"
 path = "src/bin/drk.rs"
 
 
 [profile.release]
 [profile.release]
 debug = 1
 debug = 1
-

+ 101 - 0
src/bin/cashierd.rs

@@ -0,0 +1,101 @@
+use std::net::SocketAddr;
+use std::str;
+use std::sync::Arc;
+
+use std::fs::OpenOptions;
+use std::io::Read;
+use std::{fs, path::Path, path::PathBuf};
+use toml;
+
+use drk::blockchain::{rocks::columns, Rocks, RocksColumn};
+use drk::cli::{CashierdCli, CashierdConfig};
+use drk::wallet::{WalletDb, WalletPtr};
+use drk::service::CashierService;
+
+use drk::util::join_config_path;
+use drk::Result;
+
+use async_executor::Executor;
+use easy_parallel::Parallel;
+
+fn setup_addr(address: Option<SocketAddr>, default: SocketAddr) -> SocketAddr {
+    match address {
+        Some(addr) => addr,
+        None => default,
+    }
+}
+
+async fn start(executor: Arc<Executor<'_>>, config: Arc<&CashierdConfig>) -> Result<()> {
+    let accept_addr: SocketAddr = config.accept_url.parse()?;
+
+    let database_path = config.database_path.clone();
+
+    let database_path = join_config_path(&PathBuf::from(database_path))?;
+
+    let rocks = Rocks::new(&database_path)?;
+
+    let rocks_cashierstore_column = RocksColumn::<columns::CashierKeys>::new(rocks);
+    // Use pw: PASSWORD for now
+    //let cashier_wallet = Arc::new(WalletDB::new("cashier.db", "PASSWORD")?);
+    let wallet = Arc::new(WalletDb::new("cashier.db", config.password.clone())?);
+
+    let cashier = CashierService::new(accept_addr, rocks_cashierstore_column, wallet)?;
+
+    cashier.start(executor.clone()).await?;
+    Ok(())
+}
+
+fn main() -> Result<()> {
+    use simplelog::*;
+
+    let ex = Arc::new(Executor::new());
+    let (signal, shutdown) = async_channel::unbounded::<()>();
+
+    let path = join_config_path(&PathBuf::from("cashierd.toml")).unwrap();
+
+    let config: CashierdConfig = if Path::new(&path).exists() {
+        CashierdConfig::load(path)?
+    } else {
+        CashierdConfig::load_default(path)?
+    };
+
+    let config_ptr = Arc::new(&config);
+
+    let options = CashierdCli::load()?;
+
+    let logger_config = ConfigBuilder::new().set_time_format_str("%T%.6f").build();
+
+    let debug_level = if options.verbose {
+        LevelFilter::Debug
+    } else {
+        LevelFilter::Off
+    };
+
+    let log_path = config.log_path.clone();
+    CombinedLogger::init(vec![
+        TermLogger::new(debug_level, logger_config, TerminalMode::Mixed).unwrap(),
+        WriteLogger::new(
+            LevelFilter::Debug,
+            Config::default(),
+            std::fs::File::create(log_path).unwrap(),
+        ),
+    ])
+    .unwrap();
+
+
+    let ex2 = ex.clone();
+
+    let (_, result) = Parallel::new()
+        // Run four executor threads.
+        .each(0..3, |_| 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_ptr).await?;
+                drop(signal);
+                Ok::<(), drk::Error>(())
+            })
+        });
+
+    result
+}

+ 44 - 0
src/blockchain/cashier_keypair.rs

@@ -0,0 +1,44 @@
+use crate::serial::{Decodable, Encodable};
+use crate::Result;
+
+#[derive(Clone, Debug)]
+pub struct CashierKeypair {
+    zk_public: jubjub::SubgroupPoint,
+    payload: Vec<u8>,
+}
+
+impl CashierKeypair {
+    pub fn new(zk_public: jubjub::SubgroupPoint, payload: Vec<u8>) -> Self {
+        CashierKeypair { zk_public, payload }
+    }
+
+    pub fn set_index(&mut self, index: jubjub::SubgroupPoint) {
+        self.zk_public = index;
+    }
+
+    pub fn get_index(&self) -> jubjub::SubgroupPoint {
+        self.zk_public
+    }
+
+    pub fn get_payload(&self) -> Vec<u8> {
+        self.payload.clone()
+    }
+}
+
+impl Encodable for CashierKeypair {
+    fn encode<S: std::io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.zk_public.encode(&mut s)?;
+        len += self.payload.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+impl Decodable for CashierKeypair {
+    fn decode<D: std::io::Read>(mut d: D) -> Result<Self> {
+        Ok(Self {
+            zk_public: Decodable::decode(&mut d)?,
+            payload: Decodable::decode(&mut d)?,
+        })
+    }
+}

+ 55 - 0
src/blockchain/cashierstore.rs

@@ -0,0 +1,55 @@
+use std::sync::Arc;
+
+use crate::serial::{deserialize, serialize};
+use crate::Result;
+
+use super::rocks::{columns, IteratorMode, RocksColumn};
+use super::cashier_keypair::CashierKeypair;
+
+pub struct CashierStore {
+    rocks: RocksColumn<columns::CashierKeys>,
+}
+
+impl CashierStore {
+    pub fn new(rocks: RocksColumn<columns::CashierKeys>) -> Result<Arc<Self>> {
+        Ok(Arc::new(CashierStore { rocks }))
+    }
+
+    pub fn get(&self, key: jubjub::SubgroupPoint) -> Result<Option<Vec<u8>>> {
+        let value = self.rocks.get(key)?;
+        Ok(value)
+    }
+
+    pub fn put(&self, keypair: CashierKeypair) -> Result<Option<jubjub::SubgroupPoint>> {
+
+        let index = keypair.get_index();
+        let check = self.get(index);
+        match self.get(index) {
+            Ok(_v) => Ok(None),
+            Err(_e) => {
+                self.rocks.put(index.clone(), keypair)?;
+                Ok(Some(index))
+            },
+        }
+    }
+
+    pub fn get_value_deserialized(&self, key: Vec<u8>) -> Result<Option<CashierKeypair>> {
+        self.rocks.get_value_deserialized::<CashierKeypair>(key)
+    }
+    // Fix this
+    // pub fn get_last_index(&self) -> Result<jubjub::SubgroupPoint> {
+    //     let last_index = self.rocks.iterator(IteratorMode::End)?.next();
+    //     match last_index {
+    //         Some((index, _)) => Ok(deserialize(&index)?),
+    //         None => Ok()
+    //     }
+    // }
+
+    pub fn get_last_index_as_bytes(&self) -> Result<Vec<u8>> {
+        let last_index = self.rocks.iterator(IteratorMode::End)?.next();
+        match last_index {
+            Some((index, _)) => Ok(index.to_vec()),
+            None => Ok(serialize::<u64>(&0)),
+        }
+    }
+}

+ 4 - 0
src/blockchain/mod.rs

@@ -1,7 +1,11 @@
 pub mod rocks;
 pub mod rocks;
 pub mod slab;
 pub mod slab;
 pub mod slabstore;
 pub mod slabstore;
+pub mod cashier_keypair;
+pub mod cashierstore;
 
 
 pub use rocks::{Rocks, RocksColumn};
 pub use rocks::{Rocks, RocksColumn};
 pub use slab::Slab;
 pub use slab::Slab;
 pub use slabstore::SlabStore;
 pub use slabstore::SlabStore;
+pub use cashier_keypair::CashierKeypair;
+pub use cashierstore::CashierStore;

+ 8 - 3
src/blockchain/rocks.rs

@@ -20,6 +20,7 @@ pub mod columns {
     pub struct Slabs;
     pub struct Slabs;
     pub struct Nullifiers;
     pub struct Nullifiers;
     pub struct MerkleRoots;
     pub struct MerkleRoots;
+    pub struct CashierKeys;
 }
 }
 
 
 impl Column for columns::Slabs {
 impl Column for columns::Slabs {
@@ -33,7 +34,9 @@ impl Column for columns::Nullifiers {
 impl Column for columns::MerkleRoots {
 impl Column for columns::MerkleRoots {
     const NAME: &'static str = "merkleroots";
     const NAME: &'static str = "merkleroots";
 }
 }
-
+impl Column for columns::CashierKeys {
+    const NAME: &'static str = "cashierkeys";
+}
 pub struct Rocks {
 pub struct Rocks {
     db: DB,
     db: DB,
 }
 }
@@ -51,10 +54,12 @@ impl Rocks {
         // nullifiers column family
         // nullifiers column family
         let nullifiers_cf = ColumnFamilyDescriptor::new(columns::Nullifiers::NAME, cf_opts.clone());
         let nullifiers_cf = ColumnFamilyDescriptor::new(columns::Nullifiers::NAME, cf_opts.clone());
         // merkleroots column family
         // merkleroots column family
-        let merkleroots_cf = ColumnFamilyDescriptor::new(columns::MerkleRoots::NAME, cf_opts);
+        let merkleroots_cf = ColumnFamilyDescriptor::new(columns::MerkleRoots::NAME, cf_opts.clone());
+        // cashierkeypair column family
+        let cashierkeys_cf = ColumnFamilyDescriptor::new(columns::CashierKeys::NAME, cf_opts);
 
 
         // column families
         // column families
-        let cfs = vec![default_cf, slab_cf, nullifiers_cf, merkleroots_cf];
+        let cfs = vec![default_cf, slab_cf, nullifiers_cf, merkleroots_cf, cashierkeys_cf];
 
 
         // database options
         // database options
         let mut opt = Options::default();
         let mut opt = Options::default();

+ 23 - 0
src/cli/cashierd_cli.rs

@@ -0,0 +1,23 @@
+use crate::Result;
+
+pub struct CashierdCli {
+    pub verbose: bool,
+}
+
+impl CashierdCli {
+    pub fn load() -> Result<Self> {
+        let app = clap_app!(dfi =>
+            (version: "0.1.0")
+            (author: "Amir Taaki <amir@dyne.org>")
+            (about: "run service daemon")
+            (@arg VERBOSE: -v --verbose "Increase verbosity")
+        )
+        .get_matches();
+
+        let verbose = app.is_present("VERBOSE");
+
+        Ok(Self {
+            verbose,
+        })
+    }
+}

+ 45 - 0
src/cli/cli_config.rs

@@ -160,3 +160,48 @@ impl Default for GatewaydConfig {
         }
         }
     }
     }
 }
 }
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct CashierdConfig {
+    #[serde(default)]
+    #[serde(rename = "connect_url")]
+    pub accept_url: String,
+
+    #[serde(default)]
+    #[serde(rename = "database_path")]
+    pub database_path: String,
+
+    #[serde(default)]
+    #[serde(rename = "log_path")]
+    pub log_path: String,
+
+    #[serde(default)]
+    #[serde(rename = "password")]
+    pub password: String,
+}
+
+impl CashierdConfig {
+    pub fn load(path: PathBuf) -> Result<Self> {
+        let toml = fs::read(&path)?;
+        let str_buff = str::from_utf8(&toml)?;
+        let config: Self = toml::from_str(str_buff)?;
+        Ok(config)
+    }
+    pub fn load_default(path: PathBuf) -> Result<Self> {
+        let toml = Self::default();
+        let config_file = toml::to_string(&toml)?;
+        fs::write(&path, &config_file)?;
+        let config = Self::load(path)?;
+        Ok(config)
+    }
+}
+
+impl Default for CashierdConfig {
+    fn default() -> Self {
+        let accept_url = String::from("127.0.0.1:7777");
+        let database_path = String::from("cashierd.db");
+        let log_path = String::from("/tmp/cashierd.log");
+        let password = String::new();
+        Self { accept_url, database_path, log_path, password }
+    }
+}

+ 146 - 0
src/cli/client_cli/cli_config.rs

@@ -0,0 +1,146 @@
+//use crate::serial::{deserialize, serialize, Decodable, Encodable};
+//use toml::{map::Map, Value};
+use crate::util::join_config_path;
+//use crate::Result;
+
+use serde::{Deserialize, Serialize};
+//use log::*;
+
+use std::{fs::OpenOptions, io::prelude::*, path::PathBuf};
+
+//pub trait ClientConfig: Default + Deserialize {
+//    fn load(path: PathBuf) -> Result<Self> {
+//        let path = join_config_path(&path)?;
+//        let mut file = OpenOptions::new()
+//            .read(true)
+//            .write(true)
+//            .create(true)
+//            .open(path)?;
+//
+//        //let mut toml_map = Map::new();
+//        let mut buffer = String::new();
+//        file.read_to_string(&mut buffer)?;
+//        //let buffer: &'static str = buffer;
+//
+//        //let tomlstring = toml::to_string(&file).expect("Could not encode TTOML value");
+//        if !buffer.is_empty() {
+//            let config: Self = toml::from_str(&buffer)?;
+//            //let config = toml::to_string(&buffer).unwrap();
+//            //let config: Self = deserialize(&buffer)?;
+//            //Ok(config)
+//            Ok(config)
+//        } else {
+//            Ok(Self::default())
+//        }
+//    }
+//    fn save(&self, path: PathBuf) -> Result<()> {
+//        //let path = join_config_path(&path)?;
+//        //let mut file = OpenOptions::new().write(true).create(true).open(&path)?;
+//        //let serialized = serialize(self);
+//        //file.write_all(&serialized)?;
+//        Ok(())
+//    }
+//}
+//
+//impl ClientConfig for DrkConfig {}
+//impl ClientConfig for DarkfidConfig {}
+
+#[derive(Serialize, Default, Deserialize, Debug)]
+pub struct DrkConfig {
+    #[serde(rename = "rpc_url")]
+    pub rpc_url: String,
+
+    #[serde(rename = "log_path")]
+    pub log_path: String,
+}
+
+#[derive(Serialize, Default, Deserialize, Debug)]
+pub struct DarkfidConfig {
+    #[serde(rename = "connect_url")]
+    pub connect_url: String,
+
+    #[serde(rename = "subscriber_url")]
+    pub subscriber_url: String,
+
+    #[serde(rename = "rpc_url")]
+    pub rpc_url: String,
+
+    #[serde(rename = "database_path")]
+    pub database_path: String,
+
+    #[serde(rename = "log_path")]
+    pub log_path: String,
+
+    #[serde(rename = "password")]
+    pub password: String,
+}
+
+
+#[derive(Serialize, Default, Deserialize, Debug)]
+pub struct GatewaydConfig {
+    #[serde(rename = "connect_url")]
+    pub accept_url: String,
+
+    #[serde(rename = "subscriber_url")]
+    pub publisher_url: String,
+
+    #[serde(rename = "database_path")]
+    pub database_path: String,
+
+    #[serde(rename = "log_path")]
+    pub log_path: String,
+}
+#[derive(Serialize, Default, Deserialize, Debug)]
+pub struct CashierdConfig {
+    #[serde(rename = "connect_url")]
+    pub accept_url: String,
+
+    #[serde(rename = "database_path")]
+    pub database_path: String,
+
+    #[serde(rename = "log_path")]
+    pub log_path: String,
+}
+//impl Default for DrkCliConfig {
+//    // default toml file
+//    fn default() -> Self {
+//        let rpc_url = String::from("http://127.0.0.1:8000");
+//        let log_path = String::from("/tmp/drk_cli.log");
+//        Self {
+//            rpc_url,
+//            log_path,
+//        }
+//    }
+//}
+
+//impl Default for DarkfidCliConfig {
+//    // create default config file
+//    fn default() -> Self {
+//        //toml::toml! {
+//        //    connect-url = "127.0.0.1:3333"
+//        //};
+//        let connect_url = String::from("127.0.0.1:3333");
+//        let subscriber_url = String::from("127.0.0.1:4444");
+//        let rpc_url = String::from("127.0.0.1:8000");
+//
+//        let database_path = String::from("database_client.db");
+//        let database_path = join_config_path(&PathBuf::from(database_path))
+//            .expect("error during join database_path to config path");
+//        let database_path = String::from(
+//            database_path
+//                .to_str()
+//                .expect("error convert Path to String"),
+//        );
+//        let log_path = String::from("/tmp/darkfid_service_daemon.log");
+//
+//        let password = String::new();
+//        Self {
+//            connect_url,
+//            subscriber_url,
+//            rpc_url,
+//            database_path,
+//            log_path,
+//            password,
+//        }
+//    }
+//}

+ 3 - 1
src/cli/mod.rs

@@ -2,9 +2,11 @@ pub mod cli_config;
 pub mod darkfid_cli;
 pub mod darkfid_cli;
 pub mod drk_cli;
 pub mod drk_cli;
 pub mod gatewayd_cli;
 pub mod gatewayd_cli;
+pub mod cashierd_cli;
 
 
-pub use cli_config::{DarkfidConfig, DrkConfig, GatewaydConfig};
+pub use cli_config::{DarkfidConfig, DrkConfig, CashierdConfig, GatewaydConfig};
 pub use darkfid_cli::DarkfidCli;
 pub use darkfid_cli::DarkfidCli;
 pub use drk_cli::DrkCli;
 pub use drk_cli::DrkCli;
 pub use drk_cli::Transfer;
 pub use drk_cli::Transfer;
 pub use gatewayd_cli::GatewaydCli;
 pub use gatewayd_cli::GatewaydCli;
+pub use cashierd_cli::CashierdCli;

+ 1 - 1
src/rpc/adapter.rs

@@ -39,7 +39,7 @@ impl RpcAdapter {
 
 
     pub fn cash_key_gen(&self) -> Result<()> {
     pub fn cash_key_gen(&self) -> Result<()> {
         debug!(target: "adapter", "key_gen() [START]");
         debug!(target: "adapter", "key_gen() [START]");
-        let (public, private) = self.wallet.key_gen();
+        let (public, private) = self.wallet.cash_key_gen();
         self.wallet.put_keypair(public, private)?;
         self.wallet.put_keypair(public, private)?;
         Ok(())
         Ok(())
     }
     }

+ 254 - 0
src/service/bitcoin_bridge.rs

@@ -0,0 +1,254 @@
+use rand::{thread_rng, Rng};
+use rand::distributions::Alphanumeric;
+use secp256k1::key::{SecretKey, PublicKey};
+use bitcoin::util::ecdsa::{PrivateKey, PublicKey as BitcoinPubKey};
+use bitcoin::util::{address::Payload, address::Address};
+// Use p2pkh for 1st iteration
+use bitcoin::hash_types::PubkeyHash;
+use bitcoin::network::constants::Network;
+use super::reqrep::{PeerId, RepProtocol, Reply, ReqProtocol, Request};
+use crate::blockchain::{rocks::columns, RocksColumn, CashierKeypair, CashierStore};
+use crate::{serial::deserialize, serial::serialize, Error, Result};
+use crate::wallet::{WalletDb, WalletPtr};
+use std::net::SocketAddr;
+use async_std::sync::Arc;
+use async_executor::Executor;
+use log::*;
+
+#[repr(u8)]
+enum CashierError {
+    NoError,
+    UpdateIndex,
+}
+
+#[repr(u8)]
+enum CashierCommand {
+    GetDBTC,
+    GetBTC,
+}
+
+pub struct BitcoinKeys {
+    secret_key: SecretKey,
+    bitcoin_private_key: PrivateKey,
+    pub bitcoin_public_key: BitcoinPubKey,
+    pub pub_address: Address,
+}
+impl BitcoinKeys {
+    pub fn new(
+
+    ) -> Result<BitcoinKeys> {
+
+        let context = secp256k1::Secp256k1::new();
+
+        // Probably not good enough for release
+        let rand: String = thread_rng()
+            .sample_iter(&Alphanumeric)
+            .take(32)
+            .map(char::from)
+            .collect();
+
+        let rand_hex = hex::encode(rand);
+
+        // Generate simple byte array from rand
+        let data_slice: &[u8] = rand_hex.as_bytes();
+
+        let secret_key = SecretKey::from_slice(&hex::decode(data_slice).unwrap()).unwrap();
+
+        //let public_key = PublicKey::from_secret_key(&context, &secret_key);
+
+        // Use Testnet
+        let bitcoin_private_key = PrivateKey::new(secret_key, Network::Testnet);
+
+        let bitcoin_public_key = BitcoinPubKey::from_private_key(&context, &bitcoin_private_key);
+
+        let pub_address = Address::p2pkh(&bitcoin_public_key, Network::Testnet);
+
+        Ok(Self {
+            secret_key,
+            bitcoin_private_key,
+            bitcoin_public_key,
+            pub_address,
+        })
+    }
+    pub fn get_deposit_address(&self) -> &Address {
+        &self.pub_address
+    }
+}
+
+pub struct CashierService {
+    addr: SocketAddr,
+    cashierstore: Arc<CashierStore>,
+    wallet: Arc<WalletDb>,
+}
+
+impl CashierService {
+    pub fn new(
+        addr: SocketAddr,
+        rocks: RocksColumn<columns::CashierKeys>,
+        wallet: Arc<WalletDb>,
+    )-> Result<Arc<CashierService>> {
+        let cashierstore = CashierStore::new(rocks)?;
+
+        Ok(Arc::new(CashierService {
+            cashierstore,
+            addr,
+            wallet,
+        }))
+    }
+    pub async fn start(self: Arc<Self>, executor: Arc<Executor<'_>>) -> Result<()> {
+        let service_name = String::from("CASHIER DAEMON");
+
+        let mut protocol = RepProtocol::new(self.addr.clone(), service_name.clone());
+
+        let (send, recv) = protocol.start().await?;
+
+        let handle_request_task = executor.spawn(self.handle_request_loop(
+            send.clone(),
+            recv.clone(),
+            executor.clone(),
+        ));
+
+        protocol.run(executor.clone()).await?;
+
+        let _ = handle_request_task.cancel().await;
+        Ok(())
+    }
+
+    async fn handle_request_loop(
+        self: Arc<Self>,
+        send_queue: async_channel::Sender<(PeerId, Reply)>,
+        recv_queue: async_channel::Receiver<(PeerId, Request)>,
+        executor: Arc<Executor<'_>>,
+    ) -> Result<()> {
+        loop {
+            match recv_queue.recv().await {
+                Ok(msg) => {
+                    let cashierstore = self.cashierstore.clone();
+                    let _ = executor
+                        .spawn(Self::handle_request(
+                            msg,
+                            cashierstore,
+                            send_queue.clone(),
+                        ))
+                        .detach();
+                }
+                Err(_) => {
+                    break;
+                }
+            }
+        }
+        Ok(())
+    }
+    async fn handle_request(
+        msg: (PeerId, Request),
+        cashierstore: Arc<CashierStore>,
+        send_queue: async_channel::Sender<(PeerId, Reply)>,
+    ) -> Result<()> {
+        let request = msg.1;
+        let peer = msg.0;
+        match request.get_command() {
+            0 => {
+                // Exchange zk_pubkey for bitcoin address
+                let zkpub = request.get_payload();
+
+                // Generate bitcoin Address
+                let btc_keys = BitcoinKeys::new().unwrap();
+                let deposit_address = btc_keys.get_deposit_address();
+
+                let mut reply = Reply::from(&request, CashierError::NoError as u32, vec![]);
+                // if let None = error {
+                //     reply.set_error(CashierError::UpdateIndex as u32);
+                // }
+                // send reply
+                send_queue.send((peer, reply)).await?;
+
+            }
+            1 => {
+                // Withdraw
+            }
+            _ => {
+                return Err(Error::ServicesError("received wrong command"));
+            }
+        }
+        Ok(())
+    }
+}
+
+pub struct CashierClient {
+    protocol: ReqProtocol,
+    cashierstore: Arc<CashierStore>,
+}
+
+impl CashierClient {
+    pub fn new(addr: SocketAddr, rocks: RocksColumn<columns::CashierKeys>) -> Result<Self> {
+        let protocol = ReqProtocol::new(addr, String::from("CASHIER CLIENT"));
+
+        let cashierstore = CashierStore::new(rocks)?;
+
+        Ok(CashierClient {
+            protocol,
+            cashierstore,
+        })
+    }
+
+    pub async fn start(&mut self) -> Result<()> {
+        self.protocol.start().await?;
+        //self.sync().await?;
+
+        Ok(())
+    }
+
+    pub async fn get_keys(&mut self, index: jubjub::SubgroupPoint) -> Result<Option<CashierKeypair>> {
+        let rep = self
+            .protocol
+            .request(
+                CashierCommand::GetDBTC as u8,
+                serialize(&index),
+                &handle_error,
+            )
+            .await?;
+
+        if let Some(keys) = rep {
+            let keys: CashierKeypair = deserialize(&keys)?;
+            //self.gateway_slabs_sub_s.send(slab.clone()).await?;
+            self.cashierstore.put(keys.clone())?;
+            return Ok(Some(keys));
+        }
+        Ok(None)
+    }
+
+    // pub async fn put_keys(&mut self, mut keys: CashierKeys) -> Result<()> {
+    //     loop {
+    //         let last_index = self.sync().await?;
+    //         //keys.set_index(last_index + 1);
+    //         let keys = serialize(&keys);
+
+    //         let rep = self
+    //             .protocol
+    //             .request(CashierCommand::PutSlab as u8, slab.clone(), &handle_error)
+    //             .await?;
+
+    //         if let Some(_) = rep {
+    //             break;
+    //         }
+    //     }
+    //     Ok(())
+    // }
+
+    pub fn get_cashierstore(&self) -> Arc<CashierStore> {
+        self.cashierstore.clone()
+    }
+
+}
+
+fn handle_error(status_code: u32) {
+    match status_code {
+        1 => {
+            warn!("Reply has an Error: Index is not updated");
+        }
+        2 => {
+            warn!("Reply has an Error: Index Not Exist");
+        }
+        _ => {}
+    }
+}

+ 3 - 0
src/service/mod.rs

@@ -1,4 +1,7 @@
 pub mod gateway;
 pub mod gateway;
 pub mod reqrep;
 pub mod reqrep;
+pub mod bitcoin_bridge;
 
 
 pub use gateway::{GatewayClient, GatewayService, GatewaySlabsSubscriber};
 pub use gateway::{GatewayClient, GatewayService, GatewaySlabsSubscriber};
+
+pub use bitcoin_bridge::{BitcoinKeys, CashierService, CashierClient};

+ 1 - 1
src/wallet/walletdb.rs

@@ -68,7 +68,7 @@ impl WalletDb {
 
 
     pub fn init_cashier_db(&self) -> Result<()> {
     pub fn init_cashier_db(&self) -> Result<()> {
         let conn = Connection::open(&self.path)?;
         let conn = Connection::open(&self.path)?;
-        debug!(target: "walletdb", "OPENED CONNECTION AT PATH {:?}", self.path);
+        debug!(target: "cashierdb", "OPENED CONNECTION AT PATH {:?}", self.path);
         let contents = include_str!("../../res/schema.sql");
         let contents = include_str!("../../res/schema.sql");
         conn.execute_batch(&contents)?;
         conn.execute_batch(&contents)?;
         Ok(())
         Ok(())