Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

protocol upgrade (v2): Burn slashed rewards (phase RD 1) + changes to system epoch info event #3739

Merged
merged 35 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ab64e72
initial changes
oliviasaa Oct 29, 2024
945abaa
add tests
oliviasaa Oct 29, 2024
fe8a51f
remove unused function
oliviasaa Oct 29, 2024
959eeb6
add snapshot related files
oliviasaa Oct 29, 2024
702935e
fix(node): regenerate baselines for tests
muXxer Nov 1, 2024
51452cf
fix(node): simplify `compute_adjusted_reward_distribution`
muXxer Nov 5, 2024
cc82ac9
fix(node): update framework manifest
muXxer Nov 5, 2024
36478bc
fix(node): add some checks and comments to the rewards tests
muXxer Nov 5, 2024
666eac6
Update crates/iota-framework/packages/iota-system/sources/validator_s…
oliviasaa Nov 5, 2024
97c22f9
fix(node): regenerate baselines for tests
muXxer Nov 5, 2024
0f8533a
add comments to reward distribution tests
oliviasaa Nov 27, 2024
fe94fdf
Merge branch 'develop' into protocol/burn_slashed_rewards-RD1
cyberphysic4l Dec 4, 2024
93ee46e
show correct burnt amount in SystemEpochInfoEventV1
cyberphysic4l Dec 4, 2024
c2523d1
remove parentheses
cyberphysic4l Dec 4, 2024
0a5646f
Add checks that slashed rewards are burned to tests
cyberphysic4l Dec 4, 2024
904b9ea
get burned and minted amounts directly from match function logic
cyberphysic4l Dec 5, 2024
245ef95
fix mixed up burned and minted amounts.
cyberphysic4l Dec 5, 2024
2db1618
Merge pull request #4352 from iotaledger/fix/system-epoch-info-event
cyberphysic4l Dec 5, 2024
527d462
update protocol config for v2
cyberphysic4l Dec 5, 2024
dcc6733
generate v2 framework snapshot
cyberphysic4l Dec 5, 2024
c132a15
Merge branch 'develop' into protocol/burn_slashed_rewards-RD1
cyberphysic4l Dec 5, 2024
1377b93
fix(iota-protocol-config): fixed resolving protocol version for simul…
valeriyr Nov 26, 2024
28d4830
update swarm-config snapshots
cyberphysic4l Dec 5, 2024
95c7685
generate snapshot for e2e tests
cyberphysic4l Dec 5, 2024
f20be52
update open-rpc
cyberphysic4l Dec 5, 2024
efde8a2
fix(iota-indexer): tests use protocol max version instead of 1
miker83z Dec 9, 2024
bb60ab4
update framework snapshot
cyberphysic4l Dec 9, 2024
13104ca
fix(iota-swarm-config): test baseline
miker83z Dec 9, 2024
35d84f7
fix(iota-e2e-tests): update snapshot for snapshot tests
miker83z Dec 9, 2024
9e8eea8
fix(iota-e2e-tests): update snapshot-tests object ids and tx digest
miker83z Dec 9, 2024
15b1160
Merge branch 'develop' into protocol/burn_slashed_rewards-RD1
miker83z Dec 10, 2024
1f70e9f
fix: add comment about protocol version 2
lzpap Dec 11, 2024
9dedda4
fix: fmt
lzpap Dec 11, 2024
0d00ae6
fix: update protocol config comment
miker83z Dec 11, 2024
dd4ae32
Merge branch 'develop' into protocol/burn_slashed_rewards-RD1
miker83z Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions crates/iota-e2e-tests/tests/snapshot_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,17 @@ async fn basic_read_cmd_snapshot_tests() -> Result<(), anyhow::Error> {
let mut test_cluster = TestClusterBuilder::new().build().await;
let context = &mut test_cluster.wallet;

// These object ids and transaction digest are picked by executing this test and
// copying over some random ids from the snapshot file
let cmds = vec![
"iota client objects {ME}", // valid addr
"iota client objects 0x0000000000000000000000000000000000000000000000000000000000000000", /* empty addr */
"iota client object 0x5", // valid object
"iota client object 0x5 --bcs", // valid object BCS
// Simtest object IDs are not stable so these object IDs may or may not exist currently --
// commenting them out for now.
// "iota client object 0x3b5121a0603ef7ab4cb57827fceca17db3338ef2cd76126cc1523b681df27cee",
// // valid object "iota client object
// 0x3b5121a0603ef7ab4cb57827fceca17db3338ef2cd76126cc1523b681df27cee --bcs", // valid
// object BCS
"iota client object 0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1", /* valid object */
"iota client object 0x9135cb3b5aca99a1555b742bd11ddc45fba33343be182bdc161be69da2c41be1 --bcs", /* valid object BCS */
"iota client object 0x0000000000000000000000000000000000000000000000000000000000000000", /* non-existent object */
"iota client tx-block 5zibcom3dMckjyN16ygFwr5XNa9Exi1MmY3BQs984x1N", // valid tx digest
"iota client tx-block E5Zp4QQ84PQEceSw4JRi4VTScSAQweKSgdwp9XH4aVPd", // valid tx digest
"iota client tx-block 11111111111111111111111111111111", /* non-existent tx
* digest */
];
Expand Down
925 changes: 486 additions & 439 deletions crates/iota-e2e-tests/tests/snapshots/snapshot_tests__body_fn.snap

Large diffs are not rendered by default.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
10 changes: 10 additions & 0 deletions crates/iota-framework-snapshot/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,15 @@
"0x000000000000000000000000000000000000000000000000000000000000000b",
"0x000000000000000000000000000000000000000000000000000000000000107a"
]
},
"2": {
"git_revision": "f20be52f200b",
"package_ids": [
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000002",
"0x0000000000000000000000000000000000000000000000000000000000000003",
"0x000000000000000000000000000000000000000000000000000000000000000b",
"0x000000000000000000000000000000000000000000000000000000000000107a"
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,9 @@ module iota_system::iota_system_state_inner {
let storage_charge_value = storage_charge.value();
let computation_charge = computation_reward.value();

// Mints or burns tokens depending on the target reward.
// Since not all rewards are distributed in case of slashed validators,
// tokens might be minted here and burnt in the same epoch change.
let (mut total_validator_rewards, minted_tokens_amount, mut burnt_tokens_amount) = match_computation_reward_to_target_reward(
validator_target_reward,
computation_reward,
Expand All @@ -701,7 +704,7 @@ module iota_system::iota_system_state_inner {
let new_total_stake = self.validators.total_stake();

let remaining_validator_rewards_amount_after_distribution = total_validator_rewards.value();
let total_validator_rewards_distributed = total_validator_rewards_amount_before_distribution - remaining_validator_rewards_amount_after_distribution;
let total_stake_rewards_distributed = total_validator_rewards_amount_before_distribution - remaining_validator_rewards_amount_after_distribution;

self.protocol_version = next_protocol_version;

Expand Down Expand Up @@ -731,9 +734,9 @@ module iota_system::iota_system_state_inner {
storage_rebate: storage_rebate_amount,
storage_fund_balance: self.storage_fund.total_balance(),
total_gas_fees: computation_charge,
total_stake_rewards_distributed: total_validator_rewards_distributed,
total_stake_rewards_distributed,
burnt_tokens_amount,
minted_tokens_amount
minted_tokens_amount,
}
);
self.safe_mode = false;
Expand All @@ -751,24 +754,22 @@ module iota_system::iota_system_state_inner {
/// and the amount of computation fees burned in this epoch.
fun match_computation_reward_to_target_reward(
validator_target_reward: u64,
mut computation_reward: Balance<IOTA>,
mut computation_charges: Balance<IOTA>,
iota_treasury_cap: &mut iota::iota::IotaTreasuryCap,
ctx: &TxContext,
): (Balance<IOTA>, u64, u64) {
let mut burnt_tokens_amount = 0;
let mut minted_tokens_amount = 0;
if (computation_reward.value() < validator_target_reward) {
let tokens_to_mint = validator_target_reward - computation_reward.value();
let new_tokens = iota_treasury_cap.mint_balance(tokens_to_mint, ctx);
minted_tokens_amount = new_tokens.value();
computation_reward.join(new_tokens);
} else if (computation_reward.value() > validator_target_reward) {
let tokens_to_burn = computation_reward.value() - validator_target_reward;
let rewards_to_burn = computation_reward.split(tokens_to_burn);
burnt_tokens_amount = rewards_to_burn.value();
iota_treasury_cap.burn_balance(rewards_to_burn, ctx);
let burnt_tokens_amount = computation_charges.value();
let minted_tokens_amount = validator_target_reward;
if (burnt_tokens_amount < minted_tokens_amount) {
let actual_amount_to_mint = minted_tokens_amount - burnt_tokens_amount;
let balance_to_mint = iota_treasury_cap.mint_balance(actual_amount_to_mint, ctx);
computation_charges.join(balance_to_mint);
} else if (burnt_tokens_amount > minted_tokens_amount) {
let actual_amount_to_burn = burnt_tokens_amount - minted_tokens_amount;
let balance_to_burn = computation_charges.split(actual_amount_to_burn);
iota_treasury_cap.burn_balance(balance_to_burn, ctx);
};
(computation_reward, minted_tokens_amount, burnt_tokens_amount)
(computation_charges, minted_tokens_amount, burnt_tokens_amount)
}

/// Return the current epoch number. Useful for applications that need a coarse-grained concept of time,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module iota_system::validator_set {
use iota_system::staking_pool::{PoolTokenExchangeRate, StakedIota, pool_id};
use iota::priority_queue as pq;
use iota::vec_map::{Self, VecMap};
use iota::vec_set::VecSet;
use iota::vec_set::{Self, VecSet};
use iota::table::{Self, Table};
use iota::event;
use iota::table_vec::{Self, TableVec};
Expand Down Expand Up @@ -341,28 +341,14 @@ module iota_system::validator_set {
// punished.
let slashed_validators = compute_slashed_validators(self, *validator_report_records);

let total_slashed_validator_voting_power = sum_voting_power_by_addresses(&self.active_validators, &slashed_validators);

// Compute the reward adjustments of slashed validators, to be taken into
// account in adjusted reward computation.
let (total_staking_reward_adjustment, individual_staking_reward_adjustments) =
compute_reward_adjustments(
get_validator_indices(&self.active_validators, &slashed_validators),
reward_slashing_rate,
&unadjusted_staking_reward_amounts,
);

// Compute the adjusted amounts of stake each validator should get given the tallying rule
// reward adjustments we computed before.
// Compute the adjusted amounts of stake each validator should get according to the tallying rule.
// `compute_adjusted_reward_distribution` must be called before `distribute_reward` and `adjust_stake_and_gas_price` to
// make sure we are using the current epoch's stake information to compute reward distribution.
let adjusted_staking_reward_amounts = compute_adjusted_reward_distribution(
&self.active_validators,
total_voting_power,
total_slashed_validator_voting_power,
unadjusted_staking_reward_amounts,
total_staking_reward_adjustment,
individual_staking_reward_adjustments,
get_validator_indices_set(&self.active_validators, &slashed_validators),
reward_slashing_rate,
);

// Distribute the rewards before adjusting stake so that we immediately start compounding
Expand Down Expand Up @@ -638,18 +624,17 @@ module iota_system::validator_set {
option::none()
}


/// Given a vector of validator addresses, return their indices in the validator set.
/// Given a vector of validator addresses, return a set of all indices of the validators.
/// Aborts if any address isn't in the given validator set.
fun get_validator_indices(validators: &vector<ValidatorV1>, validator_addresses: &vector<address>): vector<u64> {
fun get_validator_indices_set(validators: &vector<ValidatorV1>, validator_addresses: &vector<address>): VecSet<u64> {
let length = validator_addresses.length();
let mut i = 0;
let mut res = vector[];
let mut res = vec_set::empty();
while (i < length) {
let addr = validator_addresses[i];
let index_opt = find_validator(validators, addr);
assert!(index_opt.is_some(), ENotAValidator);
res.push_back(index_opt.destroy_some());
res.insert(index_opt.destroy_some());
i = i + 1;
};
res
Expand Down Expand Up @@ -941,35 +926,6 @@ module iota_system::validator_set {
}
}

/// Compute both the individual reward adjustments and total reward adjustment for staking rewards.
fun compute_reward_adjustments(
mut slashed_validator_indices: vector<u64>,
reward_slashing_rate: u64,
unadjusted_staking_reward_amounts: &vector<u64>,
): (
u64, // sum of staking reward adjustments
VecMap<u64, u64>, // mapping of individual validator's staking reward adjustment from index -> amount
) {
let mut total_staking_reward_adjustment = 0;
let mut individual_staking_reward_adjustments = vec_map::empty();

while (!slashed_validator_indices.is_empty()) {
let validator_index = slashed_validator_indices.pop_back();

// Use the slashing rate to compute the amount of staking rewards slashed from this punished validator.
let unadjusted_staking_reward = unadjusted_staking_reward_amounts[validator_index];
let staking_reward_adjustment_u128 =
unadjusted_staking_reward as u128 * (reward_slashing_rate as u128)
/ BASIS_POINT_DENOMINATOR;

// Insert into individual mapping and record into the total adjustment sum.
individual_staking_reward_adjustments.insert(validator_index, staking_reward_adjustment_u128 as u64);
total_staking_reward_adjustment = total_staking_reward_adjustment + (staking_reward_adjustment_u128 as u64);
};

(total_staking_reward_adjustment, individual_staking_reward_adjustments)
}

/// Process the validator report records of the epoch and return the addresses of the
/// non-performant validators according to the input threshold.
fun compute_slashed_validators(
Expand Down Expand Up @@ -1023,44 +979,37 @@ module iota_system::validator_set {
/// The staking rewards are shared with the stakers.
fun compute_adjusted_reward_distribution(
validators: &vector<ValidatorV1>,
total_voting_power: u64,
total_slashed_validator_voting_power: u64,
unadjusted_staking_reward_amounts: vector<u64>,
total_staking_reward_adjustment: u64,
individual_staking_reward_adjustments: VecMap<u64, u64>,
slashed_validator_indices_set: VecSet<u64>,
reward_slashing_rate: u64,
): vector<u64> {
let total_unslashed_validator_voting_power = total_voting_power - total_slashed_validator_voting_power;
let mut adjusted_staking_reward_amounts = vector[];


// Loop through each validator and adjust rewards as necessary
let length = validators.length();

let mut i = 0;
while (i < length) {
let validator = &validators[i];
// Integer divisions will truncate the results. Because of this, we expect that at the end
// there will be some reward remaining in `total_reward`.
// Use u128 to avoid multiplication overflow.
let voting_power = validator.voting_power() as u128;

// Compute adjusted staking reward.
let unadjusted_staking_reward_amount = unadjusted_staking_reward_amounts[i];
let adjusted_staking_reward_amount =
// If the validator is one of the slashed ones, then subtract the adjustment.
if (individual_staking_reward_adjustments.contains(&i)) {
let adjustment = individual_staking_reward_adjustments[&i];
unadjusted_staking_reward_amount - adjustment
} else {
// Otherwise the slashed rewards should be distributed among the unslashed
// validators so add the corresponding adjustment.
let adjustment = total_staking_reward_adjustment as u128 * voting_power
/ (total_unslashed_validator_voting_power as u128);
unadjusted_staking_reward_amount + (adjustment as u64)
};

// Check if the validator is slashed
let adjusted_staking_reward_amount = if (slashed_validator_indices_set.contains(&i)) {
// Use the slashing rate to compute the amount of staking rewards slashed from this punished validator.
// Use u128 to avoid multiplication overflow.
let staking_reward_adjustment_u128 = ((unadjusted_staking_reward_amount as u128) * (reward_slashing_rate as u128)) / BASIS_POINT_DENOMINATOR;
unadjusted_staking_reward_amount - (staking_reward_adjustment_u128 as u64)
} else {
// Otherwise, unadjusted staking reward amount is assigned to the unslashed validators
unadjusted_staking_reward_amount
};

adjusted_staking_reward_amounts.push_back(adjusted_staking_reward_amount);


// Move to the next validator
i = i + 1;
};

// The sum of the adjusted staking rewards may not be equal to the total staking reward,
// because of integer division truncation and the slashing of the rewards for the slashed validators.
adjusted_staking_reward_amounts
}

Expand Down
Loading
Loading