Add deploy err if program-account balance is too high (#13091)

* Add deploy err if program-account balance is too high

* Review comments

* Add system-program check

* Rename and unhide flag
This commit is contained in:
Tyera Eulberg 2020-10-22 22:42:35 -06:00 committed by GitHub
parent 368aeb2cee
commit 4669fa0f98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 5 deletions

View File

@ -49,6 +49,7 @@ use solana_sdk::{
instruction::{Instruction, InstructionError}, instruction::{Instruction, InstructionError},
loader_instruction, loader_instruction,
message::Message, message::Message,
native_token::Sol,
pubkey::{Pubkey, MAX_SEED_LEN}, pubkey::{Pubkey, MAX_SEED_LEN},
signature::{keypair_from_seed, Keypair, Signature, Signer, SignerError}, signature::{keypair_from_seed, Keypair, Signature, Signer, SignerError},
signers::Signers, signers::Signers,
@ -178,6 +179,7 @@ pub enum CliCommand {
program_location: String, program_location: String,
address: Option<SignerIndex>, address: Option<SignerIndex>,
use_deprecated_loader: bool, use_deprecated_loader: bool,
allow_excessive_balance: bool,
}, },
// Stake Commands // Stake Commands
CreateStakeAccount { CreateStakeAccount {
@ -609,13 +611,13 @@ pub fn parse_command(
signers.push(signer); signers.push(signer);
1 1
}); });
let use_deprecated_loader = matches.is_present("use_deprecated_loader");
Ok(CliCommandInfo { Ok(CliCommandInfo {
command: CliCommand::Deploy { command: CliCommand::Deploy {
program_location: matches.value_of("program_location").unwrap().to_string(), program_location: matches.value_of("program_location").unwrap().to_string(),
address, address,
use_deprecated_loader, use_deprecated_loader: matches.is_present("use_deprecated_loader"),
allow_excessive_balance: matches.is_present("allow_excessive_balance"),
}, },
signers, signers,
}) })
@ -1132,6 +1134,7 @@ fn process_deploy(
program_location: &str, program_location: &str,
address: Option<SignerIndex>, address: Option<SignerIndex>,
use_deprecated_loader: bool, use_deprecated_loader: bool,
allow_excessive_balance: bool,
) -> ProcessResult { ) -> ProcessResult {
const WORDS: usize = 12; const WORDS: usize = 12;
// Create ephemeral keypair to use for program address, if not provided // Create ephemeral keypair to use for program address, if not provided
@ -1145,6 +1148,7 @@ fn process_deploy(
program_location, program_location,
address, address,
use_deprecated_loader, use_deprecated_loader,
allow_excessive_balance,
new_keypair, new_keypair,
); );
@ -1160,7 +1164,7 @@ fn process_deploy(
WORDS WORDS
); );
eprintln!( eprintln!(
"then pass it as the [ADDRESS_SIGNER] argument to `solana deploy ...`\n{}\n{}\n{}", "then pass it as the [PROGRAM_ADDRESS_SIGNER] argument to `solana deploy ...`\n{}\n{}\n{}",
divider, phrase, divider divider, phrase, divider
); );
} }
@ -1173,6 +1177,7 @@ fn do_process_deploy(
program_location: &str, program_location: &str,
address: Option<SignerIndex>, address: Option<SignerIndex>,
use_deprecated_loader: bool, use_deprecated_loader: bool,
allow_excessive_balance: bool,
new_keypair: Keypair, new_keypair: Keypair,
) -> ProcessResult { ) -> ProcessResult {
let program_id = if let Some(i) = address { let program_id = if let Some(i) = address {
@ -1237,6 +1242,15 @@ fn do_process_deploy(
balance, balance,
)); ));
balance_needed = balance; balance_needed = balance;
} else if account.lamports > minimum_balance
&& system_program::check_id(&account.owner)
&& !allow_excessive_balance
{
return Err(CliError::DynamicProgramError(format!(
"Program account has a balance: {:?}; it may already be in use",
Sol(account.lamports)
))
.into());
} }
(instructions, balance_needed) (instructions, balance_needed)
} else { } else {
@ -1619,12 +1633,14 @@ pub fn process_command(config: &CliConfig) -> ProcessResult {
program_location, program_location,
address, address,
use_deprecated_loader, use_deprecated_loader,
allow_excessive_balance,
} => process_deploy( } => process_deploy(
&rpc_client, &rpc_client,
config, config,
program_location, program_location,
*address, *address,
*use_deprecated_loader, *use_deprecated_loader,
*allow_excessive_balance,
), ),
// Stake Commands // Stake Commands
@ -2241,7 +2257,7 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.arg( .arg(
Arg::with_name("address_signer") Arg::with_name("address_signer")
.index(2) .index(2)
.value_name("ADDRESS_SIGNER") .value_name("PROGRAM_ADDRESS_SIGNER")
.takes_value(true) .takes_value(true)
.validator(is_valid_signer) .validator(is_valid_signer)
.help("The signer for the desired address of the program [default: new random address]") .help("The signer for the desired address of the program [default: new random address]")
@ -2253,6 +2269,12 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, '
.hidden(true) // Don't document this argument to discourage its use .hidden(true) // Don't document this argument to discourage its use
.help("Use the deprecated BPF loader") .help("Use the deprecated BPF loader")
) )
.arg(
Arg::with_name("allow_excessive_balance")
.long("allow-excessive-deploy-account-balance")
.takes_value(false)
.help("Use the designated program id, even if the account already holds a large balance of SOL")
)
.arg(commitment_arg_with_default("max")), .arg(commitment_arg_with_default("max")),
) )
.subcommand( .subcommand(
@ -2621,6 +2643,7 @@ mod tests {
program_location: "/Users/test/program.o".to_string(), program_location: "/Users/test/program.o".to_string(),
address: None, address: None,
use_deprecated_loader: false, use_deprecated_loader: false,
allow_excessive_balance: false,
}, },
signers: vec![read_keypair_file(&keypair_file).unwrap().into()], signers: vec![read_keypair_file(&keypair_file).unwrap().into()],
} }
@ -2642,6 +2665,7 @@ mod tests {
program_location: "/Users/test/program.o".to_string(), program_location: "/Users/test/program.o".to_string(),
address: Some(1), address: Some(1),
use_deprecated_loader: false, use_deprecated_loader: false,
allow_excessive_balance: false,
}, },
signers: vec![ signers: vec![
read_keypair_file(&keypair_file).unwrap().into(), read_keypair_file(&keypair_file).unwrap().into(),
@ -2955,6 +2979,7 @@ mod tests {
program_location: pathbuf.to_str().unwrap().to_string(), program_location: pathbuf.to_str().unwrap().to_string(),
address: None, address: None,
use_deprecated_loader: false, use_deprecated_loader: false,
allow_excessive_balance: false,
}; };
let result = process_command(&config); let result = process_command(&config);
let json: Value = serde_json::from_str(&result.unwrap()).unwrap(); let json: Value = serde_json::from_str(&result.unwrap()).unwrap();
@ -2973,6 +2998,7 @@ mod tests {
program_location: "bad/file/location.so".to_string(), program_location: "bad/file/location.so".to_string(),
address: None, address: None,
use_deprecated_loader: false, use_deprecated_loader: false,
allow_excessive_balance: false,
}; };
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());
} }

View File

@ -55,7 +55,7 @@ fn test_cli_deploy_program() {
faucet_host: None, faucet_host: None,
faucet_port: faucet_addr.port(), faucet_port: faucet_addr.port(),
pubkey: None, pubkey: None,
lamports: 3 * minimum_balance_for_rent_exemption, // min balance for rent exemption for two programs + leftover for tx processing lamports: 4 * minimum_balance_for_rent_exemption, // min balance for rent exemption for three programs + leftover for tx processing
}; };
config.signers = vec![&keypair]; config.signers = vec![&keypair];
process_command(&config).unwrap(); process_command(&config).unwrap();
@ -64,6 +64,7 @@ fn test_cli_deploy_program() {
program_location: pathbuf.to_str().unwrap().to_string(), program_location: pathbuf.to_str().unwrap().to_string(),
address: None, address: None,
use_deprecated_loader: false, use_deprecated_loader: false,
allow_excessive_balance: false,
}; };
let response = process_command(&config); let response = process_command(&config);
@ -98,6 +99,7 @@ fn test_cli_deploy_program() {
program_location: pathbuf.to_str().unwrap().to_string(), program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1), address: Some(1),
use_deprecated_loader: false, use_deprecated_loader: false,
allow_excessive_balance: false,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
let account1 = rpc_client let account1 = rpc_client
@ -113,6 +115,44 @@ fn test_cli_deploy_program() {
// Attempt to redeploy to the same address // Attempt to redeploy to the same address
process_command(&config).unwrap_err(); process_command(&config).unwrap_err();
// Attempt to deploy to account with excess balance
let custom_address_keypair = Keypair::new();
config.command = CliCommand::Airdrop {
faucet_host: None,
faucet_port: faucet_addr.port(),
pubkey: None,
lamports: 2 * minimum_balance_for_rent_exemption, // Anything over minimum_balance_for_rent_exemption should trigger err
};
config.signers = vec![&custom_address_keypair];
process_command(&config).unwrap();
config.signers = vec![&keypair, &custom_address_keypair];
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: false,
};
process_command(&config).unwrap_err();
// Use forcing parameter to deploy to account with excess balance
config.command = CliCommand::Deploy {
program_location: pathbuf.to_str().unwrap().to_string(),
address: Some(1),
use_deprecated_loader: false,
allow_excessive_balance: true,
};
process_command(&config).unwrap();
let account2 = rpc_client
.get_account_with_commitment(&custom_address_keypair.pubkey(), CommitmentConfig::recent())
.unwrap()
.value
.unwrap();
assert_eq!(account2.lamports, 2 * minimum_balance_for_rent_exemption);
assert_eq!(account2.owner, bpf_loader::id());
assert_eq!(account2.executable, true);
assert_eq!(account0.data, account2.data);
server.close().unwrap(); server.close().unwrap();
remove_dir_all(ledger_path).unwrap(); remove_dir_all(ledger_path).unwrap();
} }