Skip to content

Commit c97afe6

Browse files
authored
CIP-16 Collect suicide logic at the end of transaction processing (#1748)
* Remove fresh collateral and read substate for collateral settlement Add comment remove unneccessary mutable Collect suicide logic at the end of transaction processing update after refactoring settlement Update suicide logic and refactor substate Several fixes add change log Burn when invalid suicide refund Refactor commit ownership Merge with master
1 parent 38b0765 commit c97afe6

File tree

8 files changed

+163
-157
lines changed

8 files changed

+163
-157
lines changed

changelogs/CHANGELOG-1.0.x.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Use `mining_type` to allow start CPU mining or disable mining manually.
99
- Fix bug where users need to restart node before they can use a newly created account to send transactions.
1010
- Fix code() return value for uninitialized contract.
1111
- Fix bug in kill_account after which the contract account is revived by simple transaction.
12-
- Fix the place of collateral refund for suicide contracts.
1312
- Fix missing StorageKey conversion from bytes of DepositList and VoteList.
1413

1514
## Incompatible changes
@@ -19,3 +18,4 @@ Use `mining_type` to allow start CPU mining or disable mining manually.
1918
- CIP-10 Base mining reward finalization.
2019
- CIP-12 Allow non-zero collateral contract to be killed.
2120
- CIP-13 Use Big-Endian MPT Keys.
21+
- CIP-16 Collect suicide logic at the end of transaction processing

core/src/executive/context.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::{
1616
ReturnData, Spec, TrapKind,
1717
},
1818
};
19-
use cfx_types::{address_util::AddressUtil, Address, H256, U256};
19+
use cfx_types::{Address, H256, U256};
2020
use primitives::transaction::UNSIGNED_SENDER;
2121
use std::sync::Arc;
2222

@@ -315,11 +315,10 @@ impl<'a> ContextTrait for Context<'a> {
315315
let collateral_for_code =
316316
U256::from(data.len()) * *COLLATERAL_PER_BYTE;
317317
debug!("ret() collateral_for_code={:?}", collateral_for_code);
318-
*self
319-
.substate
320-
.storage_collateralized
321-
.entry(self.origin.storage_owner)
322-
.or_insert(0) += data.len() as u64;
318+
self.substate.record_storage_occupy(
319+
&self.origin.storage_owner,
320+
data.len() as u64,
321+
);
323322

324323
self.state.init_code(
325324
&self.origin.address,
@@ -355,10 +354,6 @@ impl<'a> ContextTrait for Context<'a> {
355354
return Err(vm::Error::MutableCallInStaticContext);
356355
}
357356

358-
if !refund_address.is_valid_address() {
359-
return Err(vm::Error::InvalidAddress(*refund_address));
360-
}
361-
362357
suicide_impl(
363358
&self.origin.address,
364359
refund_address,

core/src/executive/executive.rs

Lines changed: 88 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use crate::{
1616
hash::keccak,
1717
machine::Machine,
1818
parameters::staking::*,
19-
state::{CallStackInfo, CleanupMode, State, Substate},
19+
state::{
20+
CallStackInfo, CleanupMode, CollateralCheckResult, State, Substate,
21+
},
2022
statedb::Result as DbResult,
2123
verification::VerificationConfig,
2224
vm::{
@@ -1321,7 +1323,7 @@ impl<'a> Executive<'a> {
13211323
));
13221324
}
13231325

1324-
let mut substate = Substate::new();
1326+
let mut tx_substate = Substate::new();
13251327
if balance512 < sender_intended_cost {
13261328
// Sender is responsible for the insufficient balance.
13271329
// Sub tx fee if not enough cash, and substitute all remaining
@@ -1347,7 +1349,7 @@ impl<'a> Executive<'a> {
13471349
self.state.sub_balance(
13481350
&sender,
13491351
&actual_gas_cost,
1350-
&mut substate.to_cleanup_mode(&spec),
1352+
&mut tx_substate.to_cleanup_mode(&spec),
13511353
)?;
13521354

13531355
return Ok(ExecutionOutcome::ExecutionErrorBumpNonce(
@@ -1372,7 +1374,7 @@ impl<'a> Executive<'a> {
13721374
self.state.sub_balance(
13731375
&sender,
13741376
&U256::try_from(gas_cost).unwrap(),
1375-
&mut substate.to_cleanup_mode(&spec),
1377+
&mut tx_substate.to_cleanup_mode(&spec),
13761378
)?;
13771379
} else {
13781380
self.state.sub_sponsor_balance_for_gas(
@@ -1382,6 +1384,7 @@ impl<'a> Executive<'a> {
13821384
}
13831385

13841386
self.state.checkpoint();
1387+
let mut substate = Substate::new();
13851388

13861389
let res = match tx.action {
13871390
Action::Create => {
@@ -1461,6 +1464,7 @@ impl<'a> Executive<'a> {
14611464
let out = match &res {
14621465
Ok(res) => {
14631466
self.state.discard_checkpoint();
1467+
tx_substate.accrue(substate);
14641468
res.return_data.to_vec()
14651469
}
14661470
Err(vm::Error::StateDbError(_)) => {
@@ -1483,14 +1487,81 @@ impl<'a> Executive<'a> {
14831487

14841488
Ok(self.finalize(
14851489
tx,
1486-
substate,
1490+
tx_substate,
14871491
result,
14881492
output,
14891493
refund_receiver,
14901494
storage_sponsored,
14911495
)?)
14921496
}
14931497

1498+
fn kill_process(
1499+
&mut self, suicides: &HashSet<Address>,
1500+
) -> DbResult<Substate> {
1501+
let mut substate = Substate::new();
1502+
for address in suicides {
1503+
if let Some(code_size) = self.state.code_size(address)? {
1504+
// Only refund the code collateral when code exists.
1505+
// If a contract suicides during creation, the code will be
1506+
// empty.
1507+
let code_owner =
1508+
self.state.code_owner(address)?.expect("code owner exists");
1509+
substate.record_storage_release(&code_owner, code_size as u64);
1510+
}
1511+
1512+
self.state
1513+
.record_storage_entries_release(address, &mut substate)?;
1514+
}
1515+
1516+
let res = self.state.settle_collateral_for_all(&substate)?;
1517+
// The storage recycling process should never occupy new collateral.
1518+
assert_eq!(res, CollateralCheckResult::Valid);
1519+
1520+
for contract_address in suicides {
1521+
let sponsor_for_gas =
1522+
self.state.sponsor_for_gas(contract_address)?;
1523+
let sponsor_for_collateral =
1524+
self.state.sponsor_for_collateral(contract_address)?;
1525+
let sponsor_balance_for_gas =
1526+
self.state.sponsor_balance_for_gas(contract_address)?;
1527+
let sponsor_balance_for_collateral = self
1528+
.state
1529+
.sponsor_balance_for_collateral(contract_address)?;
1530+
1531+
if sponsor_for_gas.is_some() {
1532+
self.state.add_balance(
1533+
sponsor_for_gas.as_ref().unwrap(),
1534+
&sponsor_balance_for_gas,
1535+
substate.to_cleanup_mode(self.spec),
1536+
)?;
1537+
self.state.sub_sponsor_balance_for_gas(
1538+
contract_address,
1539+
&sponsor_balance_for_gas,
1540+
)?;
1541+
}
1542+
if sponsor_for_collateral.is_some() {
1543+
self.state.add_balance(
1544+
sponsor_for_collateral.as_ref().unwrap(),
1545+
&sponsor_balance_for_collateral,
1546+
substate.to_cleanup_mode(self.spec),
1547+
)?;
1548+
self.state.sub_sponsor_balance_for_collateral(
1549+
contract_address,
1550+
&sponsor_balance_for_collateral,
1551+
)?;
1552+
}
1553+
}
1554+
1555+
for contract_address in suicides {
1556+
let burnt_balance = self.state.balance(contract_address)?
1557+
+ self.state.staking_balance(contract_address)?;
1558+
self.state.remove_contract(contract_address)?;
1559+
self.state.subtract_total_issued(burnt_balance);
1560+
}
1561+
1562+
Ok(substate)
1563+
}
1564+
14941565
/// Finalizes the transaction (does refunds and suicides).
14951566
fn finalize(
14961567
&mut self, tx: &SignedTransaction, mut substate: Substate,
@@ -1531,9 +1602,9 @@ impl<'a> Executive<'a> {
15311602
};
15321603

15331604
// perform suicides
1534-
for address in &substate.suicides {
1535-
self.state.kill_account(address)?;
1536-
}
1605+
1606+
let subsubstate = self.kill_process(&substate.suicides)?;
1607+
substate.accrue(subsubstate);
15371608

15381609
// TODO should be added back after enabling dust collection
15391610
// Should be executed once per block, instead of per transaction?
@@ -1567,36 +1638,24 @@ impl<'a> Executive<'a> {
15671638
let mut storage_released = Vec::new();
15681639

15691640
if r.apply_state {
1570-
let affected_address1: HashSet<_> = substate
1571-
.storage_collateralized
1572-
.keys()
1641+
let mut affected_address: Vec<_> = substate
1642+
.keys_for_collateral_changed()
1643+
.iter()
15731644
.cloned()
15741645
.collect();
1575-
let affected_address2: HashSet<_> =
1576-
substate.storage_released.keys().cloned().collect();
1577-
let mut affected_address: Vec<_> =
1578-
affected_address1.union(&affected_address2).collect();
15791646
affected_address.sort();
15801647
for address in affected_address {
1581-
let inc = substate
1582-
.storage_collateralized
1583-
.get(address)
1584-
.cloned()
1585-
.unwrap_or(0);
1586-
let sub = substate
1587-
.storage_released
1588-
.get(address)
1589-
.cloned()
1590-
.unwrap_or(0);
1591-
if inc > sub {
1648+
let (inc, sub) =
1649+
substate.get_collateral_change(address);
1650+
if inc > 0 {
15921651
storage_collateralized.push(StorageChange {
15931652
address: *address,
1594-
amount: inc - sub,
1653+
amount: inc,
15951654
});
1596-
} else if inc < sub {
1655+
} else if sub > 0 {
15971656
storage_released.push(StorageChange {
15981657
address: *address,
1599-
amount: sub - inc,
1658+
amount: sub,
16001659
});
16011660
}
16021661
}

core/src/executive/internal_contract/impls/admin.rs

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
state::{State, Substate},
88
vm::{self, ActionParams, CallType, Spec},
99
};
10-
use cfx_types::{Address, U256};
10+
use cfx_types::{address_util::AddressUtil, Address, U256};
1111
use std::str::FromStr;
1212

1313
lazy_static! {
@@ -35,48 +35,8 @@ pub fn suicide(
3535
substate.suicides.insert(contract_address.clone());
3636
let balance = state.balance(contract_address)?;
3737

38-
if let Some(code_size) = state.code_size(contract_address)? {
39-
// Only refund the code collateral when code exists.
40-
// If a contract suicides during creation, the code will be empty.
41-
let code_owner = state
42-
.code_owner(contract_address)?
43-
.expect("code owner exists");
44-
*substate.storage_released.entry(code_owner).or_insert(0) +=
45-
code_size as u64;
46-
}
47-
48-
let sponsor_for_gas = state.sponsor_for_gas(contract_address)?;
49-
let sponsor_for_collateral =
50-
state.sponsor_for_collateral(contract_address)?;
51-
let sponsor_balance_for_gas =
52-
state.sponsor_balance_for_gas(contract_address)?;
53-
let sponsor_balance_for_collateral =
54-
state.sponsor_balance_for_collateral(contract_address)?;
55-
56-
if sponsor_for_gas.is_some() {
57-
state.add_balance(
58-
sponsor_for_gas.as_ref().unwrap(),
59-
&sponsor_balance_for_gas,
60-
substate.to_cleanup_mode(spec),
61-
)?;
62-
state.sub_sponsor_balance_for_gas(
63-
contract_address,
64-
&sponsor_balance_for_gas,
65-
)?;
66-
}
67-
if sponsor_for_collateral.is_some() {
68-
state.add_balance(
69-
sponsor_for_collateral.as_ref().unwrap(),
70-
&sponsor_balance_for_collateral,
71-
substate.to_cleanup_mode(spec),
72-
)?;
73-
state.sub_sponsor_balance_for_collateral(
74-
contract_address,
75-
&sponsor_balance_for_collateral,
76-
)?;
77-
}
78-
if refund_address == contract_address {
79-
// This is the corner case that the sponsor of the contract is itself.
38+
if refund_address == contract_address || !refund_address.is_valid_address()
39+
{
8040
// When destroying, the balance will be burnt.
8141
state.sub_balance(
8242
contract_address,

core/src/state/account_entry.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use crate::{
66
bytes::Bytes,
77
consensus::debug::ComputeEpochDebugRecord,
88
hash::{keccak, KECCAK_EMPTY},
9-
state::{AccountEntryProtectedMethods, State},
9+
parameters::staking::BYTES_PER_STORAGE_KEY,
10+
state::{AccountEntryProtectedMethods, State, Substate},
1011
statedb::{Result as DbResult, StateDb, StateDbExt},
1112
};
1213
use cfx_types::{address_util::AddressUtil, Address, H256, U256};
@@ -732,9 +733,8 @@ impl OverlayAccount {
732733
/// execution. The second value means the number of keys released by this
733734
/// account in current execution.
734735
pub fn commit_ownership_change(
735-
&mut self, db: &StateDb,
736-
) -> DbResult<HashMap<Address, (u64, u64)>> {
737-
let mut storage_delta = HashMap::new();
736+
&mut self, db: &StateDb, substate: &mut Substate,
737+
) -> DbResult<()> {
738738
let ownership_changes: Vec<_> =
739739
self.ownership_changes.drain().collect();
740740
for (k, v) in ownership_changes {
@@ -755,16 +755,16 @@ impl OverlayAccount {
755755
// If the current value is zero or the owner has changed for the
756756
// key, it means the key has released from previous owner.
757757
if cur_value_is_zero || ownership_changed {
758-
storage_delta
759-
.entry(original_ownership)
760-
.or_insert((0, 0))
761-
.1 += 1;
758+
substate.record_storage_release(
759+
&original_ownership,
760+
BYTES_PER_STORAGE_KEY,
761+
);
762762
}
763763
}
764764
// If the current value is not zero and the owner has changed, it
765765
// means the owner has occupied a new key.
766766
if !cur_value_is_zero && ownership_changed {
767-
storage_delta.entry(v).or_insert((0, 0)).0 += 1;
767+
substate.record_storage_occupy(&v, BYTES_PER_STORAGE_KEY);
768768
}
769769
// Commit ownership change to `ownership_cache`.
770770
if cur_value_is_zero {
@@ -774,7 +774,7 @@ impl OverlayAccount {
774774
}
775775
}
776776
assert!(self.ownership_changes.is_empty());
777-
Ok(storage_delta)
777+
Ok(())
778778
}
779779

780780
pub fn commit(

0 commit comments

Comments
 (0)