rwasm 0.4.3

ZK-friendly WebAssembly runtime optimized for blockchain and zero-knowledge applications
Documentation
use hex_literal::hex;
use rwasm::{
    for_each_strategy,
    wasmtime::{compile_wasmtime_module, WasmtimeExecutor},
    CompilationConfig, ExecutionEngine, ImportLinker, ImportName, Opcode, RwasmModule, RwasmStore,
    StateRouterConfig, StoreTr, StrategyDefinition, TrapCode, TypedCaller, Value,
};
use rwasm_fuel_policy::SyscallFuelParams;
use std::{str::from_utf8, sync::Arc};
use wasmparser::ValType;

#[derive(Default, Clone)]
struct HostState {
    input: Vec<u8>,
    output: Vec<u8>,
    state: u32,
}

pub(crate) fn fluentbase_syscall_handler(
    caller: &mut TypedCaller<HostState>,
    sys_func_idx: u32,
    params: &[Value],
    result: &mut [Value],
) -> Result<(), TrapCode> {
    match sys_func_idx {
        // _debug_log
        70 => {
            let ptr = params[0].i32().unwrap() as usize;
            let len = params[1].i32().unwrap() as usize;
            let mut buffer = vec![0u8; len];
            caller.memory_read(ptr, &mut buffer)?;
            println!("debug_log: {}", from_utf8(&buffer).unwrap());
        }
        // _input_size
        71 => {
            result[0] = Value::I32(caller.data().input.len() as i32); // size of context input
        }
        // _output_size
        72 => {
            result[0] = Value::I32(0);
        }
        // _read
        73 => {
            let target = params[0].i32().unwrap() as usize;
            let offset = params[1].i32().unwrap() as usize; // size of context input
            let length = params[2].i32().unwrap() as usize;
            println!(
                "read: target={}, offset={}, length={}",
                target, offset, length
            );
            let data = caller.data().input[offset..(offset + length)].to_vec();
            caller.memory_write(target, &data)?;
        }
        // _write
        74 => {
            let offset = params[0].i32().unwrap() as usize;
            let length = params[1].i32().unwrap() as usize;
            let mut buffer = vec![0u8; length];
            caller.memory_read(offset, &mut buffer)?;
            println!(
                "write: {:?} ({})",
                buffer.as_slice(),
                from_utf8(&buffer).unwrap_or("can't parse utf-8 text")
            );
            caller.data_mut().output.extend_from_slice(&buffer)
        }
        // _exit
        75 => {
            let exit_code = params[0].i32().unwrap();
            println!("exit code: {}", exit_code);
            return Err(TrapCode::ExecutionHalted);
        }
        // _read_output
        76 => {
            unimplemented!("_read_output");
        }
        _ => unreachable!(),
    }
    Ok(())
}

pub(crate) fn create_import_linker() -> Arc<ImportLinker> {
    let mut import_linker = ImportLinker::default();
    import_linker.insert_function(
        ImportName::new("fluentbase_v1preview", "_debug_log"),
        70,
        SyscallFuelParams::default(),
        &[ValType::I32; 2],
        &[],
    );
    import_linker.insert_function(
        ImportName::new("fluentbase_v1preview", "_input_size"),
        71,
        SyscallFuelParams::default(),
        &[],
        &[ValType::I32; 1],
    );
    import_linker.insert_function(
        ImportName::new("fluentbase_v1preview", "_output_size"),
        72,
        SyscallFuelParams::default(),
        &[],
        &[ValType::I32; 1],
    );
    import_linker.insert_function(
        ImportName::new("fluentbase_v1preview", "_read"),
        73,
        SyscallFuelParams::default(),
        &[ValType::I32; 3],
        &[],
    );
    import_linker.insert_function(
        ImportName::new("fluentbase_v1preview", "_write"),
        74,
        SyscallFuelParams::default(),
        &[ValType::I32; 2],
        &[],
    );
    import_linker.insert_function(
        ImportName::new("fluentbase_v1preview", "_exit"),
        75,
        SyscallFuelParams::default(),
        &[ValType::I32; 1],
        &[],
    );
    import_linker.insert_function(
        ImportName::new("fluentbase_v1preview", "_read_output"),
        76,
        SyscallFuelParams::default(),
        &[ValType::I32; 3],
        &[],
    );
    Arc::new(import_linker)
}

const ATTESTATION_INPUT: &[u8] = &hex!("8444a1013822a0591220a9696d6f64756c655f69647827692d30393138663663353565336236316438392d656e633031386161386238653232383564313366646967657374665348413338346974696d657374616d701b0000018aa8d1692c6470637273b00058300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000158300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000258300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000358304a9329d69c836267b18abbf9f4a38889124490453419e426818626348d21f989dc930b1562682a9082887454e53425aa045830d0531b1400dd43288c82c226c16bf647c637dd5e4d9b4f7a8aaadc6d6760b854a06c7008cca0d15ca80094dd33a650650558300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000658300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000758300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000858300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000958300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b63657274696669636174655902803082027c30820201a0030201020210018aa8b8e2285d130000000065086098300a06082a8648ce3d04030330818e310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3110300e06035504070c0753656174746c65310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533139303706035504030c30692d30393138663663353565336236316438392e75732d656173742d312e6177732e6e6974726f2d656e636c61766573301e170d3233303931383134333730395a170d3233303931383137333731325a308193310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3110300e06035504070c0753656174746c65310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753313e303c06035504030c35692d30393138663663353565336236316438392d656e63303138616138623865323238356431332e75732d656173742d312e6177733076301006072a8648ce3d020106052b8104002203620004d966364b11a4a171ef783db158b6b1d7594739fb3b8ea0a7b60a484b2d9df549eb4d30b95f41a6fce7a3980188873f739490b41db447fddecb65ae443e49203023b3689298ecf15bd588729b96df1c2c5d5c2a290e32f31554d62f543616d0ffa31d301b300c0603551d130101ff04023000300b0603551d0f0404030206c0300a06082a8648ce3d0403030369003066023100c8b3fcbf79a6b7ec2e0f885dfeb60e669b98a2dc155dd7d3f927363c1dba8b4c721d9495d61bbc62e560aff4bab713e6023100f816abee9d7f4b6f47fac9ce3192fa94a24b6027ddafd5630a0f021623322a09c1b8f469e97e2891a27b0a40d584411968636162756e646c65845902153082021130820196a003020102021100f93175681b90afe11d46ccb4e4e7f856300a06082a8648ce3d0403033049310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753311b301906035504030c126177732e6e6974726f2d656e636c61766573301e170d3139313032383133323830355a170d3439313032383134323830355a3049310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753311b301906035504030c126177732e6e6974726f2d656e636c617665733076301006072a8648ce3d020106052b8104002203620004fc0254eba608c1f36870e29ada90be46383292736e894bfff672d989444b5051e534a4b1f6dbe3c0bc581a32b7b176070ede12d69a3fea211b66e752cf7dd1dd095f6f1370f4170843d9dc100121e4cf63012809664487c9796284304dc53ff4a3423040300f0603551d130101ff040530030101ff301d0603551d0e041604149025b50dd90547e796c396fa729dcf99a9df4b96300e0603551d0f0101ff040403020186300a06082a8648ce3d0403030369003066023100a37f2f91a1c9bd5ee7b8627c1698d255038e1f0343f95b63a9628c3d39809545a11ebcbf2e3b55d8aeee71b4c3d6adf3023100a2f39b1605b27028a5dd4ba069b5016e65b4fbde8fe0061d6a53197f9cdaf5d943bc61fc2beb03cb6fee8d2302f3dff65902c3308202bf30820244a003020102021068184d8ffbb171b19cc685d8e4b58ab7300a06082a8648ce3d0403033049310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753311b301906035504030c126177732e6e6974726f2d656e636c61766573301e170d3233303931373032303734355a170d3233313030373033303734355a3064310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533136303406035504030c2d636335343332313363663866346361342e75732d656173742d312e6177732e6e6974726f2d656e636c617665733076301006072a8648ce3d020106052b810400220362000433a3c46cfef59e887d652628fab486a190114f72dc4592fb3a0c0d9d9f14257163cd7bdedade51fe183a2bc29fb3d302298b725f90bc800bf5f9b0328e9e2b7d5b58b72868307b355888a884d20afe92c7e91eb3be91a6930c05c8e428a20c65a381d53081d230120603551d130101ff040830060101ff020102301f0603551d230418301680149025b50dd90547e796c396fa729dcf99a9df4b96301d0603551d0e0416041457583badb7cc976738ed98092cc36183aac1d36b300e0603551d0f0101ff040403020186306c0603551d1f046530633061a05fa05d865b687474703a2f2f6177732d6e6974726f2d656e636c617665732d63726c2e73332e616d617a6f6e6177732e636f6d2f63726c2f61623439363063632d376436332d343262642d396539662d3539333338636236376638342e63726c300a06082a8648ce3d0403030369003066023100ed25470e163893a16a11add11166647e1618fa74e5db7a4d5d17b78fe9aaa7259d16fcb210325a46ca40add6b9e9d48c023100904f68622f5f8e5bbff01d21dc47989ec66b303aea0cd45865b9b7368215f58a6973c2b20af98c0e9a44362fb35bb4e9590319308203153082029ba003020102021100c8ddef6845627ed181a7a91e716c92f9300a06082a8648ce3d0403033064310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533136303406035504030c2d636335343332313363663866346361342e75732d656173742d312e6177732e6e6974726f2d656e636c61766573301e170d3233303931373139303335345a170d3233303932333134303335335a308189313c303a06035504030c33633466396430366336306437636239322e7a6f6e616c2e75732d656173742d312e6177732e6e6974726f2d656e636c61766573310c300a060355040b0c03415753310f300d060355040a0c06416d617a6f6e310b3009060355040613025553310b300906035504080c0257413110300e06035504070c0753656174746c653076301006072a8648ce3d020106052b8104002203620004edc17268efbb5649426ae7b34b5de689a9dc8650936cd2f979e08a49359db600f95eb5e144084674f677979275d100b85b47179d055d125cfe11057c79b1ea3e5c2f0ed4289bb699738a212a2f7893150ae8592aea4d9087d6484b68c34c2a3ca381ea3081e730120603551d130101ff040830060101ff020101301f0603551d2304183016801457583badb7cc976738ed98092cc36183aac1d36b301d0603551d0e04160414650f61726ac9f304b54c5b8abe2b6ec871e0c724300e0603551d0f0101ff0404030201863081800603551d1f047930773075a073a071866f687474703a2f2f63726c2d75732d656173742d312d6177732d6e6974726f2d656e636c617665732e73332e75732d656173742d312e616d617a6f6e6177732e636f6d2f63726c2f37316336376235382d663261642d346532652d383634632d3066383665643163343364322e63726c300a06082a8648ce3d040303036800306502303b3f3f4a0cf3d22220da3e5cc2ac14ee0a1a30e3225c3fddb8020b68ae4f4843e232f8f5ccc57a1b32a5d0e28adaddfb023100ebb89c6ff76d3a8139209c51b511bf80bc9f90d985e1a18bd4281b428d441ef0fef2507ef1ec061f9ab85806277cef615902833082027f30820204a003020102021464fb29c5ba37c8c7f539fe3991147ae7063e16f9300a06082a8648ce3d040303308189313c303a06035504030c33633466396430366336306437636239322e7a6f6e616c2e75732d656173742d312e6177732e6e6974726f2d656e636c61766573310c300a060355040b0c03415753310f300d060355040a0c06416d617a6f6e310b3009060355040613025553310b300906035504080c0257413110300e06035504070c0753656174746c65301e170d3233303931383034343634395a170d3233303931393034343634395a30818e310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3110300e06035504070c0753656174746c65310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533139303706035504030c30692d30393138663663353565336236316438392e75732d656173742d312e6177732e6e6974726f2d656e636c617665733076301006072a8648ce3d020106052b81040022036200042c90cb367a3d124bc7a9dd58e7a18d3d2ec998051bc4d232396c89e10f90dbcbc7f77a8b047a5397b5f429dff27b69b2588a0a663581af059d40b07bcc407a9de216a688b82b931e0fa8caaff676cfe0177851c26c4dead495b466db7316de6ba326302430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020204300a06082a8648ce3d04030303690030660231008d777a69f73a52ed061ae0dffcbf45d18aadf9a04e8f6d25830eff92d4a2d20e5294b69dd30ec98b9fdc3db2bdb3c62f023100b22ff1b3071c34eb9421eed0aae61bb2ebd945a660375bfbf9ab07722f2ec721a00d6531a3481ab2fc643592506b0a796a7075626c69635f6b6579f669757365725f64617461585b3059301306072a8648ce3d020106082a8648ce3d030107034200042afc52fe36bd5f190b5c90a7ef3349716dcbc4aa003dde71114b11d2faa6648e0713c527439746a8d23dab9e1b999e847762ef385bb5cf27295323b9908b2acb656e6f6e6365590100bba6bfd51866d2e4e095ba3277f208a4692e62f76b98595bf13204bc5f36be7a13120a5de19a3b5fcebcca0722983901db66d35c419c2e70ea8c7aa48a56df715ae4a39ad5fe0e4d056b2d8b2eb756b69fd231f86e8e39a1607ea5d9ab8d078c5d27147fdc2b0b5404c281f79a45381ed6533c048f38e3765b5e9776d36c7452b9d6cdae09ecfdd088c74b680dcf3bb520d0ff926074e7b6fc2c0b6a5d0b07adeba14295b01bdf7a155d0ad08f40d958ee6a837a5655a7fff35a16f7fb7e40aadaf39399f08987941950c50847e0232cd4a1d3161071f54fdad3e1f5706f4140b28859c169c0fc2526993e9d94d4657644100cd32efd6e6671ab4ae4119c1f215860efb3c8e015ab5748fc3a211c81739855a147003cdf46917edaf2fcf7c6d1d1beb535f2320f2a89c44f8500a796596d1af70dcfd950b59fdfeb6677c0992d7863236a289cc54a801f7caea200ae548301f059eaf8a7c3688e6c864d5dcada3fef");

#[test]
#[ignore] // run this test manually with the "--release" flag
fn test_nitro_verifier_rwasm() {
    let wasm_binary = include_bytes!("assets/nitro-verifier-stack-ub.wasm");
    let import_linker = create_import_linker();
    let config = CompilationConfig::default()
        .with_entrypoint_name("main".into())
        .with_allow_malformed_entrypoint_func_type(true)
        .with_import_linker(import_linker.clone());
    let (rwasm_module, _) = RwasmModule::compile(config, wasm_binary).unwrap();
    let mut store = RwasmStore::<HostState>::new(
        import_linker.clone(),
        HostState {
            input: ATTESTATION_INPUT.to_vec(),
            ..Default::default()
        },
        fluentbase_syscall_handler,
        None,
        None,
    );
    let instance = import_linker
        .instantiate(&mut store, ExecutionEngine::new(), rwasm_module)
        .unwrap();
    instance.execute(&mut store, &[], &mut []).unwrap();
}

#[cfg(feature = "wasmtime")]
#[test]
fn test_nitro_verifier_wasmtime() {
    let wasm_binary = include_bytes!("assets/nitro-verifier-stack-ub.wasm");
    let import_linker = create_import_linker();
    let config = CompilationConfig::default()
        .with_entrypoint_name("main".into())
        .with_allow_malformed_entrypoint_func_type(true)
        .with_import_linker(import_linker.clone())
        .with_consume_fuel(false);
    let (rwasm_module, _) = RwasmModule::compile(config, wasm_binary).unwrap();
    // compile & run using wasmtime
    let module = compile_wasmtime_module(
        CompilationConfig::default().with_consume_fuel(false),
        &rwasm_module.hint_section,
    )
    .unwrap();
    let mut worker = WasmtimeExecutor::<HostState>::new(
        module,
        import_linker,
        HostState {
            input: ATTESTATION_INPUT.to_vec(),
            ..Default::default()
        },
        fluentbase_syscall_handler,
        None,
        None,
    );
    worker.execute("main", &[], &mut []).unwrap();
}

#[test]
#[ignore] // run this test manually with the "--release" flag
fn test_nitro_verifier_strategy() {
    let wasm_binary = include_bytes!("assets/nitro-verifier-stack-ub.wasm");
    let import_linker = create_import_linker();
    let config = CompilationConfig::default()
        .with_entrypoint_name("main".into())
        .with_allow_malformed_entrypoint_func_type(true)
        .with_import_linker(import_linker.clone());
    let exec_strategy = |strategy: StrategyDefinition| {
        let mut executor = strategy
            .create_executor(
                import_linker.clone(),
                HostState {
                    input: ATTESTATION_INPUT.to_vec(),
                    ..Default::default()
                },
                fluentbase_syscall_handler,
                Some(1_000_000_000),
                None,
            )
            .unwrap();
        executor.execute("main", &[], &mut []).map_err(Into::into)
    };
    // run with rwasm strategy first
    for_each_strategy(exec_strategy, config, wasm_binary).unwrap();
}

const STATE_MAIN: u32 = 1;
const STATE_DEPLOY: u32 = 2;

fn run_fluentbase_binary(wasm_binary: &[u8], host_state: HostState) -> HostState {
    let import_linker = create_import_linker();
    let config = CompilationConfig::default()
        .with_state_router(StateRouterConfig {
            states: Box::new([("deploy".into(), STATE_DEPLOY), ("main".into(), STATE_MAIN)]),
            opcode: Some(Opcode::I32Const(STATE_MAIN.into())),
        })
        .with_import_linker(import_linker.clone());
    let (rwasm_module, _) = RwasmModule::compile(config, wasm_binary).unwrap();
    let mut store = RwasmStore::new(
        import_linker.clone(),
        host_state,
        fluentbase_syscall_handler,
        None,
        None,
    );
    let instance = ImportLinker::default()
        .instantiate(&mut store, ExecutionEngine::new(), rwasm_module)
        .unwrap();
    instance.execute(&mut store, &[], &mut []).unwrap();
    store.data().clone()
}

#[test]
fn test_wasm_panic() {
    let wasm_binary = include_bytes!("assets/panic-stack-ub.wasm");
    let mut host_state = HostState {
        state: STATE_MAIN,
        ..Default::default()
    };
    host_state.state = STATE_MAIN;
    let host_state = run_fluentbase_binary(wasm_binary, host_state);
    assert_eq!(
        from_utf8(host_state.output.as_slice()).unwrap(),
        "it's panic time"
    )
}

#[test]
fn test_wasm_secp256k1() {
    let wasm_binary = include_bytes!("assets/secp256k1-stack-ub.wasm");
    let mut host_state = HostState {
        input: vec![0u8; 1024],
        output: vec![],
        state: STATE_MAIN,
    };
    host_state.input.extend_from_slice(&hex!("a04a451028d0f9284ce82243755e245238ab1e4ecf7b9dd8bf4734d9ecfd0529cf09dd8d0eb3c3968aca8846a249424e5537d3470f979ff902b57914dc77d02316bd29784f668a73cc7a36f4cc5b9ce704481e6cb5b1c2c832af02ca6837ebec044e3b81af9c2234cad09d679ce6035ed1392347ce64ce405f5dcd36228a25de6e47fd35c4215d1edf53e6f83de344615ce719bdb0fd878f6ed76f06dd277956de"));
    run_fluentbase_binary(wasm_binary, host_state);
}