Skip to content

Commit

Permalink
Merge branch 'brent/validator-commission-rates' (#695)
Browse files Browse the repository at this point in the history
* brent/validator-commission-rates:
  changelog: add #695
  [ci] wasm checksums update
  addressing 2nd round of review comments
  async tx to change validator commission rate
  add vp validator to wasms
  fix: critical flaw in pos VP that was prematurely returning true
  add max commission rate info to validation and pos state machine test
  validator VP that checks source and signature for a commission rate change tx
  changes in response to review comments
  [ci] wasm checksums update
  fix pos state machine test
  add max change info to query of validator commission rate
  fix get of epoched commission rate before pipeline
  bug fix: consensus key validation error
  commission change wasm tx test: fix and update validation
  fix error convention
  wasm tx test for changing validator commission rate
  [ci] wasm checksums update
  improve docs
  fix commission rate validation on validator initialization
  init validator: add commission rate required args for tests
  cleaning: incl fmt + clippy
  bug fix from splitting this PR off of #388
  include and update `rust_decimal`
  add missing commission rate-related instances
  commission rate: query + refactor validator change tx
  epoched commission rate and tx for validator to change their rate
  require commission rate input data for new validators
  introduce validator commission rate and changes
  changelog: #687
  remove staking rewards address from cli strings and docs strings
  wallet: remove validator rewards key
  client: remove staking rewards address from init-validator result
  remove staking reward address from genesis toml files
  remove staking reward address from all code
  changelog: #436
  [ci] wasm checksums update
  replace floating point arithm from token module with rust_decimal
  • Loading branch information
tzemanovic committed Nov 10, 2022
2 parents dabbec4 + 6b92f2c commit 1ffd8ff
Show file tree
Hide file tree
Showing 38 changed files with 2,114 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- Allow to set validator's commission rates and a limit on change of commission
rate per epoch. Commission rate can be changed via a transaction authorized
by the validator, but the limit is immutable value, set when the validator's
account is initialized. ([#695](https://github.com/anoma/namada/pull/695))
17 changes: 17 additions & 0 deletions Cargo.lock

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

2 changes: 2 additions & 0 deletions apps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ tracing-subscriber = {version = "0.3.7", features = ["env-filter"]}
websocket = "0.26.2"
winapi = "0.3.9"
bimap = {version = "0.6.2", features = ["serde"]}
rust_decimal = "1.26.1"
rust_decimal_macros = "1.26.1"

[dev-dependencies]
namada = {path = "../shared", features = ["testing", "wasm-runtime"]}
Expand Down
3 changes: 3 additions & 0 deletions apps/src/bin/anoma-client/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ pub async fn main() -> Result<()> {
Sub::QueryVotingPower(QueryVotingPower(args)) => {
rpc::query_voting_power(ctx, args).await;
}
Sub::QueryCommissionRate(QueryCommissionRate(args)) => {
rpc::query_commission_rate(ctx, args).await;
}
Sub::QuerySlashes(QuerySlashes(args)) => {
rpc::query_slashes(ctx, args).await;
}
Expand Down
129 changes: 129 additions & 0 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ pub mod cmds {
QueryBalance(QueryBalance),
QueryBonds(QueryBonds),
QueryVotingPower(QueryVotingPower),
QueryCommissionRate(QueryCommissionRate),
QuerySlashes(QuerySlashes),
QueryRawBytes(QueryRawBytes),
QueryProposal(QueryProposal),
Expand Down Expand Up @@ -999,6 +1000,25 @@ pub mod cmds {
}
}

#[derive(Clone, Debug)]
pub struct QueryCommissionRate(pub args::QueryCommissionRate);

impl SubCmd for QueryCommissionRate {
const CMD: &'static str = "commission-rate";

fn parse(matches: &ArgMatches) -> Option<Self> {
matches.subcommand_matches(Self::CMD).map(|matches| {
QueryCommissionRate(args::QueryCommissionRate::parse(matches))
})
}

fn def() -> App {
App::new(Self::CMD)
.about("Query commission rate.")
.add_args::<args::QueryCommissionRate>()
}
}

#[derive(Clone, Debug)]
pub struct QuerySlashes(pub args::QuerySlashes);

Expand Down Expand Up @@ -1217,6 +1237,7 @@ pub mod args {
use namada::types::storage::{self, Epoch};
use namada::types::token;
use namada::types::transaction::GasLimit;
use rust_decimal::Decimal;

use super::context::{WalletAddress, WalletKeypair, WalletPublicKey};
use super::utils::*;
Expand Down Expand Up @@ -1245,6 +1266,7 @@ pub mod args {
const CHAIN_ID_PREFIX: Arg<ChainIdPrefix> = arg("chain-prefix");
const CODE_PATH: Arg<PathBuf> = arg("code-path");
const CODE_PATH_OPT: ArgOpt<PathBuf> = CODE_PATH.opt();
const COMMISSION_RATE: Arg<Decimal> = arg("commission-rate");
const CONSENSUS_TIMEOUT_COMMIT: ArgDefault<Timeout> = arg_default(
"consensus-timeout-commit",
DefaultFn(|| Timeout::from_str("1s").unwrap()),
Expand Down Expand Up @@ -1276,6 +1298,8 @@ pub mod args {

const LEDGER_ADDRESS: Arg<TendermintAddress> = arg("ledger-address");
const LOCALHOST: ArgFlag = flag("localhost");
const MAX_COMMISSION_RATE_CHANGE: Arg<Decimal> =
arg("max-commission-rate-change");
const MODE: ArgOpt<String> = arg_opt("mode");
const NET_ADDRESS: Arg<SocketAddr> = arg("net-address");
const OWNER: ArgOpt<WalletAddress> = arg_opt("owner");
Expand Down Expand Up @@ -1527,6 +1551,8 @@ pub mod args {
pub account_key: Option<WalletPublicKey>,
pub consensus_key: Option<WalletKeypair>,
pub protocol_key: Option<WalletPublicKey>,
pub commission_rate: Decimal,
pub max_commission_rate_change: Decimal,
pub validator_vp_code_path: Option<PathBuf>,
pub unsafe_dont_encrypt: bool,
}
Expand All @@ -1539,6 +1565,9 @@ pub mod args {
let account_key = VALIDATOR_ACCOUNT_KEY.parse(matches);
let consensus_key = VALIDATOR_CONSENSUS_KEY.parse(matches);
let protocol_key = PROTOCOL_KEY.parse(matches);
let commission_rate = COMMISSION_RATE.parse(matches);
let max_commission_rate_change =
MAX_COMMISSION_RATE_CHANGE.parse(matches);
let validator_vp_code_path = VALIDATOR_CODE_PATH.parse(matches);
let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches);
Self {
Expand All @@ -1548,6 +1577,8 @@ pub mod args {
account_key,
consensus_key,
protocol_key,
commission_rate,
max_commission_rate_change,
validator_vp_code_path,
unsafe_dont_encrypt,
}
Expand All @@ -1574,6 +1605,17 @@ pub mod args {
"A public key for signing protocol transactions. A new \
one will be generated if none given.",
))
.arg(COMMISSION_RATE.def().about(
"The commission rate charged by the validator for \
delegation rewards. Expressed as a decimal between 0 and \
1. This is a required parameter.",
))
.arg(MAX_COMMISSION_RATE_CHANGE.def().about(
"The maximum change per epoch in the commission rate \
charged by the validator for delegation rewards. \
Expressed as a decimal between 0 and 1. This is a \
required parameter.",
))
.arg(VALIDATOR_CODE_PATH.def().about(
"The path to the validity predicate WASM code to be used \
for the validator account. Uses the default validator VP \
Expand Down Expand Up @@ -2053,6 +2095,77 @@ pub mod args {
}
}

#[derive(Clone, Debug)]
/// Commission rate change args
pub struct TxCommissionRateChange {
/// Common tx arguments
pub tx: Tx,
/// Validator address (should be self)
pub validator: WalletAddress,
/// Value to which the tx changes the commission rate
pub rate: Decimal,
}

impl Args for TxCommissionRateChange {
fn parse(matches: &ArgMatches) -> Self {
let tx = Tx::parse(matches);
let validator = VALIDATOR.parse(matches);
let rate = COMMISSION_RATE.parse(matches);
Self {
tx,
validator,
rate,
}
}

fn def(app: App) -> App {
app.add_args::<Query>()
.arg(VALIDATOR.def().about(
"The validator's address whose commission rate to change.",
))
.arg(
COMMISSION_RATE
.def()
.about("The desired new commission rate."),
)
}
}

/// Query PoS commission rate
#[derive(Clone, Debug)]
pub struct QueryCommissionRate {
/// Common query args
pub query: Query,
/// Address of a validator
pub validator: WalletAddress,
/// Epoch in which to find commission rate
pub epoch: Option<Epoch>,
}

impl Args for QueryCommissionRate {
fn parse(matches: &ArgMatches) -> Self {
let query = Query::parse(matches);
let validator = VALIDATOR.parse(matches);
let epoch = EPOCH.parse(matches);
Self {
query,
validator,
epoch,
}
}

fn def(app: App) -> App {
app.add_args::<Query>()
.arg(VALIDATOR.def().about(
"The validator's address whose commission rate to query.",
))
.arg(EPOCH.def().about(
"The epoch at which to query (last committed, if not \
specified).",
))
}
}

/// Query PoS slashes
#[derive(Clone, Debug)]
pub struct QuerySlashes {
Expand Down Expand Up @@ -2552,6 +2665,8 @@ pub mod args {
#[derive(Clone, Debug)]
pub struct InitGenesisValidator {
pub alias: String,
pub commission_rate: Decimal,
pub max_commission_rate_change: Decimal,
pub net_address: SocketAddr,
pub unsafe_dont_encrypt: bool,
pub key_scheme: SchemeType,
Expand All @@ -2560,6 +2675,9 @@ pub mod args {
impl Args for InitGenesisValidator {
fn parse(matches: &ArgMatches) -> Self {
let alias = ALIAS.parse(matches);
let commission_rate = COMMISSION_RATE.parse(matches);
let max_commission_rate_change =
MAX_COMMISSION_RATE_CHANGE.parse(matches);
let net_address = NET_ADDRESS.parse(matches);
let unsafe_dont_encrypt = UNSAFE_DONT_ENCRYPT.parse(matches);
let key_scheme = SCHEME.parse(matches);
Expand All @@ -2568,6 +2686,8 @@ pub mod args {
net_address,
unsafe_dont_encrypt,
key_scheme,
commission_rate,
max_commission_rate_change,
}
}

Expand All @@ -2578,6 +2698,15 @@ pub mod args {
Anoma uses port `26656` for P2P connections by default, \
but you can configure a different value.",
))
.arg(COMMISSION_RATE.def().about(
"The commission rate charged by the validator for \
delegation rewards. This is a required parameter.",
))
.arg(MAX_COMMISSION_RATE_CHANGE.def().about(
"The maximum change per epoch in the commission rate \
charged by the validator for delegation rewards. This is \
a required parameter.",
))
.arg(UNSAFE_DONT_ENCRYPT.def().about(
"UNSAFE: Do not encrypt the generated keypairs. Do not \
use this for keys used in a live network.",
Expand Down
57 changes: 57 additions & 0 deletions apps/src/lib/client/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use namada::types::key::*;
use namada::types::storage::{Epoch, Key, KeySeg, PrefixValue};
use namada::types::token::{balance_key, Amount};
use namada::types::{address, storage, token};
use rust_decimal::Decimal;

use crate::cli::{self, args, Context};
use crate::client::tendermint_rpc_types::TxResponse;
Expand Down Expand Up @@ -986,6 +987,62 @@ pub async fn query_voting_power(ctx: Context, args: args::QueryVotingPower) {
println!("Total voting power: {}", total_voting_power);
}

/// Query PoS validator's commission rate
pub async fn query_commission_rate(
ctx: Context,
args: args::QueryCommissionRate,
) {
let epoch = match args.epoch {
Some(epoch) => epoch,
None => query_epoch(args.query.clone()).await,
};
let client = HttpClient::new(args.query.ledger_address.clone()).unwrap();
let validator = ctx.get(&args.validator);
let is_validator =
is_validator(&validator, args.query.ledger_address).await;

if is_validator {
let validator_commission_key =
pos::validator_commission_rate_key(&validator);
let validator_max_commission_change_key =
pos::validator_max_commission_rate_change_key(&validator);
let commission_rates = query_storage_value::<pos::CommissionRates>(
&client,
&validator_commission_key,
)
.await;
let max_rate_change = query_storage_value::<Decimal>(
&client,
&validator_max_commission_change_key,
)
.await;
let max_rate_change =
max_rate_change.expect("No max rate change found");
let commission_rates =
commission_rates.expect("No commission rate found ");
match commission_rates.get(epoch) {
Some(rate) => {
println!(
"Validator {} commission rate: {}, max change per epoch: \
{}",
validator.encode(),
*rate,
max_rate_change,
)
}
None => {
println!(
"No commission rate found for {} in epoch {}",
validator.encode(),
epoch
)
}
}
} else {
println!("Cannot find validator with address {}", validator);
}
}

/// Query PoS slashes
pub async fn query_slashes(ctx: Context, args: args::QuerySlashes) {
let client = HttpClient::new(args.query.ledger_address).unwrap();
Expand Down
Loading

0 comments on commit 1ffd8ff

Please sign in to comment.