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

implicit VP #592

Merged
merged 15 commits into from
Nov 30, 2022
Merged
Show file tree
Hide file tree
Changes from 14 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
6 changes: 6 additions & 0 deletions .changelog/unreleased/features/592-implicit-vp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Added a validity predicate for implicit accounts. This is set in
protocol parameters and may be changed via governance. Additionally,
added automatic public key reveal in the client that use an implicit
account that hasn't revealed its PK yet as a source. It's also
possible to manually submit reveal transaction with client command
([#592](https://github.com/anoma/namada/pull/592))
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 @@ -39,6 +39,9 @@ pub async fn main() -> Result<()> {
Sub::TxVoteProposal(TxVoteProposal(args)) => {
tx::submit_vote_proposal(ctx, args).await;
}
Sub::TxRevealPk(TxRevealPk(args)) => {
tx::submit_reveal_pk(ctx, args).await;
}
Sub::Bond(Bond(args)) => {
tx::submit_bond(ctx, args).await;
}
Expand Down
1 change: 1 addition & 0 deletions apps/src/bin/anoma/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ fn handle_command(cmd: cli::cmds::Anoma, raw_sub_cmd: String) -> Result<()> {
| cli::cmds::Anoma::TxCustom(_)
| cli::cmds::Anoma::TxTransfer(_)
| cli::cmds::Anoma::TxUpdateVp(_)
| cli::cmds::Anoma::TxRevealPk(_)
| cli::cmds::Anoma::TxInitNft(_)
| cli::cmds::Anoma::TxMintNft(_)
| cli::cmds::Anoma::TxInitProposal(_)
Expand Down
64 changes: 62 additions & 2 deletions apps/src/lib/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub mod cmds {
TxMintNft(TxMintNft),
TxInitProposal(TxInitProposal),
TxVoteProposal(TxVoteProposal),
TxRevealPk(TxRevealPk),
}

impl Cmd for Anoma {
Expand All @@ -66,6 +67,7 @@ pub mod cmds {
.subcommand(TxMintNft::def())
.subcommand(TxInitProposal::def())
.subcommand(TxVoteProposal::def())
.subcommand(TxRevealPk::def())
}

fn parse(matches: &ArgMatches) -> Option<Self> {
Expand All @@ -82,6 +84,7 @@ pub mod cmds {
SubCmd::parse(matches).map(Self::TxInitProposal);
let tx_vote_proposal =
SubCmd::parse(matches).map(Self::TxVoteProposal);
let tx_reveal_pk = SubCmd::parse(matches).map(Self::TxRevealPk);
node.or(client)
.or(wallet)
.or(ledger)
Expand All @@ -92,6 +95,7 @@ pub mod cmds {
.or(tx_nft_mint)
.or(tx_init_proposal)
.or(tx_vote_proposal)
.or(tx_reveal_pk)
}
}

Expand Down Expand Up @@ -154,14 +158,15 @@ pub mod cmds {
.subcommand(TxTransfer::def().display_order(1))
.subcommand(TxUpdateVp::def().display_order(1))
.subcommand(TxInitAccount::def().display_order(1))
.subcommand(TxInitValidator::def().display_order(1))
.subcommand(TxRevealPk::def().display_order(1))
// Nft transactions
.subcommand(TxInitNft::def().display_order(1))
.subcommand(TxMintNft::def().display_order(1))
// Proposal transactions
.subcommand(TxInitProposal::def().display_order(1))
.subcommand(TxVoteProposal::def().display_order(1))
// PoS transactions
.subcommand(TxInitValidator::def().display_order(2))
.subcommand(Bond::def().display_order(2))
.subcommand(Unbond::def().display_order(2))
.subcommand(Withdraw::def().display_order(2))
Expand All @@ -188,6 +193,7 @@ pub mod cmds {
let tx_init_account = Self::parse_with_ctx(matches, TxInitAccount);
let tx_init_validator =
Self::parse_with_ctx(matches, TxInitValidator);
let tx_reveal_pk = Self::parse_with_ctx(matches, TxRevealPk);
let tx_nft_create = Self::parse_with_ctx(matches, TxInitNft);
let tx_nft_mint = Self::parse_with_ctx(matches, TxMintNft);
let tx_init_proposal =
Expand Down Expand Up @@ -215,11 +221,12 @@ pub mod cmds {
.or(tx_transfer)
.or(tx_update_vp)
.or(tx_init_account)
.or(tx_init_validator)
.or(tx_reveal_pk)
.or(tx_nft_create)
.or(tx_nft_mint)
.or(tx_init_proposal)
.or(tx_vote_proposal)
.or(tx_init_validator)
.or(bond)
.or(unbond)
.or(withdraw)
Expand Down Expand Up @@ -279,6 +286,7 @@ pub mod cmds {
TxMintNft(TxMintNft),
TxInitProposal(TxInitProposal),
TxVoteProposal(TxVoteProposal),
TxRevealPk(TxRevealPk),
Bond(Bond),
Unbond(Unbond),
Withdraw(Withdraw),
Expand Down Expand Up @@ -1122,6 +1130,36 @@ pub mod cmds {
}
}

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

impl SubCmd for TxRevealPk {
const CMD: &'static str = "reveal-pk";

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

fn def() -> App {
App::new(Self::CMD)
.about(
"Submit a tx to reveal the public key an implicit \
account. Typically, you don't have to do this manually \
and the client will detect when a tx to reveal PK is \
needed and submit it automatically. This will write the \
PK into the account's storage so that it can be used for \
signature verification on transactions authorized by \
this account.",
)
.add_args::<args::RevealPk>()
}
}

#[derive(Clone, Debug)]
pub enum Utils {
JoinNetwork(JoinNetwork),
Expand Down Expand Up @@ -1863,6 +1901,28 @@ pub mod args {
}
}

#[derive(Clone, Debug)]
pub struct RevealPk {
/// Common tx arguments
pub tx: Tx,
/// A public key to be revealed on-chain
pub public_key: WalletPublicKey,
}

impl Args for RevealPk {
fn parse(matches: &ArgMatches) -> Self {
let tx = Tx::parse(matches);
let public_key = PUBLIC_KEY.parse(matches);

Self { tx, public_key }
}

fn def(app: App) -> App {
app.add_args::<Tx>()
.arg(PUBLIC_KEY.def().about("A public key to reveal."))
}
}

#[derive(Clone, Debug)]
pub struct QueryProposal {
/// Common query args
Expand Down
13 changes: 13 additions & 0 deletions apps/src/lib/client/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,25 @@ pub async fn sign_tx(
) -> (Context, TxBroadcastData) {
let (tx, keypair) = if let Some(signing_key) = &args.signing_key {
let signing_key = ctx.get_cached(signing_key);

// Check if the signing key needs to reveal its PK first
let pk: common::PublicKey = signing_key.ref_to();
super::tx::reveal_pk_if_needed(&mut ctx, &pk, args).await;

(tx.sign(&signing_key), signing_key)
} else if let Some(signer) = args.signer.as_ref().or(default) {
let signer = ctx.get(signer);
let signing_key =
find_keypair(&mut ctx.wallet, &signer, args.ledger_address.clone())
.await;

// Check if the signer is implicit account that needs to reveal its PK
// first
if matches!(signer, Address::Implicit(_)) {
let pk: common::PublicKey = signing_key.ref_to();
super::tx::reveal_pk_if_needed(&mut ctx, &pk, args).await;
}

(tx.sign(&signing_key), signing_key)
} else {
panic!(
Expand Down
109 changes: 109 additions & 0 deletions apps/src/lib/client/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const TX_INIT_ACCOUNT_WASM: &str = "tx_init_account.wasm";
const TX_INIT_VALIDATOR_WASM: &str = "tx_init_validator.wasm";
const TX_INIT_PROPOSAL: &str = "tx_init_proposal.wasm";
const TX_VOTE_PROPOSAL: &str = "tx_vote_proposal.wasm";
const TX_REVEAL_PK: &str = "tx_reveal_pk.wasm";
const TX_UPDATE_VP_WASM: &str = "tx_update_vp.wasm";
const TX_TRANSFER_WASM: &str = "tx_transfer.wasm";
const TX_INIT_NFT: &str = "tx_init_nft.wasm";
Expand Down Expand Up @@ -817,6 +818,114 @@ pub async fn submit_vote_proposal(mut ctx: Context, args: args::VoteProposal) {
}
}

pub async fn submit_reveal_pk(mut ctx: Context, args: args::RevealPk) {
let args::RevealPk {
tx: args,
public_key,
} = args;
let public_key = ctx.get_cached(&public_key);
if !reveal_pk_if_needed(&mut ctx, &public_key, &args).await {
let addr: Address = (&public_key).into();
println!("PK for {addr} is already revealed, nothing to do.");
}
}

pub async fn reveal_pk_if_needed(
ctx: &mut Context,
public_key: &common::PublicKey,
args: &args::Tx,
) -> bool {
let addr: Address = public_key.into();
// Check if PK revealed
if args.force || !has_revealed_pk(&addr, args.ledger_address.clone()).await
{
// If not, submit it
submit_reveal_pk_aux(ctx, public_key, args).await;
true
} else {
false
}
}

pub async fn has_revealed_pk(
addr: &Address,
ledger_address: TendermintAddress,
) -> bool {
rpc::get_public_key(addr, ledger_address).await.is_some()
}

pub async fn submit_reveal_pk_aux(
ctx: &mut Context,
public_key: &common::PublicKey,
args: &args::Tx,
) {
let addr: Address = public_key.into();
println!("Submitting a tx to reveal the public key for address {addr}...");
let tx_data = public_key
.try_to_vec()
.expect("Encoding a public key shouldn't fail");
let tx_code = ctx.read_wasm(TX_REVEAL_PK);
let tx = Tx::new(tx_code, Some(tx_data));

// submit_tx without signing the inner tx
let keypair = if let Some(signing_key) = &args.signing_key {
ctx.get_cached(signing_key)
} else if let Some(signer) = args.signer.as_ref() {
let signer = ctx.get(signer);
find_keypair(&mut ctx.wallet, &signer, args.ledger_address.clone())
.await
} else {
find_keypair(&mut ctx.wallet, &addr, args.ledger_address.clone()).await
};
let epoch = rpc::query_epoch(args::Query {
ledger_address: args.ledger_address.clone(),
})
.await;
let to_broadcast = if args.dry_run {
TxBroadcastData::DryRun(tx)
} else {
super::signing::sign_wrapper(ctx, args, epoch, tx, &keypair).await
};

if args.dry_run {
if let TxBroadcastData::DryRun(tx) = to_broadcast {
rpc::dry_run_tx(&args.ledger_address, tx.to_bytes()).await;
} else {
panic!(
"Expected a dry-run transaction, received a wrapper \
transaction instead"
);
}
} else {
// Either broadcast or submit transaction and collect result into
// sum type
let result = if args.broadcast_only {
Left(broadcast_tx(args.ledger_address.clone(), &to_broadcast).await)
} else {
Right(submit_tx(args.ledger_address.clone(), to_broadcast).await)
};
// Return result based on executed operation, otherwise deal with
// the encountered errors uniformly
match result {
Right(Err(err)) => {
eprintln!(
"Encountered error while broadcasting transaction: {}",
err
);
safe_exit(1)
}
Left(Err(err)) => {
eprintln!(
"Encountered error while broadcasting transaction: {}",
err
);
safe_exit(1)
}
_ => {}
}
}
}

/// Check if current epoch is in the last third of the voting period of the
/// proposal. This ensures that it is safe to optimize the vote writing to
/// storage.
Expand Down
Loading