Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

CLI: Remote wallet signing#8318

Closed
t-nelson wants to merge 32 commits into
solana-labs:masterfrom
t-nelson:cli-remote-wallet-signing
Closed

CLI: Remote wallet signing#8318
t-nelson wants to merge 32 commits into
solana-labs:masterfrom
t-nelson:cli-remote-wallet-signing

Conversation

@t-nelson
Copy link
Copy Markdown
Contributor

@t-nelson t-nelson commented Feb 18, 2020

Problem

Our signer interfaces are too restrictive to accommodate a rich signing experience. This makes things like hardware wallets, offline signing and certain instruction constructions difficult to implement and mixed signer types impossible.

Summary of Changes

Co-conspirator: @CriesofCarrots

  • Consolidate signer CLI argument specification and support HW wallet paths
  • Replace KeypairUtil generics-based interfaces with KeypairUtil trait-objects-based ones
  • Replace Keypair data structure fields with Box<dyn KeypairUtil> where appropriate
  • Plumbing and adaptations throughout
  • Doc updates

For review purposes:
The most essential changes in this PR can be seen in sdk/src/transaction.rs (use of KeypairUtil trait objects), and the generate_keypair_util() method in clap-utils/src/keypair.rs demonstrates how keypair inputs are parsed into different KeypairUtil objects.

Tyera Eulberg and others added 29 commits February 18, 2020 08:27
what to do about write_keypair outstanding
This reverts commit 88fe185.

Create-from-seed should be re-enabled in another changeset
@t-nelson t-nelson requested a review from jstarry February 18, 2020 15:58
@t-nelson t-nelson requested review from jackcmay and mvines February 18, 2020 15:58
@t-nelson
Copy link
Copy Markdown
Contributor Author

Supersedes #8252

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 18, 2020

Codecov Report

Merging #8318 into master will increase coverage by <.1%.
The diff coverage is 68.8%.

@@           Coverage Diff            @@
##           master   #8318     +/-   ##
========================================
+ Coverage    80.5%   80.5%   +<.1%     
========================================
  Files         254     254             
  Lines       55477   55386     -91     
========================================
- Hits        44700   44641     -59     
+ Misses      10777   10745     -32

Comment thread sdk/src/signature.rs

impl PartialEq for dyn KeypairUtil {
fn eq(&self, other: &dyn KeypairUtil) -> bool {
self.pubkey() == other.pubkey()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit dangerous since it would return true if both keypairs return an error for pubkey. How about slapping a #[cfg(test)] on here along with a warning comment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, PartialEq is a requirement on the CliCommand enum, so gating to cfg(test) won't quite work. @CriesofCarrots is also working on some KeypairUtil filtering that will require it.

I think we can eliminate the default behavior. I'll address it in a reply to one of your later comments where more appropriate

Comment thread keygen/src/keygen.rs
derivation_of(matches, "derivation_path"),
)?)),
}
generate_keypair_util(matches, path, "pubkey recovery")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is pretty confusing to me. Maybe keypair_util_from_path?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I also suggest that we rename KeypairUtil to Signer?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can work something out here 😉

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can work something out here 😉

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for Signer and ergo signer_from_path()(?)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That said, the Signer rename is going to cause a ton of churn. Separate pr, perhaps?

fn tx_uses_nonce_empty_ix_fail() {
let tx =
Transaction::new_signed_instructions(&[&Keypair::new(); 0], vec![], Hash::default());
let tx = Transaction::new_signed_instructions(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: &vec![] is more succint

Comment thread sdk-c/src/lib.rs
return 1;
};
let keypairs_ref: Vec<&KeypairNative> = keypairs.iter().collect();
let mut keypairs_ref: Vec<&dyn KeypairUtil> = Vec::new();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you could coerce like this as well. Not much cleaner though 🤷‍♂

    let keypairs_ref = keypairs
        .iter()
        .map(|k| -> &dyn KeypairUtil { k })
        .collect::<Vec<_>>();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we couldn't come up with anything very aesthetically pleasing either 😕

Comment thread cli/src/cli.rs
};

if let Some(signers) = signers {
replace_signatures(&mut tx, &signers)?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like fn replace_signatures is dead code now. I'm confused why we don't need this anymore, could you please explain?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah I was expecting clippy to nag about this one.

replace_signatures() is unnecessary as of Presigner

solana/sdk/src/signature.rs

Lines 170 to 175 in d0bcde0

#[derive(Debug, Default)]
pub struct Presigner {
pubkey: Pubkey,
signature: Signature,
}

We can now emit the correct signature in sign_message(), so no post-processing is required. This is a step toward supporting TX construction over multiple signing sessions

Comment thread cli/src/main.rs
} = parse_command(&matches)?;

let (keypair, keypair_path) = if require_keypair {
let KeypairWithSource { keypair, source } = keypair_input(&matches, "keypair")?;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you can remove the global ASK_SEED_PHRASE_ARG arg now

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh... Github, what are you doing?

Comment thread clap-utils/src/keypair.rs
derivation_of(matches, "derivation_path"),
)?)),
KeypairUrl::Pubkey(pubkey) => {
let presigner = pubkeys_sigs_of(matches, "signer")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"signer" > SIGNER_ARG.name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I meant to come back to this one. I'm pretty sure that would create a cli <-> clap_utils circular dependency. Probably need to move the constant into the latter

/// Create and sign new system_instruction::Assign transaction
pub fn assign(from_keypair: &Keypair, recent_blockhash: Hash, program_id: &Pubkey) -> Transaction {
pub fn assign(
from_keypair: &dyn KeypairUtil,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it easier to create bad transactions. Anything which takes a KeypairUtil could create bad pubkeys or bad signatures because of the fallback behavior in KeypairUtil::pubkey() and KeypairUtil::sign_message. I'm not sure it's a good idea to make this easier. Anyone who creates a system transaction with a remote keypair or presigner keypair will have to be aware that the transaction they create, might fail due to an error that isn't bubbled up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can eliminate the default fallbacks. sign_message() has a manageable number of callers, many just setting up tests, so probably not too bad to make fallible throughout. pubkey() however, is called all over the place. I believe it can be made infallible, though. Any implementers that might fail, can query and cache the pubkey at construction and fail there instead. @CriesofCarrots wdyt?

@jstarry would that alleviate your concerns RE default fallbacks?

@garious
Copy link
Copy Markdown
Contributor

garious commented Feb 19, 2020

I'm surprised to see existing methods generalized here, which results in changing lots and lots of code. What about the approach of adding new methods to KeypairUtil and Client and only using them in the cases where multi-sig was needed from the CLI?

@garious
Copy link
Copy Markdown
Contributor

garious commented Feb 21, 2020

@t-nelson, does this PR still have a future? Or is it an earlier version of what @CriesofCarrots is working on?

@t-nelson t-nelson deleted the cli-remote-wallet-signing branch February 21, 2020 18:33
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants