Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced test environment #1

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
b0d88e8
initial change for advanced ink test environment
Artemka374 Dec 29, 2022
8d99c80
initial change for advanced ink test environment
Artemka374 Dec 29, 2022
54e63e2
fix never type
Artemka374 Dec 29, 2022
f97aeb2
Merge remote-tracking branch 'origin/feature/advanced-test-environmen…
Artemka374 Dec 29, 2022
328882c
fix bugs
Artemka374 Jan 3, 2023
ca5a333
fix dispatch
Artemka374 Jan 3, 2023
2a35721
initial implementation of call flags with tests
Artemka374 Jan 9, 2023
52485b9
fix call_flags bug
Artemka374 Jan 10, 2023
e347054
implement revert storage
Artemka374 Jan 10, 2023
1af298d
minor changes and apply suggestions
Artemka374 Jan 11, 2023
d512e53
fix bug
Artemka374 Jan 11, 2023
e7ea5b5
fix bug
Artemka374 Jan 11, 2023
55443ab
implement caller_is_origin
Artemka374 Jan 11, 2023
029ed7f
apply suggestions
Artemka374 Jan 11, 2023
a09c388
add README for the reentrancy example and apply suggestions
Artemka374 Jan 12, 2023
0a16557
Merge branch 'master' into feature/advanced-test-environment
Artemka374 Jan 12, 2023
5fc7b08
cargo clippy
Artemka374 Jan 16, 2023
251f7ba
fix clippy
Artemka374 Jan 16, 2023
76cf2ce
fix unreachable code
Artemka374 Jan 16, 2023
7f09f31
add allow nonminimal bool
Artemka374 Jan 16, 2023
edd466f
apply clippy on examples
Artemka374 Jan 16, 2023
6184402
apply clippy on delegator example
Artemka374 Jan 16, 2023
f6c885a
fix no_implicit_prelude error
Artemka374 Jan 16, 2023
91ae09f
move tests to fallback_contract
Artemka374 Jan 16, 2023
2bf8af8
move fallback_contract to reentrancy dir
Artemka374 Jan 16, 2023
1134ad1
add tests for contract storage
Artemka374 Jan 16, 2023
bd20f87
fix clippy for test
Artemka374 Jan 16, 2023
78c7869
tail call is not supported yet
Artemka374 Jan 17, 2023
5fe1137
add generate address method
Artemka374 Jan 19, 2023
df3dac2
implement invoke_delegate
Artemka374 Jan 19, 2023
2fb2533
fix compilation errors
Artemka374 Jan 20, 2023
37f461c
apply suggestions
Artemka374 Jan 20, 2023
50a8c9b
minor code style changes
Artemka374 Jan 24, 2023
c1fcf2f
wrap engine into Rc<RefCell>
Artemka374 Jan 24, 2023
cef7ab2
use `let engine = self.engine.borrow()` where it is possible
Artemka374 Jan 24, 2023
15afa37
Merge branch 'master' into feature/advanced-test-environment
Artemka374 Jan 24, 2023
27c60bd
Merge branch 'master' into feature/advanced-test-environment
Artemka374 Jan 25, 2023
bc5bb68
fix compilation with ink! 4.0.0-beta.1
Artemka374 Jan 25, 2023
9c8f951
fix tests and update test in delegator example
Artemka374 Jan 25, 2023
f0b1f35
add unreachable to test to work with our api
Artemka374 Jan 26, 2023
319d599
Merge branch 'master' into feature/advanced-test-environment
Artemka374 Jan 26, 2023
edb9b7b
overwrite ui tests
Artemka374 Jan 26, 2023
0a14dc2
delete unused methods in examples
Artemka374 Jan 27, 2023
5b4761b
update ui tests
Artemka374 Jan 27, 2023
d5524bd
update readme
Artemka374 Jan 27, 2023
3e78b76
Change the way contracts are instantiated in example, update readme
Artemka374 Jan 30, 2023
6804763
fix clippy
Artemka374 Jan 30, 2023
8089eff
Merge branch 'master' into feature/advanced-test-environment
Artemka374 Mar 1, 2023
68c11bc
update tests to new ink! version
Artemka374 Mar 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/engine/src/exec_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ pub struct ExecContext {
pub block_number: BlockNumber,
/// The current block timestamp.
pub block_timestamp: BlockTimestamp,
/// The input of the call.
pub input: Vec<u8>,
/// The output buffer of the call.
pub output: Vec<u8>,
}

impl ExecContext {
Expand Down
164 changes: 132 additions & 32 deletions crates/engine/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ use crate::{
},
};
use scale::Encode;
use std::panic::panic_any;
use std::{
cell::RefCell,
collections::HashMap,
panic::panic_any,
rc::Rc,
};

type Result = core::result::Result<(), Error>;

Expand Down Expand Up @@ -110,20 +115,75 @@ impl ReturnCode {
}
}

#[derive(Default)]
pub struct ContractStore {
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
pub instantiated: HashMap<Vec<u8>, Vec<u8>>,
pub entrance_count: HashMap<Vec<u8>, u32>,
pub allow_reentry: HashMap<Vec<u8>, bool>,
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
pub deployed: HashMap<Vec<u8>, Contract>,
}

impl ContractStore {
pub fn get_entrance_count(&self, callee: Vec<u8>) -> u32 {
self.entrance_count.get(&callee).unwrap_or(&0).clone()
}

pub fn get_allow_reentry(&self, callee: Vec<u8>) -> bool {
self.allow_reentry.get(&callee).unwrap_or(&false).clone()
}

pub fn set_allow_reentry(&mut self, callee: Vec<u8>, allow: bool) {
if allow == false {
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
self.allow_reentry.remove(&callee);
} else {
self.allow_reentry.insert(callee, allow);
}
}

pub fn increase_entrance(&mut self, callee: Vec<u8>) -> Result {
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
let entrance = self.entrance_count.get(&callee).unwrap_or(&0).clone();
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved

self.entrance_count.insert(callee, entrance + 1);

Ok(())
}

pub fn decrease_entrance(&mut self, callee: Vec<u8>) -> Result {
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
let entrance = self.entrance_count.get(&callee).unwrap_or(&0).clone();
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved

if entrance == 0 {
// todo: update error
return Err(Error::Unknown)
}

self.entrance_count.insert(callee, entrance - 1);

Ok(())
}
}

pub struct Contract {
pub deploy: fn(),
pub call: fn(),
}

/// The off-chain engine.
#[derive(Clone)]
pub struct Engine {
/// The environment database.
pub database: Database,
pub database: Rc<RefCell<Database>>,
/// The current execution context.
pub exec_context: ExecContext,
pub exec_context: Rc<RefCell<ExecContext>>,
/// Recorder for relevant interactions with the engine.
/// This is specifically about debug info. This info is
/// not available in the `contracts` pallet.
pub(crate) debug_info: DebugInfo,
pub(crate) debug_info: Rc<RefCell<DebugInfo>>,
/// The chain specification.
pub chain_spec: ChainSpec,
pub chain_spec: Rc<RefCell<ChainSpec>>,
/// Handler for registered chain extensions.
pub chain_extension_handler: ChainExtensionHandler,
pub chain_extension_handler: Rc<RefCell<ChainExtensionHandler>>,
Artemka374 marked this conversation as resolved.
Show resolved Hide resolved
/// Contracts' store.
pub contracts: Rc<RefCell<ContractStore>>,
}

/// The chain specification.
Expand Down Expand Up @@ -158,11 +218,12 @@ impl Engine {
// Creates a new `Engine instance.
pub fn new() -> Self {
Self {
database: Database::new(),
exec_context: ExecContext::new(),
debug_info: DebugInfo::new(),
chain_spec: ChainSpec::default(),
chain_extension_handler: ChainExtensionHandler::new(),
database: Rc::new(RefCell::new(Database::new())),
exec_context: Rc::new(RefCell::new(ExecContext::new())),
debug_info: Rc::new(RefCell::new(DebugInfo::new())),
chain_spec: Rc::new(RefCell::new(ChainSpec::default())),
chain_extension_handler: Rc::new(RefCell::new(ChainExtensionHandler::new())),
contracts: Rc::new(RefCell::new(ContractStore::default())),
}
}
}
Expand Down Expand Up @@ -190,8 +251,10 @@ impl Engine {
.map_err(|_| Error::TransferFailed)?;

self.database
.borrow_mut()
.set_balance(&contract, contract_old_balance - increment);
self.database
.borrow_mut()
.set_balance(&dest, dest_old_balance + increment);
Ok(())
}
Expand All @@ -217,7 +280,7 @@ impl Engine {
Vec::new()
};

self.debug_info.record_event(EmittedEvent {
self.debug_info.borrow_mut().record_event(EmittedEvent {
topics: topics_vec,
data: data.to_vec(),
});
Expand All @@ -229,11 +292,13 @@ impl Engine {
let callee = self.get_callee();
let account_id = AccountId::from_bytes(&callee[..]);

self.debug_info.inc_writes(account_id.clone());
self.debug_info.borrow_mut().inc_writes(account_id.clone());
self.debug_info
.borrow_mut()
.record_cell_for_account(account_id, key.to_vec());

self.database
.borrow_mut()
.insert_into_contract_storage(&callee, key, encoded_value.to_vec())
.map(|v| <u32>::try_from(v.len()).expect("usize to u32 conversion failed"))
}
Expand All @@ -243,8 +308,12 @@ impl Engine {
let callee = self.get_callee();
let account_id = AccountId::from_bytes(&callee[..]);

self.debug_info.inc_reads(account_id);
match self.database.get_from_contract_storage(&callee, key) {
self.debug_info.borrow_mut().inc_reads(account_id);
match self
.database
.borrow_mut()
.get_from_contract_storage(&callee, key)
{
Some(val) => {
set_output(output, val);
Ok(())
Expand All @@ -259,8 +328,12 @@ impl Engine {
let callee = self.get_callee();
let account_id = AccountId::from_bytes(&callee[..]);

self.debug_info.inc_writes(account_id);
match self.database.remove_contract_storage(&callee, key) {
self.debug_info.borrow_mut().inc_writes(account_id);
match self
.database
.borrow_mut()
.remove_contract_storage(&callee, key)
{
Some(val) => {
set_output(output, &val);
Ok(())
Expand All @@ -274,8 +347,9 @@ impl Engine {
let callee = self.get_callee();
let account_id = AccountId::from_bytes(&callee[..]);

self.debug_info.inc_reads(account_id);
self.debug_info.borrow_mut().inc_reads(account_id);
self.database
.borrow_mut()
.get_from_contract_storage(&callee, key)
.map(|val| val.len() as u32)
}
Expand All @@ -285,11 +359,13 @@ impl Engine {
pub fn clear_storage(&mut self, key: &[u8]) -> Option<u32> {
let callee = self.get_callee();
let account_id = AccountId::from_bytes(&callee[..]);
self.debug_info.inc_writes(account_id.clone());
self.debug_info.borrow_mut().inc_writes(account_id.clone());
let _ = self
.debug_info
.borrow_mut()
.remove_cell_for_account(account_id, key.to_vec());
self.database
.borrow_mut()
.remove_contract_storage(&callee, key)
.map(|val| val.len() as u32)
}
Expand Down Expand Up @@ -320,23 +396,27 @@ impl Engine {
pub fn caller(&self, output: &mut &mut [u8]) {
let caller = self
.exec_context
.borrow()
.caller
.as_ref()
.expect("no caller has been set")
.as_bytes();
set_output(output, caller);
.clone();
set_output(output, caller.as_bytes());
}

/// Returns the balance of the executed contract.
pub fn balance(&self, output: &mut &mut [u8]) {
let contract = self
.exec_context
.borrow()
.callee
.as_ref()
.expect("no callee has been set");
.expect("no callee has been set")
.clone();

let balance_in_storage = self
.database
.borrow()
.get_balance(contract.as_bytes())
.expect("currently executing contract must exist");
let balance = scale::Encode::encode(&balance_in_storage);
Expand All @@ -346,24 +426,27 @@ impl Engine {
/// Returns the transferred value for the called contract.
pub fn value_transferred(&self, output: &mut &mut [u8]) {
let value_transferred: Vec<u8> =
scale::Encode::encode(&self.exec_context.value_transferred);
scale::Encode::encode(&self.exec_context.borrow().value_transferred);
set_output(output, &value_transferred[..])
}

/// Returns the address of the executed contract.
pub fn address(&self, output: &mut &mut [u8]) {
let callee = self
.exec_context
.borrow()
.callee
.as_ref()
.expect("no callee has been set")
.as_bytes();
set_output(output, callee)
.clone();
set_output(output, callee.as_bytes())
}

/// Records the given debug message and appends to stdout.
pub fn debug_message(&mut self, message: &str) {
self.debug_info.record_debug_message(String::from(message));
self.debug_info
.borrow_mut()
.record_debug_message(String::from(message));
print!("{}", message);
}

Expand All @@ -390,14 +473,14 @@ impl Engine {
/// Returns the current block number.
pub fn block_number(&self, output: &mut &mut [u8]) {
let block_number: Vec<u8> =
scale::Encode::encode(&self.exec_context.block_number);
scale::Encode::encode(&self.exec_context.borrow().block_number);
set_output(output, &block_number[..])
}

/// Returns the timestamp of the current block.
pub fn block_timestamp(&self, output: &mut &mut [u8]) {
let block_timestamp: Vec<u8> =
scale::Encode::encode(&self.exec_context.block_timestamp);
scale::Encode::encode(&self.exec_context.borrow().block_timestamp);
set_output(output, &block_timestamp[..])
}

Expand All @@ -409,7 +492,7 @@ impl Engine {
/// (i.e. the chain's existential deposit).
pub fn minimum_balance(&self, output: &mut &mut [u8]) {
let minimum_balance: Vec<u8> =
scale::Encode::encode(&self.chain_spec.minimum_balance);
scale::Encode::encode(&self.chain_spec.borrow().minimum_balance);
set_output(output, &minimum_balance[..])
}

Expand Down Expand Up @@ -440,7 +523,11 @@ impl Engine {

/// Emulates gas price calculation.
pub fn weight_to_fee(&self, gas: u64, output: &mut &mut [u8]) {
let fee = self.chain_spec.gas_price.saturating_mul(gas.into());
let fee = self
.chain_spec
.borrow()
.gas_price
.saturating_mul(gas.into());
let fee: Vec<u8> = scale::Encode::encode(&fee);
set_output(output, &fee[..])
}
Expand All @@ -453,8 +540,8 @@ impl Engine {
output: &mut &mut [u8],
) {
let encoded_input = input.encode();
let (status_code, out) = self
.chain_extension_handler
let mut chain_extension_handler = self.chain_extension_handler.borrow_mut();
let (status_code, out) = chain_extension_handler
.eval(func_id, &encoded_input)
.unwrap_or_else(|error| {
panic!(
Expand Down Expand Up @@ -513,6 +600,19 @@ impl Engine {
Err(_) => Err(Error::EcdsaRecoveryFailed),
}
}

/// Register the contract into the local storage.
pub fn register_contract(
&mut self,
hash: &[u8],
deploy: fn(),
call: fn(),
) -> Option<Contract> {
self.contracts
.borrow_mut()
.deployed
.insert(hash.to_vec(), Contract { deploy, call })
}
}

/// Copies the `slice` into `output`.
Expand Down
2 changes: 1 addition & 1 deletion crates/engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub mod test_api;

mod chain_extension;
mod database;
mod exec_context;
pub mod exec_context;
coreggon11 marked this conversation as resolved.
Show resolved Hide resolved
mod hashing;
mod types;

Expand Down
Loading