diff --git a/Makefile b/Makefile index 2c485aa7..cc365062 100644 --- a/Makefile +++ b/Makefile @@ -21,3 +21,6 @@ lint: clippy clean: cargo clean rm -rf tests/wasm/*.wasm + +docs: + cargo doc --features test-support --no-deps --open \ No newline at end of file diff --git a/README.md b/README.md index bc1022c8..b187984a 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,29 @@ -# contracts +# MVPR DAO for Casper -Voting contract - data: - - voting_address - Address +Reusable smart contracts for building DAOs on top of Casper. -Reputation contract - data: - - reputation_address - Address - - owner - Address - - whitelist - Vec
+Repository contains following modules: +- `contract` provides smart contracts implementation, +- `utils` and `macros` makes writing code easier, +- `tests` contain integration tests, +- `client` implements a JavaScript client for smart contracts interactions. - methods: - only_onwer: - - change_onwership(new_owner: Address) - - add_to_whitelist(addr: Address) - - remove_from_whitelist(addr: Address) - - whitelist_only: - - mint - - burn - - transfer_from - - stake +## Build contracts +Build `WASM` files. -Variable Repository - data: - ... - - methods: - only_onwer: - - change_onwership(new_owner: Address) - - add_to_whitelist(addr: Address) - - remove_from_whitelist(addr: Address) - - whitelist_only: - - set_string(name: String, value: String) - - set_u256(name: String, value: U256) +```bash +$ make build-contracts +``` - all: - - get_string(name: String) -> String - - get_u256(name: String) -> U256 +## Test +Run integration tests. -Master Voting Contract: - - vote for whitelist changes. \ No newline at end of file +```bash +$ make test +``` + +## Docs +Generate `rustdoc`. Opens a new browser window. +```bash +$ make docs +``` diff --git a/contracts/Cargo.toml b/contracts/Cargo.toml index d1524a2b..148e62c1 100644 --- a/contracts/Cargo.toml +++ b/contracts/Cargo.toml @@ -14,6 +14,7 @@ path = "bin/reputation_contract.rs" bench = false doctest = false test = false +doc = false [profile.release] codegen-units = 1 diff --git a/contracts/src/reputation.rs b/contracts/src/reputation.rs index b34e7f46..a7c5ac37 100644 --- a/contracts/src/reputation.rs +++ b/contracts/src/reputation.rs @@ -17,18 +17,108 @@ use casper_types::{ EntryPointType, EntryPoints, Group, RuntimeArgs, URef, U256, }; +/// Interface of the Reputation Contract. +/// +/// It should be implemented by [`ReputationContract`], [`ReputationContractCaller`] +/// and [`ReputationContractTest`]. pub trait ReputationContractInterface { + /// Constructor method. + /// + /// It initializes contract elements: + /// * Events dictionary. + /// * Named keys of [`TokenWithStaking`], [`Owner`] and [`Whitelist`]. + /// * Set [`caller`] as the owner of the contract. + /// * Add [`caller`] to the whitelist. + /// + /// It emits [`OwnerChanged`](casper_dao_utils::owner::events::OwnerChanged), + /// [`AddedToWhitelist`](casper_dao_utils::whitelist::events::AddedToWhitelist) events. fn init(&mut self); + + /// Mint new tokens. Add `amount` of new tokens to the balance of the `recipient` and + /// increment the total supply. Only whitelisted addresses are permited to call this method. + /// + /// It throws [`NotWhitelisted`](casper_dao_utils::Error::NotWhitelisted) if caller + /// is not whitelisted. + /// + /// It emits [`Mint`](casper_dao_utils::token::events::Mint) event. fn mint(&mut self, recipient: Address, amount: U256); + + /// Burn existing tokens. Remove `amount` of existing tokens from the balance of the `owner` + /// and decrement the total supply. Only whitelisted addresses are permited to call this + /// method. + /// + /// It throws [`NotWhitelisted`](casper_dao_utils::Error::NotWhitelisted) if caller + /// is not whitelisted. + /// + /// It emits [`Burn`](casper_dao_utils::token::events::Burn) event. fn burn(&mut self, owner: Address, amount: U256); + + /// Transfer `amount` of tokens from `owner` to `recipient`. Only whitelisted addresses are + /// permited to call this method. + /// + /// It throws [`NotWhitelisted`](casper_dao_utils::Error::NotWhitelisted) if caller + /// is not whitelisted. + /// + /// It throws [`InsufficientBalance`](casper_dao_utils::Error::InsufficientBalance) + /// if `recipient`'s balance is less then `amount`. + /// + /// It emits [`Transfer`](casper_dao_utils::token::events::Transfer) event. fn transfer_from(&mut self, owner: Address, recipient: Address, amount: U256); + + /// Change ownership of the contract. Transfer the ownership to the `owner`. Only current owner + /// is permited to call this method. + /// + /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller + /// is not the current owner. + /// + /// It emits [`OwnerChanged`](casper_dao_utils::owner::events::OwnerChanged), + /// [`AddedToWhitelist`](casper_dao_utils::whitelist::events::AddedToWhitelist) events. fn change_ownership(&mut self, owner: Address); + + /// Add new address to the whitelist. + /// + /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller + /// is not the current owner. + /// + /// It emits [`AddedToWhitelist`](casper_dao_utils::whitelist::events::AddedToWhitelist) event. fn add_to_whitelist(&mut self, address: Address); + + /// Remove address from the whitelist. + /// + /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller + /// is not the current owner. + /// + /// It emits [`RemovedFromWhitelist`](casper_dao_utils::whitelist::events::RemovedFromWhitelist) + /// event. fn remove_from_whitelist(&mut self, address: Address); + + /// Stake `amount` of tokens for the `address`. It decrements `address`'s balance by `amount`. + /// + /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller + /// is not the current owner. + /// + /// It throws [`InsufficientBalance`](casper_dao_utils::Error::InsufficientBalance) + /// if `address`'s balance is less then `amount`. + /// + /// It emits [`TokensStaked`](casper_dao_utils::staking::events::TokensStaked) + /// event. fn stake(&mut self, address: Address, amount: U256); + + /// Unstake `amount` of tokens for the `address`. It increments `address`'s balance by + /// `amount`. + /// + /// It throws [`NotAnOwner`](casper_dao_utils::Error::NotAnOwner) if caller + /// is not the current owner. + /// + /// It throws [`InsufficientBalance`](casper_dao_utils::Error::InsufficientBalance) + /// if `address`'s staked amount is less then `amount`. + /// + /// It emits [`TokensUnstaked`](casper_dao_utils::staking::events::TokensUnstaked) + /// event. fn unstake(&mut self, address: Address, amount: U256); } +/// Implementation of the Reputation Contract. See [`ReputationContractInterface`]. #[derive(Default)] pub struct ReputationContract { pub token: TokenWithStaking, @@ -148,6 +238,7 @@ impl ReputationContract { } } +/// Implementation of the Reputation Contract Caller. See [`ReputationContractInterface`]. pub struct ReputationContractCaller { contract_package_hash: ContractPackageHash, } @@ -275,6 +366,7 @@ mod tests { use crate::{ReputationContract, ReputationContractInterface}; + /// Implementation of the Reputation Contract Test. See [`ReputationContractInterface`]. pub struct ReputationContractTest { env: TestEnv, package_hash: ContractPackageHash, diff --git a/macros/src/lib.rs b/macros/src/lib.rs index f3ce29c8..af36f200 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -3,6 +3,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::{quote, TokenStreamExt}; use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields}; +/// Derive events on top of any struct. #[proc_macro_derive(Event)] pub fn derive_events(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 0ae80320..b620ee9f 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -13,4 +13,4 @@ name = "reputation-tests" path = "src/reputation_tests.rs" bench = false doctest = false - +doc = false \ No newline at end of file diff --git a/utils/src/casper_env.rs b/utils/src/casper_env.rs index 5e17392e..dafed490 100644 --- a/utils/src/casper_env.rs +++ b/utils/src/casper_env.rs @@ -1,3 +1,5 @@ +//! Interact with the CasperVM inside the contract. + use std::convert::TryInto; use casper_contract::{ @@ -10,8 +12,9 @@ use casper_types::{ CLTyped, }; -use crate::{modules::events::Events, Address}; +use crate::{Address, Events}; +/// Read value from the storage. pub fn get_key(name: &str) -> Option { match runtime::get_key(name) { None => None, @@ -23,6 +26,7 @@ pub fn get_key(name: &str) -> Option { } } +/// Save value to the storage. pub fn set_key(name: &str, value: T) { match runtime::get_key(name) { Some(key) => { @@ -44,8 +48,8 @@ fn call_stack_element_to_address(call_stack_element: CallStackElement) -> Addres match call_stack_element { CallStackElement::Session { account_hash } => Address::from(account_hash), CallStackElement::StoredSession { account_hash, .. } => { - // Stored session code acts in account's context, so if stored session wants to interact - // with an ERC20 token caller's address will be used. + // Stored session code acts in account's context, so if stored session + // wants to interact, caller's address will be used. Address::from(account_hash) } CallStackElement::StoredContract { @@ -71,14 +75,17 @@ pub fn caller() -> Address { call_stack_element_to_address(second_elem) } +/// Initialize events dictionary. pub fn init_events() { Events::default().init(); } +/// Record event to the contract's storage. pub fn emit(event: T) { Events::default().emit(event); } +/// Convert any key to base64. pub fn to_dictionary_key(key: &T) -> String { let preimage = key.to_bytes().unwrap_or_revert(); base64::encode(&preimage) diff --git a/utils/src/lib.rs b/utils/src/lib.rs index bfa3eacc..926594ed 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -15,6 +15,7 @@ pub use modules::owner; pub use modules::staking; pub use modules::token; pub use modules::whitelist; +use modules::Events; #[cfg(feature = "test-support")] mod test_env; diff --git a/utils/src/modules/mod.rs b/utils/src/modules/mod.rs index 0a6fe545..03bfe229 100644 --- a/utils/src/modules/mod.rs +++ b/utils/src/modules/mod.rs @@ -1,5 +1,7 @@ -pub mod events; +mod events; pub mod owner; pub mod staking; pub mod token; pub mod whitelist; + +pub(crate) use events::Events; diff --git a/utils/src/modules/owner.rs b/utils/src/modules/owner.rs index 51072ab4..ff8bf687 100644 --- a/utils/src/modules/owner.rs +++ b/utils/src/modules/owner.rs @@ -1,3 +1,5 @@ +//! Single-owner-based access control system. + use casper_contract::contract_api::runtime; use crate::{ @@ -7,6 +9,7 @@ use crate::{ use self::events::OwnerChanged; +/// The Owner module. pub struct Owner { pub owner: Variable>, } @@ -20,15 +23,18 @@ impl Default for Owner { } impl Owner { + /// Initialize the module. pub fn init(&mut self, owner: Address) { self.change_ownership(owner); } + /// Set the owner to the new address. pub fn change_ownership(&mut self, owner: Address) { self.owner.set(Some(owner)); emit(OwnerChanged { new_owner: owner }); } + /// Verify if the contract caller is the owner. Revert otherwise. pub fn ensure_owner(&self) { if let Some(owner) = self.owner.get() { if owner != caller() { @@ -41,10 +47,11 @@ impl Owner { } pub mod entry_points { - use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter}; - + //! Entry points definitions. use crate::{consts, Address}; + use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter}; + /// Public `change_ownership` entry point. Corresponds to [`change_ownership`](super::Owner::change_ownership). pub fn change_ownership() -> EntryPoint { EntryPoint::new( consts::EP_CHANGE_OWNERSHIP, @@ -57,9 +64,11 @@ pub mod entry_points { } pub mod events { + //! Events definitions. use crate::Address; use casper_dao_macros::Event; + /// Informs the owner change. #[derive(Debug, PartialEq, Event)] pub struct OwnerChanged { pub new_owner: Address, diff --git a/utils/src/modules/staking.rs b/utils/src/modules/staking.rs index 706b3f8c..78a1c253 100644 --- a/utils/src/modules/staking.rs +++ b/utils/src/modules/staking.rs @@ -1,3 +1,5 @@ +//! Token with staking powers. + use casper_contract::contract_api::runtime; use casper_types::U256; @@ -5,6 +7,7 @@ use crate::{casper_env::emit, consts, token::Token, Address, Error, Mapping}; use self::events::{TokensStaked, TokensUnstaked}; +/// The TokenWithStaking module. pub struct TokenWithStaking { pub stakes: Mapping, pub token: Token, @@ -20,25 +23,30 @@ impl Default for TokenWithStaking { } impl TokenWithStaking { + /// Initialize the module. pub fn init(&mut self) { self.stakes.init(); self.token.init(); } + /// Mint new tokens. See [`Token::mint`](Token::mint). pub fn mint(&mut self, recipient: Address, amount: U256) { self.token.mint(recipient, amount); } + /// Burn unstaked tokens. See [`Token::burn`](Token::burn) pub fn burn(&mut self, owner: Address, amount: U256) { self.ensure_balance(&owner, amount); self.token.burn(owner, amount); } + /// Transfer unstaked tokens. See [`Token::raw_transfer`](Token::raw_transfer) pub fn raw_transfer(&mut self, sender: Address, recipient: Address, amount: U256) { self.ensure_balance(&sender, amount); self.token.raw_transfer(sender, recipient, amount); } + /// Stake `amount` of tokens for the `address`. It decrements `address`'s balance by `amount`. pub fn stake(&mut self, address: Address, amount: U256) { self.ensure_balance(&address, amount); self.stakes @@ -46,6 +54,7 @@ impl TokenWithStaking { emit(TokensStaked { address, amount }); } + /// Unstake `amount` of tokens for the `address`. It increments `address`'s balance by `amount`. pub fn unstake(&mut self, address: Address, amount: U256) { self.ensure_staked_balance(&address, amount); self.stakes @@ -66,11 +75,12 @@ impl TokenWithStaking { } pub mod entry_points { - use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter, U256}; - + //! Entry points definitions. pub use crate::token::entry_points::{burn, mint, transfer_from}; use crate::{consts, Address}; + use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter, U256}; + /// Public `stake` entry point. Corresponds to [`stake`](super::TokenWithStaking::stake). pub fn stake() -> EntryPoint { EntryPoint::new( consts::EP_STAKE, @@ -84,6 +94,7 @@ pub mod entry_points { ) } + /// Public `unstake` entry point. Corresponds to [`unstake`](super::TokenWithStaking::unstake). pub fn unstake() -> EntryPoint { EntryPoint::new( consts::EP_UNSTAKE, @@ -99,17 +110,19 @@ pub mod entry_points { } pub mod events { + //! Events definitions. + use crate::Address; use casper_dao_macros::Event; use casper_types::U256; - use crate::Address; - + /// Informs tokens have been staked. #[derive(Debug, PartialEq, Event)] pub struct TokensStaked { pub address: Address, pub amount: U256, } + /// Informs tokens have been unstaked. #[derive(Debug, PartialEq, Event)] pub struct TokensUnstaked { pub address: Address, diff --git a/utils/src/modules/token.rs b/utils/src/modules/token.rs index 6b482202..c9529c22 100644 --- a/utils/src/modules/token.rs +++ b/utils/src/modules/token.rs @@ -1,9 +1,12 @@ +//! Token module with balances and total supply. + use casper_contract::contract_api::runtime; use casper_types::U256; use self::events::{Burn, Mint, Transfer}; use crate::{casper_env::emit, consts, Address, Error, Mapping, Variable}; +/// The Token module. pub struct Token { pub total_supply: Variable, pub balances: Mapping, @@ -19,11 +22,18 @@ impl Default for Token { } impl Token { + /// Initialize the module. pub fn init(&mut self) { self.balances.init(); self.total_supply.set(U256::zero()); } + /// Mint new tokens. + /// + /// Add `amount` of new tokens to the balance of the `recipient` and + /// increment the total supply. + /// + /// It emits [`Mint`](events::Mint) event. pub fn mint(&mut self, recipient: Address, amount: U256) { let (new_supply, is_overflowed) = self.total_supply.get().overflowing_add(amount); if is_overflowed { @@ -40,6 +50,12 @@ impl Token { }); } + /// Burn existing tokens. + /// + /// Remove `amount` of existing tokens from the balance of the `owner` + /// and decrement the total supply. + /// + /// It emits [`Burn`](events::Burn) event. pub fn burn(&mut self, owner: Address, amount: U256) { self.total_supply.set(self.total_supply.get() - amount); self.balances @@ -50,6 +66,9 @@ impl Token { }); } + /// Transfer `amount` of tokens from `owner` to `recipient`. + /// + /// It emits [`Transfer`](events::Transfer) event. pub fn raw_transfer(&mut self, sender: Address, recipient: Address, amount: U256) { self.balances .set(&sender, self.balances.get(&sender) - amount); @@ -63,6 +82,9 @@ impl Token { }); } + /// Assert `address` has at least `amount` of tokens. + /// + /// Revert otherwise. pub fn ensure_balance(&mut self, address: &Address, amount: U256) { if self.balances.get(address) < amount { runtime::revert(Error::InsufficientBalance); @@ -71,10 +93,11 @@ impl Token { } pub mod entry_points { - use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter, U256}; - + //! Entry points definitions. use crate::{consts, Address}; + use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter, U256}; + /// Public `mint` entry point. Corresponds to [`mint`](super::Token::mint). pub fn mint() -> EntryPoint { EntryPoint::new( consts::EP_MINT, @@ -88,6 +111,7 @@ pub mod entry_points { ) } + /// Public `burn` entry point. Corresponds to [`burn`](super::Token::burn). pub fn burn() -> EntryPoint { EntryPoint::new( consts::EP_BURN, @@ -101,6 +125,7 @@ pub mod entry_points { ) } + /// Public `transfer_from` entry point. Corresponds to [`raw_transfer`](super::Token::raw_transfer). pub fn transfer_from() -> EntryPoint { EntryPoint::new( consts::EP_TRANSFER_FROM, diff --git a/utils/src/modules/whitelist.rs b/utils/src/modules/whitelist.rs index 160b52d3..f500341a 100644 --- a/utils/src/modules/whitelist.rs +++ b/utils/src/modules/whitelist.rs @@ -1,3 +1,5 @@ +//! Whitelist-based access control system. + use casper_contract::contract_api::runtime; use crate::{ @@ -7,6 +9,7 @@ use crate::{ use self::events::{AddedToWhitelist, RemovedFromWhitelist}; +/// The Whitelist module. pub struct Whitelist { pub whitelist: Mapping, } @@ -20,20 +23,24 @@ impl Default for Whitelist { } impl Whitelist { + /// Initialize the module. pub fn init(&mut self) { self.whitelist.init(); } + /// Add new `address` to the whitelist. pub fn add_to_whitelist(&mut self, address: Address) { self.whitelist.set(&address, true); emit(AddedToWhitelist { address }); } + /// Remove an `address` from the whitelist. pub fn remove_from_whitelist(&mut self, address: Address) { self.whitelist.set(&address, false); emit(RemovedFromWhitelist { address }); } + /// Assert the caller is on the list. Revert otherwise. pub fn ensure_whitelisted(&self) { if !self.whitelist.get(&caller()) { runtime::revert(Error::NotWhitelisted); @@ -42,10 +49,12 @@ impl Whitelist { } pub mod entry_points { - use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter}; + //! Entry points definitions. use crate::{consts, Address}; + use casper_types::{CLTyped, EntryPoint, EntryPointAccess, EntryPointType, Parameter}; + /// Public `add_to_whitelist` entry point. Corresponds to [`add_to_whitelist`](super::Whitelist::add_to_whitelist). pub fn add_to_whitelist() -> EntryPoint { EntryPoint::new( consts::EP_ADD_TO_WHITELIST, @@ -56,6 +65,7 @@ pub mod entry_points { ) } + /// Public `remove_from_whitelist` entry point. Corresponds to [`remove_from_whitelist`](super::Whitelist::remove_from_whitelist). pub fn remove_from_whitelist() -> EntryPoint { EntryPoint::new( consts::EP_REMOVE_FROM_WHITELIST, @@ -68,14 +78,17 @@ pub mod entry_points { } pub mod events { + //! Events definitions. use crate::Address; use casper_dao_macros::Event; + /// Informs new address has been added to the whitelist. #[derive(Debug, PartialEq, Event)] pub struct AddedToWhitelist { pub address: Address, } + /// Informs new address has been removed from the whitelist. #[derive(Debug, PartialEq, Event)] pub struct RemovedFromWhitelist { pub address: Address, diff --git a/utils/src/parts/address.rs b/utils/src/parts/address.rs index a1b1f9ca..5541b2ea 100644 --- a/utils/src/parts/address.rs +++ b/utils/src/parts/address.rs @@ -1,6 +1,3 @@ -/// Taken from: https://raw.githubusercontent.com/casper-ecosystem/erc20/master/erc20/src/address.rs -/// TODO: Check that with CasperLabs. -// ! Implementation of an `Address` which refers either an account hash, or a contract hash. use alloc::vec::Vec; use casper_types::{ account::AccountHash, @@ -9,6 +6,9 @@ use casper_types::{ }; /// An enum representing an [`AccountHash`] or a [`ContractPackageHash`]. +/// +/// It is taken from [`CasperLabs's ERC20`](https://raw.githubusercontent.com/casper-ecosystem/erc20/master/erc20/src/address.rs). +/// It is copied instead of imported for the flexebility. #[derive(PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy, Debug)] pub enum Address { /// Represents an account hash. diff --git a/utils/src/parts/consts.rs b/utils/src/parts/consts.rs index 65e7cc8a..218bfe8b 100644 --- a/utils/src/parts/consts.rs +++ b/utils/src/parts/consts.rs @@ -1,3 +1,5 @@ +//! Useful constants for common strings. + pub const EP_INIT: &str = "init"; pub const EP_MINT: &str = "mint"; pub const EP_BURN: &str = "burn"; diff --git a/utils/src/parts/error.rs b/utils/src/parts/error.rs index be70716f..7ad4ae95 100644 --- a/utils/src/parts/error.rs +++ b/utils/src/parts/error.rs @@ -1,8 +1,6 @@ use casper_types::ApiError; -#[cfg(feature = "test-support")] -pub use casper_execution_engine::core::execution::Error as ExecutionError; - +/// All possible errors that can be raised by the utils crate. pub enum Error { NotAnOwner, OwnerIsNotInitialized, diff --git a/utils/src/parts/mapping.rs b/utils/src/parts/mapping.rs index 04e16547..80301a5a 100644 --- a/utils/src/parts/mapping.rs +++ b/utils/src/parts/mapping.rs @@ -12,6 +12,11 @@ use lazy_static::lazy_static; use crate::casper_env::to_dictionary_key; +/// Data structure for storing key-value pairs. +/// +/// It's is a wrapper on top of Casper's dictionary. +/// The main difference is that Mapping returns default value, if the value doesn't exists +/// and it stores dictionary's URef for later use. pub struct Mapping { name: String, key_ty: PhantomData, @@ -23,6 +28,7 @@ lazy_static! { } impl Mapping { + /// Create new Mapping instance. pub fn new(name: String) -> Self { Mapping { name, @@ -31,20 +37,28 @@ impl Mapping V { storage::dictionary_get(self.get_uref(), &to_dictionary_key(key)) .unwrap_or_revert() .unwrap_or_default() } + /// Set `value` under `key` to the storage. It overrides by default. pub fn set(&self, key: &K, value: V) { storage::dictionary_put(self.get_uref(), &to_dictionary_key(key), value); } + /// Return the named key path to the dictionarie's URef. + pub fn path(&self) -> &str { + &self.name + } + fn get_uref(&self) -> URef { let mut seeds = SEEDS.lock().unwrap(); match seeds.get(&self.name) { @@ -57,10 +71,6 @@ impl Mapping &str { - &self.name - } } impl From<&str> diff --git a/utils/src/parts/variable.rs b/utils/src/parts/variable.rs index ab32a6e2..1c9db75d 100644 --- a/utils/src/parts/variable.rs +++ b/utils/src/parts/variable.rs @@ -7,12 +7,14 @@ use casper_types::{ use crate::casper_env::{get_key, set_key}; +/// Data structure for storing a single value. pub struct Variable { name: String, ty: PhantomData, } impl Variable { + /// Create a new Variable instance. pub fn new(name: String) -> Self { Variable { name, @@ -20,14 +22,17 @@ impl Variable { } } + /// Read from the storage or return default value. pub fn get(&self) -> T { get_key(&self.name).unwrap_or_default() } + /// Store `value` to the storage. pub fn set(&mut self, value: T) { set_key(&self.name, value); } + /// Return the named key path to the variable's URef. pub fn path(&self) -> &str { &self.name } diff --git a/utils/src/test_env.rs b/utils/src/test_env.rs index 302d36e8..52d34815 100644 --- a/utils/src/test_env.rs +++ b/utils/src/test_env.rs @@ -22,18 +22,21 @@ use casper_types::{ pub use casper_execution_engine::core::execution::Error as ExecutionError; +/// CasperVM based testing environment. #[derive(Clone)] pub struct TestEnv { state: Arc>, } impl TestEnv { + /// Create new TestEnv. pub fn new() -> TestEnv { TestEnv { state: Arc::new(Mutex::new(TestEnvState::new())), } } + /// Deploy new wasm file. pub fn deploy_wasm_file(&self, session_code: &str, session_args: RuntimeArgs) { self.state .lock() @@ -41,6 +44,7 @@ impl TestEnv { .deploy_wasm_file(session_code, session_args); } + /// Call already deployed contract. pub fn call_contract_package( &mut self, hash: ContractPackageHash, @@ -53,14 +57,17 @@ impl TestEnv { .call_contract_package(hash, entry_point, args); } + /// Read [`ContractPackageHash`] from the active user's named keys. pub fn get_contract_package_hash(&self, name: &str) -> ContractPackageHash { self.state.lock().unwrap().get_contract_package_hash(name) } + /// Read [`casper_types::CLValue`] from the contract's named keys. pub fn get_value(&self, hash: ContractPackageHash, name: &str) -> T { self.state.lock().unwrap().get_value(hash, name) } + /// Read [`casper_types::CLValue`] from the contract's dictionary. pub fn get_dict_value( &self, hash: ContractPackageHash, @@ -70,18 +77,22 @@ impl TestEnv { self.state.lock().unwrap().get_dict_value(hash, name, key) } + /// Get account by index. pub fn get_account(&self, n: usize) -> Address { self.state.lock().unwrap().get_account(n) } + /// Set the account context. pub fn as_account(&self, account: Address) { self.state.lock().unwrap().as_account(account); } + /// Set the [`ApiError`] expected to occur. pub fn expect_error>(&self, error: T) { self.state.lock().unwrap().expect_error(error); } + /// Set the [`execution::Error`] expected to occur. pub fn expect_execution_error(&self, error: execution::Error) { self.state.lock().unwrap().expect_execution_error(error); }