Skip to content

Commit

Permalink
piecrust: change ObjectCode into Module
Browse files Browse the repository at this point in the history
A `Module` now contains an actual `dusk_wasmtime::Module`, as opposed to
just some bytes, since it allows us to check for some invariants.
  • Loading branch information
Eduardo Leegwater Simões committed Nov 1, 2023
1 parent 78caaca commit 71560df
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 79 deletions.
6 changes: 3 additions & 3 deletions piecrust/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::call_tree::{CallTree, CallTreeElem};
use crate::contract::{ContractData, ContractMetadata, WrappedContract};
use crate::error::Error::{self, InitalizationError, PersistenceError};
use crate::instance::WrappedInstance;
use crate::store::{ContractSession, Objectcode, PAGE_SIZE};
use crate::store::{ContractSession, PAGE_SIZE};
use crate::types::StandardBufSerializer;
use crate::vm::HostQueries;

Expand Down Expand Up @@ -275,7 +275,7 @@ impl Session {
}

let wrapped_contract =
WrappedContract::new(&self.engine, bytecode, None::<Objectcode>)?;
WrappedContract::new(&self.engine, bytecode, None::<&[u8]>)?;
let contract_metadata = ContractMetadata { contract_id, owner };
let metadata_bytes = Self::serialize_data(&contract_metadata)?;

Expand Down Expand Up @@ -531,7 +531,7 @@ impl Session {
let contract = WrappedContract::new(
&self.engine,
store_data.bytecode,
Some(store_data.objectcode),
Some(store_data.module.serialize()),
)?;

self.inner.current = contract_id;
Expand Down
45 changes: 28 additions & 17 deletions piecrust/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@
mod bytecode;
mod memory;
mod metadata;
mod objectcode;
mod module;
mod session;
mod tree;

use std::collections::btree_map::Entry::*;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{Debug, Formatter};
use std::path::{Path, PathBuf};
use std::sync::mpsc;
use std::{fs, io, thread};

pub use bytecode::Bytecode;
use dusk_wasmtime::{Engine, Module};
use dusk_wasmtime::Engine;
pub use memory::{Memory, MAX_MEM_SIZE, PAGE_SIZE};
pub use metadata::Metadata;
pub use objectcode::Objectcode;
pub use module::Module;
use piecrust_uplink::ContractId;
use session::ContractDataEntry;
pub use session::ContractSession;
Expand All @@ -36,13 +37,24 @@ const OBJECTCODE_EXTENSION: &str = "a";
const METADATA_EXTENSION: &str = "m";

/// A store for all contract commits.
#[derive(Debug)]
pub struct ContractStore {
sync_loop: thread::JoinHandle<()>,
engine: Engine,

call: mpsc::Sender<Call>,
root_dir: PathBuf,
}

impl Debug for ContractStore {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ContractStore")
.field("sync_loop", &self.sync_loop)
.field("call", &self.call)
.field("root_dir", &self.root_dir)
.finish()
}
}

impl ContractStore {
/// Loads a new contract store from the given `dir`ectory.
///
Expand All @@ -53,13 +65,13 @@ impl ContractStore {
/// [`commit`]: ContractSession::commit
/// [`delete`]: ContractStore::delete_commit
/// [`session spawning`]: ContractStore::session
pub fn new<P: AsRef<Path>>(engine: &Engine, dir: P) -> io::Result<Self> {
pub fn new<P: AsRef<Path>>(engine: Engine, dir: P) -> io::Result<Self> {
let root_dir = dir.as_ref();

fs::create_dir_all(root_dir)?;

let (call, calls) = mpsc::channel();
let commits = read_all_commits(engine, root_dir)?;
let commits = read_all_commits(&engine, root_dir)?;

let loop_root_dir = root_dir.to_path_buf();

Expand All @@ -71,6 +83,7 @@ impl ContractStore {

Ok(Self {
sync_loop,
engine,
call,
root_dir: root_dir.into(),
})
Expand Down Expand Up @@ -144,7 +157,12 @@ impl ContractStore {
}

fn session_with_base(&self, base: Option<Commit>) -> ContractSession {
ContractSession::new(&self.root_dir, base, self.call.clone())
ContractSession::new(
&self.root_dir,
self.engine.clone(),
base,
self.call.clone(),
)
}
}

Expand Down Expand Up @@ -217,20 +235,13 @@ fn commit_from_dir<P: AsRef<Path>>(

// SAFETY it is safe to deserialize the file here, since we don't use
// the module here. We just want to check if the file is valid.
if unsafe {
Module::deserialize_file(engine, &objectcode_path).is_err()
} {
if Module::from_file(engine, &objectcode_path).is_err() {
let bytecode = Bytecode::from_file(bytecode_path)?;
let module =
Module::new(engine, bytecode.as_ref()).map_err(|err| {
io::Error::new(io::ErrorKind::InvalidData, err)
})?;
fs::write(
objectcode_path,
module.serialize().map_err(|err| {
io::Error::new(io::ErrorKind::InvalidData, err)
})?,
)?;
fs::write(objectcode_path, module.serialize())?;
}

let memory_dir = memory_dir.join(&contract_hex);
Expand Down Expand Up @@ -537,7 +548,7 @@ fn write_commit_inner<P: AsRef<Path>>(
}
} else {
fs::write(bytecode_path, &contract_data.bytecode)?;
fs::write(objectcode_path, &contract_data.objectcode)?;
fs::write(objectcode_path, &contract_data.module.serialize())?;
fs::write(metadata_path, &contract_data.metadata)?;
}
}
Expand Down
97 changes: 97 additions & 0 deletions piecrust/src/store/module.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use std::io;
use std::ops::Deref;
use std::path::Path;

use dusk_wasmtime::Engine;

/// WASM object code belonging to a given contract.
#[derive(Debug, Clone)]
pub struct Module {
module: dusk_wasmtime::Module,
}

fn check_single_memory(module: &dusk_wasmtime::Module) -> io::Result<()> {
// Ensure the module only has one memory
let n_memories = module
.exports()
.filter_map(|exp| exp.ty().memory().map(|_| ()))
.count();
if n_memories != 1 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"module has {} memories, but only one is allowed",
n_memories
),
));
}
Ok(())
}

impl Module {
pub(crate) fn new<B: AsRef<[u8]>>(
engine: &Engine,
bytes: B,
) -> io::Result<Self> {
let module = unsafe {
dusk_wasmtime::Module::deserialize(engine, bytes).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("failed to deserialize module: {}", e),
)
})?
};

check_single_memory(&module)?;

Ok(Self { module })
}

pub(crate) fn from_file<P: AsRef<Path>>(
engine: &Engine,
path: P,
) -> io::Result<Self> {
let module = unsafe {
dusk_wasmtime::Module::deserialize_file(engine, path).map_err(
|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("failed to deserialize module: {}", e),
)
},
)?
};

check_single_memory(&module)?;

Ok(Self { module })
}

pub(crate) fn serialize(&self) -> Vec<u8> {
self.module
.serialize()
.expect("We don't use WASM components")
}

pub(crate) fn is_64(&self) -> bool {
self.module
.exports()
.filter_map(|exp| exp.ty().memory().map(|mem_ty| mem_ty.is_64()))
.next()
.expect("We guarantee the module has one memory")
}
}

impl Deref for Module {
type Target = dusk_wasmtime::Module;

fn deref(&self) -> &Self::Target {
&self.module
}
}
47 changes: 0 additions & 47 deletions piecrust/src/store/objectcode.rs

This file was deleted.

Loading

0 comments on commit 71560df

Please sign in to comment.