@@ -11,7 +11,6 @@ use filecoin_proofs_api::{self as proofs, ProverId, PublicReplicaInfo, SectorId}
11
11
use fvm_ipld_blockstore:: Blockstore ;
12
12
use fvm_ipld_encoding:: { bytes_32, IPLD_RAW } ;
13
13
use fvm_shared:: address:: Payload ;
14
- use fvm_shared:: bigint:: Zero ;
15
14
use fvm_shared:: chainid:: ChainID ;
16
15
use fvm_shared:: consensus:: ConsensusFault ;
17
16
use fvm_shared:: crypto:: signature;
@@ -36,7 +35,7 @@ use crate::call_manager::{CallManager, InvocationResult, NO_DATA_BLOCK_ID};
36
35
use crate :: externs:: { Chain , Consensus , Rand } ;
37
36
use crate :: gas:: GasTimer ;
38
37
use crate :: init_actor:: INIT_ACTOR_ID ;
39
- use crate :: machine:: { MachineContext , NetworkConfig } ;
38
+ use crate :: machine:: { MachineContext , NetworkConfig , BURNT_FUNDS_ACTOR_ID } ;
40
39
use crate :: state_tree:: ActorState ;
41
40
use crate :: { ipld, syscall_error} ;
42
41
@@ -244,35 +243,37 @@ where
244
243
t. record ( Ok ( self . get_self ( ) ?. map ( |a| a. balance ) . unwrap_or_default ( ) ) )
245
244
}
246
245
247
- fn self_destruct ( & mut self , beneficiary : & Address ) -> Result < ( ) > {
246
+ fn self_destruct ( & mut self , burn_unspent : bool ) -> Result < ( ) > {
248
247
if self . read_only {
249
248
return Err ( syscall_error ! ( ReadOnly ; "cannot self-destruct when read-only" ) . into ( ) ) ;
250
249
}
251
250
252
- // Idempotentcy : If the actor doesn't exist, this won't actually do anything. The current
251
+ // Idempotent : If the actor doesn't exist, this won't actually do anything. The current
253
252
// balance will be zero, and `delete_actor_id` will be a no-op.
254
253
let t = self
255
254
. call_manager
256
255
. charge_gas ( self . call_manager . price_list ( ) . on_delete_actor ( ) ) ?;
257
256
257
+ // If there are remaining funds, burn them. We do this instead of letting the user to
258
+ // specify the beneficiary as:
259
+ //
260
+ // 1. This lets the user handle transfer failure cases themselves. The only way _this_ can
261
+ // fail is for the caller to run out of gas.
262
+ // 2. If we ever decide to allow code on method 0, allowing transfers here would be
263
+ // unfortunate.
258
264
let balance = self . current_balance ( ) ?;
259
- if balance != TokenAmount :: zero ( ) {
260
- // Starting from network version v7, the runtime checks if the beneficiary
261
- // exists; if missing, it fails the self destruct.
262
- //
263
- // In FVM we check unconditionally, since we only support nv13+.
264
- let beneficiary_id = self . resolve_address ( beneficiary) ?;
265
-
266
- if beneficiary_id == self . actor_id {
267
- return Err ( syscall_error ! ( Forbidden , "benefactor cannot be beneficiary" ) . into ( ) ) ;
265
+ if !balance. is_zero ( ) {
266
+ if !burn_unspent {
267
+ return Err (
268
+ syscall_error ! ( IllegalOperation ; "self-destruct with unspent funds" ) . into ( ) ,
269
+ ) ;
268
270
}
269
-
270
- // Transfer the entirety of funds to beneficiary.
271
271
self . call_manager
272
- . transfer ( self . actor_id , beneficiary_id, & balance) ?;
272
+ . transfer ( self . actor_id , BURNT_FUNDS_ACTOR_ID , & balance)
273
+ . or_fatal ( ) ?;
273
274
}
274
275
275
- // Delete the executing actor
276
+ // Delete the executing actor.
276
277
t. record ( self . call_manager . delete_actor ( self . actor_id ) )
277
278
}
278
279
}
0 commit comments