-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(platform)!: rotate always to top quorum (#2290)
Co-authored-by: Ivan Shumkov <[email protected]>
- Loading branch information
1 parent
244f5f9
commit 7c040e8
Showing
7 changed files
with
426 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
221 changes: 221 additions & 0 deletions
221
...ages/rs-drive-abci/src/execution/platform_events/block_end/validator_set_update/v2/mod.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
use crate::error::execution::ExecutionError; | ||
use crate::error::Error; | ||
use crate::execution::types::block_execution_context::v0::{ | ||
BlockExecutionContextV0Getters, BlockExecutionContextV0MutableGetters, | ||
}; | ||
use crate::execution::types::block_execution_context::BlockExecutionContext; | ||
use crate::platform_types::platform::Platform; | ||
use crate::platform_types::platform_state::v0::PlatformStateV0Methods; | ||
use crate::platform_types::platform_state::PlatformState; | ||
use crate::platform_types::validator_set::v0::ValidatorSetV0Getters; | ||
use crate::rpc::core::CoreRPCLike; | ||
use itertools::Itertools; | ||
|
||
use crate::platform_types::validator_set::ValidatorSetExt; | ||
use dpp::dashcore::hashes::Hash; | ||
use tenderdash_abci::proto::abci::ValidatorSetUpdate; | ||
|
||
impl<C> Platform<C> | ||
where | ||
C: CoreRPCLike, | ||
{ | ||
/// We need to validate against the platform state for rotation and not the block execution | ||
/// context state | ||
/// We introduced v1 because the end quorums could be rotating out, giving a small advantage | ||
/// to proposers with a smaller pro_tx_hash | ||
/// To understand this imagine we have proposers | ||
/// `a b c d e f g h i j k` in quorum 24 | ||
/// c is the current proposer | ||
/// Quorum 24 no longer is valid | ||
/// We jump to quorum 0. | ||
/// a b and c just got paid, but the rest did not. | ||
#[inline(always)] | ||
pub(super) fn validator_set_update_v2( | ||
&self, | ||
proposer_pro_tx_hash: [u8; 32], | ||
platform_state: &PlatformState, | ||
block_execution_context: &mut BlockExecutionContext, | ||
) -> Result<Option<ValidatorSetUpdate>, Error> { | ||
let mut perform_rotation = false; | ||
|
||
if let Some(validator_set) = block_execution_context | ||
.block_platform_state() | ||
.validator_sets() | ||
.get(&platform_state.current_validator_set_quorum_hash()) | ||
{ | ||
if let Some((last_member_pro_tx_hash, _)) = validator_set.members().last_key_value() { | ||
// we should also perform a rotation if the validator set went through all quorum members | ||
// this means we are at the last member of the quorum | ||
if last_member_pro_tx_hash.as_byte_array() == &proposer_pro_tx_hash { | ||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"rotation: quorum finished as we hit last member {} of quorum {}. All known quorums are: [{}]. quorum rotation expected", | ||
hex::encode(proposer_pro_tx_hash), | ||
hex::encode(platform_state.current_validator_set_quorum_hash().as_byte_array()), | ||
block_execution_context | ||
.block_platform_state() | ||
.validator_sets() | ||
.keys() | ||
.map(hex::encode).collect::<Vec<_>>().join(" | "), | ||
); | ||
perform_rotation = true; | ||
} | ||
} else { | ||
// the validator set has no members, very weird, but let's just perform a rotation | ||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"rotation: validator set has no members", | ||
); | ||
perform_rotation = true; | ||
} | ||
|
||
// We should also perform a rotation if there are more than one quorum in the system | ||
// and that the new proposer is on the same quorum and the last proposer but is before | ||
// them in the list of proposers. | ||
// This only works if Tenderdash goes through proposers properly | ||
if &platform_state.last_committed_quorum_hash() | ||
== platform_state | ||
.current_validator_set_quorum_hash() | ||
.as_byte_array() | ||
&& platform_state.last_committed_block_proposer_pro_tx_hash() > proposer_pro_tx_hash | ||
&& platform_state.validator_sets().len() > 1 | ||
{ | ||
// 1 - We haven't changed quorums | ||
// 2 - The new proposer is before the old proposer | ||
// 3 - There are more than one quorum in the system | ||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"rotation: quorum finished as we hit last an earlier member {} than last block proposer {} for quorum {}. All known quorums are: [{}]. quorum rotation expected", | ||
hex::encode(proposer_pro_tx_hash), | ||
hex::encode(block_execution_context.block_platform_state().last_committed_block_proposer_pro_tx_hash()), | ||
hex::encode(platform_state.current_validator_set_quorum_hash().as_byte_array()), | ||
block_execution_context | ||
.block_platform_state() | ||
.validator_sets() | ||
.keys() | ||
.map(hex::encode).collect::<Vec<_>>().join(" | "), | ||
); | ||
perform_rotation = true; | ||
} | ||
} else { | ||
// we also need to perform a rotation if the validator set is being removed | ||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"rotation: new quorums not containing current quorum current {:?}, {}. quorum rotation expected", | ||
block_execution_context | ||
.block_platform_state() | ||
.validator_sets() | ||
.keys() | ||
.map(|quorum_hash| format!("{}", quorum_hash)), | ||
&platform_state.current_validator_set_quorum_hash() | ||
); | ||
perform_rotation = true; | ||
} | ||
|
||
//todo: (maybe) perform a rotation if quorum health is low | ||
|
||
if perform_rotation { | ||
// get the index of the previous quorum | ||
let mut index = platform_state | ||
.validator_sets() | ||
.get_index_of(&platform_state.current_validator_set_quorum_hash()) | ||
.ok_or(Error::Execution(ExecutionError::CorruptedCachedState( | ||
format!("perform_rotation: current validator set quorum hash {} not in current known validator sets [{}] processing block {}", platform_state.current_validator_set_quorum_hash(), platform_state | ||
.validator_sets().keys().map(|quorum_hash| quorum_hash.to_string()).join(" | "), | ||
platform_state.last_committed_block_height() + 1, | ||
))))?; | ||
// we should rotate the quorum | ||
let quorum_count = platform_state.validator_sets().len(); | ||
match quorum_count { | ||
0 => Err(Error::Execution(ExecutionError::CorruptedCachedState( | ||
"no current quorums".to_string(), | ||
))), | ||
1 => Ok(None), // no rotation as we are the only quorum | ||
count => { | ||
let start_index = index; | ||
let oldest_quorum_index_we_can_go_to = if count > 10 { | ||
// if we have a lot of quorums (like on testnet and mainnet) | ||
// we shouldn't start using the last ones as they could cycle out | ||
count - 2 | ||
} else { | ||
count | ||
}; | ||
index = if index + 1 >= oldest_quorum_index_we_can_go_to { | ||
0 | ||
} else { | ||
index + 1 | ||
}; | ||
// We can't just take the next item because it might no longer be in the state | ||
for _i in 0..oldest_quorum_index_we_can_go_to { | ||
let (quorum_hash, _) = platform_state | ||
.validator_sets() | ||
.get_index(index) | ||
.expect("expected next validator set"); | ||
|
||
// We still have it in the state | ||
if let Some(new_validator_set) = block_execution_context | ||
.block_platform_state() | ||
.validator_sets() | ||
.get(quorum_hash) | ||
{ | ||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"rotation: to new quorum: {} with {} members", | ||
&quorum_hash, | ||
new_validator_set.members().len() | ||
); | ||
let validator_set_update = new_validator_set.to_update(); | ||
block_execution_context | ||
.block_platform_state_mut() | ||
.set_next_validator_set_quorum_hash(Some(*quorum_hash)); | ||
return Ok(Some(validator_set_update)); | ||
} | ||
index = (index + 1) % oldest_quorum_index_we_can_go_to; | ||
if index == start_index { | ||
break; | ||
} | ||
} | ||
// All quorums changed | ||
if let Some((quorum_hash, new_validator_set)) = block_execution_context | ||
.block_platform_state() | ||
.validator_sets() | ||
.first() | ||
{ | ||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"rotation: all quorums changed, rotation to new quorum: {}", | ||
&quorum_hash | ||
); | ||
let validator_set_update = new_validator_set.to_update(); | ||
let new_quorum_hash = *quorum_hash; | ||
block_execution_context | ||
.block_platform_state_mut() | ||
.set_next_validator_set_quorum_hash(Some(new_quorum_hash)); | ||
return Ok(Some(validator_set_update)); | ||
} | ||
tracing::debug!("no new quorums to choose from"); | ||
Ok(None) | ||
} | ||
} | ||
} else { | ||
let current_validator_set = block_execution_context | ||
.block_platform_state() | ||
.current_validator_set()?; | ||
if current_validator_set != platform_state.current_validator_set()? { | ||
// Something changed, for example the IP of a validator changed, or someone's ban status | ||
|
||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"validator set update without rotation" | ||
); | ||
Ok(Some(current_validator_set.to_update())) | ||
} else { | ||
tracing::debug!( | ||
method = "validator_set_update_v2", | ||
"no validator set update", | ||
); | ||
Ok(None) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
...ages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v4.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use crate::version::drive_abci_versions::drive_abci_method_versions::{ | ||
DriveAbciBlockEndMethodVersions, DriveAbciBlockFeeProcessingMethodVersions, | ||
DriveAbciBlockStartMethodVersions, DriveAbciCoreBasedUpdatesMethodVersions, | ||
DriveAbciCoreChainLockMethodVersionsAndConstants, DriveAbciCoreInstantSendLockMethodVersions, | ||
DriveAbciEngineMethodVersions, DriveAbciEpochMethodVersions, | ||
DriveAbciFeePoolInwardsDistributionMethodVersions, | ||
DriveAbciFeePoolOutwardsDistributionMethodVersions, | ||
DriveAbciIdentityCreditWithdrawalMethodVersions, DriveAbciInitializationMethodVersions, | ||
DriveAbciMasternodeIdentitiesUpdatesMethodVersions, DriveAbciMethodVersions, | ||
DriveAbciPlatformStateStorageMethodVersions, DriveAbciProtocolUpgradeMethodVersions, | ||
DriveAbciStateTransitionProcessingMethodVersions, DriveAbciVotingMethodVersions, | ||
}; | ||
|
||
pub const DRIVE_ABCI_METHOD_VERSIONS_V4: DriveAbciMethodVersions = DriveAbciMethodVersions { | ||
engine: DriveAbciEngineMethodVersions { | ||
init_chain: 0, | ||
check_tx: 0, | ||
run_block_proposal: 0, | ||
finalize_block_proposal: 0, | ||
consensus_params_update: 1, | ||
}, | ||
initialization: DriveAbciInitializationMethodVersions { | ||
initial_core_height_and_time: 0, | ||
create_genesis_state: 0, | ||
}, | ||
core_based_updates: DriveAbciCoreBasedUpdatesMethodVersions { | ||
update_core_info: 0, | ||
update_masternode_list: 0, | ||
update_quorum_info: 0, | ||
masternode_updates: DriveAbciMasternodeIdentitiesUpdatesMethodVersions { | ||
get_voter_identity_key: 0, | ||
get_operator_identity_keys: 0, | ||
get_owner_identity_withdrawal_key: 0, | ||
get_owner_identity_owner_key: 0, | ||
get_voter_identifier_from_masternode_list_item: 0, | ||
get_operator_identifier_from_masternode_list_item: 0, | ||
create_operator_identity: 0, | ||
create_owner_identity: 1, | ||
create_voter_identity: 0, | ||
disable_identity_keys: 0, | ||
update_masternode_identities: 0, | ||
update_operator_identity: 0, | ||
update_owner_withdrawal_address: 1, | ||
update_voter_identity: 0, | ||
}, | ||
}, | ||
protocol_upgrade: DriveAbciProtocolUpgradeMethodVersions { | ||
check_for_desired_protocol_upgrade: 1, | ||
upgrade_protocol_version_on_epoch_change: 0, | ||
perform_events_on_first_block_of_protocol_change: Some(0), | ||
protocol_version_upgrade_percentage_needed: 67, | ||
}, | ||
block_fee_processing: DriveAbciBlockFeeProcessingMethodVersions { | ||
add_process_epoch_change_operations: 0, | ||
process_block_fees: 0, | ||
}, | ||
core_chain_lock: DriveAbciCoreChainLockMethodVersionsAndConstants { | ||
choose_quorum: 0, | ||
verify_chain_lock: 0, | ||
verify_chain_lock_locally: 0, | ||
verify_chain_lock_through_core: 0, | ||
make_sure_core_is_synced_to_chain_lock: 0, | ||
recent_block_count_amount: 2, | ||
}, | ||
core_instant_send_lock: DriveAbciCoreInstantSendLockMethodVersions { | ||
verify_recent_signature_locally: 0, | ||
}, | ||
fee_pool_inwards_distribution: DriveAbciFeePoolInwardsDistributionMethodVersions { | ||
add_distribute_block_fees_into_pools_operations: 0, | ||
add_distribute_storage_fee_to_epochs_operations: 0, | ||
}, | ||
fee_pool_outwards_distribution: DriveAbciFeePoolOutwardsDistributionMethodVersions { | ||
add_distribute_fees_from_oldest_unpaid_epoch_pool_to_proposers_operations: 0, | ||
add_epoch_pool_to_proposers_payout_operations: 0, | ||
find_oldest_epoch_needing_payment: 0, | ||
fetch_reward_shares_list_for_masternode: 0, | ||
}, | ||
withdrawals: DriveAbciIdentityCreditWithdrawalMethodVersions { | ||
build_untied_withdrawal_transactions_from_documents: 0, | ||
dequeue_and_build_unsigned_withdrawal_transactions: 0, | ||
fetch_transactions_block_inclusion_status: 0, | ||
pool_withdrawals_into_transactions_queue: 0, | ||
update_broadcasted_withdrawal_statuses: 0, | ||
rebroadcast_expired_withdrawal_documents: 0, | ||
append_signatures_and_broadcast_withdrawal_transactions: 0, | ||
cleanup_expired_locks_of_withdrawal_amounts: 0, | ||
}, | ||
voting: DriveAbciVotingMethodVersions { | ||
keep_record_of_finished_contested_resource_vote_poll: 0, | ||
clean_up_after_vote_poll_end: 0, | ||
clean_up_after_contested_resources_vote_poll_end: 0, | ||
check_for_ended_vote_polls: 0, | ||
tally_votes_for_contested_document_resource_vote_poll: 0, | ||
award_document_to_winner: 0, | ||
delay_vote_poll: 0, | ||
run_dao_platform_events: 0, | ||
remove_votes_for_removed_masternodes: 0, | ||
}, | ||
state_transition_processing: DriveAbciStateTransitionProcessingMethodVersions { | ||
execute_event: 0, | ||
process_raw_state_transitions: 0, | ||
decode_raw_state_transitions: 0, | ||
validate_fees_of_event: 0, | ||
}, | ||
epoch: DriveAbciEpochMethodVersions { | ||
gather_epoch_info: 0, | ||
get_genesis_time: 0, | ||
}, | ||
block_start: DriveAbciBlockStartMethodVersions { | ||
clear_drive_block_cache: 0, | ||
}, | ||
block_end: DriveAbciBlockEndMethodVersions { | ||
update_state_cache: 0, | ||
update_drive_cache: 0, | ||
validator_set_update: 2, | ||
}, | ||
platform_state_storage: DriveAbciPlatformStateStorageMethodVersions { | ||
fetch_platform_state: 0, | ||
store_platform_state: 0, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.