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

This commit is contained in:
x
2022-11-05 18:12:59 +00:00
parent 3aba630188
commit 1c9ff6fdba
11 changed files with 79 additions and 73 deletions

View File

@@ -4,11 +4,11 @@ use darkfi_sdk::{
define_contract,
error::ContractResult,
msg,
pasta::pallas,
tx::FuncCall,
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.
/// 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![
(
"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(),
vec![
pallas::Base::from(1),
pallas::Base::from(3),
pallas::Base::from(5),
pallas::Base::from(7)
]
)
pallas::Base::from(7),
],
),
];
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 (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 mut update_data = vec![Function::Foo as u8];
@@ -153,10 +144,10 @@ fn process_instruction(_cid: ContractId, ix: &[u8]) -> ContractResult {
msg!("update is set!");
// 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 => {
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 {
msg!("Make update!");
msg!("Make 1 update!");
match Function::from(update_data[0]) {
Function::Foo => {
let update: FooUpdate = deserialize(&update_data[1..])?;
msg!("fooupp");
//let update: FooUpdate = deserialize(&update_data[1..])?;
// Write the wagie to the db
//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")?;
//db_end_tx(db_handle, tx_handle)?;
}
_ => unreachable!(),
//_ => unreachable!(),
_ => {
msg!("other branch");
}
}
msg!("process_update() finished");
Ok(())
}

View File

@@ -16,7 +16,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::io::Cursor;
use darkfi::{
blockchain::Blockchain,
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_serial::{serialize, Decodable, Encodable, WriteExt};
use std::io::Cursor;
use smart_contract::{FooCallData, Function};

View File

@@ -74,7 +74,7 @@ impl ContractStore {
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)?;

View File

@@ -260,11 +260,11 @@ fn create_leadcoin(
let c_cm_msg = [*c_cm_coordinates.x(), *c_cm_coordinates.y()];
let c_cm_base: pallas::Base =
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);
tree_cm.append(&c_cm_node.clone());
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());
let c_root_cm = tree_cm.root(0).unwrap();
let c_cm_path = tree_cm.authentication_path(leaf_position, &c_root_cm).unwrap();

View File

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

View File

@@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use log::error;
use log::{debug, error};
use wasmer::{FunctionEnvMut, WasmPtr};
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();
match env.contract_section {
ContractSection::Exec | ContractSection::Metadata => {
let memory_view = env.memory_view(&ctx);
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.
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
}
_ => -1,
_ => darkfi_sdk::error::CALLER_ACCESS_DENIED,
}
}

View File

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

View File

@@ -21,7 +21,7 @@ use std::{mem::size_of, slice::from_raw_parts};
use crate::crypto::ContractId;
/// Success exit code for a contract
pub const SUCCESS: u64 = 0;
pub const SUCCESS: i64 = 0;
#[macro_export]
macro_rules! define_contract {
@@ -33,7 +33,7 @@ macro_rules! define_contract {
) => {
/// # Safety
#[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);
match $init_func(contract_id, &instruction_data) {
@@ -42,7 +42,7 @@ macro_rules! define_contract {
}
}
#[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);
match $exec_func(contract_id, &instruction_data) {
@@ -51,7 +51,7 @@ macro_rules! define_contract {
}
}
#[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);
match $apply_func(contract_id, &update_data) {
@@ -60,7 +60,7 @@ macro_rules! define_contract {
}
}
#[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);
match $metadata_func(contract_id, &instruction_data) {

View File

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

View File

@@ -5,9 +5,7 @@ pub fn set_return_data(data: &[u8]) -> Result<(), ContractError> {
unsafe {
return match set_return_data_(data.as_ptr(), data.len() as u32) {
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")]
extern "C" {
fn set_return_data_(ptr: *const u8, len: u32) -> i32;
fn set_return_data_(ptr: *const u8, len: u32) -> i64;
}

View File

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