Add SystemInstruction::CreateAccountWithSeed (#7390)
* address generator * coverage * fixups * remove ascii restriction * illustrate that utf-8 doesn't expand the space
This commit is contained in:
@ -1,23 +1,55 @@
|
|||||||
use log::*;
|
use log::*;
|
||||||
use solana_sdk::account::KeyedAccount;
|
|
||||||
use solana_sdk::instruction::InstructionError;
|
|
||||||
use solana_sdk::instruction_processor_utils::{limited_deserialize, next_keyed_account};
|
|
||||||
use solana_sdk::pubkey::Pubkey;
|
|
||||||
use solana_sdk::system_instruction::{SystemError, SystemInstruction};
|
|
||||||
use solana_sdk::system_program;
|
|
||||||
use solana_sdk::sysvar;
|
|
||||||
|
|
||||||
// 10 MB
|
use solana_sdk::{
|
||||||
const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
|
account::KeyedAccount,
|
||||||
|
instruction::InstructionError,
|
||||||
|
instruction_processor_utils::{limited_deserialize, next_keyed_account},
|
||||||
|
pubkey::Pubkey,
|
||||||
|
system_instruction::{
|
||||||
|
create_address_with_seed, SystemError, SystemInstruction, MAX_PERMITTED_DATA_LENGTH,
|
||||||
|
},
|
||||||
|
system_program, sysvar,
|
||||||
|
};
|
||||||
|
|
||||||
fn create_system_account(
|
fn create_account_with_seed(
|
||||||
|
from: &mut KeyedAccount,
|
||||||
|
to: &mut KeyedAccount,
|
||||||
|
seed: &str,
|
||||||
|
lamports: u64,
|
||||||
|
data_length: u64,
|
||||||
|
program_id: &Pubkey,
|
||||||
|
) -> Result<(), InstructionError> {
|
||||||
|
// `from` is the source of the derived address, the caller must have
|
||||||
|
// signed, even if no lamports will be transferred
|
||||||
|
if from.signer_key().is_none() {
|
||||||
|
debug!("CreateAccountWithSeed: from must sign");
|
||||||
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
// re-derive the address, must match `to`
|
||||||
|
let address = create_address_with_seed(from.unsigned_key(), seed, program_id)?;
|
||||||
|
|
||||||
|
if to.unsigned_key() != &address {
|
||||||
|
debug!(
|
||||||
|
"CreateAccountWithSeed: invalid argument; generated {} 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,
|
from: &mut KeyedAccount,
|
||||||
to: &mut KeyedAccount,
|
to: &mut KeyedAccount,
|
||||||
lamports: u64,
|
lamports: u64,
|
||||||
data_length: u64,
|
data_length: u64,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
) -> Result<(), InstructionError> {
|
) -> Result<(), InstructionError> {
|
||||||
// if lamports == 0, the from account isn't touched
|
// if lamports == 0, the `from` account isn't touched
|
||||||
if lamports != 0 && from.signer_key().is_none() {
|
if lamports != 0 && from.signer_key().is_none() {
|
||||||
debug!("CreateAccount: from must sign");
|
debug!("CreateAccount: from must sign");
|
||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
@ -28,7 +60,17 @@ fn create_system_account(
|
|||||||
return Err(InstructionError::MissingRequiredSignature);
|
return Err(InstructionError::MissingRequiredSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it looks like the to account is already in use, bail
|
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 it looks like the `to` account is already in use, bail
|
||||||
if to.account.lamports != 0
|
if to.account.lamports != 0
|
||||||
|| !to.account.data.is_empty()
|
|| !to.account.data.is_empty()
|
||||||
|| !system_program::check_id(&to.account.owner)
|
|| !system_program::check_id(&to.account.owner)
|
||||||
@ -61,11 +103,18 @@ fn create_system_account(
|
|||||||
return Err(SystemError::InvalidAccountDataLength.into());
|
return Err(SystemError::InvalidAccountDataLength.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
assign_account_to_program(to, program_id)?;
|
// guard against sysvars being assigned
|
||||||
|
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;
|
from.account.lamports -= lamports;
|
||||||
to.account.lamports += lamports;
|
to.account.lamports += lamports;
|
||||||
to.account.data = vec![0; data_length as usize];
|
to.account.data = vec![0; data_length as usize];
|
||||||
to.account.executable = false;
|
to.account.executable = false;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +183,17 @@ 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_system_account(from, to, lamports, space, &program_id)
|
create_account(from, to, lamports, space, &program_id)
|
||||||
|
}
|
||||||
|
SystemInstruction::CreateAccountWithSeed {
|
||||||
|
seed,
|
||||||
|
lamports,
|
||||||
|
space,
|
||||||
|
program_id,
|
||||||
|
} => {
|
||||||
|
let from = next_keyed_account(keyed_accounts_iter)?;
|
||||||
|
let to = next_keyed_account(keyed_accounts_iter)?;
|
||||||
|
create_account_with_seed(from, to, &seed, lamports, space, &program_id)
|
||||||
}
|
}
|
||||||
SystemInstruction::Assign { program_id } => {
|
SystemInstruction::Assign { program_id } => {
|
||||||
let account = next_keyed_account(keyed_accounts_iter)?;
|
let account = next_keyed_account(keyed_accounts_iter)?;
|
||||||
@ -163,38 +222,120 @@ mod tests {
|
|||||||
use solana_sdk::transaction::TransactionError;
|
use solana_sdk::transaction::TransactionError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_system_account() {
|
fn test_create_account() {
|
||||||
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();
|
||||||
let mut from_account = Account::new(100, 0, &system_program::id());
|
|
||||||
|
|
||||||
let to = Pubkey::new_rand();
|
let to = Pubkey::new_rand();
|
||||||
|
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());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create_system_account(
|
process_instruction(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&mut [
|
||||||
|
KeyedAccount::new(&from, true, &mut from_account),
|
||||||
|
KeyedAccount::new(&to, true, &mut to_account)
|
||||||
|
],
|
||||||
|
&bincode::serialize(&SystemInstruction::CreateAccount {
|
||||||
|
lamports: 50,
|
||||||
|
space: 2,
|
||||||
|
program_id: new_program_owner
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(from_account.lamports, 50);
|
||||||
|
assert_eq!(to_account.lamports, 50);
|
||||||
|
assert_eq!(to_account.owner, new_program_owner);
|
||||||
|
assert_eq!(to_account.data, [0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_account_with_seed() {
|
||||||
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
||||||
|
let from = Pubkey::new_rand();
|
||||||
|
let seed = "shiny pepper";
|
||||||
|
let to = create_address_with_seed(&from, seed, &new_program_owner).unwrap();
|
||||||
|
|
||||||
|
let mut from_account = Account::new(100, 0, &system_program::id());
|
||||||
|
let mut to_account = Account::new(0, 0, &Pubkey::default());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
process_instruction(
|
||||||
|
&Pubkey::default(),
|
||||||
|
&mut [
|
||||||
|
KeyedAccount::new(&from, true, &mut from_account),
|
||||||
|
KeyedAccount::new(&to, false, &mut to_account)
|
||||||
|
],
|
||||||
|
&bincode::serialize(&SystemInstruction::CreateAccountWithSeed {
|
||||||
|
seed: seed.to_string(),
|
||||||
|
lamports: 50,
|
||||||
|
space: 2,
|
||||||
|
program_id: new_program_owner
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(from_account.lamports, 50);
|
||||||
|
assert_eq!(to_account.lamports, 50);
|
||||||
|
assert_eq!(to_account.owner, new_program_owner);
|
||||||
|
assert_eq!(to_account.data, [0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_account_with_seed_mismatch() {
|
||||||
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
||||||
|
let from = Pubkey::new_rand();
|
||||||
|
let seed = "dull boy";
|
||||||
|
let to = 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!(
|
||||||
|
create_account_with_seed(
|
||||||
&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 KeyedAccount::new(&to, false, &mut to_account),
|
||||||
|
seed,
|
||||||
50,
|
50,
|
||||||
2,
|
2,
|
||||||
&new_program_owner,
|
&new_program_owner,
|
||||||
),
|
),
|
||||||
Ok(())
|
Err(SystemError::AddressWithSeedMismatch.into())
|
||||||
);
|
);
|
||||||
|
assert_eq!(from_account.lamports, 100);
|
||||||
|
assert_eq!(to_account, Account::default());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_create_account_with_seed_missing_sig() {
|
||||||
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
||||||
|
let from = Pubkey::new_rand();
|
||||||
|
let seed = "dull boy";
|
||||||
|
let to = create_address_with_seed(&from, seed, &new_program_owner).unwrap();
|
||||||
|
|
||||||
let from_lamports = from_account.lamports;
|
let mut from_account = Account::new(100, 0, &system_program::id());
|
||||||
let to_lamports = to_account.lamports;
|
let mut to_account = Account::new(0, 0, &Pubkey::default());
|
||||||
let to_owner = to_account.owner;
|
|
||||||
let to_data = to_account.data.clone();
|
assert_eq!(
|
||||||
assert_eq!(from_lamports, 50);
|
create_account_with_seed(
|
||||||
assert_eq!(to_lamports, 50);
|
&mut KeyedAccount::new(&from, false, &mut from_account),
|
||||||
assert_eq!(to_owner, new_program_owner);
|
&mut KeyedAccount::new(&to, false, &mut to_account),
|
||||||
assert_eq!(to_data, [0, 0]);
|
seed,
|
||||||
|
50,
|
||||||
|
2,
|
||||||
|
&new_program_owner,
|
||||||
|
),
|
||||||
|
Err(InstructionError::MissingRequiredSignature)
|
||||||
|
);
|
||||||
|
assert_eq!(from_account.lamports, 100);
|
||||||
|
assert_eq!(to_account, Account::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_with_zero_lamports() {
|
fn test_create_with_zero_lamports() {
|
||||||
// Attempt to create account with zero lamports
|
// create account with zero lamports tranferred
|
||||||
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();
|
||||||
let mut from_account = Account::new(100, 0, &Pubkey::new_rand()); // not from system account
|
let mut from_account = Account::new(100, 0, &Pubkey::new_rand()); // not from system account
|
||||||
@ -203,7 +344,7 @@ mod tests {
|
|||||||
let mut to_account = Account::new(0, 0, &Pubkey::default());
|
let mut to_account = Account::new(0, 0, &Pubkey::default());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
create_system_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 KeyedAccount::new(&to, true, &mut to_account),
|
||||||
0,
|
0,
|
||||||
@ -234,7 +375,7 @@ mod tests {
|
|||||||
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 unchanged_account = to_account.clone();
|
||||||
|
|
||||||
let result = create_system_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 KeyedAccount::new(&to, true, &mut to_account),
|
||||||
150,
|
150,
|
||||||
@ -255,7 +396,7 @@ mod tests {
|
|||||||
let to_account_key = Pubkey::new_rand();
|
let to_account_key = Pubkey::new_rand();
|
||||||
|
|
||||||
// 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_system_account(
|
let result = create_account(
|
||||||
&mut KeyedAccount::new(&from_account_key, true, &mut from_account),
|
&mut KeyedAccount::new(&from_account_key, true, &mut from_account),
|
||||||
&mut KeyedAccount::new(&to_account_key, true, &mut to_account),
|
&mut KeyedAccount::new(&to_account_key, true, &mut to_account),
|
||||||
50,
|
50,
|
||||||
@ -269,7 +410,7 @@ 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_system_account(
|
let result = create_account(
|
||||||
&mut KeyedAccount::new(&from_account_key, true, &mut from_account),
|
&mut KeyedAccount::new(&from_account_key, true, &mut from_account),
|
||||||
&mut KeyedAccount::new(&to_account_key, true, &mut to_account),
|
&mut KeyedAccount::new(&to_account_key, true, &mut to_account),
|
||||||
50,
|
50,
|
||||||
@ -293,7 +434,7 @@ 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 result = create_system_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 KeyedAccount::new(&owned_key, true, &mut owned_account),
|
||||||
50,
|
50,
|
||||||
@ -308,7 +449,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut owned_account = Account::new(10, 0, &Pubkey::default());
|
let mut owned_account = Account::new(10, 0, &Pubkey::default());
|
||||||
let unchanged_account = owned_account.clone();
|
let unchanged_account = owned_account.clone();
|
||||||
let result = create_system_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 KeyedAccount::new(&owned_key, true, &mut owned_account),
|
||||||
50,
|
50,
|
||||||
@ -333,7 +474,7 @@ mod tests {
|
|||||||
let unchanged_account = owned_account.clone();
|
let unchanged_account = owned_account.clone();
|
||||||
|
|
||||||
// Haven't signed from account
|
// Haven't signed from account
|
||||||
let result = create_system_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 KeyedAccount::new(&owned_key, true, &mut owned_account),
|
||||||
50,
|
50,
|
||||||
@ -345,7 +486,7 @@ mod tests {
|
|||||||
assert_eq!(owned_account, unchanged_account);
|
assert_eq!(owned_account, unchanged_account);
|
||||||
|
|
||||||
// Haven't signed to account
|
// Haven't signed to account
|
||||||
let result = create_system_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 KeyedAccount::new(&owned_key, false, &mut owned_account),
|
||||||
50,
|
50,
|
||||||
@ -357,7 +498,7 @@ mod tests {
|
|||||||
assert_eq!(owned_account, unchanged_account);
|
assert_eq!(owned_account, unchanged_account);
|
||||||
|
|
||||||
// support creation/assignment with zero lamports (ephemeral account)
|
// support creation/assignment with zero lamports (ephemeral account)
|
||||||
let result = create_system_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 KeyedAccount::new(&owned_key, true, &mut owned_account),
|
||||||
0,
|
0,
|
||||||
@ -379,7 +520,7 @@ mod tests {
|
|||||||
let mut to_account = Account::default();
|
let mut to_account = Account::default();
|
||||||
|
|
||||||
// fail to create a sysvar::id() owned account
|
// fail to create a sysvar::id() owned account
|
||||||
let result = create_system_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 KeyedAccount::new(&to, true, &mut to_account),
|
||||||
50,
|
50,
|
||||||
@ -392,7 +533,7 @@ mod tests {
|
|||||||
let mut to_account = Account::default();
|
let mut to_account = Account::default();
|
||||||
|
|
||||||
// fail to create an account with a sysvar id
|
// fail to create an account with a sysvar id
|
||||||
let result = create_system_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 KeyedAccount::new(&to, true, &mut to_account),
|
||||||
50,
|
50,
|
||||||
@ -419,7 +560,7 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let unchanged_account = populated_account.clone();
|
let unchanged_account = populated_account.clone();
|
||||||
|
|
||||||
let result = create_system_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 KeyedAccount::new(&populated_key, true, &mut populated_account),
|
||||||
50,
|
50,
|
||||||
@ -447,9 +588,13 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
assign_account_to_program(
|
process_instruction(
|
||||||
&mut KeyedAccount::new(&from, true, &mut from_account),
|
&Pubkey::default(),
|
||||||
&new_program_owner,
|
&mut [KeyedAccount::new(&from, true, &mut from_account)],
|
||||||
|
&bincode::serialize(&SystemInstruction::Assign {
|
||||||
|
program_id: new_program_owner
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
),
|
),
|
||||||
Ok(())
|
Ok(())
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::hash::hashv;
|
||||||
use crate::instruction::{AccountMeta, Instruction};
|
use crate::instruction::{AccountMeta, Instruction};
|
||||||
use crate::instruction_processor_utils::DecodeError;
|
use crate::instruction_processor_utils::DecodeError;
|
||||||
use crate::pubkey::Pubkey;
|
use crate::pubkey::Pubkey;
|
||||||
@ -11,6 +12,9 @@ pub enum SystemError {
|
|||||||
InvalidProgramId,
|
InvalidProgramId,
|
||||||
InvalidAccountId,
|
InvalidAccountId,
|
||||||
InvalidAccountDataLength,
|
InvalidAccountDataLength,
|
||||||
|
InvalidSeed,
|
||||||
|
MaxSeedLengthExceeded,
|
||||||
|
AddressWithSeedMismatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> DecodeError<T> for SystemError {
|
impl<T> DecodeError<T> for SystemError {
|
||||||
@ -26,6 +30,12 @@ impl std::fmt::Display for SystemError {
|
|||||||
}
|
}
|
||||||
impl std::error::Error for SystemError {}
|
impl std::error::Error for SystemError {}
|
||||||
|
|
||||||
|
/// maximum length of derived address seed
|
||||||
|
pub const MAX_ADDRESS_SEED_LEN: usize = 32;
|
||||||
|
|
||||||
|
/// maximum permitted size of data: 10 MB
|
||||||
|
pub const MAX_PERMITTED_DATA_LENGTH: u64 = 10 * 1024 * 1024;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||||
pub enum SystemInstruction {
|
pub enum SystemInstruction {
|
||||||
/// Create a new account
|
/// Create a new account
|
||||||
@ -46,6 +56,20 @@ pub enum SystemInstruction {
|
|||||||
/// * Transaction::keys[0] - source
|
/// * Transaction::keys[0] - source
|
||||||
/// * Transaction::keys[1] - destination
|
/// * Transaction::keys[1] - destination
|
||||||
Transfer { lamports: u64 },
|
Transfer { lamports: u64 },
|
||||||
|
/// Create a new account at an address derived from
|
||||||
|
/// the from account and a seed
|
||||||
|
/// * Transaction::keys[0] - source
|
||||||
|
/// * Transaction::keys[1] - new account key
|
||||||
|
/// * seed - string of ascii chars, no longer than MAX_ADDRESS_SEED_LEN
|
||||||
|
/// * lamports - number of lamports to transfer to the new account
|
||||||
|
/// * space - memory to allocate if greater then zero
|
||||||
|
/// * program_id - the program id of the new account
|
||||||
|
CreateAccountWithSeed {
|
||||||
|
seed: String,
|
||||||
|
lamports: u64,
|
||||||
|
space: u64,
|
||||||
|
program_id: Pubkey,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_account(
|
pub fn create_account(
|
||||||
@ -101,6 +125,20 @@ pub fn transfer_many(from_pubkey: &Pubkey, to_lamports: &[(Pubkey, u64)]) -> Vec
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_address_with_seed(
|
||||||
|
from_pubkey: &Pubkey,
|
||||||
|
seed: &str,
|
||||||
|
program_id: &Pubkey,
|
||||||
|
) -> Result<Pubkey, SystemError> {
|
||||||
|
if seed.len() > MAX_ADDRESS_SEED_LEN {
|
||||||
|
return Err(SystemError::MaxSeedLengthExceeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Pubkey::new(
|
||||||
|
hashv(&[from_pubkey.as_ref(), seed.as_ref(), program_id.as_ref()]).as_ref(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -109,6 +147,58 @@ mod tests {
|
|||||||
instruction.accounts.iter().map(|x| x.pubkey).collect()
|
instruction.accounts.iter().map(|x| x.pubkey).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_address_with_seed() {
|
||||||
|
assert!(create_address_with_seed(&Pubkey::new_rand(), "☉", &Pubkey::new_rand()).is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
create_address_with_seed(
|
||||||
|
&Pubkey::new_rand(),
|
||||||
|
std::str::from_utf8(&[127; MAX_ADDRESS_SEED_LEN + 1]).unwrap(),
|
||||||
|
&Pubkey::new_rand()
|
||||||
|
),
|
||||||
|
Err(SystemError::MaxSeedLengthExceeded)
|
||||||
|
);
|
||||||
|
assert!(create_address_with_seed(
|
||||||
|
&Pubkey::new_rand(),
|
||||||
|
"\
|
||||||
|
\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
|
||||||
|
",
|
||||||
|
&Pubkey::new_rand()
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
// utf-8 abuse ;)
|
||||||
|
assert_eq!(
|
||||||
|
create_address_with_seed(
|
||||||
|
&Pubkey::new_rand(),
|
||||||
|
"\
|
||||||
|
x\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\u{10FFFF}\
|
||||||
|
",
|
||||||
|
&Pubkey::new_rand()
|
||||||
|
),
|
||||||
|
Err(SystemError::MaxSeedLengthExceeded)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(create_address_with_seed(
|
||||||
|
&Pubkey::new_rand(),
|
||||||
|
std::str::from_utf8(&[0; MAX_ADDRESS_SEED_LEN]).unwrap(),
|
||||||
|
&Pubkey::new_rand(),
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
|
||||||
|
assert!(create_address_with_seed(&Pubkey::new_rand(), "", &Pubkey::new_rand(),).is_ok());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
create_address_with_seed(
|
||||||
|
&Pubkey::default(),
|
||||||
|
"limber chicken: 4/45",
|
||||||
|
&Pubkey::default(),
|
||||||
|
),
|
||||||
|
Ok("9h1HyLCW5dZnBVap8C5egQ9Z6pHyjsh5MNy83iPqqRuq"
|
||||||
|
.parse()
|
||||||
|
.unwrap())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_move_many() {
|
fn test_move_many() {
|
||||||
let alice_pubkey = Pubkey::new_rand();
|
let alice_pubkey = Pubkey::new_rand();
|
||||||
|
Reference in New Issue
Block a user