-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
dispatch.rs
120 lines (112 loc) · 4.33 KB
/
dispatch.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use crate::Program;
use heck::CamelCase;
use quote::quote;
pub fn generate(program: &Program) -> proc_macro2::TokenStream {
// Dispatch all global instructions.
let global_dispatch_arms: Vec<proc_macro2::TokenStream> = program
.ixs
.iter()
.map(|ix| {
let ix_method_name = &ix.raw_method.sig.ident;
let ix_name_camel: proc_macro2::TokenStream = ix_method_name
.to_string()
.to_camel_case()
.parse()
.expect("Failed to parse ix method name in camel as `TokenStream`");
quote! {
instruction::#ix_name_camel::DISCRIMINATOR => {
__private::__global::#ix_method_name(
program_id,
accounts,
ix_data,
)
}
}
})
.collect();
let fallback_fn = gen_fallback(program).unwrap_or(quote! {
Err(anchor_lang::error::ErrorCode::InstructionFallbackNotFound.into())
});
let event_cpi_handler = generate_event_cpi_handler();
quote! {
/// Performs method dispatch.
///
/// Each method in an anchor program is uniquely defined by a namespace
/// and a rust identifier (i.e., the name given to the method). These
/// two pieces can be combined to create a method identifier,
/// specifically, Anchor uses
///
/// Sha256("<namespace>:<rust-identifier>")[..8],
///
/// where the namespace can be one type. "global" for a
/// regular instruction.
///
/// With this 8 byte identifier, Anchor performs method dispatch,
/// matching the given 8 byte identifier to the associated method
/// handler, which leads to user defined code being eventually invoked.
fn dispatch<'info>(
program_id: &Pubkey,
accounts: &'info [AccountInfo<'info>],
data: &[u8],
) -> anchor_lang::Result<()> {
// Split the instruction data into the first 8 byte method
// identifier (sighash) and the serialized instruction data.
let mut ix_data: &[u8] = data;
let sighash: [u8; 8] = {
let mut sighash: [u8; 8] = [0; 8];
sighash.copy_from_slice(&ix_data[..8]);
ix_data = &ix_data[8..];
sighash
};
match sighash.as_slice() {
#(#global_dispatch_arms)*
anchor_lang::idl::IDL_IX_TAG_LE => {
// If the method identifier is the IDL tag, then execute an IDL
// instruction, injected into all Anchor programs unless they have
// no-idl enabled
#[cfg(not(feature = "no-idl"))]
{
__private::__idl::__idl_dispatch(
program_id,
accounts,
&ix_data,
)
}
#[cfg(feature = "no-idl")]
{
Err(anchor_lang::error::ErrorCode::IdlInstructionStub.into())
}
}
anchor_lang::event::EVENT_IX_TAG_LE => {
#event_cpi_handler
}
_ => {
#fallback_fn
}
}
}
}
}
pub fn gen_fallback(program: &Program) -> Option<proc_macro2::TokenStream> {
program.fallback_fn.as_ref().map(|fallback_fn| {
let program_name = &program.name;
let method = &fallback_fn.raw_method;
let fn_name = &method.sig.ident;
quote! {
#program_name::#fn_name(program_id, accounts, data)
}
})
}
/// Generate the event-cpi instruction handler based on whether the `event-cpi` feature is enabled.
pub fn generate_event_cpi_handler() -> proc_macro2::TokenStream {
#[cfg(feature = "event-cpi")]
quote! {
// `event-cpi` feature is enabled, dispatch self-cpi instruction
__private::__events::__event_dispatch(program_id, accounts, &ix_data)
}
#[cfg(not(feature = "event-cpi"))]
quote! {
// `event-cpi` feature is not enabled
Err(anchor_lang::error::ErrorCode::EventInstructionStub.into())
}
}