Przeglądaj źródła

wasm: fix error codes situation so we get contract errors from inside wasm

x 3 lat temu
rodzic
commit
1c9ff6fdba

+ 23 - 27
example/smart-contract/src/lib.rs

@@ -4,11 +4,11 @@ use darkfi_sdk::{
     define_contract,
     define_contract,
     error::ContractResult,
     error::ContractResult,
     msg,
     msg,
+    pasta::pallas,
     tx::FuncCall,
     tx::FuncCall,
     util::set_return_data,
     util::set_return_data,
-    pasta::pallas
 };
 };
-use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable, Encodable};
+use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable};
 
 
 /// Available functions for this contract.
 /// Available functions for this contract.
 /// We identify them with the first byte passed in through the payload.
 /// We identify them with the first byte passed in through the payload.
@@ -89,27 +89,18 @@ fn get_metadata(_cid: ContractId, ix: &[u8]) -> ContractResult {
             let zk_public_values = vec![
             let zk_public_values = vec![
                 (
                 (
                     "DaoProposeInput".to_string(),
                     "DaoProposeInput".to_string(),
-                    vec![
-                        pallas::Base::from(110),
-                        pallas::Base::from(4)
-                    ]
-                ),
-                (
-                    "DaoProposeInput".to_string(),
-                    vec![
-                        pallas::Base::from(7),
-                        pallas::Base::from(4)
-                    ]
+                    vec![pallas::Base::from(110), pallas::Base::from(4)],
                 ),
                 ),
+                ("DaoProposeInput".to_string(), vec![pallas::Base::from(7), pallas::Base::from(4)]),
                 (
                 (
                     "DaoProposeMain".to_string(),
                     "DaoProposeMain".to_string(),
                     vec![
                     vec![
                         pallas::Base::from(1),
                         pallas::Base::from(1),
                         pallas::Base::from(3),
                         pallas::Base::from(3),
                         pallas::Base::from(5),
                         pallas::Base::from(5),
-                        pallas::Base::from(7)
-                    ]
-                )
+                        pallas::Base::from(7),
+                    ],
+                ),
             ];
             ];
 
 
             let signature_public_keys: Vec<pallas::Point> = vec![
             let signature_public_keys: Vec<pallas::Point> = vec![
@@ -141,10 +132,10 @@ fn process_instruction(_cid: ContractId, ix: &[u8]) -> ContractResult {
             let tx_data = &ix[1..];
             let tx_data = &ix[1..];
             // ...
             // ...
             let (func_call_index, func_calls): (u32, Vec<FuncCall>) = deserialize(tx_data)?;
             let (func_call_index, func_calls): (u32, Vec<FuncCall>) = deserialize(tx_data)?;
-            let call_data: FooCallData =
-                deserialize(&func_calls[func_call_index as usize].call_data)?;
-            msg!("call_data {{ a: {}, b: {} }}", call_data.a, call_data.b);
-            // ...
+            //let call_data: FooCallData =
+            //    deserialize(&func_calls[func_call_index as usize].call_data)?;
+            //msg!("call_data {{ a: {}, b: {} }}", call_data.a, call_data.b);
+            //// ...
             let update = FooUpdate { name: "john_doe".to_string(), age: 110 };
             let update = FooUpdate { name: "john_doe".to_string(), age: 110 };
 
 
             let mut update_data = vec![Function::Foo as u8];
             let mut update_data = vec![Function::Foo as u8];
@@ -153,10 +144,10 @@ fn process_instruction(_cid: ContractId, ix: &[u8]) -> ContractResult {
             msg!("update is set!");
             msg!("update is set!");
 
 
             // Example: try to get a value from the db
             // Example: try to get a value from the db
-            let db_handle = db_lookup("wagies")?;
-            // FIXME: this is just empty right now
-            let age_data = db_get(db_handle, "jason_gulag".as_bytes())?;
-            msg!("wagie age data: {:?}", age_data);
+            //let db_handle = db_lookup("wagies")?;
+            //// FIXME: this is just empty right now
+            //let age_data = db_get(db_handle, "jason_gulag".as_bytes())?;
+            //msg!("wagie age data: {:?}", age_data);
         }
         }
         Function::Bar => {
         Function::Bar => {
             let tx_data = &ix[1..];
             let tx_data = &ix[1..];
@@ -169,11 +160,12 @@ fn process_instruction(_cid: ContractId, ix: &[u8]) -> ContractResult {
 }
 }
 
 
 fn process_update(_cid: ContractId, update_data: &[u8]) -> ContractResult {
 fn process_update(_cid: ContractId, update_data: &[u8]) -> ContractResult {
-    msg!("Make update!");
+    msg!("Make 1 update!");
 
 
     match Function::from(update_data[0]) {
     match Function::from(update_data[0]) {
         Function::Foo => {
         Function::Foo => {
-            let update: FooUpdate = deserialize(&update_data[1..])?;
+            msg!("fooupp");
+            //let update: FooUpdate = deserialize(&update_data[1..])?;
 
 
             // Write the wagie to the db
             // Write the wagie to the db
             //let tx_handle = db_begin_tx()?;
             //let tx_handle = db_begin_tx()?;
@@ -181,8 +173,12 @@ fn process_update(_cid: ContractId, update_data: &[u8]) -> ContractResult {
             //let db_handle = db_lookup("wagies")?;
             //let db_handle = db_lookup("wagies")?;
             //db_end_tx(db_handle, tx_handle)?;
             //db_end_tx(db_handle, tx_handle)?;
         }
         }
-        _ => unreachable!(),
+        //_ => unreachable!(),
+        _ => {
+            msg!("other branch");
+        }
     }
     }
 
 
+    msg!("process_update() finished");
     Ok(())
     Ok(())
 }
 }

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

@@ -16,7 +16,6 @@
  * 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 std::io::Cursor;
 use darkfi::{
 use darkfi::{
     blockchain::Blockchain,
     blockchain::Blockchain,
     consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
     consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
@@ -25,6 +24,7 @@ use darkfi::{
 };
 };
 use darkfi_sdk::{crypto::ContractId, pasta::pallas, tx::FuncCall};
 use darkfi_sdk::{crypto::ContractId, pasta::pallas, tx::FuncCall};
 use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
 use darkfi_serial::{serialize, Decodable, Encodable, WriteExt};
+use std::io::Cursor;
 
 
 use smart_contract::{FooCallData, Function};
 use smart_contract::{FooCallData, Function};
 
 

+ 1 - 1
src/blockchain/contractstore.rs

@@ -74,7 +74,7 @@ impl ContractStore {
             return Err(ContractNotFound(contract_id.to_string()))
             return Err(ContractNotFound(contract_id.to_string()))
         }
         }
 
 
-        let state_pointers =  self.0.get(&contract_id_bytes)?.unwrap();
+        let state_pointers = self.0.get(&contract_id_bytes)?.unwrap();
 
 
         let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
         let state_pointers: Vec<[u8; 32]> = deserialize(&state_pointers)?;
 
 

+ 2 - 2
src/consensus/coins.rs

@@ -260,11 +260,11 @@ fn create_leadcoin(
     let c_cm_msg = [*c_cm_coordinates.x(), *c_cm_coordinates.y()];
     let c_cm_msg = [*c_cm_coordinates.x(), *c_cm_coordinates.y()];
     let c_cm_base: pallas::Base =
     let c_cm_base: pallas::Base =
         poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
         poseidon::Hash::<_, poseidon::P128Pow5T3, poseidon::ConstantLength<2>, 3, 2>::init()
-        .hash(c_cm_msg);
+            .hash(c_cm_msg);
     let c_cm_node = MerkleNode::from(c_cm_base);
     let c_cm_node = MerkleNode::from(c_cm_base);
     tree_cm.append(&c_cm_node.clone());
     tree_cm.append(&c_cm_node.clone());
     let leaf_position = tree_cm.witness().unwrap();
     let leaf_position = tree_cm.witness().unwrap();
-    let leaf_position_usize : usize = leaf_position.into();
+    let leaf_position_usize: usize = leaf_position.into();
     //info!("leaf position odd parity: {:?}", leaf_position.is_odd());
     //info!("leaf position odd parity: {:?}", leaf_position.is_odd());
     let c_root_cm = tree_cm.root(0).unwrap();
     let c_root_cm = tree_cm.root(0).unwrap();
     let c_cm_path = tree_cm.authentication_path(leaf_position, &c_root_cm).unwrap();
     let c_cm_path = tree_cm.authentication_path(leaf_position, &c_root_cm).unwrap();

+ 11 - 0
src/error.rs

@@ -303,6 +303,10 @@ pub enum Error {
     #[error("wasm runtime out of memory")]
     #[error("wasm runtime out of memory")]
     WasmerOomError(String),
     WasmerOomError(String),
 
 
+    #[cfg(feature = "wasm-runtime")]
+    #[error("contract initialize error")]
+    ContractError(darkfi_sdk::error::ContractError),
+
     #[cfg(feature = "wasm-runtime")]
     #[cfg(feature = "wasm-runtime")]
     #[error("contract initialize error")]
     #[error("contract initialize error")]
     ContractInitError(u64),
     ContractInitError(u64),
@@ -603,3 +607,10 @@ impl From<wasmer::MemoryError> for Error {
         Self::WasmerOomError(err.to_string())
         Self::WasmerOomError(err.to_string())
     }
     }
 }
 }
+
+#[cfg(feature = "wasm-runtime")]
+impl From<darkfi_sdk::error::ContractError> for Error {
+    fn from(err: darkfi_sdk::error::ContractError) -> Self {
+        Self::ContractError(err)
+    }
+}

+ 8 - 8
src/runtime/import/util.rs

@@ -16,7 +16,7 @@
  * 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 log::error;
+use log::{debug, error};
 use wasmer::{FunctionEnvMut, WasmPtr};
 use wasmer::{FunctionEnvMut, WasmPtr};
 
 
 use crate::runtime::vm_runtime::{ContractSection, Env};
 use crate::runtime::vm_runtime::{ContractSection, Env};
@@ -39,27 +39,27 @@ pub(crate) fn drk_log(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) {
     }
     }
 }
 }
 
 
-pub(crate) fn set_return_data(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i32 {
+pub(crate) fn set_return_data(ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32) -> i64 {
     let env = ctx.data();
     let env = ctx.data();
     match env.contract_section {
     match env.contract_section {
         ContractSection::Exec | ContractSection::Metadata => {
         ContractSection::Exec | ContractSection::Metadata => {
             let memory_view = env.memory_view(&ctx);
             let memory_view = env.memory_view(&ctx);
 
 
             let Ok(slice) = ptr.slice(&memory_view, len) else {
             let Ok(slice) = ptr.slice(&memory_view, len) else {
-                return -2
+                return darkfi_sdk::error::INTERNAL_ERROR
             };
             };
 
 
-            let Ok(update_data) = slice.read_to_vec() else {
-                return -2;
+            let Ok(return_data) = slice.read_to_vec() else {
+                return darkfi_sdk::error::INTERNAL_ERROR
             };
             };
 
 
             // This function should only ever be called once on the runtime.
             // This function should only ever be called once on the runtime.
             if !env.contract_return_data.take().is_none() {
             if !env.contract_return_data.take().is_none() {
-                return -3
+                return darkfi_sdk::error::SET_RETVAL_ERROR
             }
             }
-            env.contract_return_data.set(Some(update_data));
+            env.contract_return_data.set(Some(return_data));
             0
             0
         }
         }
-        _ => -1,
+        _ => darkfi_sdk::error::CALLER_ACCESS_DENIED,
     }
     }
 }
 }

+ 5 - 2
src/runtime/vm_runtime.rs

@@ -258,7 +258,7 @@ impl Runtime {
         };
         };
 
 
         let retval = match ret[0] {
         let retval = match ret[0] {
-            Value::I64(v) => v as u64,
+            Value::I64(v) => v,
             _ => unreachable!(),
             _ => unreachable!(),
         };
         };
 
 
@@ -266,7 +266,10 @@ impl Runtime {
             entrypoint::SUCCESS => Ok(retdata),
             entrypoint::SUCCESS => Ok(retdata),
             // FIXME: we should be able to see the error returned from the contract
             // FIXME: we should be able to see the error returned from the contract
             // We can put sdk::Error inside of this.
             // We can put sdk::Error inside of this.
-            _ => Err(Error::ContractInitError(retval)),
+            _ => {
+                let err = darkfi_sdk::error::ContractError::from(retval);
+                Err(Error::ContractError(err))
+            }
         }
         }
     }
     }
 
 

+ 5 - 5
src/sdk/src/entrypoint.rs

@@ -21,7 +21,7 @@ use std::{mem::size_of, slice::from_raw_parts};
 use crate::crypto::ContractId;
 use crate::crypto::ContractId;
 
 
 /// Success exit code for a contract
 /// Success exit code for a contract
-pub const SUCCESS: u64 = 0;
+pub const SUCCESS: i64 = 0;
 
 
 #[macro_export]
 #[macro_export]
 macro_rules! define_contract {
 macro_rules! define_contract {
@@ -33,7 +33,7 @@ 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) -> i64 {
             let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
             let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
 
 
             match $init_func(contract_id, &instruction_data) {
             match $init_func(contract_id, &instruction_data) {
@@ -42,7 +42,7 @@ macro_rules! define_contract {
             }
             }
         }
         }
         #[no_mangle]
         #[no_mangle]
-        pub unsafe extern "C" fn __entrypoint(input: *mut u8) -> u64 {
+        pub unsafe extern "C" fn __entrypoint(input: *mut u8) -> i64 {
             let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
             let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
 
 
             match $exec_func(contract_id, &instruction_data) {
             match $exec_func(contract_id, &instruction_data) {
@@ -51,7 +51,7 @@ macro_rules! define_contract {
             }
             }
         }
         }
         #[no_mangle]
         #[no_mangle]
-        pub unsafe extern "C" fn __update(input: *mut u8) -> u64 {
+        pub unsafe extern "C" fn __update(input: *mut u8) -> i64 {
             let (contract_id, update_data) = $crate::entrypoint::deserialize(input);
             let (contract_id, update_data) = $crate::entrypoint::deserialize(input);
 
 
             match $apply_func(contract_id, &update_data) {
             match $apply_func(contract_id, &update_data) {
@@ -60,7 +60,7 @@ macro_rules! define_contract {
             }
             }
         }
         }
         #[no_mangle]
         #[no_mangle]
-        pub unsafe extern "C" fn __metadata(input: *mut u8) -> u64 {
+        pub unsafe extern "C" fn __metadata(input: *mut u8) -> i64 {
             let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
             let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
 
 
             match $metadata_func(contract_id, &instruction_data) {
             match $metadata_func(contract_id, &instruction_data) {

+ 22 - 23
src/sdk/src/error.rs

@@ -22,7 +22,7 @@ pub type GenericResult<T> = ResultGeneric<T, ContractError>;
 pub type ContractResult = ResultGeneric<(), ContractError>;
 pub type ContractResult = ResultGeneric<(), ContractError>;
 
 
 /// Error codes available in the contract.
 /// Error codes available in the contract.
-#[derive(Debug, thiserror::Error)]
+#[derive(Debug, Clone, thiserror::Error)]
 pub enum ContractError {
 pub enum ContractError {
     /// Allows on-chain programs to implement contract-specific error types and
     /// Allows on-chain programs to implement contract-specific error types and
     /// see them returned by the runtime. A contract-specific error may be any
     /// see them returned by the runtime. A contract-specific error may be any
@@ -36,8 +36,8 @@ pub enum ContractError {
     #[error("IO error: {0}")]
     #[error("IO error: {0}")]
     IoError(String),
     IoError(String),
 
 
-    #[error("Error setting update")]
-    SetUpdateError,
+    #[error("Error setting return value")]
+    SetRetvalError,
 
 
     #[error("Error checking if nullifier exists")]
     #[error("Error checking if nullifier exists")]
     NullifierExistCheck,
     NullifierExistCheck,
@@ -62,31 +62,30 @@ pub enum ContractError {
 }
 }
 
 
 /// Builtin return values occupy the upper 32 bits
 /// Builtin return values occupy the upper 32 bits
-const BUILTIN_BIT_SHIFT: usize = 32;
 macro_rules! to_builtin {
 macro_rules! to_builtin {
     ($error:expr) => {
     ($error:expr) => {
-        ($error as u64) << BUILTIN_BIT_SHIFT
+        i64::MIN + $error
     };
     };
 }
 }
 
 
-pub const CUSTOM_ZERO: u64 = to_builtin!(1);
-pub const INTERNAL_ERROR: u64 = to_builtin!(2);
-pub const SET_UPDATE_ERROR: u64 = to_builtin!(3);
-pub const IO_ERROR: u64 = to_builtin!(4);
-pub const NULLIFIER_EXIST_CHECK: u64 = to_builtin!(5);
-pub const VALID_MERKLE_CHECK: u64 = to_builtin!(6);
-pub const UPDATE_ALREADY_SET: u64 = to_builtin!(7);
-pub const DB_INIT_FAILED: u64 = to_builtin!(8);
-pub const CALLER_ACCESS_DENIED: u64 = to_builtin!(9);
-pub const DB_NOT_FOUND: u64 = to_builtin!(10);
-pub const DB_SET_FAILED: u64 = to_builtin!(11);
-
-impl From<ContractError> for u64 {
+pub const CUSTOM_ZERO: i64 = to_builtin!(1);
+pub const INTERNAL_ERROR: i64 = to_builtin!(2);
+pub const SET_RETVAL_ERROR: i64 = to_builtin!(3);
+pub const IO_ERROR: i64 = to_builtin!(4);
+pub const NULLIFIER_EXIST_CHECK: i64 = to_builtin!(5);
+pub const VALID_MERKLE_CHECK: i64 = to_builtin!(6);
+pub const UPDATE_ALREADY_SET: i64 = to_builtin!(7);
+pub const DB_INIT_FAILED: i64 = to_builtin!(8);
+pub const CALLER_ACCESS_DENIED: i64 = to_builtin!(9);
+pub const DB_NOT_FOUND: i64 = to_builtin!(10);
+pub const DB_SET_FAILED: i64 = to_builtin!(11);
+
+impl From<ContractError> for i64 {
     fn from(err: ContractError) -> Self {
     fn from(err: ContractError) -> Self {
         match err {
         match err {
             ContractError::Internal => INTERNAL_ERROR,
             ContractError::Internal => INTERNAL_ERROR,
             ContractError::IoError(_) => IO_ERROR,
             ContractError::IoError(_) => IO_ERROR,
-            ContractError::SetUpdateError => SET_UPDATE_ERROR,
+            ContractError::SetRetvalError => SET_RETVAL_ERROR,
             ContractError::NullifierExistCheck => NULLIFIER_EXIST_CHECK,
             ContractError::NullifierExistCheck => NULLIFIER_EXIST_CHECK,
             ContractError::ValidMerkleCheck => VALID_MERKLE_CHECK,
             ContractError::ValidMerkleCheck => VALID_MERKLE_CHECK,
             ContractError::UpdateAlreadySet => UPDATE_ALREADY_SET,
             ContractError::UpdateAlreadySet => UPDATE_ALREADY_SET,
@@ -98,19 +97,19 @@ impl From<ContractError> for u64 {
                 if error == 0 {
                 if error == 0 {
                     CUSTOM_ZERO
                     CUSTOM_ZERO
                 } else {
                 } else {
-                    error as u64
+                    error as i64
                 }
                 }
             }
             }
         }
         }
     }
     }
 }
 }
 
 
-impl From<u64> for ContractError {
-    fn from(error: u64) -> Self {
+impl From<i64> for ContractError {
+    fn from(error: i64) -> Self {
         match error {
         match error {
             CUSTOM_ZERO => Self::Custom(0),
             CUSTOM_ZERO => Self::Custom(0),
             INTERNAL_ERROR => Self::Internal,
             INTERNAL_ERROR => Self::Internal,
-            SET_UPDATE_ERROR => Self::SetUpdateError,
+            SET_RETVAL_ERROR => Self::SetRetvalError,
             IO_ERROR => Self::IoError("Unknown".to_string()),
             IO_ERROR => Self::IoError("Unknown".to_string()),
             NULLIFIER_EXIST_CHECK => Self::NullifierExistCheck,
             NULLIFIER_EXIST_CHECK => Self::NullifierExistCheck,
             VALID_MERKLE_CHECK => Self::ValidMerkleCheck,
             VALID_MERKLE_CHECK => Self::ValidMerkleCheck,

+ 2 - 4
src/sdk/src/util.rs

@@ -5,9 +5,7 @@ pub fn set_return_data(data: &[u8]) -> Result<(), ContractError> {
     unsafe {
     unsafe {
         return match set_return_data_(data.as_ptr(), data.len() as u32) {
         return match set_return_data_(data.as_ptr(), data.len() as u32) {
             0 => Ok(()),
             0 => Ok(()),
-            -1 => Err(ContractError::SetUpdateError),
-            -2 => Err(ContractError::UpdateAlreadySet),
-            _ => unreachable!(),
+            errcode => Err(ContractError::from(errcode)),
         }
         }
     }
     }
 
 
@@ -17,5 +15,5 @@ pub fn set_return_data(data: &[u8]) -> Result<(), ContractError> {
 
 
 #[cfg(target_arch = "wasm32")]
 #[cfg(target_arch = "wasm32")]
 extern "C" {
 extern "C" {
-    fn set_return_data_(ptr: *const u8, len: u32) -> i32;
+    fn set_return_data_(ptr: *const u8, len: u32) -> i64;
 }
 }

+ 0 - 1
src/zk/circuit/lead_contract.rs

@@ -697,7 +697,6 @@ impl Circuit<pallas::Base> for LeadContract {
             |mut region| region.constrain_equal(sn_commit.cell(), coin1_serial.cell()),
             |mut region| region.constrain_equal(sn_commit.cell(), coin1_serial.cell()),
         )?;
         )?;
 
 
-
         info!("coin2_commit LHS: x {:?}", coin2_commitment.inner().x());
         info!("coin2_commit LHS: x {:?}", coin2_commitment.inner().x());
         info!("coin2_commit LHS: y {:?}", coin2_commitment.inner().y());
         info!("coin2_commit LHS: y {:?}", coin2_commitment.inner().y());
         info!("coin2_commit RHS: x {:?}", coin2_commit.inner().x());
         info!("coin2_commit RHS: x {:?}", coin2_commit.inner().x());