#![allow(unused_imports)]
use super::common::*;
use neo_devpack_solidity::cli::compile_contracts;
use neo_devpack_solidity::runtime::types::StackItem;
use neo_devpack_solidity::runtime::{NeoRuntime, RuntimeConfig};
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(8))]
#[test]
fn native_policy_exec_fee_factor_roundtrip(
factor in prop_oneof![
Just(0u32),
Just(1u32),
Just(1000u32),
Just(u32::MAX),
1u32..=10_000u32,
],
) {
let src = r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {
function rt(uint32 v) external returns (uint32) {
NativeCalls.setExecFeeFactor(v);
return NativeCalls.getExecFeeFactor();
}
}"#;
let arts = compile_contracts(src, false, 2)
.unwrap_or_else(|e| panic!("Policy.setExecFeeFactor compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(
&art.bytecode, &art.tokens, &art.manifest, "rt",
&[StackItem::UnsignedInteger(factor as u64)],
)
.expect("Policy set/get exec-fee-factor host-level");
prop_assert!(r.success,
"Policy.set/getExecFeeFactor({}) must succeed; exc={:?}.",
factor, r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(factor as u64),
"Policy.set/getExecFeeFactor round-trip: wrote {} read {} \
(rd_hex={}). If they disagree, the handler dropped or \
truncated the value, or read from a different storage slot.",
factor, v, hex::encode(&r.return_data));
}
#[test]
fn native_policy_storage_price_roundtrip(
price in prop_oneof![
Just(0u64),
Just(1u64),
Just(u32::MAX as u64),
Just((1u64 << 50) - 1),
1u64..=1_000_000_000u64,
],
) {
let src = r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {
function rt(uint256 v) external returns (uint256) {
NativeCalls.setStoragePrice(v);
return NativeCalls.getStoragePrice();
}
}"#;
let arts = compile_contracts(src, false, 2)
.unwrap_or_else(|e| panic!("Policy.setStoragePrice compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(
&art.bytecode, &art.tokens, &art.manifest, "rt",
&[StackItem::UnsignedInteger(price)],
)
.expect("Policy set/get storage-price host-level");
prop_assert!(r.success,
"Policy.set/getStoragePrice({}) must succeed; exc={:?}.",
price, r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(price),
"Policy.set/getStoragePrice round-trip: wrote {} read {} \
(rd_hex={}).", price, v, hex::encode(&r.return_data));
}
#[test]
fn native_policy_is_blocked_returns_false_for_unknown(
addr_bytes in any::<[u8; 20]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function b() external view returns (bool) {{
return NativeCalls.isBlocked(address(0x{}));
}}
}}"#,
hex::encode(addr_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("Policy.isBlocked compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "b", &[])
.expect("Policy.isBlocked host-level");
prop_assert!(r.success,
"Policy.isBlocked must succeed for any address; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
let is_zero = r.return_data.is_empty()
|| r.return_data.iter().all(|b| *b == 0);
prop_assert!(is_zero,
"Policy.isBlocked(unknown) must return false; got rd_hex={} \
({}B). If non-zero, the blocked-accounts set is leaking state \
across calls or the handler synthesises a random truthy value.",
hex::encode(&r.return_data), r.return_data.len());
}
#[test]
fn native_policy_block_unblock_roundtrip(
addr_bytes in any::<[u8; 20]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function rt() external returns (bool blockedAfterAdd, bool blockedAfterRemove) {{
address acc = address(0x{});
NativeCalls.blockAccount(acc);
blockedAfterAdd = NativeCalls.isBlocked(acc);
NativeCalls.unblockAccount(acc);
blockedAfterRemove = NativeCalls.isBlocked(acc);
}}
}}"#,
hex::encode(addr_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("Policy block/unblock compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "rt", &[])
.expect("Policy block/unblock host-level");
prop_assert!(r.success,
"Policy block→isBlocked→unblock→isBlocked must succeed; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
prop_assert!(!r.return_data.is_empty(),
"Policy block/unblock must return data; got 0B.");
let any_truthy_first =
r.return_data.iter().take(32).any(|b| *b != 0);
let all_zero_second = r.return_data.len() < 64
|| r.return_data[32..64].iter().all(|b| *b == 0);
prop_assert!(any_truthy_first,
"After blockAccount, isBlocked must return true; first 32B all \
zero in rd_hex={}.", hex::encode(&r.return_data));
prop_assert!(all_zero_second,
"After unblockAccount, isBlocked must return false; second 32B \
not all zero in rd_hex={}.", hex::encode(&r.return_data));
}
#[test]
fn native_oracle_request_accepts_random_strings(
url in "[a-zA-Z0-9: filter in "[a-zA-Z0-9./_$-]{0,16}",
callback in "[a-zA-Z_][a-zA-Z0-9_]{0,16}",
user_data in prop::collection::vec(any::<u8>(), 0..=32),
gas in 0u64..=1_000_000_000u64,
) {
let src = r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {
function r(string memory u, string memory f, string memory cb,
bytes memory ud, uint256 g) external {
NativeCalls.requestOracleData(u, f, cb, ud, g);
}
}"#;
let arts = compile_contracts(src, false, 2)
.unwrap_or_else(|e| panic!("Oracle.request compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(
&art.bytecode, &art.tokens, &art.manifest, "r",
&[
StackItem::byte_array(url.as_bytes().to_vec()),
StackItem::byte_array(filter.as_bytes().to_vec()),
StackItem::byte_array(callback.as_bytes().to_vec()),
StackItem::byte_array(user_data.clone()),
StackItem::UnsignedInteger(gas),
],
)
.expect("Oracle.request host-level");
prop_assert!(r.success,
"Oracle.request(url={:?}, filter={:?}, cb={:?}, ud={}B, gas={}) \
must execute without faulting; exc={:?}.",
url, filter, callback, user_data.len(), gas,
r.exception.as_ref().map(|e| &e.message));
}
#[test]
fn native_oracle_price_roundtrip(
price in prop_oneof![
Just(0u64),
Just(1u64),
1u64..=10_000_000_000u64,
],
) {
let src = r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {
function rt(uint256 p) external returns (uint256) {
NativeCalls.setOraclePrice(p);
return NativeCalls.getOraclePrice();
}
}"#;
let arts = compile_contracts(src, false, 2)
.unwrap_or_else(|e| panic!("Oracle.set/getPrice compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(
&art.bytecode, &art.tokens, &art.manifest, "rt",
&[StackItem::UnsignedInteger(price)],
)
.expect("Oracle price round-trip host-level");
prop_assert!(r.success,
"Oracle set/getPrice({}) must succeed; exc={:?}.",
price, r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(price),
"Oracle.set/getPrice round-trip: wrote {} read {} (rd_hex={}).",
price, v, hex::encode(&r.return_data));
}
#[test]
fn native_role_management_get_designated_by_role_empty_default(
role in 0u8..=255u8,
index in prop_oneof![
Just(0u64),
Just(u64::MAX),
0u64..=1_000_000u64,
],
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function g() external view returns (uint256) {{
bytes[] memory keys = NativeCalls.getDesignatedByRole(
bytes1(uint8({})), {}
);
return keys.length;
}}
}}"#,
role, index
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("RoleManagement.getDesignatedByRole compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "g", &[])
.expect("RoleManagement.getDesignatedByRole host-level");
prop_assert!(r.success,
"RoleManagement.getDesignatedByRole(role={}, index={}) must \
succeed; exc={:?}. If this faults, the handler may be returning \
Null instead of an empty array, causing the ABI decoder to throw.",
role, index, r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(0u64),
"RoleManagement.getDesignatedByRole(role={}, index={}) on a \
fresh runtime must return an empty array; got length={} \
(rd_hex={}).",
role, index, v, hex::encode(&r.return_data));
}
#[test]
fn native_contract_management_is_contract_false_for_unknown(
addr_bytes in any::<[u8; 20]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function f() external view returns (bool) {{
return NativeCalls.isContract(address(0x{}));
}}
}}"#,
hex::encode(addr_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("ContractManagement.isContract compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "f", &[])
.expect("ContractManagement.isContract host-level");
prop_assert!(r.success,
"ContractManagement.isContract({:#x?}) must succeed; exc={:?}.",
addr_bytes, r.exception.as_ref().map(|e| &e.message));
prop_assert!(r.return_data.len() <= 32,
"isContract return must fit in a uint256 / bool ABI slot; got \
{}B (rd_hex={}).",
r.return_data.len(), hex::encode(&r.return_data));
}
#[test]
fn native_contract_management_has_method_returns_bool(
addr_bytes in any::<[u8; 20]>(),
method in "[a-zA-Z_][a-zA-Z0-9_]{0,16}",
param_count in 0u8..=10u8,
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function f() external view returns (bool) {{
return NativeCalls.hasMethod(address(0x{}), "{}", {});
}}
}}"#,
hex::encode(addr_bytes), method, param_count
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("ContractManagement.hasMethod compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "f", &[])
.expect("ContractManagement.hasMethod host-level");
prop_assert!(r.success,
"ContractManagement.hasMethod(addr, {:?}, {}) must succeed; \
exc={:?}.",
method, param_count, r.exception.as_ref().map(|e| &e.message));
prop_assert!(r.return_data.len() <= 32,
"hasMethod return must fit in a uint256 / bool ABI slot; got \
{}B (rd_hex={}).",
r.return_data.len(), hex::encode(&r.return_data));
}
#[test]
fn native_ledger_get_block_by_index(
index in prop_oneof![
Just(0u64),
Just(1u64),
Just(u64::MAX),
0u64..=1_000_000u64,
],
) {
let src = r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {
function g(uint256 i) external view returns (uint256) {
Syscalls.Block memory b = NativeCalls.getBlock(i);
return b.timestamp;
}
}"#;
let arts = compile_contracts(src, false, 2)
.unwrap_or_else(|e| panic!("Ledger.getBlock compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(
&art.bytecode, &art.tokens, &art.manifest, "g",
&[StackItem::UnsignedInteger(index)],
)
.expect("Ledger.getBlock host-level");
if r.success {
prop_assert!(r.return_data.len() <= 32,
"Ledger.getBlock({}).timestamp must fit in uint256; got {}B \
(rd_hex={}).",
index, r.return_data.len(), hex::encode(&r.return_data));
} else {
prop_assert!(r.exception.is_some(),
"Ledger.getBlock({}) failed silently (no exception); \
rd_hex={}. A null block must surface as a clean exception, \
not a silent zero return.",
index, hex::encode(&r.return_data));
}
}
#[test]
fn native_ledger_get_transaction_height_unknown_returns_negative(
hash_bytes in any::<[u8; 32]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function h() external view returns (int256) {{
return NativeCalls.getTransactionHeight(bytes32(0x{}));
}}
}}"#,
hex::encode(hash_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("Ledger.getTransactionHeight compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "h", &[])
.expect("Ledger.getTransactionHeight host-level");
prop_assert!(r.success,
"Ledger.getTransactionHeight must succeed for any hash; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
prop_assert!(!r.return_data.is_empty(),
"Ledger.getTransactionHeight(unknown hash) must return -1, not \
empty bytes (which would decode to 0, falsely matching a real \
height); got rd_hex='' for hash={}.",
hex::encode(hash_bytes));
}
#[test]
fn native_neo_balance_of_unknown_returns_zero(
addr_bytes in any::<[u8; 20]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function b() external view returns (uint256) {{
return NativeCalls.neoBalanceOf(address(0x{}));
}}
}}"#,
hex::encode(addr_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("NeoToken.balanceOf compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "b", &[])
.expect("NeoToken.balanceOf host-level");
prop_assert!(r.success,
"NeoToken.balanceOf must succeed for any address; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(0u64),
"NeoToken.balanceOf(unknown addr {:#x?}) must be 0; got {} \
(rd_hex={}). Non-zero would indicate a leaking balance state.",
addr_bytes, v, hex::encode(&r.return_data));
}
#[test]
fn native_neo_get_committee_non_empty(
seed in 0u64..1_000_000u64,
) {
let _ = seed;
let src = r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {
function c() external view returns (uint256) {
bytes[] memory members = NativeCalls.getCommittee();
return members.length;
}
}"#;
let arts = compile_contracts(src, false, 2)
.unwrap_or_else(|e| panic!("NeoToken.getCommittee compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "c", &[])
.expect("NeoToken.getCommittee host-level");
prop_assert!(r.success,
"NeoToken.getCommittee must succeed; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert!(v > num_bigint::BigUint::from(0u64),
"NeoToken.getCommittee must yield a non-empty array (handler \
hard-codes ≥1 member); got length={} (rd_hex={}).",
v, hex::encode(&r.return_data));
}
#[test]
fn native_gas_balance_of_unknown_returns_zero(
addr_bytes in any::<[u8; 20]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function b() external view returns (uint256) {{
return NativeCalls.gasBalanceOf(address(0x{}));
}}
}}"#,
hex::encode(addr_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("GasToken.balanceOf compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "b", &[])
.expect("GasToken.balanceOf host-level");
prop_assert!(r.success,
"GasToken.balanceOf must succeed for any address; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(0u64),
"GasToken.balanceOf(unknown addr {:#x?}) must be 0; got {} \
(rd_hex={}).",
addr_bytes, v, hex::encode(&r.return_data));
}
#[test]
fn native_notary_balance_of_unknown_returns_zero(
addr_bytes in any::<[u8; 20]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function b() external view returns (uint256) {{
return NativeCalls.notaryBalanceOf(address(0x{}));
}}
}}"#,
hex::encode(addr_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("Notary.balanceOf compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "b", &[])
.expect("Notary.balanceOf host-level");
prop_assert!(r.success,
"Notary.balanceOf must succeed for any address; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(0u64),
"Notary.balanceOf(unknown addr {:#x?}) must be 0; got {} \
(rd_hex={}).",
addr_bytes, v, hex::encode(&r.return_data));
}
#[test]
fn native_notary_expiration_of_unknown_returns_zero(
addr_bytes in any::<[u8; 20]>(),
) {
let source = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {{
function e() external view returns (uint256) {{
return NativeCalls.notaryExpirationOf(address(0x{}));
}}
}}"#,
hex::encode(addr_bytes)
);
let arts = compile_contracts(&source, false, 2)
.unwrap_or_else(|e| panic!("Notary.expirationOf compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(&art.bytecode, &art.tokens, &art.manifest, "e", &[])
.expect("Notary.expirationOf host-level");
prop_assert!(r.success,
"Notary.expirationOf must succeed for any address; exc={:?}.",
r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(0u64),
"Notary.expirationOf(unknown addr {:#x?}) must be 0; got {} \
(rd_hex={}).",
addr_bytes, v, hex::encode(&r.return_data));
}
#[test]
fn native_notary_max_not_valid_before_delta_roundtrip(
delta in prop_oneof![
Just(0u64),
Just(u32::MAX as u64),
1u64..=1_000_000u64,
],
) {
let src = r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract C {
function rt(uint256 d) external returns (uint256) {
NativeCalls.notarySetMaxNotValidBeforeDelta(d);
return NativeCalls.notaryGetMaxNotValidBeforeDelta();
}
}"#;
let arts = compile_contracts(src, false, 2)
.unwrap_or_else(|e| panic!("Notary set/getMaxNotValidBeforeDelta compile: {:?}", e));
let art = &arts[0];
let mut rt = NeoRuntime::new(RuntimeConfig::default()).expect("rt");
let r = rt
.call_method(
&art.bytecode, &art.tokens, &art.manifest, "rt",
&[StackItem::UnsignedInteger(delta)],
)
.expect("Notary delta round-trip host-level");
prop_assert!(r.success,
"Notary set/getMaxNotValidBeforeDelta({}) must succeed; exc={:?}.",
delta, r.exception.as_ref().map(|e| &e.message));
let v = decode_uint_le(&r.return_data);
let expected = (delta as u32) as u64;
prop_assert_eq!(v.clone(), num_bigint::BigUint::from(expected),
"Notary set/getMaxNotValidBeforeDelta round-trip: wrote {} \
expected (after u32 cast) {} read {} (rd_hex={}).",
delta, expected, v, hex::encode(&r.return_data));
}
}