runtime: sled blockchain access

This commit is contained in:
parazyd
2022-11-04 14:41:20 +01:00
parent dbdbc42705
commit 9848a1e5f5
5 changed files with 72 additions and 38 deletions

View File

@@ -2601,6 +2601,7 @@ dependencies = [
"darkfi-serial",
"getrandom",
"simplelog",
"sled",
]
[[package]]

View File

@@ -27,4 +27,4 @@ getrandom = { version = "0.2", features = ["custom"] }
[dev-dependencies]
darkfi = { path = "../../", features = ["wasm-runtime"] }
simplelog = "0.12.0"
sled = "0.34.7"

View File

@@ -16,11 +16,14 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use darkfi::{crypto::contract_id::ContractId, runtime::vm_runtime::Runtime, Result};
use darkfi_sdk::{
pasta::pallas,
tx::FuncCall
use darkfi::{
blockchain::Blockchain,
consensus::{TESTNET_GENESIS_HASH_BYTES, TESTNET_GENESIS_TIMESTAMP},
crypto::contract_id::ContractId,
runtime::vm_runtime::Runtime,
Result,
};
use darkfi_sdk::{pasta::pallas, tx::FuncCall};
use darkfi_serial::{serialize, Encodable, WriteExt};
use smart_contract::{FooCallData, Function};
@@ -36,20 +39,20 @@ fn run_contract() -> Result<()> {
simplelog::TerminalMode::Mixed,
simplelog::ColorChoice::Auto,
)?;
// =============================================================
// Build a ledger state so the runtime has something to work on
// =============================================================
//let state_machine = State::dummy()?;
// Add a nullifier to the nullifier set. (This is checked by the contract)
//state_machine.nullifiers.insert(&[Nullifier::from(pallas::Base::from(0x10))])?;
// =============================
// Initialize a dummy blockchain
// =============================
// TODO: This blockchain interface should perhaps be ValidatorState and Mutex/RwLock.
let db = sled::Config::new().temporary(true).open()?;
let blockchain = Blockchain::new(&db, *TESTNET_GENESIS_TIMESTAMP, *TESTNET_GENESIS_HASH_BYTES)?;
// ================================================================
// Load the wasm binary into memory and create an execution runtime
// ================================================================
let wasm_bytes = std::fs::read("contract.wasm")?;
let contract_id = ContractId::new(pallas::Base::from(1));
let mut runtime = Runtime::new(&wasm_bytes, contract_id)?;
let mut runtime = Runtime::new(&wasm_bytes, blockchain, contract_id)?;
// Deploy function to initialize the smart contract state.
// Here we pass an empty payload, but it's possible to feed in arbitrary data.
@@ -59,9 +62,9 @@ fn run_contract() -> Result<()> {
// Build some kind of payload to show an example
// =============================================
let func_calls = vec![FuncCall {
contract_id: pallas::Base::from(110),
func_id: pallas::Base::from(4),
call_data: serialize(&FooCallData { a: 777, b: 666 }),
contract_id: pallas::Base::from(110),
func_id: pallas::Base::from(4),
call_data: serialize(&FooCallData { a: 777, b: 666 }),
}];
let func_call_index: u32 = 0;

View File

@@ -19,7 +19,22 @@
use log::error;
use wasmer::{FunctionEnvMut, WasmPtr};
use crate::runtime::vm_runtime::{ContractSection, Env};
use crate::{
crypto::contract_id::ContractId,
runtime::vm_runtime::{ContractSection, Env},
};
/// Internal wasm runtime API for sled trees
pub struct DbHandle {
contract_id: ContractId,
tree: sled::Tree,
}
impl DbHandle {
pub fn new(contract_id: ContractId, tree: sled::Tree) -> Self {
Self { contract_id, tree }
}
}
/// Only deploy() can call this. Creates a new database instance for this contract.
///
@@ -31,23 +46,32 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32)
let env = ctx.data();
match env.contract_section {
ContractSection::Deploy => {
let env = ctx.data();
let memory_view = env.memory_view(&ctx);
let db = &env.blockchain.sled_db;
let contracts = &env.blockchain.contracts;
let contract_id = &env.contract_id;
match ptr.read_utf8_string(&memory_view, len) {
Ok(db_name) => {
// TODO:
// * db_name = blake3_hash(contract_id, db_name)
// * create db_name sled database
}
Err(_) => {
error!(target: "wasm_runtime::drk_log", "Failed to read UTF-8 string from VM memory");
let Ok(db_name) = ptr.read_utf8_string(&memory_view, len) else {
error!(target: "wasm_runtime::db_init", "Failed to read string from VM memory");
return -2
};
let tree_handle = match contracts.init(db, contract_id, &db_name) {
Ok(v) => v,
Err(e) => {
error!(target: "wasm_runtime::db_init", "Failed to init db: {}", e);
return -2
}
}
0
};
let mut db_handles = env.db_handles.borrow_mut();
db_handles.push(DbHandle::new(*contract_id, tree_handle));
return (db_handles.len() - 1) as i32
}
_ => {
error!(target: "wasm_runtime::db_init", "db_init called in unauthorized section");
return -1
}
_ => -1,
}
}

View File

@@ -33,12 +33,8 @@ use wasmer_middlewares::{
Metering,
};
use super::{
import,
//chain_state::{is_valid_merkle, nullifier_exists, set_update},
memory::MemoryManipulation,
};
use crate::{crypto::contract_id::ContractId, Error, Result};
use super::{import, import::db::DbHandle, memory::MemoryManipulation};
use crate::{blockchain::Blockchain, crypto::contract_id::ContractId, Error, Result};
/// Name of the wasm linear memory in our guest module
const MEMORY: &str = "memory";
@@ -60,10 +56,16 @@ pub enum ContractSection {
/// The wasm vm runtime instantiated for every smart contract that runs.
pub struct Env {
/// Blockchain access
pub blockchain: Blockchain,
/// sled tree handles used with `db_*`
pub db_handles: RefCell<Vec<DbHandle>>,
/// The contract ID being executed
pub contract_id: ContractId,
/// The contract section being executed
pub contract_section: ContractSection,
/// State update produced by a smart contract function call
pub contract_update: Cell<Option<(u8, Vec<u8>)>>,
//pub func_id:
/// Logs produced by the contract
pub logs: RefCell<Vec<String>>,
/// Direct memory access to the VM
@@ -96,7 +98,7 @@ pub struct Runtime {
impl Runtime {
/// Create a new wasm runtime instance that contains the given wasm module.
pub fn new(wasm_bytes: &[u8], contract_id: ContractId) -> Result<Self> {
pub fn new(wasm_bytes: &[u8], blockchain: Blockchain, contract_id: ContractId) -> Result<Self> {
info!(target: "warm_runtime::new", "Instantiating a new runtime");
// This function will be called for each `Operator` encountered during
// the wasm module execution. It should return the cost of the operator
@@ -124,13 +126,17 @@ impl Runtime {
debug!(target: "wasm_runtime::new", "Compiling module");
let module = Module::new(&store, wasm_bytes)?;
// This section will need changing
debug!(target: "wasm_runtime::new", "Importing functions");
// Initialize data
let db_handles = RefCell::new(vec![]);
let logs = RefCell::new(vec![]);
debug!(target: "wasm_runtime::new", "Importing functions");
let ctx = FunctionEnv::new(
&mut store,
Env {
blockchain,
db_handles,
contract_id,
contract_section: ContractSection::Null,
contract_update: Cell::new(None),