Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
92d2152
first partial implementation
gui1117 Apr 2, 2019
1a07b09
merge + some improvments
gui1117 Apr 8, 2019
ab1bcad
update rent allowance
gui1117 Apr 8, 2019
94a8a00
fmt
bkchr Apr 8, 2019
4a0937f
remove comments
gui1117 Apr 8, 2019
ef073f2
reward surcharge claims
gui1117 Apr 8, 2019
cab4aa3
remove rent allowance in param + code_hash changed
gui1117 Apr 9, 2019
0841bfe
Fix bug
gui1117 Apr 9, 2019
1be670e
fix tests
gui1117 Apr 9, 2019
723eb65
fmt
gui1117 Apr 9, 2019
c7506a1
impl getter setter rent allowance
gui1117 Apr 9, 2019
d5c8477
fmt
pepyakin Apr 9, 2019
3711ac2
comments
gui1117 Apr 9, 2019
8a2bee2
doc + be->le
gui1117 Apr 9, 2019
6578895
doc
gui1117 Apr 9, 2019
2598f00
doc
gui1117 Apr 9, 2019
3fc2cf2
fix improve fast return
gui1117 Apr 10, 2019
aba8d7f
renamings
gui1117 Apr 10, 2019
c30778c
rename + COMPLEXITY
gui1117 Apr 10, 2019
36b70a9
COMPLEXITY
gui1117 Apr 10, 2019
712fe17
add test
gui1117 Apr 10, 2019
005752b
etrinsic claim surcharge delay configurable
gui1117 Apr 10, 2019
dfc4e66
comment addressed
gui1117 Apr 11, 2019
ed016c2
move and rewrite of pay_rent
gui1117 Apr 11, 2019
37c6e9b
remove child trie
gui1117 Apr 11, 2019
2b027f2
fmt
gui1117 Apr 11, 2019
efa6afd
use derive
gui1117 Apr 11, 2019
f640622
arithmetic operation
gui1117 Apr 11, 2019
1e6206b
fix
gui1117 Apr 11, 2019
ab4ca5b
fix storage root + checked_mul + test
gui1117 Apr 12, 2019
14e3756
WIP: test
gui1117 Apr 12, 2019
eaa3173
Merge remote-tracking branch 'origin/master' into gui-contract-accoun…
gui1117 Apr 18, 2019
867dc88
WIP
gui1117 Apr 18, 2019
8b81596
add tests and fix
gui1117 Apr 25, 2019
d0fe071
fmt
gui1117 Apr 25, 2019
7215e14
Merge remote-tracking branch 'origin/master' into gui-contract-accoun…
gui1117 Apr 25, 2019
1659fb0
typo and doc suggestions
pepyakin Apr 26, 2019
d6f5d8c
WIP
gui1117 Apr 28, 2019
25fc7f0
address some comments
gui1117 Apr 28, 2019
1651e0b
use br_table
gui1117 Apr 28, 2019
323fb6a
remove unused function
gui1117 Apr 28, 2019
3618ace
Bump the runtime version
pepyakin Apr 29, 2019
59a4a17
insert_with
pepyakin Apr 29, 2019
86e330b
Add some comments.
pepyakin Apr 29, 2019
7952ff3
Refactor
pepyakin Apr 29, 2019
806a479
Shuffle and fix comments
pepyakin Apr 29, 2019
f6c696d
More comment fixes.
pepyakin Apr 29, 2019
405ae98
dues limited
pepyakin Apr 29, 2019
b3c7795
Add comment
pepyakin Apr 29, 2019
c6338ff
Handicap
pepyakin Apr 29, 2019
d040fb9
Docs.
pepyakin Apr 29, 2019
a252095
Apply suggestions from code review
gui1117 Apr 29, 2019
5053ef8
Merge branch 'gui-contract-accounting-removal' of github.com:parityte…
pepyakin Apr 29, 2019
bfb8a13
Coalesce block_passed in a block
pepyakin Apr 29, 2019
9a491c7
Fix build
pepyakin Apr 29, 2019
be7e1d8
Paid → Ok
pepyakin Apr 29, 2019
63620c5
match → if
pepyakin Apr 29, 2019
381cf62
Imrpove handicap description
pepyakin Apr 29, 2019
0bc79d9
Merge remote-tracking branch 'origin/master' into gui-contract-accoun…
gui1117 Apr 29, 2019
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
2 changes: 1 addition & 1 deletion core/state-machine/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
}

fn child_storage_root(&mut self, _storage_key: ChildStorageKey<H>) -> Vec<u8> {
unimplemented!()
vec![]
}

fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> {
Expand Down
12 changes: 12 additions & 0 deletions node/cli/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
burn: Permill::from_percent(50),
}),
contract: Some(ContractConfig {
signed_claim_handicap: 2,
rent_byte_price: 4,
rent_deposit_offset: 1000,
storage_size_offset: 8,
surcharge_reward: 150,
tombstone_deposit: 16,
transaction_base_fee: 1 * CENTS,
transaction_byte_fee: 10 * MILLICENTS,
transfer_fee: 1 * CENTS,
Expand Down Expand Up @@ -239,6 +245,12 @@ pub fn testnet_genesis(
const ENDOWMENT: u128 = 1 << 20;

let mut contract_config = ContractConfig {
signed_claim_handicap: 2,
rent_byte_price: 4,
rent_deposit_offset: 1000,
storage_size_offset: 8,
surcharge_reward: 150,
tombstone_deposit: 16,
transaction_base_fee: 1,
transaction_byte_fee: 0,
transfer_fee: 0,
Expand Down
8 changes: 7 additions & 1 deletion node/executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,13 @@ mod tests {

runtime_io::with_externalities(&mut t, || {
// Verify that the contract constructor worked well and code of TRANSFER contract is actually deployed.
assert_eq!(&contract::CodeHashOf::<Runtime>::get(addr).unwrap(), &transfer_ch);
assert_eq!(
&contract::ContractInfoOf::<Runtime>::get(addr)
.and_then(|c| c.get_alive())
.unwrap()
.code_hash,
&transfer_ch
);
});
}

Expand Down
2 changes: 1 addition & 1 deletion node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
spec_version: 67,
impl_version: 67,
impl_version: 68,
apis: RUNTIME_API_VERSIONS,
};

Expand Down
32 changes: 30 additions & 2 deletions srml/contract/COMPLEXITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ execution contexts operate on the AccountDb. All changes are flushed into underl

Today `AccountDb` is implemented as a cascade of overlays with the direct storage at the bottom. Each overlay is represented by a `Map`. On a commit from an overlay to an overlay, maps are merged. On commit from an overlay to the bottommost `AccountDb` all changes are flushed to the storage. On revert, the overlay is just discarded.

## get_storage, get_code, get_balance
## get_storage, get_code_hash, get_rent_allowance, get_balance, contract_exists

These functions check the local cache for a requested value and, if it is there, the value is returned. Otherwise, these functions will ask an underlying `AccountDb` for the value. This means that the number of lookups is proportional to the depth of the overlay cascade. If the value can't be found before reaching the bottommost `AccountDb`, then a DB read will be performed (in case `get_balance` the function `free_balance` will be invoked).

Expand All @@ -95,7 +95,7 @@ These functions return an owned value as its result, so memory usage depends on

**complexity**: The memory complexity is proportional to the size of the value. The computational complexity is proportional to the depth of the overlay cascade and the size of the value; the cost is dominated by the DB read though.

## set_storage, set_code, set_balance
## set_storage, set_balance, set_rent_allowance

These functions only modify the local `Map`.

Expand All @@ -105,6 +105,12 @@ While these functions only modify the local `Map`, if changes made by them are c

**complexity**: Each lookup has a logarithmical computing time to the number of already inserted entries. No additional memory is required.

## create_contract

Calls `contract_exists` and if it doesn't exist, do not modify the local `Map` similarly to `set_rent_allowance`.

**complexity**: The computational complexity is proportional to the depth of the overlay cascade and the size of the value; the cost is dominated by the DB read though. No additional memory is required.

## commit

In this function, all cached values will be inserted into the underlying `AccountDb` or into the storage.
Expand Down Expand Up @@ -327,3 +333,25 @@ This function copies slice of data from the scratch buffer to the sandbox memory
1. Storing a specified slice of the scratch buffer into the sandbox memory (see sandboxing memory set)

**complexity**: The computing complexity of this function is proportional to the length of the slice. No additional memory is required.

## ext_set_rent_allowance

This function receives the following argument:

- `value` buffer of a marshaled `Balance`,

It consists of the following steps:

1. Loading `value` buffer from the sandbox memory and then decoding it.
2. Invoking `set_rent_allowance` AccountDB function.

**complexity**: Complexity is proportional to the size of the `value`. This function induces a DB write of size proportional to the `value` size (if flushed to the storage), so should be priced accordingly.

## ext_rent_allowance

It consists of the following steps:

1. Invoking `get_rent_allowance` AccountDB function.
2. Serializing the rent allowance of the current contract into the scratch buffer.

**complexity**: Assuming that the rent allowance is of constant size, this function has constant complexity.
159 changes: 111 additions & 48 deletions srml/contract/src/account_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,38 @@

//! Auxilliaries to help with managing partial changes to accounts state.

use super::{CodeHash, CodeHashOf, Trait, TrieId, AccountInfoOf, BalanceOf, AccountInfo, TrieIdGenerator};
use super::{
AliveContractInfo, BalanceOf, CodeHash, ContractInfo, ContractInfoOf, Module, Trait, TrieId,
TrieIdGenerator,
};
use crate::exec::StorageKey;
use system;
use rstd::cell::RefCell;
use rstd::collections::btree_map::{BTreeMap, Entry};
use rstd::prelude::*;
use runtime_io::blake2_256;
use runtime_primitives::traits::Zero;
use srml_support::{StorageMap, traits::{UpdateBalanceOutcome,
SignedImbalance, Currency, Imbalance}, storage::child};
use srml_support::traits::{Currency, Imbalance, SignedImbalance, UpdateBalanceOutcome};
use srml_support::{storage::child, StorageMap};
use system;

// Note: we don't provide Option<Contract> because we can't create
// the trie_id in the overlay, thus we provide an overlay on the fields
// specifically.
pub struct ChangeEntry<T: Trait> {
balance: Option<BalanceOf<T>>,
/// In the case the outer option is None, the code_hash remains untouched, while providing `Some(None)` signifies a removing of the code in question
code: Option<Option<CodeHash<T>>>,
/// If None, the code_hash remains untouched.
code_hash: Option<CodeHash<T>>,
rent_allowance: Option<BalanceOf<T>>,
storage: BTreeMap<StorageKey, Option<Vec<u8>>>,
}

// Cannot derive(Default) since it erroneously bounds T by Default.
impl<T: Trait> Default for ChangeEntry<T> {
fn default() -> Self {
ChangeEntry {
rent_allowance: Default::default(),
balance: Default::default(),
code: Default::default(),
code_hash: Default::default(),
storage: Default::default(),
}
}
Expand All @@ -51,10 +59,15 @@ pub trait AccountDb<T: Trait> {
/// Account is used when overlayed otherwise trie_id must be provided.
/// This is for performance reason.
///
/// Trie id can be None iff account doesn't have an associated trie id in <AccountInfoOf<T>>.
/// Trie id is None iff account doesn't have an associated trie id in <ContractInfoOf<T>>.
/// Because DirectAccountDb bypass the lookup for this association.
fn get_storage(&self, account: &T::AccountId, trie_id: Option<&TrieId>, location: &StorageKey) -> Option<Vec<u8>>;
fn get_code(&self, account: &T::AccountId) -> Option<CodeHash<T>>;
/// If account has an alive contract then return the code hash associated.
fn get_code_hash(&self, account: &T::AccountId) -> Option<CodeHash<T>>;
/// If account has an alive contract then return the rent allowance associated.
fn get_rent_allowance(&self, account: &T::AccountId) -> Option<BalanceOf<T>>;
/// Returns false iff account has no alive contract nor tombstone.
fn contract_exists(&self, account: &T::AccountId) -> bool;
fn get_balance(&self, account: &T::AccountId) -> BalanceOf<T>;

fn commit(&mut self, change_set: ChangeSet<T>);
Expand All @@ -65,8 +78,14 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
fn get_storage(&self, _account: &T::AccountId, trie_id: Option<&TrieId>, location: &StorageKey) -> Option<Vec<u8>> {
trie_id.and_then(|id| child::get_raw(id, &blake2_256(location)))
}
fn get_code(&self, account: &T::AccountId) -> Option<CodeHash<T>> {
<CodeHashOf<T>>::get(account)
fn get_code_hash(&self, account: &T::AccountId) -> Option<CodeHash<T>> {
<ContractInfoOf<T>>::get(account).and_then(|i| i.as_alive().map(|i| i.code_hash))
}
fn get_rent_allowance(&self, account: &T::AccountId) -> Option<BalanceOf<T>> {
<ContractInfoOf<T>>::get(account).and_then(|i| i.as_alive().map(|i| i.rent_allowance))
}
fn contract_exists(&self, account: &T::AccountId) -> bool {
<ContractInfoOf<T>>::exists(account)
}
fn get_balance(&self, account: &T::AccountId) -> BalanceOf<T> {
T::Currency::free_balance(account)
Expand All @@ -84,42 +103,58 @@ impl<T: Trait> AccountDb<T> for DirectAccountDb {
continue;
}
}
if changed.code.is_some() || !changed.storage.is_empty() {
let mut info = if !<AccountInfoOf<T>>::exists(&address) {
let info = AccountInfo {
trie_id: <T as Trait>::TrieIdGenerator::trie_id(&address),
storage_size: 0,
};
<AccountInfoOf<T>>::insert(&address, &info);

if changed.code_hash.is_some()
|| changed.rent_allowance.is_some()
|| !changed.storage.is_empty()
{
let old_info = match <ContractInfoOf<T>>::get(&address) {
Some(ContractInfo::Alive(alive)) => Some(alive),
None => None,
// Cannot commit changes to tombstone contract
Some(ContractInfo::Tombstone(_)) => continue,
};

let mut new_info = if let Some(info) = old_info.clone() {
info
} else if let Some(code_hash) = changed.code_hash {
AliveContractInfo::<T> {
code_hash,
storage_size: <Module<T>>::storage_size_offset(),
trie_id: <T as Trait>::TrieIdGenerator::trie_id(&address),
deduct_block: <system::Module<T>>::block_number(),
rent_allowance: <BalanceOf<T>>::zero(),
}
} else {
<AccountInfoOf<T>>::get(&address).unwrap()
// No contract exist and no code_hash provided
continue;
};

if let Some(code) = changed.code {
if let Some(code) = code {
<CodeHashOf<T>>::insert(&address, code);
} else {
<CodeHashOf<T>>::remove(&address);
}
if let Some(rent_allowance) = changed.rent_allowance {
new_info.rent_allowance = rent_allowance;
}

if let Some(code_hash) = changed.code_hash {
new_info.code_hash = code_hash;
}

let mut new_storage_size = info.storage_size;
for (k, v) in changed.storage.into_iter() {
if let Some(value) = child::get_raw(&info.trie_id[..], &blake2_256(&k)) {
new_storage_size -= value.len() as u64;
if let Some(value) = child::get_raw(&new_info.trie_id[..], &blake2_256(&k)) {
new_info.storage_size -= value.len() as u64;
}
if let Some(value) = v {
new_storage_size += value.len() as u64;
child::put_raw(&info.trie_id[..], &blake2_256(&k), &value[..]);
new_info.storage_size += value.len() as u64;
child::put_raw(&new_info.trie_id[..], &blake2_256(&k), &value[..]);
} else {
child::kill(&info.trie_id[..], &blake2_256(&k));
child::kill(&new_info.trie_id[..], &blake2_256(&k));
}
}

if new_storage_size != info.storage_size {
info.storage_size = new_storage_size;
<AccountInfoOf<T>>::insert(&address, info);
if old_info
.map(|old_info| old_info != new_info)
.unwrap_or(true)
{
<ContractInfoOf<T>>::insert(&address, ContractInfo::Alive(new_info));
}
}
}
Expand All @@ -140,9 +175,7 @@ pub struct OverlayAccountDb<'a, T: Trait + 'a> {
underlying: &'a AccountDb<T>,
}
impl<'a, T: Trait> OverlayAccountDb<'a, T> {
pub fn new(
underlying: &'a AccountDb<T>,
) -> OverlayAccountDb<'a, T> {
pub fn new(underlying: &'a AccountDb<T>) -> OverlayAccountDb<'a, T> {
OverlayAccountDb {
local: RefCell::new(ChangeSet::new()),
underlying,
Expand All @@ -166,12 +199,31 @@ impl<'a, T: Trait> OverlayAccountDb<'a, T> {
.insert(location, value);
}

pub fn set_code(&mut self, account: &T::AccountId, code: Option<CodeHash<T>>) {
/// Return an error if contract already exists (either if it is alive or tombstone)
pub fn create_contract(
&mut self,
account: &T::AccountId,
code_hash: CodeHash<T>,
) -> Result<(), &'static str> {
if self.contract_exists(account) {
return Err("Alive contract or tombstone already exists");
}

let mut local = self.local.borrow_mut();
let contract = local.entry(account.clone()).or_insert_with(|| Default::default());

contract.code_hash = Some(code_hash);
contract.rent_allowance = Some(<BalanceOf<T>>::zero());

Ok(())
}
/// Assume contract exists
pub fn set_rent_allowance(&mut self, account: &T::AccountId, rent_allowance: BalanceOf<T>) {
self.local
.borrow_mut()
.entry(account.clone())
.or_insert(Default::default())
.code = Some(code);
.rent_allowance = Some(rent_allowance);
}
pub fn set_balance(&mut self, account: &T::AccountId, balance: BalanceOf<T>) {
self.local
Expand All @@ -191,12 +243,26 @@ impl<'a, T: Trait> AccountDb<T> for OverlayAccountDb<'a, T> {
.cloned()
.unwrap_or_else(|| self.underlying.get_storage(account, trie_id, location))
}
fn get_code(&self, account: &T::AccountId) -> Option<CodeHash<T>> {
fn get_code_hash(&self, account: &T::AccountId) -> Option<CodeHash<T>> {
self.local
.borrow()
.get(account)
.and_then(|changes| changes.code_hash)
.or_else(|| self.underlying.get_code_hash(account))
}
fn get_rent_allowance(&self, account: &T::AccountId) -> Option<BalanceOf<T>> {
self.local
.borrow()
.get(account)
.and_then(|changes| changes.rent_allowance)
.or_else(|| self.underlying.get_rent_allowance(account))
}
fn contract_exists(&self, account: &T::AccountId) -> bool {
self.local
.borrow()
.get(account)
.and_then(|a| a.code.clone())
.unwrap_or_else(|| self.underlying.get_code(account))
.map(|a| a.code_hash.is_some())
.unwrap_or_else(|| self.underlying.contract_exists(account))
}
fn get_balance(&self, account: &T::AccountId) -> BalanceOf<T> {
self.local
Expand All @@ -212,12 +278,9 @@ impl<'a, T: Trait> AccountDb<T> for OverlayAccountDb<'a, T> {
match local.entry(address) {
Entry::Occupied(e) => {
let mut value = e.into_mut();
if changed.balance.is_some() {
value.balance = changed.balance;
}
if changed.code.is_some() {
value.code = changed.code;
}
value.balance = changed.balance.or(value.balance);
value.code_hash = changed.code_hash.or(value.code_hash);
value.rent_allowance = changed.rent_allowance.or(value.rent_allowance);
value.storage.extend(changed.storage.into_iter());
}
Entry::Vacant(e) => {
Expand Down
Loading