Skip to content

Commit

Permalink
change a validator's consensus key
Browse files Browse the repository at this point in the history
  • Loading branch information
brentstone committed Nov 9, 2023
1 parent 3a0ecd8 commit 534fcc5
Show file tree
Hide file tree
Showing 20 changed files with 464 additions and 41 deletions.
1 change: 1 addition & 0 deletions apps/src/lib/bench_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,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_IBC_WASM: &str = "tx_ibc.wasm";
Expand Down
80 changes: 76 additions & 4 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ pub mod cmds {
.subcommand(Withdraw::def().display_order(2))
.subcommand(Redelegate::def().display_order(2))
.subcommand(TxCommissionRateChange::def().display_order(2))
.subcommand(TxChangeConsensusKey::def().display_order(2))
// Ethereum bridge transactions
.subcommand(AddToEthBridgePool::def().display_order(3))
// PGF transactions
Expand Down Expand Up @@ -284,6 +285,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 bond = Self::parse_with_ctx(matches, Bond);
let unbond = Self::parse_with_ctx(matches, Unbond);
let withdraw = Self::parse_with_ctx(matches, Withdraw);
Expand Down Expand Up @@ -329,6 +332,7 @@ pub mod cmds {
.or(tx_vote_proposal)
.or(tx_init_validator)
.or(tx_commission_rate_change)
.or(tx_change_consensus_key)
.or(tx_unjail_validator)
.or(bond)
.or(unbond)
Expand Down Expand Up @@ -402,6 +406,7 @@ pub mod cmds {
TxInitAccount(TxInitAccount),
TxInitValidator(TxInitValidator),
TxCommissionRateChange(TxCommissionRateChange),
TxChangeConsensusKey(TxChangeConsensusKey),
TxUnjailValidator(TxUnjailValidator),
TxInitProposal(TxInitProposal),
TxVoteProposal(TxVoteProposal),
Expand Down Expand Up @@ -1855,6 +1860,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 @@ -2648,6 +2677,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_IBC_WASM: &str = "tx_ibc.wasm";
pub const TX_INIT_ACCOUNT_WASM: &str = "tx_init_account.wasm";
pub const TX_INIT_PROPOSAL: &str = "tx_init_proposal.wasm";
Expand Down Expand Up @@ -2861,8 +2892,10 @@ pub mod args {
arg_opt("account-key");
pub const VALIDATOR_ACCOUNT_KEYS: ArgMulti<WalletPublicKey> =
arg_multi("account-keys");
pub const VALIDATOR_CONSENSUS_KEY: ArgOpt<WalletKeypair> =
arg_opt("consensus-key");
pub const VALIDATOR_CONSENSUS_KEY: Arg<WalletKeypair> =
arg("consensus-key");
pub const VALIDATOR_CONSENSUS_KEY_OPT: ArgOpt<WalletKeypair> =
VALIDATOR_CONSENSUS_KEY.opt();
pub const VALIDATOR_CODE_PATH: ArgOpt<PathBuf> =
arg_opt("validator-code-path");
pub const VALIDATOR_ETH_COLD_KEY: ArgOpt<WalletKeypair> =
Expand Down Expand Up @@ -3848,7 +3881,7 @@ pub mod args {
let tx = Tx::parse(matches);
let scheme = SCHEME.parse(matches);
let account_keys = VALIDATOR_ACCOUNT_KEYS.parse(matches);
let consensus_key = VALIDATOR_CONSENSUS_KEY.parse(matches);
let consensus_key = VALIDATOR_CONSENSUS_KEY_OPT.parse(matches);
let eth_cold_key = VALIDATOR_ETH_COLD_KEY.parse(matches);
let eth_hot_key = VALIDATOR_ETH_HOT_KEY.parse(matches);
let protocol_key = PROTOCOL_KEY.parse(matches);
Expand Down Expand Up @@ -3889,7 +3922,7 @@ pub mod args {
in hexadecimal encoding. A new one will be generated if \
none given.",
))
.arg(VALIDATOR_CONSENSUS_KEY.def().help(
.arg(VALIDATOR_CONSENSUS_KEY_OPT.def().help(
"A consensus key for the validator account. A new one \
will be generated if none given. Note that this must be \
ed25519.",
Expand Down Expand Up @@ -4890,6 +4923,45 @@ 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: chain_ctx.get_cached(&self.consensus_key),
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. Note this key must be \
ed25519.",
))
}
}

impl CliToSdk<TxUnjailValidator<SdkTypes>> for TxUnjailValidator<CliTypes> {
fn to_sdk(self, ctx: &mut Context) -> TxUnjailValidator<SdkTypes> {
TxUnjailValidator::<SdkTypes> {
Expand Down
13 changes: 13 additions & 0 deletions apps/src/lib/cli/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,19 @@ 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 namada = ctx.to_sdk(&client, io);
tx::submit_change_consensus_key(&namada, args).await?;
}
// Eth bridge
Sub::AddToEthBridgePool(args) => {
let mut args = args.0;
Expand Down
22 changes: 22 additions & 0 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,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
36 changes: 33 additions & 3 deletions benches/txs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ use namada::types::transaction::governance::{
InitProposalData, VoteProposalData,
};
use namada::types::transaction::pos::{
Bond, CommissionChange, Redelegation, Withdraw,
Bond, CommissionChange, ConsensusKeyChange, Redelegation, Withdraw,
};
use namada::types::transaction::EllipticCurve;
use namada_apps::bench_utils::{
generate_ibc_transfer_tx, generate_tx, BenchShell, BenchShieldedCtx,
ALBERT_PAYMENT_ADDRESS, ALBERT_SPENDING_KEY, BERTHA_PAYMENT_ADDRESS,
TX_BOND_WASM, TX_CHANGE_VALIDATOR_COMMISSION_WASM, TX_INIT_ACCOUNT_WASM,
TX_BOND_WASM, TX_CHANGE_CONSENSUS_KEY_WASM,
TX_CHANGE_VALIDATOR_COMMISSION_WASM, TX_INIT_ACCOUNT_WASM,
TX_INIT_PROPOSAL_WASM, TX_INIT_VALIDATOR_WASM, TX_REDELEGATE_WASM,
TX_REVEAL_PK_WASM, TX_UNBOND_WASM, TX_UNJAIL_VALIDATOR_WASM,
TX_UPDATE_ACCOUNT_WASM, TX_VOTE_PROPOSAL_WASM, TX_WITHDRAW_WASM,
Expand Down Expand Up @@ -656,6 +657,34 @@ fn change_validator_commission(c: &mut Criterion) {
});
}

fn change_consensus_key(c: &mut Criterion) {
let mut csprng = rand::rngs::OsRng {};
let consensus_key: common::PublicKey =
secp256k1::SigScheme::generate(&mut csprng)
.try_to_sk::<common::SecretKey>()
.unwrap()
.to_public();

let signed_tx = generate_tx(
TX_CHANGE_CONSENSUS_KEY_WASM,
ConsensusKeyChange {
validator: defaults::validator_address(),
consensus_key,
},
None,
None,
Some(&defaults::validator_keypair()),
);

c.bench_function("change_consensus_key", |b| {
b.iter_batched_ref(
BenchShell::default,
|shell| shell.execute_tx(&signed_tx),
criterion::BatchSize::LargeInput,
)
});
}

fn ibc(c: &mut Criterion) {
let signed_tx = generate_ibc_transfer_tx();

Expand Down Expand Up @@ -733,6 +762,7 @@ criterion_group!(
init_validator,
change_validator_commission,
ibc,
unjail_validator
unjail_validator,
change_consensus_key
);
criterion_main!(whitelisted_txs);
20 changes: 20 additions & 0 deletions core/src/types/transaction/pos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,23 @@ pub struct CommissionChange {
/// The new commission rate
pub new_rate: Dec,
}

/// A change to the validator's consensus key.
#[derive(
Debug,
Clone,
PartialEq,
BorshSerialize,
BorshDeserialize,
BorshSchema,
Hash,
Eq,
Serialize,
Deserialize,
)]
pub struct ConsensusKeyChange {
/// Validator address
pub validator: Address,
/// The new consensus key
pub consensus_key: common::PublicKey,
}
44 changes: 44 additions & 0 deletions proof_of_stake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2790,6 +2790,38 @@ where
Ok(())
}

/// Consensus key change for a validator
pub fn change_consensus_key<S>(
storage: &mut S,
validator: &Address,
consensus_key: &common::PublicKey,
current_epoch: Epoch,
) -> storage_api::Result<()>
where
S: StorageRead + StorageWrite,
{
tracing::debug!("Changing consensus key for validator {}", validator);

// Check for uniqueness of the consensus key
try_insert_consensus_key(storage, consensus_key)?;

// Set the new consensus key at the pipeline epoch
let params = read_pos_params(storage)?;
validator_consensus_key_handle(validator).set(
storage,
consensus_key.clone(),
current_epoch,
params.pipeline_len,
)?;

// Write validator's new raw hash
write_validator_address_raw_hash(storage, validator, consensus_key)?;

// TODO: anything else?

Ok(())
}

/// Withdraw tokens from those that have been unbonded from proof-of-stake
pub fn withdraw_tokens<S>(
storage: &mut S,
Expand Down Expand Up @@ -3003,6 +3035,18 @@ where
LazySet::open(key).try_insert(storage, consensus_key.clone())
}

/// Get the unique set of consensus keys in storage
pub fn get_consensus_key_set<S>(
storage: &S,
) -> storage_api::Result<BTreeSet<common::PublicKey>>
where
S: StorageRead,
{
let key = consensus_keys_key();
let lazy_set = LazySet::<common::PublicKey>::open(key);
Ok(lazy_set.iter(storage)?.map(Result::unwrap).collect())
}

/// Check if the given consensus key is already being used to ensure uniqueness.
pub fn is_consensus_key_used<S>(
storage: &S,
Expand Down
Loading

0 comments on commit 534fcc5

Please sign in to comment.