chainparser/ixs/
instruction_mapper.rs1use lazy_static::lazy_static;
2use log::*;
3use std::{collections::HashMap, str::FromStr};
4
5use solana_idl::{Idl, IdlInstruction};
6use solana_sdk::pubkey::Pubkey;
7
8use super::{discriminator::discriminator_from_ix, ParseableInstruction};
9
10#[rustfmt::skip]
11lazy_static! {
12 pub static ref BUILTIN_PROGRAMS: HashMap<Pubkey, &'static str> = [
13 ("System Program" , "11111111111111111111111111111111") ,
14 ("BPF Upgradeable Loader" , "BPFLoaderUpgradeab1e11111111111111111111111"),
15 ("BPF Loader 2" , "BPFLoader2111111111111111111111111111111111"),
16 ("Config Program" , "Config1111111111111111111111111111111111111"),
17 ("Feature Program" , "Feature111111111111111111111111111111111111"),
18 ("Native Loader" , "NativeLoader1111111111111111111111111111111"),
19 ("Stake Program" , "Stake11111111111111111111111111111111111111"),
20 ("Sysvar" , "Sysvar1111111111111111111111111111111111111"),
21 ("Vote Program" , "Vote111111111111111111111111111111111111111"),
22 ("Stake Config" , "StakeConfig11111111111111111111111111111111"),
23 ("Sol Program" , "So11111111111111111111111111111111111111112"),
24 ("Clock Sysvar" , "SysvarC1ock11111111111111111111111111111111"),
25 ("Epoch Schedule Sysvar" , "SysvarEpochSchedu1e111111111111111111111111"),
26 ("Fees Sysvar" , "SysvarFees111111111111111111111111111111111"),
27 ("Last Restart Slog Sysvar" , "SysvarLastRestartS1ot1111111111111111111111"),
28 ("Recent Blockhashes Sysvar" , "SysvarRecentB1ockHashes11111111111111111111"),
29 ("Rent Sysvar" , "SysvarRent111111111111111111111111111111111"),
30 ("Slot Hashes" , "SysvarS1otHashes111111111111111111111111111"),
31 ("Slot History" , "SysvarS1otHistory11111111111111111111111111"),
32 ("Stake History" , "SysvarStakeHistory1111111111111111111111111"),
33 ("MagicBlock System Program" , "Magic11111111111111111111111111111111111111"),
34 ("MagicBlock Delegation Program" , "DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh"),
35 ("Luzid Authority" , "LUzidNSiPNjYNkxZcUm5hYHwnWPwsUfh2US1cpWwaBm"),
36 ]
37 .into_iter()
38 .map(|(name, key)| (Pubkey::from_str(key).unwrap(), name))
39 .collect();
40}
41
42pub fn map_instruction(
43 instruction: &impl ParseableInstruction,
44 idl: Option<&Idl>,
45) -> InstructionMapResult {
46 InstructionMapper::map_accounts(instruction, idl)
47}
48
49pub struct InstructionMapper {
50 idl_instruction: IdlInstruction,
51}
52
53pub struct InstructionMapResult {
54 pub accounts: HashMap<Pubkey, String>,
55 pub instruction_name: Option<String>,
56 pub program_name: Option<String>,
57}
58
59impl InstructionMapper {
60 pub fn map_accounts(
64 instruction: &impl ParseableInstruction,
65 idl: Option<&Idl>,
66 ) -> InstructionMapResult {
67 let mapper = idl
68 .as_ref()
69 .and_then(|idl| Self::determine_accounts_mapper(instruction, idl));
70 let program_name = idl.as_ref().map(|idl| idl.name.to_string());
71 let program_id = instruction.program_id();
72
73 let mut accounts = HashMap::new();
74 let mut instruction_name = None::<String>;
75 let ix_accounts = instruction.accounts();
76 for (idx, pubkey) in ix_accounts.into_iter().enumerate() {
77 if let Some(name) = BUILTIN_PROGRAMS.get(&pubkey) {
78 accounts.insert(pubkey, name.to_string());
79 continue;
80 }
81 if let Some(program_name) = program_name.as_ref() {
82 if &pubkey == program_id {
83 accounts.insert(pubkey, program_name.to_string());
84 continue;
85 }
86 }
87 if let Some(mapper) = &mapper {
88 let name = mapper
89 .idl_instruction
90 .accounts
91 .get(idx)
92 .map(|x| x.name().to_string());
93 if let Some(name) = name {
94 accounts.insert(pubkey, name);
95 }
96 instruction_name
97 .replace(mapper.idl_instruction.name.to_string());
98 }
99 }
100 let program_name = idl.map(|x| x.name.to_string()).or_else(|| {
101 BUILTIN_PROGRAMS.get(program_id).map(|x| x.to_string())
102 });
103
104 InstructionMapResult {
105 accounts,
106 instruction_name,
107 program_name,
108 }
109 }
110
111 fn determine_accounts_mapper(
112 instruction: &impl ParseableInstruction,
113 idl: &Idl,
114 ) -> Option<InstructionMapper> {
115 find_best_matching_idl_ix(&idl.instructions, instruction)
116 .map(|idl_instruction| InstructionMapper { idl_instruction })
117 }
118}
119
120fn find_best_matching_idl_ix(
121 ix_idls: &[IdlInstruction],
122 ix: &impl ParseableInstruction,
123) -> Option<IdlInstruction> {
124 let mut best_match = None;
125 let mut best_match_score = 0;
126 for idl_ix in ix_idls {
127 let disc = discriminator_from_ix(idl_ix);
128 trace!("Discriminator for '{}': {:?}", idl_ix.name, disc);
129 if disc.len() > ix.data().len() {
130 continue;
131 }
132 let mut score = 0;
133 for (a, b) in disc.iter().zip(ix.data()) {
134 if a != b {
135 break;
136 }
137 score += 1;
138 }
139 if score > best_match_score {
140 best_match = Some(idl_ix);
141 best_match_score = score;
142 }
143 }
144 best_match.cloned()
145}