diff --git a/src/runtime/import/db.rs b/src/runtime/import/db.rs index 94e3958f5..9a885a97a 100644 --- a/src/runtime/import/db.rs +++ b/src/runtime/import/db.rs @@ -25,12 +25,14 @@ use darkfi_sdk::{ DB_LOOKUP_FAILED, DB_SET_FAILED, DB_SUCCESS, }, }; -use darkfi_serial::Decodable; -use log::{debug, error}; +use darkfi_serial::{deserialize, serialize, Decodable}; +use log::{debug, error, info}; use wasmer::{FunctionEnvMut, WasmPtr}; use crate::{ runtime::vm_runtime::{ContractSection, Env}, + zk::{empty_witnesses, VerifyingKey, ZkCircuit}, + zkas::ZkBinary, Result, }; @@ -529,3 +531,94 @@ pub(crate) fn db_contains_key(ctx: FunctionEnvMut, ptr: WasmPtr, len: u } } } + +/// Only `deploy()` can call this. Given a zkas circuit, create a VerifyingKey and insert +/// them both into the db. +pub(crate) fn zkas_db_set(ctx: FunctionEnvMut, ptr: WasmPtr, len: u32) -> i32 { + let env = ctx.data(); + + if env.contract_section != ContractSection::Deploy { + error!(target: "runtime::db::zkas_db_set()", "zkas_db_set called in unauthorized section"); + return CALLER_ACCESS_DENIED + } + + let memory_view = env.memory_view(&ctx); + let contract_id = &env.contract_id; + + let Ok(mem_slice) = ptr.slice(&memory_view, len) else { + error!(target: "runtime::db::zkas_db_set()", "Failed to make slice from ptr"); + return DB_SET_FAILED + }; + + let mut buf = vec![0u8; len as usize]; + if let Err(e) = mem_slice.read_slice(&mut buf) { + error!(target: "runtime::db::zkas_db_set()", "Failed to read from memory slice: {}", e); + return DB_SET_FAILED + }; + + let mut buf_reader = Cursor::new(buf); + + let zkas_bincode: Vec = match Decodable::decode(&mut buf_reader) { + Ok(v) => v, + Err(e) => { + error!(target: "runtime::db::zkas_db_set()", "Failed to decode zkas bincode bytes: {}", e); + return DB_SET_FAILED + } + }; + + // Make sure that we're actually working on legitimate bincode. + let Ok(zkbin) = ZkBinary::decode(&zkas_bincode) else { + error!(target: "runtime::db::zkas_db_set()", "Invalid zkas bincode passed to function"); + return DB_SET_FAILED + }; + + // Because of `Runtime::Deploy`, we should be sure that the zkas db is index zero. + let db_handles = env.db_handles.borrow(); + let mut db_batches = env.db_batches.borrow_mut(); + let db_handle = &db_handles[0]; + let db_batch = &mut db_batches[0]; + // Redundant check + if &db_handle.contract_id != contract_id { + error!(target: "runtime::db::zkas_db_set()", "Internal error, zkas db at index 0 incorrect"); + return DB_SET_FAILED + } + + // Check if there is existing bincode and compare it. Return DB_SUCCESS if + // they're the same. The assumption should be that VerifyingKey was generated + // already so we can skip things after this guard. + match db_handle.get(&serialize(&zkbin.namespace)) { + Ok(v) => { + if let Some(bytes) = v { + // We allow a panic here because this db should never be corrupted in this way. + let (existing_zkbin, _): (Vec, Vec) = + deserialize(&bytes).expect("deserialize tuple"); + + if existing_zkbin == zkas_bincode { + debug!(target: "runtime::db::zkas_db_set()", "Existing zkas bincode is the same. Skipping."); + return DB_SUCCESS + } + } + } + Err(e) => { + error!(target: "runtime::db::zkas_db_get()", "Internal error getting from tree: {}", e); + return DB_SET_FAILED + } + }; + + // We didn't find any existing bincode, so let's create a new VerifyingKey and write it all. + info!(target: "runtime::db::zkas_db_set()", "Creating VerifyingKey for {} zkas circuit", zkbin.namespace); + let witnesses = empty_witnesses(&zkbin); + let circuit = ZkCircuit::new(witnesses, zkbin.clone()); + let vk = VerifyingKey::build(13, &circuit); + let mut vk_buf = vec![]; + if let Err(e) = vk.write(&mut vk_buf) { + error!(target: "runtime::db::zkas_db_set()", "Failed to serialize VerifyingKey: {}", e); + return DB_SET_FAILED + } + + let key = serialize(&zkbin.namespace); + let value = serialize(&(zkas_bincode, vk_buf)); + db_batch.insert(key, value); + + DB_SUCCESS +}