|
|
@@ -15,6 +15,7 @@ r* This program is distributed in the hope that it will be useful,
|
|
|
* 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 std::io::Cursor;
|
|
|
|
|
|
use darkfi_sdk::crypto::ContractId;
|
|
|
@@ -22,6 +23,7 @@ use darkfi_serial::{deserialize, serialize};
|
|
|
use log::{debug, error};
|
|
|
|
|
|
use crate::{
|
|
|
+ blockchain::SledDbOverlayPtr,
|
|
|
runtime::vm_runtime::SMART_CONTRACT_ZKAS_DB_NAME,
|
|
|
zk::{VerifyingKey, ZkCircuit},
|
|
|
zkas::ZkBinary,
|
|
|
@@ -57,11 +59,23 @@ impl WasmStore {
|
|
|
|
|
|
Err(Error::WasmBincodeNotFound)
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+/// Overlay structure over a [`WasmStore`] instance.
|
|
|
+pub struct WasmStoreOverlay(SledDbOverlayPtr);
|
|
|
+
|
|
|
+impl WasmStoreOverlay {
|
|
|
+ pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
|
|
|
+ overlay.lock().unwrap().open_tree(SLED_BINCODE_TREE)?;
|
|
|
+ Ok(Self(overlay))
|
|
|
+ }
|
|
|
|
|
|
/// 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!(target: "blockchain::contractstore", "Failed to insert bincode to WasmStore: {}", e);
|
|
|
+ if let Err(e) =
|
|
|
+ self.0.lock().unwrap().insert(SLED_BINCODE_TREE, &serialize(&contract_id), bincode)
|
|
|
+ {
|
|
|
+ error!(target: "blockchain::contractstoreoverlay", "Failed to insert bincode to WasmStore: {}", e);
|
|
|
return Err(e.into())
|
|
|
}
|
|
|
|
|
|
@@ -89,58 +103,6 @@ impl ContractStateStore {
|
|
|
Ok(Self(tree))
|
|
|
}
|
|
|
|
|
|
- /// 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 `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 `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: "blockchain::contractstore", "Initializing state tree for {}:{}", contract_id, tree_name);
|
|
|
-
|
|
|
- let contract_id_bytes = serialize(contract_id);
|
|
|
- let ptr = contract_id.hash_state_id(tree_name);
|
|
|
-
|
|
|
- // See if there are existing state trees.
|
|
|
- // If not, just start with an empty vector.
|
|
|
- let mut state_pointers: Vec<[u8; 32]> = if self.0.contains_key(&contract_id_bytes)? {
|
|
|
- let bytes = self.0.get(&contract_id_bytes)?.unwrap();
|
|
|
- deserialize(&bytes)?
|
|
|
- } else {
|
|
|
- vec![]
|
|
|
- };
|
|
|
-
|
|
|
- // If the db was never initialized, it should not be in here.
|
|
|
- if state_pointers.contains(&ptr) {
|
|
|
- return Err(Error::ContractAlreadyInitialized)
|
|
|
- }
|
|
|
-
|
|
|
- // Now we add it so it's marked as initialized
|
|
|
- state_pointers.push(ptr);
|
|
|
-
|
|
|
- // We do this as a batch so in case of not being able to open the tree
|
|
|
- // we don't write that it's initialized.
|
|
|
- let mut batch = sled::Batch::default();
|
|
|
- batch.insert(contract_id_bytes, serialize(&state_pointers));
|
|
|
-
|
|
|
- // We open the tree and return its handle
|
|
|
- let tree = db.open_tree(ptr)?;
|
|
|
-
|
|
|
- // On success, apply the batch
|
|
|
- self.0.apply_batch(batch)?;
|
|
|
-
|
|
|
- Ok(tree)
|
|
|
- }
|
|
|
-
|
|
|
/// Do a lookup of an existing contract state. In order to succeed, the
|
|
|
/// state must have been previously initialized with `init()`. If the
|
|
|
/// state has been found, a handle to it will be returned. Otherwise, we
|
|
|
@@ -241,3 +203,83 @@ impl ContractStateStore {
|
|
|
Ok((zkbin, vk))
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/// Overlay structure over a [`ContractStateStore`] instance.
|
|
|
+pub struct ContractStateStoreOverlay(SledDbOverlayPtr);
|
|
|
+
|
|
|
+impl ContractStateStoreOverlay {
|
|
|
+ pub fn new(overlay: SledDbOverlayPtr) -> Result<Self> {
|
|
|
+ overlay.lock().unwrap().open_tree(SLED_CONTRACTS_TREE)?;
|
|
|
+ Ok(Self(overlay))
|
|
|
+ }
|
|
|
+
|
|
|
+ /// 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 `ContractStateStoreOverlay`
|
|
|
+ /// 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 `ContractStateStoreOverlay` tree and a handle to it will be
|
|
|
+ /// returned.
|
|
|
+ pub fn init(&self, contract_id: &ContractId, tree_name: &str) -> Result<[u8; 32]> {
|
|
|
+ debug!(target: "blockchain::contractstoreoverlay", "Initializing state overlay tree for {}:{}", contract_id, tree_name);
|
|
|
+
|
|
|
+ let contract_id_bytes = serialize(contract_id);
|
|
|
+ let ptr = contract_id.hash_state_id(tree_name);
|
|
|
+ let mut lock = self.0.lock().unwrap();
|
|
|
+
|
|
|
+ // See if there are existing state trees.
|
|
|
+ // If not, just start with an empty vector.
|
|
|
+ let mut state_pointers: Vec<[u8; 32]> =
|
|
|
+ if lock.contains_key(SLED_CONTRACTS_TREE, &contract_id_bytes)? {
|
|
|
+ let bytes = lock.get(SLED_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
|
|
|
+ deserialize(&bytes)?
|
|
|
+ } else {
|
|
|
+ vec![]
|
|
|
+ };
|
|
|
+
|
|
|
+ // If the db was never initialized, it should not be in here.
|
|
|
+ if state_pointers.contains(&ptr) {
|
|
|
+ return Err(Error::ContractAlreadyInitialized)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now we add it so it's marked as initialized and create its tree.
|
|
|
+ state_pointers.push(ptr);
|
|
|
+ lock.insert(SLED_CONTRACTS_TREE, &contract_id_bytes, &serialize(&state_pointers))?;
|
|
|
+ lock.open_tree(&ptr)?;
|
|
|
+
|
|
|
+ Ok(ptr)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Do a lookup of an existing contract state. In order to succeed, the
|
|
|
+ /// state must have been previously initialized with `init()`. If the
|
|
|
+ /// state has been found, a handle to it will be returned. Otherwise, we
|
|
|
+ /// return an error.
|
|
|
+ pub fn lookup(&self, contract_id: &ContractId, tree_name: &str) -> Result<[u8; 32]> {
|
|
|
+ debug!(target: "blockchain::contractstoreoverlay", "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);
|
|
|
+ let mut lock = self.0.lock().unwrap();
|
|
|
+
|
|
|
+ // A guard to make sure we went through init()
|
|
|
+ if !lock.contains_key(SLED_CONTRACTS_TREE, &contract_id_bytes)? {
|
|
|
+ return Err(Error::ContractNotFound(contract_id.to_string()))
|
|
|
+ }
|
|
|
+
|
|
|
+ let state_pointers = lock.get(SLED_CONTRACTS_TREE, &contract_id_bytes)?.unwrap();
|
|
|
+ let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
|
|
|
+
|
|
|
+ // 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(Error::ContractStateNotFound)
|
|
|
+ }
|
|
|
+
|
|
|
+ // We open the tree and return its handle
|
|
|
+ lock.open_tree(&ptr)?;
|
|
|
+ Ok(ptr)
|
|
|
+ }
|
|
|
+}
|