Skip to content

Performance issue for guest function: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS <= self.max_available_rows #118

@medvedNick

Description

@medvedNick

I'm not able to run pretty simple fibonacci guest function in zkWasm with reasonable performance, meaning I won't be able to run my custom tough app on it.

I've tried to launch that fibonacci program on zkWasm for n > 10000 and it always ends in assertion failed: event_table.0.len() * EVENT_TABLE_ENTRY_ROWS as usize <= self.max_available_rows). I've tried to increase k from default 18 up to 24 and more, and got the same error. For k=25 setup run for more than 20mins, btw.

I understand that wasm is a pretty complex case to zk-verify, but failing calculate fibonacci program means for me no production code could be run on zkWasm. Moreover, other zk-sokutions (such as risk-0) show much better performance. For instance, I'm able to generate proof for n=150000 in 2 mins and verify it for 5ms, compared to my best result with zkWasm with n=1000 with proof generation in 1.5 mins and verification in 170ms.

The question is - is there any way to run the full setup-proof-verify cycle for such a large n like 10000? Is there any parameter other than k which I can change?

Here is the guest function
extern "C" { 
    fn wasm_input(n: i32) -> i64;
    fn log_foreign(value: i64);
}

#[no_mangle]
pub extern "C" fn entrypoint() {
    let input: i32 = unsafe { wasm_input(1) } as i32;
    let f = fibonacci(input);
    unsafe { log_foreign(f as i64) };
}

pub fn fibonacci(n: i32) -> i32 {
    let (mut a, mut b) = (0, 1);
    for _ in 0..n {
        let c = a;
        a = b;
        b += c;
    }
    a
}

log_foreign is just a renamed method log added in register_log_foreign.

I've used methods same as cli uses and my host looks like this
fn main() {
    let n = 10000;
    let wasm_binary = fs::read("res/fib.wasm").expect("No wasm");
    let prefix = "zkwasm";
    let zkwasm_k = 24;
    let function_name = "entrypoint";
    let output_dir: PathBuf = "zk".into();

    let public_inputs: Vec<u64> = vec![n];
    let private_inputs: Vec<u64> = vec![];

    let proof_path: PathBuf = "zk/zkwasm.0.transcript.data".into();

    /////////// setup
    let start = std::time::Instant::now();

    let circuit = build_circuit_without_witness(&wasm_binary, function_name);

    let params = {
        let params_path = &output_dir.join(format!("K{}.params", zkwasm_k));
        load_or_build_unsafe_params::<Bn256>(zkwasm_k, Some(params_path))
    };
    {
        let vk_path = &output_dir.join(format!("{}.{}.vkey.data", prefix, 0));
        load_or_build_vkey::<Bn256, _>(&params, &circuit, Some(vk_path))
    };
    println!("setup time: {:?}", start.elapsed());


    /////////// proof
    let start = std::time::Instant::now();

    let circuit =
        build_circuit_with_witness(&wasm_binary, function_name, &public_inputs, &private_inputs).expect("=== Something went wrong");
    let mut public_inputs_fri = public_inputs
        .iter()
        .map(|v| Fr::from(*v))
        .collect::<Vec<_>>()
        .clone();

    let mut instances = vec![];
    instances.append(&mut public_inputs_fri);

    circuit.tables.write_json(Some(output_dir.clone()));

    let vkey = load_vkey::<Bn256, TestCircuit<_>>(
        &params,
        &output_dir.join(format!("{}.{}.vkey.data", prefix, 0)),
    );

    load_or_create_proof::<Bn256, _>(
        &params,
        vkey,
        circuit.clone(),
        &[&instances],
        Some(&output_dir.join(format!("{}.{}.transcript.data", prefix, 0))),
        TranscriptHash::Poseidon,
        false,
    );
    println!("proof time: {:?}", start.elapsed());


    /////////// dry run
    build_circuit_with_witness(&wasm_binary, function_name, &public_inputs, &private_inputs);

    /////////// verify
    let start = std::time::Instant::now();

    let public_inputs_size = public_inputs.len() + 1;
    let mut public_inputs_fri = public_inputs
        .iter()
        .map(|v| Fr::from(*v))
        .collect::<Vec<_>>()
        .clone();

    let instances = {
        let mut instances = vec![];
        instances.append(&mut public_inputs_fri);
        instances
    };
    let vkey = load_vkey::<Bn256, TestCircuit<_>>(
        &params,
        &output_dir.join(format!("{}.{}.vkey.data", prefix, 0)),
    );

    let proof = load_proof(&proof_path);
    let params_verifier: ParamsVerifier<Bn256> = params.verifier(public_inputs_size).unwrap();
    let strategy = SingleVerifier::new(&params_verifier);

    verify_proof(
        &params_verifier,
        &vkey,
        strategy,
        &[&[&instances]],
        &mut PoseidonRead::init(&proof[..]),
    )
    .expect("Something is incorrect noooooooo");

    println!("verify time: {:?}", start.elapsed());
}

I tried using both main and latest v2 branches of zkWasm with default features. I'm running these benchmarks on my M1 mac btw.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions