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
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
7 changes: 6 additions & 1 deletion crates/iota-protocol-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1448,7 +1448,12 @@ impl ProtocolConfig {
}

pub fn score_based_rewards(&self) -> bool {
self.feature_flags.score_based_rewards
let res = self.feature_flags.score_based_rewards;
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
assert!(
res && self.scorer_version.is_none(),
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
"score_based_rewards requires scorer_version to be set"
);
res && self.select_committee_from_eligible_validators()
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/iota-types/src/iota_system_state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ pub struct AdvanceEpochParams {
pub epoch_start_timestamp_ms: u64,
pub max_committee_members_count: u64,
pub eligible_active_validators: Vec<u64>,
pub scores: Vec<u64>,
}

#[cfg(msim)]
Expand Down
40 changes: 24 additions & 16 deletions crates/iota-types/src/messages_consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ use shared_crypto::intent::IntentScope;

use crate::{
base_types::{
AuthorityName, CommitRound, ConciseableName, ObjectID, ObjectRef, SequenceNumber,
TransactionDigest,
AuthorityName, ConciseableName, ObjectID, ObjectRef, SequenceNumber, TransactionDigest,
},
crypto::{AuthoritySignature, DefaultHash},
digests::{ConsensusCommitDigest, Digest},
Expand Down Expand Up @@ -91,10 +90,10 @@ pub enum ConsensusTransactionKey {
NewJWKFetched(Box<(AuthorityName, JwkId, JWK)>),
RandomnessDkgMessage(AuthorityName),
RandomnessDkgConfirmation(AuthorityName),
// If a validator submits more than one report for the same round, we update the
// scoring metrics using the maximum reported metric, so CommitRound is sufficient to
// identify a report from a single AuthorityName.
MisbehaviorReport(AuthorityName, CommitRound),
// If a validator submits more than one report for the same checkpoint, we update the
// scoring metrics using the maximum reported metric, so the checkpoint sequence number is
// sufficient to identify a report from a single AuthorityName.
MisbehaviorReport(AuthorityName, u64 /* checkpoint_seq */),
// New entries should be added at the end to preserve serialization compatibility. DO NOT
// CHANGE THE ORDER OF EXISTING ENTRIES!
}
Expand All @@ -107,8 +106,13 @@ impl Debug for ConsensusTransactionKey {
write!(f, "CheckpointSignature({:?}, {:?})", name.concise(), seq)
}
Self::EndOfPublish(name) => write!(f, "EndOfPublish({:?})", name.concise()),
Self::MisbehaviorReport(name, round) => {
write!(f, "MisbehaviorReport({:?},{:?})", name.concise(), round)
Self::MisbehaviorReport(name, checkpoint_seq) => {
write!(
f,
"MisbehaviorReport({:?},{:?})",
name.concise(),
checkpoint_seq
)
}
Self::CapabilityNotification(name, generation) => write!(
f,
Expand Down Expand Up @@ -268,7 +272,11 @@ pub enum ConsensusTransactionKind {
// of `RandomnessDkgMessages` have been received locally, to complete the key generation
// process. Contents are a serialized `fastcrypto_tbls::dkg::Confirmation`.
RandomnessDkgConfirmation(AuthorityName, Vec<u8>),
MisbehaviorReport(AuthorityName, VersionedMisbehaviorReport, CommitRound),
MisbehaviorReport(
AuthorityName,
VersionedMisbehaviorReport,
u64, // checkpoint_seq
),
// New entries should be added at the end to preserve serialization compatibility. DO NOT
// CHANGE THE ORDER OF EXISTING ENTRIES!
}
Expand Down Expand Up @@ -534,10 +542,10 @@ impl ConsensusTransaction {
}
}

pub fn new_misbehavior_report_v1(
pub fn new_misbehavior_report(
authority: AuthorityName,
report: &MisbehaviorReportV1,
round: CommitRound,
report: &VersionedMisbehaviorReport,
checkpoint_seq: u64,
) -> Self {
let serialized_report =
bcs::to_bytes(report).expect("report serialization should not fail");
Expand All @@ -548,8 +556,8 @@ impl ConsensusTransaction {
tracking_id,
kind: ConsensusTransactionKind::MisbehaviorReport(
authority,
VersionedMisbehaviorReport::V1(report.clone()),
round,
report.clone(),
checkpoint_seq,
),
}
}
Expand All @@ -574,8 +582,8 @@ impl ConsensusTransaction {
ConsensusTransactionKind::EndOfPublish(authority) => {
ConsensusTransactionKey::EndOfPublish(*authority)
}
ConsensusTransactionKind::MisbehaviorReport(authority, _, round) => {
ConsensusTransactionKey::MisbehaviorReport(*authority, *round)
ConsensusTransactionKind::MisbehaviorReport(authority, _, checkpoint_seq) => {
ConsensusTransactionKey::MisbehaviorReport(*authority, *checkpoint_seq)
}
ConsensusTransactionKind::CapabilityNotificationV1(cap) => {
ConsensusTransactionKey::CapabilityNotification(cap.authority, cap.generation)
Expand Down
28 changes: 28 additions & 0 deletions crates/iota-types/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,34 @@ impl EndOfEpochTransactionKind {
})
}

pub fn new_change_epoch_v4(
next_epoch: EpochId,
protocol_version: ProtocolVersion,
storage_charge: u64,
computation_charge: u64,
computation_charge_burned: u64,
storage_rebate: u64,
non_refundable_storage_fee: u64,
epoch_start_timestamp_ms: u64,
system_packages: Vec<(SequenceNumber, Vec<Vec<u8>>, Vec<ObjectID>)>,
eligible_active_validators: Vec<u64>,
scores: Vec<u64>,
) -> Self {
Self::ChangeEpochV4(ChangeEpochV4 {
epoch: next_epoch,
protocol_version,
storage_charge,
computation_charge,
computation_charge_burned,
storage_rebate,
non_refundable_storage_fee,
epoch_start_timestamp_ms,
system_packages,
eligible_active_validators,
scores,
})
}

pub fn new_authenticator_state_expire(
min_epoch: u64,
authenticator_obj_initial_shared_version: SequenceNumber,
Expand Down
35 changes: 33 additions & 2 deletions iota-execution/latest/iota-adapter/src/execution_engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,31 @@ mod checked {
construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
}

pub fn construct_advance_epoch_pt_v4(
builder: ProgrammableTransactionBuilder,
params: &AdvanceEpochParams,
) -> Result<ProgrammableTransaction, ExecutionError> {
// the first three arguments to the advance_epoch function, namely
// validator_subsidy, storage_charges and computation_charges, are
// common to both v1, v2, v3 and v4 and are added in
// `construct_advance_epoch_pt_impl`. The remaining arguments are added
// here.
let call_arg_vec = vec![
CallArg::Pure(bcs::to_bytes(&params.computation_charge_burned).unwrap()), /* computation_charge_burned: u64 */
CallArg::IOTA_SYSTEM_MUT, // wrapper: &mut IotaSystemState
CallArg::Pure(bcs::to_bytes(&params.epoch).unwrap()), // new_epoch: u64
CallArg::Pure(bcs::to_bytes(&params.next_protocol_version.as_u64()).unwrap()), /* next_protocol_version: u64 */
CallArg::Pure(bcs::to_bytes(&params.storage_rebate).unwrap()), // storage_rebate: u64
CallArg::Pure(bcs::to_bytes(&params.non_refundable_storage_fee).unwrap()), /* non_refundable_storage_fee: u64 */
CallArg::Pure(bcs::to_bytes(&params.reward_slashing_rate).unwrap()), /* reward_slashing_rate: u64 */
CallArg::Pure(bcs::to_bytes(&params.epoch_start_timestamp_ms).unwrap()), /* epoch_start_timestamp_ms: u64 */
CallArg::Pure(bcs::to_bytes(&params.max_committee_members_count).unwrap()), /* max_committee_members_count: u64 */
CallArg::Pure(bcs::to_bytes(&params.eligible_active_validators).unwrap()), /* eligible_active_validators: Vec<u64> */
/* CallArg::Pure(bcs::to_bytes(&params.scores).unwrap()), // scores: Vec<u64> */
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
];
construct_advance_epoch_pt_impl(builder, params, call_arg_vec)
}

/// Advances the epoch by executing a `ProgrammableTransaction`. If the
/// transaction fails, it switches to safe mode and retries the epoch
/// advancement in a more controlled environment. The function also
Expand Down Expand Up @@ -1045,6 +1070,7 @@ mod checked {
// separate AdvanceEpochParams struct.
max_committee_members_count: 0,
eligible_active_validators: vec![],
scores: vec![],
};
let advance_epoch_pt = construct_advance_epoch_pt_v1(builder, &params)?;
advance_epoch_impl(
Expand Down Expand Up @@ -1087,9 +1113,10 @@ mod checked {
reward_slashing_rate: protocol_config.reward_slashing_rate(),
epoch_start_timestamp_ms: change_epoch_v2.epoch_start_timestamp_ms,
max_committee_members_count: protocol_config.max_committee_members_count(),
// AdvanceEpochV2 does not use this field, but keeping them to avoid creating a
// AdvanceEpochV2 does not use those fields, but keeping them to avoid creating a
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
// separate AdvanceEpochParams struct.
eligible_active_validators: vec![],
scores: vec![],
};
let advance_epoch_pt = construct_advance_epoch_pt_v2(builder, &params)?;
advance_epoch_impl(
Expand Down Expand Up @@ -1133,6 +1160,9 @@ mod checked {
epoch_start_timestamp_ms: change_epoch_v3.epoch_start_timestamp_ms,
max_committee_members_count: protocol_config.max_committee_members_count(),
eligible_active_validators: change_epoch_v3.eligible_active_validators,
// AdvanceEpochV3 does not use this field, but keeping them to avoid creating a
Comment thread
cyberphysic4l marked this conversation as resolved.
Outdated
// separate AdvanceEpochParams struct.
scores: vec![],
};
let advance_epoch_pt = construct_advance_epoch_pt_v3(builder, &params)?;
advance_epoch_impl(
Expand Down Expand Up @@ -1177,8 +1207,9 @@ mod checked {
epoch_start_timestamp_ms: change_epoch_v4.epoch_start_timestamp_ms,
max_committee_members_count: protocol_config.max_committee_members_count(),
eligible_active_validators: change_epoch_v4.eligible_active_validators,
scores: change_epoch_v4.scores,
Comment thread
cyberphysic4l marked this conversation as resolved.
};
let advance_epoch_pt = construct_advance_epoch_pt_v3(builder, &params)?;
let advance_epoch_pt = construct_advance_epoch_pt_v4(builder, &params)?;
advance_epoch_impl(
advance_epoch_pt,
params,
Expand Down
Loading