mod barretenberg_structures;
mod pedersen;
mod schnorr;
use barretenberg_structures::Assignments;
pub(crate) use pedersen::Pedersen;
pub(crate) use schnorr::SchnorrSig;
const FIELD_BYTES: usize = 32;
#[derive(Debug, thiserror::Error)]
pub(crate) enum Error {
#[error(transparent)]
FromFeature(#[from] FeatureError),
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum FeatureError {
#[error("Trying to call {name} resulted in an error")]
FunctionCallFailed { name: String, source: wasmer::RuntimeError },
#[error("Could not find function export named {name}")]
InvalidExport { name: String, source: wasmer::ExportError },
#[error("No value available when value was expected")]
NoValue,
#[error("Value expected to be i32")]
InvalidI32,
#[error("Could not convert value {value} from i32 to u32")]
InvalidU32 { value: i32, source: std::num::TryFromIntError },
#[error("Could not convert value {value} from i32 to usize")]
InvalidUsize { value: i32, source: std::num::TryFromIntError },
#[error("Value expected to be 0 or 1 representing a boolean")]
InvalidBool,
}
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub(crate) struct BackendError(#[from] Error);
impl From<FeatureError> for BackendError {
fn from(value: FeatureError) -> Self {
BackendError(Error::FromFeature(value))
}
}
#[derive(Debug)]
pub(crate) struct Barretenberg {
store: std::cell::RefCell<wasmer::Store>,
memory: wasmer::Memory,
instance: wasmer::Instance,
}
use std::cell::RefCell;
use wasmer::{
imports, Function, FunctionEnv, FunctionEnvMut, Imports, Instance, Memory, MemoryType, Store,
Value, WasmPtr,
};
pub(super) const WASM_SCRATCH_BYTES: usize = 1024;
const WASM_BIN: &[u8] = include_bytes!("./acvm_backend.wasm");
impl Barretenberg {
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn new() -> Barretenberg {
let (instance, memory, store) = instance_load();
let barretenberg = Barretenberg { memory, instance, store: RefCell::new(store) };
barretenberg.call_wasi_initialize();
barretenberg
}
#[cfg(target_arch = "wasm32")]
pub(crate) async fn initialize() -> Barretenberg {
let (instance, memory, store) = instance_load().await;
let barretenberg = Barretenberg { memory, instance, store: RefCell::new(store) };
barretenberg.call_wasi_initialize();
barretenberg
}
fn call_wasi_initialize(&self) {
self.call_multiple("_initialize", vec![])
.expect("expected call to WASI initialization function to not fail");
}
}
#[derive(Debug, Clone)]
pub(crate) struct WASMValue(Option<Value>);
impl From<usize> for WASMValue {
fn from(value: usize) -> Self {
WASMValue(Some(Value::I32(value as i32)))
}
}
impl From<u32> for WASMValue {
fn from(value: u32) -> Self {
WASMValue(Some(Value::I32(value as i32)))
}
}
impl From<i32> for WASMValue {
fn from(value: i32) -> Self {
WASMValue(Some(Value::I32(value)))
}
}
impl From<Value> for WASMValue {
fn from(value: Value) -> Self {
WASMValue(Some(value))
}
}
impl TryFrom<WASMValue> for bool {
type Error = FeatureError;
fn try_from(value: WASMValue) -> Result<Self, Self::Error> {
match value.try_into()? {
0 => Ok(false),
1 => Ok(true),
_ => Err(FeatureError::InvalidBool),
}
}
}
impl TryFrom<WASMValue> for usize {
type Error = FeatureError;
fn try_from(value: WASMValue) -> Result<Self, Self::Error> {
let value: i32 = value.try_into()?;
value.try_into().map_err(|source| FeatureError::InvalidUsize { value, source })
}
}
impl TryFrom<WASMValue> for u32 {
type Error = FeatureError;
fn try_from(value: WASMValue) -> Result<Self, Self::Error> {
let value = value.try_into()?;
u32::try_from(value).map_err(|source| FeatureError::InvalidU32 { value, source })
}
}
impl TryFrom<WASMValue> for i32 {
type Error = FeatureError;
fn try_from(value: WASMValue) -> Result<Self, Self::Error> {
value.0.map_or(Err(FeatureError::NoValue), |val| val.i32().ok_or(FeatureError::InvalidI32))
}
}
impl TryFrom<WASMValue> for Value {
type Error = FeatureError;
fn try_from(value: WASMValue) -> Result<Self, Self::Error> {
value.0.ok_or(FeatureError::NoValue)
}
}
impl Barretenberg {
pub(crate) fn transfer_to_heap(&self, data: &[u8], offset: usize) {
let memory = &self.memory;
let store = self.store.borrow();
let memory_view = memory.view(&store);
memory_view.write(offset as u64, data).unwrap();
}
pub(crate) fn read_memory<const SIZE: usize>(&self, start: usize) -> [u8; SIZE] {
self.read_memory_variable_length(start, SIZE)
.try_into()
.expect("Read memory should be of the specified length")
}
pub(crate) fn read_memory_variable_length(&self, offset: usize, length: usize) -> Vec<u8> {
let memory = &self.memory;
let store = &self.store.borrow();
let memory_view = memory.view(&store);
let mut buf = vec![0; length];
memory_view.read(offset as u64, &mut buf).unwrap();
buf
}
pub(crate) fn call(&self, name: &str, param: &WASMValue) -> Result<WASMValue, Error> {
self.call_multiple(name, vec![param])
}
pub(crate) fn call_multiple(
&self,
name: &str,
params: Vec<&WASMValue>,
) -> Result<WASMValue, Error> {
let mut args: Vec<Value> = vec![];
for param in params.into_iter().cloned() {
args.push(param.try_into()?);
}
let func = self
.instance
.exports
.get_function(name)
.map_err(|source| FeatureError::InvalidExport { name: name.to_string(), source })?;
let boxed_value = func.call(&mut self.store.borrow_mut(), &args).map_err(|source| {
FeatureError::FunctionCallFailed { name: name.to_string(), source }
})?;
let option_value = boxed_value.first().cloned();
Ok(WASMValue(option_value))
}
pub(crate) fn allocate(&self, bytes: &[u8]) -> Result<WASMValue, Error> {
let ptr: i32 = self.call("bbmalloc", &bytes.len().into())?.try_into()?;
let i32_bytes = ptr.to_be_bytes();
let u32_bytes = u32::from_be_bytes(i32_bytes);
self.transfer_to_heap(bytes, u32_bytes as usize);
Ok(ptr.into())
}
}
fn init_memory_and_state() -> (Memory, Store, Imports) {
let mut store = Store::default();
let mem_type = MemoryType::new(18, Some(65536), false);
let memory = Memory::new(&mut store, mem_type).unwrap();
let function_env = FunctionEnv::new(&mut store, memory.clone());
let custom_imports = imports! {
"env" => {
"logstr" => Function::new_typed_with_env(
&mut store,
&function_env,
logstr,
),
"memory" => memory.clone(),
},
"wasi_snapshot_preview1" => {
"proc_exit" => Function::new_typed(&mut store, proc_exit),
"random_get" => Function::new_typed_with_env(
&mut store,
&function_env,
random_get
),
},
};
(memory, store, custom_imports)
}
#[cfg(not(target_arch = "wasm32"))]
fn instance_load() -> (Instance, Memory, Store) {
use wasmer::Module;
let (memory, mut store, custom_imports) = init_memory_and_state();
let module = Module::new(&store, WASM_BIN).unwrap();
(Instance::new(&mut store, &module, &custom_imports).unwrap(), memory, store)
}
#[cfg(target_arch = "wasm32")]
async fn instance_load() -> (Instance, Memory, Store) {
use js_sys::WebAssembly::{self};
use wasmer::AsJs;
let (memory, mut store, custom_imports) = init_memory_and_state();
let js_bytes = unsafe { js_sys::Uint8Array::view(&WASM_BIN) };
let js_module_promise = WebAssembly::compile(&js_bytes);
let js_module: js_sys::WebAssembly::Module =
wasm_bindgen_futures::JsFuture::from(js_module_promise).await.unwrap().into();
let js_instance_promise =
WebAssembly::instantiate_module(&js_module, &custom_imports.as_jsvalue(&store).into());
let js_instance = wasm_bindgen_futures::JsFuture::from(js_instance_promise).await.unwrap();
let module = wasmer::Module::from((js_module, WASM_BIN));
let instance: wasmer::Instance = Instance::from_jsvalue(&mut store, &module, &js_instance)
.map_err(|_| "Error while creating BlackBox Functions vendor instance")
.unwrap();
(instance, memory, store)
}
fn logstr(mut env: FunctionEnvMut<Memory>, ptr: i32) {
let (memory, store) = env.data_and_store_mut();
let memory_view = memory.view(&store);
let log_str_wasm_ptr: WasmPtr<u8, wasmer::Memory32> = WasmPtr::new(ptr as u32);
match log_str_wasm_ptr.read_utf8_string_with_nul(&memory_view) {
Ok(log_string) => println!("{log_string}"),
Err(err) => println!("Error while reading log string from memory: {err}"),
};
}
fn random_get(mut env: FunctionEnvMut<Memory>, buf_ptr: i32, buf_len: i32) -> i32 {
let mut u8_buffer = vec![0; buf_len as usize];
let res = getrandom::getrandom(&mut u8_buffer);
match res {
Ok(()) => {
let (memory, store) = env.data_and_store_mut();
let memory_view = memory.view(&store);
match memory_view.write(buf_ptr as u64, u8_buffer.as_mut_slice()) {
Ok(_) => {
0_i32 }
Err(_) => {
29_i32 }
}
}
Err(_) => {
29_i32 }
}
}
fn proc_exit(_: i32) {
unimplemented!("proc_exit is not implemented")
}