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

runtime: sled blockchain access

parazyd 3 лет назад
Родитель
Сommit
9848a1e5f5

+ 1 - 0
example/smart-contract/Cargo.lock

@@ -2601,6 +2601,7 @@ dependencies = [
  "darkfi-serial",
  "getrandom",
  "simplelog",
+ "sled",
 ]
 
 [[package]]

+ 1 - 1
example/smart-contract/Cargo.toml

@@ -27,4 +27,4 @@ getrandom = { version = "0.2", features = ["custom"] }
 [dev-dependencies]
 darkfi = { path = "../../", features = ["wasm-runtime"] }
 simplelog = "0.12.0"
-
+sled = "0.34.7"

+ 17 - 14
example/smart-contract/tests/runtime.rs

@@ -16,11 +16,14 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use darkfi::{crypto::contract_id::ContractId, runtime::vm_runtime::Runtime, Result};
-use darkfi_sdk::{
-    pasta::pallas,
-    tx::FuncCall
+use darkfi::{
+    blockchain::Blockchain,
+    consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
+    crypto::contract_id::ContractId,
+    runtime::vm_runtime::Runtime,
+    Result,
 };
+use darkfi_sdk::{pasta::pallas, tx::FuncCall};
 use darkfi_serial::{serialize, Encodable, WriteExt};
 
 use smart_contract::{FooCallData, Function};
@@ -36,20 +39,20 @@ fn run_contract() -> Result<()> {
         simplelog::TerminalMode::Mixed,
         simplelog::ColorChoice::Auto,
     )?;
-    // =============================================================
-    // Build a ledger state so the runtime has something to work on
-    // =============================================================
-    //let state_machine = State::dummy()?;
 
-    // Add a nullifier to the nullifier set. (This is checked by the contract)
-    //state_machine.nullifiers.insert(&[Nullifier::from(pallas::Base::from(0x10))])?;
+    // =============================
+    // Initialize a dummy blockchain
+    // =============================
+    // TODO: This blockchain interface should perhaps be ValidatorState and Mutex/RwLock.
+    let db = sled::Config::new().temporary(true).open()?;
+    let blockchain = Blockchain::new(&db, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?;
 
     // ================================================================
     // Load the wasm binary into memory and create an execution runtime
     // ================================================================
     let wasm_bytes = std::fs::read("contract.wasm")?;
     let contract_id = ContractId::new(pallas::Base::from(1));
-    let mut runtime = Runtime::new(&wasm_bytes, contract_id)?;
+    let mut runtime = Runtime::new(&wasm_bytes, blockchain, contract_id)?;
 
     // Deploy function to initialize the smart contract state.
     // Here we pass an empty payload, but it's possible to feed in arbitrary data.
@@ -59,9 +62,9 @@ fn run_contract() -> Result<()> {
     // Build some kind of payload to show an example
     // =============================================
     let func_calls = vec![FuncCall {
-            contract_id: pallas::Base::from(110),
-            func_id: pallas::Base::from(4),
-            call_data: serialize(&FooCallData { a: 777, b: 666 }),
+        contract_id: pallas::Base::from(110),
+        func_id: pallas::Base::from(4),
+        call_data: serialize(&FooCallData { a: 777, b: 666 }),
     }];
     let func_call_index: u32 = 0;
 

+ 37 - 13
src/runtime/import/db.rs

@@ -19,7 +19,22 @@
 use log::error;
 use wasmer::{FunctionEnvMut, WasmPtr};
 
-use crate::runtime::vm_runtime::{ContractSection, Env};
+use crate::{
+    crypto::contract_id::ContractId,
+    runtime::vm_runtime::{ContractSection, Env},
+};
+
+/// Internal wasm runtime API for sled trees
+pub struct DbHandle {
+    contract_id: ContractId,
+    tree: sled::Tree,
+}
+
+impl DbHandle {
+    pub fn new(contract_id: ContractId, tree: sled::Tree) -> Self {
+        Self { contract_id, tree }
+    }
+}
 
 /// Only deploy() can call this. Creates a new database instance for this contract.
 ///
@@ -31,23 +46,32 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32)
     let env = ctx.data();
     match env.contract_section {
         ContractSection::Deploy => {
-            let env = ctx.data();
             let memory_view = env.memory_view(&ctx);
+            let db = &env.blockchain.sled_db;
+            let contracts = &env.blockchain.contracts;
+            let contract_id = &env.contract_id;
 
-            match ptr.read_utf8_string(&memory_view, len) {
-                Ok(db_name) => {
-                    // TODO:
-                    // * db_name = blake3_hash(contract_id, db_name)
-                    // * create db_name sled database
-                }
-                Err(_) => {
-                    error!(target: "wasm_runtime::drk_log", "Failed to read UTF-8 string from VM memory");
+            let Ok(db_name) = ptr.read_utf8_string(&memory_view, len) else {
+                error!(target: "wasm_runtime::db_init", "Failed to read string from VM memory");
+                return -2
+            };
+
+            let tree_handle = match contracts.init(db, contract_id, &db_name) {
+                Ok(v) => v,
+                Err(e) => {
+                    error!(target: "wasm_runtime::db_init", "Failed to init db: {}", e);
                     return -2
                 }
-            }
-            0
+            };
+
+            let mut db_handles = env.db_handles.borrow_mut();
+            db_handles.push(DbHandle::new(*contract_id, tree_handle));
+            return (db_handles.len() - 1) as i32
+        }
+        _ => {
+            error!(target: "wasm_runtime::db_init", "db_init called in unauthorized section");
+            return -1
         }
-        _ => -1,
     }
 }
 

+ 16 - 10
src/runtime/vm_runtime.rs

@@ -33,12 +33,8 @@ use wasmer_middlewares::{
     Metering,
 };
 
-use super::{
-    import,
-    //chain_state::{is_valid_merkle, nullifier_exists, set_update},
-    memory::MemoryManipulation,
-};
-use crate::{crypto::contract_id::ContractId, Error, Result};
+use super::{import, import::db::DbHandle, memory::MemoryManipulation};
+use crate::{blockchain::Blockchain, crypto::contract_id::ContractId, Error, Result};
 
 /// Name of the wasm linear memory in our guest module
 const MEMORY: &str = "memory";
@@ -60,10 +56,16 @@ pub enum ContractSection {
 
 /// The wasm vm runtime instantiated for every smart contract that runs.
 pub struct Env {
+    /// Blockchain access
+    pub blockchain: Blockchain,
+    /// sled tree handles used with `db_*`
+    pub db_handles: RefCell<Vec<DbHandle>>,
+    /// The contract ID being executed
     pub contract_id: ContractId,
+    /// The contract section being executed
     pub contract_section: ContractSection,
+    /// State update produced by a smart contract function call
     pub contract_update: Cell<Option<(u8, Vec<u8>)>>,
-    //pub func_id:
     /// Logs produced by the contract
     pub logs: RefCell<Vec<String>>,
     /// Direct memory access to the VM
@@ -96,7 +98,7 @@ pub struct Runtime {
 
 impl Runtime {
     /// Create a new wasm runtime instance that contains the given wasm module.
-    pub fn new(wasm_bytes: &[u8], contract_id: ContractId) -> Result<Self> {
+    pub fn new(wasm_bytes: &[u8], blockchain: Blockchain, contract_id: ContractId) -> Result<Self> {
         info!(target: "warm_runtime::new", "Instantiating a new runtime");
         // This function will be called for each `Operator` encountered during
         // the wasm module execution. It should return the cost of the operator
@@ -124,13 +126,17 @@ impl Runtime {
         debug!(target: "wasm_runtime::new", "Compiling module");
         let module = Module::new(&store, wasm_bytes)?;
 
-        // This section will need changing
-        debug!(target: "wasm_runtime::new", "Importing functions");
+        // Initialize data
+        let db_handles = RefCell::new(vec![]);
         let logs = RefCell::new(vec![]);
 
+        debug!(target: "wasm_runtime::new", "Importing functions");
+
         let ctx = FunctionEnv::new(
             &mut store,
             Env {
+                blockchain,
+                db_handles,
                 contract_id,
                 contract_section: ContractSection::Null,
                 contract_update: Cell::new(None),