Skip to content

Commit

Permalink
optionally burn
Browse files Browse the repository at this point in the history
  • Loading branch information
Stebalien committed Aug 11, 2023
1 parent f87895e commit 389263e
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 31 deletions.
10 changes: 7 additions & 3 deletions fvm/src/kernel/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use filecoin_proofs_api::{self as proofs, ProverId, PublicReplicaInfo, SectorId}
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::{bytes_32, IPLD_RAW};
use fvm_shared::address::Payload;
use fvm_shared::bigint::Zero;
use fvm_shared::chainid::ChainID;
use fvm_shared::consensus::ConsensusFault;
use fvm_shared::crypto::signature;
Expand Down Expand Up @@ -224,7 +223,7 @@ where
t.record(Ok(self.get_self()?.map(|a| a.balance).unwrap_or_default()))
}

fn self_destruct(&mut self) -> Result<()> {
fn self_destruct(&mut self, burn_unspent: bool) -> Result<()> {
if self.read_only {
return Err(syscall_error!(ReadOnly; "cannot self-destruct when read-only").into());
}
Expand All @@ -243,7 +242,12 @@ where
// 2. If we ever decide to allow code on method 0, allowing transfers here would be
// unfortunate.
let balance = self.current_balance()?;
if balance != TokenAmount::zero() {
if !balance.is_zero() {
if !burn_unspent {
return Err(
syscall_error!(IllegalOperation; "self-destruct with unspent funds").into(),
);
}
self.call_manager
.transfer(self.actor_id, BURNT_FUNDS_ACTOR_ID, &balance)
.or_fatal()?;
Expand Down
4 changes: 2 additions & 2 deletions fvm/src/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@ pub trait SelfOps: IpldBlockOps {
/// The balance of the receiver.
fn current_balance(&self) -> Result<TokenAmount>;

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

/// Actors operations whose scope of action is actors other than the calling
Expand Down
4 changes: 2 additions & 2 deletions fvm/src/syscalls/sself.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn current_balance(context: Context<'_, impl Kernel>) -> Result<sys::TokenAm
.or_fatal()
}

pub fn self_destruct(context: Context<'_, impl Kernel>) -> Result<()> {
context.kernel.self_destruct()?;
pub fn self_destruct(context: Context<'_, impl Kernel>, burn_unspent: u32) -> Result<()> {
context.kernel.self_destruct(burn_unspent > 0)?;
Ok(())
}
8 changes: 6 additions & 2 deletions sdk/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ pub enum StateUpdateError {
}

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

#[derive(Copy, Clone, Debug, Error, Eq, PartialEq)]
pub enum EpochBoundsError {
Expand Down
7 changes: 4 additions & 3 deletions sdk/src/sself.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,11 @@ pub fn current_balance() -> TokenAmount {
}

/// Destroys the calling actor, burning any remaining balance.
pub fn self_destruct() -> Result<(), ActorDeleteError> {
pub fn self_destruct(burn_funds: bool) -> Result<(), ActorDeleteError> {
unsafe {
sys::sself::self_destruct().map_err(|e| match e {
ErrorNumber::ReadOnly => ActorDeleteError,
sys::sself::self_destruct(burn_funds).map_err(|e| match e {
ErrorNumber::IllegalOperation => ActorDeleteError::UnspentFunds,
ErrorNumber::ReadOnly => ActorDeleteError::ReadOnly,
_ => panic!("unexpected error from `self::self_destruct` syscall: {}", e),
})
}
Expand Down
20 changes: 9 additions & 11 deletions sdk/src/sys/sself.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,19 @@ super::fvm_syscalls! {
/// None.
pub fn current_balance() -> Result<super::TokenAmount>;

/// Destroys the calling actor, sending its current balance
/// to the supplied address, which cannot be itself.
///
/// Fails when calling actor has a non zero balance and the beneficiary doesn't
/// exist or is the actor being deleted.
/// Destroys the calling actor. If `burn_funds` is true, any unspent balance will be burnt
/// (destroyed). Otherwise, if `burnt_funds` is false and there are unspent funds, this syscall
/// will fail.
///
/// # Arguments
///
/// - `addr_off` and `addr_len` specify the location and length of beneficiary's address in wasm
/// memory.
/// - `burn_funds` must be true to delete an actor with unspent funds.
///
/// # Errors
///
/// | Error | Reason |
/// |---------------|-------------------------------------------|
/// | [`ReadOnly`] | the actor is executing in read-only mode |
pub fn self_destruct() -> Result<()>;
/// | Error | Reason |
/// |-----------------------|-------------------------------------------|
/// | [`IllegalOperation`] | the actor has unspent funds |
/// | [`ReadOnly`] | the actor is executing in read-only mode |
pub fn self_destruct(burn_funds: bool) -> Result<()>;
}
4 changes: 2 additions & 2 deletions testing/conformance/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,8 +542,8 @@ where
self.0.current_balance()
}

fn self_destruct(&mut self) -> Result<()> {
self.0.self_destruct()
fn self_destruct(&mut self, burn_unspent: bool) -> Result<()> {
self.0.self_destruct(burn_unspent)
}
}

Expand Down
7 changes: 5 additions & 2 deletions testing/test_actors/actors/fil-readonly-actor/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use fvm_shared::econ::TokenAmount;
use fvm_shared::event::{Entry, Flags};
use fvm_shared::sys::SendFlags;
use fvm_shared::METHOD_SEND;
use sdk::error::StateUpdateError;
use sdk::error::{ActorDeleteError, StateUpdateError};
use sdk::sys::ErrorNumber;

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

// Should not be able to delete self.
sdk::sself::self_destruct().expect_err("deleted self while read-only");
assert_eq!(
sdk::sself::self_destruct(true).unwrap_err(),
ActorDeleteError::ReadOnly
);
}
4 => {
assert!(sdk::vm::read_only());
Expand Down
14 changes: 10 additions & 4 deletions testing/test_actors/actors/fil-sself-actor/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cid::Cid;
use fvm_ipld_encoding::{to_vec, DAG_CBOR};
use fvm_sdk as sdk;
use fvm_shared::econ::TokenAmount;
use sdk::error::{StateReadError, StateUpdateError};
use sdk::error::{ActorDeleteError, StateReadError, StateUpdateError};

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

// now lets destroy the actor, burning the funds.
sdk::sself::self_destruct().unwrap();
// Now destroy the actor without burning funds. This should fail because we have unspent funds.
assert_eq!(
sdk::sself::self_destruct(false).unwrap_err(),
ActorDeleteError::UnspentFunds
);

// Now lets destroy the actor, burning the funds.
sdk::sself::self_destruct(true).unwrap();

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

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

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

0 comments on commit 389263e

Please sign in to comment.