Skip to content

Commit

Permalink
Merge branch 'brent/consensus-key-change' (#2137)
Browse files Browse the repository at this point in the history
* brent/consensus-key-change:
  benches: consensus key must be ed25519
  add pos error
  add unit test that covers `validator_set_update_tendermint`
  changelog: add #2137
  change a validator's consensus key
brentstone committed Nov 15, 2023
2 parents c23bf62 + a353fd7 commit 48c94cb
Showing 25 changed files with 1,100 additions and 47 deletions.
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
@@ -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 =
72 changes: 72 additions & 0 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
@@ -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))
@@ -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);
@@ -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)
@@ -424,6 +428,7 @@ pub mod cmds {
TxInitAccount(TxInitAccount),
TxInitValidator(TxInitValidator),
TxCommissionRateChange(TxCommissionRateChange),
TxChangeConsensusKey(TxChangeConsensusKey),
TxMetadataChange(TxMetadataChange),
TxUnjailValidator(TxUnjailValidator),
TxDeactivateValidator(TxDeactivateValidator),
@@ -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>);

@@ -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 =
@@ -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> {
30 changes: 30 additions & 0 deletions apps/src/lib/cli/client.rs
Original file line number Diff line number Diff line change
@@ -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(
8 changes: 8 additions & 0 deletions apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
@@ -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> {
170 changes: 167 additions & 3 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
@@ -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;

@@ -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,
@@ -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,
@@ -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,
@@ -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,
Loading

0 comments on commit 48c94cb

Please sign in to comment.