mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 07:08:05 -05:00
wasm: fix error codes situation so we get contract errors from inside wasm
This commit is contained in:
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
11
src/error.rs
11
src/error.rs
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user