runtime: Pass ContractId to section functions.

This commit is contained in:
parazyd
2022-11-05 10:34:44 +01:00
parent d821a3854a
commit ff952172fa
12 changed files with 134 additions and 114 deletions

View File

@@ -1,10 +1,11 @@
use darkfi_sdk::{
crypto::ContractId,
db::{db_begin_tx, db_end_tx, db_get, db_init, db_lookup, db_set},
define_contract,
error::ContractResult,
msg,
state::set_update,
tx::FuncCall,
define_contract,
};
use darkfi_serial::{deserialize, serialize, SerialDecodable, SerialEncodable};
@@ -44,15 +45,11 @@ pub struct FooUpdate {
pub age: u32,
}
define_contract!(
init: init_contract,
exec: process_instruction,
apply: process_update
);
define_contract!(init: init_contract, exec: process_instruction, apply: process_update);
fn init_contract(_ix: &[u8]) -> ContractResult {
fn init_contract(cid: ContractId, _ix: &[u8]) -> ContractResult {
msg!("wakeup wagies!");
db_init("wagies")?;
db_init(cid, "wagies")?;
// Lets write a value in there
let tx_handle = db_begin_tx()?;
@@ -68,7 +65,7 @@ fn init_contract(_ix: &[u8]) -> ContractResult {
// This is the main entrypoint function where the payload is fed.
// Through here, you can branch out into different functions inside
// this library.
fn process_instruction(ix: &[u8]) -> ContractResult {
fn process_instruction(cid: ContractId, ix: &[u8]) -> ContractResult {
match Function::from(ix[0]) {
Function::Foo => {
let tx_data = &ix[1..];
@@ -127,7 +124,7 @@ fn process_instruction(ix: &[u8]) -> ContractResult {
Ok(())
}
fn process_update(update_data: &[u8]) -> ContractResult {
fn process_update(cid: ContractId, update_data: &[u8]) -> ContractResult {
msg!("Make update!");
match Function::from(update_data[0]) {

View File

@@ -19,11 +19,10 @@
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_sdk::{crypto::ContractId, pasta::pallas, tx::FuncCall};
use darkfi_serial::{serialize, Encodable, WriteExt};
use smart_contract::{FooCallData, Function};

View File

@@ -16,10 +16,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use darkfi_sdk::crypto::ContractId;
use darkfi_serial::deserialize;
use crate::{
crypto::contract_id::ContractId,
Error::{ContractAlreadyInitialized, ContractNotFound, ContractStateNotFound},
Result,
};

View File

@@ -34,9 +34,6 @@ pub use nfstore::NullifierStore;
pub mod rootstore;
pub use rootstore::RootStore;
pub mod statestore;
pub use statestore::StateStore;
pub mod txstore;
pub use txstore::TxStore;

View File

@@ -1,63 +0,0 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2022 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use darkfi_serial::serialize;
use crate::{crypto::contract_id::ContractId, Result};
const SLED_STATES_TREE: &[u8] = b"_states";
/// The `StateStore` is a `sled` tree storing states of deployed contracts.
/// The states themselves are data that is allocated and stored as raw bytes.
/// These bytes are (de)serialized by the code in wasm and the contracts can
/// operate on the state data themselves. Regarding on the (byte) size of the
/// state, the contract deployer should allocate and pay for a certain size of
/// their state stored by all the nodes. The cost should be linear to the byte
/// size used.
#[derive(Clone)]
pub struct StateStore(sled::Tree);
impl StateStore {
/// Opens a new or existing `StateStore` on the given sled database.
pub fn new(db: &sled::Db) -> Result<Self> {
let tree = db.open_tree(SLED_STATES_TREE)?;
Ok(Self(tree))
}
/// Insert a state into the store. This will replace the previous state.
/// The contract's ID is used as a key, while the value is the contract
/// state serialized to bytes.
pub fn insert(&self, contract_id: &ContractId, contract_state: &[u8]) -> Result<()> {
self.0.insert(serialize(contract_id), contract_state.to_vec())?;
Ok(())
}
/// Check if the `StateStore` contains a state for the given `ContractId`.
pub fn contains(&self, contract_id: &ContractId) -> Result<bool> {
Ok(self.0.contains_key(serialize(contract_id))?)
}
/// Retrieve a state from the `StateStore` given a `ContractId` if it exists.
pub fn get(&self, contract_id: &ContractId) -> Result<Option<Vec<u8>>> {
if let Some(data) = self.0.get(serialize(contract_id))? {
return Ok(Some(data.to_vec()))
}
Ok(None)
}
}

View File

@@ -30,9 +30,6 @@ pub mod token_list;
pub mod types;
pub mod util;
/// Contract ID definitions
pub mod contract_id;
/// VDF (Verifiable Delay Function) using MiMC
pub mod mimc_vdf;

View File

@@ -16,13 +16,11 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use darkfi_sdk::crypto::ContractId;
use log::error;
use wasmer::{FunctionEnvMut, WasmPtr};
use crate::{
crypto::contract_id::ContractId,
runtime::vm_runtime::{ContractSection, Env},
};
use crate::runtime::vm_runtime::{ContractSection, Env};
/// Internal wasm runtime API for sled trees
pub struct DbHandle {
@@ -51,6 +49,26 @@ pub(crate) fn db_init(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32)
let contracts = &env.blockchain.contracts;
let contract_id = &env.contract_id;
/*
let Ok(cid_slice) = cid_ptr.slice(&memory_view, 32) else {
error!(target: "wasm_runtime::db_init", "Failed to read contract id from ptr");
return -2
};
let Ok(cid_bytes) = cid_slice.read_to_vec() else {
error!(target: "wasm_runtime::db_init", "Failed to read slice to vec in db_init");
return -2
};
// FIXME: Could panic
let cid = ContractId::from_bytes(cid_bytes.try_into().unwrap());
if &cid != contract_id {
error!(target: "wasm_runtime::db_init", "Unauthorized ContractId for db_init");
return -1
}
*/
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
@@ -98,7 +116,6 @@ pub(crate) fn db_lookup(mut ctx: FunctionEnvMut<Env>, ptr: WasmPtr<u8>, len: u32
return -2
}
}
0
}
_ => -1,
}

View File

@@ -21,7 +21,7 @@ use std::{
sync::Arc,
};
use darkfi_sdk::entrypoint;
use darkfi_sdk::{crypto::ContractId, entrypoint};
use log::{debug, info};
use wasmer::{
imports, wasmparser::Operator, AsStoreRef, CompilerConfig, Function, FunctionEnv, Instance,
@@ -34,7 +34,7 @@ use wasmer_middlewares::{
};
use super::{import, import::db::DbHandle, memory::MemoryManipulation};
use crate::{blockchain::Blockchain, crypto::contract_id::ContractId, Error, Result};
use crate::{blockchain::Blockchain, Error, Result};
/// Name of the wasm linear memory in our guest module
const MEMORY: &str = "memory";
@@ -231,7 +231,7 @@ impl Runtime {
env_mut.contract_section = ContractSection::Deploy;
// Serialize the payload for the format the wasm runtime is expecting.
let payload = Self::serialize_payload(payload);
let payload = Self::serialize_payload(&env_mut.contract_id, payload);
// Allocate enough memory for the payload and copy it into the memory.
let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
@@ -281,7 +281,7 @@ impl Runtime {
env_mut.contract_section = ContractSection::Exec;
// Serialize the payload for the format the wasm runtime is expecting.
let payload = Self::serialize_payload(payload);
let payload = Self::serialize_payload(&env_mut.contract_id, payload);
// Allocate enough memory for the payload and copy it into the memory.
let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
@@ -332,11 +332,12 @@ impl Runtime {
// Take the update data from env, and serialize it for the format the wasm
// runtime is expecting.
// FIXME: Can panic
let update_data = env_mut.contract_update.take().unwrap();
let mut payload = Vec::with_capacity(1 + update_data.1.len());
payload.extend_from_slice(&[update_data.0]);
payload.extend_from_slice(&update_data.1);
let payload = Self::serialize_payload(&payload);
let payload = Self::serialize_payload(&env_mut.contract_id, &payload);
// Allocate enough memory for the payload and copy it into the memory.
let pages_required = payload.len() / WASM_PAGE_SIZE + 1;
@@ -378,7 +379,7 @@ impl Runtime {
fn print_logs(&self) {
let logs = self.ctx.as_ref(&self.store).logs.borrow();
for msg in logs.iter() {
debug!(target: "wasm_runtime::run", "Contract log: {}", msg);
debug!(target: "wasm_runtime::print_logs", "Contract log: {}", msg);
}
}
@@ -425,9 +426,11 @@ impl Runtime {
/// Serialize contract payload to the format accepted by the runtime functions.
/// We keep the same payload as a slice of bytes, and prepend it with a
/// little-endian u64 to tell the payload's length.
fn serialize_payload(payload: &[u8]) -> Vec<u8> {
fn serialize_payload(cid: &ContractId, payload: &[u8]) -> Vec<u8> {
let ser_cid = cid.to_bytes();
let payload_len = payload.len();
let mut out = Vec::with_capacity(8 + payload_len);
let mut out = Vec::with_capacity(ser_cid.len() + 8 + payload_len);
out.extend_from_slice(&ser_cid);
out.extend_from_slice(&(payload_len as u64).to_le_bytes());
out.extend_from_slice(payload);
out

View File

@@ -0,0 +1,49 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2022 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use pasta_curves::{group::ff::PrimeField, pallas};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ContractId(pallas::Base);
impl ContractId {
pub fn new(contract_id: pallas::Base) -> Self {
Self(contract_id)
}
pub fn inner(&self) -> pallas::Base {
self.0
}
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_repr()
}
pub fn from_bytes(x: [u8; 32]) -> Self {
// FIXME: Handle Option
Self(pallas::Base::from_repr(x).unwrap())
}
}
impl std::fmt::Display for ContractId {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
// base58 encoding
let contractid: String = bs58::encode(self.0.to_repr()).into_string();
write!(f, "{}", contractid)
}
}

View File

@@ -27,6 +27,13 @@
//! If you feel like trying, please help out with this migration, but do
//! it properly, with care, and write documentation while you're at it.
/// Cryptographic constants
pub mod constants;
/// Contract ID definitions and methods
pub mod contract_id;
pub use contract_id::ContractId;
/// Merkle node definitions
pub mod merkle_node;
pub use merkle_node::MerkleNode;
@@ -37,6 +44,3 @@ pub use nullifier::Nullifier;
/// Pedersen commitment utilities
pub mod pedersen;
/// Cryptographic constants
pub mod constants;

View File

@@ -1,4 +1,7 @@
use super::error::{ContractError, GenericResult};
use super::{
crypto::ContractId,
error::{ContractError, GenericResult},
};
type DbHandle = u32;
type TxHandle = u32;
@@ -9,19 +12,30 @@ type TxHandle = u32;
/// type DbHandle = u32;
/// db_init(db_name) -> DbHandle
/// ```
pub fn db_init(db_name: &str) -> GenericResult<()> {
pub fn db_init(contract_id: ContractId, db_name: &str) -> GenericResult<DbHandle> {
#[cfg(target_arch = "wasm32")]
unsafe {
return match db_init_(db_name.as_ptr(), db_name.len() as u32) {
0 => Ok(()),
-1 => Err(ContractError::CallerAccessDenied),
-2 => Err(ContractError::DbInitFailed),
_ => unreachable!(),
println!("sdk/src/db.rs:db_init() BEGIN");
let ret = db_init_(
//contract_id.to_bytes().as_ptr(),
//32_u32,
db_name.as_ptr(),
db_name.len() as u32,
);
if ret < 0 {
match ret {
-1 => return Err(ContractError::CallerAccessDenied),
-2 => return Err(ContractError::DbInitFailed),
_ => unimplemented!(),
}
}
return Ok(ret as u32)
}
#[cfg(not(target_arch = "wasm32"))]
todo!("{}", db_name);
unimplemented!()
}
pub fn db_lookup(db_name: &str) -> GenericResult<DbHandle> {

View File

@@ -18,6 +18,8 @@
use std::{mem::size_of, slice::from_raw_parts};
use crate::crypto::ContractId;
/// Success exit code for a contract
pub const SUCCESS: u64 = 0;
@@ -27,27 +29,27 @@ macro_rules! define_contract {
/// # Safety
#[no_mangle]
pub unsafe extern "C" fn __initialize(input: *mut u8) -> u64 {
let instruction_data = $crate::entrypoint::deserialize(input);
let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
match $init_func(&instruction_data) {
match $init_func(contract_id, &instruction_data) {
Ok(()) => $crate::entrypoint::SUCCESS,
Err(e) => e.into(),
}
}
#[no_mangle]
pub unsafe extern "C" fn __entrypoint(input: *mut u8) -> u64 {
let instruction_data = $crate::entrypoint::deserialize(input);
let (contract_id, instruction_data) = $crate::entrypoint::deserialize(input);
match $exec_func(&instruction_data) {
match $exec_func(contract_id, &instruction_data) {
Ok(()) => $crate::entrypoint::SUCCESS,
Err(e) => e.into(),
}
}
#[no_mangle]
pub unsafe extern "C" fn __update(input: *mut u8) -> u64 {
let update_data = $crate::entrypoint::deserialize(input);
let (contract_id, update_data) = $crate::entrypoint::deserialize(input);
match $apply_func(&update_data) {
match $apply_func(contract_id, &update_data) {
Ok(()) => $crate::entrypoint::SUCCESS,
Err(e) => e.into(),
}
@@ -57,12 +59,16 @@ macro_rules! define_contract {
/// Deserialize a given payload in `entrypoint`
/// # Safety
pub unsafe fn deserialize<'a>(input: *mut u8) -> &'a [u8] {
pub unsafe fn deserialize<'a>(input: *mut u8) -> (ContractId, &'a [u8]) {
let mut offset: usize = 0;
let contract_id_len = 32;
let contract_id_slice = { from_raw_parts(input.add(offset), contract_id_len) };
offset += contract_id_len;
let instruction_data_len = *(input.add(offset) as *const u64) as usize;
offset += size_of::<u64>();
let instruction_data = { from_raw_parts(input.add(offset), instruction_data_len) };
instruction_data
(ContractId::from_bytes(contract_id_slice.try_into().unwrap()), instruction_data)
}