Skip to content

Commit 38e222d

Browse files
committed
Implement .terminate_contract using precompile
1 parent ab8e795 commit 38e222d

File tree

8 files changed

+47
-33
lines changed

8 files changed

+47
-33
lines changed

crates/engine/src/ext.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl Engine {
191191
/// This function never returns. Either the termination was successful and the
192192
/// execution of the destroyed contract is halted. Or it failed during the
193193
/// termination which is considered fatal.
194-
pub fn terminate(&mut self, beneficiary: Address) -> ! {
194+
pub fn terminate(&mut self, beneficiary: Address) -> Result<(), Error> {
195195
// Send the remaining balance to the beneficiary
196196
let contract = self.get_callee();
197197
let all = self
@@ -201,11 +201,7 @@ impl Engine {
201201
self.transfer(beneficiary, value)
202202
.unwrap_or_else(|err| panic!("transfer did not work: {err:?}"));
203203

204-
// Encode the result of the termination and panic with it.
205-
// This enables testing for the proper result and makes sure this
206-
// method returns `Never`.
207-
let res = (all, beneficiary);
208-
panic_any(scale::Encode::encode(&res));
204+
Ok(())
209205
}
210206

211207
/// Returns the address of the caller.

crates/env/src/api.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -436,8 +436,7 @@ where
436436
/// This function never returns. Either the termination was successful and the
437437
/// execution of the destroyed contract is halted. Or it failed during the termination
438438
/// which is considered fatal and results in a trap and rollback.
439-
#[cfg(feature = "unstable-hostfn")]
440-
pub fn terminate_contract(beneficiary: Address) -> ! {
439+
pub fn terminate_contract(beneficiary: Address) -> Result<()> {
441440
<EnvInstance as OnInstance>::on_instance(|instance| {
442441
TypedEnvBackend::terminate_contract(instance, beneficiary)
443442
})

crates/env/src/backend.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,7 @@ pub trait TypedEnvBackend: EnvBackend {
368368
/// # Note
369369
///
370370
/// For more details visit: [`terminate_contract`][`crate::terminate_contract`]
371-
#[cfg(feature = "unstable-hostfn")]
372-
fn terminate_contract(&mut self, beneficiary: Address) -> !;
371+
fn terminate_contract(&mut self, beneficiary: Address) -> Result<()>;
373372

374373
/// Transfers value from the contract to the destination account ID.
375374
///

crates/env/src/engine/off_chain/impls.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -747,9 +747,8 @@ impl TypedEnvBackend for EnvInstance {
747747
))))
748748
}
749749

750-
#[cfg(feature = "unstable-hostfn")]
751-
fn terminate_contract(&mut self, beneficiary: Address) -> ! {
752-
self.engine.terminate(beneficiary)
750+
fn terminate_contract(&mut self, beneficiary: Address) -> Result<()> {
751+
self.engine.terminate(beneficiary).map_err(Into::into)
753752
}
754753

755754
fn transfer<E>(&mut self, destination: Address, value: U256) -> Result<()>

crates/env/src/engine/on_chain/pallet_revive.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,14 +1213,29 @@ impl TypedEnvBackend for EnvInstance {
12131213
)
12141214
}
12151215

1216-
#[cfg(feature = "unstable-hostfn")]
1217-
fn terminate_contract(&mut self, beneficiary: Address) -> ! {
1218-
let buffer: &mut [u8; 20] = self.scoped_buffer().take_encoded(&beneficiary)
1219-
[0..20]
1220-
.as_mut()
1221-
.try_into()
1222-
.unwrap();
1223-
ext::terminate(buffer);
1216+
fn terminate_contract(&mut self, beneficiary: Address) -> Result<()> {
1217+
let mut scope = self.scoped_buffer();
1218+
let addr = beneficiary.as_fixed_bytes();
1219+
1220+
const ADDR: [u8; 20] =
1221+
hex_literal::hex!("0000000000000000000000000000000000000900");
1222+
1223+
let mut input = [0u8; 32 + 4];
1224+
let sel = const { solidity_selector("terminate(address)") };
1225+
input[..4].copy_from_slice(&sel[..4]);
1226+
input[16..36].copy_from_slice(&addr[..]);
1227+
1228+
let _ = ext::call(
1229+
CallFlags::empty(),
1230+
&ADDR,
1231+
u64::MAX, // `ref_time` to devote for execution. `u64::MAX` = all
1232+
u64::MAX, // `proof_size` to devote for execution. `u64::MAX` = all
1233+
&[u8::MAX; 32], // No deposit limit.
1234+
&U256::zero().to_little_endian(), // Value transferred to the contract.
1235+
&input[..],
1236+
None,
1237+
);
1238+
Ok(())
12241239
}
12251240

12261241
fn transfer<E>(&mut self, destination: Address, value: U256) -> Result<()>

crates/ink/src/env_access.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -762,8 +762,7 @@ where
762762
/// # Note
763763
///
764764
/// For more details visit: [`ink_env::terminate_contract`]
765-
#[cfg(feature = "unstable-hostfn")]
766-
pub fn terminate_contract(self, beneficiary: Address) -> ! {
765+
pub fn terminate_contract(self, beneficiary: Address) -> Result<()> {
767766
ink_env::terminate_contract(beneficiary)
768767
}
769768

integration-tests/public/contract-terminate/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2024"
66
publish = false
77

88
[dependencies]
9-
ink = { path = "../../../crates/ink", default-features = false, features = ["unstable-hostfn"] }
9+
ink = { path = "../../../crates/ink", default-features = false }
1010

1111
[dev-dependencies]
1212
ink_e2e = { path = "../../../crates/e2e" }

integration-tests/public/contract-terminate/lib.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@ pub mod just_terminates {
2020
/// Terminates with the caller as beneficiary.
2121
#[ink(message)]
2222
pub fn terminate_me(&mut self) {
23-
self.env().terminate_contract(self.env().caller());
23+
self.env()
24+
.terminate_contract(self.env().caller())
25+
.expect("must succeed");
26+
}
27+
28+
#[ink(message)]
29+
pub fn get(&self) -> u64 {
30+
13
2431
}
2532
}
2633

@@ -77,17 +84,17 @@ pub mod just_terminates {
7784
.await
7885
.expect("terminate_me messages failed");
7986

80-
assert!(
81-
call_res.return_data().is_empty(),
82-
"Terminated contract never returns"
83-
);
84-
8587
// then
8688
assert!(call_res.contains_event("System", "KilledAccount"));
8789
assert!(call_res.contains_event("Balances", "Withdraw"));
88-
// todo this event below no longer exists, but we could try getting
89-
// info for the contract and asserting that it fails.
90-
// assert!(call_res.contains_event("Revive", "Terminated"));
90+
91+
let get = call_builder.get();
92+
let call_res = client
93+
.call(&ink_e2e::alice(), &get)
94+
.submit()
95+
.await
96+
.expect("get message failed");
97+
assert!(call_res.dry_run.exec_result.result.unwrap().data.is_empty());
9198

9299
Ok(())
93100
}

0 commit comments

Comments
 (0)