1use neo_types::*;
7
8#[cfg(not(target_arch = "wasm32"))]
9use crate::storage::*;
10use crate::syscalls::SYSCALLS;
11use crate::NeoVMSyscallInfo;
12
13#[cfg(target_arch = "wasm32")]
14#[link(wasm_import_module = "neo")]
15extern "C" {
16 #[link_name = "runtime_check_witness_bytes"]
17 fn neo_runtime_check_witness_bytes(ptr: i32, len: i32) -> i32;
18
19 #[link_name = "runtime_check_witness_i64"]
20 fn neo_runtime_check_witness_i64(account: i64) -> i32;
21
22 #[link_name = "runtime_get_time"]
23 fn neo_runtime_get_time() -> i64;
24
25 #[link_name = "runtime_get_calling_script_hash_i64"]
26 fn neo_runtime_get_calling_script_hash_i64() -> i64;
27
28 #[link_name = "runtime_get_entry_script_hash_i64"]
29 fn neo_runtime_get_entry_script_hash_i64() -> i64;
30
31 #[link_name = "runtime_get_executing_script_hash_i64"]
32 fn neo_runtime_get_executing_script_hash_i64() -> i64;
33
34 #[link_name = "runtime_log"]
35 fn neo_runtime_log(ptr: i32, len: i32);
36
37 #[link_name = "runtime_notify"]
38 fn neo_runtime_notify(event_ptr: i32, event_len: i32);
39
40 #[link_name = "neo_storage_put_bytes"]
45 fn neo_storage_put_bytes(key_ptr: i32, key_len: i32, value_ptr: i32, value_len: i32);
46
47 #[link_name = "neo_storage_delete_bytes"]
52 fn neo_storage_delete_bytes(key_ptr: i32, key_len: i32);
53
54 #[link_name = "neo_storage_get_into"]
61 fn neo_storage_get_into(key_ptr: i32, key_len: i32, out_ptr: i32, out_cap: i32) -> i32;
62}
63
64#[cfg(not(target_arch = "wasm32"))]
65const CALL_FLAGS_VALID_MASK: i32 = 0x0F;
66#[cfg(not(target_arch = "wasm32"))]
67const CALL_FLAGS_READ_STATES: i32 = 0x01;
68#[cfg(not(target_arch = "wasm32"))]
69const CALL_FLAGS_WRITE_STATES: i32 = 0x02;
70
71fn find_syscall(name: &str) -> Option<&'static NeoVMSyscallInfo> {
72 SYSCALLS.iter().find(|info| info.name == name)
73}
74
75fn syscall_hash(name: &str) -> NeoResult<u32> {
76 find_syscall(name)
77 .map(|info| info.hash)
78 .ok_or_else(|| NeoError::new(&format!("unknown syscall: {name}")))
79}
80
81fn default_value_for(return_type: &str) -> NeoValue {
82 match return_type {
83 "Void" => NeoValue::Null,
84 "Boolean" => NeoBoolean::FALSE.into(),
86 "Integer" => NeoInteger::new(0).into(),
87 "Hash160" => NeoByteString::new(vec![0u8; 20]).into(),
88 "ByteString" => NeoByteString::new(vec![0u8; 1]).into(),
89 "String" => NeoString::from_str("Neo N3").into(),
90 "Array" => NeoArray::<NeoValue>::new().into(),
91 "Iterator" => NeoArray::<NeoValue>::new().into(),
92 "StackItem" => NeoArray::<NeoValue>::new().into(),
93 "StorageContext" => NeoValue::Null,
94 _ => NeoValue::Null,
95 }
96}
97
98fn value_matches_param_type(value: &NeoValue, param_type: &str) -> bool {
99 match param_type {
100 "Boolean" => value.as_boolean().is_some(),
101 "Integer" => value.as_integer().is_some(),
102 "Hash160" => {
103 value.is_null()
104 || value
105 .as_byte_string()
106 .map(|bytes| bytes.len() == 20)
107 .unwrap_or(false)
108 }
109 "ByteString" => value.as_byte_string().is_some(),
110 "String" => value.as_string().is_some(),
111 "Array" => value.as_array().is_some(),
112 "Iterator" => value.as_array().is_some(),
113 "StorageContext" => value.is_null() || value.as_integer().is_some(),
114 "StackItem" | "Any" | "ExecutionContext" => true,
115 _ => true,
116 }
117}
118
119#[cfg(not(target_arch = "wasm32"))]
120fn call_flags_allow_write(flags: i32) -> bool {
121 (flags & CALL_FLAGS_WRITE_STATES) != 0
122}
123
124#[cfg(not(target_arch = "wasm32"))]
125fn call_flags_allow_read(flags: i32) -> bool {
126 (flags & CALL_FLAGS_READ_STATES) != 0
127}
128
129#[cfg(not(target_arch = "wasm32"))]
130fn hash160_prefix_i64(hash: &[u8; 20]) -> i64 {
131 let mut buf = [0u8; 8];
132 buf.copy_from_slice(&hash[..8]);
133 i64::from_le_bytes(buf)
134}
135
136pub fn neovm_syscall(hash: u32, args: &[NeoValue]) -> NeoResult<NeoValue> {
138 let registry = crate::NeoVMSyscallRegistry::get_instance();
139 let info = registry
140 .get_syscall_by_hash(hash)
141 .ok_or_else(|| NeoError::new(&format!("unknown syscall hash: 0x{hash:08x}")))?;
142
143 if args.len() != info.parameters.len() {
144 return Err(NeoError::new(&format!(
145 "invalid syscall argument count for {}: expected {}, got {}",
146 info.name,
147 info.parameters.len(),
148 args.len()
149 )));
150 }
151
152 for (index, (arg, expected_type)) in args.iter().zip(info.parameters.iter()).enumerate() {
153 if !value_matches_param_type(arg, expected_type) {
154 return Err(NeoError::new(&format!(
155 "invalid syscall argument type for {} param #{}: expected {}",
156 info.name, index, expected_type
157 )));
158 }
159 }
160
161 #[cfg(not(target_arch = "wasm32"))]
162 {
163 if info.name == "System.Runtime.CheckWitness" {
164 let has_witness = args
165 .first()
166 .and_then(NeoValue::as_byte_string)
167 .map(|account| has_active_witness(account.as_slice()))
168 .unwrap_or(false);
169 return Ok(NeoBoolean::new(has_witness).into());
170 }
171
172 if info.name == "System.Crypto.CheckSig" {
173 let results = active_crypto_verification_results();
174 return Ok(NeoBoolean::new(results.check_sig).into());
175 }
176
177 if info.name == "System.Crypto.CheckMultisig" {
178 let results = active_crypto_verification_results();
179 return Ok(NeoBoolean::new(results.check_multisig).into());
180 }
181
182 if info.name == "Neo.Crypto.VerifyWithECDsa" {
183 let results = active_crypto_verification_results();
184 return Ok(NeoBoolean::new(results.verify_with_ecdsa).into());
185 }
186
187 if info.name == "System.Runtime.GetCallingScriptHash" {
188 return Ok(NeoByteString::from_slice(¤t_calling_script_hash()).into());
189 }
190
191 if info.name == "System.Runtime.GetEntryScriptHash" {
192 return Ok(NeoByteString::from_slice(¤t_entry_script_hash()).into());
193 }
194
195 if info.name == "System.Runtime.GetExecutingScriptHash" {
196 return Ok(NeoByteString::from_slice(¤t_executing_script_hash()).into());
197 }
198
199 if info.name == "System.Contract.GetCallFlags" {
200 return Ok(NeoInteger::new(current_call_flags()).into());
201 }
202 }
203
204 Ok(default_value_for(info.return_type))
205}
206
207pub struct NeoVMSyscall;
209
210impl NeoVMSyscall {
211 #[cfg(not(target_arch = "wasm32"))]
212 fn parse_hash160(hash: &NeoByteString) -> NeoResult<[u8; 20]> {
213 if hash.len() != 20 {
214 return Err(NeoError::InvalidArgument);
215 }
216 let mut value = [0u8; 20];
217 value.copy_from_slice(hash.as_slice());
218 Ok(value)
219 }
220
221 #[cfg(not(target_arch = "wasm32"))]
222 fn parse_call_flags(flags: &NeoInteger) -> NeoResult<i32> {
223 let parsed = flags.as_i32_saturating();
224 if parsed < 0 || (parsed & !CALL_FLAGS_VALID_MASK) != 0 {
225 return Err(NeoError::InvalidArgument);
226 }
227 Ok(parsed)
228 }
229
230 #[cfg(not(target_arch = "wasm32"))]
231 fn begin_contract_invocation_with_flags(
232 next_executing: &NeoByteString,
233 call_flags: i32,
234 ) -> NeoResult<()> {
235 if call_flags < 0 || (call_flags & !CALL_FLAGS_VALID_MASK) != 0 {
236 return Err(NeoError::InvalidArgument);
237 }
238 push_current_executing_script_hash(Self::parse_hash160(next_executing)?, call_flags)
239 }
240
241 #[cfg(not(target_arch = "wasm32"))]
243 pub fn set_active_contract_hash(hash: &NeoByteString) -> NeoResult<()> {
244 set_current_contract_hash(Self::parse_hash160(hash)?);
245 Ok(())
246 }
247
248 #[cfg(not(target_arch = "wasm32"))]
250 pub fn set_active_script_hashes(
251 calling: &NeoByteString,
252 entry: &NeoByteString,
253 executing: &NeoByteString,
254 ) -> NeoResult<()> {
255 set_current_script_hashes(
256 Self::parse_hash160(calling)?,
257 Self::parse_hash160(entry)?,
258 Self::parse_hash160(executing)?,
259 );
260 Ok(())
261 }
262
263 #[cfg(not(target_arch = "wasm32"))]
266 pub fn set_active_calling_script_hash(hash: &NeoByteString) -> NeoResult<()> {
267 set_current_calling_script_hash(Self::parse_hash160(hash)?);
268 Ok(())
269 }
270
271 #[cfg(not(target_arch = "wasm32"))]
274 pub fn set_active_entry_script_hash(hash: &NeoByteString) -> NeoResult<()> {
275 set_current_entry_script_hash(Self::parse_hash160(hash)?);
276 Ok(())
277 }
278
279 #[cfg(not(target_arch = "wasm32"))]
282 pub fn set_active_executing_script_hash(hash: &NeoByteString) -> NeoResult<()> {
283 set_current_executing_script_hash(Self::parse_hash160(hash)?);
284 Ok(())
285 }
286
287 #[cfg(not(target_arch = "wasm32"))]
290 pub fn set_active_call_flags(call_flags: &NeoInteger) -> NeoResult<()> {
291 set_current_call_flags(Self::parse_call_flags(call_flags)?);
292 Ok(())
293 }
294
295 #[cfg(not(target_arch = "wasm32"))]
300 pub fn begin_contract_invocation(next_executing: &NeoByteString) -> NeoResult<()> {
301 Self::begin_contract_invocation_with_flags(next_executing, current_call_flags())
302 }
303
304 #[cfg(not(target_arch = "wasm32"))]
306 pub fn end_contract_invocation() -> NeoResult<()> {
307 pop_current_script_hash_frame()
308 }
309
310 #[cfg(not(target_arch = "wasm32"))]
312 pub fn with_contract_invocation<T, F>(
313 next_executing: &NeoByteString,
314 operation: F,
315 ) -> NeoResult<T>
316 where
317 F: FnOnce() -> NeoResult<T>,
318 {
319 Self::begin_contract_invocation(next_executing)?;
320 let operation_result = operation();
321 let unwind_result = Self::end_contract_invocation();
322
323 match (operation_result, unwind_result) {
324 (Ok(value), Ok(())) => Ok(value),
325 (Err(err), Ok(())) => Err(err),
326 (Ok(_), Err(unwind_err)) => Err(unwind_err),
327 (Err(operation_err), Err(unwind_err)) => Err(NeoError::new(&format!(
328 "invocation operation failed ({}) and frame unwind failed ({})",
329 operation_err.message(),
330 unwind_err.message()
331 ))),
332 }
333 }
334
335 #[cfg(target_arch = "wasm32")]
337 pub fn set_active_contract_hash(_hash: &NeoByteString) -> NeoResult<()> {
338 Ok(())
339 }
340
341 #[cfg(target_arch = "wasm32")]
343 pub fn set_active_script_hashes(
344 _calling: &NeoByteString,
345 _entry: &NeoByteString,
346 _executing: &NeoByteString,
347 ) -> NeoResult<()> {
348 Ok(())
349 }
350
351 #[cfg(target_arch = "wasm32")]
353 pub fn set_active_calling_script_hash(_hash: &NeoByteString) -> NeoResult<()> {
354 Ok(())
355 }
356
357 #[cfg(target_arch = "wasm32")]
359 pub fn set_active_entry_script_hash(_hash: &NeoByteString) -> NeoResult<()> {
360 Ok(())
361 }
362
363 #[cfg(target_arch = "wasm32")]
365 pub fn set_active_executing_script_hash(_hash: &NeoByteString) -> NeoResult<()> {
366 Ok(())
367 }
368
369 #[cfg(target_arch = "wasm32")]
371 pub fn set_active_call_flags(_call_flags: &NeoInteger) -> NeoResult<()> {
372 Ok(())
373 }
374
375 #[cfg(target_arch = "wasm32")]
377 pub fn begin_contract_invocation(_next_executing: &NeoByteString) -> NeoResult<()> {
378 Ok(())
379 }
380
381 #[cfg(target_arch = "wasm32")]
383 pub fn end_contract_invocation() -> NeoResult<()> {
384 Ok(())
385 }
386
387 #[cfg(target_arch = "wasm32")]
389 pub fn with_contract_invocation<T, F>(
390 _next_executing: &NeoByteString,
391 operation: F,
392 ) -> NeoResult<T>
393 where
394 F: FnOnce() -> NeoResult<T>,
395 {
396 operation()
397 }
398
399 #[cfg(not(target_arch = "wasm32"))]
401 pub fn reset_host_state() -> NeoResult<()> {
402 STORAGE_STATE.reset()?;
403 reset_current_contract_hash();
404 clear_active_witnesses();
405 reset_crypto_verification_results();
406 Ok(())
407 }
408
409 #[cfg(target_arch = "wasm32")]
415 pub fn reset_host_state() -> NeoResult<()> {
416 Ok(())
417 }
418
419 fn call_value(name: &str, args: &[NeoValue]) -> NeoResult<NeoValue> {
420 neovm_syscall(syscall_hash(name)?, args)
421 }
422
423 fn call_integer(name: &str) -> NeoResult<NeoInteger> {
424 let value = Self::call_value(name, &[])?;
425 value.as_integer().cloned().ok_or(NeoError::InvalidType)
426 }
427
428 fn call_boolean(name: &str, args: &[NeoValue]) -> NeoResult<NeoBoolean> {
429 let value = Self::call_value(name, args)?;
430 value.as_boolean().ok_or(NeoError::InvalidType)
431 }
432
433 fn call_bytes_with_args(name: &str, args: &[NeoValue]) -> NeoResult<NeoByteString> {
434 let value = Self::call_value(name, args)?;
435 value.as_byte_string().cloned().ok_or(NeoError::InvalidType)
436 }
437
438 fn call_string(name: &str) -> NeoResult<NeoString> {
439 let value = Self::call_value(name, &[])?;
440 value.as_string().cloned().ok_or(NeoError::InvalidType)
441 }
442
443 fn call_array(name: &str, args: &[NeoValue]) -> NeoResult<NeoArray<NeoValue>> {
444 let value = Self::call_value(name, args)?;
445 value.as_array().cloned().ok_or(NeoError::InvalidType)
446 }
447
448 #[cfg(not(target_arch = "wasm32"))]
450 pub fn set_active_witnesses(witnesses: &[NeoByteString]) -> NeoResult<()> {
451 crate::storage::set_active_witnesses(
452 witnesses.iter().map(|witness| witness.as_slice().to_vec()),
453 );
454 Ok(())
455 }
456
457 #[cfg(target_arch = "wasm32")]
459 pub fn set_active_witnesses(_witnesses: &[NeoByteString]) -> NeoResult<()> {
460 Ok(())
461 }
462
463 #[cfg(not(target_arch = "wasm32"))]
467 pub fn set_crypto_verification_results(check_sig: bool, check_multisig: bool) -> NeoResult<()> {
468 Self::set_crypto_verification_results_full(check_sig, check_multisig, check_sig)
469 }
470
471 #[cfg(not(target_arch = "wasm32"))]
473 pub fn set_crypto_verification_results_full(
474 check_sig: bool,
475 check_multisig: bool,
476 verify_with_ecdsa: bool,
477 ) -> NeoResult<()> {
478 crate::storage::set_crypto_verification_results(CryptoVerificationResults {
479 check_sig,
480 check_multisig,
481 verify_with_ecdsa,
482 });
483 Ok(())
484 }
485
486 #[cfg(not(target_arch = "wasm32"))]
488 pub fn set_verify_with_ecdsa_result(result: bool) -> NeoResult<()> {
489 let mut current = active_crypto_verification_results();
490 current.verify_with_ecdsa = result;
491 crate::storage::set_crypto_verification_results(current);
492 Ok(())
493 }
494
495 #[cfg(target_arch = "wasm32")]
497 pub fn set_crypto_verification_results(
498 _check_sig: bool,
499 _check_multisig: bool,
500 ) -> NeoResult<()> {
501 Ok(())
502 }
503
504 #[cfg(target_arch = "wasm32")]
506 pub fn set_crypto_verification_results_full(
507 _check_sig: bool,
508 _check_multisig: bool,
509 _verify_with_ecdsa: bool,
510 ) -> NeoResult<()> {
511 Ok(())
512 }
513
514 #[cfg(target_arch = "wasm32")]
516 pub fn set_verify_with_ecdsa_result(_result: bool) -> NeoResult<()> {
517 Ok(())
518 }
519
520 pub fn get_time() -> NeoResult<NeoInteger> {
522 #[cfg(target_arch = "wasm32")]
523 {
524 return Ok(NeoInteger::new(unsafe { neo_runtime_get_time() }));
525 }
526
527 #[cfg(not(target_arch = "wasm32"))]
528 {
529 Self::call_integer("System.Runtime.GetTime")
530 }
531 }
532
533 pub fn get_time_i64() -> NeoResult<i64> {
539 #[cfg(target_arch = "wasm32")]
540 {
541 return Ok(unsafe { neo_runtime_get_time() });
542 }
543
544 #[cfg(not(target_arch = "wasm32"))]
545 {
546 Self::call_integer("System.Runtime.GetTime")?.try_into_i64()
547 }
548 }
549
550 pub fn check_witness(account: &NeoByteString) -> NeoResult<NeoBoolean> {
552 Self::check_witness_bytes(account.as_slice())
553 }
554
555 pub fn check_witness_bytes(account: &[u8]) -> NeoResult<NeoBoolean> {
557 #[cfg(target_arch = "wasm32")]
558 {
559 let result = unsafe {
560 neo_runtime_check_witness_bytes(account.as_ptr() as i32, account.len() as i32)
561 };
562 return Ok(NeoBoolean::new(result != 0));
563 }
564
565 #[cfg(not(target_arch = "wasm32"))]
566 {
567 let args = [NeoValue::from(NeoByteString::from_slice(account))];
568 Self::call_boolean("System.Runtime.CheckWitness", &args)
569 }
570 }
571
572 pub fn check_witness_i64(account: i64) -> NeoResult<NeoBoolean> {
578 #[cfg(target_arch = "wasm32")]
579 {
580 let result = unsafe { neo_runtime_check_witness_i64(account) };
581 return Ok(NeoBoolean::new(result != 0));
582 }
583
584 #[cfg(not(target_arch = "wasm32"))]
585 {
586 let mut bytes = [0u8; 20];
587 bytes[..8].copy_from_slice(&account.to_le_bytes());
588 Self::check_witness_bytes(&bytes)
589 }
590 }
591
592 pub fn notify(event: &NeoString, state: &NeoArray<NeoValue>) -> NeoResult<()> {
594 #[cfg(target_arch = "wasm32")]
595 {
596 let _ = state;
597 return Self::notify_event(event.as_str());
598 }
599
600 #[cfg(not(target_arch = "wasm32"))]
601 {
602 let event_bytes = NeoByteString::from_slice(event.as_str().as_bytes());
603 let args = [NeoValue::from(event_bytes), NeoValue::from(state.clone())];
604 neovm_syscall(syscall_hash("System.Runtime.Notify")?, &args)?;
605 Ok(())
606 }
607 }
608
609 pub fn notify_event(event: &str) -> NeoResult<()> {
611 #[cfg(target_arch = "wasm32")]
612 unsafe {
613 neo_runtime_notify(event.as_ptr() as i32, event.len() as i32);
614 Ok(())
615 }
616
617 #[cfg(not(target_arch = "wasm32"))]
618 {
619 let label = NeoString::from_str(event);
620 let state = NeoArray::new();
621 Self::notify(&label, &state)
622 }
623 }
624
625 pub fn log(message: &NeoString) -> NeoResult<()> {
627 #[cfg(target_arch = "wasm32")]
628 unsafe {
629 let message = message.as_str();
630 neo_runtime_log(message.as_ptr() as i32, message.len() as i32);
631 Ok(())
632 }
633
634 #[cfg(not(target_arch = "wasm32"))]
635 {
636 let message_bytes = NeoByteString::from_slice(message.as_str().as_bytes());
637 let args = [NeoValue::from(message_bytes)];
638 neovm_syscall(syscall_hash("System.Runtime.Log")?, &args)?;
639 Ok(())
640 }
641 }
642
643 pub fn platform() -> NeoResult<NeoString> {
645 Self::call_string("System.Runtime.Platform")
646 }
647
648 pub fn get_trigger() -> NeoResult<NeoInteger> {
649 Self::call_integer("System.Runtime.GetTrigger")
650 }
651
652 pub fn get_invocation_counter() -> NeoResult<NeoInteger> {
653 Self::call_integer("System.Runtime.GetInvocationCounter")
654 }
655
656 pub fn get_random() -> NeoResult<NeoInteger> {
657 Self::call_integer("System.Runtime.GetRandom")
658 }
659
660 pub fn get_network() -> NeoResult<NeoInteger> {
661 Self::call_integer("System.Runtime.GetNetwork")
662 }
663
664 pub fn get_address_version() -> NeoResult<NeoInteger> {
665 Self::call_integer("System.Runtime.GetAddressVersion")
666 }
667
668 pub fn get_gas_left() -> NeoResult<NeoInteger> {
669 Self::call_integer("System.Runtime.GasLeft")
670 }
671
672 #[cfg(not(target_arch = "wasm32"))]
673 pub fn get_calling_script_hash() -> NeoResult<NeoByteString> {
674 Ok(NeoByteString::from_slice(¤t_calling_script_hash()))
675 }
676
677 #[cfg(target_arch = "wasm32")]
678 pub fn get_calling_script_hash() -> NeoResult<NeoByteString> {
679 Ok(NeoByteString::new(vec![0u8; 20]))
680 }
681
682 #[cfg(not(target_arch = "wasm32"))]
683 pub fn get_calling_script_hash_i64() -> NeoResult<i64> {
684 Ok(hash160_prefix_i64(¤t_calling_script_hash()))
685 }
686
687 #[cfg(target_arch = "wasm32")]
688 pub fn get_calling_script_hash_i64() -> NeoResult<i64> {
689 Ok(unsafe { neo_runtime_get_calling_script_hash_i64() })
690 }
691
692 #[cfg(not(target_arch = "wasm32"))]
693 pub fn get_entry_script_hash() -> NeoResult<NeoByteString> {
694 Ok(NeoByteString::from_slice(¤t_entry_script_hash()))
695 }
696
697 #[cfg(target_arch = "wasm32")]
698 pub fn get_entry_script_hash() -> NeoResult<NeoByteString> {
699 Ok(NeoByteString::new(vec![0u8; 20]))
700 }
701
702 #[cfg(not(target_arch = "wasm32"))]
703 pub fn get_entry_script_hash_i64() -> NeoResult<i64> {
704 Ok(hash160_prefix_i64(¤t_entry_script_hash()))
705 }
706
707 #[cfg(target_arch = "wasm32")]
708 pub fn get_entry_script_hash_i64() -> NeoResult<i64> {
709 Ok(unsafe { neo_runtime_get_entry_script_hash_i64() })
710 }
711
712 #[cfg(not(target_arch = "wasm32"))]
713 pub fn get_executing_script_hash() -> NeoResult<NeoByteString> {
714 Ok(NeoByteString::from_slice(¤t_executing_script_hash()))
715 }
716
717 #[cfg(target_arch = "wasm32")]
718 pub fn get_executing_script_hash() -> NeoResult<NeoByteString> {
719 Ok(NeoByteString::new(vec![0u8; 20]))
720 }
721
722 #[cfg(not(target_arch = "wasm32"))]
723 pub fn get_executing_script_hash_i64() -> NeoResult<i64> {
724 Ok(hash160_prefix_i64(¤t_executing_script_hash()))
725 }
726
727 #[cfg(target_arch = "wasm32")]
728 pub fn get_executing_script_hash_i64() -> NeoResult<i64> {
729 Ok(unsafe { neo_runtime_get_executing_script_hash_i64() })
730 }
731
732 pub fn get_notifications(script_hash: Option<&NeoByteString>) -> NeoResult<NeoArray<NeoValue>> {
734 let script_hash_value = script_hash
735 .map(|hash| NeoValue::from(hash.clone()))
736 .unwrap_or(NeoValue::Null);
737 let args = [script_hash_value];
738 Self::call_array("System.Runtime.GetNotifications", &args)
739 }
740
741 pub fn get_script_container() -> NeoResult<NeoArray<NeoValue>> {
742 Self::call_array("System.Runtime.GetScriptContainer", &[])
743 }
744
745 pub fn burn_gas(gas: &NeoInteger) -> NeoResult<()> {
747 let args = [NeoValue::from(gas.clone())];
748 Self::call_value("System.Runtime.BurnGas", &args)?;
749 Ok(())
750 }
751
752 pub fn current_signers() -> NeoResult<NeoArray<NeoValue>> {
754 Self::call_array("System.Runtime.CurrentSigners", &[])
755 }
756
757 pub fn load_script(
759 script: &NeoByteString,
760 call_flags: &NeoInteger,
761 args: &NeoArray<NeoValue>,
762 ) -> NeoResult<()> {
763 let values = [
764 NeoValue::from(script.clone()),
765 NeoValue::from(call_flags.clone()),
766 NeoValue::from(args.clone()),
767 ];
768 Self::call_value("System.Runtime.LoadScript", &values)?;
769 Ok(())
770 }
771
772 pub fn contract_call(
774 script_hash: &NeoByteString,
775 method: &NeoString,
776 call_flags: &NeoInteger,
777 args: &NeoArray<NeoValue>,
778 ) -> NeoResult<NeoValue> {
779 let values = [
780 NeoValue::from(script_hash.clone()),
781 NeoValue::from(method.clone()),
782 NeoValue::from(call_flags.clone()),
783 NeoValue::from(args.clone()),
784 ];
785
786 #[cfg(not(target_arch = "wasm32"))]
787 {
788 let parsed_flags = Self::parse_call_flags(call_flags)?;
789 Self::begin_contract_invocation_with_flags(script_hash, parsed_flags)?;
790 let call_result = Self::call_value("System.Contract.Call", &values);
791 let unwind_result = Self::end_contract_invocation();
792 match (call_result, unwind_result) {
793 (Ok(value), Ok(())) => Ok(value),
794 (Err(err), Ok(())) => Err(err),
795 (Ok(_), Err(unwind_err)) => Err(unwind_err),
796 (Err(call_err), Err(unwind_err)) => Err(NeoError::new(&format!(
797 "contract_call failed ({}) and invocation unwind failed ({})",
798 call_err.message(),
799 unwind_err.message()
800 ))),
801 }
802 }
803
804 #[cfg(target_arch = "wasm32")]
805 {
806 Self::call_value("System.Contract.Call", &values)
807 }
808 }
809
810 pub fn contract_call_native(native_id: &NeoInteger) -> NeoResult<NeoValue> {
812 let values = [NeoValue::from(native_id.clone())];
813 Self::call_value("System.Contract.CallNative", &values)
814 }
815
816 pub fn get_call_flags() -> NeoResult<NeoInteger> {
817 #[cfg(not(target_arch = "wasm32"))]
818 {
819 Ok(NeoInteger::new(current_call_flags()))
820 }
821
822 #[cfg(target_arch = "wasm32")]
823 {
824 Self::call_integer("System.Contract.GetCallFlags")
825 }
826 }
827
828 pub fn create_standard_account(pubkey: &NeoByteString) -> NeoResult<NeoByteString> {
829 let values = [NeoValue::from(pubkey.clone())];
830 Self::call_bytes_with_args("System.Contract.CreateStandardAccount", &values)
831 }
832
833 pub fn create_multisig_account(
834 threshold: &NeoInteger,
835 public_keys: &NeoArray<NeoValue>,
836 ) -> NeoResult<NeoByteString> {
837 let values = [
838 NeoValue::from(threshold.clone()),
839 NeoValue::from(public_keys.clone()),
840 ];
841 Self::call_bytes_with_args("System.Contract.CreateMultisigAccount", &values)
842 }
843
844 pub fn native_on_persist() -> NeoResult<()> {
845 Self::call_value("System.Contract.NativeOnPersist", &[])?;
846 Ok(())
847 }
848
849 pub fn native_post_persist() -> NeoResult<()> {
850 Self::call_value("System.Contract.NativePostPersist", &[])?;
851 Ok(())
852 }
853
854 pub fn check_sig(pubkey: &NeoByteString, signature: &NeoByteString) -> NeoResult<NeoBoolean> {
855 let values = [
856 NeoValue::from(pubkey.clone()),
857 NeoValue::from(signature.clone()),
858 ];
859 Self::call_boolean("System.Crypto.CheckSig", &values)
860 }
861
862 pub fn check_multisig(
863 pubkeys: &NeoArray<NeoValue>,
864 signatures: &NeoArray<NeoValue>,
865 ) -> NeoResult<NeoBoolean> {
866 let values = [
867 NeoValue::from(pubkeys.clone()),
868 NeoValue::from(signatures.clone()),
869 ];
870 Self::call_boolean("System.Crypto.CheckMultisig", &values)
871 }
872
873 pub fn verify_with_ecdsa(
874 message: &NeoByteString,
875 pubkey: &NeoByteString,
876 signature: &NeoByteString,
877 curve: &NeoInteger,
878 ) -> NeoResult<NeoBoolean> {
879 let values = [
880 NeoValue::from(message.clone()),
881 NeoValue::from(pubkey.clone()),
882 NeoValue::from(signature.clone()),
883 NeoValue::from(curve.clone()),
884 ];
885 Self::call_boolean("Neo.Crypto.VerifyWithECDsa", &values)
886 }
887
888 pub fn iterator_next(items: &NeoArray<NeoValue>) -> NeoResult<NeoBoolean> {
889 let values = [NeoValue::from(items.clone())];
890 Self::call_boolean("System.Iterator.Next", &values)
891 }
892
893 pub fn iterator_value(items: &NeoArray<NeoValue>) -> NeoResult<NeoValue> {
894 let values = [NeoValue::from(items.clone())];
895 Self::call_value("System.Iterator.Value", &values)
896 }
897
898 #[cfg(not(target_arch = "wasm32"))]
899 pub fn storage_get_context() -> NeoResult<NeoStorageContext> {
900 let flags = current_call_flags();
901 if !call_flags_allow_read(flags) {
902 return Err(NeoError::InvalidOperation);
903 }
904 let read_only = !call_flags_allow_write(flags);
905 STORAGE_STATE.create_context(current_executing_script_hash(), read_only)
906 }
907
908 #[cfg(target_arch = "wasm32")]
914 pub fn storage_get_context() -> NeoResult<NeoStorageContext> {
915 Ok(NeoStorageContext::new(1))
916 }
917
918 #[cfg(not(target_arch = "wasm32"))]
919 pub fn storage_get_read_only_context() -> NeoResult<NeoStorageContext> {
920 if !call_flags_allow_read(current_call_flags()) {
921 return Err(NeoError::InvalidOperation);
922 }
923 STORAGE_STATE.create_context(current_executing_script_hash(), true)
924 }
925
926 #[cfg(target_arch = "wasm32")]
927 pub fn storage_get_read_only_context() -> NeoResult<NeoStorageContext> {
928 Ok(NeoStorageContext::read_only(1))
929 }
930
931 #[cfg(not(target_arch = "wasm32"))]
932 pub fn storage_as_read_only(context: &NeoStorageContext) -> NeoResult<NeoStorageContext> {
933 STORAGE_STATE.clone_as_read_only(context)
934 }
935
936 #[cfg(target_arch = "wasm32")]
937 pub fn storage_as_read_only(context: &NeoStorageContext) -> NeoResult<NeoStorageContext> {
938 Ok(context.as_read_only())
939 }
940
941 #[cfg(not(target_arch = "wasm32"))]
942 pub fn storage_get(
943 context: &NeoStorageContext,
944 key: &NeoByteString,
945 ) -> NeoResult<NeoByteString> {
946 if !call_flags_allow_read(current_call_flags()) {
947 return Err(NeoError::InvalidOperation);
948 }
949 let handle = STORAGE_STATE.get_handle(context)?;
950 let store = handle.store.read().map_err(|_| NeoError::InvalidState)?;
951 let value = store.get(key.as_slice()).cloned().unwrap_or_else(Vec::new);
952 Ok(NeoByteString::new(value))
953 }
954
955 #[cfg(not(target_arch = "wasm32"))]
956 pub fn storage_try_get(
957 context: &NeoStorageContext,
958 key: &NeoByteString,
959 ) -> NeoResult<Option<NeoByteString>> {
960 if !call_flags_allow_read(current_call_flags()) {
961 return Err(NeoError::InvalidOperation);
962 }
963 let handle = STORAGE_STATE.get_handle(context)?;
964 let store = handle.store.read().map_err(|_| NeoError::InvalidState)?;
965 Ok(store.get(key.as_slice()).cloned().map(NeoByteString::new))
966 }
967
968 #[cfg(not(target_arch = "wasm32"))]
969 pub fn storage_put(
970 context: &NeoStorageContext,
971 key: &NeoByteString,
972 value: &NeoByteString,
973 ) -> NeoResult<()> {
974 if !call_flags_allow_write(current_call_flags()) {
975 return Err(NeoError::InvalidOperation);
976 }
977 let handle = STORAGE_STATE.get_handle(context)?;
978 if handle.read_only {
979 return Err(NeoError::InvalidOperation);
980 }
981 let mut store = handle.store.write().map_err(|_| NeoError::InvalidState)?;
982 store.insert(key.as_slice().to_vec(), value.as_slice().to_vec());
983 Ok(())
984 }
985
986 #[cfg(target_arch = "wasm32")]
993 pub fn storage_put(
994 context: &NeoStorageContext,
995 key: &NeoByteString,
996 value: &NeoByteString,
997 ) -> NeoResult<()> {
998 if context.is_read_only() {
999 return Err(NeoError::InvalidOperation);
1000 }
1001
1002 let key_slice = key.as_slice();
1003 let value_slice = value.as_slice();
1004 unsafe {
1005 neo_storage_put_bytes(
1006 key_slice.as_ptr() as i32,
1007 key_slice.len() as i32,
1008 value_slice.as_ptr() as i32,
1009 value_slice.len() as i32,
1010 );
1011 }
1012 Ok(())
1013 }
1014
1015 #[cfg(target_arch = "wasm32")]
1021 pub fn storage_get(
1022 _context: &NeoStorageContext,
1023 key: &NeoByteString,
1024 ) -> NeoResult<NeoByteString> {
1025 const INITIAL_CAPACITY: usize = 64;
1026 const MAX_CAPACITY: usize = 64 * 1024;
1027
1028 let key_slice = key.as_slice();
1029 let mut buffer: Vec<u8> = vec![0u8; INITIAL_CAPACITY];
1030 loop {
1031 let actual = unsafe {
1032 neo_storage_get_into(
1033 key_slice.as_ptr() as i32,
1034 key_slice.len() as i32,
1035 buffer.as_mut_ptr() as i32,
1036 buffer.len() as i32,
1037 )
1038 };
1039 if actual == -1 {
1040 return Ok(NeoByteString::new(Vec::new()));
1041 }
1042 if actual >= 0 {
1043 let len = actual as usize;
1044 buffer.truncate(len);
1045 return Ok(NeoByteString::new(buffer));
1046 }
1047 let needed = (-actual) as usize;
1049 if needed > MAX_CAPACITY {
1050 return Err(NeoError::InvalidState);
1051 }
1052 buffer.resize(needed, 0);
1053 }
1054 }
1055
1056 #[cfg(not(target_arch = "wasm32"))]
1057 pub fn storage_delete(context: &NeoStorageContext, key: &NeoByteString) -> NeoResult<()> {
1058 if !call_flags_allow_write(current_call_flags()) {
1059 return Err(NeoError::InvalidOperation);
1060 }
1061 let handle = STORAGE_STATE.get_handle(context)?;
1062 if handle.read_only {
1063 return Err(NeoError::InvalidOperation);
1064 }
1065 let mut store = handle.store.write().map_err(|_| NeoError::InvalidState)?;
1066 store.remove(key.as_slice());
1067 Ok(())
1068 }
1069
1070 #[cfg(target_arch = "wasm32")]
1074 pub fn storage_delete(context: &NeoStorageContext, key: &NeoByteString) -> NeoResult<()> {
1075 if context.is_read_only() {
1076 return Err(NeoError::InvalidOperation);
1077 }
1078
1079 let key_slice = key.as_slice();
1080 unsafe {
1081 neo_storage_delete_bytes(key_slice.as_ptr() as i32, key_slice.len() as i32);
1082 }
1083 Ok(())
1084 }
1085
1086 #[cfg(not(target_arch = "wasm32"))]
1087 pub fn storage_find(
1088 context: &NeoStorageContext,
1089 prefix: &NeoByteString,
1090 ) -> NeoResult<NeoIterator<NeoValue>> {
1091 if !call_flags_allow_read(current_call_flags()) {
1092 return Err(NeoError::InvalidOperation);
1093 }
1094 let handle = STORAGE_STATE.get_handle(context)?;
1095 let prefix_bytes = prefix.as_slice();
1096 let store = handle.store.read().map_err(|_| NeoError::InvalidState)?;
1097 let matches: Vec<NeoValue> = store
1098 .iter()
1099 .filter_map(|(key_bytes, value)| {
1100 if key_bytes.starts_with(prefix_bytes) {
1101 let mut entry = NeoStruct::new();
1102 entry.set_field("key", NeoValue::from(NeoByteString::from_slice(key_bytes)));
1103 entry.set_field("value", NeoValue::from(NeoByteString::from_slice(value)));
1104 Some(NeoValue::from(entry))
1105 } else {
1106 None
1107 }
1108 })
1109 .collect();
1110 Ok(NeoIterator::new(matches))
1111 }
1112
1113 #[cfg(target_arch = "wasm32")]
1120 pub fn storage_find(
1121 _context: &NeoStorageContext,
1122 _prefix: &NeoByteString,
1123 ) -> NeoResult<NeoIterator<NeoValue>> {
1124 Ok(NeoIterator::new(Vec::new()))
1125 }
1126}