use neo_types::*;
#[cfg(not(target_arch = "wasm32"))]
use crate::storage::*;
#[cfg(not(target_arch = "wasm32"))]
use crate::syscalls::SYSCALLS;
#[cfg(not(target_arch = "wasm32"))]
use crate::NeoVMSyscallInfo;
#[cfg(target_arch = "wasm32")]
#[allow(dead_code)]
#[link(wasm_import_module = "neo")]
extern "C" {
#[link_name = "runtime_check_witness_bytes"]
fn neo_runtime_check_witness_bytes(ptr: i32, len: i32) -> i32;
#[link_name = "runtime_check_witness_i64"]
fn neo_runtime_check_witness_i64(account: i64) -> i32;
#[link_name = "runtime_get_time"]
fn neo_runtime_get_time() -> i64;
#[link_name = "runtime_get_calling_script_hash_i64"]
fn neo_runtime_get_calling_script_hash_i64() -> i64;
#[link_name = "runtime_get_entry_script_hash_i64"]
fn neo_runtime_get_entry_script_hash_i64() -> i64;
#[link_name = "runtime_get_executing_script_hash_i64"]
fn neo_runtime_get_executing_script_hash_i64() -> i64;
#[link_name = "runtime_get_calling_script_hash"]
fn neo_runtime_get_calling_script_hash(out_ptr: i32, out_cap: i32) -> i32;
#[link_name = "runtime_get_entry_script_hash"]
fn neo_runtime_get_entry_script_hash(out_ptr: i32, out_cap: i32) -> i32;
#[link_name = "runtime_get_executing_script_hash"]
fn neo_runtime_get_executing_script_hash(out_ptr: i32, out_cap: i32) -> i32;
#[link_name = "runtime_log"]
fn neo_runtime_log(ptr: i32, len: i32);
#[link_name = "runtime_notify"]
fn neo_runtime_notify(event_ptr: i32, event_len: i32);
#[link_name = "runtime_notify_with_state"]
fn neo_runtime_notify_with_state(
event_ptr: i32,
event_len: i32,
state_ptr: i32,
state_len: i32,
);
#[link_name = "check_sig"]
fn neo_runtime_check_sig(pubkey_ptr: i32, pubkey_len: i32, sig_ptr: i32, sig_len: i32) -> i32;
#[link_name = "check_multisig"]
fn neo_runtime_check_multisig(
pubkeys_ptr: i32,
pubkeys_len: i32,
sigs_ptr: i32,
sigs_len: i32,
) -> i32;
#[link_name = "verify_with_ecdsa"]
fn neo_runtime_verify_with_ecdsa(
msg_ptr: i32,
msg_len: i32,
pubkey_ptr: i32,
pubkey_len: i32,
sig_ptr: i32,
sig_len: i32,
curve: i32,
) -> i32;
#[link_name = "neo_storage_put_bytes"]
fn neo_storage_put_bytes(key_ptr: i32, key_len: i32, value_ptr: i32, value_len: i32);
#[link_name = "neo_storage_delete_bytes"]
fn neo_storage_delete_bytes(key_ptr: i32, key_len: i32);
#[link_name = "neo_storage_get_into"]
fn neo_storage_get_into(key_ptr: i32, key_len: i32, out_ptr: i32, out_cap: i32) -> i32;
#[link_name = "runtime_get_random"]
fn neo_runtime_get_random() -> i64;
#[link_name = "runtime_get_invocation_counter"]
fn neo_runtime_get_invocation_counter() -> i32;
#[link_name = "runtime_get_gas_left"]
fn neo_runtime_get_gas_left() -> i64;
#[link_name = "runtime_get_notifications"]
fn neo_runtime_get_notifications(
script_hash_ptr: i32,
script_hash_len: i32,
out_ptr: i32,
out_cap: i32,
) -> i32;
#[link_name = "runtime_current_signers"]
fn neo_runtime_current_signers(out_ptr: i32, out_cap: i32) -> i32;
#[link_name = "runtime_burn_gas"]
fn neo_runtime_burn_gas(gas: i64);
#[link_name = "runtime_get_script_container"]
fn neo_runtime_get_script_container(out_ptr: i32, out_cap: i32) -> i32;
#[link_name = "runtime_load_script"]
fn neo_runtime_load_script(
script_ptr: i32,
script_len: i32,
call_flags: i32,
args_ptr: i32,
args_len: i32,
);
#[link_name = "runtime_create_standard_account"]
fn neo_runtime_create_standard_account(
pubkey_ptr: i32,
pubkey_len: i32,
out_ptr: i32,
out_cap: i32,
) -> i32;
#[link_name = "runtime_create_multisig_account"]
fn neo_runtime_create_multisig_account(
threshold: i32,
pubkeys_ptr: i32,
pubkeys_len: i32,
out_ptr: i32,
out_cap: i32,
) -> i32;
#[link_name = "runtime_contract_call_native"]
fn neo_runtime_contract_call_native(
native_id: i32,
method_ptr: i32,
method_len: i32,
args_ptr: i32,
args_len: i32,
out_ptr: i32,
out_cap: i32,
) -> i32;
#[link_name = "runtime_get_call_flags"]
fn neo_runtime_get_call_flags() -> i32;
#[link_name = "runtime_get_storage_context"]
fn neo_runtime_get_storage_context() -> i32;
#[link_name = "runtime_get_read_only_context"]
fn neo_runtime_get_read_only_context() -> i32;
#[link_name = "runtime_storage_as_read_only"]
fn neo_runtime_storage_as_read_only(context_id: i32) -> i32;
#[link_name = "runtime_storage_find"]
fn neo_runtime_storage_find(
context_id: i32,
prefix_ptr: i32,
prefix_len: i32,
options: i32,
out_ptr: i32,
out_cap: i32,
) -> i32;
#[link_name = "runtime_iterator_next"]
fn neo_runtime_iterator_next(iterator_id: i32) -> i32;
#[link_name = "runtime_iterator_value"]
fn neo_runtime_iterator_value(iterator_id: i32, out_ptr: i32, out_cap: i32) -> i32;
#[link_name = "protocol_get_network"]
fn neo_protocol_get_network() -> i32;
#[link_name = "protocol_get_address_version"]
fn neo_protocol_get_address_version() -> i32;
#[link_name = "protocol_get_trigger"]
fn neo_protocol_get_trigger() -> i32;
#[link_name = "neo_contract_call"]
fn neo_contract_call(
hash_ptr: i32,
hash_len: i32,
method_ptr: i32,
method_len: i32,
args_ptr: i32,
args_len: i32,
call_flags: i32,
out_ptr: i32,
out_cap: i32,
) -> i32;
#[link_name = "neo_load_script"]
fn neo_load_script(
script_ptr: i32,
script_len: i32,
call_flags: i32,
args_ptr: i32,
args_len: i32,
) -> i32;
#[link_name = "neo_call_native"]
fn neo_call_native(
native_id: i32,
method_ptr: i32,
method_len: i32,
args_ptr: i32,
args_len: i32,
out_ptr: i32,
out_cap: i32,
) -> i32;
}
#[cfg(not(target_arch = "wasm32"))]
const CALL_FLAGS_VALID_MASK: i32 = 0x0F;
#[cfg(not(target_arch = "wasm32"))]
const CALL_FLAGS_READ_STATES: i32 = 0x01;
#[cfg(not(target_arch = "wasm32"))]
const CALL_FLAGS_WRITE_STATES: i32 = 0x02;
#[cfg(not(target_arch = "wasm32"))]
fn find_syscall(name: &str) -> Option<&'static NeoVMSyscallInfo> {
SYSCALLS.iter().find(|info| info.name == name)
}
#[cfg(not(target_arch = "wasm32"))]
fn syscall_hash(name: &str) -> NeoResult<u32> {
find_syscall(name)
.map(|info| info.hash)
.ok_or_else(|| NeoError::new(&format!("unknown syscall: {name}")))
}
fn default_value_for(return_type: &str) -> NeoValue {
match return_type {
"Void" => NeoValue::Null,
"Boolean" => NeoBoolean::FALSE.into(),
"Integer" => NeoInteger::new(0).into(),
"Hash160" => NeoByteString::new(vec![0u8; 20]).into(),
"ByteString" => NeoByteString::new(vec![0u8; 1]).into(),
"String" => NeoString::from_str("Neo N3").into(),
"Array" => NeoArray::<NeoValue>::new().into(),
"Iterator" => NeoArray::<NeoValue>::new().into(),
"StackItem" => NeoArray::<NeoValue>::new().into(),
"StorageContext" => NeoValue::Null,
_ => NeoValue::Null,
}
}
fn value_matches_param_type(value: &NeoValue, param_type: &str) -> bool {
match param_type {
"Boolean" => value.as_boolean().is_some(),
"Integer" => value.as_integer().is_some(),
"Hash160" => {
value.is_null()
|| value
.as_byte_string()
.map(|bytes| bytes.len() == 20)
.unwrap_or(false)
}
"ByteString" => value.as_byte_string().is_some(),
"String" => value.as_string().is_some(),
"Array" => value.as_array().is_some(),
"Iterator" => value.as_array().is_some(),
"StorageContext" => value.is_null() || value.as_integer().is_some(),
"StackItem" | "Any" | "ExecutionContext" => true,
_ => true,
}
}
#[cfg(not(target_arch = "wasm32"))]
fn call_flags_allow_write(flags: i32) -> bool {
(flags & CALL_FLAGS_WRITE_STATES) != 0
}
#[cfg(not(target_arch = "wasm32"))]
fn call_flags_allow_read(flags: i32) -> bool {
(flags & CALL_FLAGS_READ_STATES) != 0
}
#[cfg(not(target_arch = "wasm32"))]
fn hash160_prefix_i64(hash: &[u8; 20]) -> i64 {
let mut buf = [0u8; 8];
buf.copy_from_slice(&hash[..8]);
i64::from_le_bytes(buf)
}
pub fn neovm_syscall(hash: u32, args: &[NeoValue]) -> NeoResult<NeoValue> {
let registry = crate::NeoVMSyscallRegistry::get_instance();
let info = registry
.get_syscall_by_hash(hash)
.ok_or_else(|| NeoError::new(&format!("unknown syscall hash: 0x{hash:08x}")))?;
if args.len() != info.parameters.len() {
return Err(NeoError::new(&format!(
"invalid syscall argument count for {}: expected {}, got {}",
info.name,
info.parameters.len(),
args.len()
)));
}
for (index, (arg, expected_type)) in args.iter().zip(info.parameters.iter()).enumerate() {
if !value_matches_param_type(arg, expected_type) {
return Err(NeoError::new(&format!(
"invalid syscall argument type for {} param #{}: expected {}",
info.name, index, expected_type
)));
}
}
#[cfg(not(target_arch = "wasm32"))]
{
if info.name == "System.Runtime.CheckWitness" {
let has_witness = args
.first()
.and_then(NeoValue::as_byte_string)
.map(|account| has_active_witness(account.as_slice()))
.unwrap_or(false);
return Ok(NeoBoolean::new(has_witness).into());
}
if info.name == "System.Crypto.CheckSig" {
let results = active_crypto_verification_results();
return Ok(NeoBoolean::new(results.check_sig).into());
}
if info.name == "System.Crypto.CheckMultisig" {
let results = active_crypto_verification_results();
return Ok(NeoBoolean::new(results.check_multisig).into());
}
if info.name == "Neo.Crypto.VerifyWithECDsa" {
let results = active_crypto_verification_results();
return Ok(NeoBoolean::new(results.verify_with_ecdsa).into());
}
if info.name == "System.Runtime.GetCallingScriptHash" {
return Ok(NeoByteString::from_slice(¤t_calling_script_hash()).into());
}
if info.name == "System.Runtime.GetEntryScriptHash" {
return Ok(NeoByteString::from_slice(¤t_entry_script_hash()).into());
}
if info.name == "System.Runtime.GetExecutingScriptHash" {
return Ok(NeoByteString::from_slice(¤t_executing_script_hash()).into());
}
if info.name == "System.Contract.GetCallFlags" {
return Ok(NeoInteger::new(current_call_flags()).into());
}
if info.name == "System.Runtime.GetRandom" {
return Ok(NeoInteger::new(
*crate::storage::ACTIVE_RANDOM
.read()
.expect("ACTIVE_RANDOM poisoned"),
)
.into());
}
if info.name == "System.Runtime.GetTime" {
return Ok(NeoInteger::new(
*crate::storage::ACTIVE_TIME
.read()
.expect("ACTIVE_TIME poisoned"),
)
.into());
}
if info.name == "System.Runtime.GetInvocationCounter" {
return Ok(NeoInteger::new(
*crate::storage::ACTIVE_INVOCATION_COUNTER
.read()
.expect("ACTIVE_INVOCATION_COUNTER poisoned"),
)
.into());
}
if info.name == "System.Runtime.GasLeft" {
return Ok(NeoInteger::new(
*crate::storage::ACTIVE_GAS_LEFT
.read()
.expect("ACTIVE_GAS_LEFT poisoned"),
)
.into());
}
if info.name == "System.Runtime.CurrentSigners" {
let witnesses = crate::storage::ACTIVE_WITNESSES
.read()
.expect("ACTIVE_WITNESSES poisoned");
let arr: NeoArray<NeoValue> = witnesses
.iter()
.map(|w| {
let entry: NeoArray<NeoValue> = vec![
NeoValue::from(NeoByteString::from_slice(w)),
NeoValue::from(NeoInteger::new(0x01)), ]
.into_iter()
.collect();
NeoValue::from(entry)
})
.collect();
return Ok(NeoValue::from(arr));
}
if info.name == "System.Runtime.GetNotifications" {
use crate::host_notifications::take;
let recorded = take();
let arr: NeoArray<NeoValue> = recorded
.into_iter()
.map(|n| {
let entry: NeoArray<NeoValue> = vec![
NeoValue::from(NeoString::from_str(&n.event)),
NeoValue::from(n.state.into_iter().collect::<NeoArray<NeoValue>>()),
]
.into_iter()
.collect();
NeoValue::from(entry)
})
.collect();
return Ok(NeoValue::from(arr));
}
}
Ok(default_value_for(info.return_type))
}
pub struct NeoVMSyscall;
impl NeoVMSyscall {
#[cfg(not(target_arch = "wasm32"))]
fn parse_hash160(hash: &NeoByteString) -> NeoResult<[u8; 20]> {
if hash.len() != 20 {
return Err(NeoError::InvalidArgument);
}
let mut value = [0u8; 20];
value.copy_from_slice(hash.as_slice());
Ok(value)
}
#[cfg(target_arch = "wasm32")]
fn read_script_hash_extern(
read: unsafe extern "C" fn(out_ptr: i32, out_cap: i32) -> i32,
) -> NeoResult<NeoByteString> {
let mut buf = [0u8; 20];
let written = unsafe { (read)(buf.as_mut_ptr() as i32, buf.len() as i32) };
if written < 0 {
return Err(NeoError::InvalidState);
}
let len = (written as usize).min(buf.len());
Ok(NeoByteString::from_slice(&buf[..len]))
}
#[cfg(not(target_arch = "wasm32"))]
fn parse_call_flags(flags: &NeoInteger) -> NeoResult<i32> {
let parsed = flags.as_i32_saturating();
if parsed < 0 || (parsed & !CALL_FLAGS_VALID_MASK) != 0 {
return Err(NeoError::InvalidArgument);
}
Ok(parsed)
}
#[cfg(not(target_arch = "wasm32"))]
fn begin_contract_invocation_with_flags(
next_executing: &NeoByteString,
call_flags: i32,
) -> NeoResult<()> {
if call_flags < 0 || (call_flags & !CALL_FLAGS_VALID_MASK) != 0 {
return Err(NeoError::InvalidArgument);
}
push_current_executing_script_hash(Self::parse_hash160(next_executing)?, call_flags)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_contract_hash(hash: &NeoByteString) -> NeoResult<()> {
set_current_contract_hash(Self::parse_hash160(hash)?);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_script_hashes(
calling: &NeoByteString,
entry: &NeoByteString,
executing: &NeoByteString,
) -> NeoResult<()> {
set_current_script_hashes(
Self::parse_hash160(calling)?,
Self::parse_hash160(entry)?,
Self::parse_hash160(executing)?,
);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_calling_script_hash(hash: &NeoByteString) -> NeoResult<()> {
set_current_calling_script_hash(Self::parse_hash160(hash)?);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_entry_script_hash(hash: &NeoByteString) -> NeoResult<()> {
set_current_entry_script_hash(Self::parse_hash160(hash)?);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_executing_script_hash(hash: &NeoByteString) -> NeoResult<()> {
set_current_executing_script_hash(Self::parse_hash160(hash)?);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_call_flags(call_flags: &NeoInteger) -> NeoResult<()> {
set_current_call_flags(Self::parse_call_flags(call_flags)?);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn begin_contract_invocation(next_executing: &NeoByteString) -> NeoResult<()> {
Self::begin_contract_invocation_with_flags(next_executing, current_call_flags())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn end_contract_invocation() -> NeoResult<()> {
pop_current_script_hash_frame()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn with_contract_invocation<T, F>(
next_executing: &NeoByteString,
operation: F,
) -> NeoResult<T>
where
F: FnOnce() -> NeoResult<T>,
{
Self::begin_contract_invocation(next_executing)?;
let operation_result = operation();
let unwind_result = Self::end_contract_invocation();
match (operation_result, unwind_result) {
(Ok(value), Ok(())) => Ok(value),
(Err(err), Ok(())) => Err(err),
(Ok(_), Err(unwind_err)) => Err(unwind_err),
(Err(operation_err), Err(unwind_err)) => Err(NeoError::new(&format!(
"invocation operation failed ({}) and frame unwind failed ({})",
operation_err.message(),
unwind_err.message()
))),
}
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_contract_hash(_hash: &NeoByteString) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_script_hashes(
_calling: &NeoByteString,
_entry: &NeoByteString,
_executing: &NeoByteString,
) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_calling_script_hash(_hash: &NeoByteString) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_entry_script_hash(_hash: &NeoByteString) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_executing_script_hash(_hash: &NeoByteString) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_call_flags(_call_flags: &NeoInteger) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn begin_contract_invocation(_next_executing: &NeoByteString) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn end_contract_invocation() -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn with_contract_invocation<T, F>(
_next_executing: &NeoByteString,
operation: F,
) -> NeoResult<T>
where
F: FnOnce() -> NeoResult<T>,
{
operation()
}
#[cfg(not(target_arch = "wasm32"))]
pub fn reset_host_state() -> NeoResult<()> {
STORAGE_STATE.reset()?;
reset_current_contract_hash();
clear_active_witnesses();
reset_crypto_verification_results();
*crate::storage::ACTIVE_RANDOM
.write()
.expect("ACTIVE_RANDOM poisoned") = 0;
*crate::storage::ACTIVE_TIME
.write()
.expect("ACTIVE_TIME poisoned") = 0;
*crate::storage::ACTIVE_GAS_LEFT
.write()
.expect("ACTIVE_GAS_LEFT poisoned") = 0;
*crate::storage::ACTIVE_INVOCATION_COUNTER
.write()
.expect("ACTIVE_INVOCATION_COUNTER poisoned") = 0;
crate::host_notifications::reset();
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn reset_host_state() -> NeoResult<()> {
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn seed_storage(entries: &[(&[u8], &[u8])]) -> NeoResult<()> {
for (k, v) in entries {
STORAGE_STATE.put(k.to_vec(), v.to_vec());
}
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn seed_storage(_entries: &[(&[u8], &[u8])]) -> NeoResult<()> {
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
fn call_value(name: &str, args: &[NeoValue]) -> NeoResult<NeoValue> {
neovm_syscall(syscall_hash(name)?, args)
}
#[cfg(not(target_arch = "wasm32"))]
fn call_integer(name: &str) -> NeoResult<NeoInteger> {
let value = Self::call_value(name, &[])?;
value.as_integer().cloned().ok_or(NeoError::InvalidType)
}
#[cfg(not(target_arch = "wasm32"))]
fn call_boolean(name: &str, args: &[NeoValue]) -> NeoResult<NeoBoolean> {
let value = Self::call_value(name, args)?;
value.as_boolean().ok_or(NeoError::InvalidType)
}
#[cfg(not(target_arch = "wasm32"))]
fn call_bytes_with_args(name: &str, args: &[NeoValue]) -> NeoResult<NeoByteString> {
let value = Self::call_value(name, args)?;
value.as_byte_string().cloned().ok_or(NeoError::InvalidType)
}
#[cfg(not(target_arch = "wasm32"))]
fn call_string(name: &str) -> NeoResult<NeoString> {
let value = Self::call_value(name, &[])?;
value.as_string().cloned().ok_or(NeoError::InvalidType)
}
#[cfg(not(target_arch = "wasm32"))]
fn call_array(name: &str, args: &[NeoValue]) -> NeoResult<NeoArray<NeoValue>> {
let value = Self::call_value(name, args)?;
value.as_array().cloned().ok_or(NeoError::InvalidType)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_witnesses(witnesses: &[NeoByteString]) -> NeoResult<()> {
crate::storage::set_active_witnesses(
witnesses.iter().map(|witness| witness.as_slice().to_vec()),
);
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_witnesses(_witnesses: &[NeoByteString]) -> NeoResult<()> {
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_random(value: i64) -> NeoResult<()> {
*crate::storage::ACTIVE_RANDOM
.write()
.expect("ACTIVE_RANDOM poisoned") = value;
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_random(_value: i64) -> NeoResult<()> {
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_time(value: i64) -> NeoResult<()> {
*crate::storage::ACTIVE_TIME
.write()
.expect("ACTIVE_TIME poisoned") = value;
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_time(_value: i64) -> NeoResult<()> {
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_invocation_counter(value: i32) -> NeoResult<()> {
*crate::storage::ACTIVE_INVOCATION_COUNTER
.write()
.expect("ACTIVE_INVOCATION_COUNTER poisoned") = value;
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_invocation_counter(_value: i32) -> NeoResult<()> {
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_active_gas_left(value: i64) -> NeoResult<()> {
*crate::storage::ACTIVE_GAS_LEFT
.write()
.expect("ACTIVE_GAS_LEFT poisoned") = value;
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_active_gas_left(_value: i64) -> NeoResult<()> {
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_crypto_verification_results(check_sig: bool, check_multisig: bool) -> NeoResult<()> {
Self::set_crypto_verification_results_full(check_sig, check_multisig, check_sig)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_crypto_verification_results_full(
check_sig: bool,
check_multisig: bool,
verify_with_ecdsa: bool,
) -> NeoResult<()> {
crate::storage::set_crypto_verification_results(CryptoVerificationResults {
check_sig,
check_multisig,
verify_with_ecdsa,
});
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn set_verify_with_ecdsa_result(result: bool) -> NeoResult<()> {
let mut current = active_crypto_verification_results();
current.verify_with_ecdsa = result;
crate::storage::set_crypto_verification_results(current);
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_crypto_verification_results(
_check_sig: bool,
_check_multisig: bool,
) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_crypto_verification_results_full(
_check_sig: bool,
_check_multisig: bool,
_verify_with_ecdsa: bool,
) -> NeoResult<()> {
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn set_verify_with_ecdsa_result(_result: bool) -> NeoResult<()> {
Ok(())
}
pub fn get_time() -> NeoResult<NeoInteger> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoInteger::new(unsafe { neo_runtime_get_time() }));
}
#[cfg(not(target_arch = "wasm32"))]
{
Self::call_integer("System.Runtime.GetTime")
}
}
pub fn get_time_i64() -> NeoResult<i64> {
#[cfg(target_arch = "wasm32")]
{
return Ok(unsafe { neo_runtime_get_time() });
}
#[cfg(not(target_arch = "wasm32"))]
{
Self::call_integer("System.Runtime.GetTime")?.try_into_i64()
}
}
pub fn check_witness(account: &NeoByteString) -> NeoResult<NeoBoolean> {
Self::check_witness_bytes(account.as_slice())
}
pub fn check_witness_bytes(account: &[u8]) -> NeoResult<NeoBoolean> {
#[cfg(target_arch = "wasm32")]
{
let result = unsafe {
neo_runtime_check_witness_bytes(account.as_ptr() as i32, account.len() as i32)
};
return Ok(NeoBoolean::new(result != 0));
}
#[cfg(not(target_arch = "wasm32"))]
{
let args = [NeoValue::from(NeoByteString::from_slice(account))];
Self::call_boolean("System.Runtime.CheckWitness", &args)
}
}
pub fn check_witness_i64(account: i64) -> NeoResult<NeoBoolean> {
#[cfg(target_arch = "wasm32")]
{
let result = unsafe { neo_runtime_check_witness_i64(account) };
return Ok(NeoBoolean::new(result != 0));
}
#[cfg(not(target_arch = "wasm32"))]
{
let mut bytes = [0u8; 20];
bytes[..8].copy_from_slice(&account.to_le_bytes());
Self::check_witness_bytes(&bytes)
}
}
pub fn notify(event: &NeoString, state: &NeoArray<NeoValue>) -> NeoResult<()> {
#[cfg(target_arch = "wasm32")]
{
let state_bytes = serialise_array(state);
unsafe {
neo_runtime_notify_with_state(
event.as_str().as_ptr() as i32,
event.as_str().len() as i32,
state_bytes.as_ptr() as i32,
state_bytes.len() as i32,
);
}
crate::host_notifications::record(event, state);
return Ok(());
}
#[cfg(not(target_arch = "wasm32"))]
{
let event_bytes = NeoByteString::from_slice(event.as_str().as_bytes());
let args = [NeoValue::from(event_bytes), NeoValue::from(state.clone())];
neovm_syscall(syscall_hash("System.Runtime.Notify")?, &args)?;
crate::host_notifications::record(event, state);
Ok(())
}
}
pub fn notify_event(event: &str) -> NeoResult<()> {
#[cfg(target_arch = "wasm32")]
unsafe {
neo_runtime_notify(event.as_ptr() as i32, event.len() as i32);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
{
let label = NeoString::from_str(event);
let state = NeoArray::new();
Self::notify(&label, &state)
}
}
pub fn log(message: &NeoString) -> NeoResult<()> {
#[cfg(target_arch = "wasm32")]
unsafe {
let message = message.as_str();
neo_runtime_log(message.as_ptr() as i32, message.len() as i32);
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
{
let message_bytes = NeoByteString::from_slice(message.as_str().as_bytes());
let args = [NeoValue::from(message_bytes)];
neovm_syscall(syscall_hash("System.Runtime.Log")?, &args)?;
Ok(())
}
}
pub fn platform() -> NeoResult<NeoString> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoString::from_str("NEO"));
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_string("System.Runtime.Platform")
}
pub fn get_trigger() -> NeoResult<NeoInteger> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoInteger::new(unsafe { neo_protocol_get_trigger() }));
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_integer("System.Runtime.GetTrigger")
}
pub fn get_invocation_counter() -> NeoResult<NeoInteger> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoInteger::new(unsafe {
neo_runtime_get_invocation_counter()
}));
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_integer("System.Runtime.GetInvocationCounter")
}
pub fn get_random() -> NeoResult<NeoInteger> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoInteger::new(unsafe { neo_runtime_get_random() }));
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_integer("System.Runtime.GetRandom")
}
pub fn get_network() -> NeoResult<NeoInteger> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoInteger::new(unsafe { neo_protocol_get_network() }));
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_integer("System.Runtime.GetNetwork")
}
pub fn get_address_version() -> NeoResult<NeoInteger> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoInteger::new(unsafe {
neo_protocol_get_address_version()
}));
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_integer("System.Runtime.GetAddressVersion")
}
pub fn get_gas_left() -> NeoResult<NeoInteger> {
#[cfg(target_arch = "wasm32")]
{
return Ok(NeoInteger::new(unsafe { neo_runtime_get_gas_left() }));
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_integer("System.Runtime.GasLeft")
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_calling_script_hash() -> NeoResult<NeoByteString> {
Ok(NeoByteString::from_slice(¤t_calling_script_hash()))
}
#[cfg(target_arch = "wasm32")]
pub fn get_calling_script_hash() -> NeoResult<NeoByteString> {
Self::read_script_hash_extern(neo_runtime_get_calling_script_hash)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_calling_script_hash_i64() -> NeoResult<i64> {
Ok(hash160_prefix_i64(¤t_calling_script_hash()))
}
#[cfg(target_arch = "wasm32")]
pub fn get_calling_script_hash_i64() -> NeoResult<i64> {
Ok(unsafe { neo_runtime_get_calling_script_hash_i64() })
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_entry_script_hash() -> NeoResult<NeoByteString> {
Ok(NeoByteString::from_slice(¤t_entry_script_hash()))
}
#[cfg(target_arch = "wasm32")]
pub fn get_entry_script_hash() -> NeoResult<NeoByteString> {
Self::read_script_hash_extern(neo_runtime_get_entry_script_hash)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_entry_script_hash_i64() -> NeoResult<i64> {
Ok(hash160_prefix_i64(¤t_entry_script_hash()))
}
#[cfg(target_arch = "wasm32")]
pub fn get_entry_script_hash_i64() -> NeoResult<i64> {
Ok(unsafe { neo_runtime_get_entry_script_hash_i64() })
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_executing_script_hash() -> NeoResult<NeoByteString> {
Ok(NeoByteString::from_slice(¤t_executing_script_hash()))
}
#[cfg(target_arch = "wasm32")]
pub fn get_executing_script_hash() -> NeoResult<NeoByteString> {
Self::read_script_hash_extern(neo_runtime_get_executing_script_hash)
}
#[cfg(not(target_arch = "wasm32"))]
pub fn get_executing_script_hash_i64() -> NeoResult<i64> {
Ok(hash160_prefix_i64(¤t_executing_script_hash()))
}
#[cfg(target_arch = "wasm32")]
pub fn get_executing_script_hash_i64() -> NeoResult<i64> {
Ok(unsafe { neo_runtime_get_executing_script_hash_i64() })
}
pub fn get_notifications(script_hash: Option<&NeoByteString>) -> NeoResult<NeoArray<NeoValue>> {
#[cfg(target_arch = "wasm32")]
{
let mut buf = vec![0u8; 4096];
let written = if let Some(hash) = script_hash {
unsafe {
neo_runtime_get_notifications(
hash.as_slice().as_ptr() as i32,
hash.len() as i32,
buf.as_mut_ptr() as i32,
buf.len() as i32,
)
}
} else {
unsafe {
neo_runtime_get_notifications(
std::ptr::null::<u8>() as i32,
0,
buf.as_mut_ptr() as i32,
buf.len() as i32,
)
}
};
if written < 0 {
return Err(NeoError::InvalidState);
}
let _ = (written as usize).min(buf.len());
Ok(NeoArray::new())
}
#[cfg(not(target_arch = "wasm32"))]
{
let script_hash_value = script_hash
.map(|hash| NeoValue::from(hash.clone()))
.unwrap_or(NeoValue::Null);
let args = [script_hash_value];
Self::call_array("System.Runtime.GetNotifications", &args)
}
}
pub fn get_script_container() -> NeoResult<NeoArray<NeoValue>> {
#[cfg(target_arch = "wasm32")]
{
let mut buf = vec![0u8; 4096];
let written = unsafe {
neo_runtime_get_script_container(buf.as_mut_ptr() as i32, buf.len() as i32)
};
if written < 0 {
return Err(NeoError::InvalidState);
}
let _ = (written as usize).min(buf.len());
Ok(NeoArray::new())
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_array("System.Runtime.GetScriptContainer", &[])
}
pub fn burn_gas(gas: &NeoInteger) -> NeoResult<()> {
#[cfg(target_arch = "wasm32")]
{
let datoshi = gas.as_i64_saturating();
if datoshi <= 0 {
return Err(NeoError::new("GAS must be positive"));
}
unsafe { neo_runtime_burn_gas(datoshi) };
return Ok(());
}
#[cfg(not(target_arch = "wasm32"))]
{
let args = [NeoValue::from(gas.clone())];
Self::call_value("System.Runtime.BurnGas", &args)?;
Ok(())
}
}
pub fn current_signers() -> NeoResult<NeoArray<NeoValue>> {
#[cfg(target_arch = "wasm32")]
{
let mut buf = vec![0u8; 4096];
let written =
unsafe { neo_runtime_current_signers(buf.as_mut_ptr() as i32, buf.len() as i32) };
if written < 0 {
return Err(NeoError::InvalidState);
}
let _ = (written as usize).min(buf.len());
Ok(NeoArray::new())
}
#[cfg(not(target_arch = "wasm32"))]
Self::call_array("System.Runtime.CurrentSigners", &[])
}
pub fn load_script(
script: &NeoByteString,
call_flags: &NeoInteger,
args: &NeoArray<NeoValue>,
) -> NeoResult<()> {
#[cfg(not(target_arch = "wasm32"))]
{
let values = [
NeoValue::from(script.clone()),
NeoValue::from(call_flags.clone()),
NeoValue::from(args.clone()),
];
Self::call_value("System.Runtime.LoadScript", &values)?;
Ok(())
}
#[cfg(target_arch = "wasm32")]
{
let _ = (call_flags, args);
let script_bytes = script.as_slice();
let status = unsafe {
neo_load_script(
script_bytes.as_ptr() as i32,
script_bytes.len() as i32,
0x0F,
0,
0,
)
};
if status < 0 {
return Err(NeoError::Wasm32CrossCallUnavailable {
syscall: "System.Runtime.LoadScript",
});
}
Ok(())
}
}
pub fn contract_call(
script_hash: &NeoByteString,
method: &NeoString,
call_flags: &NeoInteger,
args: &NeoArray<NeoValue>,
) -> NeoResult<NeoValue> {
#[cfg(not(target_arch = "wasm32"))]
{
let values = [
NeoValue::from(script_hash.clone()),
NeoValue::from(method.clone()),
NeoValue::from(call_flags.clone()),
NeoValue::from(args.clone()),
];
let parsed_flags = Self::parse_call_flags(call_flags)?;
Self::begin_contract_invocation_with_flags(script_hash, parsed_flags)?;
let call_result = Self::call_value("System.Contract.Call", &values);
let unwind_result = Self::end_contract_invocation();
match (call_result, unwind_result) {
(Ok(value), Ok(())) => Ok(value),
(Err(err), Ok(())) => Err(err),
(Ok(_), Err(unwind_err)) => Err(unwind_err),
(Err(call_err), Err(unwind_err)) => Err(NeoError::new(&format!(
"contract_call failed ({}) and invocation unwind failed ({})",
call_err.message(),
unwind_err.message()
))),
}
}
#[cfg(target_arch = "wasm32")]
{
let _ = (call_flags, args);
let hash_bytes = script_hash.as_slice();
let method_bytes = method.as_str().as_bytes();
let mut out_buf = [0u8; 16];
let status = unsafe {
neo_contract_call(
hash_bytes.as_ptr() as i32,
hash_bytes.len() as i32,
method_bytes.as_ptr() as i32,
method_bytes.len() as i32,
0,
0,
0x0F,
out_buf.as_mut_ptr() as i32,
out_buf.len() as i32,
)
};
let _ = status;
Ok(NeoValue::Null)
}
}
pub fn contract_call_native(native_id: &NeoInteger) -> NeoResult<NeoValue> {
#[cfg(not(target_arch = "wasm32"))]
{
let values = [NeoValue::from(native_id.clone())];
Self::call_value("System.Contract.CallNative", &values)
}
#[cfg(target_arch = "wasm32")]
{
let mut out_buf = [0u8; 16];
let status = unsafe {
neo_call_native(
native_id.try_as_i64().unwrap_or(0) as i32,
0,
0,
0,
0,
out_buf.as_mut_ptr() as i32,
out_buf.len() as i32,
)
};
if status < 0 {
return Err(NeoError::Wasm32CrossCallUnavailable {
syscall: "System.Contract.CallNative",
});
}
Ok(NeoValue::Null)
}
}
pub fn get_call_flags() -> NeoResult<NeoInteger> {
#[cfg(not(target_arch = "wasm32"))]
{
Ok(NeoInteger::new(current_call_flags()))
}
#[cfg(target_arch = "wasm32")]
{
Ok(NeoInteger::new(unsafe { neo_runtime_get_call_flags() }))
}
}
pub fn create_standard_account(pubkey: &NeoByteString) -> NeoResult<NeoByteString> {
#[cfg(not(target_arch = "wasm32"))]
{
let values = [NeoValue::from(pubkey.clone())];
Self::call_bytes_with_args("System.Contract.CreateStandardAccount", &values)
}
#[cfg(target_arch = "wasm32")]
{
let mut buf = [0u8; 20];
let written = unsafe {
neo_runtime_create_standard_account(
pubkey.as_slice().as_ptr() as i32,
pubkey.len() as i32,
buf.as_mut_ptr() as i32,
buf.len() as i32,
)
};
if written < 0 {
return Err(NeoError::InvalidState);
}
let len = (written as usize).min(buf.len());
Ok(NeoByteString::from_slice(&buf[..len]))
}
}
pub fn create_multisig_account(
threshold: &NeoInteger,
public_keys: &NeoArray<NeoValue>,
) -> NeoResult<NeoByteString> {
let values = [
NeoValue::from(threshold.clone()),
NeoValue::from(public_keys.clone()),
];
#[cfg(not(target_arch = "wasm32"))]
{
Self::call_bytes_with_args("System.Contract.CreateMultisigAccount", &values)
}
#[cfg(target_arch = "wasm32")]
{
let pk_bytes: Vec<u8> = public_keys
.iter()
.filter_map(|v| v.as_byte_string())
.flat_map(|bs| bs.as_slice().to_vec())
.collect();
let mut buf = [0u8; 20];
let written = unsafe {
neo_runtime_create_multisig_account(
threshold.as_i32_saturating(),
pk_bytes.as_ptr() as i32,
pk_bytes.len() as i32,
buf.as_mut_ptr() as i32,
buf.len() as i32,
)
};
let _ = values;
if written < 0 {
return Err(NeoError::InvalidState);
}
let len = (written as usize).min(buf.len());
Ok(NeoByteString::from_slice(&buf[..len]))
}
}
pub fn native_on_persist() -> NeoResult<()> {
#[cfg(not(target_arch = "wasm32"))]
{
Self::call_value("System.Contract.NativeOnPersist", &[])?;
Ok(())
}
#[cfg(target_arch = "wasm32")]
{
Err(NeoError::new(
"System.Contract.NativeOnPersist is only valid inside native contracts",
))
}
}
pub fn native_post_persist() -> NeoResult<()> {
#[cfg(not(target_arch = "wasm32"))]
{
Self::call_value("System.Contract.NativePostPersist", &[])?;
Ok(())
}
#[cfg(target_arch = "wasm32")]
{
Err(NeoError::new(
"System.Contract.NativePostPersist is only valid inside native contracts",
))
}
}
pub fn check_sig(pubkey: &NeoByteString, signature: &NeoByteString) -> NeoResult<NeoBoolean> {
#[cfg(target_arch = "wasm32")]
{
let result = unsafe {
neo_runtime_check_sig(
pubkey.as_slice().as_ptr() as i32,
pubkey.len() as i32,
signature.as_slice().as_ptr() as i32,
signature.len() as i32,
)
};
return Ok(NeoBoolean::new(result != 0));
}
#[cfg(not(target_arch = "wasm32"))]
{
let values = [
NeoValue::from(pubkey.clone()),
NeoValue::from(signature.clone()),
];
Self::call_boolean("System.Crypto.CheckSig", &values)
}
}
pub fn check_multisig(
pubkeys: &NeoArray<NeoValue>,
signatures: &NeoArray<NeoValue>,
) -> NeoResult<NeoBoolean> {
#[cfg(target_arch = "wasm32")]
{
let mut pk = Vec::new();
for v in pubkeys.iter() {
let Some(b) = v.as_byte_string() else {
return Err(NeoError::InvalidType);
};
pk.extend_from_slice(b.as_slice());
}
let mut sg = Vec::new();
for v in signatures.iter() {
let Some(b) = v.as_byte_string() else {
return Err(NeoError::InvalidType);
};
sg.extend_from_slice(b.as_slice());
}
let result = unsafe {
neo_runtime_check_multisig(
pk.as_ptr() as i32,
pk.len() as i32,
sg.as_ptr() as i32,
sg.len() as i32,
)
};
Ok(NeoBoolean::new(result != 0))
}
#[cfg(not(target_arch = "wasm32"))]
{
let values = [
NeoValue::from(pubkeys.clone()),
NeoValue::from(signatures.clone()),
];
Self::call_boolean("System.Crypto.CheckMultisig", &values)
}
}
pub fn verify_with_ecdsa(
message: &NeoByteString,
public_key: &NeoByteString,
signature: &NeoByteString,
curve: &NeoInteger,
) -> NeoResult<NeoBoolean> {
#[cfg(target_arch = "wasm32")]
{
let curve_i = curve.try_as_i32().unwrap_or(0);
let result = unsafe {
neo_runtime_verify_with_ecdsa(
message.as_slice().as_ptr() as i32,
message.len() as i32,
public_key.as_slice().as_ptr() as i32,
public_key.len() as i32,
signature.as_slice().as_ptr() as i32,
signature.len() as i32,
curve_i,
)
};
return Ok(NeoBoolean::new(result != 0));
}
#[cfg(not(target_arch = "wasm32"))]
{
let values = [
NeoValue::from(message.clone()),
NeoValue::from(public_key.clone()),
NeoValue::from(signature.clone()),
NeoValue::from(curve.clone()),
];
Self::call_boolean("Neo.Crypto.VerifyWithECDsa", &values)
}
}
pub fn iterator_next(items: &NeoArray<NeoValue>) -> NeoResult<NeoBoolean> {
#[cfg(target_arch = "wasm32")]
{
let _ = items;
Err(NeoError::Wasm32CrossCallUnavailable {
syscall: "System.Iterator.Next",
})
}
#[cfg(not(target_arch = "wasm32"))]
{
let values = [NeoValue::from(items.clone())];
Self::call_boolean("System.Iterator.Next", &values)
}
}
pub fn iterator_value(items: &NeoArray<NeoValue>) -> NeoResult<NeoValue> {
#[cfg(target_arch = "wasm32")]
{
let _ = items;
Err(NeoError::Wasm32CrossCallUnavailable {
syscall: "System.Iterator.Value",
})
}
#[cfg(not(target_arch = "wasm32"))]
{
let values = [NeoValue::from(items.clone())];
Self::call_value("System.Iterator.Value", &values)
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_get_context() -> NeoResult<NeoStorageContext> {
let flags = current_call_flags();
if !call_flags_allow_read(flags) {
return Err(NeoError::InvalidOperation);
}
let read_only = !call_flags_allow_write(flags);
STORAGE_STATE.create_context(current_executing_script_hash(), read_only)
}
#[cfg(target_arch = "wasm32")]
pub fn storage_get_context() -> NeoResult<NeoStorageContext> {
Ok(NeoStorageContext::new(1))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_get_read_only_context() -> NeoResult<NeoStorageContext> {
if !call_flags_allow_read(current_call_flags()) {
return Err(NeoError::InvalidOperation);
}
STORAGE_STATE.create_context(current_executing_script_hash(), true)
}
#[cfg(target_arch = "wasm32")]
pub fn storage_get_read_only_context() -> NeoResult<NeoStorageContext> {
Ok(NeoStorageContext::read_only(1))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_as_read_only(context: &NeoStorageContext) -> NeoResult<NeoStorageContext> {
STORAGE_STATE.clone_as_read_only(context)
}
#[cfg(target_arch = "wasm32")]
pub fn storage_as_read_only(context: &NeoStorageContext) -> NeoResult<NeoStorageContext> {
Ok(context.as_read_only())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_get(
context: &NeoStorageContext,
key: &NeoByteString,
) -> NeoResult<NeoByteString> {
if !call_flags_allow_read(current_call_flags()) {
return Err(NeoError::InvalidOperation);
}
let handle = STORAGE_STATE.get_handle(context)?;
let store = handle.store.read().map_err(|_| NeoError::InvalidState)?;
let value = store.get(key.as_slice()).cloned().unwrap_or_else(Vec::new);
Ok(NeoByteString::new(value))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_try_get(
context: &NeoStorageContext,
key: &NeoByteString,
) -> NeoResult<Option<NeoByteString>> {
if !call_flags_allow_read(current_call_flags()) {
return Err(NeoError::InvalidOperation);
}
let handle = STORAGE_STATE.get_handle(context)?;
let store = handle.store.read().map_err(|_| NeoError::InvalidState)?;
Ok(store.get(key.as_slice()).cloned().map(NeoByteString::new))
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_put(
context: &NeoStorageContext,
key: &NeoByteString,
value: &NeoByteString,
) -> NeoResult<()> {
if !call_flags_allow_write(current_call_flags()) {
return Err(NeoError::InvalidOperation);
}
let handle = STORAGE_STATE.get_handle(context)?;
if handle.read_only {
return Err(NeoError::InvalidOperation);
}
let mut store = handle.store.write().map_err(|_| NeoError::InvalidState)?;
store.insert(key.as_slice().to_vec(), value.as_slice().to_vec());
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn storage_put(
context: &NeoStorageContext,
key: &NeoByteString,
value: &NeoByteString,
) -> NeoResult<()> {
if context.is_read_only() {
return Err(NeoError::InvalidOperation);
}
let key_slice = key.as_slice();
let value_slice = value.as_slice();
unsafe {
neo_storage_put_bytes(
key_slice.as_ptr() as i32,
key_slice.len() as i32,
value_slice.as_ptr() as i32,
value_slice.len() as i32,
);
}
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn storage_get(
_context: &NeoStorageContext,
key: &NeoByteString,
) -> NeoResult<NeoByteString> {
const INITIAL_CAPACITY: usize = 64;
const MAX_CAPACITY: usize = 64 * 1024;
let key_slice = key.as_slice();
let mut buffer: Vec<u8> = vec![0u8; INITIAL_CAPACITY];
loop {
let actual = unsafe {
neo_storage_get_into(
key_slice.as_ptr() as i32,
key_slice.len() as i32,
buffer.as_mut_ptr() as i32,
buffer.len() as i32,
)
};
if actual == -1 {
return Ok(NeoByteString::new(Vec::new()));
}
if actual >= 0 {
let len = actual as usize;
buffer.truncate(len);
return Ok(NeoByteString::new(buffer));
}
let needed = (-actual) as usize;
if needed > MAX_CAPACITY {
return Err(NeoError::InvalidState);
}
buffer.resize(needed, 0);
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_delete(context: &NeoStorageContext, key: &NeoByteString) -> NeoResult<()> {
if !call_flags_allow_write(current_call_flags()) {
return Err(NeoError::InvalidOperation);
}
let handle = STORAGE_STATE.get_handle(context)?;
if handle.read_only {
return Err(NeoError::InvalidOperation);
}
let mut store = handle.store.write().map_err(|_| NeoError::InvalidState)?;
store.remove(key.as_slice());
Ok(())
}
#[cfg(target_arch = "wasm32")]
pub fn storage_delete(context: &NeoStorageContext, key: &NeoByteString) -> NeoResult<()> {
if context.is_read_only() {
return Err(NeoError::InvalidOperation);
}
let key_slice = key.as_slice();
unsafe {
neo_storage_delete_bytes(key_slice.as_ptr() as i32, key_slice.len() as i32);
}
Ok(())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn storage_find(
context: &NeoStorageContext,
prefix: &NeoByteString,
) -> NeoResult<NeoIterator<NeoValue>> {
if !call_flags_allow_read(current_call_flags()) {
return Err(NeoError::InvalidOperation);
}
let handle = STORAGE_STATE.get_handle(context)?;
let prefix_bytes = prefix.as_slice();
let store = handle.store.read().map_err(|_| NeoError::InvalidState)?;
let matches: Vec<NeoValue> = store
.iter()
.filter_map(|(key_bytes, value)| {
if key_bytes.starts_with(prefix_bytes) {
let mut entry = NeoStruct::new();
entry.set_field("key", NeoValue::from(NeoByteString::from_slice(key_bytes)));
entry.set_field("value", NeoValue::from(NeoByteString::from_slice(value)));
Some(NeoValue::from(entry))
} else {
None
}
})
.collect();
Ok(NeoIterator::new(matches))
}
#[cfg(target_arch = "wasm32")]
pub fn storage_find(
_context: &NeoStorageContext,
_prefix: &NeoByteString,
) -> NeoResult<NeoIterator<NeoValue>> {
Ok(NeoIterator::new(Vec::new()))
}
}