id.rs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. //! Copied from solana/sdk/macro so that Anchor programs don't need to specify
  2. //! `solana_program` as an additional crate dependency, but instead can access
  3. //! it via `anchor_lang::declare_id`.
  4. //!
  5. //! Convenience macro to declare a static public key and functions to interact with it
  6. //!
  7. //! Input: a single literal base58 string representation of a program's id
  8. extern crate proc_macro;
  9. use proc_macro2::{Delimiter, Span, TokenTree};
  10. use quote::{quote, ToTokens};
  11. use std::convert::TryFrom;
  12. use syn::{
  13. bracketed,
  14. parse::{Parse, ParseStream, Result},
  15. punctuated::Punctuated,
  16. token::Bracket,
  17. Expr, Ident, LitByte, LitStr, Path, Token,
  18. };
  19. fn parse_id(
  20. input: ParseStream,
  21. pubkey_type: proc_macro2::TokenStream,
  22. ) -> Result<proc_macro2::TokenStream> {
  23. let id = if input.peek(syn::LitStr) {
  24. let id_literal: LitStr = input.parse()?;
  25. parse_pubkey(&id_literal, &pubkey_type)?
  26. } else {
  27. let expr: Expr = input.parse()?;
  28. quote! { #expr }
  29. };
  30. if !input.is_empty() {
  31. let stream: proc_macro2::TokenStream = input.parse()?;
  32. return Err(syn::Error::new_spanned(stream, "unexpected token"));
  33. }
  34. Ok(id)
  35. }
  36. fn id_to_tokens(
  37. id: &proc_macro2::TokenStream,
  38. pubkey_type: proc_macro2::TokenStream,
  39. tokens: &mut proc_macro2::TokenStream,
  40. ) {
  41. tokens.extend(quote! {
  42. /// The static program ID
  43. pub static ID: #pubkey_type = #id;
  44. /// Confirms that a given pubkey is equivalent to the program ID
  45. pub fn check_id(id: &#pubkey_type) -> bool {
  46. id == &ID
  47. }
  48. /// Returns the program ID
  49. pub fn id() -> #pubkey_type {
  50. ID
  51. }
  52. #[cfg(test)]
  53. #[test]
  54. fn test_id() {
  55. assert!(check_id(&id()));
  56. }
  57. });
  58. }
  59. fn deprecated_id_to_tokens(
  60. id: &proc_macro2::TokenStream,
  61. pubkey_type: proc_macro2::TokenStream,
  62. tokens: &mut proc_macro2::TokenStream,
  63. ) {
  64. tokens.extend(quote! {
  65. /// The static program ID
  66. pub static ID: #pubkey_type = #id;
  67. /// Confirms that a given pubkey is equivalent to the program ID
  68. #[deprecated()]
  69. pub fn check_id(id: &#pubkey_type) -> bool {
  70. id == &ID
  71. }
  72. /// Returns the program ID
  73. #[deprecated()]
  74. pub fn id() -> #pubkey_type {
  75. ID
  76. }
  77. #[cfg(test)]
  78. #[test]
  79. fn test_id() {
  80. #[allow(deprecated)]
  81. assert!(check_id(&id()));
  82. }
  83. });
  84. }
  85. pub struct Id(proc_macro2::TokenStream);
  86. impl Parse for Id {
  87. fn parse(input: ParseStream) -> Result<Self> {
  88. parse_id(
  89. input,
  90. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  91. )
  92. .map(Self)
  93. }
  94. }
  95. impl ToTokens for Id {
  96. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  97. id_to_tokens(
  98. &self.0,
  99. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  100. tokens,
  101. )
  102. }
  103. }
  104. struct IdDeprecated(proc_macro2::TokenStream);
  105. impl Parse for IdDeprecated {
  106. fn parse(input: ParseStream) -> Result<Self> {
  107. parse_id(
  108. input,
  109. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  110. )
  111. .map(Self)
  112. }
  113. }
  114. impl ToTokens for IdDeprecated {
  115. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  116. deprecated_id_to_tokens(
  117. &self.0,
  118. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  119. tokens,
  120. )
  121. }
  122. }
  123. struct ProgramSdkId(proc_macro2::TokenStream);
  124. impl Parse for ProgramSdkId {
  125. fn parse(input: ParseStream) -> Result<Self> {
  126. parse_id(
  127. input,
  128. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  129. )
  130. .map(Self)
  131. }
  132. }
  133. impl ToTokens for ProgramSdkId {
  134. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  135. id_to_tokens(
  136. &self.0,
  137. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  138. tokens,
  139. )
  140. }
  141. }
  142. struct ProgramSdkIdDeprecated(proc_macro2::TokenStream);
  143. impl Parse for ProgramSdkIdDeprecated {
  144. fn parse(input: ParseStream) -> Result<Self> {
  145. parse_id(
  146. input,
  147. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  148. )
  149. .map(Self)
  150. }
  151. }
  152. impl ToTokens for ProgramSdkIdDeprecated {
  153. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  154. deprecated_id_to_tokens(
  155. &self.0,
  156. quote! { anchor_lang::solana_program::pubkey::Pubkey },
  157. tokens,
  158. )
  159. }
  160. }
  161. #[allow(dead_code)] // `respan` may be compiled out
  162. struct RespanInput {
  163. to_respan: Path,
  164. respan_using: Span,
  165. }
  166. impl Parse for RespanInput {
  167. fn parse(input: ParseStream) -> Result<Self> {
  168. let to_respan: Path = input.parse()?;
  169. let _comma: Token![,] = input.parse()?;
  170. let respan_tree: TokenTree = input.parse()?;
  171. match respan_tree {
  172. TokenTree::Group(g) if g.delimiter() == Delimiter::None => {
  173. let ident: Ident = syn::parse2(g.stream())?;
  174. Ok(RespanInput {
  175. to_respan,
  176. respan_using: ident.span(),
  177. })
  178. }
  179. val => Err(syn::Error::new_spanned(
  180. val,
  181. "expected None-delimited group",
  182. )),
  183. }
  184. }
  185. }
  186. fn parse_pubkey(
  187. id_literal: &LitStr,
  188. pubkey_type: &proc_macro2::TokenStream,
  189. ) -> Result<proc_macro2::TokenStream> {
  190. let id_vec = bs58::decode(id_literal.value())
  191. .into_vec()
  192. .map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
  193. let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| {
  194. syn::Error::new_spanned(
  195. &id_literal,
  196. format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
  197. )
  198. })?;
  199. let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site()));
  200. Ok(quote! {
  201. #pubkey_type::new_from_array(
  202. [#(#bytes,)*]
  203. )
  204. })
  205. }
  206. struct Pubkeys {
  207. method: Ident,
  208. num: usize,
  209. pubkeys: proc_macro2::TokenStream,
  210. }
  211. impl Parse for Pubkeys {
  212. fn parse(input: ParseStream) -> Result<Self> {
  213. let pubkey_type = quote! {
  214. anchor_lang::solana_program::pubkey::Pubkey
  215. };
  216. let method = input.parse()?;
  217. let _comma: Token![,] = input.parse()?;
  218. let (num, pubkeys) = if input.peek(syn::LitStr) {
  219. let id_literal: LitStr = input.parse()?;
  220. (1, parse_pubkey(&id_literal, &pubkey_type)?)
  221. } else if input.peek(Bracket) {
  222. let pubkey_strings;
  223. bracketed!(pubkey_strings in input);
  224. let punctuated: Punctuated<LitStr, Token![,]> =
  225. Punctuated::parse_terminated(&pubkey_strings)?;
  226. let mut pubkeys: Punctuated<proc_macro2::TokenStream, Token![,]> = Punctuated::new();
  227. for string in punctuated.iter() {
  228. pubkeys.push(parse_pubkey(string, &pubkey_type)?);
  229. }
  230. (pubkeys.len(), quote! {#pubkeys})
  231. } else {
  232. let stream: proc_macro2::TokenStream = input.parse()?;
  233. return Err(syn::Error::new_spanned(stream, "unexpected token"));
  234. };
  235. Ok(Pubkeys {
  236. method,
  237. num,
  238. pubkeys,
  239. })
  240. }
  241. }
  242. impl ToTokens for Pubkeys {
  243. fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
  244. let Pubkeys {
  245. method,
  246. num,
  247. pubkeys,
  248. } = self;
  249. let pubkey_type = quote! {
  250. anchor_lang::solana_program::pubkey::Pubkey
  251. };
  252. if *num == 1 {
  253. tokens.extend(quote! {
  254. pub fn #method() -> #pubkey_type {
  255. #pubkeys
  256. }
  257. });
  258. } else {
  259. tokens.extend(quote! {
  260. pub fn #method() -> ::std::vec::Vec<#pubkey_type> {
  261. vec![#pubkeys]
  262. }
  263. });
  264. }
  265. }
  266. }