adding session keys finalized

Signed-off-by: Uncle Stretch <uncle.stretch@ghostchain.io>
This commit is contained in:
Uncle Stretch 2025-02-05 15:45:42 +03:00
parent 7e9e6ac329
commit 5b0ef1c4b7
Signed by: str3tch
GPG Key ID: 84F3190747EE79AA
6 changed files with 196 additions and 27 deletions

View File

@ -49,11 +49,13 @@ pub enum Action {
UpdateKnownValidator(String),
TransferTo(String),
AccountDetailsOf(String, Option<SystemAccount>),
StoreRotatedKeys(String),
TransferBalance(String, [u8; 32], u128),
BondValidatorExtraFrom([u8; 32], u128),
BondValidatorFrom([u8; 32], u128),
PayoutStakers([u8; 32], [u8; 32], u32),
SetSessionKeys([u8; 32], String),
EventLog(String, ActionLevel, ActionTarget),
NewBestBlock(u32),

View File

@ -10,7 +10,11 @@ use std::sync::mpsc::Sender;
use super::{Component, PartialComponent, CurrentTab};
use crate::{
action::Action, config::Config, palette::StylePalette, types::{ActionLevel, ActionTarget}, widgets::{Input, InputRequest}
action::Action,
config::Config,
palette::StylePalette,
types::{ActionLevel, ActionTarget},
widgets::{Input, InputRequest},
};
#[derive(Debug)]

View File

@ -2,10 +2,11 @@ use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
use color_eyre::Result;
use ratatui::{
layout::{Alignment, Constraint, Flex, Layout, Rect},
widgets::{Block, Clear, Paragraph},
Frame
text::Text,
widgets::{Block, Cell, Clear, Row, Table},
Frame,
};
//use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::mpsc::UnboundedSender;
use std::sync::mpsc::Sender;
use super::{Component, PartialComponent, CurrentTab};
@ -19,8 +20,10 @@ use crate::{
#[derive(Debug)]
pub struct RotatePopup {
is_active: bool,
//action_tx: Option<UnboundedSender<Action>>,
action_tx: Option<UnboundedSender<Action>>,
network_tx: Option<Sender<Action>>,
cached_keys: String,
secret_seed: [u8; 32],
palette: StylePalette
}
@ -34,17 +37,46 @@ impl RotatePopup {
pub fn new() -> Self {
Self {
is_active: false,
//action_tx: None,
action_tx: None,
network_tx: None,
cached_keys: String::new(),
secret_seed: [0u8; 32],
palette: StylePalette::default(),
}
}
fn rotate_keys(&mut self) {
todo!();
//if let Some(network_tx) = &self.network_tx {
// let _ = network_tx.send(Action::RotateSessionKeys);
//}
if !self.cached_keys.is_empty() && self.cached_keys.len() == 258 {
if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::SetSessionKeys(
self.secret_seed, self.cached_keys.clone()));
}
if let Some(action_tx) = &self.action_tx {
let _ = action_tx.send(Action::ClosePopup);
}
} else {
if let Some(network_tx) = &self.network_tx {
let _ = network_tx.send(Action::RotateSessionKeys);
}
}
}
fn parse_session_keys(&self) -> (String, String, String, String) {
if !self.cached_keys.is_empty() && self.cached_keys.len() == 258 {
let gran_key = format!("0x{}", &self.cached_keys[2..66]);
let babe_key = format!("0x{}", &self.cached_keys[66..130]);
let audi_key = format!("0x{}", &self.cached_keys[130..194]);
let slow_key = format!("0x{}", &self.cached_keys[194..258]);
(gran_key, babe_key, audi_key, slow_key)
} else {
(
String::from("not prepared"),
String::from("not prepared"),
String::from("not prepared"),
String::from("not prepared"),
)
}
}
}
@ -73,6 +105,16 @@ impl Component for RotatePopup {
}
Ok(())
}
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::StoreRotatedKeys(cached_keys) => self.cached_keys = cached_keys,
Action::SetStashSecret(secret_seed) => self.secret_seed = secret_seed,
_ => {}
};
Ok(None)
}
fn handle_key_event(&mut self, key: KeyEvent) -> Result<Option<Action>> {
if self.is_active && key.kind == KeyEventKind::Press {
match key.code {
@ -87,20 +129,48 @@ impl Component for RotatePopup {
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.is_active {
let (border_style, border_type) = self.palette.create_popup_style();
let popup = Paragraph::new(" Do you want to proceed key rotation?")
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_style(self.palette.create_popup_title_style())
.title_alignment(Alignment::Right)
.title("Enter to proceed / Esc to close"));
let v = Layout::vertical([Constraint::Max(3)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(50)]).flex(Flex::Center);
let (gran_key, babe_key, audi_key, slow_key) = self.parse_session_keys();
let table = Table::new(
vec![
Row::new(vec![
Cell::from(Text::from("gran".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(gran_key).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("babe".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(babe_key).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("audi".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(audi_key).alignment(Alignment::Right)),
]),
Row::new(vec![
Cell::from(Text::from("slow".to_string()).alignment(Alignment::Left)),
Cell::from(Text::from(slow_key).alignment(Alignment::Right)),
]),
],
[
Constraint::Min(4),
Constraint::Min(0),
],
)
.highlight_style(self.palette.create_highlight_style())
.column_spacing(1)
.block(Block::bordered()
.border_style(border_style)
.border_type(border_type)
.title_alignment(Alignment::Right)
.title_style(self.palette.create_title_style(false))
.title("Rotate session keys (Enter to proceed / Esc to close)"));
let v = Layout::vertical([Constraint::Max(6)]).flex(Flex::Center);
let h = Layout::horizontal([Constraint::Max(73)]).flex(Flex::Center);
let [area] = v.areas(area);
let [area] = h.areas(area);
frame.render_widget(Clear, area);
frame.render_widget(popup, area);
frame.render_widget(table, area);
}
Ok(())
}

View File

@ -120,3 +120,15 @@ pub async fn get_local_identity(
action_tx.send(Action::SetLocalIdentity(local_peer_id))?;
Ok(())
}
pub async fn rotate_keys(
action_tx: &UnboundedSender<Action>,
rpc_client: &RpcClient,
) -> Result<()> {
let rotated_keys: String = rpc_client
.request("author_rotateKeys", rpc_params![])
.await
.unwrap_or_default();
action_tx.send(Action::StoreRotatedKeys(rotated_keys))?;
Ok(())
}

View File

@ -166,6 +166,7 @@ impl Network {
Action::GetConnectedPeers => legacy_rpc_calls::get_connected_peers(&self.action_tx, &self.rpc_client).await,
Action::GetListenAddresses => legacy_rpc_calls::get_listen_addresses(&self.action_tx, &self.rpc_client).await,
Action::GetLocalIdentity => legacy_rpc_calls::get_local_identity(&self.action_tx, &self.rpc_client).await,
Action::RotateSessionKeys => legacy_rpc_calls::rotate_keys(&self.action_tx, &self.rpc_client).await,
Action::GetBlockAuthor(hash, logs) => predefined_calls::get_block_author(&self.action_tx, &self.online_client_api, &logs, &hash).await,
Action::GetActiveEra => predefined_calls::get_active_era(&self.action_tx, &self.online_client_api).await,
@ -296,9 +297,9 @@ impl Network {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::payout_stakers(
&self.action_tx,
&self.online_client_api,
&sender,
&self.action_tx,
&self.online_client_api,
&sender,
&stash,
era_index,
maybe_nonce,
@ -312,6 +313,24 @@ impl Network {
}
Ok(())
}
Action::SetSessionKeys(sender, hashed_keys) => {
let sender_str = hex::encode(sender);
let maybe_nonce = self.senders.get_mut(&sender_str);
if let Ok(tx_progress) = predefined_txs::set_keys(
&self.action_tx,
&self.online_client_api,
&sender,
&hashed_keys,
maybe_nonce,
).await {
self.transactions_to_watch.push(TxToWatch {
tx_progress,
sender: sender_str,
target: ActionTarget::ValidatorLog,
});
}
Ok(())
}
_ => Ok(())
}
}

View File

@ -7,10 +7,7 @@ use subxt::{
use tokio::sync::mpsc::UnboundedSender;
use crate::{
action::Action,
types::{ActionLevel, ActionTarget},
casper::{CasperExtrinsicParamsBuilder, CasperConfig},
casper_network,
action::Action, casper::{CasperConfig, CasperExtrinsicParamsBuilder}, casper_network::{self, runtime_types}, types::{ActionLevel, ActionTarget}
};
pub async fn transfer_balance(
@ -203,3 +200,68 @@ pub async fn payout_stakers(
}
}
}
pub async fn set_keys(
action_tx: &UnboundedSender<Action>,
api: &OnlineClient<CasperConfig>,
sender: &[u8; 32],
hashed_keys_str: &String,
mut maybe_nonce: Option<&mut u32>,
) -> Result<TxProgress<CasperConfig, OnlineClient<CasperConfig>>> {
let (gran_key, babe_key, audi_key, slow_key) = {
let s = hashed_keys_str.trim_start_matches("0x");
(
hex::decode(&s[0..64]).unwrap().as_slice().try_into().unwrap(),
hex::decode(&s[64..128]).unwrap().as_slice().try_into().unwrap(),
hex::decode(&s[128..192]).unwrap().as_slice().try_into().unwrap(),
hex::decode(&s[192..256]).unwrap().as_slice().try_into().unwrap(),
)
};
let session_keys = runtime_types::casper_runtime::opaque::SessionKeys {
grandpa: runtime_types::sp_consensus_grandpa::app::Public(gran_key),
babe: runtime_types::sp_consensus_babe::app::Public(babe_key),
authority_discovery: runtime_types::sp_authority_discovery::app::Public(audi_key),
slow_clap: runtime_types::ghost_slow_clap::sr25519::app_sr25519::Public(slow_key),
};
// it seems like there is no check for the second paramter, that's why
// we it can be anything.
// Not sure TBH
let transfer_tx = casper_network::tx()
.session()
.set_keys(session_keys, Vec::new());
let tx_params = match maybe_nonce {
Some(ref mut nonce) => {
**nonce = nonce.saturating_add(1);
CasperExtrinsicParamsBuilder::new()
.nonce(nonce.saturating_sub(1) as u64)
.build()
},
None => CasperExtrinsicParamsBuilder::new().build(),
};
let pair = Pair::from_seed(sender);
let signer = PairSigner::<CasperConfig, Pair>::new(pair);
match api
.tx()
.sign_and_submit_then_watch(&transfer_tx, &signer, tx_params)
.await {
Ok(tx_progress) => {
action_tx.send(Action::EventLog(
format!("set keys {} sent", tx_progress.extrinsic_hash()),
ActionLevel::Info,
ActionTarget::ValidatorLog))?;
Ok(tx_progress)
},
Err(err) => {
action_tx.send(Action::EventLog(
format!("error during set keys: {err}"),
ActionLevel::Error,
ActionTarget::ValidatorLog))?;
Err(err.into())
}
}
}