Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change validator's consensus key #2137

Merged
merged 5 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changelog/unreleased/features/2137-consensus-key-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Tx that allows a validator to change its consensus key
([\#2137](https://github.com/anoma/namada/pull/2137))
1 change: 1 addition & 0 deletions apps/src/lib/bench_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ pub const TX_UNBOND_WASM: &str = "tx_unbond.wasm";
pub const TX_REDELEGATE_WASM: &str = "tx_redelegate.wasm";
pub const TX_INIT_PROPOSAL_WASM: &str = "tx_init_proposal.wasm";
pub const TX_REVEAL_PK_WASM: &str = "tx_reveal_pk.wasm";
pub const TX_CHANGE_CONSENSUS_KEY_WASM: &str = "tx_change_consensus_key.wasm";
pub const TX_CHANGE_VALIDATOR_COMMISSION_WASM: &str =
"tx_change_validator_commission.wasm";
pub const TX_CHANGE_VALIDATOR_METADATA_WASM: &str =
Expand Down
72 changes: 72 additions & 0 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ pub mod cmds {
.subcommand(Redelegate::def().display_order(2))
.subcommand(ClaimRewards::def().display_order(2))
.subcommand(TxCommissionRateChange::def().display_order(2))
.subcommand(TxChangeConsensusKey::def().display_order(2))
.subcommand(TxMetadataChange::def().display_order(2))
// Ethereum bridge transactions
.subcommand(AddToEthBridgePool::def().display_order(3))
Expand Down Expand Up @@ -294,6 +295,8 @@ pub mod cmds {
Self::parse_with_ctx(matches, TxResignSteward);
let tx_commission_rate_change =
Self::parse_with_ctx(matches, TxCommissionRateChange);
let tx_change_consensus_key =
Self::parse_with_ctx(matches, TxChangeConsensusKey);
let tx_change_metadata =
Self::parse_with_ctx(matches, TxMetadataChange);
let bond = Self::parse_with_ctx(matches, Bond);
Expand Down Expand Up @@ -345,6 +348,7 @@ pub mod cmds {
.or(tx_vote_proposal)
.or(tx_init_validator)
.or(tx_commission_rate_change)
.or(tx_change_consensus_key)
.or(tx_change_metadata)
.or(tx_unjail_validator)
.or(tx_deactivate_validator)
Expand Down Expand Up @@ -424,6 +428,7 @@ pub mod cmds {
TxInitAccount(TxInitAccount),
TxInitValidator(TxInitValidator),
TxCommissionRateChange(TxCommissionRateChange),
TxChangeConsensusKey(TxChangeConsensusKey),
TxMetadataChange(TxMetadataChange),
TxUnjailValidator(TxUnjailValidator),
TxDeactivateValidator(TxDeactivateValidator),
Expand Down Expand Up @@ -2023,6 +2028,30 @@ pub mod cmds {
}
}

#[derive(Clone, Debug)]
pub struct TxChangeConsensusKey(
pub args::ConsensusKeyChange<args::CliTypes>,
);

impl SubCmd for TxChangeConsensusKey {
const CMD: &'static str = "change-consensus-key";

fn parse(matches: &ArgMatches) -> Option<Self>
where
Self: Sized,
{
matches.subcommand_matches(Self::CMD).map(|matches| {
TxChangeConsensusKey(args::ConsensusKeyChange::parse(matches))
})
}

fn def() -> App {
App::new(Self::CMD)
.about("Change consensus key.")
.add_args::<args::ConsensusKeyChange<args::CliTypes>>()
}
}

#[derive(Clone, Debug)]
pub struct TxVoteProposal(pub args::VoteProposal<args::CliTypes>);

Expand Down Expand Up @@ -2816,6 +2845,8 @@ pub mod args {
pub const TX_BRIDGE_POOL_WASM: &str = "tx_bridge_pool.wasm";
pub const TX_CHANGE_COMMISSION_WASM: &str =
"tx_change_validator_commission.wasm";
pub const TX_CHANGE_CONSENSUS_KEY_WASM: &str =
"tx_change_consensus_key.wasm";
pub const TX_CHANGE_METADATA_WASM: &str =
"tx_change_validator_metadata.wasm";
pub const TX_DEACTIVATE_VALIDATOR_WASM: &str =
Expand Down Expand Up @@ -5154,6 +5185,47 @@ pub mod args {
}
}

impl CliToSdk<ConsensusKeyChange<SdkTypes>> for ConsensusKeyChange<CliTypes> {
fn to_sdk(self, ctx: &mut Context) -> ConsensusKeyChange<SdkTypes> {
let tx = self.tx.to_sdk(ctx);
let chain_ctx = ctx.borrow_mut_chain_or_exit();
ConsensusKeyChange::<SdkTypes> {
tx,
validator: chain_ctx.get(&self.validator),
consensus_key: self
.consensus_key
.map(|x| chain_ctx.get_cached(&x)),
tx_code_path: self.tx_code_path.to_path_buf(),
}
}
}

impl Args for ConsensusKeyChange<CliTypes> {
fn parse(matches: &ArgMatches) -> Self {
let tx = Tx::parse(matches);
let validator = VALIDATOR.parse(matches);
let consensus_key = VALIDATOR_CONSENSUS_KEY.parse(matches);
let tx_code_path = PathBuf::from(TX_CHANGE_CONSENSUS_KEY_WASM);
Self {
tx,
validator,
consensus_key,
tx_code_path,
}
}

fn def(app: App) -> App {
app.add_args::<Tx<CliTypes>>()
.arg(VALIDATOR.def().help(
"The validator's address whose consensus key to change.",
))
.arg(VALIDATOR_CONSENSUS_KEY.def().help(
"The desired new consensus key. A new one will be \
generated if none given. Note this key must be ed25519.",
))
}
}

impl CliToSdk<MetaDataChange<SdkTypes>> for MetaDataChange<CliTypes> {
fn to_sdk(self, ctx: &mut Context) -> MetaDataChange<SdkTypes> {
MetaDataChange::<SdkTypes> {
Expand Down
30 changes: 30 additions & 0 deletions apps/src/lib/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,36 @@ impl CliApi {
tx::submit_validator_commission_change(&namada, args)
.await?;
}
Sub::TxChangeConsensusKey(TxChangeConsensusKey(
mut args,
)) => {
let client = client.unwrap_or_else(|| {
C::from_tendermint_address(
&mut args.tx.ledger_address,
)
});
client.wait_until_node_is_synced(io).await?;
let args = args.to_sdk(&mut ctx);
let cli::context::ChainContext {
mut wallet,
mut config,
mut shielded,
native_token,
} = ctx.take_chain_or_exit();
let namada = NamadaImpl::native_new(
&client,
&mut wallet,
&mut shielded,
io,
native_token,
);
tx::submit_change_consensus_key(
&namada,
&mut config,
args,
)
.await?;
}
Sub::TxMetadataChange(TxMetadataChange(mut args)) => {
let client = client.unwrap_or_else(|| {
C::from_tendermint_address(
Expand Down
8 changes: 8 additions & 0 deletions apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,14 @@ pub async fn query_pos_parameters<C: namada::ledger::queries::Client + Sync>(
)
}

pub async fn query_consensus_keys<C: namada::ledger::queries::Client + Sync>(
client: &C,
) -> BTreeSet<common::PublicKey> {
unwrap_client_response::<C, BTreeSet<common::PublicKey>>(
RPC.vp().pos().consensus_key_set(client).await,
)
}

pub async fn query_pgf_stewards<C: namada::ledger::queries::Client + Sync>(
client: &C,
) -> Vec<StewardDetail> {
Expand Down
170 changes: 167 additions & 3 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ use namada::types::address::{Address, ImplicitAddress};
use namada::types::dec::Dec;
use namada::types::io::Io;
use namada::types::key::{self, *};
use namada::types::transaction::pos::InitValidator;
use namada::types::transaction::pos::{ConsensusKeyChange, InitValidator};
use namada_sdk::rpc::{TxBroadcastData, TxResponse};
use namada_sdk::wallet::alias::validator_consensus_key;
use namada_sdk::{display_line, edisplay_line, error, signing, tx, Namada};
use rand::rngs::OsRng;

Expand Down Expand Up @@ -325,6 +326,146 @@ where
Ok(())
}

pub async fn submit_change_consensus_key<'a>(
namada: &impl Namada<'a>,
config: &mut crate::config::Config,
args::ConsensusKeyChange {
tx: tx_args,
validator,
consensus_key,
tx_code_path: _,
}: args::ConsensusKeyChange,
) -> Result<(), error::Error> {
let tx_args = args::Tx {
chain_id: tx_args
.clone()
.chain_id
.or_else(|| Some(config.ledger.chain_id.clone())),
..tx_args.clone()
};

// TODO: do I need to get the validator alias from somewhere, if it exists?
// // Don't think I should generate a new one... Should get the alias
// for the consensus key though...

let wallet = namada.wallet().await;

let alias = wallet.find_alias(&validator).cloned();
let base_consensus_key_alias = alias
.map(|al| validator_consensus_key(&al))
.unwrap_or_else(|| {
validator_consensus_key(&validator.to_string().into())
});
let mut consensus_key_alias = base_consensus_key_alias.to_string();
let all_keys = wallet.get_secret_keys();
let mut key_counter = 0;
while all_keys.contains_key(&consensus_key_alias) {
key_counter += 1;
consensus_key_alias =
format!("{base_consensus_key_alias}-{key_counter}");
}

let mut wallet = namada.wallet_mut().await;
let consensus_key = consensus_key
.map(|key| match key {
common::SecretKey::Ed25519(_) => key,
common::SecretKey::Secp256k1(_) => {
edisplay_line!(
namada.io(),
"Consensus key can only be ed25519"
);
safe_exit(1)
}
})
.unwrap_or_else(|| {
display_line!(namada.io(), "Generating new consensus key...");
let password = read_and_confirm_encryption_password(false);
wallet
.gen_store_secret_key(
// Note that TM only allows ed25519 for consensus key
SchemeType::Ed25519,
Some(consensus_key_alias.clone()),
tx_args.wallet_alias_force,
password,
&mut OsRng,
)
.expect("Key generation should not fail.")
.1
});
// To avoid wallet deadlocks in following operations
drop(wallet);

// Check that the new consensus key is unique
let consensus_keys = rpc::query_consensus_keys(namada.client()).await;

let new_ck = consensus_key.ref_to();
if consensus_keys.contains(&new_ck) {
edisplay_line!(namada.io(), "Consensus key can only be ed25519");
safe_exit(1)
}

let tx_code_hash =
query_wasm_code_hash(namada, args::TX_CHANGE_CONSENSUS_KEY_WASM)
.await
.unwrap();

let chain_id = tx_args.chain_id.clone().unwrap();
let mut tx = Tx::new(chain_id, tx_args.expiration);

let data = ConsensusKeyChange {
validator: validator.clone(),
consensus_key: new_ck,
};

tx.add_code_from_hash(tx_code_hash).add_data(data);
let signing_data = aux_signing_data(namada, &tx_args, None, None).await?;

tx::prepare_tx(
namada,
&tx_args,
&mut tx,
signing_data.fee_payer.clone(),
None,
)
.await?;

signing::generate_test_vector(namada, &tx).await?;

if tx_args.dump_tx {
tx::dump_tx(namada.io(), &tx_args, tx);
} else {
sign(namada, &mut tx, &tx_args, signing_data).await?;
namada.submit(tx, &tx_args).await?;

if !tx_args.dry_run {
namada
.wallet_mut()
.await
.save()
.unwrap_or_else(|err| edisplay_line!(namada.io(), "{}", err));

// let tendermint_home = config.ledger.cometbft_dir();
// tendermint_node::write_validator_key(
// &tendermint_home,
// &consensus_key,
// );
// tendermint_node::write_validator_state(tendermint_home);

display_line!(
namada.io(),
" Consensus key \"{}\"",
consensus_key_alias
);
} else {
display_line!(
namada.io(),
"Transaction dry run. No new consensus key has been saved."
);
}
}
Ok(())
}

pub async fn submit_init_validator<'a>(
namada: &impl Namada<'a>,
config: &mut crate::config::Config,
Expand Down Expand Up @@ -362,7 +503,8 @@ pub async fn submit_init_validator<'a>(
.unwrap_or_else(|| "validator".to_string());

let validator_key_alias = format!("{}-key", alias);
let consensus_key_alias = format!("{}-consensus-key", alias);
let consensus_key_alias = validator_consensus_key(&alias.clone().into());
// let consensus_key_alias = format!("{}-consensus-key", alias);

let threshold = match threshold {
Some(threshold) => threshold,
Expand Down Expand Up @@ -397,7 +539,7 @@ pub async fn submit_init_validator<'a>(
.gen_store_secret_key(
// Note that TM only allows ed25519 for consensus key
SchemeType::Ed25519,
Some(consensus_key_alias.clone()),
Some(consensus_key_alias.clone().into()),
tx_args.wallet_alias_force,
password,
&mut OsRng,
Expand Down Expand Up @@ -1195,6 +1337,28 @@ where
Ok(())
}

// pub async fn submit_change_consensus_key<'a, N: Namada<'a>>(
// namada: &N,
// args: args::ConsensusKeyChange,
// ) -> Result<(), error::Error>
// where
// <N::Client as namada::ledger::queries::Client>::Error: std::fmt::Display,
// {
// let (mut tx, signing_data, _fee_unshield_epoch) =
// args.build(namada).await?;
// signing::generate_test_vector(namada, &tx).await?;

// if args.tx.dump_tx {
// tx::dump_tx(namada.io(), &args.tx, tx);
// } else {
// namada.sign(&mut tx, &args.tx, signing_data).await?;

// namada.submit(tx, &args.tx).await?;
// }

// Ok(())
// }

pub async fn submit_unjail_validator<'a, N: Namada<'a>>(
namada: &N,
args: args::TxUnjailValidator,
Expand Down
Loading