neo-devpack-solidity 0.22.0

Production-focused Solidity-to-NeoVM compilation system
Documentation
use super::common::execute_bytecode_with_tokens;
use super::*;

#[test]
fn callt_emits_method_tokens_for_native_calls() {
    let source = r#"
    pragma solidity ^0.8.19;

    contract CallTHarness {
        function supply() public view returns (uint256) {
            return NativeCalls.gasTotalSupply();
        }
    }
    "#;

    let artifacts = compile_contracts_with_options(
        source,
        false,
        CompileOptions {
            optimizer_level: 2,
            use_callt: true,
            deny_wildcard_permissions: false,
            deny_wildcard_contracts: false,
            deny_wildcard_methods: false,
            manifest_permissions: None,
        },
    )
    .expect("compilation failed");
    assert_eq!(artifacts.len(), 1);

    let artifact = &artifacts[0];
    assert_eq!(
        artifact.tokens.len(),
        1,
        "expected exactly one method token for a single native call"
    );

    let token = &artifact.tokens[0];
    let expected_hash =
        super::bytecode::native_contract_hash(neo_devpack_solidity::ir::NativeContract::Gas);
    assert_eq!(token.hash, expected_hash);
    assert_eq!(token.method, "totalSupply");
    assert_eq!(token.parameters_count, 0);
    assert!(token.has_return_value);
    assert_eq!(token.call_flags, 0x05);

    assert!(
        artifact
            .bytecode
            .windows(3)
            .any(|w| w == [0x37, 0x00, 0x00]),
        "expected CALLT opcode with token index 0"
    );
}

#[test]
fn callt_emits_cryptolib_tokens_for_neo_hash_helpers() {
    let source = r#"
    pragma solidity ^0.8.19;

    contract NeoHashCallTHarness {
        function sha() public view returns (bytes32) {
            return Neo.sha256Hash(hex"616263");
        }

        function ripemd() public view returns (bytes20) {
            return Neo.ripemd160Hash(hex"616263");
        }
    }
    "#;

    let artifacts = compile_contracts_with_options(
        source,
        false,
        CompileOptions {
            optimizer_level: 2,
            use_callt: true,
            deny_wildcard_permissions: false,
            deny_wildcard_contracts: false,
            deny_wildcard_methods: false,
            manifest_permissions: None,
        },
    )
    .expect("compilation failed");
    assert_eq!(artifacts.len(), 1);

    let artifact = &artifacts[0];
    let cryptolib_hash =
        super::bytecode::native_contract_hash(neo_devpack_solidity::ir::NativeContract::CryptoLib);

    let sha_token = artifact
        .tokens
        .iter()
        .find(|token| token.hash == cryptolib_hash && token.method == "sha256")
        .expect("expected CryptoLib.sha256 method token");
    assert_eq!(sha_token.parameters_count, 1);
    assert!(sha_token.has_return_value);
    assert_eq!(sha_token.call_flags, 0x05);

    let ripemd_token = artifact
        .tokens
        .iter()
        .find(|token| token.hash == cryptolib_hash && token.method == "ripemd160")
        .expect("expected CryptoLib.ripemd160 method token");
    assert_eq!(ripemd_token.parameters_count, 1);
    assert!(ripemd_token.has_return_value);
    assert_eq!(ripemd_token.call_flags, 0x05);

    assert!(
        artifact.tokens.iter().all(|token| {
            token.method != "System.Crypto.SHA256" && token.method != "System.Crypto.RIPEMD160"
        }),
        "Neo hash helpers must not emit nonexistent System.Crypto hash method tokens"
    );
}

#[test]
fn callt_executes_neo_hash_helpers_through_cryptolib() {
    fn assert_hash_helper_executes(
        return_type: &str,
        helper: &str,
        native_method: &str,
        expected_hex: &str,
    ) {
        let source = format!(
            r#"
            pragma solidity ^0.8.19;

            contract NeoHashRuntimeHarness {{
                function run() public view returns ({return_type}) {{
                    return Neo.{helper}(hex"616263");
                }}
            }}
            "#
        );

        let artifacts = compile_contracts_with_options(
            &source,
            false,
            CompileOptions {
                optimizer_level: 2,
                use_callt: true,
                deny_wildcard_permissions: false,
                deny_wildcard_contracts: false,
                deny_wildcard_methods: false,
                manifest_permissions: None,
            },
        )
        .expect("compilation failed");
        assert_eq!(artifacts.len(), 1);

        let artifact = &artifacts[0];
        let cryptolib_hash = super::bytecode::native_contract_hash(
            neo_devpack_solidity::ir::NativeContract::CryptoLib,
        );
        assert!(
            artifact.tokens.iter().any(|token| {
                token.hash == cryptolib_hash
                    && token.method == native_method
                    && token.parameters_count == 1
                    && token.has_return_value
                    && token.call_flags == 0x05
            }),
            "expected CryptoLib.{native_method} method token"
        );

        let result = execute_bytecode_with_tokens(&artifact.bytecode, &artifact.tokens);
        assert!(
            result.is_success(),
            "expected CryptoLib.{native_method} CALLT execution to succeed: {:?}",
            result.exception
        );
        assert_eq!(
            result.return_data,
            hex::decode(expected_hex).expect("expected test vector hex"),
            "unexpected CryptoLib.{native_method} digest"
        );
    }

    assert_hash_helper_executes(
        "bytes32",
        "sha256Hash",
        "sha256",
        "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
    );
    assert_hash_helper_executes(
        "bytes20",
        "ripemd160Hash",
        "ripemd160",
        "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc",
    );
}