Przeglądaj źródła

faucetd: Code cleanup.

parazyd 3 lat temu
rodzic
commit
877c0bb2d5

+ 53 - 92
bin/faucetd/src/main.rs

@@ -27,12 +27,19 @@ use darkfi::{
     zk::{vm::ZkCircuit, vm_stack::empty_witnesses},
     zkas::ZkBinary,
 };
-use darkfi_money_contract::client::build_transfer_tx;
+use darkfi_money_contract::{
+    client::{
+        build_transfer_tx, MONEY_KEYS_COL_IS_DEFAULT, MONEY_KEYS_COL_PUBLIC, MONEY_KEYS_COL_SECRET,
+        MONEY_KEYS_TABLE, MONEY_TREE_COL_TREE, MONEY_TREE_TABLE,
+    },
+    ZKAS_BURN_NS, ZKAS_MINT_NS,
+};
 use darkfi_sdk::{
     crypto::{
         constants::MERKLE_DEPTH, schnorr::SchnorrSecret, ContractId, Keypair, MerkleNode,
         PublicKey, TokenId,
     },
+    db::ZKAS_DB_NAME,
     incrementalmerkletree::bridgetree::BridgeTree,
     pasta::{group::ff::PrimeField, pallas},
     tx::ContractCall,
@@ -73,40 +80,6 @@ use darkfi::{
 mod error;
 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_CONTENTS: &str = include_str!("../faucetd_config.toml");
 
@@ -194,14 +167,14 @@ struct Args {
 pub struct Faucetd {
     synced: Mutex<bool>, // AtomicBool is weird in Arc
     sync_p2p: P2pPtr,
-    validator_state: ValidatorStatePtr,
+    _validator_state: ValidatorStatePtr,
     keypair: Keypair,
     _wallet: WalletPtr,
     merkle_tree: BridgeTree<MerkleNode, MERKLE_DEPTH>,
     airdrop_timeout: i64,
     airdrop_limit: u64,
     airdrop_map: Arc<Mutex<HashMap<[u8; 32], i64>>>,
-    proving_keys: Arc<RwLock<HashMap<[u8; 32], Vec<(String, ProvingKey)>>>>,
+    proving_keys: Arc<RwLock<HashMap<[u8; 32], Vec<(String, ProvingKey, ZkBinary)>>>>,
 }
 
 #[async_trait]
@@ -232,33 +205,33 @@ impl Faucetd {
         let merkle_tree = Self::initialize_wallet(wallet.clone()).await?;
 
         // This is kinda bad, but whatever. The hashmaps hold proving keys for
-        // the money contract
+        // the money contract. We keep it under RwLock in case we want to add
+        // other proving keys to it later.
         let proving_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)
+        // FIXME: This shouldn't be hardcoded (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)?;
+        let db_handle = blockchain.contracts.lookup(&blockchain.sled_db, &cid, ZKAS_DB_NAME)?;
 
-        // 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 Some(mint_zkbin) = db_handle.get(&serialize(&ZKAS_MINT_NS))? else {
+            error!("{} zkas bincode not found in sled database", ZKAS_MINT_NS);
+            return Err(Error::ZkasBincodeNotFound);
+        };
+        let Some(burn_zkbin) = db_handle.get(&serialize(&ZKAS_BURN_NS))? else {
+            error!("{} zkas bincode not found in sled database", ZKAS_BURN_NS);
+            return Err(Error::ZkasBincodeNotFound);
+        };
 
         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);
+        let mint_circuit = ZkCircuit::new(empty_witnesses(&mint_zkbin), mint_zkbin.clone());
+        let burn_circuit = ZkCircuit::new(empty_witnesses(&burn_zkbin), burn_zkbin.clone());
 
         info!("Creating mint circuit proving key");
         let mint_provingkey = ProvingKey::build(k, &mint_circuit);
@@ -267,9 +240,10 @@ impl Faucetd {
 
         {
             let provingkeys = vec![
-                (zkas_mint_ns.clone(), mint_provingkey),
-                (zkas_burn_ns.clone(), burn_provingkey),
+                (ZKAS_MINT_NS.to_string(), mint_provingkey, mint_zkbin),
+                (ZKAS_BURN_NS.to_string(), burn_provingkey, burn_zkbin),
             ];
+
             let mut proving_keys_w = proving_keys.write().await;
             proving_keys_w.insert(cid.inner().to_repr(), provingkeys);
         }
@@ -281,7 +255,7 @@ impl Faucetd {
         let faucetd = Self {
             synced: Mutex::new(false),
             sync_p2p,
-            validator_state,
+            _validator_state: validator_state,
             keypair,
             _wallet: wallet,
             merkle_tree,
@@ -309,8 +283,7 @@ impl Faucetd {
         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
+                deserialize(t.get(MONEY_TREE_COL_TREE))?
             }
             Err(_) => {
                 let tree = BridgeTree::<MerkleNode, MERKLE_DEPTH>::new(100);
@@ -433,40 +406,27 @@ impl Faucetd {
         };
         drop(map);
 
-        // Get zk stuff for transaction
+        // FIXME: This hardcoded shit (see consensus/state.rs)
         let cid = ContractId::from(pallas::Base::from(u64::MAX - 420));
-        let (mint_zkbin, mint_pk, burn_zkbin, burn_pk) = {
-            // FIXME: This hardcoded shit (see consensus/state.rs)
-            // FIXME: Unwraps, should also be solved when above is solved.
-            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 = { self.validator_state.read().await.blockchain.clone() };
-            let db_handle =
-                blockchain.contracts.lookup(&blockchain.sled_db, &cid, &zkas_tree).unwrap();
 
-            // TODO: Handle possible panic of these Option unwraps
-            let mint_zkbin = db_handle.get(&serialize(&zkas_mint_ns)).unwrap().unwrap();
-            let burn_zkbin = db_handle.get(&serialize(&zkas_burn_ns)).unwrap().unwrap();
+        let (mint_zkbin, mint_pk, burn_zkbin, burn_pk) = {
+            let proving_keys_r = self.proving_keys.read().await;
+            let Some(arr) = proving_keys_r.get(&cid.to_bytes()) else {
+                error!("Contract ID {} not found in proving keys hashmap", cid);
+                return server_error(RpcError::InternalError, id)
+            };
 
-            let mint_zkbin = ZkBinary::decode(&mint_zkbin).unwrap();
-            let burn_zkbin = ZkBinary::decode(&burn_zkbin).unwrap();
+            let Some(mint_data) = arr.iter().find(|x| x.0 == ZKAS_MINT_NS) else {
+                error!("{} proof data not found in vector", ZKAS_MINT_NS);
+                return server_error(RpcError::InternalError, id)
+            };
 
-            let proving_keys_r = self.proving_keys.read().await;
-            let (mint_pk, burn_pk) = match proving_keys_r.get(&cid.inner().to_repr()) {
-                Some(arr) => {
-                    let mint_pk = arr.iter().find(|x| x.0 == zkas_mint_ns).unwrap();
-                    let burn_pk = arr.iter().find(|x| x.0 == zkas_burn_ns).unwrap();
-                    (mint_pk.1.clone(), burn_pk.1.clone())
-                }
-                None => {
-                    todo!("Create proving keys");
-                }
+            let Some(burn_data) = arr.iter().find(|x| x.0 == ZKAS_BURN_NS) else {
+                error!("{} prof data not found in vector", ZKAS_BURN_NS);
+                return server_error(RpcError::InternalError, id)
             };
 
-            (mint_zkbin, mint_pk, burn_zkbin, burn_pk)
+            (mint_data.2.clone(), mint_data.1.clone(), burn_data.2.clone(), burn_data.1.clone())
         };
 
         // Create money contract params and proofs
@@ -475,7 +435,7 @@ impl Faucetd {
             &pubkey,
             amount,
             token_id,
-            &[],
+            &[], // <-- The faucet doesn't really have to pass OwnCoins I think
             &self.merkle_tree,
             &mint_zkbin,
             &mint_pk,
@@ -505,13 +465,10 @@ impl Faucetd {
         let tx = Transaction { calls, proofs, signatures: vec![signatures] };
 
         // Broadcast transaction to the network.
-        match self.sync_p2p.broadcast(tx.clone()).await {
-            Ok(()) => {}
-            Err(e) => {
-                error!("airdrop(): Failed broadcasting transaction: {}", e);
-                return JsonError::new(InternalError, None, id).into()
-            }
-        }
+        if let Err(e) = self.sync_p2p.broadcast(tx.clone()).await {
+            error!("airdrop(): Failed broadcasting transaction: {}", e);
+            return JsonError::new(InternalError, None, id).into()
+        };
 
         // Add/Update this airdrop into the hashmap
         let mut map = self.airdrop_map.lock().await;
@@ -684,5 +641,9 @@ async fn realmain(args: Args, ex: Arc<smol::Executor<'_>>) -> Result<()> {
     let flushed_bytes = sled_db.flush_async().await?;
     info!("Flushed {} bytes", flushed_bytes);
 
+    info!("Closing wallet connection...");
+    wallet.conn.close().await;
+    info!("Closed wallet connection");
+
     Ok(())
 }

+ 26 - 1
src/contract/money/src/client.rs

@@ -54,10 +54,35 @@ use rand::rngs::OsRng;
 
 use crate::state::{ClearInput, Input, MoneyTransferParams, Output};
 
-// Wallet SQL table constant names
+// Wallet SQL table constant names. These have to represent the SQL schema.
+// TODO: They should also ideally be prefixed with the contract ID to avoid
+//       collisions.
+pub const MONEY_INFO_TABLE: &str = "money_info";
+pub const MONEY_INFO_COL_LAST_SCANNED_SLOT: &str = "last_scanned_slot";
+
 pub const MONEY_TREE_TABLE: &str = "money_tree";
 pub const MONEY_TREE_COL_TREE: &str = "tree";
 
+pub const MONEY_KEYS_TABLE: &str = "money_keys";
+pub const MONEY_KEYS_COL_KEY_ID: &str = "key_id";
+pub const MONEY_KEYS_COL_IS_DEFAULT: &str = "is_default";
+pub const MONEY_KEYS_COL_PUBLIC: &str = "public";
+pub const MONEY_KEYS_COL_SECRET: &str = "secret";
+
+pub const MONEY_COINS_TABLE: &str = "money_coins";
+pub const MONEY_COINS_COL_COIN: &str = "coin";
+pub const MONEY_COINS_COL_IS_SPENT: &str = "is_spent";
+pub const MONEY_COINS_COL_SERIAL: &str = "serial";
+pub const MONEY_COINS_COL_VALUE: &str = "value";
+pub const MONEY_COINS_COL_TOKEN_ID: &str = "token_id";
+pub const MONEY_COINS_COL_COIN_BLIND: &str = "coin_blind";
+pub const MONEY_COINS_COL_VALUE_BLIND: &str = "value_blind";
+pub const MONEY_COINS_COL_TOKEN_BLIND: &str = "token_blind";
+pub const MONEY_COINS_COL_SECRET: &str = "secret";
+pub const MONEY_COINS_COL_NULLIFIER: &str = "nullifier";
+pub const MONEY_COINS_COL_LEAF_POSITION: &str = "leaf_position";
+pub const MONEY_COINS_COL_MEMO: &str = "memo";
+
 /// Byte length of the AEAD tag of the chacha20 cipher used for note encryption
 pub const AEAD_TAG_SIZE: usize = 16;
 

+ 9 - 0
src/contract/money/src/lib.rs

@@ -16,6 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
+#[cfg(not(feature = "no-entrypoint"))]
 use darkfi_sdk::{
     crypto::{Coin, ContractId, MerkleNode, MerkleTree, PublicKey},
     db::{db_contains_key, db_get, db_init, db_lookup, db_set, ZKAS_DB_NAME},
@@ -26,6 +27,8 @@ use darkfi_sdk::{
     tx::ContractCall,
     util::set_return_data,
 };
+
+#[cfg(not(feature = "no-entrypoint"))]
 use darkfi_serial::{deserialize, serialize, Encodable, WriteExt};
 
 /// Functions we allow in this contract
@@ -45,6 +48,8 @@ impl From<u8> for MoneyFunction {
 
 /// Structures and object definitions
 pub mod state;
+
+#[cfg(not(feature = "no-entrypoint"))]
 use state::{MoneyTransferParams, MoneyTransferUpdate};
 
 #[cfg(feature = "client")]
@@ -74,6 +79,7 @@ pub const ZKAS_MINT_NS: &str = "Mint";
 pub const ZKAS_BURN_NS: &str = "Burn";
 
 /// This function runs when the contract is (re)deployed and initialized.
+#[cfg(not(feature = "no-entrypoint"))]
 fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
     // The payload for now contains a vector of `PublicKey` used to
     // whitelist faucets that can create clear inputs.
@@ -140,6 +146,7 @@ fn init_contract(cid: ContractId, ix: &[u8]) -> ContractResult {
 
 /// This function is used by the VM's host to fetch the necessary metadata for
 /// verifying signatures and zk proofs.
+#[cfg(not(feature = "no-entrypoint"))]
 fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
     let (call_idx, call): (u32, Vec<ContractCall>) = deserialize(ix)?;
     assert!(call_idx < call.len() as u32);
@@ -211,6 +218,7 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
 
 /// This function verifies a state transition and produces an
 /// update if everything is successful.
+#[cfg(not(feature = "no-entrypoint"))]
 fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
     let (call_idx, call): (u32, Vec<ContractCall>) = deserialize(ix)?;
     assert!(call_idx < call.len() as u32);
@@ -294,6 +302,7 @@ fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
     }
 }
 
+#[cfg(not(feature = "no-entrypoint"))]
 fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
     match MoneyFunction::from(update_data[0]) {
         MoneyFunction::Transfer => {

+ 3 - 0
src/error.rs

@@ -285,6 +285,9 @@ pub enum Error {
     #[error("Contract already initialized")]
     ContractAlreadyInitialized,
 
+    #[error("zkas bincode not found in sled database")]
+    ZkasBincodeNotFound,
+
     // =============
     // Wallet errors
     // =============

+ 11 - 0
src/serial/src/lib.rs

@@ -516,6 +516,17 @@ impl Encodable for String {
     }
 }
 
+impl Encodable for &str {
+    #[inline]
+    fn encode<S: Write>(&self, mut s: S) -> Result<usize, Error> {
+        let b = self.as_bytes();
+        let b_len = b.len();
+        let vi_len = VarInt(b_len as u64).encode(&mut s)?;
+        s.write_slice(b)?;
+        Ok(vi_len + b_len)
+    }
+}
+
 impl Decodable for String {
     #[inline]
     fn decode<D: Read>(d: D) -> Result<String, Error> {