-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #91 from CoinFabrik/66-issue-20-terminate_contract
66 issue 20 terminate contract
- Loading branch information
Showing
3 changed files
with
178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
[package] | ||
name = "terminate-contract" | ||
version = "0.1.0" | ||
edition = "2021" | ||
authors = ["[your_name] <[your_email]>"] | ||
|
||
[lib] | ||
path = "lib.rs" | ||
|
||
[features] | ||
default = ["std"] | ||
std = [ | ||
"ink/std", | ||
"scale/std", | ||
"scale-info/std", | ||
] | ||
ink-as-dependency = [] | ||
e2e-tests = [] | ||
|
||
[dependencies] | ||
ink = { version = "4.2.1", default-features = false } | ||
scale = { package = "parity-scale-codec", version = "=3.6.5", default-features = false, features = ["derive"] } | ||
scale-info = { version = "2.6", default-features = false, features = ["derive"], optional = true } | ||
|
||
[dev-dependencies] | ||
ink_e2e = "4.2.1" | ||
|
||
[profile.dev] | ||
overflow-checks = false | ||
|
||
[profile.release] | ||
overflow-checks = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Function `terminate_contract` | ||
|
||
```rust | ||
pub fn terminate_contract<E>(beneficiary: E::AccountId) -> ! | ||
``` | ||
|
||
## Description | ||
|
||
The `terminate_contract` function is designed to end the existence of the currently executed smart contract. By invoking this method, the calling account is removed, and all its remaining balance is transferred to the given beneficiary. | ||
This function never returns. Either the termination was successful, and the execution of the destroyed contract is halted, or it failed during the termination. A failure during termination is considered fatal and results in a trap and rollback. | ||
|
||
## Related ink! functions | ||
|
||
- [`terminate_contract`](https://paritytech.github.io/ink/ink_env/fn.terminate_contract.html) | ||
|
||
## Test case | ||
|
||
The integration test and end-to-end test both verify the termination functionality of the contract. Both tests ensure that after the `terminate_contract` function is called, the contract's balance is transferred to the caller and the contract's execution is terminated. Both tests passed successfully, demonstrating the correct functionality of the `terminate_contract` method. | ||
|
||
## Comparison Integration vs E2E | ||
|
||
Both the integration test and the end-to-end test for the terminate method passed successfully. However, it's worth noting that during the execution of the integration test, a Rust panic is observed: | ||
|
||
```rust | ||
thread 'terminate_contract::tests::terminate_works' panicked at 'Box<dyn Any>' | ||
``` | ||
This panic is expected behaviour and enables both testing for the proper result and makes sure this method returns `Never`. | ||
| \# | Test | Integration | E2E | | ||
| --- | -------------------------------------------- | :---------: | :-: | | ||
| 1 | Attempts to terminate the executing contract | ✅ | ✅ | | ||
## Result | ||
- This functionality is implemented in both environments and works as expected. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
#![cfg_attr(not(feature = "std"), no_std, no_main)] | ||
|
||
#[ink::contract] | ||
mod terminate_contract { | ||
|
||
#[ink(storage)] | ||
pub struct TerminateContract {} | ||
|
||
impl TerminateContract { | ||
#[ink(constructor, payable)] | ||
pub fn new() -> Self { | ||
Self {} | ||
} | ||
|
||
#[ink(message)] | ||
pub fn terminate(&self) { | ||
self.env().terminate_contract(self.env().caller()); | ||
} | ||
} | ||
|
||
impl Default for TerminateContract { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use ink::env::{ | ||
test::{get_account_balance, set_account_balance, set_callee, set_caller}, | ||
DefaultEnvironment, | ||
}; | ||
use std::panic::catch_unwind; | ||
|
||
#[ink::test] | ||
fn terminate_works() { | ||
// Given | ||
let caller_account_id = AccountId::from([0x01; 32]); | ||
let callee_account_id = AccountId::from([0x02; 32]); | ||
set_caller::<DefaultEnvironment>(caller_account_id); | ||
set_callee::<DefaultEnvironment>(callee_account_id); | ||
set_account_balance::<DefaultEnvironment>(caller_account_id, 0); | ||
set_account_balance::<DefaultEnvironment>(callee_account_id, 100); | ||
|
||
let contract = TerminateContract::new(); | ||
|
||
// When | ||
// Terminate contract panics after termination | ||
let result = catch_unwind(|| { | ||
contract.terminate(); | ||
}); | ||
|
||
// Then | ||
let caller_balance = get_account_balance::<DefaultEnvironment>(caller_account_id) | ||
.expect("Failed to get caller balance"); | ||
let callee_balance = get_account_balance::<DefaultEnvironment>(callee_account_id) | ||
.expect("Failed to get callee balance"); | ||
assert!(result.is_err()); | ||
assert_eq!(caller_balance, 100); | ||
assert_eq!(callee_balance, 0); | ||
} | ||
} | ||
|
||
#[cfg(all(test, feature = "e2e-tests"))] | ||
mod e2e_tests { | ||
use ink_e2e::build_message; | ||
|
||
use super::*; | ||
|
||
type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>; | ||
|
||
#[ink_e2e::test] | ||
async fn terminate_works(mut client: ink_e2e::Client<C, E>) -> E2EResult<()> { | ||
// Given | ||
let constructor = TerminateContractRef::new(); | ||
|
||
let contract_acc_id = client | ||
.instantiate( | ||
"terminate_contract", | ||
&ink_e2e::bob(), | ||
constructor, | ||
100, | ||
None, | ||
) | ||
.await | ||
.expect("instantiate failed") | ||
.account_id; | ||
|
||
// When | ||
let terminate_call = build_message::<TerminateContractRef>(contract_acc_id) | ||
.call(|contract| contract.terminate()); | ||
|
||
let terminate_call_res = client | ||
.call(&ink_e2e::bob(), terminate_call, 0, None) | ||
.await | ||
.expect("split_profit failed"); | ||
|
||
// Then | ||
let callee_balance = client | ||
.balance(contract_acc_id) | ||
.await | ||
.expect("balance failed"); | ||
|
||
assert!(!terminate_call_res.dry_run.is_err()); | ||
assert_eq!(callee_balance, 0); | ||
Ok(()) | ||
} | ||
} | ||
} |