Skip to content

Commit

Permalink
Merge pull request #91 from CoinFabrik/66-issue-20-terminate_contract
Browse files Browse the repository at this point in the history
66 issue 20 terminate contract
  • Loading branch information
aon authored Sep 20, 2023
2 parents c74cce7 + fd825c5 commit f89ef15
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
32 changes: 32 additions & 0 deletions test-cases/terminate-contract/Cargo.toml
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
36 changes: 36 additions & 0 deletions test-cases/terminate-contract/README.md
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.
110 changes: 110 additions & 0 deletions test-cases/terminate-contract/lib.rs
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(())
}
}
}

0 comments on commit f89ef15

Please sign in to comment.