123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- //! This example demonstrates the use of zero copy deserialization for accounts.
- //! The main noticeable benefit one achieves using zero copy is the ability
- //! to create accounts larger than the size of the stack or heap, as is
- //! demonstrated by the event queue in this example.
- use anchor_lang::prelude::*;
- #[program]
- pub mod zero_copy {
- use super::*;
- #[state(zero_copy)]
- pub struct Globals {
- pub authority: Pubkey,
- // The solana runtime currently restricts how much one can resize an
- // account on CPI to ~10240 bytes. State accounts are program derived
- // addresses, which means its max size is limited by this restriction
- // (i.e., this is not an Anchor specific issue).
- //
- // As a result, we only use 250 events here.
- //
- // For larger zero-copy data structures, one must use non-state anchor
- // accounts, as is demonstrated below.
- pub events: [Event; 250],
- }
- impl Globals {
- // Note that the `new` constructor is different from non-zero-copy
- // state accounts. Namely, it takes in a `&mut self` parameter.
- pub fn new(&mut self, ctx: Context<New>) -> ProgramResult {
- self.authority = *ctx.accounts.authority.key;
- Ok(())
- }
- #[access_control(auth(&self, &ctx))]
- pub fn set_event(
- &mut self,
- ctx: Context<SetEvent>,
- idx: u32,
- event: RpcEvent,
- ) -> ProgramResult {
- self.events[idx as usize] = event.into();
- Ok(())
- }
- }
- pub fn create_foo(ctx: Context<CreateFoo>) -> ProgramResult {
- let foo = &mut ctx.accounts.foo.load_init()?;
- foo.authority = *ctx.accounts.authority.key;
- foo.set_second_authority(ctx.accounts.authority.key);
- Ok(())
- }
- pub fn update_foo(ctx: Context<UpdateFoo>, data: u64) -> ProgramResult {
- let mut foo = ctx.accounts.foo.load_mut()?;
- foo.data = data;
- Ok(())
- }
- pub fn update_foo_second(ctx: Context<UpdateFooSecond>, second_data: u64) -> ProgramResult {
- let mut foo = ctx.accounts.foo.load_mut()?;
- foo.second_data = second_data;
- Ok(())
- }
- pub fn create_bar(ctx: Context<CreateBar>) -> ProgramResult {
- let bar = &mut ctx.accounts.bar.load_init()?;
- bar.authority = *ctx.accounts.authority.key;
- Ok(())
- }
- pub fn update_bar(ctx: Context<UpdateBar>, data: u64) -> ProgramResult {
- let bar = &mut ctx.accounts.bar.load_mut()?;
- bar.data = data;
- Ok(())
- }
- pub fn create_large_account(_ctx: Context<CreateLargeAccount>) -> ProgramResult {
- Ok(())
- }
- pub fn update_large_account(
- ctx: Context<UpdateLargeAccount>,
- idx: u32,
- data: u64,
- ) -> ProgramResult {
- let event_q = &mut ctx.accounts.event_q.load_mut()?;
- event_q.events[idx as usize] = Event {
- data,
- from: *ctx.accounts.from.key,
- };
- Ok(())
- }
- }
- #[derive(Accounts)]
- pub struct New<'info> {
- #[account(signer)]
- authority: AccountInfo<'info>,
- }
- #[derive(Accounts)]
- pub struct SetEvent<'info> {
- #[account(signer)]
- authority: AccountInfo<'info>,
- }
- #[derive(Accounts)]
- pub struct CreateFoo<'info> {
- #[account(init)]
- foo: Loader<'info, Foo>,
- #[account(signer)]
- authority: AccountInfo<'info>,
- rent: Sysvar<'info, Rent>,
- }
- #[derive(Accounts)]
- pub struct UpdateFoo<'info> {
- #[account(mut, has_one = authority)]
- foo: Loader<'info, Foo>,
- #[account(signer)]
- authority: AccountInfo<'info>,
- }
- #[derive(Accounts)]
- pub struct UpdateFooSecond<'info> {
- #[account(mut, "&foo.load()?.get_second_authority() == second_authority.key")]
- foo: Loader<'info, Foo>,
- #[account(signer)]
- second_authority: AccountInfo<'info>,
- }
- #[derive(Accounts)]
- pub struct CreateBar<'info> {
- #[account(init, associated = authority, with = foo)]
- bar: Loader<'info, Bar>,
- #[account(signer)]
- authority: AccountInfo<'info>,
- foo: Loader<'info, Foo>,
- rent: Sysvar<'info, Rent>,
- system_program: AccountInfo<'info>,
- }
- #[derive(Accounts)]
- pub struct UpdateBar<'info> {
- #[account(mut, associated = authority, with = foo, has_one = authority)]
- bar: Loader<'info, Bar>,
- #[account(signer)]
- authority: AccountInfo<'info>,
- foo: Loader<'info, Foo>,
- }
- #[derive(Accounts)]
- pub struct CreateLargeAccount<'info> {
- #[account(init)]
- event_q: Loader<'info, EventQ>,
- rent: Sysvar<'info, Rent>,
- }
- #[derive(Accounts)]
- pub struct UpdateLargeAccount<'info> {
- #[account(mut)]
- event_q: Loader<'info, EventQ>,
- #[account(signer)]
- from: AccountInfo<'info>,
- }
- #[account(zero_copy)]
- pub struct Foo {
- pub authority: Pubkey,
- pub data: u64,
- pub second_data: u64,
- #[accessor(Pubkey)] // The `accessor` api will likely be removed.
- pub second_authority: [u8; 32],
- }
- #[associated(zero_copy)]
- #[derive(Default)]
- pub struct Bar {
- pub authority: Pubkey,
- pub data: u64,
- }
- #[account(zero_copy)]
- pub struct EventQ {
- pub events: [Event; 25000],
- }
- #[zero_copy]
- pub struct Event {
- pub from: Pubkey,
- pub data: u64,
- }
- // A separate type is used for the RPC interface for two main reasons.
- //
- // 1. AnchorSerialize and AnchorDeserialize must be derived. Anchor requires
- // *all* instructions to implement the AnchorSerialize and AnchorDeserialize
- // traits, so any types in method signatures must as well.
- // 2. All types for zero copy deserialization are `#[repr(packed)]`. However,
- // the implementation of AnchorSerialize (i.e. borsh), uses references
- // to the fields it serializes. So if we were to just throw tehse derives
- // onto the other `Event` struct, we would have references to
- // `#[repr(packed)]` fields, which is unsafe. To avoid the unsafeness, we
- // just use a separate type.
- #[derive(AnchorSerialize, AnchorDeserialize)]
- pub struct RpcEvent {
- pub from: Pubkey,
- pub data: u64,
- }
- impl From<RpcEvent> for Event {
- fn from(e: RpcEvent) -> Event {
- Event {
- from: e.from,
- data: e.data,
- }
- }
- }
- fn auth(globals: &Globals, ctx: &Context<SetEvent>) -> ProgramResult {
- if &globals.authority != ctx.accounts.authority.key {
- return Err(ProgramError::Custom(1)); // Arbitrary error.
- }
- Ok(())
- }
|