pcli/command/tx/proposal.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
use anyhow::{Context, Result};
use penumbra_app::params::AppParameters;
use penumbra_governance::{change::ParameterChange, Proposal, ProposalPayload};
use penumbra_proto::DomainType;
use penumbra_transaction::TransactionPlan;
use super::FeeTier;
#[derive(Debug, clap::Subcommand)]
pub enum ProposalCmd {
/// Make a template file for a new proposal.
Template {
/// The file to output the template to.
#[clap(long, global = true)]
file: Option<camino::Utf8PathBuf>,
/// The kind of the proposal to template [one of: signaling, emergency, parameter-change, or community-pool-spend].
#[clap(subcommand)]
kind: ProposalKindCmd,
},
/// Submit a new governance proposal.
Submit {
/// The proposal to vote on, in TOML format.
#[clap(long)]
file: camino::Utf8PathBuf,
/// Only spend funds originally received by the given account.
#[clap(long, default_value = "0")]
source: u32,
/// The amount of the staking token to deposit alongside the proposal.
#[clap(long, default_value = "")]
deposit_amount: String,
/// The selected fee tier to multiply the fee amount by.
#[clap(short, long, default_value_t)]
fee_tier: FeeTier,
},
/// Withdraw a governance proposal that you previously submitted.
Withdraw {
/// The proposal id to withdraw.
proposal_id: u64,
/// A short description of the reason for the proposal being withdrawn, meant to be
/// displayed to users.
#[clap(long)]
reason: String,
/// Only spend funds originally received by the given account.
#[clap(long, default_value = "0")]
source: u32,
/// The selected fee tier to multiply the fee amount by.
#[clap(short, long, default_value_t)]
fee_tier: FeeTier,
},
/// Claim a governance proposal deposit for a proposal you submitted that has finished voting.
///
/// This consumes the voting or withdrawn proposal NFT and mints an NFT representing whether the
/// proposal passed, failed, or was slashed. In the case of a slash, the deposit is not returned
/// by this action; in other cases, it is returned to you.
DepositClaim {
/// The proposal id to claim the deposit for.
proposal_id: u64,
/// Only spend funds originally received by the given account.
#[clap(long, default_value = "0")]
source: u32,
/// The selected fee tier to multiply the fee amount by.
#[clap(short, long, default_value_t)]
fee_tier: FeeTier,
},
}
#[derive(Debug, clap::Subcommand)]
pub enum ProposalKindCmd {
/// Generate a template for a signaling proposal.
Signaling,
/// Generate a template for an emergency proposal.
Emergency,
/// Generate a template for a parameter change proposal.
ParameterChange,
/// Generate a template for a Community Pool spend proposal.
CommunityPoolSpend {
/// The transaction plan to include in the proposal, in JSON format.
///
/// If not specified, the default empty transaction plan will be included, to be replaced
/// in the template before submission.
#[clap(long)]
transaction_plan: Option<camino::Utf8PathBuf>,
},
/// Generate a template for an upgrade proposal,
UpgradePlan,
}
impl ProposalKindCmd {
/// Generate a default proposal of a particular kind.
pub fn template_proposal(&self, app_params: &AppParameters, id: u64) -> Result<Proposal> {
let title = "A short title (at most 80 characters)".to_string();
let description = "A longer description (at most 10,000 characters)".to_string();
let payload = match self {
ProposalKindCmd::Signaling => ProposalPayload::Signaling { commit: None },
ProposalKindCmd::Emergency => ProposalPayload::Emergency { halt_chain: false },
ProposalKindCmd::ParameterChange => {
ProposalPayload::ParameterChange(ParameterChange::encode_parameters(
serde_json::value::to_value(app_params.clone())?,
))
}
ProposalKindCmd::CommunityPoolSpend { transaction_plan } => {
if let Some(file) = transaction_plan {
ProposalPayload::CommunityPoolSpend {
transaction_plan: serde_json::from_reader(
std::fs::File::open(file).with_context(|| {
format!("Failed to open transaction plan file {:?}", file)
})?,
)
.with_context(|| {
format!("Failed to parse transaction plan file {:?}", file)
})?,
}
} else {
ProposalPayload::CommunityPoolSpend {
transaction_plan: TransactionPlan::default().encode_to_vec(),
}
}
}
ProposalKindCmd::UpgradePlan { .. } => ProposalPayload::UpgradePlan { height: 0 },
};
Ok(Proposal {
id,
title,
description,
payload,
})
}
}
impl ProposalCmd {
pub fn offline(&self) -> bool {
match self {
ProposalCmd::Template { .. } => false,
ProposalCmd::Submit { .. } => false,
ProposalCmd::Withdraw { .. } => false,
ProposalCmd::DepositClaim { .. } => false,
}
}
}