dispatch.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. use crate::codegen::program::common::*;
  2. use crate::Program;
  3. use quote::quote;
  4. pub fn generate(program: &Program) -> proc_macro2::TokenStream {
  5. // Dispatch the state constructor.
  6. let ctor_state_dispatch_arm = match &program.state {
  7. None => quote! { /* no-op */ },
  8. Some(state) => match state.ctor_and_anchor.is_some() {
  9. false => quote! {},
  10. true => {
  11. let sighash_arr = sighash_ctor();
  12. let sighash_tts: proc_macro2::TokenStream =
  13. format!("{:?}", sighash_arr).parse().unwrap();
  14. quote! {
  15. #sighash_tts => {
  16. __private::__state::__ctor(
  17. program_id,
  18. accounts,
  19. ix_data,
  20. )
  21. }
  22. }
  23. }
  24. },
  25. };
  26. // Dispatch the state impl instructions.
  27. let state_dispatch_arms: Vec<proc_macro2::TokenStream> = match &program.state {
  28. None => vec![],
  29. Some(s) => s
  30. .impl_block_and_methods
  31. .as_ref()
  32. .map(|(_impl_block, methods)| {
  33. methods
  34. .iter()
  35. .map(|ix: &crate::StateIx| {
  36. let name = &ix.raw_method.sig.ident.to_string();
  37. let ix_method_name: proc_macro2::TokenStream =
  38. { format!("__{}", name).parse().unwrap() };
  39. let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, name);
  40. let sighash_tts: proc_macro2::TokenStream =
  41. format!("{:?}", sighash_arr).parse().unwrap();
  42. quote! {
  43. #sighash_tts => {
  44. __private::__state::#ix_method_name(
  45. program_id,
  46. accounts,
  47. ix_data,
  48. )
  49. }
  50. }
  51. })
  52. .collect()
  53. })
  54. .unwrap_or_default(),
  55. };
  56. // Dispatch all trait interface implementations.
  57. let trait_dispatch_arms: Vec<proc_macro2::TokenStream> = match &program.state {
  58. None => vec![],
  59. Some(s) => s
  60. .interfaces
  61. .as_ref()
  62. .map(|interfaces| {
  63. interfaces
  64. .iter()
  65. .flat_map(|iface: &crate::StateInterface| {
  66. iface
  67. .methods
  68. .iter()
  69. .map(|m: &crate::StateIx| {
  70. let sighash_arr = sighash(&iface.trait_name, &m.ident.to_string());
  71. let sighash_tts: proc_macro2::TokenStream =
  72. format!("{:?}", sighash_arr).parse().unwrap();
  73. let name = &m.raw_method.sig.ident.to_string();
  74. let ix_method_name: proc_macro2::TokenStream =
  75. format!("__{}_{}", iface.trait_name, name).parse().unwrap();
  76. quote! {
  77. #sighash_tts => {
  78. __private::__interface::#ix_method_name(
  79. program_id,
  80. accounts,
  81. ix_data,
  82. )
  83. }
  84. }
  85. })
  86. .collect::<Vec<proc_macro2::TokenStream>>()
  87. })
  88. .collect()
  89. })
  90. .unwrap_or_default(),
  91. };
  92. // Dispatch all global instructions.
  93. let global_dispatch_arms: Vec<proc_macro2::TokenStream> = program
  94. .ixs
  95. .iter()
  96. .map(|ix| {
  97. let ix_method_name = &ix.raw_method.sig.ident;
  98. let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, &ix_method_name.to_string());
  99. let sighash_tts: proc_macro2::TokenStream =
  100. format!("{:?}", sighash_arr).parse().unwrap();
  101. quote! {
  102. #sighash_tts => {
  103. __private::__global::#ix_method_name(
  104. program_id,
  105. accounts,
  106. ix_data,
  107. )
  108. }
  109. }
  110. })
  111. .collect();
  112. let fallback_fn = gen_fallback(program).unwrap_or(quote! {
  113. Err(anchor_lang::error::ErrorCode::InstructionFallbackNotFound.into())
  114. });
  115. quote! {
  116. /// Performs method dispatch.
  117. ///
  118. /// Each method in an anchor program is uniquely defined by a namespace
  119. /// and a rust identifier (i.e., the name given to the method). These
  120. /// two pieces can be combined to creater a method identifier,
  121. /// specifically, Anchor uses
  122. ///
  123. /// Sha256("<namespace>::<rust-identifier>")[..8],
  124. ///
  125. /// where the namespace can be one of three types. 1) "global" for a
  126. /// regular instruction, 2) "state" for a state struct instruction
  127. /// handler and 3) a trait namespace (used in combination with the
  128. /// `#[interface]` attribute), which is defined by the trait name, e..
  129. /// `MyTrait`.
  130. ///
  131. /// With this 8 byte identifier, Anchor performs method dispatch,
  132. /// matching the given 8 byte identifier to the associated method
  133. /// handler, which leads to user defined code being eventually invoked.
  134. fn dispatch(
  135. program_id: &Pubkey,
  136. accounts: &[AccountInfo],
  137. data: &[u8],
  138. ) -> ProgramResult {
  139. // Split the instruction data into the first 8 byte method
  140. // identifier (sighash) and the serialized instruction data.
  141. let mut ix_data: &[u8] = data;
  142. let sighash: [u8; 8] = {
  143. let mut sighash: [u8; 8] = [0; 8];
  144. sighash.copy_from_slice(&ix_data[..8]);
  145. ix_data = &ix_data[8..];
  146. sighash
  147. };
  148. // If the method identifier is the IDL tag, then execute an IDL
  149. // instruction, injected into all Anchor programs.
  150. if cfg!(not(feature = "no-idl")) {
  151. if sighash == anchor_lang::idl::IDL_IX_TAG.to_le_bytes() {
  152. return __private::__idl::__idl_dispatch(
  153. program_id,
  154. accounts,
  155. &ix_data,
  156. );
  157. }
  158. }
  159. match sighash {
  160. #ctor_state_dispatch_arm
  161. #(#state_dispatch_arms)*
  162. #(#trait_dispatch_arms)*
  163. #(#global_dispatch_arms)*
  164. _ => {
  165. #fallback_fn
  166. }
  167. }
  168. }
  169. }
  170. }
  171. pub fn gen_fallback(program: &Program) -> Option<proc_macro2::TokenStream> {
  172. program.fallback_fn.as_ref().map(|fallback_fn| {
  173. let program_name = &program.name;
  174. let method = &fallback_fn.raw_method;
  175. let fn_name = &method.sig.ident;
  176. quote! {
  177. #program_name::#fn_name(program_id, accounts, data)
  178. }
  179. })
  180. }