Allow the same account to be passed multiple times to a single instruction (#7795)
This commit is contained in:
@ -1,6 +1,12 @@
|
||||
use crate::hash::Hash;
|
||||
use crate::instruction::InstructionError;
|
||||
use crate::{clock::Epoch, pubkey::Pubkey};
|
||||
use std::{cmp, fmt, iter::FromIterator};
|
||||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
cmp, fmt,
|
||||
iter::FromIterator,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
/// An Account with data that is stored on chain
|
||||
#[repr(C)]
|
||||
@ -69,6 +75,9 @@ impl Account {
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
pub fn new_ref(lamports: u64, space: usize, owner: &Pubkey) -> Rc<RefCell<Self>> {
|
||||
Rc::new(RefCell::new(Self::new(lamports, space, owner)))
|
||||
}
|
||||
|
||||
pub fn new_data<T: serde::Serialize>(
|
||||
lamports: u64,
|
||||
@ -83,6 +92,13 @@ impl Account {
|
||||
..Self::default()
|
||||
})
|
||||
}
|
||||
pub fn new_ref_data<T: serde::Serialize>(
|
||||
lamports: u64,
|
||||
state: &T,
|
||||
owner: &Pubkey,
|
||||
) -> Result<RefCell<Self>, bincode::Error> {
|
||||
Ok(RefCell::new(Self::new_data(lamports, state, owner)?))
|
||||
}
|
||||
|
||||
pub fn new_data_with_space<T: serde::Serialize>(
|
||||
lamports: u64,
|
||||
@ -96,6 +112,16 @@ impl Account {
|
||||
|
||||
Ok(account)
|
||||
}
|
||||
pub fn new_ref_data_with_space<T: serde::Serialize>(
|
||||
lamports: u64,
|
||||
state: &T,
|
||||
space: usize,
|
||||
owner: &Pubkey,
|
||||
) -> Result<RefCell<Self>, bincode::Error> {
|
||||
Ok(RefCell::new(Self::new_data_with_space(
|
||||
lamports, state, space, owner,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub fn deserialize_data<T: serde::de::DeserializeOwned>(&self) -> Result<T, bincode::Error> {
|
||||
bincode::deserialize(&self.data)
|
||||
@ -115,7 +141,7 @@ pub struct KeyedAccount<'a> {
|
||||
is_signer: bool, // Transaction was signed by this account's key
|
||||
is_writable: bool,
|
||||
key: &'a Pubkey,
|
||||
pub account: &'a mut Account,
|
||||
pub account: &'a RefCell<Account>,
|
||||
}
|
||||
|
||||
impl<'a> KeyedAccount<'a> {
|
||||
@ -135,7 +161,46 @@ impl<'a> KeyedAccount<'a> {
|
||||
self.is_writable
|
||||
}
|
||||
|
||||
pub fn new(key: &'a Pubkey, is_signer: bool, account: &'a mut Account) -> Self {
|
||||
pub fn lamports(&self) -> Result<u64, InstructionError> {
|
||||
Ok(self.try_borrow()?.lamports)
|
||||
}
|
||||
|
||||
pub fn data_len(&self) -> Result<usize, InstructionError> {
|
||||
Ok(self.try_borrow()?.data.len())
|
||||
}
|
||||
|
||||
pub fn data_is_empty(&self) -> Result<bool, InstructionError> {
|
||||
Ok(self.try_borrow()?.data.is_empty())
|
||||
}
|
||||
|
||||
pub fn owner(&self) -> Result<Pubkey, InstructionError> {
|
||||
Ok(self.try_borrow()?.owner)
|
||||
}
|
||||
|
||||
pub fn executable(&self) -> Result<bool, InstructionError> {
|
||||
Ok(self.try_borrow()?.executable)
|
||||
}
|
||||
|
||||
pub fn try_account_ref(&'a self) -> Result<Ref<Account>, InstructionError> {
|
||||
self.try_borrow()
|
||||
}
|
||||
|
||||
pub fn try_account_ref_mut(&'a self) -> Result<RefMut<Account>, InstructionError> {
|
||||
self.try_borrow_mut()
|
||||
}
|
||||
|
||||
fn try_borrow(&self) -> Result<Ref<Account>, InstructionError> {
|
||||
self.account
|
||||
.try_borrow()
|
||||
.map_err(|_| InstructionError::AccountBorrowFailed)
|
||||
}
|
||||
fn try_borrow_mut(&self) -> Result<RefMut<Account>, InstructionError> {
|
||||
self.account
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| InstructionError::AccountBorrowFailed)
|
||||
}
|
||||
|
||||
pub fn new(key: &'a Pubkey, is_signer: bool, account: &'a RefCell<Account>) -> Self {
|
||||
Self {
|
||||
is_signer,
|
||||
is_writable: true,
|
||||
@ -144,7 +209,7 @@ impl<'a> KeyedAccount<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_readonly(key: &'a Pubkey, is_signer: bool, account: &'a mut Account) -> Self {
|
||||
pub fn new_readonly(key: &'a Pubkey, is_signer: bool, account: &'a RefCell<Account>) -> Self {
|
||||
Self {
|
||||
is_signer,
|
||||
is_writable: false,
|
||||
@ -154,8 +219,14 @@ impl<'a> KeyedAccount<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<(&'a Pubkey, &'a mut Account)> for KeyedAccount<'a> {
|
||||
fn from((key, account): (&'a Pubkey, &'a mut Account)) -> Self {
|
||||
impl<'a> PartialEq for KeyedAccount<'a> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.key == other.key
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<(&'a Pubkey, &'a RefCell<Account>)> for KeyedAccount<'a> {
|
||||
fn from((key, account): (&'a Pubkey, &'a RefCell<Account>)) -> Self {
|
||||
Self {
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
@ -165,8 +236,8 @@ impl<'a> From<(&'a Pubkey, &'a mut Account)> for KeyedAccount<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<(&'a Pubkey, bool, &'a mut Account)> for KeyedAccount<'a> {
|
||||
fn from((key, is_signer, account): (&'a Pubkey, bool, &'a mut Account)) -> Self {
|
||||
impl<'a> From<(&'a Pubkey, bool, &'a RefCell<Account>)> for KeyedAccount<'a> {
|
||||
fn from((key, is_signer, account): (&'a Pubkey, bool, &'a RefCell<Account>)) -> Self {
|
||||
Self {
|
||||
is_signer,
|
||||
is_writable: true,
|
||||
@ -176,8 +247,8 @@ impl<'a> From<(&'a Pubkey, bool, &'a mut Account)> for KeyedAccount<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a mut (Pubkey, Account)> for KeyedAccount<'a> {
|
||||
fn from((key, account): &'a mut (Pubkey, Account)) -> Self {
|
||||
impl<'a> From<&'a mut (&'a Pubkey, &'a RefCell<Account>)> for KeyedAccount<'a> {
|
||||
fn from((key, account): &'a mut (&'a Pubkey, &'a RefCell<Account>)) -> Self {
|
||||
Self {
|
||||
is_signer: false,
|
||||
is_writable: true,
|
||||
@ -187,12 +258,14 @@ impl<'a> From<&'a mut (Pubkey, Account)> for KeyedAccount<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_keyed_accounts(accounts: &mut [(Pubkey, Account)]) -> Vec<KeyedAccount> {
|
||||
pub fn create_keyed_accounts<'a>(
|
||||
accounts: &'a mut [(&'a Pubkey, &'a RefCell<Account>)],
|
||||
) -> Vec<KeyedAccount<'a>> {
|
||||
accounts.iter_mut().map(Into::into).collect()
|
||||
}
|
||||
|
||||
pub fn create_keyed_is_signer_accounts<'a>(
|
||||
accounts: &'a mut [(&'a Pubkey, bool, &'a mut Account)],
|
||||
accounts: &'a mut [(&'a Pubkey, bool, &'a mut RefCell<Account>)],
|
||||
) -> Vec<KeyedAccount<'a>> {
|
||||
accounts
|
||||
.iter_mut()
|
||||
@ -205,7 +278,9 @@ pub fn create_keyed_is_signer_accounts<'a>(
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn create_keyed_readonly_accounts(accounts: &mut [(Pubkey, Account)]) -> Vec<KeyedAccount> {
|
||||
pub fn create_keyed_readonly_accounts(
|
||||
accounts: &mut [(Pubkey, RefCell<Account>)],
|
||||
) -> Vec<KeyedAccount> {
|
||||
accounts
|
||||
.iter_mut()
|
||||
.map(|(key, account)| KeyedAccount {
|
||||
|
@ -30,10 +30,10 @@ where
|
||||
T: serde::Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
fn state(&self) -> Result<T, InstructionError> {
|
||||
self.account.state()
|
||||
self.try_account_ref()?.state()
|
||||
}
|
||||
fn set_state(&mut self, state: &T) -> Result<(), InstructionError> {
|
||||
self.account.set_state(state)
|
||||
self.try_account_ref_mut()?.set_state(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ pub enum InstructionError {
|
||||
ReadonlyDataModified,
|
||||
|
||||
/// An account was referenced more than once in a single instruction
|
||||
// Deprecated, instructions can now contain duplicate accounts
|
||||
DuplicateAccountIndex,
|
||||
|
||||
/// Executable bit on account changed, but shouldn't have
|
||||
@ -76,6 +77,17 @@ pub enum InstructionError {
|
||||
/// The instruction expected an executable account
|
||||
AccountNotExecutable,
|
||||
|
||||
/// Failed to borrow a reference to an account, already borrowed
|
||||
AccountBorrowFailed,
|
||||
|
||||
/// Account has an outstanding reference after a program's execution
|
||||
AccountBorrowOutstanding,
|
||||
|
||||
/// The same account was multiply passed to an on-chain program's entrypoint, but the program
|
||||
/// modified them differently. A program can only modify one instance of the account because
|
||||
/// the runtime cannot determine which changes to pick or how to merge them if both are modified
|
||||
DuplicateAccountOutOfSync,
|
||||
|
||||
/// CustomError allows on-chain programs to implement program-specific error types and see
|
||||
/// them returned by the Solana runtime. A CustomError may be any type that is represented
|
||||
/// as or serialized to a u32 integer.
|
||||
|
@ -115,8 +115,8 @@ pub fn next_keyed_account<I: Iterator>(iter: &mut I) -> Result<I::Item, Instruct
|
||||
|
||||
/// Return true if the first keyed_account is executable, used to determine if
|
||||
/// the loader should call a program's 'main'
|
||||
pub fn is_executable(keyed_accounts: &[KeyedAccount]) -> bool {
|
||||
!keyed_accounts.is_empty() && keyed_accounts[0].account.executable
|
||||
pub fn is_executable(keyed_accounts: &[KeyedAccount]) -> Result<bool, InstructionError> {
|
||||
Ok(!keyed_accounts.is_empty() && keyed_accounts[0].executable()?)
|
||||
}
|
||||
|
||||
pub fn limited_deserialize<T>(instruction_data: &[u8]) -> Result<T, InstructionError>
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
sysvar::rent::Rent,
|
||||
};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashSet;
|
||||
use std::{cell::RefCell, collections::HashSet};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Clone, Copy)]
|
||||
pub struct Meta {
|
||||
@ -107,19 +107,19 @@ impl<'a> NonceAccount for KeyedAccount<'a> {
|
||||
) -> Result<(), InstructionError> {
|
||||
let signer = match self.state()? {
|
||||
NonceState::Uninitialized => {
|
||||
if lamports > self.account.lamports {
|
||||
if lamports > self.lamports()? {
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
*self.unsigned_key()
|
||||
}
|
||||
NonceState::Initialized(meta, ref hash) => {
|
||||
if lamports == self.account.lamports {
|
||||
if lamports == self.lamports()? {
|
||||
if *hash == recent_blockhashes[0] {
|
||||
return Err(NonceError::NotExpired.into());
|
||||
}
|
||||
} else {
|
||||
let min_balance = rent.minimum_balance(self.account.data.len());
|
||||
if lamports + min_balance > self.account.lamports {
|
||||
let min_balance = rent.minimum_balance(self.data_len()?);
|
||||
if lamports + min_balance > self.lamports()? {
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
}
|
||||
@ -131,8 +131,8 @@ impl<'a> NonceAccount for KeyedAccount<'a> {
|
||||
return Err(InstructionError::MissingRequiredSignature);
|
||||
}
|
||||
|
||||
self.account.lamports -= lamports;
|
||||
to.account.lamports += lamports;
|
||||
self.try_account_ref_mut()?.lamports -= lamports;
|
||||
to.try_account_ref_mut()?.lamports += lamports;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -149,8 +149,8 @@ impl<'a> NonceAccount for KeyedAccount<'a> {
|
||||
|
||||
let meta = match self.state()? {
|
||||
NonceState::Uninitialized => {
|
||||
let min_balance = rent.minimum_balance(self.account.data.len());
|
||||
if self.account.lamports < min_balance {
|
||||
let min_balance = rent.minimum_balance(self.data_len()?);
|
||||
if self.lamports()? < min_balance {
|
||||
return Err(InstructionError::InsufficientFunds);
|
||||
}
|
||||
Meta::new(nonce_authority)
|
||||
@ -178,14 +178,16 @@ impl<'a> NonceAccount for KeyedAccount<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_account(lamports: u64) -> Account {
|
||||
Account::new_data_with_space(
|
||||
lamports,
|
||||
&NonceState::Uninitialized,
|
||||
NonceState::size(),
|
||||
&system_program::id(),
|
||||
pub fn create_account(lamports: u64) -> RefCell<Account> {
|
||||
RefCell::new(
|
||||
Account::new_data_with_space(
|
||||
lamports,
|
||||
&NonceState::Uninitialized,
|
||||
NonceState::size(),
|
||||
&system_program::id(),
|
||||
)
|
||||
.expect("nonce_account"),
|
||||
)
|
||||
.expect("nonce_account")
|
||||
}
|
||||
|
||||
/// Convenience function for working with keyed accounts in tests
|
||||
@ -195,8 +197,8 @@ where
|
||||
F: FnMut(&mut KeyedAccount),
|
||||
{
|
||||
let pubkey = Pubkey::new_rand();
|
||||
let mut account = create_account(lamports);
|
||||
let mut keyed_account = KeyedAccount::new(&pubkey, signer, &mut account);
|
||||
let account = create_account(lamports);
|
||||
let mut keyed_account = KeyedAccount::new(&pubkey, signer, &account);
|
||||
f(&mut keyed_account)
|
||||
}
|
||||
|
||||
@ -262,9 +264,10 @@ mod test {
|
||||
assert_eq!(state, NonceState::Initialized(meta, stored));
|
||||
with_test_keyed_account(42, false, |mut to_keyed| {
|
||||
let recent_blockhashes = create_test_recent_blockhashes(0);
|
||||
let withdraw_lamports = keyed_account.account.lamports;
|
||||
let expect_nonce_lamports = keyed_account.account.lamports - withdraw_lamports;
|
||||
let expect_to_lamports = to_keyed.account.lamports + withdraw_lamports;
|
||||
let withdraw_lamports = keyed_account.account.borrow().lamports;
|
||||
let expect_nonce_lamports =
|
||||
keyed_account.account.borrow().lamports - withdraw_lamports;
|
||||
let expect_to_lamports = to_keyed.account.borrow().lamports + withdraw_lamports;
|
||||
keyed_account
|
||||
.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
@ -275,9 +278,12 @@ mod test {
|
||||
)
|
||||
.unwrap();
|
||||
// Empties NonceAccount balance
|
||||
assert_eq!(keyed_account.account.lamports, expect_nonce_lamports);
|
||||
assert_eq!(
|
||||
keyed_account.account.borrow().lamports,
|
||||
expect_nonce_lamports
|
||||
);
|
||||
// NonceAccount balance goes to `to`
|
||||
assert_eq!(to_keyed.account.lamports, expect_to_lamports);
|
||||
assert_eq!(to_keyed.account.borrow().lamports, expect_to_lamports);
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -297,7 +303,7 @@ mod test {
|
||||
nonce_account
|
||||
.nonce_initialize(&authorized, &recent_blockhashes, &rent)
|
||||
.unwrap();
|
||||
let pubkey = nonce_account.account.owner.clone();
|
||||
let pubkey = nonce_account.account.borrow().owner.clone();
|
||||
let mut nonce_account = KeyedAccount::new(&pubkey, false, nonce_account.account);
|
||||
let state: NonceState = nonce_account.state().unwrap();
|
||||
assert_eq!(state, NonceState::Initialized(meta, stored));
|
||||
@ -426,9 +432,10 @@ mod test {
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_keyed.signer_key().unwrap().clone());
|
||||
let recent_blockhashes = create_test_recent_blockhashes(0);
|
||||
let withdraw_lamports = nonce_keyed.account.lamports;
|
||||
let expect_nonce_lamports = nonce_keyed.account.lamports - withdraw_lamports;
|
||||
let expect_to_lamports = to_keyed.account.lamports + withdraw_lamports;
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports;
|
||||
let expect_nonce_lamports =
|
||||
nonce_keyed.account.borrow().lamports - withdraw_lamports;
|
||||
let expect_to_lamports = to_keyed.account.borrow().lamports + withdraw_lamports;
|
||||
nonce_keyed
|
||||
.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
@ -443,9 +450,9 @@ mod test {
|
||||
// Deinitializes NonceAccount state
|
||||
assert_eq!(state, NonceState::Uninitialized);
|
||||
// Empties NonceAccount balance
|
||||
assert_eq!(nonce_keyed.account.lamports, expect_nonce_lamports);
|
||||
assert_eq!(nonce_keyed.account.borrow().lamports, expect_nonce_lamports);
|
||||
// NonceAccount balance goes to `to`
|
||||
assert_eq!(to_keyed.account.lamports, expect_to_lamports);
|
||||
assert_eq!(to_keyed.account.borrow().lamports, expect_to_lamports);
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -463,8 +470,9 @@ mod test {
|
||||
with_test_keyed_account(42, false, |mut to_keyed| {
|
||||
let signers = HashSet::new();
|
||||
let recent_blockhashes = create_test_recent_blockhashes(0);
|
||||
let lamports = nonce_keyed.account.borrow().lamports;
|
||||
let result = nonce_keyed.nonce_withdraw(
|
||||
nonce_keyed.account.lamports,
|
||||
lamports,
|
||||
&mut to_keyed,
|
||||
&recent_blockhashes,
|
||||
&rent,
|
||||
@ -489,8 +497,9 @@ mod test {
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_keyed.signer_key().unwrap().clone());
|
||||
let recent_blockhashes = create_test_recent_blockhashes(0);
|
||||
let lamports = nonce_keyed.account.borrow().lamports + 1;
|
||||
let result = nonce_keyed.nonce_withdraw(
|
||||
nonce_keyed.account.lamports + 1,
|
||||
lamports,
|
||||
&mut to_keyed,
|
||||
&recent_blockhashes,
|
||||
&rent,
|
||||
@ -513,9 +522,10 @@ mod test {
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_keyed.signer_key().unwrap().clone());
|
||||
let recent_blockhashes = create_test_recent_blockhashes(0);
|
||||
let withdraw_lamports = nonce_keyed.account.lamports / 2;
|
||||
let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports;
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports / 2;
|
||||
let nonce_expect_lamports =
|
||||
nonce_keyed.account.borrow().lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports;
|
||||
nonce_keyed
|
||||
.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
@ -527,11 +537,12 @@ mod test {
|
||||
.unwrap();
|
||||
let state: NonceState = nonce_keyed.state().unwrap();
|
||||
assert_eq!(state, NonceState::Uninitialized);
|
||||
assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.lamports, to_expect_lamports);
|
||||
let withdraw_lamports = nonce_keyed.account.lamports;
|
||||
let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports;
|
||||
assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports);
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports;
|
||||
let nonce_expect_lamports =
|
||||
nonce_keyed.account.borrow().lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports;
|
||||
nonce_keyed
|
||||
.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
@ -543,8 +554,8 @@ mod test {
|
||||
.unwrap();
|
||||
let state: NonceState = nonce_keyed.state().unwrap();
|
||||
assert_eq!(state, NonceState::Uninitialized);
|
||||
assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.lamports, to_expect_lamports);
|
||||
assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports);
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -569,9 +580,10 @@ mod test {
|
||||
let stored = recent_blockhashes[0];
|
||||
assert_eq!(state, NonceState::Initialized(meta, stored));
|
||||
with_test_keyed_account(42, false, |mut to_keyed| {
|
||||
let withdraw_lamports = nonce_keyed.account.lamports - min_lamports;
|
||||
let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports;
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports - min_lamports;
|
||||
let nonce_expect_lamports =
|
||||
nonce_keyed.account.borrow().lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports;
|
||||
nonce_keyed
|
||||
.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
@ -584,12 +596,13 @@ mod test {
|
||||
let state: NonceState = nonce_keyed.state().unwrap();
|
||||
let stored = recent_blockhashes[0];
|
||||
assert_eq!(state, NonceState::Initialized(meta, stored));
|
||||
assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.lamports, to_expect_lamports);
|
||||
assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports);
|
||||
let recent_blockhashes = create_test_recent_blockhashes(0);
|
||||
let withdraw_lamports = nonce_keyed.account.lamports;
|
||||
let nonce_expect_lamports = nonce_keyed.account.lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.lamports + withdraw_lamports;
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports;
|
||||
let nonce_expect_lamports =
|
||||
nonce_keyed.account.borrow().lamports - withdraw_lamports;
|
||||
let to_expect_lamports = to_keyed.account.borrow().lamports + withdraw_lamports;
|
||||
nonce_keyed
|
||||
.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
@ -599,8 +612,8 @@ mod test {
|
||||
&signers,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(nonce_keyed.account.lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.lamports, to_expect_lamports);
|
||||
assert_eq!(nonce_keyed.account.borrow().lamports, nonce_expect_lamports);
|
||||
assert_eq!(to_keyed.account.borrow().lamports, to_expect_lamports);
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -621,7 +634,7 @@ mod test {
|
||||
with_test_keyed_account(42, false, |mut to_keyed| {
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_keyed.signer_key().unwrap().clone());
|
||||
let withdraw_lamports = nonce_keyed.account.lamports;
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports;
|
||||
let result = nonce_keyed.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
&mut to_keyed,
|
||||
@ -651,7 +664,7 @@ mod test {
|
||||
let recent_blockhashes = create_test_recent_blockhashes(63);
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_keyed.signer_key().unwrap().clone());
|
||||
let withdraw_lamports = nonce_keyed.account.lamports + 1;
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports + 1;
|
||||
let result = nonce_keyed.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
&mut to_keyed,
|
||||
@ -681,7 +694,7 @@ mod test {
|
||||
let recent_blockhashes = create_test_recent_blockhashes(63);
|
||||
let mut signers = HashSet::new();
|
||||
signers.insert(nonce_keyed.signer_key().unwrap().clone());
|
||||
let withdraw_lamports = nonce_keyed.account.lamports - min_lamports + 1;
|
||||
let withdraw_lamports = nonce_keyed.account.borrow().lamports - min_lamports + 1;
|
||||
let result = nonce_keyed.nonce_withdraw(
|
||||
withdraw_lamports,
|
||||
&mut to_keyed,
|
||||
|
@ -80,7 +80,8 @@ pub trait Sysvar:
|
||||
if !Self::check_id(keyed_account.unsigned_key()) {
|
||||
return Err(InstructionError::InvalidArgument);
|
||||
}
|
||||
Self::from_account(keyed_account.account).ok_or(InstructionError::InvalidArgument)
|
||||
Self::from_account(&*keyed_account.try_account_ref()?)
|
||||
.ok_or(InstructionError::InvalidArgument)
|
||||
}
|
||||
fn create_account(&self, lamports: u64) -> Account {
|
||||
let data_len = Self::size_of().max(bincode::serialized_size(self).unwrap() as usize);
|
||||
|
@ -21,10 +21,7 @@ pub fn verify_rent_exemption(
|
||||
rent_sysvar_account: &KeyedAccount,
|
||||
) -> Result<(), InstructionError> {
|
||||
let rent = Rent::from_keyed_account(rent_sysvar_account)?;
|
||||
if !rent.is_exempt(
|
||||
keyed_account.account.lamports,
|
||||
keyed_account.account.data.len(),
|
||||
) {
|
||||
if !rent.is_exempt(keyed_account.lamports()?, keyed_account.data_len()?) {
|
||||
Err(InstructionError::InsufficientFunds)
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -601,4 +601,29 @@ mod tests {
|
||||
);
|
||||
assert!(tx.is_signed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_instruction_with_duplicate_keys() {
|
||||
let program_id = Pubkey::default();
|
||||
let keypair0 = Keypair::new();
|
||||
let id0 = keypair0.pubkey();
|
||||
let id1 = Pubkey::new_rand();
|
||||
let ix = Instruction::new(
|
||||
program_id,
|
||||
&0,
|
||||
vec![
|
||||
AccountMeta::new(id0, true),
|
||||
AccountMeta::new(id1, false),
|
||||
AccountMeta::new(id0, false),
|
||||
AccountMeta::new(id1, false),
|
||||
],
|
||||
);
|
||||
let mut tx = Transaction::new_unsigned_instructions(vec![ix]);
|
||||
tx.sign(&[&keypair0], Hash::default());
|
||||
assert_eq!(
|
||||
tx.message.instructions[0],
|
||||
CompiledInstruction::new(2, &0, vec![0, 1, 0, 1])
|
||||
);
|
||||
assert!(tx.is_signed());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user