lang: Reduce compute unit consumption of loader accounts (#1134)
This commit is contained in:
parent
a7e80079da
commit
a3c8d20352
|
@ -258,7 +258,7 @@ jobs:
|
||||||
path: tests/cashiers-check
|
path: tests/cashiers-check
|
||||||
- cmd: cd tests/typescript && anchor test
|
- cmd: cd tests/typescript && anchor test
|
||||||
path: tests/typescript
|
path: tests/typescript
|
||||||
- cmd: cd tests/zero-copy && anchor test
|
- cmd: cd tests/zero-copy && anchor test && cd programs/zero-copy && cargo test-bpf
|
||||||
path: tests/zero-copy
|
path: tests/zero-copy
|
||||||
- cmd: cd tests/chat && anchor test
|
- cmd: cd tests/chat && anchor test
|
||||||
path: tests/chat
|
path: tests/chat
|
||||||
|
|
|
@ -212,6 +212,7 @@ dependencies = [
|
||||||
"anchor-attribute-program",
|
"anchor-attribute-program",
|
||||||
"anchor-attribute-state",
|
"anchor-attribute-state",
|
||||||
"anchor-derive-accounts",
|
"anchor-derive-accounts",
|
||||||
|
"arrayref",
|
||||||
"base64 0.13.0",
|
"base64 0.13.0",
|
||||||
"bincode",
|
"bincode",
|
||||||
"borsh",
|
"borsh",
|
||||||
|
|
|
@ -33,6 +33,7 @@ anchor-attribute-state = { path = "./attribute/state", version = "0.19.0" }
|
||||||
anchor-attribute-interface = { path = "./attribute/interface", version = "0.19.0" }
|
anchor-attribute-interface = { path = "./attribute/interface", version = "0.19.0" }
|
||||||
anchor-attribute-event = { path = "./attribute/event", version = "0.19.0" }
|
anchor-attribute-event = { path = "./attribute/event", version = "0.19.0" }
|
||||||
anchor-derive-accounts = { path = "./derive/accounts", version = "0.19.0" }
|
anchor-derive-accounts = { path = "./derive/accounts", version = "0.19.0" }
|
||||||
|
arrayref = "0.3.6"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
borsh = "0.9"
|
borsh = "0.9"
|
||||||
bytemuck = "1.4.0"
|
bytemuck = "1.4.0"
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::{
|
||||||
Accounts, AccountsClose, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
Accounts, AccountsClose, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||||
ZeroCopy,
|
ZeroCopy,
|
||||||
};
|
};
|
||||||
|
use arrayref::array_ref;
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::entrypoint::ProgramResult;
|
use solana_program::entrypoint::ProgramResult;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
|
@ -62,9 +63,8 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
||||||
}
|
}
|
||||||
let data: &[u8] = &acc_info.try_borrow_data()?;
|
let data: &[u8] = &acc_info.try_borrow_data()?;
|
||||||
// Discriminator must match.
|
// Discriminator must match.
|
||||||
let mut disc_bytes = [0u8; 8];
|
let disc_bytes = array_ref![data, 0, 8];
|
||||||
disc_bytes.copy_from_slice(&data[..8]);
|
if disc_bytes != &T::discriminator() {
|
||||||
if disc_bytes != T::discriminator() {
|
|
||||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +89,8 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
||||||
pub fn load(&self) -> Result<Ref<T>, ProgramError> {
|
pub fn load(&self) -> Result<Ref<T>, ProgramError> {
|
||||||
let data = self.acc_info.try_borrow_data()?;
|
let data = self.acc_info.try_borrow_data()?;
|
||||||
|
|
||||||
let mut disc_bytes = [0u8; 8];
|
let disc_bytes = array_ref![data, 0, 8];
|
||||||
disc_bytes.copy_from_slice(&data[..8]);
|
if disc_bytes != &T::discriminator() {
|
||||||
if disc_bytes != T::discriminator() {
|
|
||||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,9 +108,8 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
||||||
|
|
||||||
let data = self.acc_info.try_borrow_mut_data()?;
|
let data = self.acc_info.try_borrow_mut_data()?;
|
||||||
|
|
||||||
let mut disc_bytes = [0u8; 8];
|
let disc_bytes = array_ref![data, 0, 8];
|
||||||
disc_bytes.copy_from_slice(&data[..8]);
|
if disc_bytes != &T::discriminator() {
|
||||||
if disc_bytes != T::discriminator() {
|
|
||||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::{
|
||||||
Accounts, AccountsClose, AccountsExit, Key, Owner, ToAccountInfo, ToAccountInfos,
|
Accounts, AccountsClose, AccountsExit, Key, Owner, ToAccountInfo, ToAccountInfos,
|
||||||
ToAccountMetas, ZeroCopy,
|
ToAccountMetas, ZeroCopy,
|
||||||
};
|
};
|
||||||
|
use arrayref::array_ref;
|
||||||
use solana_program::account_info::AccountInfo;
|
use solana_program::account_info::AccountInfo;
|
||||||
use solana_program::entrypoint::ProgramResult;
|
use solana_program::entrypoint::ProgramResult;
|
||||||
use solana_program::instruction::AccountMeta;
|
use solana_program::instruction::AccountMeta;
|
||||||
|
@ -58,9 +59,8 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
|
||||||
}
|
}
|
||||||
let data: &[u8] = &acc_info.try_borrow_data()?;
|
let data: &[u8] = &acc_info.try_borrow_data()?;
|
||||||
// Discriminator must match.
|
// Discriminator must match.
|
||||||
let mut disc_bytes = [0u8; 8];
|
let disc_bytes = array_ref![data, 0, 8];
|
||||||
disc_bytes.copy_from_slice(&data[..8]);
|
if disc_bytes != &T::discriminator() {
|
||||||
if disc_bytes != T::discriminator() {
|
|
||||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,9 +83,8 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
|
||||||
pub fn load(&self) -> Result<Ref<T>, ProgramError> {
|
pub fn load(&self) -> Result<Ref<T>, ProgramError> {
|
||||||
let data = self.acc_info.try_borrow_data()?;
|
let data = self.acc_info.try_borrow_data()?;
|
||||||
|
|
||||||
let mut disc_bytes = [0u8; 8];
|
let disc_bytes = array_ref![data, 0, 8];
|
||||||
disc_bytes.copy_from_slice(&data[..8]);
|
if disc_bytes != &T::discriminator() {
|
||||||
if disc_bytes != T::discriminator() {
|
|
||||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,9 +101,8 @@ impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> {
|
||||||
|
|
||||||
let data = self.acc_info.try_borrow_mut_data()?;
|
let data = self.acc_info.try_borrow_mut_data()?;
|
||||||
|
|
||||||
let mut disc_bytes = [0u8; 8];
|
let disc_bytes = array_ref![data, 0, 8];
|
||||||
disc_bytes.copy_from_slice(&data[..8]);
|
if disc_bytes != &T::discriminator() {
|
||||||
if disc_bytes != T::discriminator() {
|
|
||||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
// TODO: remove once we allow segmented paths in `Accounts` structs.
|
// TODO: remove once we allow segmented paths in `Accounts` structs.
|
||||||
use #mod_name::*;
|
use self::#mod_name::*;
|
||||||
|
|
||||||
#entry
|
#entry
|
||||||
#dispatch
|
#dispatch
|
||||||
|
|
|
@ -13,6 +13,12 @@ no-entrypoint = []
|
||||||
no-idl = []
|
no-idl = []
|
||||||
cpi = ["no-entrypoint"]
|
cpi = ["no-entrypoint"]
|
||||||
default = []
|
default = []
|
||||||
|
test-bpf = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anchor-lang = { path = "../../../../lang" }
|
anchor-lang = { path = "../../../../lang" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
anchor-client = { path = "../../../../client", features = ["debug"] }
|
||||||
|
bytemuck = "1.4.0"
|
||||||
|
solana-program-test = "1.8.0"
|
||||||
|
|
|
@ -11,8 +11,6 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||||
|
|
||||||
#[program]
|
#[program]
|
||||||
pub mod zero_copy {
|
pub mod zero_copy {
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn create_foo(ctx: Context<CreateFoo>) -> ProgramResult {
|
pub fn create_foo(ctx: Context<CreateFoo>) -> ProgramResult {
|
||||||
|
@ -140,6 +138,7 @@ pub struct UpdateLargeAccount<'info> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[account(zero_copy)]
|
#[account(zero_copy)]
|
||||||
|
#[derive(Default)]
|
||||||
pub struct Foo {
|
pub struct Foo {
|
||||||
pub authority: Pubkey,
|
pub authority: Pubkey,
|
||||||
pub data: u64,
|
pub data: u64,
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
#![cfg(feature = "test-bpf")]
|
||||||
|
|
||||||
|
use {
|
||||||
|
anchor_client::{
|
||||||
|
anchor_lang::Discriminator,
|
||||||
|
solana_sdk::{
|
||||||
|
account::Account,
|
||||||
|
commitment_config::CommitmentConfig,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
transaction::Transaction,
|
||||||
|
},
|
||||||
|
Client, Cluster,
|
||||||
|
},
|
||||||
|
solana_program_test::{tokio, ProgramTest},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_foo() {
|
||||||
|
let authority = Keypair::new();
|
||||||
|
let foo_pubkey = Pubkey::new_unique();
|
||||||
|
let foo_account = {
|
||||||
|
let mut foo_data = Vec::new();
|
||||||
|
foo_data.extend_from_slice(&zero_copy::Foo::discriminator());
|
||||||
|
foo_data.extend_from_slice(bytemuck::bytes_of(&zero_copy::Foo {
|
||||||
|
authority: authority.pubkey(),
|
||||||
|
..zero_copy::Foo::default()
|
||||||
|
}));
|
||||||
|
|
||||||
|
Account {
|
||||||
|
lamports: 1,
|
||||||
|
data: foo_data,
|
||||||
|
owner: zero_copy::id(),
|
||||||
|
..Account::default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut pt = ProgramTest::new("zero_copy", zero_copy::id(), None);
|
||||||
|
pt.add_account(foo_pubkey, foo_account);
|
||||||
|
pt.set_bpf_compute_max_units(2077);
|
||||||
|
let (mut banks_client, payer, recent_blockhash) = pt.start().await;
|
||||||
|
|
||||||
|
let client = Client::new_with_options(
|
||||||
|
Cluster::Debug,
|
||||||
|
Keypair::new(),
|
||||||
|
CommitmentConfig::processed(),
|
||||||
|
);
|
||||||
|
let program = client.program(zero_copy::id());
|
||||||
|
let update_ix = program
|
||||||
|
.request()
|
||||||
|
.accounts(zero_copy::accounts::UpdateFoo {
|
||||||
|
foo: foo_pubkey,
|
||||||
|
authority: authority.pubkey(),
|
||||||
|
})
|
||||||
|
.args(zero_copy::instruction::UpdateFoo { data: 1u64 })
|
||||||
|
.instructions()
|
||||||
|
.unwrap()
|
||||||
|
.pop()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let transaction = Transaction::new_signed_with_payer(
|
||||||
|
&[update_ix],
|
||||||
|
Some(&payer.pubkey()),
|
||||||
|
&[&payer, &authority],
|
||||||
|
recent_blockhash,
|
||||||
|
);
|
||||||
|
|
||||||
|
banks_client.process_transaction(transaction).await.unwrap();
|
||||||
|
}
|
Loading…
Reference in New Issue