lang: Reduce compute unit consumption of loader accounts (#1134)

This commit is contained in:
Justin Starry 2021-12-15 10:53:48 -05:00 committed by GitHub
parent a7e80079da
commit a3c8d20352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 94 additions and 22 deletions

View File

@ -258,7 +258,7 @@ jobs:
path: tests/cashiers-check
- cmd: cd tests/typescript && anchor test
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
- cmd: cd tests/chat && anchor test
path: tests/chat

1
Cargo.lock generated
View File

@ -212,6 +212,7 @@ dependencies = [
"anchor-attribute-program",
"anchor-attribute-state",
"anchor-derive-accounts",
"arrayref",
"base64 0.13.0",
"bincode",
"borsh",

View File

@ -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-event = { path = "./attribute/event", version = "0.19.0" }
anchor-derive-accounts = { path = "./derive/accounts", version = "0.19.0" }
arrayref = "0.3.6"
base64 = "0.13.0"
borsh = "0.9"
bytemuck = "1.4.0"

View File

@ -3,6 +3,7 @@ use crate::{
Accounts, AccountsClose, AccountsExit, Key, ToAccountInfo, ToAccountInfos, ToAccountMetas,
ZeroCopy,
};
use arrayref::array_ref;
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
use solana_program::instruction::AccountMeta;
@ -62,9 +63,8 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
}
let data: &[u8] = &acc_info.try_borrow_data()?;
// Discriminator must match.
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != T::discriminator() {
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}
@ -89,9 +89,8 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
pub fn load(&self) -> Result<Ref<T>, ProgramError> {
let data = self.acc_info.try_borrow_data()?;
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != T::discriminator() {
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
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 mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != T::discriminator() {
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}

View File

@ -3,6 +3,7 @@ use crate::{
Accounts, AccountsClose, AccountsExit, Key, Owner, ToAccountInfo, ToAccountInfos,
ToAccountMetas, ZeroCopy,
};
use arrayref::array_ref;
use solana_program::account_info::AccountInfo;
use solana_program::entrypoint::ProgramResult;
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()?;
// Discriminator must match.
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != T::discriminator() {
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
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> {
let data = self.acc_info.try_borrow_data()?;
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != T::discriminator() {
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
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 mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
if disc_bytes != T::discriminator() {
let disc_bytes = array_ref![data, 0, 8];
if disc_bytes != &T::discriminator() {
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
}

View File

@ -22,7 +22,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
quote! {
// TODO: remove once we allow segmented paths in `Accounts` structs.
use #mod_name::*;
use self::#mod_name::*;
#entry
#dispatch

View File

@ -13,6 +13,12 @@ no-entrypoint = []
no-idl = []
cpi = ["no-entrypoint"]
default = []
test-bpf = []
[dependencies]
anchor-lang = { path = "../../../../lang" }
[dev-dependencies]
anchor-client = { path = "../../../../client", features = ["debug"] }
bytemuck = "1.4.0"
solana-program-test = "1.8.0"

View File

@ -11,8 +11,6 @@ declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
#[program]
pub mod zero_copy {
use std::str::FromStr;
use super::*;
pub fn create_foo(ctx: Context<CreateFoo>) -> ProgramResult {
@ -140,6 +138,7 @@ pub struct UpdateLargeAccount<'info> {
}
#[account(zero_copy)]
#[derive(Default)]
pub struct Foo {
pub authority: Pubkey,
pub data: u64,

View File

@ -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();
}