Skip to content

Commit 3071e5f

Browse files
committed
optionally burn
1 parent c474906 commit 3071e5f

File tree

9 files changed

+47
-31
lines changed

9 files changed

+47
-31
lines changed

fvm/src/kernel/default.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use filecoin_proofs_api::{self as proofs, ProverId, PublicReplicaInfo, SectorId}
1111
use fvm_ipld_blockstore::Blockstore;
1212
use fvm_ipld_encoding::{bytes_32, IPLD_RAW};
1313
use fvm_shared::address::Payload;
14-
use fvm_shared::bigint::Zero;
1514
use fvm_shared::chainid::ChainID;
1615
use fvm_shared::consensus::ConsensusFault;
1716
use fvm_shared::crypto::signature;
@@ -224,7 +223,7 @@ where
224223
t.record(Ok(self.get_self()?.map(|a| a.balance).unwrap_or_default()))
225224
}
226225

227-
fn self_destruct(&mut self) -> Result<()> {
226+
fn self_destruct(&mut self, burn_unspent: bool) -> Result<()> {
228227
if self.read_only {
229228
return Err(syscall_error!(ReadOnly; "cannot self-destruct when read-only").into());
230229
}
@@ -243,7 +242,12 @@ where
243242
// 2. If we ever decide to allow code on method 0, allowing transfers here would be
244243
// unfortunate.
245244
let balance = self.current_balance()?;
246-
if balance != TokenAmount::zero() {
245+
if !balance.is_zero() {
246+
if !burn_unspent {
247+
return Err(
248+
syscall_error!(IllegalOperation; "self-destruct with unspent funds").into(),
249+
);
250+
}
247251
self.call_manager
248252
.transfer(self.actor_id, BURNT_FUNDS_ACTOR_ID, &balance)
249253
.or_fatal()?;

fvm/src/kernel/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ pub trait SelfOps: IpldBlockOps {
176176
/// The balance of the receiver.
177177
fn current_balance(&self) -> Result<TokenAmount>;
178178

179-
/// Deletes the executing actor from the state tree, burning any remaining balance.
180-
fn self_destruct(&mut self) -> Result<()>;
179+
/// Deletes the executing actor from the state tree, burning any remaining balance if requested.
180+
fn self_destruct(&mut self, burn_unspent: bool) -> Result<()>;
181181
}
182182

183183
/// Actors operations whose scope of action is actors other than the calling

fvm/src/syscalls/sself.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ pub fn current_balance(context: Context<'_, impl Kernel>) -> Result<sys::TokenAm
3333
.or_fatal()
3434
}
3535

36-
pub fn self_destruct(context: Context<'_, impl Kernel>) -> Result<()> {
37-
context.kernel.self_destruct()?;
36+
pub fn self_destruct(context: Context<'_, impl Kernel>, burn_unspent: u32) -> Result<()> {
37+
context.kernel.self_destruct(burn_unspent > 0)?;
3838
Ok(())
3939
}

sdk/src/error.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ pub enum StateUpdateError {
1515
}
1616

1717
#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)]
18-
#[error("current execution context is read-only")]
19-
pub struct ActorDeleteError;
18+
pub enum ActorDeleteError {
19+
#[error("cannot self-destruct when read-only")]
20+
ReadOnly,
21+
#[error("actor did not request unspent funds to be burnt")]
22+
UnspentFunds,
23+
}
2024

2125
#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)]
2226
pub enum EpochBoundsError {

sdk/src/sself.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ pub fn current_balance() -> TokenAmount {
5353
}
5454

5555
/// Destroys the calling actor, burning any remaining balance.
56-
pub fn self_destruct() -> Result<(), ActorDeleteError> {
56+
pub fn self_destruct(burn_funds: bool) -> Result<(), ActorDeleteError> {
5757
unsafe {
58-
sys::sself::self_destruct().map_err(|e| match e {
59-
ErrorNumber::ReadOnly => ActorDeleteError,
58+
sys::sself::self_destruct(burn_funds).map_err(|e| match e {
59+
ErrorNumber::IllegalOperation => ActorDeleteError::UnspentFunds,
60+
ErrorNumber::ReadOnly => ActorDeleteError::ReadOnly,
6061
_ => panic!("unexpected error from `self::self_destruct` syscall: {}", e),
6162
})
6263
}

sdk/src/sys/sself.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,19 @@ super::fvm_syscalls! {
4949
/// None.
5050
pub fn current_balance() -> Result<super::TokenAmount>;
5151

52-
/// Destroys the calling actor, sending its current balance
53-
/// to the supplied address, which cannot be itself.
54-
///
55-
/// Fails when calling actor has a non zero balance and the beneficiary doesn't
56-
/// exist or is the actor being deleted.
52+
/// Destroys the calling actor. If `burn_funds` is true, any unspent balance will be burnt
53+
/// (destroyed). Otherwise, if `burnt_funds` is false and there are unspent funds, this syscall
54+
/// will fail.
5755
///
5856
/// # Arguments
5957
///
60-
/// - `addr_off` and `addr_len` specify the location and length of beneficiary's address in wasm
61-
/// memory.
58+
/// - `burn_funds` must be true to delete an actor with unspent funds.
6259
///
6360
/// # Errors
6461
///
65-
/// | Error | Reason |
66-
/// |---------------|-------------------------------------------|
67-
/// | [`ReadOnly`] | the actor is executing in read-only mode |
68-
pub fn self_destruct() -> Result<()>;
62+
/// | Error | Reason |
63+
/// |-----------------------|-------------------------------------------|
64+
/// | [`IllegalOperation`] | the actor has unspent funds |
65+
/// | [`ReadOnly`] | the actor is executing in read-only mode |
66+
pub fn self_destruct(burn_funds: bool) -> Result<()>;
6967
}

testing/conformance/src/vm.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -542,8 +542,8 @@ where
542542
self.0.current_balance()
543543
}
544544

545-
fn self_destruct(&mut self) -> Result<()> {
546-
self.0.self_destruct()
545+
fn self_destruct(&mut self, burn_unspent: bool) -> Result<()> {
546+
self.0.self_destruct(burn_unspent)
547547
}
548548
}
549549

testing/test_actors/actors/fil-readonly-actor/src/actor.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use fvm_shared::econ::TokenAmount;
1010
use fvm_shared::event::{Entry, Flags};
1111
use fvm_shared::sys::SendFlags;
1212
use fvm_shared::METHOD_SEND;
13-
use sdk::error::StateUpdateError;
13+
use sdk::error::{ActorDeleteError, StateUpdateError};
1414
use sdk::sys::ErrorNumber;
1515

1616
/// Placeholder invoke for testing
@@ -159,7 +159,10 @@ fn invoke_method(blk: u32, method: u64) -> u32 {
159159
assert_eq!(err, ErrorNumber::ReadOnly);
160160

161161
// Should not be able to delete self.
162-
sdk::sself::self_destruct().expect_err("deleted self while read-only");
162+
assert_eq!(
163+
sdk::sself::self_destruct(true).unwrap_err(),
164+
ActorDeleteError::ReadOnly
165+
);
163166
}
164167
4 => {
165168
assert!(sdk::vm::read_only());

testing/test_actors/actors/fil-sself-actor/src/actor.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use cid::Cid;
55
use fvm_ipld_encoding::{to_vec, DAG_CBOR};
66
use fvm_sdk as sdk;
77
use fvm_shared::econ::TokenAmount;
8-
use sdk::error::{StateReadError, StateUpdateError};
8+
use sdk::error::{ActorDeleteError, StateReadError, StateUpdateError};
99

1010
#[no_mangle]
1111
pub fn invoke(_: u32) -> u32 {
@@ -30,8 +30,14 @@ pub fn invoke(_: u32) -> u32 {
3030
let balance = sdk::sself::current_balance();
3131
assert_eq!(TokenAmount::from_nano(1_000_000), balance);
3232

33-
// now lets destroy the actor, burning the funds.
34-
sdk::sself::self_destruct().unwrap();
33+
// Now destroy the actor without burning funds. This should fail because we have unspent funds.
34+
assert_eq!(
35+
sdk::sself::self_destruct(false).unwrap_err(),
36+
ActorDeleteError::UnspentFunds
37+
);
38+
39+
// Now lets destroy the actor, burning the funds.
40+
sdk::sself::self_destruct(true).unwrap();
3541

3642
// test that root/set_root/self_destruct fail when the actor has been deleted
3743
// and balance is 0
@@ -43,7 +49,7 @@ pub fn invoke(_: u32) -> u32 {
4349
assert_eq!(TokenAmount::from_nano(0), sdk::sself::current_balance());
4450

4551
// calling destroy on an already destroyed actor should succeed (no-op)
46-
sdk::sself::self_destruct().expect("deleting an already deleted actor should succeed");
52+
sdk::sself::self_destruct(false).expect("deleting an already deleted actor should succeed");
4753

4854
#[cfg(coverage)]
4955
sdk::debug::store_artifact("sself_actor.profraw", minicov::capture_coverage());

0 commit comments

Comments
 (0)