Skip to content

Commit de353cb

Browse files
Account discriminators (#14)
1 parent 4792d1c commit de353cb

File tree

28 files changed

+388
-101
lines changed

28 files changed

+388
-101
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ _examples: &examples
2121
- export PATH="/home/travis/.local/share/solana/install/active_release/bin:$PATH"
2222
- export NODE_PATH="/home/travis/.nvm/versions/node/v$NODE_VERSION/lib/node_modules/:$NODE_PATH"
2323
- yes | solana-keygen new
24-
- cargo install --git https://github.com/project-serum/anchor anchor-cli
24+
- cargo install --git https://github.com/project-serum/anchor anchor-cli --locked
2525

2626
jobs:
2727
include:

Cargo.lock

Lines changed: 18 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ default = []
1313
thiserror = "1.0.20"
1414
solana-program = "1.4.3"
1515
solana-sdk = { version = "1.3.14", default-features = false, features = ["program"] }
16-
anchor-derive = { path = "./derive" }
17-
anchor-attributes-program = { path = "./attributes/program" }
18-
anchor-attributes-access-control = { path = "./attributes/access-control" }
16+
anchor-derive-accounts = { path = "./derive/accounts" }
17+
anchor-attribute-program = { path = "./attribute/program" }
18+
anchor-attribute-access-control = { path = "./attribute/access-control" }
19+
anchor-attribute-account = { path = "./attribute/account" }
1920
borsh = { git = "https://github.com/project-serum/borsh", branch = "serum", features = ["serum-program"] }
2021

2122
[workspace]
2223
members = [
2324
"cli",
2425
"syn",
25-
"attributes/*",
26-
"derive",
26+
"attribute/*",
27+
"derive/*",
2728
]

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ purposes of the Accounts macro) that can be specified on a struct deriving `Acco
9191
|:--|:--|:--|
9292
| `#[account(signer)]` | On raw `AccountInfo` structs. | Checks the given account signed the transaction. |
9393
| `#[account(mut)]` | On `ProgramAccount` structs. | Marks the account as mutable and persists the state transition. |
94+
| `#[account(init)]` | On `ProgramAccount` structs. | Marks the account as being initialized, skipping the account discriminator check. |
9495
| `#[account(belongs_to = <target>)]` | On `ProgramAccount` structs | Checks the `target` field on the account matches the `target` field in the accounts array. |
9596
| `#[account(owner = program \| skip)]` | On `ProgramAccount` and `AccountInfo` structs | Checks the owner of the account is the current program or skips the check. |
9697
| `#[account("<literal>")]` | On `ProgramAccount` structs | Executes the given code literal as a constraint. The literal should evaluate to a boolean. |

attributes/program/Cargo.toml renamed to attribute/access-control/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "anchor-attributes-program"
2+
name = "anchor-attribute-access-control"
33
version = "0.1.0"
44
authors = ["armaniferrante <[email protected]>"]
55
edition = "2018"

attributes/access-control/src/lib.rs renamed to attribute/access-control/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ extern crate proc_macro;
33
use quote::quote;
44
use syn::parse_macro_input;
55

6+
/// Executes the given access control method before running the decorated
7+
/// instruction handler. Any method in scope of the attribute can be invoked
8+
/// with any arguments from the associated instruction handler.
69
#[proc_macro_attribute]
710
pub fn access_control(
811
args: proc_macro::TokenStream,

attributes/access-control/Cargo.toml renamed to attribute/account/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "anchor-attributes-access-control"
2+
name = "anchor-attribute-account"
33
version = "0.1.0"
44
authors = ["armaniferrante <[email protected]>"]
55
edition = "2018"
@@ -12,4 +12,4 @@ proc-macro2 = "1.0"
1212
quote = "1.0"
1313
syn = { version = "1.0.54", features = ["full"] }
1414
anyhow = "1.0.32"
15-
anchor-syn = { path = "../../syn" }
15+
anchor-syn = { path = "../../syn" }

attribute/account/src/lib.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
extern crate proc_macro;
2+
3+
use quote::quote;
4+
use syn::parse_macro_input;
5+
6+
/// A data structure representing a Solana account.
7+
#[proc_macro_attribute]
8+
pub fn account(
9+
_args: proc_macro::TokenStream,
10+
input: proc_macro::TokenStream,
11+
) -> proc_macro::TokenStream {
12+
let account_strct = parse_macro_input!(input as syn::ItemStruct);
13+
let account_name = &account_strct.ident;
14+
// Namespace the discriminator to prevent future collisions, e.g.,
15+
// if we (for some unforseen reason) wanted to hash other parts of the
16+
// program.
17+
let discriminator_preimage = format!("account:{}", account_name.to_string());
18+
19+
let coder = quote! {
20+
impl anchor::AccountSerialize for #account_name {
21+
fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<(), ProgramError> {
22+
// TODO: we shouldn't have to hash at runtime. However, rust
23+
// is not happy when trying to include solana-sdk from
24+
// the proc-macro crate.
25+
let mut discriminator = [0u8; 8];
26+
discriminator.copy_from_slice(
27+
&solana_program::hash::hash(
28+
#discriminator_preimage.as_bytes(),
29+
).to_bytes()[..8],
30+
);
31+
32+
writer.write_all(&discriminator).map_err(|_| ProgramError::InvalidAccountData)?;
33+
AnchorSerialize::serialize(
34+
self,
35+
writer
36+
)
37+
.map_err(|_| ProgramError::InvalidAccountData)?;
38+
Ok(())
39+
}
40+
}
41+
42+
impl anchor::AccountDeserialize for #account_name {
43+
fn try_deserialize(buf: &mut &[u8]) -> Result<Self, ProgramError> {
44+
let mut discriminator = [0u8; 8];
45+
discriminator.copy_from_slice(
46+
&solana_program::hash::hash(
47+
#discriminator_preimage.as_bytes(),
48+
).to_bytes()[..8],
49+
);
50+
51+
if buf.len() < discriminator.len() {
52+
return Err(ProgramError::AccountDataTooSmall);
53+
}
54+
let given_disc = &buf[..8];
55+
if &discriminator != given_disc {
56+
return Err(ProgramError::InvalidInstructionData);
57+
}
58+
Self::try_deserialize_unchecked(buf)
59+
}
60+
61+
fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self, ProgramError> {
62+
let mut data: &[u8] = &buf[8..];
63+
AnchorDeserialize::deserialize(&mut data)
64+
.map_err(|_| ProgramError::InvalidAccountData)
65+
}
66+
}
67+
};
68+
69+
proc_macro::TokenStream::from(quote! {
70+
#[derive(AnchorSerialize, AnchorDeserialize)]
71+
#account_strct
72+
73+
#coder
74+
})
75+
}

attribute/program/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "anchor-attribute-program"
3+
version = "0.1.0"
4+
authors = ["armaniferrante <[email protected]>"]
5+
edition = "2018"
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[dependencies]
11+
proc-macro2 = "1.0"
12+
quote = "1.0"
13+
syn = { version = "1.0.54", features = ["full"] }
14+
anyhow = "1.0.32"
15+
anchor-syn = { path = "../../syn" }

attributes/program/src/lib.rs renamed to attribute/program/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use anchor_syn::codegen::program as program_codegen;
44
use anchor_syn::parser::program as program_parser;
55
use syn::parse_macro_input;
66

7+
/// The module containing all instruction handlers defining all entries to the
8+
/// Solana program.
79
#[proc_macro_attribute]
810
pub fn program(
911
_args: proc_macro::TokenStream,

0 commit comments

Comments
 (0)