Skip to content

Commit

Permalink
Named keys.
Browse files Browse the repository at this point in the history
Events in CEP-18
  • Loading branch information
kubaplas committed Apr 23, 2024
1 parent 351d018 commit 51203d0
Show file tree
Hide file tree
Showing 20 changed files with 832 additions and 165 deletions.
36 changes: 35 additions & 1 deletion core/src/contract_context.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use casper_types::CLValue;

use crate::{Address, OdraError};
use crate::call_def::CallDef;
use crate::casper_types::bytesrepr::Bytes;
use crate::casper_types::U512;
use crate::{Address, OdraError};

/// Trait representing the context of a smart contract.
#[cfg_attr(test, allow(unreachable_code))]
Expand All @@ -25,6 +27,38 @@ pub trait ContractContext {
/// * `key` - The key to set the value for.
/// * `value` - The value to be set.
fn set_value(&self, key: &[u8], value: Bytes);

/// Retrieves the value behind a named key.
///
/// # Arguments
///
/// * `name` - The name of the key.
fn get_named_value(&self, name: &str) -> Option<Bytes>;

/// Sets the value behind a named key.
///
/// # Arguments
///
/// * `name` - The name of the key.
/// * `value` - The value to set.
fn set_named_value(&self, name: &str, value: CLValue);

/// Retrieves the key value behind a named dictionary.
///
/// # Arguments
///
/// * `dictionary_name` - The name of the dictionary.
/// * `key` - The key to retrieve the value for.
fn get_dictionary_value(&self, dictionary_name: &str, key: &str) -> Option<Bytes>;

/// Sets the key value behind a named dictionary.
///
/// # Arguments
///
/// * `dictionary_name` - The name of the dictionary.
/// * `key` - The key to set the value for.
/// * `value` - The value to set.
fn set_dictionary_value(&self, dictionary_name: &str, key: &str, value: CLValue);

/// Retrieves the address of the caller.
fn caller(&self) -> Address;
Expand Down
91 changes: 61 additions & 30 deletions core/src/contract_env.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use casper_types::bytesrepr::deserialize_from_slice;
use casper_types::{BLAKE2B_DIGEST_LENGTH, CLValue};
use casper_types::crypto::PublicKey;

use crate::{consts, ExecutionError, prelude::*};
use crate::{UnwrapOrRevert, utils};
use crate::{Address, OdraError};
use crate::args::EntrypointArgument;
use crate::call_def::CallDef;
use crate::casper_types::bytesrepr::{Bytes, FromBytes, ToBytes};
use crate::casper_types::{CLTyped, U512};
use crate::casper_types::bytesrepr::{Bytes, FromBytes, ToBytes};
pub use crate::ContractContext;
use crate::{consts, prelude::*, ExecutionError};
use crate::{utils, UnwrapOrRevert};
use crate::{Address, OdraError};
use casper_types::crypto::PublicKey;
use crate::ExecutionError::Formatting;

const INDEX_SIZE: usize = 4;
const KEY_LEN: usize = 64;
Expand Down Expand Up @@ -49,7 +53,7 @@ impl ContractEnv {
let mut key = Vec::with_capacity(INDEX_SIZE + self.mapping_data.len());
key.extend_from_slice(self.index.to_be_bytes().as_ref());
key.extend_from_slice(&self.mapping_data);
let hashed_key = self.backend.borrow().hash(&key);
let hashed_key = self.backend.borrow().hash(key.as_slice());
utils::hex_to_slice(&hashed_key, &mut result);
result
}
Expand Down Expand Up @@ -77,7 +81,7 @@ impl ContractEnv {
self.backend
.borrow()
.get_value(key)
.map(|bytes| deserialize_bytes(bytes, self))
.map(|bytes| deserialize_from_slice(bytes).unwrap_or_revert(self))
}

/// Sets the value associated with the given key in the contract storage.
Expand All @@ -87,6 +91,50 @@ impl ContractEnv {
self.backend.borrow().set_value(key, bytes.into());
}

/// Retrieves the value associated with the given named key from the contract storage.
pub fn get_named_value<T: FromBytes + CLTyped, U: AsRef<str>>(&self, name: U) -> Option<T> {
let key = name.as_ref();
let bytes = self.backend.borrow().get_named_value(key);
bytes.map(|b| deserialize_from_slice(b).unwrap_or_revert(self))
}

/// Sets the value associated with the given named key in the contract storage.
pub fn set_named_value<T: CLTyped + ToBytes, U: AsRef<str>>(&self, name: U, value: T) {
let key = name.as_ref();
// todo: map errors to correct Odra errors
let cl_value = CLValue::from_t(value).unwrap_or_revert(self);
self.backend.borrow().set_named_value(key, cl_value);
}

/// Retrieves the value associated with the given named key from the named dictionary in the contract storage.
pub fn get_dictionary_value<T: FromBytes + CLTyped, U: AsRef<str>, V: AsRef<str>>(
&self,
dictionary_name: U,
key: V
) -> Option<T> {
let dictionary_name = dictionary_name.as_ref();
let key = key.as_ref();
let bytes = self.backend
.borrow()
.get_dictionary_value(dictionary_name, key);
bytes.map(|b| deserialize_from_slice(b).map_err(|_| Formatting).unwrap_or_revert(self))
}

/// Sets the value associated with the given named key in the named dictionary in the contract storage.
pub fn set_dictionary_value<T: CLTyped + ToBytes, U: AsRef<str>, V: AsRef<str>>(
&self,
dictionary_name: U,
key: V,
value: T
) {
let dictionary_name = dictionary_name.as_ref();
let key = key.as_ref();
let cl_value = CLValue::from_t(value).map_err(|_| Formatting).unwrap_or_revert(self);
self.backend
.borrow()
.set_dictionary_value(dictionary_name, key, cl_value);
}

/// Returns the address of the caller of the contract.
pub fn caller(&self) -> Address {
let backend = self.backend.borrow();
Expand All @@ -101,7 +149,7 @@ impl ContractEnv {
pub fn call_contract<T: FromBytes>(&self, address: Address, call: CallDef) -> T {
let backend = self.backend.borrow();
let bytes = backend.call_contract(address, call);
deserialize_bytes(bytes, self)
deserialize_from_slice(bytes).unwrap_or_revert(self)
}

/// Returns the address of the current contract.
Expand Down Expand Up @@ -175,18 +223,14 @@ impl ContractEnv {
/// # Returns
///
/// The hash value as a 32-byte array.
pub fn hash<T: ToBytes>(&self, value: T) -> [u8; 32] {
let bytes = value
.to_bytes()
.map_err(ExecutionError::from)
.unwrap_or_revert(self);
self.backend.borrow().hash(&bytes)
pub fn hash<T: AsRef<[u8]>>(&self, value: T) -> [u8; BLAKE2B_DIGEST_LENGTH] {
self.backend.borrow().hash(value.as_ref())
}
}

/// Represents the environment accessible in the contract execution context.
///
/// `ExecutionEnv` provides pre and post execution methods for the contract, such as performing non-reentrant checks
/// `ExecutionEnv` provides pre- and post-execution methods for the contract, such as performing non-reentrant checks
/// and handling the attached value.
pub struct ExecutionEnv {
env: Rc<ContractEnv>
Expand Down Expand Up @@ -240,24 +284,11 @@ impl ExecutionEnv {
pub fn get_named_arg<T: FromBytes + EntrypointArgument>(&self, name: &str) -> T {
if T::is_required() {
let bytes = self.env.backend.borrow().get_named_arg_bytes(name);
deserialize_bytes(bytes, &self.env)
deserialize_from_slice(bytes).unwrap_or_revert(&self.env)
} else {
let bytes = self.env.backend.borrow().get_opt_named_arg_bytes(name);
let result = bytes.map(|bytes| deserialize_bytes(bytes, &self.env));
let result = bytes.map(|bytes| deserialize_from_slice(bytes).unwrap_or_revert(&self.env));
T::unwrap(result, &self.env)
}
}
}

fn deserialize_bytes<T: FromBytes>(bytes: Bytes, env: &ContractEnv) -> T {
match T::from_bytes(&bytes) {
Ok((value, remainder)) => {
if remainder.is_empty() {
value
} else {
env.revert(ExecutionError::LeftOverBytes)
}
}
Err(err) => env.revert(ExecutionError::from(err))
}
}
55 changes: 51 additions & 4 deletions core/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use casper_types::CLType;
use core::any::Any;

use casper_types::{CLType, CLValueError};
use casper_types::bytesrepr::Error as BytesReprError;

use crate::arithmetic::ArithmeticsError;
use crate::prelude::*;
use core::any::Any;
use crate::ExecutionError::User;
use crate::VmError::Serialization;

/// General error type in Odra framework
#[repr(u16)]
Expand Down Expand Up @@ -144,7 +146,7 @@ impl ExecutionError {

impl From<ExecutionError> for OdraError {
fn from(error: ExecutionError) -> Self {
Self::ExecutionError(User(error.code()))
Self::ExecutionError(ExecutionError::User(error.code()))
}
}

Expand Down Expand Up @@ -241,3 +243,48 @@ pub enum EventError {

/// Represents the result of a contract call.
pub type OdraResult<T> = Result<T, OdraError>;


impl From<CLValueError> for OdraError {
fn from(error: CLValueError) -> Self {
match error {
CLValueError::Serialization(_) => {
OdraError::VmError(Serialization)
}
CLValueError::Type(cl_type_mismatch) => {
OdraError::VmError(VmError::TypeMismatch {
expected: cl_type_mismatch.expected.clone(),
found: cl_type_mismatch.found.clone()
})
}
}
}
}

impl From<BytesReprError> for OdraError {
fn from(error: BytesReprError) -> Self {
match error {
BytesReprError::EarlyEndOfStream => {
ExecutionError::EarlyEndOfStream.into()
}
BytesReprError::Formatting => {
ExecutionError::Formatting.into()
}
BytesReprError::LeftOverBytes => {
ExecutionError::LeftOverBytes.into()
}
BytesReprError::OutOfMemory => {
ExecutionError::OutOfMemory.into()
}
BytesReprError::NotRepresentable => {
ExecutionError::NotRepresentable.into()
}
BytesReprError::ExceededRecursionDepth => {
ExecutionError::ExceededRecursionDepth.into()
}
_ => {
ExecutionError::Formatting.into()
}
}
}
}
6 changes: 6 additions & 0 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ path = "bin/erc20_on_livenet.rs"
required-features = ["livenet"]
test = false

[[bin]]
name = "cep18_on_livenet"
path = "bin/cep18_on_livenet.rs"
required-features = ["livenet"]
test = false

[[bin]]
name = "livenet_tests"
path = "bin/livenet_tests.rs"
Expand Down
59 changes: 59 additions & 0 deletions examples/bin/cep18_on_livenet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//! Deploys a CEP-18 contract and transfers some tokens to another address.
use odra::casper_types::U256;
use odra::host::{Deployer, HostEnv, HostRef, HostRefLoader};
use odra::Address;
use odra_modules::cep18_token::{Cep18HostRef, Cep18InitArgs};
use std::str::FromStr;

fn main() {
let env = odra_casper_livenet_env::env();

let owner = env.caller();
let recipient = "hash-2c4a6ce0da5d175e9638ec0830e01dd6cf5f4b1fbb0724f7d2d9de12b1e0f840";
let recipient = Address::from_str(recipient).unwrap();

// Deploy new contract.
let mut token = deploy_cep18(&env);
println!("Token address: {}", token.address().to_string());

// Uncomment to load existing contract.
// let mut token = load_erc20(&env);

// println!("Token name: {}", token.name());

env.set_gas(3_000_000_000u64);
token.transfer(&recipient, &U256::from(1000));

token.approve(&recipient, &U256::from(3500));
//
// println!("Owner's balance: {:?}", token.balance_of(&owner));
// println!("Recipient's balance: {:?}", token.balance_of(&recipient));
}

/// Loads an ERC20 contract.
fn _load_cep18(env: &HostEnv) -> Cep18HostRef {
let address = "hash-d26fcbd2106e37be975d2045c580334a6d7b9d0a241c2358a4db970dfd516945";
let address = Address::from_str(address).unwrap();
Cep18HostRef::load(env, address)
}

/// Deploys an ERC20 contract.
pub fn deploy_cep18(env: &HostEnv) -> Cep18HostRef {
let name = String::from("Plascoin");
let symbol = String::from("PLS");
let decimals = 2u8;
let initial_supply = U256::from(10_000);

let init_args = Cep18InitArgs {
name,
symbol,
decimals,
initial_supply,
minter_list: vec![],
admin_list: vec![env.caller()],
modality: Some(1)
};

env.set_gas(200_000_000_000u64);
Cep18HostRef::deploy(env, init_args)
}
Loading

0 comments on commit 51203d0

Please sign in to comment.