blockchain: Add WasmStore and rename ContractStore to ContractStateStore.

This commit is contained in:
parazyd
2022-11-16 11:19:04 +01:00
parent 05befe509b
commit 337f80361d
3 changed files with 79 additions and 34 deletions

View File

@@ -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<u8>
pub fn new(db: &sled::Db) -> Result<Self> {
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<Vec<u8>> {
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<sled::Tree> {
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<sled::Tree> {
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.

View File

@@ -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,
})
}

View File

@@ -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),