Skip to content

Commit

Permalink
cli command to verify if it is safe to update (#4092)
Browse files Browse the repository at this point in the history
* cli command to verify if it is safe to update

* address comments

* revert to unwrap_or_else

* updated command to return general informations

* address comments

* cargo fmt

* added test
  • Loading branch information
marcellorigotti authored Oct 17, 2023
1 parent a39591d commit b80cb9c
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 2 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions api/bin/chainflip-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ async fn run_cli() -> Result<()> {
VanityName { name } => {
api.operator_api().set_vanity_name(name).await?;
},
PreUpdateCheck {} => pre_update_check(api.query_api()).await?,
ForceRotation {} => {
api.governance_api().force_rotation().await?;
},
Expand Down Expand Up @@ -291,6 +292,18 @@ async fn get_bound_executor_address(api: QueryApi) -> Result<()> {
Ok(())
}

async fn pre_update_check(api: QueryApi) -> Result<()> {
let can_update = api.pre_update_check(None, None).await?;

println!("Your node is an authority: {}", can_update.is_authority);
println!("A rotation is occurring: {}", can_update.rotation);
if let Some(blocks) = can_update.next_block_in {
println!("Your validator will produce a block in {} blocks", blocks);
}

Ok(())
}

fn confirm_submit() -> bool {
use std::{io, io::*};

Expand Down
2 changes: 2 additions & 0 deletions api/bin/chainflip-cli/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ pub enum CliCommand {
#[clap(help = "Name in UTF-8 (max length 64)")]
name: String,
},
#[clap(about = "Check if it is safe to update your node/engine")]
PreUpdateCheck {},
#[clap(
// This is only useful for testing. No need to show to the end user.
hide = true,
Expand Down
3 changes: 2 additions & 1 deletion api/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,10 @@ pallet-cf-validator = { path = "../../state-chain/pallets/cf-validator" }
state-chain-runtime = { path = "../../state-chain/runtime" }
frame-system = { git = 'https://github.com/chainflip-io/substrate.git', tag = 'chainflip-monthly-2023-08+2' }
cf-amm = { path = "../../state-chain/amm" }
frame-support = { git = "https://github.com/chainflip-io/substrate.git", tag = "chainflip-monthly-2023-08+2", default-features = false }

# Substrate key types
sp-consensus-aura = { git = 'https://github.com/chainflip-io/substrate.git', tag = 'chainflip-monthly-2023-08+2' }
sp-core = { git = 'https://github.com/chainflip-io/substrate.git', tag = 'chainflip-monthly-2023-08+2' }
sp-consensus-grandpa = { git = 'https://github.com/chainflip-io/substrate.git', tag = 'chainflip-monthly-2023-08+2' }
codec = { package = "parity-scale-codec", version = "3.6.1" }
codec = { package = "parity-scale-codec", version = "3.6.1" }
111 changes: 110 additions & 1 deletion api/lib/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ use cf_primitives::{chains::assets::any, AssetAmount};
use chainflip_engine::state_chain_observer::client::{
chain_api::ChainApi, storage_api::StorageApi,
};
use codec::Decode;
use frame_support::sp_runtime::DigestItem;
use pallet_cf_ingress_egress::DepositChannelDetails;
use pallet_cf_validator::RotationPhase;
use serde::Deserialize;
use sp_consensus_aura::{Slot, AURA_ENGINE_ID};
use state_chain_runtime::PalletInstanceAlias;
use std::{collections::BTreeMap, sync::Arc};
use std::{collections::BTreeMap, ops::Deref, sync::Arc};
use tracing::log;
use utilities::task_scope;

Expand All @@ -18,6 +22,12 @@ pub struct SwapChannelInfo<C: Chain> {
destination_asset: any::Asset,
}

pub struct PreUpdateStatus {
pub rotation: bool,
pub is_authority: bool,
pub next_block_in: Option<usize>,
}

pub struct QueryApi {
pub(crate) state_chain_client: Arc<StateChainClient>,
}
Expand Down Expand Up @@ -143,4 +153,103 @@ impl QueryApi {
)
.await?)
}

pub async fn pre_update_check(
&self,
block_hash: Option<state_chain_runtime::Hash>,
account_id: Option<state_chain_runtime::AccountId>,
) -> Result<PreUpdateStatus, anyhow::Error> {
let block_hash =
block_hash.unwrap_or_else(|| self.state_chain_client.latest_finalized_hash());
let account_id = account_id.unwrap_or_else(|| self.state_chain_client.account_id());

let mut result =
PreUpdateStatus { rotation: false, is_authority: false, next_block_in: None };

if self
.state_chain_client
.storage_value::<pallet_cf_validator::CurrentRotationPhase<state_chain_runtime::Runtime>>(
block_hash,
)
.await? != RotationPhase::Idle
{
result.rotation = true;
}

let current_validators = self
.state_chain_client
.storage_value::<pallet_cf_validator::CurrentAuthorities<state_chain_runtime::Runtime>>(
block_hash,
)
.await?;

if current_validators.contains(&account_id) {
result.is_authority = true;
} else {
return Ok(result)
}

let header = self.state_chain_client.base_rpc_client.block_header(block_hash).await?;

let slot: usize =
*extract_slot_from_digest_item(&header.digest.logs[0]).unwrap().deref() as usize;

let validator_len = current_validators.len();
let current_relative_slot = slot % validator_len;
let index = current_validators.iter().position(|account| account == &account_id).unwrap();

result.next_block_in = Some(compute_distance(index, current_relative_slot, validator_len));
Ok(result)
}
}

// https://github.com/chainflip-io/substrate/blob/c172d0f683fab3792b90d876fd6ca27056af9fe9/frame/aura/src/lib.rs#L179
fn extract_slot_from_digest_item(item: &DigestItem) -> Option<Slot> {
item.as_pre_runtime().and_then(|(id, mut data)| {
if id == AURA_ENGINE_ID {
Slot::decode(&mut data).ok()
} else {
None
}
})
}

fn compute_distance(index: usize, slot: usize, len: usize) -> usize {
if index >= slot {
index - slot
} else {
len - slot + index
}
}

#[test]
fn test_slot_extraction() {
let slot = Slot::from(42);
assert_eq!(
Some(slot),
extract_slot_from_digest_item(&DigestItem::PreRuntime(
AURA_ENGINE_ID,
Encode::encode(&slot)
))
);
assert_eq!(
None,
extract_slot_from_digest_item(&DigestItem::PreRuntime(*b"BORA", Encode::encode(&slot)))
);
assert_eq!(None, extract_slot_from_digest_item(&DigestItem::Other(b"SomethingElse".to_vec())));
}

#[test]
fn test_compute_distance() {
let index: usize = 5;
let slot: usize = 7;
let len: usize = 15;

assert_eq!(compute_distance(index, slot, len), 13);

let index: usize = 18;
let slot: usize = 7;
let len: usize = 24;

assert_eq!(compute_distance(index, slot, len), 11);
}

0 comments on commit b80cb9c

Please sign in to comment.