Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lang/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ anchor-debug = [
"anchor-attribute-access-control/anchor-debug",
"anchor-attribute-account/anchor-debug",
"anchor-attribute-constant/anchor-debug",
"anchor-attribute-discriminator/anchor-debug",
"anchor-attribute-error/anchor-debug",
"anchor-attribute-event/anchor-debug",
"anchor-attribute-interface/anchor-debug",
Expand All @@ -29,6 +30,7 @@ anchor-debug = [
anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.25.0" }
anchor-attribute-account = { path = "./attribute/account", version = "0.25.0" }
anchor-attribute-constant = { path = "./attribute/constant", version = "0.25.0" }
anchor-attribute-discriminator = { path = "./attribute/discriminator", version = "0.25.0" }
anchor-attribute-error = { path = "./attribute/error", version = "0.25.0" }
anchor-attribute-program = { path = "./attribute/program", version = "0.25.0" }
anchor-attribute-state = { path = "./attribute/state", version = "0.25.0" }
Expand Down
20 changes: 20 additions & 0 deletions lang/attribute/discriminator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "anchor-attribute-discriminator"
version = "0.25.0"
authors = ["Serum Foundation <[email protected]>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor attribute macro for creating discriminator types"
rust-version = "1.56"
edition = "2021"

[lib]
proc-macro = true

[features]
anchor-debug = ["anchor-syn/anchor-debug"]

[dependencies]
proc-macro2 = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anchor-syn = { path = "../../syn", version = "0.25.0" }
10 changes: 10 additions & 0 deletions lang/attribute/discriminator/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
extern crate proc_macro;

/// A marker attribute used to override the discriminator value that should be used.
#[proc_macro_attribute]
pub fn discriminator(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on the use case for this macro? When would an anchor program use it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the plan was that #[discriminator(x)] would you allow you to override the discriminator, so that we can test an idl with different discriminator than the standard discriminator created with hashing.

As for a use case,I did imagine a usecase where you can specify a single byte as a discriminator, but this syntax does not allow this yet. This would allow you to just use a single byte rather than 8 bytes for discriminator.

What do you think of:

#[discriminator([42])]
fn foo(..)

or for two byte discriminators:

#[discriminator([42,12])]
fn foo(..)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you don't think the attribute is useful, I can create a test using a non-anchor rust contract and a hand-written idl, which is then used from rust and ts.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be interesting. How would you handle potential collisions and/or ambiguity with discriminators of different lengths?

_attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
input
}
8 changes: 5 additions & 3 deletions lang/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub use crate::bpf_upgradeable_state::*;
pub use anchor_attribute_access_control::access_control;
pub use anchor_attribute_account::{account, declare_id, zero_copy};
pub use anchor_attribute_constant::constant;
pub use anchor_attribute_discriminator::discriminator;
pub use anchor_attribute_error::*;
pub use anchor_attribute_event::{emit, event};
pub use anchor_attribute_interface::interface;
Expand Down Expand Up @@ -238,9 +239,10 @@ pub mod prelude {
accounts::account_loader::AccountLoader, accounts::program::Program,
accounts::signer::Signer, accounts::system_account::SystemAccount,
accounts::sysvar::Sysvar, accounts::unchecked_account::UncheckedAccount, constant,
context::Context, context::CpiContext, declare_id, emit, err, error, event, interface,
program, require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state,
context::Context, context::CpiContext, declare_id, discriminator, emit, err, error, event,
interface, program, require, require_eq, require_gt, require_gte, require_keys_eq,
require_keys_neq, require_neq,
solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state,
system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts,
AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result,
ToAccountInfo, ToAccountInfos, ToAccountMetas,
Expand Down
3 changes: 3 additions & 0 deletions lang/syn/src/idl/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn parse(
);
IdlInstruction {
name,
discriminator: None,
docs: None,
accounts,
args,
Expand Down Expand Up @@ -131,6 +132,7 @@ pub fn parse(
idl_accounts(&ctx, accounts_strct, &accs, seeds_feature, no_docs);
IdlInstruction {
name,
discriminator: None,
docs: None,
accounts,
args,
Expand Down Expand Up @@ -216,6 +218,7 @@ pub fn parse(
};
IdlInstruction {
name: ix.ident.to_string().to_mixed_case(),
discriminator: ix.discriminator.clone(),
docs: ix.docs.clone(),
accounts,
args,
Expand Down
2 changes: 2 additions & 0 deletions lang/syn/src/idl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct IdlState {
pub struct IdlInstruction {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub discriminator: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub docs: Option<Vec<String>>,
pub accounts: Vec<IdlAccountItem>,
pub args: Vec<IdlField>,
Expand Down
1 change: 1 addition & 0 deletions lang/syn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub struct StateInterface {
pub struct Ix {
pub raw_method: ItemFn,
pub ident: Ident,
pub discriminator: Option<Vec<u8>>,
pub docs: Option<Vec<String>>,
pub args: Vec<IxArg>,
pub returns: IxReturn,
Expand Down
32 changes: 32 additions & 0 deletions lang/syn/src/parser/program/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::parser::program::ctx_accounts_ident;
use crate::{FallbackFn, Ix, IxArg, IxReturn};
use syn::parse::{Error as ParseError, Result as ParseResult};
use syn::spanned::Spanned;
use syn::Lit;

// Parse all non-state ix handlers from the program mod definition.
pub fn parse(program_mod: &syn::ItemMod) -> ParseResult<(Vec<Ix>, Option<FallbackFn>)> {
Expand All @@ -25,11 +26,13 @@ pub fn parse(program_mod: &syn::ItemMod) -> ParseResult<(Vec<Ix>, Option<Fallbac
.map(|method: &syn::ItemFn| {
let (ctx, args) = parse_args(method)?;
let docs = docs::parse(&method.attrs);
let discriminator = parse_discriminator(&method.attrs)?;
let returns = parse_return(method)?;
let anchor_ident = ctx_accounts_ident(&ctx.raw_arg)?;
Ok(Ix {
raw_method: method.clone(),
ident: method.sig.ident.clone(),
discriminator,
docs,
args,
anchor_ident,
Expand Down Expand Up @@ -129,3 +132,32 @@ pub fn parse_return(method: &syn::ItemFn) -> ParseResult<IxReturn> {
)),
}
}

pub fn parse_discriminator(attrs: &[syn::Attribute]) -> ParseResult<Option<Vec<u8>>> {
let mut discriminators = Vec::new();

for attr in attrs {
if attr.path.segments.last().unwrap().ident == "discriminator" {
if let Ok(Lit::Int(dis)) = attr.parse_args::<Lit>() {
if let Ok(value) = dis.base10_parse::<u64>() {
discriminators.push((attr.span(), value));
continue;
}
}

return Err(ParseError::new(
attr.span(),
"Discriminator value like `#[discriminator(0xdeadbeeffeedcafe)]` expected",
));
}
}

match discriminators.len() {
0 => Ok(None),
1 => Ok(Some(discriminators[0].1.to_be_bytes().to_vec())),
_ => Err(ParseError::new(
discriminators[1].0,
"More than one discriminator function found",
)),
}
}