Skip to content
47 changes: 32 additions & 15 deletions crates/iota-core/src/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4763,6 +4763,7 @@ impl AuthorityState {
gas_cost_summary: &GasCostSummary,
checkpoint: CheckpointSequenceNumber,
epoch_start_timestamp_ms: CheckpointTimestamp,
scores: Vec<u64>,
) -> anyhow::Result<(
IotaSystemState,
Option<SystemEpochInfoEvent>,
Expand Down Expand Up @@ -4817,9 +4818,8 @@ impl AuthorityState {
bail!("missing system packages: cannot form ChangeEpochTx");
};

// Use ChangeEpochV3 when the feature flag is enabled and ChangeEpochV2
// requirements are met

// Use ChangeEpochV3 or ChangeEpochV4 when the feature flags are enabled and
// ChangeEpochV2 requirements are met
if config.select_committee_from_eligible_validators() {
// Get the list of eligible validators that support the target protocol version
let active_validators = epoch_store.epoch_start_state().get_active_validators();
Expand Down Expand Up @@ -4861,18 +4861,35 @@ impl AuthorityState {
}
}

txns.push(EndOfEpochTransactionKind::new_change_epoch_v3(
next_epoch,
next_epoch_protocol_version,
gas_cost_summary.storage_cost,
gas_cost_summary.computation_cost,
gas_cost_summary.computation_cost_burned,
gas_cost_summary.storage_rebate,
gas_cost_summary.non_refundable_storage_fee,
epoch_start_timestamp_ms,
next_epoch_system_package_bytes,
eligible_active_validators,
));
// Use ChangeEpochV4 when the feature flag is enabled
if config.score_based_rewards() {
txns.push(EndOfEpochTransactionKind::new_change_epoch_v4(
next_epoch,
next_epoch_protocol_version,
gas_cost_summary.storage_cost,
gas_cost_summary.computation_cost,
gas_cost_summary.computation_cost_burned,
gas_cost_summary.storage_rebate,
gas_cost_summary.non_refundable_storage_fee,
epoch_start_timestamp_ms,
next_epoch_system_package_bytes,
eligible_active_validators,
scores,
));
} else {
txns.push(EndOfEpochTransactionKind::new_change_epoch_v3(
next_epoch,
next_epoch_protocol_version,
gas_cost_summary.storage_cost,
gas_cost_summary.computation_cost,
gas_cost_summary.computation_cost_burned,
gas_cost_summary.storage_rebate,
gas_cost_summary.non_refundable_storage_fee,
epoch_start_timestamp_ms,
next_epoch_system_package_bytes,
eligible_active_validators,
));
}
} else if config.protocol_defined_base_fee()
&& config.max_committee_members_count_as_option().is_some()
{
Expand Down
2 changes: 1 addition & 1 deletion crates/iota-core/src/authority/scorer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl Scorer {
protocol_config,
));

let max_score = 2_u64.pow(16);
let max_score = 2_u64.pow(16); // Note: must be consistent with MAX_SCORE in validator_set.move in iota-framework.
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
let (received_metrics, has_not_sent_report, current_scores, invalid_reports_count) =
(0..committee_size)
.map(|_| {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,8 @@ async fn sync_end_of_epoch_checkpoint(
&authority_state.epoch_store_for_testing().clone(),
&GasCostSummary::new(0, 0, 0, 0, 0),
*checkpoint.sequence_number(),
0, // epoch_start_timestamp_ms
0, // epoch_start_timestamp_ms
vec![], // scores
)
.await
.expect("Failed to create and execute advance epoch tx");
Expand Down
14 changes: 14 additions & 0 deletions crates/iota-core/src/checkpoints/checkpoint_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ impl<T: SubmitToConsensus + ReconfigurationInitiator> CheckpointOutput
.set(checkpoint_seq as i64);
}

// We also send misbehavior reports to consensus at this point. Misbehavior
// reports containing proofs of misbehaviour can be send whenever the
// misbehavior is detected, but we choose to send the ones that include only
// unprovable counts at this point, due to periodicity reasons and to ensure a
// (approximate) synchronization with the score updates.
let misbehavior_report = epoch_store.scorer.current_local_metrics_count.to_report();
let transaction = ConsensusTransaction::new_misbehavior_report(
epoch_store.name,
&misbehavior_report,
checkpoint_seq,
);
self.sender
.submit_to_consensus(&vec![transaction], epoch_store)?;

if checkpoint_timestamp >= self.next_reconfiguration_timestamp_ms {
// close_epoch is ok if called multiple times
self.sender.close_epoch(epoch_store);
Expand Down
13 changes: 12 additions & 1 deletion crates/iota-core/src/checkpoints/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1381,7 +1381,8 @@ impl CheckpointBuilder {

batch.write()?;

// Send all checkpoint sigs to consensus.
// Send all checkpoint sigs to consensus. The messages including
// MisbehaviorReports are also sent in this step.
for (summary, contents) in &new_checkpoints {
self.output
.checkpoint_created(summary, contents, &self.epoch_store, &self.store)
Expand Down Expand Up @@ -1622,6 +1623,13 @@ impl CheckpointBuilder {
// too frequent and not needed, since scores are not used during the epoch
// (except for monitoring purposes, which does not need to be 100% exact)
self.epoch_store.scorer.update_scores();
let scores: Vec<u64> = self
.epoch_store
.scorer
.current_scores
.iter()
.map(|x| x.load(std::sync::atomic::Ordering::Relaxed))
.collect();

let (mut effects, mut signatures): (Vec<_>, Vec<_>) = transactions.into_iter().unzip();
let epoch_rolling_gas_cost_summary =
Expand All @@ -1635,6 +1643,7 @@ impl CheckpointBuilder {
&mut effects,
&mut signatures,
sequence_number,
scores,
)
.await?;

Expand Down Expand Up @@ -1780,6 +1789,7 @@ impl CheckpointBuilder {
checkpoint_effects: &mut Vec<TransactionEffects>,
signatures: &mut Vec<Vec<GenericSignature>>,
checkpoint: CheckpointSequenceNumber,
scores: Vec<u64>,
) -> anyhow::Result<(IotaSystemState, Option<SystemEpochInfoEvent>)> {
let (system_state, system_epoch_info_event, effects) = self
.state
Expand All @@ -1788,6 +1798,7 @@ impl CheckpointBuilder {
epoch_total_gas_cost,
checkpoint,
epoch_start_timestamp_ms,
scores,
)
.await?;
checkpoint_effects.push(effects);
Expand Down
5 changes: 3 additions & 2 deletions crates/iota-e2e-tests/tests/reconfiguration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ async fn advance_epoch_tx_test() {
.create_and_execute_advance_epoch_tx(
&state.epoch_store_for_testing(),
&GasCostSummary::new(0, 0, 0, 0, 0),
0, // checkpoint
0, // epoch_start_timestamp_ms
0, // checkpoint
0, // epoch_start_timestamp_ms
vec![], // scores
)
.await
.unwrap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ fun advance_epoch(
epoch_start_timestamp_ms: u64, // Timestamp of the epoch start
max_committee_members_count: u64,
eligible_active_validators: vector<u64>,
scores : vector<u64>,
ctx: &mut TxContext,
): Balance<IOTA> {
let self = load_system_state_mut(wrapper);
Expand All @@ -550,6 +551,7 @@ fun advance_epoch(
epoch_start_timestamp_ms,
max_committee_members_count,
eligible_active_validators,
scores,
ctx,
);

Expand Down Expand Up @@ -765,6 +767,7 @@ public(package) fun advance_epoch_for_testing(
epoch_start_timestamp_ms: u64,
max_committee_members_count: u64,
eligible_active_validators: vector<u64>,
scores : vector<u64>,
ctx: &mut TxContext,
): Balance<IOTA> {
let storage_charge = balance::create_for_testing(storage_charge);
Expand All @@ -783,6 +786,7 @@ public(package) fun advance_epoch_for_testing(
epoch_start_timestamp_ms,
max_committee_members_count,
eligible_active_validators,
scores,
ctx,
);
storage_rebate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,7 @@ public(package) fun advance_epoch(
epoch_start_timestamp_ms: u64, // Timestamp of the epoch start
max_committee_members_count: u64,
eligible_active_validators: vector<u64>,
scores: vector<u64>,
ctx: &mut TxContext,
): Balance<IOTA> {
self.epoch_start_timestamp_ms = epoch_start_timestamp_ms;
Expand Down Expand Up @@ -792,6 +793,7 @@ public(package) fun advance_epoch(
self.parameters.validator_low_stake_grace_period,
max_committee_members_count,
eligible_active_validators,
scores,
ctx,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ const ACTIVE_OR_PENDING_VALIDATOR: u8 = 2;
const ANY_VALIDATOR: u8 = 3;

const BASIS_POINT_DENOMINATOR: u128 = 10000;
const MAX_SCORE: u128 = 65536; // Note: must be consistent with max score used in iota-core.
const MIN_STAKING_THRESHOLD: u64 = 1_000_000_000; // 1 IOTA

// Errors
Expand All @@ -161,6 +162,7 @@ const EValidatorSetEmpty: u64 = 13;
const ENotACommitteeValidator: u64 = 14;
const EInvalidStakeAmount: u64 = 15;
const EInvalidEligibleValidatorIndex: u64 = 16;
const EInvalidRewardAdjustmentData:u64 = 19;
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated

const EInvalidCap: u64 = 101;

Expand Down Expand Up @@ -457,12 +459,13 @@ public(package) fun advance_epoch(
low_stake_grace_period: u64,
committee_size: u64,
eligible_active_validators: vector<u64>,
scores: vector<u64>,
ctx: &mut TxContext,
) {
let new_epoch = ctx.epoch() + 1;
let total_voting_power = voting_power::total_voting_power();

// Compute the reward distribution without taking into account the tallying rule slashing.
// Compute the reward distribution without taking into account the scores or reporting.
let unadjusted_staking_reward_amounts = compute_unadjusted_reward_distribution(
&self.active_validators,
&self.committee_members,
Expand All @@ -482,6 +485,7 @@ public(package) fun advance_epoch(
unadjusted_staking_reward_amounts,
get_validator_indices_set(&self.active_validators, &slashed_validators),
reward_slashing_rate,
scores,
);

// Distribute the rewards before adjusting stake so that we immediately start compounding
Expand All @@ -506,6 +510,7 @@ public(package) fun advance_epoch(
&adjusted_staking_reward_amounts,
validator_report_records,
&slashed_validators,
scores,
);

// Collect committee validator addresses before modifying the `active_validators`.
Expand Down Expand Up @@ -1326,31 +1331,38 @@ fun compute_adjusted_reward_distribution(
unadjusted_staking_reward_amounts: vector<u64>,
slashed_validator_indices_set: VecSet<u64>,
reward_slashing_rate: u64,
scores: vector<u64>,
): vector<u64> {
let mut adjusted_staking_reward_amounts = vector[];

// Loop through each validator and adjust rewards as necessary
let length = committee_members.length();
assert!(unadjusted_staking_reward_amounts.length() == scores.length(), EInvalidRewardAdjustmentData);
assert!(length == unadjusted_staking_reward_amounts.length(), EInvalidRewardAdjustmentData);

let mut i = 0;
while (i < length) {
let unadjusted_staking_reward_amount = unadjusted_staking_reward_amounts[i];

// Calculate staking reward amount adjusted for the validator's score
let score_adjusted_staking_reward_amount = scores[i] as u128 * (unadjusted_staking_reward_amount as u128)
/ MAX_SCORE;

// Check if the validator is slashed
let adjusted_staking_reward_amount = if (
slashed_validator_indices_set.contains(&committee_members[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)
((score_adjusted_staking_reward_amount as u128) * (reward_slashing_rate as u128)) / BASIS_POINT_DENOMINATOR;
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
score_adjusted_staking_reward_amount - staking_reward_adjustment_u128
} else {
// Otherwise, unadjusted staking reward amount is assigned to the unslashed validators
unadjusted_staking_reward_amount
score_adjusted_staking_reward_amount
};

adjusted_staking_reward_amounts.push_back(adjusted_staking_reward_amount);

adjusted_staking_reward_amounts.push_back(adjusted_staking_reward_amount as u64);
// Move to the next validator
i = i + 1;
};
Expand Down Expand Up @@ -1408,6 +1420,7 @@ fun emit_validator_epoch_events(
pool_staking_reward_amounts: &vector<u64>,
report_records: &VecMap<address, VecSet<address>>,
slashed_validators: &vector<address>,
scores: vector<u64>,
) {
assert!(committee_members.length() == pool_staking_reward_amounts.length());
let mut i = 0;
Expand All @@ -1418,9 +1431,9 @@ fun emit_validator_epoch_events(
} else {
vector[]
};
let tallying_rule_global_score = if (slashed_validators.contains(&validator_address)) 0
else 1;
let mut committee_member_index = committee_members.find_index!(|c| c == i);
let tallying_rule_global_score = if (slashed_validators.contains(&validator_address) || !committee_member_index.is_some()) 0
else scores[committee_member_index.extract()];
let pool_staking_reward = if (committee_member_index.is_some()) {
// prepare event for a committee validator
pool_staking_reward_amounts[committee_member_index.extract()]
Expand Down
Loading
Loading