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

src/validator: set foundation for validator rewrite

aggstam 3 лет назад
Родитель
Сommit
dd3d6a15e7

+ 1 - 1
src/consensus/validator.rs

@@ -1105,7 +1105,7 @@ impl ValidatorState {
         };
 
         info!(target: "consensus::validator", "Verifying ZK proofs for transaction {}", tx_hash);
-        match tx.verify_zkps(verifying_keys.clone(), zkp_table).await {
+        match tx.verify_zkps(&verifying_keys, zkp_table).await {
             Ok(()) => {
                 info!(target: "consensus::validator", "ZK proof verification for tx {} successful", tx_hash)
             }

+ 19 - 51
src/error.rs

@@ -22,9 +22,6 @@
 /// Main result type used throughout the codebase.
 pub type Result<T> = std::result::Result<T, Error>;
 
-/// Result type used in transaction verifications
-pub type VerifyResult<T> = std::result::Result<T, VerifyFailed>;
-
 /// Result type used in the Client module
 pub type ClientResult<T> = std::result::Result<T, ClientFailed>;
 
@@ -297,6 +294,9 @@ pub enum Error {
     #[error("Transaction {0} not found in database")]
     TransactionNotFound(String),
 
+    #[error("Transactioon already seen")]
+    TransactionAlreadySeen,
+
     #[error("Header {0} not found in database")]
     HeaderNotFound(String),
 
@@ -422,10 +422,10 @@ pub enum Error {
     // Wrappers for other error types in this library
     // ==============================================
     #[error(transparent)]
-    VerifyFailed(#[from] VerifyFailed),
+    ClientFailed(#[from] ClientFailed),
 
     #[error(transparent)]
-    ClientFailed(#[from] ClientFailed),
+    TxVerifyFailed(#[from] TxVerifyFailed),
 
     //=============
     // clock
@@ -451,47 +451,27 @@ pub enum Error {
     Custom(String),
 }
 
+#[cfg(feature = "tx")]
 /// Transaction verification errors
 #[derive(Debug, Clone, thiserror::Error)]
-pub enum VerifyFailed {
-    #[error("Transaction has no inputs")]
-    LackingInputs,
-
-    #[error("Transaction has no outputs")]
-    LackingOutputs,
-
-    #[error("Invalid cashier/faucet public key for clear input {0}")]
-    InvalidCashierOrFaucetKey(usize),
-
-    #[error("Invalid Merkle root for input {0}")]
-    InvalidMerkle(usize),
-
-    #[error("Nullifier already exists for input {0}")]
-    NullifierExists(usize),
-
-    #[error("Invalid signature for input {0}")]
-    InputSignature(usize),
-
-    #[error("Invalid signature for clear input {0}")]
-    ClearInputSignature(usize),
-
-    #[error("Token commitments in inputs or outputs to not match")]
-    TokenMismatch,
+pub enum TxVerifyFailed {
+    #[error("Invalid transaction signature")]
+    InvalidSignature,
 
-    #[error("Money in does not match money out (value commitments)")]
-    MissingFunds,
+    #[error("Missing signatures in transaction")]
+    MissingSignatures,
 
-    #[error("Mint proof verification failure for input {0}")]
-    MintProof(usize),
+    #[error("Missing contract calls in transaction")]
+    MissingCalls,
 
-    #[error("Burn proof verification failure for input {0}")]
-    BurnProof(usize),
+    #[error("Missing Money::Fee call in transaction")]
+    MissingFee,
 
-    #[error("Failed verifying zk proofs: {0}")]
-    ProofVerifyFailed(String),
+    #[error("Invalid ZK proof in transaction")]
+    InvalidZkProof,
 
-    #[error("Internal error: {0}")]
-    InternalError(String),
+    #[error("Erroneous transactions found")]
+    ErroneousTxs(Vec<crate::tx::Transaction>),
 }
 
 /// Client module errors
@@ -519,24 +499,12 @@ pub enum ClientFailed {
     VerifyError(String),
 }
 
-impl From<Error> for VerifyFailed {
-    fn from(err: Error) -> Self {
-        Self::InternalError(err.to_string())
-    }
-}
-
 impl From<Error> for ClientFailed {
     fn from(err: Error) -> Self {
         Self::InternalError(err.to_string())
     }
 }
 
-impl From<VerifyFailed> for ClientFailed {
-    fn from(err: VerifyFailed) -> Self {
-        Self::VerifyError(err.to_string())
-    }
-}
-
 impl From<std::io::Error> for ClientFailed {
     fn from(err: std::io::Error) -> Self {
         Self::Io(err.kind())

+ 4 - 1
src/lib.rs

@@ -17,7 +17,7 @@
  */
 
 pub mod error;
-pub use error::{ClientFailed, ClientResult, Error, Result, VerifyFailed, VerifyResult};
+pub use error::{ClientFailed, ClientResult, Error, Result, TxVerifyFailed};
 
 #[cfg(feature = "blockchain")]
 pub mod blockchain;
@@ -25,6 +25,9 @@ pub mod blockchain;
 #[cfg(feature = "blockchain")]
 pub mod consensus;
 
+#[cfg(feature = "blockchain")]
+pub mod validator;
+
 #[cfg(feature = "dht")]
 pub mod dht;
 #[cfg(feature = "dht")]

+ 5 - 5
src/tx/mod.rs

@@ -32,7 +32,7 @@ use rand::{CryptoRng, RngCore};
 
 use crate::{
     zk::{proof::VerifyingKey, Proof},
-    Error, Result, VerifyFailed,
+    Error, Result, TxVerifyFailed,
 };
 
 macro_rules! zip {
@@ -60,7 +60,7 @@ impl Transaction {
     /// Verify ZK proofs for the entire transaction.
     pub async fn verify_zkps(
         &self,
-        verifying_keys: HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
+        verifying_keys: &HashMap<[u8; 32], HashMap<String, VerifyingKey>>,
         zkp_table: Vec<Vec<(String, Vec<pallas::Base>)>>,
     ) -> Result<()> {
         // TODO: Are we sure we should assert here?
@@ -72,7 +72,7 @@ impl Transaction {
 
             let Some(contract_map) = verifying_keys.get(&call.contract_id.to_bytes()) else {
                 error!("Verifying keys not found for contract {}", call.contract_id);
-                return Err(VerifyFailed::ProofVerifyFailed("VKs not found for contract".to_string()).into())
+                return Err(TxVerifyFailed::InvalidZkProof.into())
             };
 
             for (proof, (zk_ns, public_vals)) in proofs.iter().zip(pubvals.iter()) {
@@ -85,7 +85,7 @@ impl Transaction {
                             "Failed verifying {}::{} ZK proof: {:#?}",
                             call.contract_id, zk_ns, e
                         );
-                        return Err(VerifyFailed::ProofVerifyFailed(e.to_string()).into())
+                        return Err(TxVerifyFailed::InvalidZkProof.into())
                     }
                     debug!("Successfully verified {}::{} ZK proof", call.contract_id, zk_ns);
                     continue
@@ -93,7 +93,7 @@ impl Transaction {
 
                 let e = format!("{}:{} circuit VK nonexistent", call.contract_id, zk_ns);
                 error!("{}", e);
-                return Err(VerifyFailed::ProofVerifyFailed(e).into())
+                return Err(TxVerifyFailed::InvalidZkProof.into())
             }
         }
 

+ 38 - 0
src/validator/consensus/consensus.rs

@@ -0,0 +1,38 @@
+/* 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 crate::{
+    blockchain::Blockchain,
+    consensus::constants::{EPOCH_LENGTH, SLOT_TIME},
+    util::time::{TimeKeeper, Timestamp},
+};
+
+/// This struct represents the information required by the consensus algorithm
+pub struct Consensus {
+    /// Canonical (finalized) blockchain
+    pub blockchain: Blockchain,
+    /// Helper structure to calculate time related operations
+    pub time_keeper: TimeKeeper,
+}
+
+impl Consensus {
+    pub fn new(blockchain: Blockchain, genesis_ts: Timestamp) -> Self {
+        let time_keeper = TimeKeeper::new(genesis_ts, EPOCH_LENGTH as u64, SLOT_TIME, 0);
+        Self { blockchain, time_keeper }
+    }
+}

+ 21 - 0
src/validator/consensus/mod.rs

@@ -0,0 +1,21 @@
+/* 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/>.
+ */
+
+/// DarkFi consensus
+pub mod consensus;
+pub use consensus::Consensus;

+ 24 - 0
src/validator/mod.rs

@@ -0,0 +1,24 @@
+/* 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/>.
+ */
+
+/// DarkFi validator
+pub mod validator;
+pub use validator::{Validator, ValidatorPtr};
+
+/// DarkFi consensus
+pub mod consensus;

+ 140 - 0
src/validator/validator.rs

@@ -0,0 +1,140 @@
+/* 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 async_std::sync::{Arc, RwLock};
+use darkfi_sdk::crypto::{PublicKey, CONSENSUS_CONTRACT_ID, DAO_CONTRACT_ID, MONEY_CONTRACT_ID};
+use darkfi_serial::serialize;
+use log::info;
+
+use crate::{
+    blockchain::{Blockchain, BlockchainOverlay},
+    runtime::vm_runtime::Runtime,
+    util::time::Timestamp,
+    Result,
+};
+
+use super::consensus::Consensus;
+
+/// Atomic pointer to validator.
+pub type ValidatorPtr = Arc<RwLock<Validator>>;
+
+/// This struct represents a DarkFi validator node.
+pub struct Validator {
+    /// Canonical (finalized) blockchain
+    pub blockchain: Blockchain,
+    /// Hot/Live data used by the consensus algorithm
+    pub consensus: Consensus,
+}
+
+/// Configuration for initializing [`Validator`]
+pub struct ValidatorConfig {
+    /// Genesis timestamp
+    pub genesis_ts: Timestamp,
+    /// Genesis data
+    pub genesis_data: blake3::Hash,
+    /// Whitelisted faucet pubkeys (testnet stuff)
+    pub faucet_pubkeys: Vec<PublicKey>,
+}
+
+impl Validator {
+    pub async fn new(db: &sled::Db, config: ValidatorConfig) -> Result<ValidatorPtr> {
+        info!(target: "consensus::validator", "Initializing Validator");
+
+        info!(target: "consensus::validator", "Initializing Blockchain");
+        // TODO: Initialize chain, then check if its empty, so we can execute
+        // the transactions of the genesis block
+        let blockchain = Blockchain::new(db, config.genesis_ts, config.genesis_data)?;
+
+        info!(target: "consensus::validator", "Initializing Consensus");
+        let consensus = Consensus::new(blockchain.clone(), config.genesis_ts);
+
+        // =====================
+        // NATIVE WASM CONTRACTS
+        // =====================
+        // This is the current place where native contracts are being deployed.
+        // When the `Blockchain` object is created, it doesn't care whether it
+        // already has the contract data or not. If there's existing data, it
+        // will just open the necessary db and trees, and give back what it has.
+        // This means that on subsequent runs our native contracts will already
+        // be in a deployed state, so what we actually do here is a redeployment.
+        // This kind of operation should only modify the contract's state in case
+        // it wasn't deployed before (meaning the initial run). Otherwise, it
+        // shouldn't touch anything, or just potentially update the db schemas or
+        // whatever is necessary. This logic should be handled in the init function
+        // of the actual contract, so make sure the native contracts handle this well.
+
+        // The faucet pubkeys are pubkeys which are allowed to create clear inputs
+        // in the Money contract.
+        let money_contract_deploy_payload = serialize(&config.faucet_pubkeys);
+
+        // The DAO contract uses an empty payload to deploy itself.
+        let dao_contract_deploy_payload = vec![];
+
+        // The Consensus contract uses an empty payload to deploy itself.
+        let consensus_contract_deploy_payload = vec![];
+
+        let native_contracts = vec![
+            (
+                "Money Contract",
+                *MONEY_CONTRACT_ID,
+                include_bytes!("../contract/money/money_contract.wasm").to_vec(),
+                money_contract_deploy_payload,
+            ),
+            (
+                "DAO Contract",
+                *DAO_CONTRACT_ID,
+                include_bytes!("../contract/dao/dao_contract.wasm").to_vec(),
+                dao_contract_deploy_payload,
+            ),
+            (
+                "Consensus Contract",
+                *CONSENSUS_CONTRACT_ID,
+                include_bytes!("../contract/consensus/consensus_contract.wasm").to_vec(),
+                consensus_contract_deploy_payload,
+            ),
+        ];
+
+        info!(target: "consensus::validator", "Deploying native WASM contracts");
+        let blockchain_overlay = BlockchainOverlay::new(&blockchain)?;
+
+        for nc in native_contracts {
+            info!(target: "consensus::validator", "Deploying {} with ContractID {}", nc.0, nc.1);
+
+            let mut runtime = Runtime::new(
+                &nc.2[..],
+                blockchain_overlay.clone(),
+                nc.1,
+                consensus.time_keeper.clone(),
+            )?;
+
+            runtime.deploy(&nc.3)?;
+
+            info!(target: "consensus::validator", "Successfully deployed {}", nc.0);
+        }
+
+        // Write the changes to the actual chain db
+        blockchain_overlay.lock().unwrap().overlay.lock().unwrap().apply()?;
+
+        info!(target: "consensus::validator", "Finished deployment of native WASM contracts");
+
+        // Create the actual state
+        let state = Arc::new(RwLock::new(Self { blockchain, consensus }));
+
+        Ok(state)
+    }
+}