Add system_instruction::{allocate, allocate_with_seed, assign_with_seed}, (#7847)

* cleanup test checks cargo audit

* Add system_instruction allocate

* fixup

* fixup
This commit is contained in:
Rob Walker 2020-01-17 09:29:15 -08:00 committed by GitHub
parent 87598c7612
commit 470d9cd752
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 421 additions and 224 deletions

View File

@ -16,130 +16,89 @@ use solana_sdk::{
use std::collections::HashSet; use std::collections::HashSet;
fn create_account_with_seed( // represents an address that may or may not have been generated
from: &mut KeyedAccount, // from a seed
to: &mut KeyedAccount, #[derive(PartialEq, Default, Debug)]
base: &Pubkey, struct Address {
seed: &str, address: Pubkey,
lamports: u64, base: Option<Pubkey>,
data_length: u64, }
program_id: &Pubkey,
impl Address {
fn is_signer(&self, signers: &HashSet<Pubkey>) -> bool {
if let Some(base) = self.base {
signers.contains(&base)
} else {
signers.contains(&self.address)
}
}
fn create(
address: &Pubkey,
with_seed: Option<(&Pubkey, &str, &Pubkey)>,
) -> Result<Self, InstructionError> {
let base = if let Some((base, seed, program_id)) = with_seed {
// re-derive the address, must match the supplied address
if *address != create_address_with_seed(base, seed, program_id)? {
return Err(SystemError::AddressWithSeedMismatch.into());
}
Some(*base)
} else {
None
};
Ok(Self {
address: *address,
base,
})
}
}
fn allocate(
account: &mut Account,
address: &Address,
space: u64,
signers: &HashSet<Pubkey>, signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
// `base` is the source of the derived address and must sign for if !address.is_signer(signers) {
// rights to the address debug!("Allocate: must carry signature of `to`");
if !signers.contains(&base) {
debug!("CreateAccountWithSeed: must carry signature of `base`");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
// re-derive the address, must match the `to` account
let address = create_address_with_seed(base, seed, program_id)?;
if to.unsigned_key() != &address {
debug!(
"CreateAccountWithSeed: invalid argument; derived {} does not match `to` {}",
address,
to.unsigned_key()
);
return Err(SystemError::AddressWithSeedMismatch.into());
}
// all of finish_create_account's rules apply
finish_create_account(from, to, lamports, data_length, program_id)
}
fn create_account(
from: &mut KeyedAccount,
to: &mut KeyedAccount,
lamports: u64,
data_length: u64,
program_id: &Pubkey,
) -> Result<(), InstructionError> {
if to.signer_key().is_none() {
debug!("CreateAccount: `to` must sign");
return Err(InstructionError::MissingRequiredSignature);
}
finish_create_account(from, to, lamports, data_length, program_id)
}
fn finish_create_account(
from: &mut KeyedAccount,
to: &mut KeyedAccount,
lamports: u64,
data_length: u64,
program_id: &Pubkey,
) -> Result<(), InstructionError> {
// if lamports == 0, the `from` account isn't touched
if lamports != 0 {
if from.signer_key().is_none() {
debug!("CreateAccount: `from` must sign transfer");
return Err(InstructionError::MissingRequiredSignature);
}
if !from.account.data.is_empty() {
debug!("CreateAccount: `from` must not carry data");
return Err(InstructionError::InvalidArgument);
}
}
// if it looks like the `to` account is already in use, bail // if it looks like the `to` account is already in use, bail
// (note that the id check is also enforced by message_processor) // (note that the id check is also enforced by message_processor)
if !to.account.data.is_empty() || !system_program::check_id(&to.account.owner) { if !account.data.is_empty() || !system_program::check_id(&account.owner) {
debug!( debug!(
"CreateAccount: invalid argument; account {} already in use", "Allocate: invalid argument; account {:?} already in use",
to.unsigned_key() address
); );
return Err(SystemError::AccountAlreadyInUse.into()); return Err(SystemError::AccountAlreadyInUse.into());
} }
if sysvar::is_sysvar_id(&to.unsigned_key()) { if space > MAX_PERMITTED_DATA_LENGTH {
debug!("CreateAccount: account id {} invalid", program_id);
return Err(SystemError::InvalidAccountId.into());
}
if lamports > from.account.lamports {
debug!( debug!(
"CreateAccount: insufficient lamports ({}, need {})", "Allocate: requested space: {} is more than maximum allowed",
from.account.lamports, lamports space
);
return Err(SystemError::ResultWithNegativeLamports.into());
}
if data_length > MAX_PERMITTED_DATA_LENGTH {
debug!(
"CreateAccount: requested data_length: {} is more than maximum allowed",
data_length
); );
return Err(SystemError::InvalidAccountDataLength.into()); return Err(SystemError::InvalidAccountDataLength.into());
} }
// guard against sysvars being made account.data = vec![0; space as usize];
if sysvar::check_id(&program_id) {
debug!("Assign: program id {} invalid", program_id);
return Err(SystemError::InvalidProgramId.into());
}
to.account.owner = *program_id;
from.account.lamports -= lamports;
to.account.lamports += lamports;
to.account.data = vec![0; data_length as usize];
to.account.executable = false;
Ok(()) Ok(())
} }
fn assign_account_to_program( fn assign(
keyed_account: &mut KeyedAccount, account: &mut Account,
address: &Address,
program_id: &Pubkey, program_id: &Pubkey,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
// no work to do, just return // no work to do, just return
if keyed_account.account.owner == *program_id { if account.owner == *program_id {
return Ok(()); return Ok(());
} }
if keyed_account.signer_key().is_none() { if !address.is_signer(&signers) {
debug!("Assign: account must sign"); debug!("Assign: account must sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
@ -150,13 +109,37 @@ fn assign_account_to_program(
return Err(SystemError::InvalidProgramId.into()); return Err(SystemError::InvalidProgramId.into());
} }
keyed_account.account.owner = *program_id; account.owner = *program_id;
Ok(()) Ok(())
} }
fn transfer_lamports( fn allocate_and_assign(
to: &mut Account,
to_address: &Address,
space: u64,
program_id: &Pubkey,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError> {
allocate(to, to_address, space, signers)?;
assign(to, to_address, program_id, signers)
}
fn create_account(
from: &mut KeyedAccount, from: &mut KeyedAccount,
to: &mut KeyedAccount, to: &mut Account,
to_address: &Address,
lamports: u64,
space: u64,
program_id: &Pubkey,
signers: &HashSet<Pubkey>,
) -> Result<(), InstructionError> {
allocate_and_assign(to, to_address, space, program_id, signers)?;
transfer(from, to, lamports)
}
fn transfer(
from: &mut KeyedAccount,
to: &mut Account,
lamports: u64, lamports: u64,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
if lamports == 0 { if lamports == 0 {
@ -182,7 +165,7 @@ fn transfer_lamports(
} }
from.account.lamports -= lamports; from.account.lamports -= lamports;
to.account.lamports += lamports; to.lamports += lamports;
Ok(()) Ok(())
} }
@ -207,7 +190,16 @@ pub fn process_instruction(
} => { } => {
let from = next_keyed_account(keyed_accounts_iter)?; let from = next_keyed_account(keyed_accounts_iter)?;
let to = next_keyed_account(keyed_accounts_iter)?; let to = next_keyed_account(keyed_accounts_iter)?;
create_account(from, to, lamports, space, &program_id) let to_address = Address::create(to.unsigned_key(), None)?;
create_account(
from,
to.account,
&to_address,
lamports,
space,
&program_id,
&signers,
)
} }
SystemInstruction::CreateAccountWithSeed { SystemInstruction::CreateAccountWithSeed {
base, base,
@ -218,12 +210,13 @@ pub fn process_instruction(
} => { } => {
let from = next_keyed_account(keyed_accounts_iter)?; let from = next_keyed_account(keyed_accounts_iter)?;
let to = next_keyed_account(keyed_accounts_iter)?; let to = next_keyed_account(keyed_accounts_iter)?;
let to_address =
Address::create(&to.unsigned_key(), Some((&base, &seed, &program_id)))?;
create_account_with_seed( create_account(
from, from,
to, to.account,
&base, &to_address,
&seed,
lamports, lamports,
space, space,
&program_id, &program_id,
@ -231,13 +224,14 @@ pub fn process_instruction(
) )
} }
SystemInstruction::Assign { program_id } => { SystemInstruction::Assign { program_id } => {
let account = next_keyed_account(keyed_accounts_iter)?; let keyed_account = next_keyed_account(keyed_accounts_iter)?;
assign_account_to_program(account, &program_id) let address = Address::create(keyed_account.unsigned_key(), None)?;
assign(keyed_account.account, &address, &program_id, &signers)
} }
SystemInstruction::Transfer { lamports } => { SystemInstruction::Transfer { lamports } => {
let from = next_keyed_account(keyed_accounts_iter)?; let from = next_keyed_account(keyed_accounts_iter)?;
let to = next_keyed_account(keyed_accounts_iter)?; let to = next_keyed_account(keyed_accounts_iter)?;
transfer_lamports(from, to, lamports) transfer(from, to.account, lamports)
} }
SystemInstruction::AdvanceNonceAccount => { SystemInstruction::AdvanceNonceAccount => {
let me = &mut next_keyed_account(keyed_accounts_iter)?; let me = &mut next_keyed_account(keyed_accounts_iter)?;
@ -269,6 +263,44 @@ pub fn process_instruction(
let me = &mut next_keyed_account(keyed_accounts_iter)?; let me = &mut next_keyed_account(keyed_accounts_iter)?;
me.nonce_authorize(&nonce_authority, &signers) me.nonce_authorize(&nonce_authority, &signers)
} }
SystemInstruction::Allocate { space } => {
let keyed_account = next_keyed_account(keyed_accounts_iter)?;
let address = Address::create(keyed_account.unsigned_key(), None)?;
allocate(keyed_account.account, &address, space, &signers)
}
SystemInstruction::AllocateWithSeed {
base,
seed,
space,
program_id,
} => {
let keyed_account = next_keyed_account(keyed_accounts_iter)?;
let address = Address::create(
keyed_account.unsigned_key(),
Some((&base, &seed, &program_id)),
)?;
allocate_and_assign(
keyed_account.account,
&address,
space,
&program_id,
&signers,
)
}
SystemInstruction::AssignWithSeed {
base,
seed,
program_id,
} => {
let keyed_account = next_keyed_account(keyed_accounts_iter)?;
let address = Address::create(
keyed_account.unsigned_key(),
Some((&base, &seed, &program_id)),
)?;
assign(keyed_account.account, &address, &program_id, &signers)
}
} }
} }
@ -295,20 +327,29 @@ pub fn get_system_account_kind(account: &Account) -> Option<SystemAccountKind> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::bank::Bank; use crate::{bank::Bank, bank_client::BankClient};
use crate::bank_client::BankClient;
use bincode::serialize; use bincode::serialize;
use solana_sdk::account::Account; use solana_sdk::{
use solana_sdk::client::SyncClient; account::Account,
use solana_sdk::genesis_config::create_genesis_config; client::SyncClient,
use solana_sdk::hash::{hash, Hash}; genesis_config::create_genesis_config,
use solana_sdk::instruction::{AccountMeta, Instruction, InstructionError}; hash::{hash, Hash},
use solana_sdk::nonce_state; instruction::{AccountMeta, Instruction, InstructionError},
use solana_sdk::signature::{Keypair, KeypairUtil}; message::Message,
use solana_sdk::system_instruction; nonce_state,
use solana_sdk::system_program; signature::{Keypair, KeypairUtil},
use solana_sdk::sysvar; system_instruction, system_program, sysvar,
use solana_sdk::transaction::TransactionError; transaction::TransactionError,
};
impl From<Pubkey> for Address {
fn from(address: Pubkey) -> Self {
Self {
address,
base: None,
}
}
}
#[test] #[test]
fn test_create_account() { fn test_create_account() {
@ -375,31 +416,18 @@ mod tests {
} }
#[test] #[test]
fn test_create_account_with_seed_mismatch() { fn test_address_create_with_seed_mismatch() {
let new_program_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_rand(); let from = Pubkey::new_rand();
let seed = "dull boy"; let seed = "dull boy";
let to = Pubkey::new_rand(); let to = Pubkey::new_rand();
let program_id = Pubkey::new_rand();
let mut from_account = Account::new(100, 0, &system_program::id());
let mut to_account = Account::new(0, 0, &Pubkey::default());
assert_eq!( assert_eq!(
create_account_with_seed( Address::create(&to, Some((&from, seed, &program_id))),
&mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to, false, &mut to_account),
&from,
seed,
50,
2,
&new_program_owner,
&[from].iter().cloned().collect::<HashSet<_>>(),
),
Err(SystemError::AddressWithSeedMismatch.into()) Err(SystemError::AddressWithSeedMismatch.into())
); );
assert_eq!(from_account.lamports, 100);
assert_eq!(to_account, Account::default());
} }
#[test] #[test]
fn test_create_account_with_seed_missing_sig() { fn test_create_account_with_seed_missing_sig() {
let new_program_owner = Pubkey::new(&[9; 32]); let new_program_owner = Pubkey::new(&[9; 32]);
@ -409,17 +437,17 @@ mod tests {
let mut from_account = Account::new(100, 0, &system_program::id()); let mut from_account = Account::new(100, 0, &system_program::id());
let mut to_account = Account::new(0, 0, &Pubkey::default()); let mut to_account = Account::new(0, 0, &Pubkey::default());
let to_address = Address::create(&to, Some((&from, seed, &new_program_owner))).unwrap();
assert_eq!( assert_eq!(
create_account_with_seed( create_account(
&mut KeyedAccount::new(&from, false, &mut from_account), &mut KeyedAccount::new(&from, false, &mut from_account),
&mut KeyedAccount::new(&to, false, &mut to_account), &mut to_account,
&from, &to_address,
seed,
50, 50,
2, 2,
&new_program_owner, &new_program_owner,
&[from].iter().cloned().collect::<HashSet<_>>(), &HashSet::new(),
), ),
Err(InstructionError::MissingRequiredSignature) Err(InstructionError::MissingRequiredSignature)
); );
@ -440,10 +468,12 @@ mod tests {
assert_eq!( assert_eq!(
create_account( create_account(
&mut KeyedAccount::new(&from, false, &mut from_account), // no signer &mut KeyedAccount::new(&from, false, &mut from_account), // no signer
&mut KeyedAccount::new(&to, true, &mut to_account), &mut to_account,
&to.into(),
0, 0,
2, 2,
&new_program_owner, &new_program_owner,
&[to].iter().cloned().collect::<HashSet<_>>(),
), ),
Ok(()) Ok(())
); );
@ -467,35 +497,38 @@ mod tests {
let to = Pubkey::new_rand(); let to = Pubkey::new_rand();
let mut to_account = Account::new(0, 0, &Pubkey::default()); let mut to_account = Account::new(0, 0, &Pubkey::default());
let unchanged_account = to_account.clone();
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to, true, &mut to_account), &mut to_account,
&to.into(),
150, 150,
2, 2,
&new_program_owner, &new_program_owner,
&[from, to].iter().cloned().collect::<HashSet<_>>(),
); );
assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into()));
let from_lamports = from_account.lamports;
assert_eq!(from_lamports, 100);
assert_eq!(to_account, unchanged_account);
} }
#[test] #[test]
fn test_request_more_than_allowed_data_length() { fn test_request_more_than_allowed_data_length() {
let mut from_account = Account::new(100, 0, &system_program::id()); let mut from_account = Account::new(100, 0, &system_program::id());
let from_account_key = Pubkey::new_rand(); let from = Pubkey::new_rand();
let mut to_account = Account::default(); let mut to_account = Account::default();
let to_account_key = Pubkey::new_rand(); let to = Pubkey::new_rand();
let signers = &[from, to].iter().cloned().collect::<HashSet<_>>();
let address = &to.into();
// Trying to request more data length than permitted will result in failure // Trying to request more data length than permitted will result in failure
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from_account_key, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to_account_key, true, &mut to_account), &mut to_account,
&address,
50, 50,
MAX_PERMITTED_DATA_LENGTH + 1, MAX_PERMITTED_DATA_LENGTH + 1,
&system_program::id(), &system_program::id(),
&signers,
); );
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
@ -505,11 +538,13 @@ mod tests {
// Trying to request equal or less data length than permitted will be successful // Trying to request equal or less data length than permitted will be successful
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from_account_key, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to_account_key, true, &mut to_account), &mut to_account,
&address,
50, 50,
MAX_PERMITTED_DATA_LENGTH, MAX_PERMITTED_DATA_LENGTH,
&system_program::id(), &system_program::id(),
&signers,
); );
assert!(result.is_ok()); assert!(result.is_ok());
assert_eq!(to_account.lamports, 50); assert_eq!(to_account.lamports, 50);
@ -518,7 +553,6 @@ mod tests {
#[test] #[test]
fn test_create_already_in_use() { fn test_create_already_in_use() {
solana_logger::setup();
// Attempt to create system account in account already owned by another program // Attempt to create system account in account already owned by another program
let new_program_owner = Pubkey::new(&[9; 32]); let new_program_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_rand(); let from = Pubkey::new_rand();
@ -529,12 +563,17 @@ mod tests {
let mut owned_account = Account::new(0, 0, &original_program_owner); let mut owned_account = Account::new(0, 0, &original_program_owner);
let unchanged_account = owned_account.clone(); let unchanged_account = owned_account.clone();
let signers = &[from, owned_key].iter().cloned().collect::<HashSet<_>>();
let owned_address = owned_key.into();
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&owned_key, true, &mut owned_account), &mut owned_account,
&owned_address,
50, 50,
2, 2,
&new_program_owner, &new_program_owner,
&signers,
); );
assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into()));
@ -547,10 +586,12 @@ mod tests {
let unchanged_account = owned_account.clone(); let unchanged_account = owned_account.clone();
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&owned_key, true, &mut owned_account), &mut owned_account,
&owned_address,
50, 50,
2, 2,
&new_program_owner, &new_program_owner,
&signers,
); );
assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into()));
let from_lamports = from_account.lamports; let from_lamports = from_account.lamports;
@ -561,10 +602,12 @@ mod tests {
let mut owned_account = Account::new(1, 0, &Pubkey::default()); let mut owned_account = Account::new(1, 0, &Pubkey::default());
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&owned_key, true, &mut owned_account), &mut owned_account,
&owned_address,
50, 50,
2, 2,
&new_program_owner, &new_program_owner,
&signers,
); );
assert_eq!(result, Ok(())); assert_eq!(result, Ok(()));
assert_eq!(from_account.lamports, from_lamports - 50); assert_eq!(from_account.lamports, from_lamports - 50);
@ -580,43 +623,46 @@ mod tests {
let owned_key = Pubkey::new_rand(); let owned_key = Pubkey::new_rand();
let mut owned_account = Account::new(0, 0, &Pubkey::default()); let mut owned_account = Account::new(0, 0, &Pubkey::default());
let unchanged_account = owned_account.clone();
let owned_address = owned_key.into();
// Haven't signed from account // Haven't signed from account
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, false, &mut from_account), &mut KeyedAccount::new(&from, false, &mut from_account),
&mut KeyedAccount::new(&owned_key, true, &mut owned_account), &mut owned_account,
&owned_address,
50, 50,
2, 2,
&new_program_owner, &new_program_owner,
&[owned_key].iter().cloned().collect::<HashSet<_>>(),
); );
assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); assert_eq!(result, Err(InstructionError::MissingRequiredSignature));
assert_eq!(from_account.lamports, 100);
assert_eq!(owned_account, unchanged_account);
// Haven't signed to account // Haven't signed to account
let mut owned_account = Account::new(0, 0, &Pubkey::default());
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&owned_key, false, &mut owned_account), &mut owned_account,
&owned_address,
50, 50,
2, 2,
&new_program_owner, &new_program_owner,
&[from].iter().cloned().collect::<HashSet<_>>(),
); );
assert_eq!(result, Err(InstructionError::MissingRequiredSignature)); assert_eq!(result, Err(InstructionError::MissingRequiredSignature));
assert_eq!(from_account.lamports, 100);
assert_eq!(owned_account, unchanged_account);
// support creation/assignment with zero lamports (ephemeral account) // support creation/assignment with zero lamports (ephemeral account)
let mut owned_account = Account::new(0, 0, &Pubkey::default());
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, false, &mut from_account), &mut KeyedAccount::new(&from, false, &mut from_account),
&mut KeyedAccount::new(&owned_key, true, &mut owned_account), &mut owned_account,
&owned_address,
0, 0,
2, 2,
&new_program_owner, &new_program_owner,
&[owned_key].iter().cloned().collect::<HashSet<_>>(),
); );
assert_eq!(result, Ok(())); assert_eq!(result, Ok(()));
assert_eq!(from_account.lamports, 100);
assert_eq!(owned_account.owner, new_program_owner);
} }
#[test] #[test]
@ -628,31 +674,21 @@ mod tests {
let to = Pubkey::new_rand(); let to = Pubkey::new_rand();
let mut to_account = Account::default(); let mut to_account = Account::default();
let signers = [from, to].iter().cloned().collect::<HashSet<_>>();
let to_address = to.into();
// fail to create a sysvar::id() owned account // fail to create a sysvar::id() owned account
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to, true, &mut to_account), &mut to_account,
&to_address,
50, 50,
2, 2,
&sysvar::id(), &sysvar::id(),
&signers,
); );
assert_eq!(result, Err(SystemError::InvalidProgramId.into())); assert_eq!(result, Err(SystemError::InvalidProgramId.into()));
let to = sysvar::fees::id();
let mut to_account = Account::default();
// fail to create an account with a sysvar id
let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to, true, &mut to_account),
50,
2,
&system_program::id(),
);
assert_eq!(result, Err(SystemError::InvalidAccountId.into()));
let from_lamports = from_account.lamports;
assert_eq!(from_lamports, 100);
} }
#[test] #[test]
@ -667,18 +703,23 @@ mod tests {
data: vec![0, 1, 2, 3], data: vec![0, 1, 2, 3],
..Account::default() ..Account::default()
}; };
let unchanged_account = populated_account.clone();
let signers = [from, populated_key]
.iter()
.cloned()
.collect::<HashSet<_>>();
let populated_address = populated_key.into();
let result = create_account( let result = create_account(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&populated_key, true, &mut populated_account), &mut populated_account,
&populated_address,
50, 50,
2, 2,
&new_program_owner, &new_program_owner,
&signers,
); );
assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into())); assert_eq!(result, Err(SystemError::AccountAlreadyInUse.into()));
assert_eq!(from_account.lamports, 100);
assert_eq!(populated_account, unchanged_account);
} }
#[test] #[test]
@ -697,32 +738,47 @@ mod tests {
let new = Pubkey::new_rand(); let new = Pubkey::new_rand();
let mut new_account = Account::default(); let mut new_account = Account::default();
let mut to = KeyedAccount::new(&new, true, &mut new_account);
let signers = [nonce, new].iter().cloned().collect::<HashSet<_>>();
let new_address = new.into();
assert_eq!( assert_eq!(
create_account(&mut from, &mut to, 42, 0, &Pubkey::new_rand()), create_account(
&mut from,
&mut new_account,
&new_address,
42,
0,
&Pubkey::new_rand(),
&signers
),
Err(InstructionError::InvalidArgument), Err(InstructionError::InvalidArgument),
); );
} }
#[test] #[test]
fn test_assign_account_to_program() { fn test_assign() {
let new_program_owner = Pubkey::new(&[9; 32]); let new_program_owner = Pubkey::new(&[9; 32]);
let from = Pubkey::new_rand(); let pubkey = Pubkey::new_rand();
let mut from_account = Account::new(100, 0, &system_program::id()); let mut account = Account::new(100, 0, &system_program::id());
assert_eq!( assert_eq!(
assign_account_to_program( assign(
&mut KeyedAccount::new(&from, false, &mut from_account), &mut account,
&pubkey.into(),
&new_program_owner, &new_program_owner,
&HashSet::new()
), ),
Err(InstructionError::MissingRequiredSignature) Err(InstructionError::MissingRequiredSignature)
); );
// no change, no signature needed // no change, no signature needed
assert_eq!( assert_eq!(
assign_account_to_program( assign(
&mut KeyedAccount::new(&from, false, &mut from_account), &mut account,
&pubkey.into(),
&system_program::id(), &system_program::id(),
&HashSet::new()
), ),
Ok(()) Ok(())
); );
@ -730,7 +786,7 @@ mod tests {
assert_eq!( assert_eq!(
process_instruction( process_instruction(
&Pubkey::default(), &Pubkey::default(),
&mut [KeyedAccount::new(&from, true, &mut from_account)], &mut [KeyedAccount::new(&pubkey, true, &mut account)],
&bincode::serialize(&SystemInstruction::Assign { &bincode::serialize(&SystemInstruction::Assign {
program_id: new_program_owner program_id: new_program_owner
}) })
@ -741,16 +797,18 @@ mod tests {
} }
#[test] #[test]
fn test_assign_account_to_sysvar() { fn test_assign_to_sysvar() {
let new_program_owner = sysvar::id(); let new_program_owner = sysvar::id();
let from = Pubkey::new_rand(); let from = Pubkey::new_rand();
let mut from_account = Account::new(100, 0, &system_program::id()); let mut from_account = Account::new(100, 0, &system_program::id());
assert_eq!( assert_eq!(
assign_account_to_program( assign(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut from_account,
&from.into(),
&new_program_owner, &new_program_owner,
&[from].iter().cloned().collect::<HashSet<_>>(),
), ),
Err(SystemError::InvalidProgramId.into()) Err(SystemError::InvalidProgramId.into())
); );
@ -783,11 +841,10 @@ mod tests {
fn test_transfer_lamports() { fn test_transfer_lamports() {
let from = Pubkey::new_rand(); let from = Pubkey::new_rand();
let mut from_account = Account::new(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter let mut from_account = Account::new(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter
let to = Pubkey::new_rand();
let mut to_account = Account::new(1, 0, &Pubkey::new(&[3; 32])); // account owner should not matter let mut to_account = Account::new(1, 0, &Pubkey::new(&[3; 32])); // account owner should not matter
transfer_lamports( transfer(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to, false, &mut to_account), &mut to_account,
50, 50,
) )
.unwrap(); .unwrap();
@ -797,9 +854,9 @@ mod tests {
assert_eq!(to_lamports, 51); assert_eq!(to_lamports, 51);
// Attempt to move more lamports than remaining in from_account // Attempt to move more lamports than remaining in from_account
let result = transfer_lamports( let result = transfer(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to, false, &mut to_account), &mut to_account,
100, 100,
); );
assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into())); assert_eq!(result, Err(SystemError::ResultWithNegativeLamports.into()));
@ -807,9 +864,9 @@ mod tests {
assert_eq!(to_account.lamports, 51); assert_eq!(to_account.lamports, 51);
// test unsigned transfer of zero // test unsigned transfer of zero
assert!(transfer_lamports( assert!(transfer(
&mut KeyedAccount::new(&from, false, &mut from_account), &mut KeyedAccount::new(&from, false, &mut from_account),
&mut KeyedAccount::new(&to, false, &mut to_account), &mut to_account,
0, 0,
) )
.is_ok(),); .is_ok(),);
@ -830,18 +887,87 @@ mod tests {
get_system_account_kind(&from_account), get_system_account_kind(&from_account),
Some(SystemAccountKind::Nonce) Some(SystemAccountKind::Nonce)
); );
let to = Pubkey::new_rand();
let mut to_account = Account::new(1, 0, &Pubkey::new(&[3; 32])); // account owner should not matter let mut to_account = Account::new(1, 0, &Pubkey::new(&[3; 32])); // account owner should not matter
assert_eq!( assert_eq!(
transfer_lamports( transfer(
&mut KeyedAccount::new(&from, true, &mut from_account), &mut KeyedAccount::new(&from, true, &mut from_account),
&mut KeyedAccount::new(&to, false, &mut to_account), &mut to_account,
50, 50,
), ),
Err(InstructionError::InvalidArgument), Err(InstructionError::InvalidArgument),
) )
} }
#[test]
fn test_allocate() {
let (genesis_config, mint_keypair) = create_genesis_config(100);
let bank = Bank::new(&genesis_config);
let bank_client = BankClient::new(bank);
let alice_keypair = Keypair::new();
let alice_pubkey = alice_keypair.pubkey();
let seed = "seed";
let program_id = Pubkey::new_rand();
let alice_with_seed = create_address_with_seed(&alice_pubkey, seed, &program_id).unwrap();
bank_client
.transfer(50, &mint_keypair, &alice_pubkey)
.unwrap();
let allocate_with_seed = Message::new_with_payer(
vec![system_instruction::allocate_with_seed(
&alice_with_seed,
&alice_pubkey,
seed,
2,
&program_id,
)],
Some(&alice_pubkey),
);
assert!(bank_client
.send_message(&[&alice_keypair], allocate_with_seed)
.is_ok());
let allocate = system_instruction::allocate(&alice_pubkey, 2);
assert!(bank_client
.send_instruction(&alice_keypair, allocate)
.is_ok());
}
#[test]
fn test_assign_with_seed() {
let (genesis_config, mint_keypair) = create_genesis_config(100);
let bank = Bank::new(&genesis_config);
let bank_client = BankClient::new(bank);
let alice_keypair = Keypair::new();
let alice_pubkey = alice_keypair.pubkey();
let seed = "seed";
let program_id = Pubkey::new_rand();
let alice_with_seed = create_address_with_seed(&alice_pubkey, seed, &program_id).unwrap();
bank_client
.transfer(50, &mint_keypair, &alice_pubkey)
.unwrap();
let assign_with_seed = Message::new_with_payer(
vec![system_instruction::assign_with_seed(
&alice_with_seed,
&alice_pubkey,
seed,
&program_id,
)],
Some(&alice_pubkey),
);
assert!(bank_client
.send_message(&[&alice_keypair], assign_with_seed)
.is_ok());
}
#[test] #[test]
fn test_system_unsigned_transaction() { fn test_system_unsigned_transaction() {
let (genesis_config, alice_keypair) = create_genesis_config(100); let (genesis_config, alice_keypair) = create_genesis_config(100);

View File

@ -54,8 +54,8 @@ mkdir -p target/cov/tmp
find target/cov -name \*.gcda -newer "$timing_file" -print0 | find target/cov -name \*.gcda -newer "$timing_file" -print0 |
(while IFS= read -r -d '' gcda_file; do (while IFS= read -r -d '' gcda_file; do
gcno_file="${gcda_file%.gcda}.gcno" gcno_file="${gcda_file%.gcda}.gcno"
ln -s "../../../$gcda_file" "target/cov/tmp/$(basename "$gcda_file")" ln -sf "../../../$gcda_file" "target/cov/tmp/$(basename "$gcda_file")"
ln -s "../../../$gcno_file" "target/cov/tmp/$(basename "$gcno_file")" ln -sf "../../../$gcno_file" "target/cov/tmp/$(basename "$gcno_file")"
done) done)
_ grcov target/cov/tmp > target/cov/lcov-full.info _ grcov target/cov/tmp > target/cov/lcov-full.info

View File

@ -13,7 +13,6 @@ pub enum SystemError {
AccountAlreadyInUse, AccountAlreadyInUse,
ResultWithNegativeLamports, ResultWithNegativeLamports,
InvalidProgramId, InvalidProgramId,
InvalidAccountId,
InvalidAccountDataLength, InvalidAccountDataLength,
InvalidSeed, InvalidSeed,
MaxSeedLengthExceeded, MaxSeedLengthExceeded,
@ -63,7 +62,7 @@ pub enum SystemInstruction {
/// * Transaction::keys[0] - source /// * Transaction::keys[0] - source
/// * Transaction::keys[1] - new account key /// * Transaction::keys[1] - new account key
/// * lamports - number of lamports to transfer to the new account /// * lamports - number of lamports to transfer to the new account
/// * space - memory to allocate if greater then zero /// * space - number of bytes of memory to allocate
/// * program_id - the program id of the new account /// * program_id - the program id of the new account
CreateAccount { CreateAccount {
lamports: u64, lamports: u64,
@ -78,12 +77,12 @@ pub enum SystemInstruction {
/// * Transaction::keys[1] - destination /// * Transaction::keys[1] - destination
Transfer { lamports: u64 }, Transfer { lamports: u64 },
/// Create a new account at an address derived from /// Create a new account at an address derived from
/// the from account and a seed /// a base pubkey and a seed
/// * Transaction::keys[0] - source /// * Transaction::keys[0] - source
/// * Transaction::keys[1] - new account key /// * Transaction::keys[1] - new account key
/// * seed - string of ascii chars, no longer than MAX_ADDRESS_SEED_LEN /// * seed - string of ascii chars, no longer than MAX_ADDRESS_SEED_LEN
/// * lamports - number of lamports to transfer to the new account /// * lamports - number of lamports to transfer to the new account
/// * space - memory to allocate if greater then zero /// * space - number of bytes of memory to allocate
/// * program_id - the program id of the new account /// * program_id - the program id of the new account
CreateAccountWithSeed { CreateAccountWithSeed {
base: Pubkey, base: Pubkey,
@ -137,6 +136,31 @@ pub enum SystemInstruction {
/// ///
/// The current authority must sign a transaction executing this instruction /// The current authority must sign a transaction executing this instruction
AuthorizeNonceAccount(Pubkey), AuthorizeNonceAccount(Pubkey),
/// Allocate space in a (possibly new) account without funding
/// * Transaction::keys[0] - new account key
/// * space - number of bytes of memory to allocate
Allocate { space: u64 },
/// Allocate space for and assign an account at an address
/// derived from a base pubkey and a seed
/// * Transaction::keys[0] - new account key
/// * seed - string of ascii chars, no longer than MAX_ADDRESS_SEED_LEN
/// * space - number of bytes of memory to allocate
/// * program_id - the program id of the new account
AllocateWithSeed {
base: Pubkey,
seed: String,
space: u64,
program_id: Pubkey,
},
/// Assign account to a program based on a seed
/// * Transaction::keys[0] - account to assign
/// * seed - string of ascii chars, no longer than MAX_ADDRESS_SEED_LEN
/// * program_id - the program id of the new account
AssignWithSeed {
base: Pubkey,
seed: String,
program_id: Pubkey,
},
} }
pub fn create_account( pub fn create_account(
@ -191,8 +215,8 @@ pub fn create_account_with_seed(
) )
} }
pub fn assign(from_pubkey: &Pubkey, program_id: &Pubkey) -> Instruction { pub fn assign(pubkey: &Pubkey, program_id: &Pubkey) -> Instruction {
let account_metas = vec![AccountMeta::new(*from_pubkey, true)]; let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new( Instruction::new(
system_program::id(), system_program::id(),
&SystemInstruction::Assign { &SystemInstruction::Assign {
@ -202,6 +226,24 @@ pub fn assign(from_pubkey: &Pubkey, program_id: &Pubkey) -> Instruction {
) )
} }
pub fn assign_with_seed(
address: &Pubkey, // must match create_address_with_seed(base, seed, program_id)
base: &Pubkey,
seed: &str,
program_id: &Pubkey,
) -> Instruction {
let account_metas = vec![AccountMeta::new(*address, false)].with_signer(base);
Instruction::new(
system_program::id(),
&SystemInstruction::AssignWithSeed {
base: *base,
seed: seed.to_string(),
program_id: *program_id,
},
account_metas,
)
}
pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction { pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Instruction {
let account_metas = vec![ let account_metas = vec![
AccountMeta::new(*from_pubkey, true), AccountMeta::new(*from_pubkey, true),
@ -214,6 +256,35 @@ pub fn transfer(from_pubkey: &Pubkey, to_pubkey: &Pubkey, lamports: u64) -> Inst
) )
} }
pub fn allocate(pubkey: &Pubkey, space: u64) -> Instruction {
let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new(
system_program::id(),
&SystemInstruction::Allocate { space },
account_metas,
)
}
pub fn allocate_with_seed(
address: &Pubkey, // must match create_address_with_seed(base, seed, program_id)
base: &Pubkey,
seed: &str,
space: u64,
program_id: &Pubkey,
) -> Instruction {
let account_metas = vec![AccountMeta::new(*address, false)].with_signer(base);
Instruction::new(
system_program::id(),
&SystemInstruction::AllocateWithSeed {
base: *base,
seed: seed.to_string(),
space,
program_id: *program_id,
},
account_metas,
)
}
/// Create and sign new SystemInstruction::Transfer transaction to many destinations /// Create and sign new SystemInstruction::Transfer transaction to many destinations
pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec<Instruction> { pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec<Instruction> {
to_lamports to_lamports
@ -223,7 +294,7 @@ pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec
} }
pub fn create_address_with_seed( pub fn create_address_with_seed(
from_pubkey: &Pubkey, base: &Pubkey,
seed: &str, seed: &str,
program_id: &Pubkey, program_id: &Pubkey,
) -> Result<Pubkey, SystemError> { ) -> Result<Pubkey, SystemError> {
@ -232,7 +303,7 @@ pub fn create_address_with_seed(
} }
Ok(Pubkey::new( Ok(Pubkey::new(
hashv(&[from_pubkey.as_ref(), seed.as_ref(), program_id.as_ref()]).as_ref(), hashv(&[base.as_ref(), seed.as_ref(), program_id.as_ref()]).as_ref(),
)) ))
} }