use super::*;
use crate::instruction::OpCode;
#[test]
fn decompile_syscall_includes_human_name() {
let script = [
OpCode::Syscall.byte(),
0xB2,
0x79,
0xFC,
0xF6,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
assert!(decompilation
.pseudocode
.as_deref()
.expect("pseudocode output")
.contains("System.Runtime.Platform"));
assert!(decompilation
.high_level
.as_deref()
.expect("high-level output")
.contains("System.Runtime.Platform()"));
}
#[test]
fn void_syscall_does_not_push_stack_value() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0x95,
0x01,
0x6F,
0x61,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Runtime.Notify("),
"void syscall should be emitted as a statement"
);
assert!(
!high_level.contains("let t0 = System.Runtime.Notify()"),
"void syscall should not push a temp onto the stack"
);
}
#[test]
fn unknown_syscall_is_assumed_to_return_value() {
let unknown_hash = 0xDEADBEEF;
assert!(
crate::syscalls::lookup(unknown_hash).is_none(),
"fixture hash should not be present in the syscall catalog"
);
let script = [
OpCode::Syscall.byte(),
0xEF,
0xBE,
0xAD,
0xDE,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high.contains("syscall(0xDEADBEEF)"),
"unknown syscalls should conservatively push a stack value: {high}"
);
assert!(
high.contains("// warning: unknown syscall 0xDEADBEEF"),
"unknown-syscall warning should be visible inline: {high}"
);
}
#[test]
fn syscall_arguments_render_in_declaration_order() {
let script = [
OpCode::Newarray0.byte(), OpCode::Pushdata1.byte(),
0x03,
b'e',
b'v',
b't', OpCode::Syscall.byte(),
0x95,
0x01,
0x6F,
0x61, OpCode::Ret.byte(), ];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.with_inline_single_use_temps(true)
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Runtime.Notify(\"evt\","),
"event name must render as the first Notify argument: {high_level}"
);
}
#[test]
fn storage_put_arguments_render_in_pop_order() {
let script = [
OpCode::Push1.byte(),
OpCode::Push2.byte(),
OpCode::Push3.byte(),
OpCode::Syscall.byte(),
0xE6,
0x3F,
0x18,
0x84,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.with_inline_single_use_temps(true)
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Storage.Put(3, 2, 1)"),
"Storage.Put arguments must render in pop order: {high_level}"
);
}
#[test]
fn void_storage_syscall_is_emitted_as_statement() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0xE6,
0x3F,
0x18,
0x84,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Storage.Put("),
"void storage syscall should be emitted as a statement"
);
assert!(
!high_level.contains("let t0 = System.Storage.Put()"),
"void storage syscall should not push a temp onto the stack"
);
}
#[test]
fn void_storage_local_syscall_is_emitted_as_statement() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0x39,
0x0C,
0xE3,
0x0A,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Storage.Local.Put("),
"void storage local syscall should be emitted as a statement"
);
assert!(
!high_level.contains("let t0 = System.Storage.Local.Put()"),
"void storage local syscall should not push a temp onto the stack"
);
}
#[test]
fn syscall_contract_call_expands_to_native_contract_form() {
let gas_token_hash: [u8; 20] = [
0xCF, 0x76, 0xE2, 0x8B, 0xD0, 0x06, 0x2C, 0x4A, 0x47, 0x8E, 0xE3, 0x55, 0x61, 0x01, 0x13,
0x19, 0xF3, 0xCF, 0xA4, 0xD2,
];
let mut script = vec![
OpCode::Newarray0.byte(),
OpCode::Pushint8.byte(),
0x0F,
OpCode::Pushdata1.byte(),
0x08,
b't',
b'r',
b'a',
b'n',
b's',
b'f',
b'e',
b'r',
OpCode::Pushdata1.byte(),
0x14,
];
script.extend_from_slice(&gas_token_hash);
script.extend_from_slice(&[
OpCode::Syscall.byte(),
0x62,
0x7D,
0x5B,
0x52, OpCode::Ret.byte(),
]);
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("GasToken.transfer("),
"native contract call should expand to `GasToken.transfer(args)`: {high_level}"
);
assert!(
!high_level.contains("syscall(\"System.Contract.Call\""),
"native contract call must not surface the raw syscall: {high_level}"
);
assert!(
high_level.contains("= GasToken.transfer("),
"native contract call returns a value, must push a temp: {high_level}"
);
}
#[test]
fn syscall_contract_call_with_unknown_hash_uses_hex_form() {
let custom_hash: [u8; 20] = [
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
0x00, 0x12, 0x34, 0x56, 0x78,
];
let mut script = vec![
OpCode::Newarray0.byte(),
OpCode::Pushint8.byte(),
0x0F,
OpCode::Pushdata1.byte(),
0x04,
b't',
b'e',
b's',
b't',
OpCode::Pushdata1.byte(),
0x14,
];
script.extend_from_slice(&custom_hash);
script.extend_from_slice(&[
OpCode::Syscall.byte(),
0x62,
0x7D,
0x5B,
0x52,
OpCode::Ret.byte(),
]);
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("0x112233445566778899AABBCCDDEEFF0012345678.test("),
"unknown-hash contract call must show the hex form so the reader can grep the manifest: {high_level}"
);
assert!(
!high_level.contains("syscall(\"System.Contract.Call\""),
"unknown-hash contract call must not fall back to the syscall wrapper: {high_level}"
);
}
#[test]
fn syscall_contract_call_returns_value() {
let script = [
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0x62,
0x7D,
0x5B,
0x52,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Contract.Call("),
"untracked contract-call hash should fall back to the syscall form: {high_level}"
);
assert!(
decompilation
.warnings
.iter()
.any(|warning| warning.contains("contract-call hash was not a tracked 20-byte literal")),
"structured warning should fire when the hash isn't a tracked 20-byte literal: {:?}",
decompilation.warnings
);
}
#[test]
fn syscall_runtime_log_is_void() {
let script = [
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0xCF,
0xE7,
0x47,
0x96,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Runtime.Log("),
"System.Runtime.Log is void and should be emitted as statement: {high_level}"
);
assert!(
!high_level.contains("let t0 = System.Runtime.Log()"),
"void syscall should not push a temp: {high_level}"
);
}
#[test]
fn syscall_runtime_log_missing_argument_emits_warning() {
let script = [
OpCode::Syscall.byte(),
0xCF,
0xE7,
0x47,
0x96,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Runtime.Log(???)"),
"missing syscall argument should still be shown in output: {high_level}"
);
assert!(
high_level.contains("missing syscall argument values for System.Runtime.Log"),
"missing syscall argument should be called out inline in the output: {high_level}"
);
assert!(
decompilation
.warnings
.iter()
.any(|warning| warning.contains("missing syscall argument values")),
"missing syscall argument should emit a structured warning: {:?}",
decompilation.warnings
);
}
#[test]
fn syscall_runtime_log_after_packed_store_reports_consumed_slot_context() {
let script = [
OpCode::Initslot.byte(),
0x01,
0x00, OpCode::Pushdata1.byte(),
0x05,
b'H',
b'e',
b'l',
b'l',
b'o', OpCode::Push1.byte(), OpCode::Pack.byte(), OpCode::Stloc0.byte(), OpCode::Syscall.byte(),
0xCF,
0xE7,
0x47,
0x96, OpCode::Ret.byte(), ];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Runtime.Log(???)"),
"missing syscall argument should still be rendered: {high_level}"
);
assert!(
high_level.contains("preceding STLOC0 stored a packed value into loc0"),
"packed-store context should be surfaced inline: {high_level}"
);
assert!(
decompilation.warnings.iter().any(|warning| {
warning.contains("preceding STLOC0 stored a packed value into loc0")
}),
"packed-store context should also be emitted as a structured warning: {:?}",
decompilation.warnings
);
}
#[test]
fn syscall_check_witness_returns_value() {
let script = [
OpCode::Push0.byte(),
OpCode::Syscall.byte(),
0xF8,
0x27,
0xEC,
0x8C,
OpCode::Ret.byte(),
];
let nef_bytes = build_nef(&script);
let decompilation = Decompiler::new()
.decompile_bytes(&nef_bytes)
.expect("decompile succeeds");
let high_level = decompilation
.high_level
.as_deref()
.expect("high-level output");
assert!(
high_level.contains("System.Runtime.CheckWitness("),
"CheckWitness should resolve to human name: {high_level}"
);
assert!(
high_level.contains("= System.Runtime.CheckWitness("),
"CheckWitness returns a value: {high_level}"
);
}