dispatch.rs 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. use crate::Program;
  2. use heck::CamelCase;
  3. use quote::quote;
  4. pub fn generate(program: &Program) -> proc_macro2::TokenStream {
  5. // Dispatch all global instructions.
  6. let global_dispatch_arms: Vec<proc_macro2::TokenStream> = program
  7. .ixs
  8. .iter()
  9. .map(|ix| {
  10. let ix_method_name = &ix.raw_method.sig.ident;
  11. let ix_name_camel: proc_macro2::TokenStream = ix_method_name
  12. .to_string()
  13. .as_str()
  14. .to_camel_case()
  15. .parse()
  16. .expect("Failed to parse ix method name in camel as `TokenStream`");
  17. quote! {
  18. instruction::#ix_name_camel::DISCRIMINATOR => {
  19. __private::__global::#ix_method_name(
  20. program_id,
  21. accounts,
  22. ix_data,
  23. )
  24. }
  25. }
  26. })
  27. .collect();
  28. let fallback_fn = gen_fallback(program).unwrap_or(quote! {
  29. Err(anchor_lang::error::ErrorCode::InstructionFallbackNotFound.into())
  30. });
  31. quote! {
  32. /// Performs method dispatch.
  33. ///
  34. /// Each method in an anchor program is uniquely defined by a namespace
  35. /// and a rust identifier (i.e., the name given to the method). These
  36. /// two pieces can be combined to creater a method identifier,
  37. /// specifically, Anchor uses
  38. ///
  39. /// Sha256("<namespace>:<rust-identifier>")[..8],
  40. ///
  41. /// where the namespace can be one type. "global" for a
  42. /// regular instruction.
  43. ///
  44. /// With this 8 byte identifier, Anchor performs method dispatch,
  45. /// matching the given 8 byte identifier to the associated method
  46. /// handler, which leads to user defined code being eventually invoked.
  47. fn dispatch(
  48. program_id: &Pubkey,
  49. accounts: &[AccountInfo],
  50. data: &[u8],
  51. ) -> anchor_lang::Result<()> {
  52. // Split the instruction data into the first 8 byte method
  53. // identifier (sighash) and the serialized instruction data.
  54. let mut ix_data: &[u8] = data;
  55. let sighash: [u8; 8] = {
  56. let mut sighash: [u8; 8] = [0; 8];
  57. sighash.copy_from_slice(&ix_data[..8]);
  58. ix_data = &ix_data[8..];
  59. sighash
  60. };
  61. use anchor_lang::Discriminator;
  62. match sighash {
  63. #(#global_dispatch_arms)*
  64. anchor_lang::idl::IDL_IX_TAG_LE => {
  65. // If the method identifier is the IDL tag, then execute an IDL
  66. // instruction, injected into all Anchor programs.
  67. if cfg!(not(feature = "no-idl")) {
  68. __private::__idl::__idl_dispatch(
  69. program_id,
  70. accounts,
  71. &ix_data,
  72. )
  73. } else {
  74. Err(anchor_lang::error::ErrorCode::IdlInstructionStub.into())
  75. }
  76. }
  77. _ => {
  78. #fallback_fn
  79. }
  80. }
  81. }
  82. }
  83. }
  84. pub fn gen_fallback(program: &Program) -> Option<proc_macro2::TokenStream> {
  85. program.fallback_fn.as_ref().map(|fallback_fn| {
  86. let program_name = &program.name;
  87. let method = &fallback_fn.raw_method;
  88. let fn_name = &method.sig.ident;
  89. quote! {
  90. #program_name::#fn_name(program_id, accounts, data)
  91. }
  92. })
  93. }