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

faucetd: Initialization of wallet and zkvm keys.

parazyd 3 лет назад
Родитель
Сommit
e07f035c62
2 измененных файлов с 191 добавлено и 52 удалено
  1. 3 1
      bin/faucetd/Cargo.toml
  2. 188 51
      bin/faucetd/src/main.rs

+ 3 - 1
bin/faucetd/Cargo.toml

@@ -15,7 +15,7 @@ blake3 = "1.3.1"
 bs58 = "0.4.0"
 bs58 = "0.4.0"
 chrono = "0.4.22"
 chrono = "0.4.22"
 ctrlc = { version = "3.2.3", features = ["termination"] }
 ctrlc = { version = "3.2.3", features = ["termination"] }
-darkfi = {path = "../../", features = ["blockchain", "wallet", "rpc", "net"]}
+darkfi = {path = "../../", features = ["blockchain", "wallet", "rpc", "net", "zkas", "crypto"]}
 darkfi-serial = {path = "../../src/serial"}
 darkfi-serial = {path = "../../src/serial"}
 darkfi-sdk = {path = "../../src/sdk"}
 darkfi-sdk = {path = "../../src/sdk"}
 easy-parallel = "3.2.0"
 easy-parallel = "3.2.0"
@@ -27,6 +27,8 @@ serde_json = "1.0.87"
 simplelog = "0.12.0"
 simplelog = "0.12.0"
 sled = "0.34.7"
 sled = "0.34.7"
 smol = "1.2.5"
 smol = "1.2.5"
+libsqlite3-sys = {version = "0.24.2", features = ["bundled-sqlcipher"]}
+sqlx = {version = "0.6.2", features = ["runtime-async-std-native-tls", "sqlite"]}
 url = "2.3.1"
 url = "2.3.1"
 
 
 # Argument parsing
 # Argument parsing

+ 188 - 51
bin/faucetd/src/main.rs

@@ -18,27 +18,39 @@
 
 
 use std::{collections::HashMap, str::FromStr};
 use std::{collections::HashMap, str::FromStr};
 
 
-use async_std::sync::{Arc, Mutex};
+use async_std::sync::{Arc, Mutex, RwLock};
 use async_trait::async_trait;
 use async_trait::async_trait;
 use chrono::Utc;
 use chrono::Utc;
-use darkfi_sdk::crypto::{Address, PublicKey, TokenId};
-use darkfi_serial::serialize;
+use darkfi::{
+    crypto::proof::{ProvingKey, VerifyingKey},
+    zk::{vm::ZkCircuit, vm_stack::empty_witnesses},
+    zkas::ZkBinary,
+};
+use darkfi_sdk::{
+    crypto::{constants::MERKLE_DEPTH, ContractId, MerkleNode, PublicKey, TokenId},
+    incrementalmerkletree::bridgetree::BridgeTree,
+    pasta::{group::ff::PrimeField, pallas},
+};
+use darkfi_serial::{deserialize, serialize};
 use log::{debug, error, info};
 use log::{debug, error, info};
 use serde_json::{json, Value};
 use serde_json::{json, Value};
+use sqlx::Row;
 use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
 use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
 use url::Url;
 use url::Url;
 
 
 use darkfi::{
 use darkfi::{
     async_daemonize, cli_desc,
     async_daemonize, cli_desc,
     consensus::{
     consensus::{
+        constants::{
+            MAINNET_GENESIS_HASH_BYTES, MAINNET_GENESIS_TIMESTAMP, TESTNET_GENESIS_HASH_BYTES,
+            TESTNET_GENESIS_TIMESTAMP,
+        },
         proto::{ProtocolSync, ProtocolTx},
         proto::{ProtocolSync, ProtocolTx},
         task::block_sync_task,
         task::block_sync_task,
-        ValidatorState, ValidatorStatePtr, MAINNET_GENESIS_HASH_BYTES, MAINNET_GENESIS_TIMESTAMP,
-        TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP,
+        ValidatorState, ValidatorStatePtr,
     },
     },
     net,
     net,
     net::P2pPtr,
     net::P2pPtr,
-    node::Client,
     rpc::{
     rpc::{
         jsonrpc::{
         jsonrpc::{
             ErrorCode::{InternalError, InvalidParams, MethodNotFound},
             ErrorCode::{InternalError, InvalidParams, MethodNotFound},
@@ -47,13 +59,47 @@ use darkfi::{
         server::{listen_and_serve, RequestHandler},
         server::{listen_and_serve, RequestHandler},
     },
     },
     util::{async_util::sleep, parse::decode_base10, path::expand_path},
     util::{async_util::sleep, parse::decode_base10, path::expand_path},
-    wallet::walletdb::init_wallet,
+    wallet::{walletdb::init_wallet, WalletPtr},
     Error, Result,
     Error, Result,
 };
 };
 
 
 mod error;
 mod error;
 use error::{server_error, RpcError};
 use error::{server_error, RpcError};
 
 
+// TODO: FIXME:
+// Find a way to have these constants be deterministic for the actual
+// contract. e.g. they could be prefixed with the contract_id in order
+// not to have collisions happen. This is because right now it's easy
+// to overwrite any table in the wallet if the developer doesn't take
+// care of it. The wallet's SQL schema comes from the money contract
+// and here we just hardcode it. There should be a nice way to parse
+// the schema and fill some map.
+const MONEY_INFO_TABLE: &str = "money_info";
+const MONEY_INFO_COL_LAST_SCANNED_SLOT: &str = "last_scanned_slot";
+
+const MONEY_TREE_TABLE: &str = "money_tree";
+const MONEY_TREE_COL_TREE: &str = "tree";
+
+const MONEY_KEYS_TABLE: &str = "money_keys";
+const MONEY_KEYS_COL_KEY_ID: &str = "key_id";
+const MONEY_KEYS_COL_IS_DEFAULT: &str = "is_default";
+const MONEY_KEYS_COL_PUBLIC: &str = "public";
+const MONEY_KEYS_COL_SECRET: &str = "secret";
+
+const MONEY_COINS_TABLE: &str = "money_coins";
+const MONEY_COINS_COL_COIN: &str = "coin";
+const MONEY_COINS_COL_IS_SPENT: &str = "is_spent";
+const MONEY_COINS_COL_SERIAL: &str = "serial";
+const MONEY_COINS_COL_VALUE: &str = "value";
+const MONEY_COINS_COL_TOKEN_ID: &str = "token_id";
+const MONEY_COINS_COL_COIN_BLIND: &str = "coin_blind";
+const MONEY_COINS_COL_VALUE_BLIND: &str = "value_blind";
+const MONEY_COINS_COL_TOKEN_BLIND: &str = "token_blind";
+const MONEY_COINS_COL_SECRET: &str = "secret";
+const MONEY_COINS_COL_NULLIFIER: &str = "nullifier";
+const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
+const MONEY_COINS_COL_MEMO: &str = "memo";
+
 const CONFIG_FILE: &str = "faucetd_config.toml";
 const CONFIG_FILE: &str = "faucetd_config.toml";
 const CONFIG_FILE_CONTENTS: &str = include_str!("../faucetd_config.toml");
 const CONFIG_FILE_CONTENTS: &str = include_str!("../faucetd_config.toml");
 
 
@@ -141,11 +187,14 @@ struct Args {
 pub struct Faucetd {
 pub struct Faucetd {
     synced: Mutex<bool>, // AtomicBool is weird in Arc
     synced: Mutex<bool>, // AtomicBool is weird in Arc
     sync_p2p: P2pPtr,
     sync_p2p: P2pPtr,
-    client: Arc<Client>,
     validator_state: ValidatorStatePtr,
     validator_state: ValidatorStatePtr,
+    wallet: WalletPtr,
+    merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
     airdrop_timeout: i64,
     airdrop_timeout: i64,
     airdrop_limit: u64,
     airdrop_limit: u64,
-    airdrop_map: Arc<Mutex<HashMap<Address, i64>>>,
+    airdrop_map: Arc<Mutex<HashMap<[u8; 32], i64>>>,
+    proving_keys: Arc<RwLock<HashMap<[u8; 32], Vec<(String, ProvingKey)>>>>,
+    verifying_keys: Arc<RwLock<HashMap<[u8; 32], Vec<(String, VerifyingKey)>>>>,
 }
 }
 
 
 #[async_trait]
 #[async_trait]
@@ -168,20 +217,117 @@ impl Faucetd {
     pub async fn new(
     pub async fn new(
         validator_state: ValidatorStatePtr,
         validator_state: ValidatorStatePtr,
         sync_p2p: P2pPtr,
         sync_p2p: P2pPtr,
+        wallet: WalletPtr,
         timeout: i64,
         timeout: i64,
         limit: u64,
         limit: u64,
     ) -> Result<Self> {
     ) -> Result<Self> {
-        let client = validator_state.read().await.client.clone();
+        // Here we initialize the wallet for the money contract.
+        let merkle_tree = Self::initialize_wallet(wallet.clone()).await?;
+
+        // This is kinda bad, but whatever. The hashmaps hold proving and verifying
+        // keys for various zk circuits. They should be periodically pruned because
+        // of memory issues.
+        let proving_keys = Arc::new(RwLock::new(HashMap::new()));
+        let verifying_keys = Arc::new(RwLock::new(HashMap::new()));
+
+        // For now we'll create the keys for the money contract
+        // FIXME: This hardcoded shit (see consensus/state.rs)
+        let cid = ContractId::from(pallas::Base::from(u64::MAX - 420));
+        let zkas_tree = String::from("zkas");
+        let zkas_mint_ns = String::from("Mint");
+        let zkas_burn_ns = String::from("Burn");
+
+        // Do a lookup for the money contract's zkas database and fetch the circuits.
+        let blockchain = { validator_state.read().await.blockchain.clone() };
+        let db_handle = blockchain.contracts.lookup(&blockchain.sled_db, &cid, &zkas_tree)?;
+
+        // TODO: Handle possible panic of these Option unwraps
+        let mint_zkbin = db_handle.get(&serialize(&zkas_mint_ns))?.unwrap();
+        let burn_zkbin = db_handle.get(&serialize(&zkas_burn_ns))?.unwrap();
+
+        let mint_zkbin = ZkBinary::decode(&mint_zkbin)?;
+        let burn_zkbin = ZkBinary::decode(&burn_zkbin)?;
+
+        let k = 13;
+        let mint_witnesses = empty_witnesses(&mint_zkbin);
+        let burn_witnesses = empty_witnesses(&burn_zkbin);
+
+        let mint_circuit = ZkCircuit::new(mint_witnesses, mint_zkbin);
+        let burn_circuit = ZkCircuit::new(burn_witnesses, burn_zkbin);
+
+        info!("Creating mint circuit proving and verifying keys");
+        let mint_provingkey = ProvingKey::build(k, &mint_circuit);
+        let mint_verifyingkey = VerifyingKey::build(k, &mint_circuit);
+        info!("Creating burn circuit proving and verifying keys");
+        let burn_verifyingkey = VerifyingKey::build(k, &burn_circuit);
+        let burn_provingkey = ProvingKey::build(k, &burn_circuit);
+
+        {
+            let provingkeys = vec![
+                (zkas_mint_ns.clone(), mint_provingkey),
+                (zkas_burn_ns.clone(), burn_provingkey),
+            ];
+            let mut proving_keys_w = proving_keys.write().await;
+            proving_keys_w.insert(cid.inner().to_repr(), provingkeys);
+        }
+
+        {
+            let verifyingkeys = vec![
+                (zkas_mint_ns.clone(), mint_verifyingkey),
+                (zkas_burn_ns.clone(), burn_verifyingkey),
+            ];
+            let mut verifying_keys_w = verifying_keys.write().await;
+            verifying_keys_w.insert(cid.inner().to_repr(), verifyingkeys);
+        }
 
 
-        Ok(Self {
+        let faucetd = Self {
             synced: Mutex::new(false),
             synced: Mutex::new(false),
             sync_p2p,
             sync_p2p,
-            client,
             validator_state,
             validator_state,
+            wallet,
+            merkle_tree,
             airdrop_timeout: timeout,
             airdrop_timeout: timeout,
             airdrop_limit: limit,
             airdrop_limit: limit,
             airdrop_map: Arc::new(Mutex::new(HashMap::new())),
             airdrop_map: Arc::new(Mutex::new(HashMap::new())),
-        })
+            proving_keys,
+            verifying_keys,
+        };
+
+        Ok(faucetd)
+    }
+
+    async fn initialize_wallet(wallet: WalletPtr) -> Result<BridgeTree<MerkleNode, MERKLE_DEPTH>> {
+        // Perform wallet initialization for the money contract
+        let wallet_schema = include_str!("../../../src/contract/money/wallet.sql");
+
+        // Get a wallet connection
+        info!("Acquiring wallet connection");
+        let mut conn = wallet.conn.acquire().await?;
+
+        info!("Initializing wallet schema");
+        sqlx::query(wallet_schema).execute(&mut conn).await?;
+
+        let query = format!("SELECT * FROM {}", MONEY_TREE_COL_TREE);
+        let merkle_tree = match sqlx::query(&query).fetch_one(&mut conn).await {
+            Ok(t) => {
+                info!("Merkle tree already exists");
+                let tree = deserialize(t.get(MONEY_TREE_COL_TREE))?;
+                tree
+            }
+            Err(_) => {
+                let tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
+                let tree_bytes = serialize(&tree);
+                let query = format!(
+                    "DELETE FROM {}; INSERT INTO {} ({}) VALUES (?1)",
+                    MONEY_TREE_TABLE, MONEY_TREE_TABLE, MONEY_TREE_COL_TREE
+                );
+                sqlx::query(&query).bind(tree_bytes).execute(&mut conn).await?;
+                info!("Successfully initialized Merkle tree");
+                tree
+            }
+        };
+
+        Ok(merkle_tree)
     }
     }
 
 
     // RPCAPI:
     // RPCAPI:
@@ -204,18 +350,10 @@ impl Faucetd {
             return JsonError::new(InternalError, None, id).into()
             return JsonError::new(InternalError, None, id).into()
         }
         }
 
 
-        let address = match Address::from_str(params[0].as_str().unwrap()) {
-            Ok(v) => v,
-            Err(_) => {
-                error!("airdrop(): Failed parsing address from string");
-                return server_error(RpcError::ParseError, id)
-            }
-        };
-
-        let pubkey = match PublicKey::try_from(address) {
+        let pubkey = match PublicKey::from_str(params[0].as_str().unwrap()) {
             Ok(v) => v,
             Ok(v) => v,
-            Err(_) => {
-                error!("airdrop(): Failed parsing PublicKey from Address");
+            Err(e) => {
+                error!("airdrop(): Failed parsing PublicKey from String: {}", e);
                 return server_error(RpcError::ParseError, id)
                 return server_error(RpcError::ParseError, id)
             }
             }
         };
         };
@@ -246,13 +384,16 @@ impl Faucetd {
         // Check if there as a previous airdrop and the timeout has passed.
         // Check if there as a previous airdrop and the timeout has passed.
         let now = Utc::now().timestamp();
         let now = Utc::now().timestamp();
         let map = self.airdrop_map.lock().await;
         let map = self.airdrop_map.lock().await;
-        if let Some(last_airdrop) = map.get(&address) {
+        if let Some(last_airdrop) = map.get(&pubkey.to_bytes()) {
             if now - last_airdrop <= self.airdrop_timeout {
             if now - last_airdrop <= self.airdrop_timeout {
                 return server_error(RpcError::TimeLimitReached, id)
                 return server_error(RpcError::TimeLimitReached, id)
             }
             }
         };
         };
         drop(map);
         drop(map);
 
 
+        // Create airdrop transaction
+
+        /*
         let tx = match self
         let tx = match self
             .client
             .client
             .build_transaction(
             .build_transaction(
@@ -279,18 +420,20 @@ impl Faucetd {
                 return JsonError::new(InternalError, None, id).into()
                 return JsonError::new(InternalError, None, id).into()
             }
             }
         }
         }
+        */
 
 
         // Add/Update this airdrop into the hashmap
         // Add/Update this airdrop into the hashmap
         let mut map = self.airdrop_map.lock().await;
         let mut map = self.airdrop_map.lock().await;
-        map.insert(address, now);
+        map.insert(pubkey.to_bytes(), now);
         drop(map);
         drop(map);
 
 
-        let tx_hash = blake3::hash(&serialize(&tx)).to_hex().as_str().to_string();
+        //let tx_hash = blake3::hash(&serialize(&tx)).to_hex().as_str().to_string();
+        let tx_hash = "foo";
         JsonResponse::new(json!(tx_hash), id).into()
         JsonResponse::new(json!(tx_hash), id).into()
     }
     }
 }
 }
 
 
-async fn prune_airdrop_map(map: Arc<Mutex<HashMap<Address, i64>>>, timeout: i64) {
+async fn prune_airdrop_map(map: Arc<Mutex<HashMap<[u8; 32], i64>>>, timeout: i64) {
     loop {
     loop {
         sleep(timeout as u64).await;
         sleep(timeout as u64).await;
         debug!("Pruning airdrop map");
         debug!("Pruning airdrop map");
@@ -330,6 +473,7 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'_>>) -> Result<()> {
     let wallet = init_wallet(&args.wallet_path, &args.wallet_pass).await?;
     let wallet = init_wallet(&args.wallet_path, &args.wallet_pass).await?;
 
 
     // Initialize or open sled database
     // Initialize or open sled database
+    // TODO: Use proper OsPath here, not {}/{}
     let db_path = format!("{}/{}", expand_path(&args.database)?.to_str().unwrap(), args.chain);
     let db_path = format!("{}/{}", expand_path(&args.database)?.to_str().unwrap(), args.chain);
     let sled_db = sled::open(&db_path)?;
     let sled_db = sled::open(&db_path)?;
 
 
@@ -343,36 +487,23 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'_>>) -> Result<()> {
         }
         }
     };
     };
 
 
-    // TODO: sqldb init cleanup
-    // Initialize client
-    let client = Arc::new(Client::new(wallet.clone()).await?);
+    // Parse faucet addresses
+    let mut faucet_pubkeys = vec![];
 
 
-    // Parse cashier addresses
-    let mut cashier_pubkeys = vec![];
     for i in args.cashier_pub {
     for i in args.cashier_pub {
-        let addr = Address::from_str(&i)?;
-        let pk = PublicKey::try_from(addr)?;
-        cashier_pubkeys.push(pk);
+        let pk = PublicKey::from_str(&i)?;
+        faucet_pubkeys.push(pk);
     }
     }
 
 
-    // Parse faucet addresses
-    let mut faucet_pubkeys = vec![wallet.get_default_keypair().await?.public];
     for i in args.faucet_pub {
     for i in args.faucet_pub {
-        let addr = Address::from_str(&i)?;
-        let pk = PublicKey::try_from(addr)?;
+        let pk = PublicKey::from_str(&i)?;
         faucet_pubkeys.push(pk);
         faucet_pubkeys.push(pk);
     }
     }
 
 
     // Initialize validator state
     // Initialize validator state
-    let state = ValidatorState::new(
-        &sled_db,
-        genesis_ts,
-        genesis_data,
-        client,
-        cashier_pubkeys,
-        faucet_pubkeys,
-    )
-    .await?;
+    let state =
+        ValidatorState::new(&sled_db, genesis_ts, genesis_data, wallet.clone(), faucet_pubkeys)
+            .await?;
 
 
     // P2P network. The faucet doesn't participate in consensus, so we only
     // P2P network. The faucet doesn't participate in consensus, so we only
     // build the sync protocol.
     // build the sync protocol.
@@ -412,8 +543,14 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'_>>) -> Result<()> {
     let airdrop_limit = decode_base10(&args.airdrop_limit, 8, true)?;
     let airdrop_limit = decode_base10(&args.airdrop_limit, 8, true)?;
 
 
     // Initialize program state
     // Initialize program state
-    let faucetd =
-        Faucetd::new(state.clone(), sync_p2p.clone(), airdrop_timeout, airdrop_limit).await?;
+    let faucetd = Faucetd::new(
+        state.clone(),
+        sync_p2p.clone(),
+        wallet.clone(),
+        airdrop_timeout,
+        airdrop_limit,
+    )
+    .await?;
     let faucetd = Arc::new(faucetd);
     let faucetd = Arc::new(faucetd);
 
 
     // Task to periodically clean up the hashmap of airdrops.
     // Task to periodically clean up the hashmap of airdrops.