소스 검색

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

Justin Starry 3 년 전
부모
커밋
a3c8d20352

+ 1 - 1
.github/workflows/tests.yaml

@@ -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 - 0
Cargo.lock

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

+ 1 - 0
lang/Cargo.toml

@@ -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"

+ 7 - 9
lang/src/loader.rs

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

+ 7 - 9
lang/src/loader_account.rs

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

+ 1 - 1
lang/syn/src/codegen/program/mod.rs

@@ -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

+ 6 - 0
tests/zero-copy/programs/zero-copy/Cargo.toml

@@ -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"

+ 1 - 2
tests/zero-copy/programs/zero-copy/src/lib.rs

@@ -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,

+ 69 - 0
tests/zero-copy/programs/zero-copy/tests/compute_unit_test.rs

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