mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-08 22:28:12 -05:00
runtime: Pass ContractId to section functions.
This commit is contained in:
@@ -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]) {
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
49
src/sdk/src/crypto/contract_id.rs
Normal file
49
src/sdk/src/crypto/contract_id.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user