Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

contracts: New contract events + unconfusions #4685

Merged
merged 9 commits into from
Jan 21, 2020
2 changes: 1 addition & 1 deletion frame/contracts/src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ where
fn deposit_event(&mut self, topics: Vec<T::Hash>, data: Vec<u8>) {
self.ctx.deferred.push(DeferredAction::DepositEvent {
topics,
event: RawEvent::Contract(self.ctx.self_account.clone(), data),
event: RawEvent::ContractExecution(self.ctx.self_account.clone(), data),
});
}

Expand Down
30 changes: 27 additions & 3 deletions frame/contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,12 @@ impl<T: Trait> Module<T> {
rent_allowance,
delta,
} => {
let _result = Self::restore_to(donor, dest, code_hash, rent_allowance, delta);
let result = Self::restore_to(
donor.clone(), dest.clone(), code_hash.clone(), rent_allowance.clone(), delta
);
Self::deposit_event(
RawEvent::Restored(donor, dest, code_hash, rent_allowance, result.is_ok())
);
}
}
});
Expand Down Expand Up @@ -896,6 +901,25 @@ decl_event! {
/// Contract deployed by address at the specified address.
Instantiated(AccountId, AccountId),

/// Contract has been evicted and is now in tombstone state.
///
/// # Params
///
/// - `contract`: `AccountId`: The account ID of the evicted contract.
/// - `tombstone`: `bool`: True if the evicted contract left behind a tombstone.
Evicted(AccountId, bool),

/// Restoration for a contract has been initiated.
///
/// # Params
///
/// - `donor`: `AccountId`: Account ID of the restoring contract
/// - `dest`: `AccountId`: Account ID of the restored contract
/// - `code_hash`: `Hash`: Code hash of the restored contract
/// - `rent_allowance: `Balance`: Rent allowance of the restored contract
/// - `success`: `bool`: True if the restoration was successful
Restored(AccountId, AccountId, Hash, Balance, bool),

/// Code with the specified hash has been stored.
CodeStored(Hash),

Expand All @@ -906,8 +930,8 @@ decl_event! {
/// successful execution or not.
Dispatched(AccountId, bool),

/// An event from contract of account.
Contract(AccountId, Vec<u8>),
/// An event deposited upon execution of a contract from the account.
ContractExecution(AccountId, Vec<u8>),
}
}

Expand Down
6 changes: 5 additions & 1 deletion frame/contracts/src/rent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

use crate::{BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo, Trait, AliveContractInfo};
use crate::{Module, RawEvent, BalanceOf, ContractInfo, ContractInfoOf, TombstoneContractInfo,
Trait, AliveContractInfo};
use sp_runtime::traits::{Bounded, CheckedDiv, CheckedMul, Saturating, Zero,
SaturatedConversion};
use frame_support::traits::{Currency, ExistenceRequirement, Get, WithdrawReason, OnUnbalanced};
Expand Down Expand Up @@ -101,6 +102,7 @@ fn try_evict_or_and_pay_rent<T: Trait>(
// The contract cannot afford to leave a tombstone, so remove the contract info altogether.
<ContractInfoOf<T>>::remove(account);
child::kill_storage(&contract.trie_id, contract.child_trie_unique_id());
<Module<T>>::deposit_event(RawEvent::Evicted(account.clone(), false));
return (RentOutcome::Evicted, None);
}

Expand Down Expand Up @@ -160,6 +162,8 @@ fn try_evict_or_and_pay_rent<T: Trait>(

child::kill_storage(&contract.trie_id, contract.child_trie_unique_id());

<Module<T>>::deposit_event(RawEvent::Evicted(account.clone(), true));

return (RentOutcome::Evicted, Some(tombstone_info));
}

Expand Down
98 changes: 93 additions & 5 deletions frame/contracts/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ fn instantiate_and_call_and_deposit_event() {
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Contract(BOB, vec![1, 2, 3, 4])),
event: MetaEvent::contract(RawEvent::ContractExecution(BOB, vec![1, 2, 3, 4])),
topics: vec![],
},
EventRecord {
Expand Down Expand Up @@ -650,7 +650,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() {
100_000,
vec![],
),
"during execution"
"contract trapped during execution"
);
assert_eq!(System::events(), vec![
EventRecord {
Expand Down Expand Up @@ -1139,8 +1139,16 @@ fn call_removed_contract() {
Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
"contract has been evicted"
);
// Calling a contract that is about to evict shall emit an event.
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Evicted(BOB, true)),
topics: vec![],
},
]);

// Subsequent contract calls should also fail.
// Subsequent contract calls should also fail.
assert_err!(
Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
"contract has been evicted"
Expand Down Expand Up @@ -1367,13 +1375,25 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
// Advance 4 blocks, to the 5th.
initialize_block(5);

/// Preserve `BOB`'s code hash for later introspection.
let bob_code_hash = ContractInfoOf::<Test>::get(BOB).unwrap()
.get_alive().unwrap().code_hash;
// Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0
// we expect that it will get removed leaving tombstone.
assert_err!(
Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()),
"contract has been evicted"
);
assert!(ContractInfoOf::<Test>::get(BOB).unwrap().get_tombstone().is_some());
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(
RawEvent::Evicted(BOB.clone(), true)
),
topics: vec![],
},
]);

/// Create another account with the address `DJANGO` with `CODE_RESTORATION`.
///
Expand Down Expand Up @@ -1416,6 +1436,60 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
assert_eq!(django_contract.storage_size, 16);
assert_eq!(django_contract.trie_id, django_trie_id);
assert_eq!(django_contract.deduct_block, System::block_number());
match (test_different_storage, test_restore_to_with_dirty_storage) {
(true, false) => {
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(
RawEvent::Restored(DJANGO, BOB, bob_code_hash, 50, false)
),
topics: vec![],
},
]);
}
(_, true) => {
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Evicted(BOB, true)),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(CHARLIE, 1_000_000)),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(DJANGO, 30_000)),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Instantiated(CHARLIE, DJANGO)),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(RawEvent::Restored(
DJANGO,
BOB,
bob_code_hash,
50,
false,
)),
topics: vec![],
},
]);
}
_ => unreachable!(),
}
} else {
// Here we expect that the restoration is succeeded. Check that the restoration
// contract `DJANGO` ceased to exist and that `BOB` returned back.
Expand All @@ -1427,6 +1501,20 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage:
assert_eq!(bob_contract.trie_id, django_trie_id);
assert_eq!(bob_contract.deduct_block, System::block_number());
assert!(ContractInfoOf::<Test>::get(DJANGO).is_none());
assert_eq!(System::events(), vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::balances(balances::RawEvent::ReapedAccount(DJANGO, 0)),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: MetaEvent::contract(
RawEvent::Restored(DJANGO, BOB, bob_contract.code_hash, 50, true)
),
topics: vec![],
},
]);
}
});
}
Expand Down Expand Up @@ -1533,7 +1621,7 @@ fn storage_max_value_limit() {
100_000,
Encode::encode(&(self::MaxValueSize::get() + 1)),
),
"during execution"
"contract trapped during execution"
);
});
}
Expand Down Expand Up @@ -2056,7 +2144,7 @@ fn cannot_self_destruct_while_live() {
100_000,
vec![0],
),
"during execution"
"contract trapped during execution"
);

// Check that BOB is still alive.
Expand Down
6 changes: 4 additions & 2 deletions frame/contracts/src/wasm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,9 @@ mod tests {
MockExt::default(),
&mut gas_meter
),
Err(ExecError { reason: DispatchError::Other("during execution"), buffer: _ })
Err(ExecError {
reason: DispatchError::Other("contract trapped during execution"), buffer: _
})
);
}

Expand Down Expand Up @@ -1472,7 +1474,7 @@ mod tests {
MockExt::default(),
&mut gas_meter
),
Err(ExecError { reason: DispatchError::Other("during execution"), buffer: _ })
Err(ExecError { reason: DispatchError::Other("contract trapped during execution"), buffer: _ })
);
}

Expand Down
2 changes: 1 addition & 1 deletion frame/contracts/src/wasm/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub(crate) fn to_execution_result<E: Ext>(
Err(ExecError { reason: "validation error".into(), buffer: runtime.scratch_buf }),
// Any other kind of a trap should result in a failure.
Err(sp_sandbox::Error::Execution) | Err(sp_sandbox::Error::OutOfBounds) =>
Err(ExecError { reason: "during execution".into(), buffer: runtime.scratch_buf }),
Err(ExecError { reason: "contract trapped during execution".into(), buffer: runtime.scratch_buf }),
}
}

Expand Down