lib.rs 2.8 KB

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