Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 89 additions & 89 deletions bench/COMPUTE_UNITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,95 +16,95 @@ The programs and their tests are located in [/tests/bench](https://github.com/co

Solana version: 2.1.0

| Instruction | Compute Units | - |
| --------------------------- | ------------- | --- |
| accountInfo1 | 571 | - |
| accountInfo2 | 895 | - |
| accountInfo4 | 1,553 | - |
| accountInfo8 | 2,923 | - |
| accountEmptyInit1 | 5,083 | - |
| accountEmpty1 | 645 | - |
| accountEmptyInit2 | 9,301 | - |
| accountEmpty2 | 1,007 | - |
| accountEmptyInit4 | 17,764 | - |
| accountEmpty4 | 1,724 | - |
| accountEmptyInit8 | 34,723 | - |
| accountEmpty8 | 3,163 | - |
| accountSizedInit1 | 5,192 | - |
| accountSized1 | 693 | - |
| accountSizedInit2 | 9,489 | - |
| accountSized2 | 1,075 | - |
| accountSizedInit4 | 18,170 | - |
| accountSized4 | 1,848 | - |
| accountSizedInit8 | 35,433 | - |
| accountSized8 | 3,387 | - |
| accountUnsizedInit1 | 5,305 | - |
| accountUnsized1 | 746 | - |
| accountUnsizedInit2 | 9,759 | - |
| accountUnsized2 | 1,163 | - |
| accountUnsizedInit4 | 18,603 | - |
| accountUnsized4 | 2,002 | - |
| accountUnsizedInit8 | 35,993 | - |
| accountUnsized8 | 3,673 | - |
| boxedAccountEmptyInit1 | 5,175 | - |
| boxedAccountEmpty1 | 734 | - |
| boxedAccountEmptyInit2 | 9,414 | - |
| boxedAccountEmpty2 | 1,116 | - |
| boxedAccountEmptyInit4 | 17,918 | - |
| boxedAccountEmpty4 | 1,872 | - |
| boxedAccountEmptyInit8 | 34,953 | - |
| boxedAccountEmpty8 | 3,401 | - |
| boxedAccountSizedInit1 | 5,271 | - |
| boxedAccountSized1 | 783 | - |
| boxedAccountSizedInit2 | 9,583 | - |
| boxedAccountSized2 | 1,190 | - |
| boxedAccountSizedInit4 | 18,230 | - |
| boxedAccountSized4 | 1,996 | - |
| boxedAccountSizedInit8 | 35,553 | - |
| boxedAccountSized8 | 3,628 | - |
| boxedAccountUnsizedInit1 | 5,371 | - |
| boxedAccountUnsized1 | 836 | - |
| boxedAccountUnsizedInit2 | 9,759 | - |
| boxedAccountUnsized2 | 1,270 | - |
| boxedAccountUnsizedInit4 | 18,558 | - |
| boxedAccountUnsized4 | 2,132 | - |
| boxedAccountUnsizedInit8 | 36,185 | - |
| boxedAccountUnsized8 | 3,881 | - |
| boxedInterfaceAccountMint1 | 1,351 | - |
| boxedInterfaceAccountMint2 | 2,123 | - |
| boxedInterfaceAccountMint4 | 3,656 | - |
| boxedInterfaceAccountMint8 | 6,738 | - |
| boxedInterfaceAccountToken1 | 2,011 | - |
| boxedInterfaceAccountToken2 | 3,431 | - |
| boxedInterfaceAccountToken4 | 6,260 | - |
| boxedInterfaceAccountToken8 | 11,934 | - |
| interfaceAccountMint1 | 1,476 | - |
| interfaceAccountMint2 | 2,489 | - |
| interfaceAccountMint4 | 4,511 | - |
| interfaceAccountMint8 | 8,550 | - |
| interfaceAccountToken1 | 2,111 | - |
| interfaceAccountToken2 | 3,729 | - |
| interfaceAccountToken4 | 6,955 | - |
| interface1 | 769 | - |
| interface2 | 912 | - |
| interface4 | 1,189 | - |
| interface8 | 1,748 | - |
| program1 | 779 | - |
| program2 | 920 | - |
| program4 | 1,193 | - |
| program8 | 1,744 | - |
| signer1 | 774 | - |
| signer2 | 1,064 | - |
| signer4 | 1,637 | - |
| signer8 | 2,788 | - |
| systemAccount1 | 796 | - |
| systemAccount2 | 1,096 | - |
| systemAccount4 | 1,689 | - |
| systemAccount8 | 2,880 | - |
| uncheckedAccount1 | 783 | - |
| uncheckedAccount2 | 1,056 | - |
| uncheckedAccount4 | 1,594 | - |
| uncheckedAccount8 | 2,679 | - |
| Instruction | Compute Units | - |
| --------------------------- | ------------- | ------------------ |
| accountInfo1 | 571 | - |
| accountInfo2 | 895 | - |
| accountInfo4 | 1,553 | - |
| accountInfo8 | 2,923 | - |
| accountEmptyInit1 | 5,083 | - |
| accountEmpty1 | 645 | - |
| accountEmptyInit2 | 9,301 | - |
| accountEmpty2 | 1,007 | - |
| accountEmptyInit4 | 17,764 | - |
| accountEmpty4 | 1,724 | - |
| accountEmptyInit8 | 34,723 | - |
| accountEmpty8 | 3,163 | - |
| accountSizedInit1 | 5,192 | - |
| accountSized1 | 693 | - |
| accountSizedInit2 | 9,489 | - |
| accountSized2 | 1,075 | - |
| accountSizedInit4 | 18,170 | - |
| accountSized4 | 1,848 | - |
| accountSizedInit8 | 35,433 | - |
| accountSized8 | 3,387 | - |
| accountUnsizedInit1 | 5,305 | - |
| accountUnsized1 | 746 | - |
| accountUnsizedInit2 | 9,759 | - |
| accountUnsized2 | 1,163 | - |
| accountUnsizedInit4 | 18,603 | - |
| accountUnsized4 | 2,002 | - |
| accountUnsizedInit8 | 35,993 | - |
| accountUnsized8 | 3,673 | - |
| boxedAccountEmptyInit1 | 5,175 | - |
| boxedAccountEmpty1 | 734 | - |
| boxedAccountEmptyInit2 | 9,414 | - |
| boxedAccountEmpty2 | 1,116 | - |
| boxedAccountEmptyInit4 | 17,918 | - |
| boxedAccountEmpty4 | 1,872 | - |
| boxedAccountEmptyInit8 | 34,953 | - |
| boxedAccountEmpty8 | 3,401 | - |
| boxedAccountSizedInit1 | 5,271 | - |
| boxedAccountSized1 | 783 | - |
| boxedAccountSizedInit2 | 9,583 | - |
| boxedAccountSized2 | 1,190 | - |
| boxedAccountSizedInit4 | 18,230 | - |
| boxedAccountSized4 | 1,996 | - |
| boxedAccountSizedInit8 | 35,553 | - |
| boxedAccountSized8 | 3,628 | - |
| boxedAccountUnsizedInit1 | 5,371 | - |
| boxedAccountUnsized1 | 836 | - |
| boxedAccountUnsizedInit2 | 9,759 | - |
| boxedAccountUnsized2 | 1,270 | - |
| boxedAccountUnsizedInit4 | 18,558 | - |
| boxedAccountUnsized4 | 2,132 | - |
| boxedAccountUnsizedInit8 | 36,185 | - |
| boxedAccountUnsized8 | 3,881 | - |
| boxedInterfaceAccountMint1 | 1,351 | - |
| boxedInterfaceAccountMint2 | 2,123 | - |
| boxedInterfaceAccountMint4 | 3,656 | - |
| boxedInterfaceAccountMint8 | 6,738 | - |
| boxedInterfaceAccountToken1 | 2,011 | - |
| boxedInterfaceAccountToken2 | 3,431 | - |
| boxedInterfaceAccountToken4 | 6,260 | - |
| boxedInterfaceAccountToken8 | 11,934 | - |
| interfaceAccountMint1 | 1,476 | - |
| interfaceAccountMint2 | 2,489 | - |
| interfaceAccountMint4 | 4,511 | - |
| interfaceAccountMint8 | 8,550 | - |
| interfaceAccountToken1 | 2,111 | - |
| interfaceAccountToken2 | 3,729 | - |
| interfaceAccountToken4 | 6,955 | - |
| interface1 | 769 | - |
| interface2 | 912 | - |
| interface4 | 1,189 | - |
| interface8 | 1,748 | - |
| program1 | 779 | - |
| program2 | 934 | 🔴 **+14 (1.52%)** |
| program4 | 1,221 | 🔴 **+28 (2.35%)** |
| program8 | 1,800 | 🔴 **+56 (3.21%)** |
| signer1 | 774 | - |
| signer2 | 1,064 | - |
| signer4 | 1,637 | - |
| signer8 | 2,788 | - |
| systemAccount1 | 796 | - |
| systemAccount2 | 1,096 | - |
| systemAccount4 | 1,689 | - |
| systemAccount8 | 2,880 | - |
| uncheckedAccount1 | 783 | - |
| uncheckedAccount2 | 1,056 | - |
| uncheckedAccount4 | 1,594 | - |
| uncheckedAccount8 | 2,679 | - |

### Notable changes

Expand Down
37 changes: 34 additions & 3 deletions lang/src/accounts/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,24 @@ use std::ops::Deref;
///
/// # Table of Contents
/// - [Basic Functionality](#basic-functionality)
/// - [Generic Program Validation](#generic-program-validation)
/// - [Out of the Box Types](#out-of-the-box-types)
///
/// # Basic Functionality
///
/// For `Program<'info, T>` where T implements Id:
/// Checks:
///
/// - `account_info.key == expected_program`
/// - `account_info.executable == true`
///
/// # Generic Program Validation
///
/// For `Program<'info>` (without type parameter):
/// - Only checks: `account_info.executable == true`
/// - Use this when you only need to verify that an address is executable,
/// without validating against a specific program ID.
///
/// # Example
/// ```ignore
/// #[program]
Expand Down Expand Up @@ -65,6 +74,16 @@ use std::ops::Deref;
/// - `program_data`'s constraint checks that its upgrade authority is the `authority` account.
/// - Finally, `authority` needs to sign the transaction.
///
/// ## Generic Program Example
/// ```ignore
/// #[derive(Accounts)]
/// pub struct ValidateExecutableProgram<'info> {
/// // Only validates that the provided account is executable
/// pub any_program: Program<'info>,
/// pub authority: Signer<'info>,
/// }
/// ```
///
/// # Out of the Box Types
///
/// Between the [`anchor_lang`](https://docs.rs/anchor-lang/latest/anchor_lang) and [`anchor_spl`](https://docs.rs/anchor_spl/latest/anchor_spl) crates,
Expand All @@ -75,7 +94,7 @@ use std::ops::Deref;
/// - [`Token`](https://docs.rs/anchor-spl/latest/anchor_spl/token/struct.Token.html)
///
#[derive(Clone)]
pub struct Program<'info, T> {
pub struct Program<'info, T = ()> {
info: &'info AccountInfo<'info>,
_phantom: PhantomData<T>,
}
Expand Down Expand Up @@ -128,13 +147,15 @@ impl<'a, T: Id> TryFrom<&'a AccountInfo<'a>> for Program<'a, T> {
type Error = Error;
/// Deserializes the given `info` into a `Program`.
fn try_from(info: &'a AccountInfo<'a>) -> Result<Self> {
if info.key != &T::id() {
// Special handling for unit type () - only check executable, not program ID
let is_unit_type = T::id() == Pubkey::default();

if !is_unit_type && info.key != &T::id() {
return Err(Error::from(ErrorCode::InvalidProgramId).with_pubkeys((*info.key, T::id())));
}
if !info.executable {
return Err(ErrorCode::InvalidProgramExecutable.into());
}

Ok(Program::new(info))
}
}
Expand Down Expand Up @@ -195,3 +216,13 @@ impl<T: AccountDeserialize> Key for Program<'_, T> {
*self.info.key
}
}

// Implement Id trait for unit type to support Program<'info> without type parameter
impl crate::Id for () {
fn id() -> Pubkey {
// For generic programs, this should never be called since they don't validate specific program IDs.
// However, we need to implement it to satisfy the trait bounds.
// Using a special marker value that indicates "any program"
Pubkey::default()
}
}
3 changes: 1 addition & 2 deletions lang/syn/src/codegen/accounts/constraints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ fn generate_constraint_realloc(
let account_name = field.to_string();
let new_space = &c.space;
let payer = &c.payer;
let zero = &c.zero;
Comment thread
swaroop-osec marked this conversation as resolved.
Outdated

let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, field);
let payer_optional_check = optional_check_scope.generate_check(payer);
Expand Down Expand Up @@ -465,7 +464,7 @@ fn generate_constraint_realloc(
**__field_info.lamports.borrow_mut() = __field_info.lamports().checked_sub(__lamport_amt).unwrap();
}

__field_info.realloc(#new_space, #zero)?;
__field_info.resize(#new_space)?;
__reallocs.insert(#field.key());
}
}
Expand Down
2 changes: 1 addition & 1 deletion lang/syn/src/codegen/program/idl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ pub fn idl_accounts_and_functions() -> proc_macro2::TokenStream {
.checked_sub(idl_ref.lamports())
.unwrap(),
)?;
idl_ref.realloc(new_account_space, false)?;
idl_ref.resize(new_account_space)?;
}

Ok(())
Expand Down
15 changes: 11 additions & 4 deletions lang/syn/src/idl/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,17 @@ fn get_address(acc: &Field) -> TokenStream {
match &acc.ty {
Ty::Program(_) | Ty::Sysvar(_) => {
let ty = acc.account_ty();
let id_trait = matches!(acc.ty, Ty::Program(_))
.then(|| quote!(anchor_lang::Id))
.unwrap_or_else(|| quote!(anchor_lang::solana_program::sysvar::SysvarId));
quote! { Some(<#ty as #id_trait>::id().to_string()) }
// Check if this is the unit type marker (for generic Program<'info>)
let ty_str = quote!(#ty).to_string();
if ty_str == "" || ty_str == "__SolanaProgramUnitType" {
// For generic programs, we don't have a specific address
quote! { None }
} else {
let id_trait = matches!(acc.ty, Ty::Program(_))
.then(|| quote!(anchor_lang::Id))
.unwrap_or_else(|| quote!(anchor_lang::solana_program::sysvar::SysvarId));
quote! { Some(<#ty as #id_trait>::id().to_string()) }
}
}
_ => acc
.constraints
Expand Down
24 changes: 22 additions & 2 deletions lang/syn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,20 @@ impl Field {
Sysvar<#account>
}
}
Ty::Program(ty) => {
let program = &ty.account_type_path;
// Check if this is the generic Program<'info> (unit type)
let program_str = quote!(#program).to_string();
if program_str == "__SolanaProgramUnitType" {
quote! {
#container_ty<'info>
}
} else {
quote! {
#container_ty<'info, #program>
}
}
}
_ => quote! {
#container_ty<#account_ty>
},
Expand Down Expand Up @@ -542,8 +556,14 @@ impl Field {
},
Ty::Program(ty) => {
let program = &ty.account_type_path;
quote! {
#program
// Check if this is the special marker for generic Program<'info> (unit type)
let program_str = quote!(#program).to_string();
if program_str == "__SolanaProgramUnitType" {
quote! {}
} else {
quote! {
#program
}
}
}
Ty::Interface(ty) => {
Expand Down
Loading