constraints.rs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. use crate::*;
  2. use proc_macro2_diagnostics::SpanDiagnosticExt;
  3. use quote::quote;
  4. use syn::Expr;
  5. pub fn generate(f: &Field) -> proc_macro2::TokenStream {
  6. let constraints = linearize(&f.constraints);
  7. let rent = constraints
  8. .iter()
  9. .any(|c| matches!(c, Constraint::RentExempt(ConstraintRentExempt::Enforce)))
  10. .then(|| quote! { let __anchor_rent = Rent::get()?; })
  11. .unwrap_or_else(|| quote! {});
  12. let checks: Vec<proc_macro2::TokenStream> = constraints
  13. .iter()
  14. .map(|c| generate_constraint(f, c))
  15. .collect();
  16. quote! {
  17. #rent
  18. #(#checks)*
  19. }
  20. }
  21. pub fn generate_composite(f: &CompositeField) -> proc_macro2::TokenStream {
  22. let checks: Vec<proc_macro2::TokenStream> = linearize(&f.constraints)
  23. .iter()
  24. .filter_map(|c| match c {
  25. Constraint::Raw(_) => Some(c),
  26. Constraint::Literal(_) => Some(c),
  27. _ => panic!("Invariant violation: composite constraints can only be raw or literals"),
  28. })
  29. .map(|c| generate_constraint_composite(f, c))
  30. .collect();
  31. quote! {
  32. #(#checks)*
  33. }
  34. }
  35. // Linearizes the constraint group so that constraints with dependencies
  36. // run after those without.
  37. pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
  38. let ConstraintGroup {
  39. init,
  40. zeroed,
  41. mutable,
  42. signer,
  43. has_one,
  44. literal,
  45. raw,
  46. owner,
  47. rent_exempt,
  48. seeds,
  49. executable,
  50. state,
  51. close,
  52. address,
  53. associated_token,
  54. } = c_group.clone();
  55. let mut constraints = Vec::new();
  56. if let Some(c) = zeroed {
  57. constraints.push(Constraint::Zeroed(c));
  58. }
  59. if let Some(c) = init {
  60. constraints.push(Constraint::Init(c));
  61. }
  62. if let Some(c) = seeds {
  63. constraints.push(Constraint::Seeds(c));
  64. }
  65. if let Some(c) = associated_token {
  66. constraints.push(Constraint::AssociatedToken(c));
  67. }
  68. if let Some(c) = mutable {
  69. constraints.push(Constraint::Mut(c));
  70. }
  71. if let Some(c) = signer {
  72. constraints.push(Constraint::Signer(c));
  73. }
  74. constraints.append(&mut has_one.into_iter().map(Constraint::HasOne).collect());
  75. constraints.append(&mut literal.into_iter().map(Constraint::Literal).collect());
  76. constraints.append(&mut raw.into_iter().map(Constraint::Raw).collect());
  77. if let Some(c) = owner {
  78. constraints.push(Constraint::Owner(c));
  79. }
  80. if let Some(c) = rent_exempt {
  81. constraints.push(Constraint::RentExempt(c));
  82. }
  83. if let Some(c) = executable {
  84. constraints.push(Constraint::Executable(c));
  85. }
  86. if let Some(c) = state {
  87. constraints.push(Constraint::State(c));
  88. }
  89. if let Some(c) = close {
  90. constraints.push(Constraint::Close(c));
  91. }
  92. if let Some(c) = address {
  93. constraints.push(Constraint::Address(c));
  94. }
  95. constraints
  96. }
  97. fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
  98. match c {
  99. Constraint::Init(c) => generate_constraint_init(f, c),
  100. Constraint::Zeroed(c) => generate_constraint_zeroed(f, c),
  101. Constraint::Mut(c) => generate_constraint_mut(f, c),
  102. Constraint::HasOne(c) => generate_constraint_has_one(f, c),
  103. Constraint::Signer(c) => generate_constraint_signer(f, c),
  104. Constraint::Literal(c) => generate_constraint_literal(c),
  105. Constraint::Raw(c) => generate_constraint_raw(c),
  106. Constraint::Owner(c) => generate_constraint_owner(f, c),
  107. Constraint::RentExempt(c) => generate_constraint_rent_exempt(f, c),
  108. Constraint::Seeds(c) => generate_constraint_seeds(f, c),
  109. Constraint::Executable(c) => generate_constraint_executable(f, c),
  110. Constraint::State(c) => generate_constraint_state(f, c),
  111. Constraint::Close(c) => generate_constraint_close(f, c),
  112. Constraint::Address(c) => generate_constraint_address(f, c),
  113. Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c),
  114. }
  115. }
  116. fn generate_constraint_composite(_f: &CompositeField, c: &Constraint) -> proc_macro2::TokenStream {
  117. match c {
  118. Constraint::Raw(c) => generate_constraint_raw(c),
  119. Constraint::Literal(c) => generate_constraint_literal(c),
  120. _ => panic!("Invariant violation"),
  121. }
  122. }
  123. fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream {
  124. let field = &f.ident;
  125. let addr = &c.address;
  126. let error = generate_custom_error(&c.error, quote! { ConstraintAddress });
  127. quote! {
  128. if #field.key() != #addr {
  129. return Err(#error);
  130. }
  131. }
  132. }
  133. pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
  134. generate_constraint_init_group(f, c)
  135. }
  136. pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream {
  137. let field = &f.ident;
  138. let ty_decl = f.ty_decl();
  139. let account_ty = f.account_ty();
  140. let from_account_info = f.from_account_info(None);
  141. let header_write = {
  142. if cfg!(feature = "deprecated-layout") {
  143. quote! {
  144. use std::io::{Write, Cursor};
  145. use anchor_lang::Discriminator;
  146. let __dst: &mut [u8] = &mut __data;
  147. let mut __cursor = Cursor::new(__dst);
  148. Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
  149. }
  150. } else {
  151. quote! {
  152. use std::io::{Write, Cursor};
  153. use anchor_lang::Discriminator;
  154. let __dst: &mut [u8] = &mut __data[2..];
  155. let mut __cursor = Cursor::new(__dst);
  156. Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
  157. }
  158. }
  159. };
  160. // Check the *entire* account header is zero.
  161. quote! {
  162. let #field: #ty_decl = {
  163. {
  164. let mut __data: &mut [u8] = &mut #field.try_borrow_mut_data()?;
  165. let mut __header_bytes = [0u8; 8];
  166. __header_bytes.copy_from_slice(&__data[..8]);
  167. let __header = u64::from_le_bytes(__header_bytes);
  168. if __header != 0 {
  169. return Err(anchor_lang::__private::ErrorCode::ConstraintZero.into());
  170. }
  171. #header_write
  172. }
  173. #from_account_info
  174. };
  175. }
  176. }
  177. pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
  178. let field = &f.ident;
  179. let target = &c.sol_dest;
  180. quote! {
  181. if #field.key() == #target.key() {
  182. return Err(anchor_lang::__private::ErrorCode::ConstraintClose.into());
  183. }
  184. }
  185. }
  186. pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream {
  187. let ident = &f.ident;
  188. let error = generate_custom_error(&c.error, quote! { ConstraintMut });
  189. quote! {
  190. if !#ident.to_account_info().is_writable {
  191. return Err(#error);
  192. }
  193. }
  194. }
  195. pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream {
  196. let target = c.join_target.clone();
  197. let ident = &f.ident;
  198. let field = match &f.ty {
  199. Ty::Loader(_) => quote! {#ident.load()?},
  200. Ty::AccountLoader(_) => quote! {#ident.load()?},
  201. _ => quote! {#ident},
  202. };
  203. let error = generate_custom_error(&c.error, quote! { ConstraintHasOne });
  204. quote! {
  205. if #field.#target != #target.key() {
  206. return Err(#error);
  207. }
  208. }
  209. }
  210. pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro2::TokenStream {
  211. let ident = &f.ident;
  212. let info = match f.ty {
  213. Ty::AccountInfo => quote! { #ident },
  214. Ty::ProgramAccount(_) => quote! { #ident.to_account_info() },
  215. Ty::Account(_) => quote! { #ident.to_account_info() },
  216. Ty::Loader(_) => quote! { #ident.to_account_info() },
  217. Ty::AccountLoader(_) => quote! { #ident.to_account_info() },
  218. Ty::CpiAccount(_) => quote! { #ident.to_account_info() },
  219. _ => panic!("Invalid syntax: signer cannot be specified."),
  220. };
  221. let error = generate_custom_error(&c.error, quote! { ConstraintSigner });
  222. quote! {
  223. if !#info.is_signer {
  224. return Err(#error);
  225. }
  226. }
  227. }
  228. pub fn generate_constraint_literal(c: &ConstraintLiteral) -> proc_macro2::TokenStream {
  229. let lit: proc_macro2::TokenStream = {
  230. let lit = &c.lit;
  231. let constraint = lit.value().replace('\"', "");
  232. let message = format!(
  233. "Deprecated. Should be used with constraint: #[account(constraint = {})]",
  234. constraint,
  235. );
  236. lit.span().warning(message).emit_as_item_tokens();
  237. constraint.parse().unwrap()
  238. };
  239. quote! {
  240. if !(#lit) {
  241. return Err(anchor_lang::__private::ErrorCode::Deprecated.into());
  242. }
  243. }
  244. }
  245. pub fn generate_constraint_raw(c: &ConstraintRaw) -> proc_macro2::TokenStream {
  246. let raw = &c.raw;
  247. let error = generate_custom_error(&c.error, quote! { ConstraintRaw });
  248. quote! {
  249. if !(#raw) {
  250. return Err(#error);
  251. }
  252. }
  253. }
  254. pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream {
  255. let ident = &f.ident;
  256. let owner_address = &c.owner_address;
  257. let error = generate_custom_error(&c.error, quote! { ConstraintOwner });
  258. quote! {
  259. if #ident.as_ref().owner != &#owner_address {
  260. return Err(#error);
  261. }
  262. }
  263. }
  264. pub fn generate_constraint_rent_exempt(
  265. f: &Field,
  266. c: &ConstraintRentExempt,
  267. ) -> proc_macro2::TokenStream {
  268. let ident = &f.ident;
  269. let info = quote! {
  270. #ident.to_account_info()
  271. };
  272. match c {
  273. ConstraintRentExempt::Skip => quote! {},
  274. ConstraintRentExempt::Enforce => quote! {
  275. if !__anchor_rent.is_exempt(#info.lamports(), #info.try_data_len()?) {
  276. return Err(anchor_lang::__private::ErrorCode::ConstraintRentExempt.into());
  277. }
  278. },
  279. }
  280. }
  281. fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
  282. let payer = {
  283. let p = &c.payer;
  284. quote! {
  285. let payer = #p.to_account_info();
  286. }
  287. };
  288. let seeds_with_nonce = match &c.seeds {
  289. None => quote! {},
  290. Some(c) => {
  291. let s = &mut c.seeds.clone();
  292. // If the seeds came with a trailing comma, we need to chop it off
  293. // before we interpolate them below.
  294. if let Some(pair) = s.pop() {
  295. s.push_value(pair.into_value());
  296. }
  297. let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
  298. quote! { #s, }
  299. });
  300. let inner = match c.bump.as_ref() {
  301. // Bump target not given. Use the canonical bump.
  302. None => {
  303. quote! {
  304. [
  305. #maybe_seeds_plus_comma
  306. &[
  307. Pubkey::find_program_address(
  308. &[#s],
  309. program_id,
  310. ).1
  311. ][..]
  312. ]
  313. }
  314. }
  315. // Bump target given. Use it.
  316. Some(b) => quote! {
  317. [#maybe_seeds_plus_comma &[#b][..]]
  318. },
  319. };
  320. quote! {
  321. &#inner[..]
  322. }
  323. }
  324. };
  325. generate_init(f, c.if_needed, seeds_with_nonce, payer, &c.space, &c.kind)
  326. }
  327. fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
  328. let name = &f.ident;
  329. let s = &mut c.seeds.clone();
  330. let deriving_program_id = c
  331. .program_seed
  332. .clone()
  333. // If they specified a seeds::program to use when deriving the PDA, use it.
  334. .map(|program_id| quote! { #program_id })
  335. // Otherwise fall back to the current program's program_id.
  336. .unwrap_or(quote! { program_id });
  337. // If the seeds came with a trailing comma, we need to chop it off
  338. // before we interpolate them below.
  339. if let Some(pair) = s.pop() {
  340. s.push_value(pair.into_value());
  341. }
  342. // If the bump is provided with init *and target*, then force it to be the
  343. // canonical bump.
  344. if c.is_init && c.bump.is_some() {
  345. let b = c.bump.as_ref().unwrap();
  346. quote! {
  347. let (__program_signer, __bump) = anchor_lang::solana_program::pubkey::Pubkey::find_program_address(
  348. &[#s],
  349. &#deriving_program_id,
  350. );
  351. if #name.key() != __program_signer {
  352. return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
  353. }
  354. if __bump != #b {
  355. return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
  356. }
  357. }
  358. } else {
  359. let maybe_seeds_plus_comma = (!s.is_empty()).then(|| {
  360. quote! { #s, }
  361. });
  362. let seeds = match c.bump.as_ref() {
  363. // Bump target not given. Find it.
  364. None => {
  365. quote! {
  366. [
  367. #maybe_seeds_plus_comma
  368. &[
  369. Pubkey::find_program_address(
  370. &[#s],
  371. &#deriving_program_id,
  372. ).1
  373. ][..]
  374. ]
  375. }
  376. }
  377. // Bump target given. Use it.
  378. Some(b) => {
  379. quote! {
  380. [#maybe_seeds_plus_comma &[#b][..]]
  381. }
  382. }
  383. };
  384. quote! {
  385. let __program_signer = Pubkey::create_program_address(
  386. &#seeds[..],
  387. &#deriving_program_id,
  388. ).map_err(|_| anchor_lang::__private::ErrorCode::ConstraintSeeds)?;
  389. if #name.key() != __program_signer {
  390. return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
  391. }
  392. }
  393. }
  394. }
  395. fn generate_constraint_associated_token(
  396. f: &Field,
  397. c: &ConstraintAssociatedToken,
  398. ) -> proc_macro2::TokenStream {
  399. let name = &f.ident;
  400. let wallet_address = &c.wallet;
  401. let spl_token_mint_address = &c.mint;
  402. quote! {
  403. if #name.owner != #wallet_address.key() {
  404. return Err(anchor_lang::__private::ErrorCode::ConstraintTokenOwner.into());
  405. }
  406. let __associated_token_address = anchor_spl::associated_token::get_associated_token_address(&#wallet_address.key(), &#spl_token_mint_address.key());
  407. if #name.key() != __associated_token_address {
  408. return Err(anchor_lang::__private::ErrorCode::ConstraintAssociated.into());
  409. }
  410. }
  411. }
  412. // `if_needed` is set if account allocation and initialization is optional.
  413. pub fn generate_init(
  414. f: &Field,
  415. if_needed: bool,
  416. seeds_with_nonce: proc_macro2::TokenStream,
  417. payer: proc_macro2::TokenStream,
  418. space: &Option<Expr>,
  419. kind: &InitKind,
  420. ) -> proc_macro2::TokenStream {
  421. let field = &f.ident;
  422. let ty_decl = f.ty_decl();
  423. let from_account_info = f.from_account_info(Some(kind));
  424. let if_needed = if if_needed {
  425. quote! {true}
  426. } else {
  427. quote! {false}
  428. };
  429. match kind {
  430. InitKind::Token { owner, mint } => {
  431. let create_account = generate_create_account(
  432. field,
  433. quote! {anchor_spl::token::TokenAccount::LEN},
  434. quote! {&token_program.key()},
  435. seeds_with_nonce,
  436. );
  437. quote! {
  438. let #field: #ty_decl = {
  439. if !#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID {
  440. // Define payer variable.
  441. #payer
  442. // Create the account with the system program.
  443. #create_account
  444. // Initialize the token account.
  445. let cpi_program = token_program.to_account_info();
  446. let accounts = anchor_spl::token::InitializeAccount {
  447. account: #field.to_account_info(),
  448. mint: #mint.to_account_info(),
  449. authority: #owner.to_account_info(),
  450. rent: rent.to_account_info(),
  451. };
  452. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
  453. anchor_spl::token::initialize_account(cpi_ctx)?;
  454. }
  455. let pa: #ty_decl = #from_account_info;
  456. if !(!#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID) {
  457. if pa.mint != #mint.key() {
  458. return Err(anchor_lang::__private::ErrorCode::ConstraintTokenMint.into());
  459. }
  460. if pa.owner != #owner.key() {
  461. return Err(anchor_lang::__private::ErrorCode::ConstraintTokenOwner.into());
  462. }
  463. }
  464. pa
  465. };
  466. }
  467. }
  468. InitKind::AssociatedToken { owner, mint } => {
  469. quote! {
  470. let #field: #ty_decl = {
  471. if !#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID {
  472. #payer
  473. let cpi_program = associated_token_program.to_account_info();
  474. let cpi_accounts = anchor_spl::associated_token::Create {
  475. payer: payer.to_account_info(),
  476. associated_token: #field.to_account_info(),
  477. authority: #owner.to_account_info(),
  478. mint: #mint.to_account_info(),
  479. system_program: system_program.to_account_info(),
  480. token_program: token_program.to_account_info(),
  481. rent: rent.to_account_info(),
  482. };
  483. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts);
  484. anchor_spl::associated_token::create(cpi_ctx)?;
  485. }
  486. let pa: #ty_decl = #from_account_info;
  487. if !(!#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID) {
  488. if pa.mint != #mint.key() {
  489. return Err(anchor_lang::__private::ErrorCode::ConstraintTokenMint.into());
  490. }
  491. if pa.owner != #owner.key() {
  492. return Err(anchor_lang::__private::ErrorCode::ConstraintTokenOwner.into());
  493. }
  494. if pa.key() != anchor_spl::associated_token::get_associated_token_address(&#owner.key(), &#mint.key()) {
  495. return Err(anchor_lang::__private::ErrorCode::AccountNotAssociatedTokenAccount.into());
  496. }
  497. }
  498. pa
  499. };
  500. }
  501. }
  502. InitKind::Mint {
  503. owner,
  504. decimals,
  505. freeze_authority,
  506. } => {
  507. let create_account = generate_create_account(
  508. field,
  509. quote! {anchor_spl::token::Mint::LEN},
  510. quote! {&token_program.key()},
  511. seeds_with_nonce,
  512. );
  513. let freeze_authority = match freeze_authority {
  514. Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) },
  515. None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None },
  516. };
  517. quote! {
  518. let #field: #ty_decl = {
  519. if !#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID {
  520. // Define payer variable.
  521. #payer
  522. // Create the account with the system program.
  523. #create_account
  524. // Initialize the mint account.
  525. let cpi_program = token_program.to_account_info();
  526. let accounts = anchor_spl::token::InitializeMint {
  527. mint: #field.to_account_info(),
  528. rent: rent.to_account_info(),
  529. };
  530. let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts);
  531. anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
  532. }
  533. let pa: #ty_decl = #from_account_info;
  534. if !(!#if_needed || #field.as_ref().owner == &anchor_lang::solana_program::system_program::ID) {
  535. if pa.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#owner.key()) {
  536. return Err(anchor_lang::__private::ErrorCode::ConstraintMintMintAuthority.into());
  537. }
  538. if pa.freeze_authority
  539. .as_ref()
  540. .map(|fa| #freeze_authority.as_ref().map(|expected_fa| fa != *expected_fa).unwrap_or(true))
  541. .unwrap_or(#freeze_authority.is_some()) {
  542. return Err(anchor_lang::__private::ErrorCode::ConstraintMintFreezeAuthority.into());
  543. }
  544. if pa.decimals != #decimals {
  545. return Err(anchor_lang::__private::ErrorCode::ConstraintMintDecimals.into());
  546. }
  547. }
  548. pa
  549. };
  550. }
  551. }
  552. InitKind::Program { owner } => {
  553. let space = match space {
  554. // If no explicit space param was given, serialize the type to bytes
  555. // and take the length (with +8 for the discriminator.)
  556. None => {
  557. let account_ty = f.account_ty();
  558. match matches!(f.ty, Ty::Loader(_) | Ty::AccountLoader(_)) {
  559. false => {
  560. quote! {
  561. let space = 8 + #account_ty::default().try_to_vec().unwrap().len();
  562. }
  563. }
  564. true => {
  565. quote! {
  566. let space = 8 + anchor_lang::__private::bytemuck::bytes_of(&#account_ty::default()).len();
  567. }
  568. }
  569. }
  570. }
  571. // Explicit account size given. Use it.
  572. Some(s) => quote! {
  573. let space = #s;
  574. },
  575. };
  576. // Owner of the account being created. If not specified,
  577. // default to the currently executing program.
  578. let owner = match owner {
  579. None => quote! {
  580. program_id
  581. },
  582. Some(o) => quote! {
  583. &#o
  584. },
  585. };
  586. let pda_check = if !seeds_with_nonce.is_empty() {
  587. quote! {
  588. let expected_key = anchor_lang::prelude::Pubkey::create_program_address(
  589. #seeds_with_nonce,
  590. #owner
  591. ).map_err(|_| anchor_lang::__private::ErrorCode::ConstraintSeeds)?;
  592. if expected_key != #field.key() {
  593. return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
  594. }
  595. }
  596. } else {
  597. quote! {}
  598. };
  599. let create_account =
  600. generate_create_account(field, quote! {space}, owner.clone(), seeds_with_nonce);
  601. let header_write = {
  602. match &f.ty {
  603. Ty::Account(_)
  604. | Ty::ProgramAccount(_)
  605. | Ty::Loader(_)
  606. | Ty::AccountLoader(_) => {
  607. let account_ty = f.account_ty();
  608. if cfg!(feature = "deprecated-layout") {
  609. quote! {
  610. {
  611. use std::io::{Write, Cursor};
  612. use anchor_lang::Discriminator;
  613. let mut __data = actual_field.try_borrow_mut_data()?;
  614. let __dst: &mut [u8] = &mut __data;
  615. let mut __cursor = Cursor::new(__dst);
  616. Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
  617. }
  618. }
  619. } else {
  620. quote! {
  621. {
  622. use std::io::{Write, Seek, SeekFrom, Cursor};
  623. use anchor_lang::Discriminator;
  624. let mut __data = actual_field.try_borrow_mut_data()?;
  625. let __dst: &mut [u8] = &mut __data;
  626. let mut __cursor = Cursor::new(__dst);
  627. Seek::seek(&mut __cursor, SeekFrom::Start(2)).unwrap();
  628. Write::write_all(&mut __cursor, &#account_ty::discriminator()).unwrap();
  629. }
  630. }
  631. }
  632. }
  633. _ => quote! {},
  634. }
  635. };
  636. quote! {
  637. let #field = {
  638. let actual_field = #field.to_account_info();
  639. let actual_owner = actual_field.owner;
  640. #space
  641. if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID {
  642. #payer
  643. #create_account
  644. }
  645. // Write the account header into the account data before
  646. // deserializing.
  647. #header_write
  648. let pa: #ty_decl = #from_account_info;
  649. if !(!#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID) {
  650. if space != actual_field.data_len() {
  651. return Err(anchor_lang::__private::ErrorCode::ConstraintSpace.into());
  652. }
  653. if actual_owner != #owner {
  654. return Err(anchor_lang::__private::ErrorCode::ConstraintOwner.into());
  655. }
  656. {
  657. let required_lamports = __anchor_rent.minimum_balance(space);
  658. if pa.to_account_info().lamports() < required_lamports {
  659. return Err(anchor_lang::__private::ErrorCode::ConstraintRentExempt.into());
  660. }
  661. }
  662. #pda_check
  663. }
  664. pa
  665. };
  666. }
  667. }
  668. }
  669. }
  670. // Generated code to create an account with with system program with the
  671. // given `space` amount of data, owned by `owner`.
  672. //
  673. // `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an
  674. // empty stream.
  675. pub fn generate_create_account(
  676. field: &Ident,
  677. space: proc_macro2::TokenStream,
  678. owner: proc_macro2::TokenStream,
  679. seeds_with_nonce: proc_macro2::TokenStream,
  680. ) -> proc_macro2::TokenStream {
  681. quote! {
  682. // If the account being initialized already has lamports, then
  683. // return them all back to the payer so that the account has
  684. // zero lamports when the system program's create instruction
  685. // is eventually called.
  686. let __current_lamports = #field.lamports();
  687. if __current_lamports == 0 {
  688. // Create the token account with right amount of lamports and space, and the correct owner.
  689. let lamports = __anchor_rent.minimum_balance(#space);
  690. anchor_lang::solana_program::program::invoke_signed(
  691. &anchor_lang::solana_program::system_instruction::create_account(
  692. &payer.key(),
  693. &#field.key(),
  694. lamports,
  695. #space as u64,
  696. #owner,
  697. ),
  698. &[
  699. payer.to_account_info(),
  700. #field.to_account_info(),
  701. system_program.to_account_info(),
  702. ],
  703. &[#seeds_with_nonce],
  704. )?;
  705. } else {
  706. // Fund the account for rent exemption.
  707. let required_lamports = __anchor_rent
  708. .minimum_balance(#space)
  709. .max(1)
  710. .saturating_sub(__current_lamports);
  711. if required_lamports > 0 {
  712. anchor_lang::solana_program::program::invoke(
  713. &anchor_lang::solana_program::system_instruction::transfer(
  714. &payer.key(),
  715. &#field.key(),
  716. required_lamports,
  717. ),
  718. &[
  719. payer.to_account_info(),
  720. #field.to_account_info(),
  721. system_program.to_account_info(),
  722. ],
  723. )?;
  724. }
  725. // Allocate space.
  726. anchor_lang::solana_program::program::invoke_signed(
  727. &anchor_lang::solana_program::system_instruction::allocate(
  728. &#field.key(),
  729. #space as u64,
  730. ),
  731. &[
  732. #field.to_account_info(),
  733. system_program.to_account_info(),
  734. ],
  735. &[#seeds_with_nonce],
  736. )?;
  737. // Assign to the spl token program.
  738. anchor_lang::solana_program::program::invoke_signed(
  739. &anchor_lang::solana_program::system_instruction::assign(
  740. &#field.key(),
  741. #owner,
  742. ),
  743. &[
  744. #field.to_account_info(),
  745. system_program.to_account_info(),
  746. ],
  747. &[#seeds_with_nonce],
  748. )?;
  749. }
  750. }
  751. }
  752. pub fn generate_constraint_executable(
  753. f: &Field,
  754. _c: &ConstraintExecutable,
  755. ) -> proc_macro2::TokenStream {
  756. let name = &f.ident;
  757. quote! {
  758. if !#name.to_account_info().executable {
  759. return Err(anchor_lang::__private::ErrorCode::ConstraintExecutable.into());
  760. }
  761. }
  762. }
  763. pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2::TokenStream {
  764. let program_target = c.program_target.clone();
  765. let ident = &f.ident;
  766. let account_ty = match &f.ty {
  767. Ty::CpiState(ty) => &ty.account_type_path,
  768. _ => panic!("Invalid state constraint"),
  769. };
  770. quote! {
  771. // Checks the given state account is the canonical state account for
  772. // the target program.
  773. if #ident.key() != anchor_lang::accounts::cpi_state::CpiState::<#account_ty>::address(&#program_target.key()) {
  774. return Err(anchor_lang::__private::ErrorCode::ConstraintState.into());
  775. }
  776. if #ident.as_ref().owner != &#program_target.key() {
  777. return Err(anchor_lang::__private::ErrorCode::ConstraintState.into());
  778. }
  779. }
  780. }
  781. fn generate_custom_error(
  782. custom_error: &Option<Expr>,
  783. error: proc_macro2::TokenStream,
  784. ) -> proc_macro2::TokenStream {
  785. match custom_error {
  786. Some(error) => quote! { #error.into() },
  787. None => quote! { anchor_lang::__private::ErrorCode::#error.into() },
  788. }
  789. }