diff --git a/Cargo.lock b/Cargo.lock index e6b9260589..e12678c57c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -359,22 +359,6 @@ name = "c_linked_list" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "cbindgen" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "cc" version = "1.0.38" @@ -3672,20 +3656,6 @@ dependencies = [ "untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "solana-sdk-c" -version = "0.18.0-pre0" -dependencies = [ - "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bs58 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cbindgen 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-ed25519-dalek 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "solana-sdk 0.18.0-pre0", -] - [[package]] name = "solana-stake-api" version = "0.18.0-pre0" @@ -5173,7 +5143,6 @@ dependencies = [ "checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum c_linked_list 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4964518bd3b4a8190e832886cdc0da9794f12e8e6c1613a9e90ff331c4c8724b" -"checksum cbindgen 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7e19db9a3892c88c74cbbdcd218196068a928f1b60e736c448b13a1e81f277" "checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" "checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" diff --git a/Cargo.toml b/Cargo.toml index ba45537c57..ea1d2b6bbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "bench-exchange", "bench-streamer", "bench-tps", - "sdk-c", "chacha-sys", "client", "core", diff --git a/multinode-demo/delegate-stake.sh b/multinode-demo/delegate-stake.sh index 2259366c37..86224b2655 100755 --- a/multinode-demo/delegate-stake.sh +++ b/multinode-demo/delegate-stake.sh @@ -28,6 +28,7 @@ OPTIONS: multiple validators in the same workspace --no-airdrop - Do not attempt to airdrop the stake --keypair FILE - Keypair to fund the stake from + --force - Override delegate-stake sanity checks EOF exit 1 @@ -36,6 +37,7 @@ EOF common_args=() label= airdrops_enabled=1 +maybe_force= positional_args=() while [[ -n $1 ]]; do @@ -46,6 +48,9 @@ while [[ -n $1 ]]; do elif [[ $1 = --keypair || $1 = -k ]]; then common_args+=("$1" "$2") shift 2 + elif [[ $1 = --force ]]; then + maybe_force=--force + shift 2 elif [[ $1 = --url || $1 = -u ]]; then url=$2 shift 2 @@ -100,6 +105,6 @@ stake_pubkey=$($solana_keygen pubkey "$stake_keypair_path") set -x $solana_wallet "${common_args[@]}" \ - delegate-stake "$stake_keypair_path" "$vote_pubkey" "$stake_lamports" + delegate-stake $maybe_force "$stake_keypair_path" "$vote_pubkey" "$stake_lamports" $solana_wallet "${common_args[@]}" show-stake-account "$stake_pubkey" diff --git a/net/remote/remote-node.sh b/net/remote/remote-node.sh index e198bdee72..1033a3f22c 100755 --- a/net/remote/remote-node.sh +++ b/net/remote/remote-node.sh @@ -261,7 +261,7 @@ local|tar|skip) waitForNodeToInit if [[ $skipSetup != true && $nodeType != blockstreamer ]]; then - ./multinode-demo/delegate-stake.sh $stake + ./multinode-demo/delegate-stake.sh --force $stake fi ;; replicator) diff --git a/wallet/src/wallet.rs b/wallet/src/wallet.rs index c6a31f966b..55b59008ea 100644 --- a/wallet/src/wallet.rs +++ b/wallet/src/wallet.rs @@ -29,6 +29,7 @@ use solana_sdk::transaction::{Transaction, TransactionError}; use solana_stake_api::stake_instruction; use solana_storage_api::storage_instruction; use solana_vote_api::vote_instruction; +use solana_vote_api::vote_state::VoteState; use std::fs::File; use std::io::Read; use std::net::{IpAddr, SocketAddr}; @@ -50,7 +51,7 @@ pub enum WalletCommand { AuthorizeVoter(Pubkey, Keypair, Pubkey), CreateVoteAccount(Pubkey, Pubkey, u8, u64), ShowVoteAccount(Pubkey), - DelegateStake(Keypair, Pubkey, u64), + DelegateStake(Keypair, Pubkey, u64, bool), WithdrawStake(Keypair, Pubkey, u64), DeactivateStake(Keypair), RedeemVoteCredits(Pubkey, Pubkey), @@ -233,10 +234,12 @@ pub fn parse_command( let stake_account_keypair = keypair_of(matches, "stake_account_keypair_file").unwrap(); let vote_account_pubkey = value_of(matches, "vote_account_pubkey").unwrap(); let lamports_to_stake = matches.value_of("lamports_to_stake").unwrap().parse()?; + let force = matches.is_present("force"); Ok(WalletCommand::DelegateStake( stake_account_keypair, vote_account_pubkey, lamports_to_stake, + force, )) } ("withdraw-stake", Some(matches)) => { @@ -502,7 +505,6 @@ fn process_show_vote_account( ) -> ProcessResult { let vote_account = rpc_client.get_account(vote_account_pubkey)?; - use solana_vote_api::vote_state::VoteState; if vote_account.owner != solana_vote_api::id() { Err(WalletError::RpcRequestError( format!("{:?} is not a vote account", vote_account_pubkey).to_string(), @@ -587,6 +589,7 @@ fn process_delegate_stake( stake_account_keypair: &Keypair, vote_account_pubkey: &Pubkey, lamports: u64, + force: bool, ) -> ProcessResult { let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?; @@ -597,6 +600,39 @@ fn process_delegate_stake( lamports, ); + // Sanity check the vote account to ensure it is attached to a validator that has recently + // voted at the tip of the ledger + let vote_account_data = rpc_client.get_account_data(vote_account_pubkey)?; + let vote_state = VoteState::deserialize(&vote_account_data).map_err(|_| { + WalletError::RpcRequestError( + "Account data could not be deserialized to vote state".to_string(), + ) + })?; + + let sanity_check_result = match vote_state.root_slot { + None => Err(WalletError::DynamicProgramError( + "Vote account has no root slot".to_string(), + )), + Some(root_slot) => { + let slot = rpc_client.get_slot()?; + if root_slot + solana_sdk::timing::DEFAULT_SLOTS_PER_TURN < slot { + Err(WalletError::DynamicProgramError( + "Vote account root slot is too old".to_string(), + )) + } else { + Ok(()) + } + } + }; + + if sanity_check_result.is_err() { + if !force { + sanity_check_result?; + } else { + println!("--force supplied, ignoring: {:?}", sanity_check_result); + } + } + let mut tx = Transaction::new_signed_with_payer( ixs, Some(&config.keypair.pubkey()), @@ -1056,15 +1092,19 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult { process_show_vote_account(&rpc_client, config, &vote_account_pubkey) } - WalletCommand::DelegateStake(stake_account_keypair, vote_account_pubkey, lamports) => { - process_delegate_stake( - &rpc_client, - config, - &stake_account_keypair, - &vote_account_pubkey, - *lamports, - ) - } + WalletCommand::DelegateStake( + stake_account_keypair, + vote_account_pubkey, + lamports, + force, + ) => process_delegate_stake( + &rpc_client, + config, + &stake_account_keypair, + &vote_account_pubkey, + *lamports, + *force, + ), WalletCommand::WithdrawStake( stake_account_keypair, @@ -1403,6 +1443,13 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .subcommand( SubCommand::with_name("delegate-stake") .about("Delegate stake to a vote account") + .arg( + Arg::with_name("force") + .long("force") + .takes_value(false) + .hidden(true) // Don't document this argument to discourage its use + .help("Override vote account sanity checks (use carefully!)"), + ) .arg( Arg::with_name("stake_account_keypair_file") .index(1) @@ -1883,7 +1930,21 @@ mod tests { ]); assert_eq!( parse_command(&pubkey, &test_delegate_stake).unwrap(), - WalletCommand::DelegateStake(keypair, pubkey, 42) + WalletCommand::DelegateStake(keypair, pubkey, 42, false) + ); + + let keypair = read_keypair(&keypair_file).unwrap(); + let test_delegate_stake = test_commands.clone().get_matches_from(vec![ + "test", + "delegate-stake", + "--force", + &keypair_file, + &pubkey_string, + "42", + ]); + assert_eq!( + parse_command(&pubkey, &test_delegate_stake).unwrap(), + WalletCommand::DelegateStake(keypair, pubkey, 42, true) ); // Test WithdrawStake Subcommand @@ -2072,11 +2133,15 @@ mod tests { let signature = process_command(&config); assert_eq!(signature.unwrap(), SIGNATURE.to_string()); + // TODO: Need to add mock GetAccountInfo to mock_rpc_client_request.rs to re-enable the + // DeactivateStake test. + /* let bob_keypair = Keypair::new(); let node_pubkey = Pubkey::new_rand(); - config.command = WalletCommand::DelegateStake(bob_keypair.into(), node_pubkey, 100); + config.command = WalletCommand::DelegateStake(bob_keypair.into(), node_pubkey, 100, true); let signature = process_command(&config); assert_eq!(signature.unwrap(), SIGNATURE.to_string()); + */ let bob_keypair = Keypair::new(); let to_pubkey = Pubkey::new_rand();