CLI: Add multi-session signing support (#8927)

* SDK: Add `NullSigner` implementation

* SDK: Split `Transaction::verify()` to gain access to results

* CLI: Minor refactor of --sign_only result parsing

* CLI: Enable paritial signing

Signers specified by pubkey, but without a matching --signer arg
supplied fall back to a `NullSigner` when --sign-only is in effect.
This allows their pubkey to be used for TX construction as usual,
but leaves their `sign_message()` a NOP. As such, with --sign-only
in effect, signing and verification must be done separately, with
the latter's per-signature results considered

* CLI: Surface/report missing/bad signers to user

* CLI: Suppress --sign-only JSON output

* nits

* Docs for multi-session offline signing
This commit is contained in:
Trent Nelson
2020-03-18 21:49:38 -06:00
committed by GitHub
parent aeb7278b00
commit 98228c392e
12 changed files with 455 additions and 86 deletions

View File

@ -1,4 +1,3 @@
use solana_clap_utils::keypair::presigner_from_pubkey_sigs;
use solana_cli::{
cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
nonce,
@ -388,9 +387,11 @@ fn test_offline_stake_delegation_and_deactivation() {
fee_payer: 0,
};
let sig_response = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner =
presigner_from_pubkey_sigs(&config_offline.signers[0].pubkey(), &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only
.presigner_of(&config_offline.signers[0].pubkey())
.unwrap();
config_payer.signers = vec![&offline_presigner];
config_payer.command = CliCommand::DelegateStake {
stake_account_pubkey: stake_keypair.pubkey(),
@ -417,9 +418,11 @@ fn test_offline_stake_delegation_and_deactivation() {
fee_payer: 0,
};
let sig_response = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner =
presigner_from_pubkey_sigs(&config_offline.signers[0].pubkey(), &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only
.presigner_of(&config_offline.signers[0].pubkey())
.unwrap();
config_payer.signers = vec![&offline_presigner];
config_payer.command = CliCommand::DeactivateStake {
stake_account_pubkey: stake_keypair.pubkey(),
@ -679,9 +682,9 @@ fn test_stake_authorize() {
fee_payer: 0,
};
let sign_reply = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sign_reply);
let offline_presigner =
presigner_from_pubkey_sigs(&offline_authority_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_reply);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only.presigner_of(&offline_authority_pubkey).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -739,12 +742,11 @@ fn test_stake_authorize() {
fee_payer: 0,
};
let sign_reply = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sign_reply);
assert_eq!(blockhash, nonce_hash);
let offline_presigner =
presigner_from_pubkey_sigs(&offline_authority_pubkey, &signers).unwrap();
let nonced_authority_presigner =
presigner_from_pubkey_sigs(&nonced_authority_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_reply);
assert!(sign_only.has_all_signers());
assert_eq!(sign_only.blockhash, nonce_hash);
let offline_presigner = sign_only.presigner_of(&offline_authority_pubkey).unwrap();
let nonced_authority_presigner = sign_only.presigner_of(&nonced_authority_pubkey).unwrap();
config.signers = vec![&offline_presigner, &nonced_authority_presigner];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -754,7 +756,7 @@ fn test_stake_authorize() {
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
blockhash,
sign_only.blockhash,
),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
@ -816,6 +818,7 @@ fn test_stake_authorize_with_fee_payer() {
let mut config_offline = CliConfig::default();
let offline_signer = Keypair::new();
config_offline.signers = vec![&offline_signer];
config_offline.json_rpc_url = String::new();
let offline_pubkey = config_offline.signers[0].pubkey();
// Verify we're offline
config_offline.command = CliCommand::ClusterVersion;
@ -886,8 +889,9 @@ fn test_stake_authorize_with_fee_payer() {
fee_payer: 0,
};
let sign_reply = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sign_reply);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sign_reply);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::StakeAuthorize {
stake_account_pubkey,
@ -1023,8 +1027,9 @@ fn test_stake_split() {
fee_payer: 0,
};
let sig_response = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
config.signers = vec![&offline_presigner, &split_account];
config.command = CliCommand::SplitStake {
stake_account_pubkey: stake_account_pubkey,
@ -1032,7 +1037,7 @@ fn test_stake_split() {
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
blockhash,
sign_only.blockhash,
),
nonce_account: Some(nonce_account.pubkey()),
nonce_authority: 0,
@ -1275,8 +1280,9 @@ fn test_stake_set_lockup() {
fee_payer: 0,
};
let sig_response = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::StakeSetLockup {
stake_account_pubkey,
@ -1285,7 +1291,7 @@ fn test_stake_set_lockup() {
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_account_pubkey),
blockhash,
sign_only.blockhash,
),
nonce_account: Some(nonce_account_pubkey),
nonce_authority: 0,
@ -1392,9 +1398,10 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
from: 0,
};
let sig_response = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
let stake_presigner = presigner_from_pubkey_sigs(&stake_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
assert!(sign_only.has_all_signers());
let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
let stake_presigner = sign_only.presigner_of(&stake_pubkey).unwrap();
config.signers = vec![&offline_presigner, &stake_presigner];
config.command = CliCommand::CreateStakeAccount {
stake_account: 1,
@ -1406,7 +1413,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_pubkey),
blockhash,
sign_only.blockhash,
),
nonce_account: Some(nonce_pubkey),
nonce_authority: 0,
@ -1438,8 +1445,8 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
fee_payer: 0,
};
let sig_response = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
config.signers = vec![&offline_presigner];
config.command = CliCommand::WithdrawStake {
stake_account_pubkey: stake_pubkey,
@ -1449,7 +1456,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_pubkey),
blockhash,
sign_only.blockhash,
),
nonce_account: Some(nonce_pubkey),
nonce_authority: 0,
@ -1482,9 +1489,9 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
from: 0,
};
let sig_response = process_command(&config_offline).unwrap();
let (blockhash, signers) = parse_sign_only_reply_string(&sig_response);
let offline_presigner = presigner_from_pubkey_sigs(&offline_pubkey, &signers).unwrap();
let stake_presigner = presigner_from_pubkey_sigs(&stake_pubkey, &signers).unwrap();
let sign_only = parse_sign_only_reply_string(&sig_response);
let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
let stake_presigner = sign_only.presigner_of(&stake_pubkey).unwrap();
config.signers = vec![&offline_presigner, &stake_presigner];
config.command = CliCommand::CreateStakeAccount {
stake_account: 1,
@ -1496,7 +1503,7 @@ fn test_offline_nonced_create_stake_account_and_withdraw() {
sign_only: false,
blockhash_query: BlockhashQuery::FeeCalculator(
blockhash_query::Source::NonceAccount(nonce_pubkey),
blockhash,
sign_only.blockhash,
),
nonce_account: Some(nonce_pubkey),
nonce_authority: 0,