Преглед изворни кода

blockchain: Add skeleton for ContractStore.

parazyd пре 3 година
родитељ
комит
8be771af04
4 измењених фајлова са 140 додато и 4 уклоњено
  1. 98 0
      src/blockchain/contractstore.rs
  2. 20 3
      src/blockchain/mod.rs
  3. 13 1
      src/crypto/contract_id.rs
  4. 9 0
      src/error.rs

+ 98 - 0
src/blockchain/contractstore.rs

@@ -0,0 +1,98 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2022 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 darkfi_serial::deserialize;
+
+use crate::{
+    crypto::contract_id::ContractId,
+    Error::{ContractAlreadyInitialized, ContractNotFound, ContractStateNotFound},
+    Result,
+};
+
+#[derive(Clone)]
+pub struct ContractStore(sled::Tree);
+
+const SLED_CONTRACTS_TREE: &[u8] = b"_contracts";
+
+impl ContractStore {
+    pub fn new(db: &sled::Db) -> Result<Self> {
+        let tree = db.open_tree(SLED_CONTRACTS_TREE)?;
+        Ok(Self(tree))
+    }
+
+    pub fn init(
+        &self,
+        db: &sled::Db,
+        contract_id: &ContractId,
+        tree_name: &str,
+    ) -> Result<sled::Tree> {
+        let contract_id_bytes = contract_id.to_bytes();
+
+        // If the db was never initialized, it should not be in here.
+        if self.0.contains_key(&contract_id_bytes)? {
+            return Err(ContractAlreadyInitialized)
+        }
+
+        let mut hasher = blake3::Hasher::new();
+        hasher.update(&contract_id_bytes);
+        hasher.update(&tree_name.as_bytes());
+        let ptr = hasher.finalize();
+
+        // Now we add it so it's marked as initialized
+        self.0.insert(&contract_id_bytes, ptr.as_bytes())?;
+
+        // We open the tree and return its handle
+        let tree = db.open_tree(ptr.as_bytes())?;
+        Ok(tree)
+    }
+
+    pub fn lookup(
+        &self,
+        db: &sled::Db,
+        contract_id: &ContractId,
+        tree_name: &str,
+    ) -> Result<sled::Tree> {
+        let contract_id_bytes = contract_id.to_bytes();
+
+        // A guard to make sure we went through init()
+        if !self.0.contains_key(&contract_id_bytes)? {
+            return Err(ContractNotFound(contract_id.to_string()))
+        }
+
+        let Some(state_pointers) = self.0.get(&contract_id_bytes)? else {
+            return Err(ContractNotFound(contract_id.to_string()))
+        };
+
+        let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
+
+        let mut hasher = blake3::Hasher::new();
+        hasher.update(&contract_id_bytes);
+        hasher.update(&tree_name.as_bytes());
+        let ptr = hasher.finalize();
+
+        // We assume the tree has been created already, so it should be listed in this array.
+        // If not, that's an error.
+        if !state_pointers.contains(ptr.as_bytes()) {
+            return Err(ContractStateNotFound)
+        }
+
+        // We open the tree and return its handle
+        let tree = db.open_tree(ptr.as_bytes())?;
+        Ok(tree)
+    }
+}

+ 20 - 3
src/blockchain/mod.rs

@@ -40,8 +40,13 @@ pub use statestore::StateStore;
 pub mod txstore;
 pub use txstore::TxStore;
 
-/// Structure holding all sled trees that comprise the concept of Blockchain.
+pub mod contractstore;
+pub use contractstore::ContractStore;
+
+/// Structure holding all sled trees that define the concept of Blockchain.
 pub struct Blockchain {
+    /// Main pointer to the sled db connection
+    pub sled_db: sled::Db,
     /// Headers sled tree
     pub headers: HeaderStore,
     /// Blocks sled tree
@@ -54,6 +59,8 @@ pub struct Blockchain {
     pub nullifiers: NullifierStore,
     /// Merkle roots sled tree
     pub merkle_roots: RootStore,
+    /// Contract states
+    pub contracts: ContractStore,
 }
 
 impl Blockchain {
@@ -67,8 +74,18 @@ impl Blockchain {
         let transactions = TxStore::new(db)?;
         let nullifiers = NullifierStore::new(db)?;
         let merkle_roots = RootStore::new(db)?;
-
-        Ok(Self { headers, blocks, order, transactions, nullifiers, merkle_roots })
+        let contracts = ContractStore::new(db)?;
+
+        Ok(Self {
+            sled_db: db.clone(),
+            headers,
+            blocks,
+            order,
+            transactions,
+            nullifiers,
+            merkle_roots,
+            contracts,
+        })
     }
 
     /// Insert a given slice of [`BlockInfo`] into the blockchain database.

+ 13 - 1
src/crypto/contract_id.rs

@@ -17,7 +17,7 @@
  */
 
 use darkfi_serial::{SerialDecodable, SerialEncodable};
-use pasta_curves::pallas;
+use pasta_curves::{group::ff::PrimeField, pallas};
 
 use super::{
     keypair::{PublicKey, SecretKey},
@@ -37,6 +37,18 @@ impl ContractId {
     pub fn inner(&self) -> pallas::Base {
         self.0
     }
+
+    pub fn to_bytes(&self) -> [u8; 32] {
+        self.0.to_repr()
+    }
+}
+
+impl std::fmt::Display for ContractId {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        // base58 encoding
+        let contractid: String = bs58::encode(self.0.to_repr()).into_string();
+        write!(f, "{}", contractid)
+    }
 }
 
 /// Derive a ContractId given a secret deploy key.

+ 9 - 0
src/error.rs

@@ -255,6 +255,15 @@ pub enum Error {
     #[error("Block {0} metadata not found in database")]
     BlockMetadataNotFound(String),
 
+    #[error("Contract {0} not found in database")]
+    ContractNotFound(String),
+
+    #[error("Contract state tree not found")]
+    ContractStateNotFound,
+
+    #[error("Contract already initialized")]
+    ContractAlreadyInitialized,
+
     // =============
     // Wallet errors
     // =============