diff --git a/src/blockchain/contractstore.rs b/src/blockchain/contractstore.rs index 26c447174..d5b0c50ad 100644 --- a/src/blockchain/contractstore.rs +++ b/src/blockchain/contractstore.rs @@ -7,7 +7,7 @@ * 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, +r* 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. @@ -18,28 +18,64 @@ use darkfi_sdk::crypto::ContractId; use darkfi_serial::{deserialize, serialize}; -use log::debug; +use log::{debug, error}; -use crate::{ - Error::{ContractAlreadyInitialized, ContractNotFound, ContractStateNotFound}, - Result, -}; - -/// The `ContractStore` is a `sled` tree that stores pointers to contracts' -/// databases. See the rustdoc for the impl functions for more info. -#[derive(Clone)] -pub struct ContractStore(sled::Tree); +use crate::{Error, Result}; const SLED_CONTRACTS_TREE: &[u8] = b"_contracts"; +const SLED_BINCODE_TREE: &[u8] = b"_wasm_bincode"; // Logger targets -const TGT_INIT: &str = "blockchain::contractstore::init"; -const TGT_LKUP: &str = "blockchain::contractstore::lookup"; -const TGT_DROP: &str = "blockchain::contractstore::remove"; +const CS_TGT_INIT: &str = "blockchain::contractstatestore::init"; +const CS_TGT_LKUP: &str = "blockchain::contractstatestore::lookup"; +const CS_TGT_DROP: &str = "blockchain::contractstatestore::remove"; -impl ContractStore { - /// Opens or creates a `ContractStore`. This main tree holds the links - /// of contracts' states, +/// The `WasmStore` is a `sled` tree that stores the wasm bincode for deployed +/// contracts. +#[derive(Clone)] +pub struct WasmStore(sled::Tree); + +impl WasmStore { + /// Opens or creates a `WasmStore`. This tree holds the wasm bincode. + /// The layout looks like this: + /// ```plaintext + /// tree: "_wasm_bincode" + /// key: ContractId + /// value: Vec + pub fn new(db: &sled::Db) -> Result { + let tree = db.open_tree(SLED_BINCODE_TREE)?; + Ok(Self(tree)) + } + + /// Fetches the bincode for a given ContractId + /// Returns an error if the bincode is not found. + pub fn get(&self, contract_id: ContractId) -> Result> { + if let Some(bincode) = self.0.get(&serialize(&contract_id))? { + return Ok(bincode.to_vec()) + } + + Err(Error::WasmBincodeNotFound) + } + + /// Inserts or replaces the bincode for a given ContractId + pub fn insert(&self, contract_id: ContractId, bincode: &[u8]) -> Result<()> { + if let Err(e) = self.0.insert(&serialize(&contract_id), bincode) { + error!("Failed to insert bincode to WasmStore: {}", e); + return Err(e.into()) + } + + Ok(()) + } +} + +/// The `ContractStateStore` is a `sled` tree that stores pointers to contracts' +/// databases. See the rustdoc for the impl functions for more info. +#[derive(Clone)] +pub struct ContractStateStore(sled::Tree); + +impl ContractStateStore { + /// Opens or creates a `ContractStateStore`. This main tree holds the links + /// of contracts' states. /// The layout looks like this: /// ```plaintext /// tree: "_contracts" @@ -54,20 +90,21 @@ impl ContractStore { /// Try to initialize a new contract state. Contracts can create a number /// of trees, separated by `tree_name`, which they can then use from the - /// smart contract API. `init()` will look into the main `ContractStore` + /// smart contract API. `init()` will look into the main `ContractStateStore` /// tree to check if the smart contract was already deployed, and if so /// it will fetch a vector of these states that were initialized. If the /// state was already found, this function will return an error, because /// in this case the handle should be fetched using `lookup()`. /// If the tree was not initialized previously, it will be appended to - /// the main `ContractStore` tree and a `sled::Tree` handle will be returned. + /// the main `ContractStateStore` tree and a `sled::Tree` handle will be + /// returned. pub fn init( &self, db: &sled::Db, contract_id: &ContractId, tree_name: &str, ) -> Result { - debug!(target: TGT_INIT, "Initializing state tree for {}:{}", contract_id, tree_name); + debug!(target: CS_TGT_INIT, "Initializing state tree for {}:{}", contract_id, tree_name); let contract_id_bytes = serialize(contract_id); let ptr = contract_id.hash_state_id(tree_name); @@ -83,7 +120,7 @@ impl ContractStore { // If the db was never initialized, it should not be in here. if state_pointers.contains(&ptr) { - return Err(ContractAlreadyInitialized) + return Err(Error::ContractAlreadyInitialized) } // Now we add it so it's marked as initialized @@ -113,14 +150,14 @@ impl ContractStore { contract_id: &ContractId, tree_name: &str, ) -> Result { - debug!(target: TGT_LKUP, "Looking up state tree for {}:{}", contract_id, tree_name); + debug!(target: CS_TGT_LKUP, "Looking up state tree for {}:{}", contract_id, tree_name); let contract_id_bytes = serialize(contract_id); let ptr = contract_id.hash_state_id(tree_name); // A guard to make sure we went through init() if !self.0.contains_key(&contract_id_bytes)? { - return Err(ContractNotFound(contract_id.to_string())) + return Err(Error::ContractNotFound(contract_id.to_string())) } let state_pointers = self.0.get(&contract_id_bytes)?.unwrap(); @@ -129,7 +166,7 @@ impl ContractStore { // 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) { - return Err(ContractStateNotFound) + return Err(Error::ContractStateNotFound) } // We open the tree and return its handle @@ -140,17 +177,17 @@ impl ContractStore { /// Attempt to remove an existing contract state. In order to succeed, the /// state must have been previously initialized with `init()`. If the state /// has been found, its contents in the tree will be cleared, and the pointer - /// will be removed from the main `ContractStore`. If anything is not found - /// as initialized, an error is returned. + /// will be removed from the main `ContractStateStore`. If anything is not + /// found as initialized, an error is returned. pub fn remove(&self, db: &sled::Db, contract_id: &ContractId, tree_name: &str) -> Result<()> { - debug!(target: TGT_DROP, "Removing state tree for {}:{}", contract_id, tree_name); + debug!(target: CS_TGT_DROP, "Removing state tree for {}:{}", contract_id, tree_name); let contract_id_bytes = serialize(contract_id); let ptr = contract_id.hash_state_id(tree_name); // A guard to make sure we went through init() if !self.0.contains_key(&contract_id_bytes)? { - return Err(ContractNotFound(contract_id.to_string())) + return Err(Error::ContractNotFound(contract_id.to_string())) } let state_pointers = self.0.get(&contract_id_bytes)?.unwrap(); @@ -159,7 +196,7 @@ impl ContractStore { // 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) { - return Err(ContractStateNotFound) + return Err(Error::ContractStateNotFound) } // We open the tree and clear it. This is unfortunately not atomic. diff --git a/src/blockchain/mod.rs b/src/blockchain/mod.rs index 816be5992..3617960e1 100644 --- a/src/blockchain/mod.rs +++ b/src/blockchain/mod.rs @@ -38,7 +38,7 @@ pub mod txstore; pub use txstore::TxStore; pub mod contractstore; -pub use contractstore::ContractStore; +pub use contractstore::{ContractStateStore, WasmStore}; /// Structure holding all sled trees that define the concept of Blockchain. #[derive(Clone)] @@ -58,7 +58,9 @@ pub struct Blockchain { /// Merkle roots sled tree pub merkle_roots: RootStore, /// Contract states - pub contracts: ContractStore, + pub contracts: ContractStateStore, + /// Wasm bincodes + pub wasm_bincode: WasmStore, } impl Blockchain { @@ -70,7 +72,8 @@ impl Blockchain { let transactions = TxStore::new(db)?; let nullifiers = NullifierStore::new(db)?; let merkle_roots = RootStore::new(db)?; - let contracts = ContractStore::new(db)?; + let contracts = ContractStateStore::new(db)?; + let wasm_bincode = WasmStore::new(db)?; Ok(Self { sled_db: db.clone(), @@ -81,6 +84,7 @@ impl Blockchain { nullifiers, merkle_roots, contracts, + wasm_bincode, }) } diff --git a/src/error.rs b/src/error.rs index 519fb7817..02229a9cb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -218,7 +218,7 @@ pub enum Error { InvalidPublicInputsError, #[error("Error during leader proof verification")] - LeaderProofVerificationError, + LeaderProofVerification, #[error("Signature could not be verified")] InvalidSignature, @@ -227,7 +227,7 @@ pub enum Error { StateTransitionError, #[error("Check if proposal extends any existing fork chains failed")] - ExtendedChainIndexNotFoundError, + ExtendedChainIndexNotFound, #[error("Proposal contains missmatched headers")] ProposalHeadersMissmatchError, @@ -311,6 +311,10 @@ pub enum Error { #[error("contract initialize error")] ContractError(darkfi_sdk::error::ContractError), + #[cfg(feature = "wasm-runtime")] + #[error("contract wasm bincode not found")] + WasmBincodeNotFound, + #[cfg(feature = "wasm-runtime")] #[error("contract initialize error")] ContractInitError(u64),