Parcourir la source

runtime: Pass ContractId to section functions.

parazyd il y a 3 ans
Parent
commit
ff952172fa

+ 7 - 10
example/smart-contract/src/lib.rs

@@ -1,10 +1,11 @@
 use darkfi_sdk::{
 use darkfi_sdk::{
+    crypto::ContractId,
     db::{db_begin_tx, db_end_tx, db_get, db_init, db_lookup, db_set},
     db::{db_begin_tx, db_end_tx, db_get, db_init, db_lookup, db_set},
+    define_contract,
     error::ContractResult,
     error::ContractResult,
     msg,
     msg,
     state::set_update,
     state::set_update,
     tx::FuncCall,
     tx::FuncCall,
-    define_contract,
 };
 };
 use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
 use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
 
 
@@ -44,15 +45,11 @@ pub struct FooUpdate {
     pub age: u32,
     pub age: u32,
 }
 }
 
 
-define_contract!(
-    init: init_contract,
-    exec: process_instruction,
-    apply: process_update
-);
+define_contract!(init: init_contract, exec: process_instruction, apply: process_update);
 
 
-fn init_contract(_ix: &[u8]) -> ContractResult {
+fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
     msg!("wakeup wagies!");
     msg!("wakeup wagies!");
-    db_init("wagies")?;
+    db_init(cid, "wagies")?;
 
 
     // Lets write a value in there
     // Lets write a value in there
     let tx_handle = db_begin_tx()?;
     let tx_handle = db_begin_tx()?;
@@ -68,7 +65,7 @@ fn init_contract(_ix: &[u8]) -> ContractResult {
 // This is the main entrypoint function where the payload is fed.
 // This is the main entrypoint function where the payload is fed.
 // Through here, you can branch out into different functions inside
 // Through here, you can branch out into different functions inside
 // this library.
 // this library.
-fn process_instruction(ix: &[u8]) -> ContractResult {
+fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
     match Function::from(ix[0]) {
     match Function::from(ix[0]) {
         Function::Foo => {
         Function::Foo => {
             let tx_data = &ix[1..];
             let tx_data = &ix[1..];
@@ -127,7 +124,7 @@ fn process_instruction(ix: &[u8]) -> ContractResult {
     Ok(())
     Ok(())
 }
 }
 
 
-fn process_update(update_data: &[u8]) -> ContractResult {
+fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
     msg!("Make update!");
     msg!("Make update!");
 
 
     match Function::from(update_data[0]) {
     match Function::from(update_data[0]) {

+ 1 - 2
example/smart-contract/tests/runtime.rs

@@ -19,11 +19,10 @@
 use darkfi::{
 use darkfi::{
     blockchain::Blockchain,
     blockchain::Blockchain,
     consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
     consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
-    crypto::contract_id::ContractId,
     runtime::vm_runtime::Runtime,
     runtime::vm_runtime::Runtime,
     Result,
     Result,
 };
 };
-use darkfi_sdk::{pasta::pallas, tx::FuncCall};
+use darkfi_sdk::{crypto::ContractId, pasta::pallas, tx::FuncCall};
 use darkfi_serial::{serialize, Encodable, WriteExt};
 use darkfi_serial::{serialize, Encodable, WriteExt};
 
 
 use smart_contract::{FooCallData, Function};
 use smart_contract::{FooCallData, Function};

+ 1 - 1
src/blockchain/contractstore.rs

@@ -16,10 +16,10 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
+use darkfi_sdk::crypto::ContractId;
 use darkfi_serial::deserialize;
 use darkfi_serial::deserialize;
 
 
 use crate::{
 use crate::{
-    crypto::contract_id::ContractId,
     Error::{ContractAlreadyInitialized, ContractNotFound, ContractStateNotFound},
     Error::{ContractAlreadyInitialized, ContractNotFound, ContractStateNotFound},
     Result,
     Result,
 };
 };

+ 0 - 3
src/blockchain/mod.rs

@@ -34,9 +34,6 @@ pub use nfstore::NullifierStore;
 pub mod rootstore;
 pub mod rootstore;
 pub use rootstore::RootStore;
 pub use rootstore::RootStore;
 
 
-pub mod statestore;
-pub use statestore::StateStore;
-
 pub mod txstore;
 pub mod txstore;
 pub use txstore::TxStore;
 pub use txstore::TxStore;
 
 

+ 0 - 63
src/blockchain/statestore.rs

@@ -1,63 +0,0 @@
-/* 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::serialize;
-
-use crate::{crypto::contract_id::ContractId, Result};
-
-const SLED_STATES_TREE: &[u8] = b"_states";
-
-/// The `StateStore` is a `sled` tree storing states of deployed contracts.
-/// The states themselves are data that is allocated and stored as raw bytes.
-/// These bytes are (de)serialized by the code in wasm and the contracts can
-/// operate on the state data themselves. Regarding on the (byte) size of the
-/// state, the contract deployer should allocate and pay for a certain size of
-/// their state stored by all the nodes. The cost should be linear to the byte
-/// size used.
-#[derive(Clone)]
-pub struct StateStore(sled::Tree);
-
-impl StateStore {
-    /// Opens a new or existing `StateStore` on the given sled database.
-    pub fn new(db: &sled::Db) -> Result<Self> {
-        let tree = db.open_tree(SLED_STATES_TREE)?;
-        Ok(Self(tree))
-    }
-
-    /// Insert a state into the store. This will replace the previous state.
-    /// The contract's ID is used as a key, while the value is the contract
-    /// state serialized to bytes.
-    pub fn insert(&self, contract_id: &ContractId, contract_state: &[u8]) -> Result<()> {
-        self.0.insert(serialize(contract_id), contract_state.to_vec())?;
-        Ok(())
-    }
-
-    /// Check if the `StateStore` contains a state for the given `ContractId`.
-    pub fn contains(&self, contract_id: &ContractId) -> Result<bool> {
-        Ok(self.0.contains_key(serialize(contract_id))?)
-    }
-
-    /// Retrieve a state from the `StateStore` given a `ContractId` if it exists.
-    pub fn get(&self, contract_id: &ContractId) -> Result<Option<Vec<u8>>> {
-        if let Some(data) = self.0.get(serialize(contract_id))? {
-            return Ok(Some(data.to_vec()))
-        }
-
-        Ok(None)
-    }
-}

+ 0 - 3
src/crypto/mod.rs

@@ -30,9 +30,6 @@ pub mod token_list;
 pub mod types;
 pub mod types;
 pub mod util;
 pub mod util;
 
 
-/// Contract ID definitions
-pub mod contract_id;
-
 /// VDF (Verifiable Delay Function) using MiMC
 /// VDF (Verifiable Delay Function) using MiMC
 pub mod mimc_vdf;
 pub mod mimc_vdf;
 
 

+ 22 - 5
src/runtime/import/db.rs

@@ -16,13 +16,11 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
+use darkfi_sdk::crypto::ContractId;
 use log::error;
 use log::error;
 use wasmer::{FunctionEnvMut, WasmPtr};
 use wasmer::{FunctionEnvMut, WasmPtr};
 
 
-use crate::{
-    crypto::contract_id::ContractId,
-    runtime::vm_runtime::{ContractSection, Env},
-};
+use crate::runtime::vm_runtime::{ContractSection, Env};
 
 
 /// Internal wasm runtime API for sled trees
 /// Internal wasm runtime API for sled trees
 pub struct DbHandle {
 pub struct DbHandle {
@@ -51,6 +49,26 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32)
             let contracts = &env.blockchain.contracts;
             let contracts = &env.blockchain.contracts;
             let contract_id = &env.contract_id;
             let contract_id = &env.contract_id;
 
 
+            /*
+            let Ok(cid_slice) = cid_ptr.slice(&memory_view, 32) else {
+                error!(target: "wasm_runtime::db_init", "Failed to read contract id from ptr");
+                return -2
+            };
+
+            let Ok(cid_bytes) = cid_slice.read_to_vec() else {
+                error!(target: "wasm_runtime::db_init", "Failed to read slice to vec in db_init");
+                return -2
+            };
+
+            // FIXME: Could panic
+            let cid = ContractId::from_bytes(cid_bytes.try_into().unwrap());
+
+            if &cid != contract_id {
+                error!(target: "wasm_runtime::db_init", "Unauthorized ContractId for db_init");
+                return -1
+            }
+            */
+
             let Ok(db_name) = ptr.read_utf8_string(&memory_view, len) else {
             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");
                 error!(target: "wasm_runtime::db_init", "Failed to read string from VM memory");
                 return -2
                 return -2
@@ -98,7 +116,6 @@ pub(crate) fn db_lookup(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32
                     return -2
                     return -2
                 }
                 }
             }
             }
-            0
         }
         }
         _ => -1,
         _ => -1,
     }
     }

+ 11 - 8
src/runtime/vm_runtime.rs

@@ -21,7 +21,7 @@ use std::{
     sync::Arc,
     sync::Arc,
 };
 };
 
 
-use darkfi_sdk::entrypoint;
+use darkfi_sdk::{crypto::ContractId, entrypoint};
 use log::{debug, info};
 use log::{debug, info};
 use wasmer::{
 use wasmer::{
     imports, wasmparser::Operator, AsStoreRef, CompilerConfig, Function, FunctionEnv, Instance,
     imports, wasmparser::Operator, AsStoreRef, CompilerConfig, Function, FunctionEnv, Instance,
@@ -34,7 +34,7 @@ use wasmer_middlewares::{
 };
 };
 
 
 use super::{import, import::db::DbHandle, memory::MemoryManipulation};
 use super::{import, import::db::DbHandle, memory::MemoryManipulation};
-use crate::{blockchain::Blockchain, crypto::contract_id::ContractId, Error, Result};
+use crate::{blockchain::Blockchain, Error, Result};
 
 
 /// Name of the wasm linear memory in our guest module
 /// Name of the wasm linear memory in our guest module
 const MEMORY: &str = "memory";
 const MEMORY: &str = "memory";
@@ -231,7 +231,7 @@ impl Runtime {
         env_mut.contract_section = ContractSection::Deploy;
         env_mut.contract_section = ContractSection::Deploy;
 
 
         // Serialize the payload for the format the wasm runtime is expecting.
         // Serialize the payload for the format the wasm runtime is expecting.
-        let payload = Self::serialize_payload(payload);
+        let payload = Self::serialize_payload(&env_mut.contract_id, payload);
 
 
         // Allocate enough memory for the payload and copy it into the memory.
         // Allocate enough memory for the payload and copy it into the memory.
         let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
         let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
@@ -281,7 +281,7 @@ impl Runtime {
         env_mut.contract_section = ContractSection::Exec;
         env_mut.contract_section = ContractSection::Exec;
 
 
         // Serialize the payload for the format the wasm runtime is expecting.
         // Serialize the payload for the format the wasm runtime is expecting.
-        let payload = Self::serialize_payload(payload);
+        let payload = Self::serialize_payload(&env_mut.contract_id, payload);
 
 
         // Allocate enough memory for the payload and copy it into the memory.
         // Allocate enough memory for the payload and copy it into the memory.
         let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
         let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
@@ -332,11 +332,12 @@ impl Runtime {
 
 
         // Take the update data from env, and serialize it for the format the wasm
         // Take the update data from env, and serialize it for the format the wasm
         // runtime is expecting.
         // runtime is expecting.
+        // FIXME: Can panic
         let update_data = env_mut.contract_update.take().unwrap();
         let update_data = env_mut.contract_update.take().unwrap();
         let mut payload = Vec::with_capacity(1 + update_data.1.len());
         let mut payload = Vec::with_capacity(1 + update_data.1.len());
         payload.extend_from_slice(&[update_data.0]);
         payload.extend_from_slice(&[update_data.0]);
         payload.extend_from_slice(&update_data.1);
         payload.extend_from_slice(&update_data.1);
-        let payload = Self::serialize_payload(&payload);
+        let payload = Self::serialize_payload(&env_mut.contract_id, &payload);
 
 
         // Allocate enough memory for the payload and copy it into the memory.
         // Allocate enough memory for the payload and copy it into the memory.
         let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
         let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
@@ -378,7 +379,7 @@ impl Runtime {
     fn print_logs(&self) {
     fn print_logs(&self) {
         let logs = self.ctx.as_ref(&self.store).logs.borrow();
         let logs = self.ctx.as_ref(&self.store).logs.borrow();
         for msg in logs.iter() {
         for msg in logs.iter() {
-            debug!(target: "wasm_runtime::run", "Contract log: {}", msg);
+            debug!(target: "wasm_runtime::print_logs", "Contract log: {}", msg);
         }
         }
     }
     }
 
 
@@ -425,9 +426,11 @@ impl Runtime {
     /// Serialize contract payload to the format accepted by the runtime functions.
     /// Serialize contract payload to the format accepted by the runtime functions.
     /// We keep the same payload as a slice of bytes, and prepend it with a
     /// We keep the same payload as a slice of bytes, and prepend it with a
     /// little-endian u64 to tell the payload's length.
     /// little-endian u64 to tell the payload's length.
-    fn serialize_payload(payload: &[u8]) -> Vec<u8> {
+    fn serialize_payload(cid: &ContractId, payload: &[u8]) -> Vec<u8> {
+        let ser_cid = cid.to_bytes();
         let payload_len = payload.len();
         let payload_len = payload.len();
-        let mut out = Vec::with_capacity(8 + payload_len);
+        let mut out = Vec::with_capacity(ser_cid.len() + 8 + payload_len);
+        out.extend_from_slice(&ser_cid);
         out.extend_from_slice(&(payload_len as u64).to_le_bytes());
         out.extend_from_slice(&(payload_len as u64).to_le_bytes());
         out.extend_from_slice(payload);
         out.extend_from_slice(payload);
         out
         out

+ 49 - 0
src/sdk/src/crypto/contract_id.rs

@@ -0,0 +1,49 @@
+/* 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 pasta_curves::{group::ff::PrimeField, pallas};
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct ContractId(pallas::Base);
+
+impl ContractId {
+    pub fn new(contract_id: pallas::Base) -> Self {
+        Self(contract_id)
+    }
+
+    pub fn inner(&self) -> pallas::Base {
+        self.0
+    }
+
+    pub fn to_bytes(&self) -> [u8; 32] {
+        self.0.to_repr()
+    }
+
+    pub fn from_bytes(x: [u8; 32]) -> Self {
+        // FIXME: Handle Option
+        Self(pallas::Base::from_repr(x).unwrap())
+    }
+}
+
+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)
+    }
+}

+ 7 - 3
src/sdk/src/crypto/mod.rs

@@ -27,6 +27,13 @@
 //! If you feel like trying, please help out with this migration, but do
 //! If you feel like trying, please help out with this migration, but do
 //! it properly, with care, and write documentation while you're at it.
 //! it properly, with care, and write documentation while you're at it.
 
 
+/// Cryptographic constants
+pub mod constants;
+
+/// Contract ID definitions and methods
+pub mod contract_id;
+pub use contract_id::ContractId;
+
 /// Merkle node definitions
 /// Merkle node definitions
 pub mod merkle_node;
 pub mod merkle_node;
 pub use merkle_node::MerkleNode;
 pub use merkle_node::MerkleNode;
@@ -37,6 +44,3 @@ pub use nullifier::Nullifier;
 
 
 /// Pedersen commitment utilities
 /// Pedersen commitment utilities
 pub mod pedersen;
 pub mod pedersen;
-
-/// Cryptographic constants
-pub mod constants;

+ 22 - 8
src/sdk/src/db.rs

@@ -1,4 +1,7 @@
-use super::error::{ContractError, GenericResult};
+use super::{
+    crypto::ContractId,
+    error::{ContractError, GenericResult},
+};
 
 
 type DbHandle = u32;
 type DbHandle = u32;
 type TxHandle = u32;
 type TxHandle = u32;
@@ -9,19 +12,30 @@ type TxHandle = u32;
 ///     type DbHandle = u32;
 ///     type DbHandle = u32;
 ///     db_init(db_name) -> DbHandle
 ///     db_init(db_name) -> DbHandle
 /// ```
 /// ```
-pub fn db_init(db_name: &str) -> GenericResult<()> {
+pub fn db_init(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
     #[cfg(target_arch = "wasm32")]
     #[cfg(target_arch = "wasm32")]
     unsafe {
     unsafe {
-        return match db_init_(db_name.as_ptr(), db_name.len() as u32) {
-            0 => Ok(()),
-            -1 => Err(ContractError::CallerAccessDenied),
-            -2 => Err(ContractError::DbInitFailed),
-            _ => unreachable!(),
+        println!("sdk/src/db.rs:db_init() BEGIN");
+        let ret = db_init_(
+            //contract_id.to_bytes().as_ptr(),
+            //32_u32,
+            db_name.as_ptr(),
+            db_name.len() as u32,
+        );
+
+        if ret < 0 {
+            match ret {
+                -1 => return Err(ContractError::CallerAccessDenied),
+                -2 => return Err(ContractError::DbInitFailed),
+                _ => unimplemented!(),
+            }
         }
         }
+
+        return Ok(ret as u32)
     }
     }
 
 
     #[cfg(not(target_arch = "wasm32"))]
     #[cfg(not(target_arch = "wasm32"))]
-    todo!("{}", db_name);
+    unimplemented!()
 }
 }
 
 
 pub fn db_lookup(db_name: &str) -> GenericResult<DbHandle> {
 pub fn db_lookup(db_name: &str) -> GenericResult<DbHandle> {

+ 14 - 8
src/sdk/src/entrypoint.rs

@@ -18,6 +18,8 @@
 
 
 use std::{mem::size_of, slice::from_raw_parts};
 use std::{mem::size_of, slice::from_raw_parts};
 
 
+use crate::crypto::ContractId;
+
 /// Success exit code for a contract
 /// Success exit code for a contract
 pub const SUCCESS: u64 = 0;
 pub const SUCCESS: u64 = 0;
 
 
@@ -27,27 +29,27 @@ macro_rules! define_contract {
         /// # Safety
         /// # Safety
         #[no_mangle]
         #[no_mangle]
         pub unsafe extern "C" fn __initialize(input: *mut u8) -> u64 {
         pub unsafe extern "C" fn __initialize(input: *mut u8) -> u64 {
-            let instruction_data = $crate::entrypoint::deserialize(input);
+            let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
 
 
-            match $init_func(&instruction_data) {
+            match $init_func(contract_id, &instruction_data) {
                 Ok(()) => $crate::entrypoint::SUCCESS,
                 Ok(()) => $crate::entrypoint::SUCCESS,
                 Err(e) => e.into(),
                 Err(e) => e.into(),
             }
             }
         }
         }
         #[no_mangle]
         #[no_mangle]
         pub unsafe extern "C" fn __entrypoint(input: *mut u8) -> u64 {
         pub unsafe extern "C" fn __entrypoint(input: *mut u8) -> u64 {
-            let instruction_data = $crate::entrypoint::deserialize(input);
+            let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
 
 
-            match $exec_func(&instruction_data) {
+            match $exec_func(contract_id, &instruction_data) {
                 Ok(()) => $crate::entrypoint::SUCCESS,
                 Ok(()) => $crate::entrypoint::SUCCESS,
                 Err(e) => e.into(),
                 Err(e) => e.into(),
             }
             }
         }
         }
         #[no_mangle]
         #[no_mangle]
         pub unsafe extern "C" fn __update(input: *mut u8) -> u64 {
         pub unsafe extern "C" fn __update(input: *mut u8) -> u64 {
-            let update_data = $crate::entrypoint::deserialize(input);
+            let (contract_id, update_data) = $crate::entrypoint::deserialize(input);
 
 
-            match $apply_func(&update_data) {
+            match $apply_func(contract_id, &update_data) {
                 Ok(()) => $crate::entrypoint::SUCCESS,
                 Ok(()) => $crate::entrypoint::SUCCESS,
                 Err(e) => e.into(),
                 Err(e) => e.into(),
             }
             }
@@ -57,12 +59,16 @@ macro_rules! define_contract {
 
 
 /// Deserialize a given payload in `entrypoint`
 /// Deserialize a given payload in `entrypoint`
 /// # Safety
 /// # Safety
-pub unsafe fn deserialize<'a>(input: *mut u8) -> &'a [u8] {
+pub unsafe fn deserialize<'a>(input: *mut u8) -> (ContractId, &'a [u8]) {
     let mut offset: usize = 0;
     let mut offset: usize = 0;
 
 
+    let contract_id_len = 32;
+    let contract_id_slice = { from_raw_parts(input.add(offset), contract_id_len) };
+    offset += contract_id_len;
+
     let instruction_data_len = *(input.add(offset) as *const u64) as usize;
     let instruction_data_len = *(input.add(offset) as *const u64) as usize;
     offset += size_of::<u64>();
     offset += size_of::<u64>();
     let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
     let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
 
 
-    instruction_data
+    (ContractId::from_bytes(contract_id_slice.try_into().unwrap()), instruction_data)
 }
 }