solana_clap_utils::keypair

Function signer_from_path

source
pub fn signer_from_path(
    matches: &ArgMatches<'_>,
    path: &str,
    keypair_name: &str,
    wallet_manager: &mut Option<Rc<RemoteWalletManager>>,
) -> Result<Box<dyn Signer>, Box<dyn Error>>
Expand description

Loads a Signer from one of several possible sources.

The path is not strictly a file system path, but is interpreted as various types of signing source, depending on its format, one of which is a path to a keypair file. Some sources may require user interaction in the course of calling this function.

The result of this function is a boxed object of the Signer trait. To load a concrete Keypair, use the keypair_from_path function, though note that it does not support all signer sources.

The matches argument is the same set of parsed clap matches from which path was parsed. It is used to parse various additional command line arguments, depending on which signing source is requested, as described below in “Signing sources”.

The keypair_name argument is the “name” of the signer, and is typically the name of the clap argument from which the path argument was parsed, like “keypair”, “from”, or “fee-payer”. It is used solely for interactively prompting the user, either when entering seed phrases or selecting from multiple hardware wallets.

The wallet_manager is used for establishing connections to a hardware device such as Ledger. If wallet_manager is a reference to None, and a hardware signer is requested, then this function will attempt to create a wallet manager, assigning it to the mutable wallet_manager reference. This argument is typically a reference to None.

§Signing sources

The path argument can simply be a path to a keypair file, but it may also be interpreted in several other ways, in the following order.

Firstly, the path argument may be interpreted as a URI, with the URI scheme indicating where to load the signer from. If it parses as a URI, then the following schemes are supported:

  • file: — Read the keypair from a JSON keypair file. The path portion of the URI is the file path.

  • stdin: — Read the keypair from stdin, in the JSON format used by the keypair file.

    Non-scheme parts of the URI are ignored.

  • prompt: — The user will be prompted at the command line for their seed phrase and passphrase.

    In this URI the query string may contain zero or one of the following key/value pairs that determine the BIP44 derivation path of the private key from the seed:

    • key — In this case the value is either one or two numerical indexes separated by a slash, which represent the “account”, and “change” components of the BIP44 derivation path. Example: key=0/0.

    • full-path — In this case the value is a full derivation path, and the user is responsible for ensuring it is correct. Example: full-path=m/44/501/0/0/0.

    If neither is provided, then the default derivation path is used.

    Note that when specifying derivation paths, this routine will convert all indexes into “hardened” indexes, even if written as “normal” indexes.

    Other components of the URI besides the scheme and query string are ignored.

    If the “skip_seed_phrase_validation” argument, as defined in SKIP_SEED_PHRASE_VALIDATION_ARG is found in matches, then the keypair seed will be generated directly from the seed phrase, without parsing or validating it as a BIP39 seed phrase. This allows the use of non-BIP39 seed phrases.

  • usb: — Use a USB hardware device as the signer. In this case, the URI host indicates the device type, and is required. The only currently valid host value is “ledger”.

    Optionally, the first segment of the URI path indicates the base-58 encoded pubkey of the wallet, and the “account” and “change” indices of the derivation path can be specified with the key= query parameter, as with the prompt: URI.

    Examples:

    • usb://ledger
    • usb://ledger?key=0/0
    • usb://ledger/9rPVSygg3brqghvdZ6wsL2i5YNQTGhXGdJzF65YxaCQd
    • usb://ledger/9rPVSygg3brqghvdZ6wsL2i5YNQTGhXGdJzF65YxaCQd?key=0/0

Next the path argument may be one of the following strings:

  • - — Read the keypair from stdin. This is the same as the stdin: URI scheme.

  • ASK — The user will be prompted at the command line for their seed phrase and passphrase. This uses a legacy key derivation method and should usually be avoided in favor of prompt:.

Next, if the path argument parses as a base-58 public key, then the signer is created without a private key, but with presigned signatures, each parsed from the additional command line arguments, provided by the matches argument.

In this case, the remaining command line arguments are searched for clap arguments named “signer”, as defined by SIGNER_ARG, and each is parsed as a key-value pair of the form “pubkey=signature”, where pubkey is the same base-58 public key, and signature is a serialized signature produced by the corresponding keypair. One of the “signer” signatures must be for the pubkey specified in path or this function will return an error; unless the “sign_only” clap argument, as defined by SIGN_ONLY_ARG, is present in matches, in which case the signer will be created with no associated signatures.

Finally, if path, interpreted as a file path, represents a file on disk, then the signer is created by reading that file as a JSON-serialized keypair. This is the same as the file: URI scheme.

§Examples

This shows a reasonable way to set up clap to parse all possible signer sources. Note the use of the OfflineArgs::offline_args method to add correct clap definitions of the --signer and --sign-only arguments, as required by the base-58 pubkey offline signing method.

use clap::{App, Arg, value_t_or_exit};
use solana_clap_utils::keypair::signer_from_path;
use solana_clap_utils::offline::OfflineArgs;

let clap_app = App::new("my-program")
    // The argument we'll parse as a signer "path"
    .arg(Arg::with_name("keypair")
        .required(true)
        .help("The default signer"))
    .offline_args();

let clap_matches = clap_app.get_matches();
let keypair_str = value_t_or_exit!(clap_matches, "keypair", String);
let mut wallet_manager = None;
let signer = signer_from_path(
    &clap_matches,
    &keypair_str,
    "keypair",
    &mut wallet_manager,
)?;