runtime/db: Implement zkas_db_set

This is used specifically for zkas circuits bundled with the WASM
contracts. If necessary, the functions will also create and serialize
VerifyingKey objects needed to verify proofs using the given circuit
and store them in the database for later reference.

We rely on the assumption that `deploy()` will do the db init or
lookup before running so the zkas db handle is always index zero.
This commit is contained in:
parazyd
2023-03-05 01:46:34 +01:00
parent fd280c6513
commit 6fda2f9142

View File

@@ -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<Env>, ptr: WasmPtr<u8>, 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<Env>, ptr: WasmPtr<u8>, 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<u8> = 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<u8>, Vec<u8>) =
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
}