solitaire: Fix prefunded accounts blocking creation
Change-Id: I8841e6595b7f8bb217991515bb9ae1ae3d4f4607
This commit is contained in:
parent
4e13d9d49c
commit
2b56fcc7da
|
@ -11,6 +11,7 @@ use crate::{
|
|||
MathOverflow,
|
||||
},
|
||||
types::ConsistencyLevel,
|
||||
IsSigned::*,
|
||||
CHAIN_ID_SOLANA,
|
||||
};
|
||||
use solana_program::{
|
||||
|
@ -57,8 +58,7 @@ pub struct PostMessage<'b> {
|
|||
pub clock: Sysvar<'b, Clock>,
|
||||
}
|
||||
|
||||
impl<'b> InstructionContext<'b> for PostMessage<'b> {
|
||||
}
|
||||
impl<'b> InstructionContext<'b> for PostMessage<'b> {}
|
||||
|
||||
#[derive(BorshDeserialize, BorshSerialize)]
|
||||
pub struct PostMessageData {
|
||||
|
@ -129,14 +129,15 @@ pub fn post_message(
|
|||
|
||||
// Create message account
|
||||
let size = accs.message.size();
|
||||
let ix = solana_program::system_instruction::create_account(
|
||||
create_account(
|
||||
ctx,
|
||||
accs.message.info(),
|
||||
accs.payer.key,
|
||||
accs.message.info().key,
|
||||
Exempt.amount(size),
|
||||
size as u64,
|
||||
Exempt,
|
||||
size,
|
||||
ctx.program_id,
|
||||
);
|
||||
solana_program::program::invoke(&ix, ctx.accounts)?;
|
||||
NotSigned,
|
||||
)?;
|
||||
|
||||
// Bump sequence number
|
||||
trace!("New Sequence: {}", accs.sequence.sequence + 1);
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::{
|
|||
},
|
||||
GuardianSet,
|
||||
GuardianSetDerivationData,
|
||||
IsSigned::*,
|
||||
SignatureSet,
|
||||
MAX_LEN_GUARDIAN_KEYS,
|
||||
};
|
||||
|
@ -89,9 +90,8 @@ pub fn verify_signatures(
|
|||
})
|
||||
.collect();
|
||||
|
||||
let current_instruction = solana_program::sysvar::instructions::load_current_index_checked(
|
||||
&accs.instruction_acc,
|
||||
)?;
|
||||
let current_instruction =
|
||||
solana_program::sysvar::instructions::load_current_index_checked(&accs.instruction_acc)?;
|
||||
if current_instruction == 0 {
|
||||
return Err(InstructionAtWrongIndex.into());
|
||||
}
|
||||
|
@ -176,14 +176,15 @@ pub fn verify_signatures(
|
|||
accs.signature_set.hash = msg_hash;
|
||||
|
||||
let size = accs.signature_set.size();
|
||||
let ix = solana_program::system_instruction::create_account(
|
||||
create_account(
|
||||
ctx,
|
||||
accs.signature_set.info(),
|
||||
accs.payer.key,
|
||||
accs.signature_set.info().key,
|
||||
Exempt.amount(size),
|
||||
size as u64,
|
||||
Exempt,
|
||||
size,
|
||||
ctx.program_id,
|
||||
);
|
||||
solana_program::program::invoke(&ix, ctx.accounts)?;
|
||||
NotSigned,
|
||||
)?;
|
||||
} else {
|
||||
// If the account already existed, check that the parameters match
|
||||
if accs.signature_set.guardian_set_index != accs.guardian_set.index {
|
||||
|
|
|
@ -17,11 +17,11 @@ use solana_program::{
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{
|
||||
trace,
|
||||
processors::seeded::{
|
||||
AccountOwner,
|
||||
Owned,
|
||||
},
|
||||
trace,
|
||||
types::*,
|
||||
AccountState::MaybeInitialized,
|
||||
Context,
|
||||
|
@ -45,26 +45,32 @@ pub trait Peel<'a, 'b: 'a, 'c> {
|
|||
/// Peel a nullable value (0-account means None)
|
||||
impl<'a, 'b: 'a, 'c, T: Peel<'a, 'b, 'c>> Peel<'a, 'b, 'c> for Option<T> {
|
||||
fn peel<I>(ctx: &'c mut Context<'a, 'b, 'c, I>) -> Result<Self> {
|
||||
// Check for 0-account
|
||||
if ctx.info().key == &Pubkey::new_from_array([0u8; 32]) {
|
||||
trace!(&format!("Peeled {} is None, returning", std::any::type_name::<Option<T>>()));
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(T::peel(ctx)?))
|
||||
}
|
||||
// Check for 0-account
|
||||
if ctx.info().key == &Pubkey::new_from_array([0u8; 32]) {
|
||||
trace!(&format!(
|
||||
"Peeled {} is None, returning",
|
||||
std::any::type_name::<Option<T>>()
|
||||
));
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(T::peel(ctx)?))
|
||||
}
|
||||
}
|
||||
|
||||
fn deps() -> Vec<Pubkey> {
|
||||
T::deps()
|
||||
T::deps()
|
||||
}
|
||||
|
||||
fn persist(&self, program_id: &Pubkey) -> Result<()> {
|
||||
if let Some(s) = self.as_ref() {
|
||||
if let Some(s) = self.as_ref() {
|
||||
T::persist(s, program_id)
|
||||
} else {
|
||||
trace!(&format!("Peeled {} is None, not persisting", std::any::type_name::<Option<T>>()));
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
trace!(&format!(
|
||||
"Peeled {} is None, not persisting",
|
||||
std::any::type_name::<Option<T>>()
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +232,7 @@ impl<
|
|||
// If we're initializing the type, we should emit system/rent as deps.
|
||||
let (initialized, data): (bool, T) = match IsInitialized {
|
||||
AccountState::Uninitialized => {
|
||||
if **ctx.info().lamports.borrow() != 0 {
|
||||
if !ctx.info().data.borrow().is_empty() {
|
||||
return Err(SolitaireError::AlreadyInitialized(*ctx.info().key));
|
||||
}
|
||||
(false, T::default())
|
||||
|
@ -235,7 +241,7 @@ impl<
|
|||
(true, T::try_from_slice(&mut *ctx.info().data.borrow_mut())?)
|
||||
}
|
||||
AccountState::MaybeInitialized => {
|
||||
if **ctx.info().lamports.borrow() == 0 {
|
||||
if ctx.info().data.borrow().is_empty() {
|
||||
(false, T::default())
|
||||
} else {
|
||||
(true, T::try_from_slice(&mut *ctx.info().data.borrow_mut())?)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use super::keyed::Keyed;
|
||||
use crate::{
|
||||
create_account,
|
||||
system_instruction,
|
||||
AccountInfo,
|
||||
AccountState,
|
||||
|
@ -10,6 +11,7 @@ use crate::{
|
|||
ExecutionContext,
|
||||
FromAccounts,
|
||||
Info,
|
||||
IsSigned::*,
|
||||
Peel,
|
||||
Result,
|
||||
Signer,
|
||||
|
@ -138,15 +140,15 @@ impl<'a, 'b: 'a, K, T: AccountSize + Seeded<K> + Keyed<'a, 'b> + Owned> Creatabl
|
|||
let mut s: Vec<&[u8]> = seeds.iter().map(|item| item.as_slice()).collect();
|
||||
let mut seed_slice = s.as_slice();
|
||||
|
||||
let ix = system_instruction::create_account(
|
||||
create_account(
|
||||
ctx,
|
||||
self.info(),
|
||||
payer,
|
||||
self.info().key,
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
lamports,
|
||||
size,
|
||||
&self.owner_pubkey(ctx.program_id)?,
|
||||
);
|
||||
|
||||
Ok(invoke_signed(&ix, ctx.accounts, &[seed_slice])?)
|
||||
SignedWithSeeds(&[seed_slice]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
program::invoke_signed,
|
||||
entrypoint::ProgramResult,
|
||||
program::{
|
||||
invoke,
|
||||
invoke_signed,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
system_instruction,
|
||||
sysvar::Sysvar as SolanaSysvar,
|
||||
|
@ -22,7 +26,9 @@ use crate::{
|
|||
CreationLamports,
|
||||
Derive,
|
||||
ExecutionContext,
|
||||
Keyed,
|
||||
Result,
|
||||
SolitaireError,
|
||||
};
|
||||
|
||||
/// A short alias for AccountInfo.
|
||||
|
@ -35,6 +41,25 @@ pub enum AccountState {
|
|||
MaybeInitialized,
|
||||
}
|
||||
|
||||
/// Describes whether a cross-program invocation (CPI) should be
|
||||
/// [`SignedWithSeeds`] or [`NotSigned`].
|
||||
///
|
||||
/// CPI calls inherit the signers of the original transaction, but the calling
|
||||
/// program may optionally sign additional accounts using the program as the
|
||||
/// signer. In this case, the signature is derived in a deterministic fashion by
|
||||
/// using a set of seeds.
|
||||
///
|
||||
/// For more on program signed accounts, see the *[cpi docs].
|
||||
///
|
||||
/// [cpi docs]: https://docs.solana.com/developing/programming-model/calling-between-programs#program-signed-accounts
|
||||
#[derive(PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
||||
pub enum IsSigned<'a> {
|
||||
SignedWithSeeds(&'a [&'a [&'a [u8]]]),
|
||||
NotSigned,
|
||||
}
|
||||
|
||||
use IsSigned::*;
|
||||
|
||||
/// An account that is known to contain serialized data.
|
||||
///
|
||||
/// Note on const generics:
|
||||
|
@ -92,15 +117,16 @@ impl<const Seed: &'static str> Derive<AccountInfo<'_>, Seed> {
|
|||
space: usize,
|
||||
owner: &Pubkey,
|
||||
) -> Result<()> {
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0.key,
|
||||
lamports.amount(space),
|
||||
space as u64,
|
||||
owner,
|
||||
);
|
||||
let (_, bump_seed) = Pubkey::find_program_address(&[Seed.as_bytes()][..], ctx.program_id);
|
||||
invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes(), &[bump_seed]]]).map_err(|e| e.into())
|
||||
create_account(
|
||||
ctx,
|
||||
self.info(),
|
||||
payer,
|
||||
lamports,
|
||||
space,
|
||||
owner,
|
||||
SignedWithSeeds(&[&[Seed.as_bytes(), &[bump_seed]]]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,14 +141,60 @@ impl<const Seed: &'static str, T: BorshSerialize + Owned + Default>
|
|||
) -> Result<()> {
|
||||
// Get serialized struct size
|
||||
let size = self.0.try_to_vec().unwrap().len();
|
||||
let ix = system_instruction::create_account(
|
||||
payer,
|
||||
self.0 .0.key,
|
||||
lamports.amount(size),
|
||||
size as u64,
|
||||
ctx.program_id,
|
||||
);
|
||||
let (_, bump_seed) = Pubkey::find_program_address(&[Seed.as_bytes()][..], ctx.program_id);
|
||||
invoke_signed(&ix, ctx.accounts, &[&[Seed.as_bytes(), &[bump_seed]]]).map_err(|e| e.into())
|
||||
create_account(
|
||||
ctx,
|
||||
self.info(),
|
||||
payer,
|
||||
lamports,
|
||||
size,
|
||||
ctx.program_id,
|
||||
SignedWithSeeds(&[&[Seed.as_bytes(), &[bump_seed]]]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an account.
|
||||
///
|
||||
/// This proceeds in the following order:
|
||||
///
|
||||
/// 1. Make sure the account has sufficient funds to cover the desired rent
|
||||
/// period (top up if necessary).
|
||||
/// 2. Allocate necessary size
|
||||
/// 3. Assign ownership
|
||||
///
|
||||
/// We're not using the [`system_instruction::create_account`] instruction,
|
||||
/// because it refuses to create an account if there's already money in the
|
||||
/// account.
|
||||
pub fn create_account(
|
||||
ctx: &ExecutionContext,
|
||||
account: &Info<'_>,
|
||||
payer: &Pubkey,
|
||||
lamports: CreationLamports,
|
||||
size: usize,
|
||||
owner: &Pubkey,
|
||||
seeds: IsSigned,
|
||||
) -> Result<()> {
|
||||
let target_rent = lamports.amount(size);
|
||||
// top up account to target rent
|
||||
if account.lamports() < target_rent {
|
||||
let transfer_ix =
|
||||
system_instruction::transfer(payer, account.key, target_rent - account.lamports());
|
||||
invoke(&transfer_ix, ctx.accounts)?
|
||||
}
|
||||
// invoke is just a synonym for invoke_signed with an empty list
|
||||
let seeds = match seeds {
|
||||
SignedWithSeeds(v) => v,
|
||||
NotSigned => &[],
|
||||
};
|
||||
|
||||
// allocate space
|
||||
let allocate_ix = system_instruction::allocate(account.key, size as u64);
|
||||
invoke_signed(&allocate_ix, ctx.accounts, seeds)?;
|
||||
|
||||
// assign ownership
|
||||
let assign_ix = system_instruction::assign(account.key, owner);
|
||||
invoke_signed(&assign_ix, ctx.accounts, seeds)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue