1use std::collections::HashMap;
2
3pub struct ResolveContext<'a> {
5 #[allow(dead_code)]
6 pub(crate) state_id: u32,
7 pub(crate) slot: u64,
8 pub(crate) signature: String,
9 pub(crate) reverse_lookups: &'a mut std::collections::HashMap<String, crate::vm::PdaReverseLookup>,
10}
11
12impl<'a> ResolveContext<'a> {
13 pub fn new(
15 state_id: u32,
16 slot: u64,
17 signature: String,
18 reverse_lookups: &'a mut std::collections::HashMap<String, crate::vm::PdaReverseLookup>,
19 ) -> Self {
20 Self {
21 state_id,
22 slot,
23 signature,
24 reverse_lookups,
25 }
26 }
27
28 pub fn pda_reverse_lookup(&mut self, pda_address: &str) -> Option<String> {
31 let lookup_name = "default_pda_lookup";
33
34 if let Some(lookup_table) = self.reverse_lookups.get_mut(lookup_name) {
35 let result = lookup_table.lookup(pda_address);
36 if result.is_some() {
37 tracing::debug!("✓ PDA reverse lookup hit: {} -> {:?}", pda_address, result);
38 } else {
39 tracing::debug!("✗ PDA reverse lookup miss: {}", pda_address);
40 }
41 result
42 } else {
43 tracing::debug!("✗ PDA reverse lookup table '{}' not found", lookup_name);
44 None
45 }
46 }
47
48 pub fn slot(&self) -> u64 {
49 self.slot
50 }
51
52 pub fn signature(&self) -> &str {
53 &self.signature
54 }
55}
56
57pub enum KeyResolution {
59 Found(String),
61
62 QueueUntil(&'static [u8]),
65
66 Skip,
68}
69
70pub struct InstructionContext<'a> {
72 pub(crate) accounts: HashMap<String, String>,
73 #[allow(dead_code)]
74 pub(crate) state_id: u32,
75 pub(crate) reverse_lookup_tx: &'a mut dyn ReverseLookupUpdater,
76 pub(crate) pending_updates: Vec<crate::vm::PendingAccountUpdate>,
77 pub(crate) registers: Option<&'a mut Vec<crate::vm::RegisterValue>>,
79 pub(crate) state_reg: Option<crate::vm::Register>,
80 #[allow(dead_code)]
81 pub(crate) compiled_paths: Option<&'a HashMap<String, crate::metrics_context::CompiledPath>>,
82 pub(crate) instruction_data: Option<&'a serde_json::Value>,
83 pub(crate) slot: Option<u64>,
84 pub(crate) signature: Option<String>,
85 pub(crate) timestamp: Option<i64>,
86 pub(crate) dirty_fields: std::collections::HashSet<String>,
88}
89
90pub trait ReverseLookupUpdater {
91 fn update(&mut self, pda_address: String, seed_value: String) -> Vec<crate::vm::PendingAccountUpdate>;
92 fn flush_pending(&mut self, pda_address: &str) -> Vec<crate::vm::PendingAccountUpdate>;
93}
94
95impl<'a> InstructionContext<'a> {
96 pub fn new(
98 accounts: HashMap<String, String>,
99 state_id: u32,
100 reverse_lookup_tx: &'a mut dyn ReverseLookupUpdater,
101 ) -> Self {
102 Self {
103 accounts,
104 state_id,
105 reverse_lookup_tx,
106 pending_updates: Vec::new(),
107 registers: None,
108 state_reg: None,
109 compiled_paths: None,
110 instruction_data: None,
111 slot: None,
112 signature: None,
113 timestamp: None,
114 dirty_fields: std::collections::HashSet::new(),
115 }
116 }
117
118 #[allow(clippy::too_many_arguments)]
120 pub fn with_metrics(
121 accounts: HashMap<String, String>,
122 state_id: u32,
123 reverse_lookup_tx: &'a mut dyn ReverseLookupUpdater,
124 registers: &'a mut Vec<crate::vm::RegisterValue>,
125 state_reg: crate::vm::Register,
126 compiled_paths: &'a HashMap<String, crate::metrics_context::CompiledPath>,
127 instruction_data: &'a serde_json::Value,
128 slot: Option<u64>,
129 signature: Option<String>,
130 timestamp: i64,
131 ) -> Self {
132 Self {
133 accounts,
134 state_id,
135 reverse_lookup_tx,
136 pending_updates: Vec::new(),
137 registers: Some(registers),
138 state_reg: Some(state_reg),
139 compiled_paths: Some(compiled_paths),
140 instruction_data: Some(instruction_data),
141 slot,
142 signature,
143 timestamp: Some(timestamp),
144 dirty_fields: std::collections::HashSet::new(),
145 }
146 }
147
148 pub fn account(&self, name: &str) -> Option<String> {
150 self.accounts.get(name).cloned()
151 }
152
153 pub fn register_pda_reverse_lookup(&mut self, pda_address: &str, seed_value: &str) {
159 tracing::info!("📝 Registering PDA reverse lookup: {} -> {}", pda_address, seed_value);
160 let pending = self.reverse_lookup_tx.update(pda_address.to_string(), seed_value.to_string());
161 if !pending.is_empty() {
162 tracing::info!(" 🔄 Flushed {} pending account update(s) for this PDA", pending.len());
163 }
164 self.pending_updates.extend(pending);
165 }
166
167 pub fn take_pending_updates(&mut self) -> Vec<crate::vm::PendingAccountUpdate> {
172 std::mem::take(&mut self.pending_updates)
173 }
174
175 pub fn dirty_fields(&self) -> &std::collections::HashSet<String> {
177 &self.dirty_fields
178 }
179
180 pub fn state_value(&self) -> Option<&serde_json::Value> {
182 if let (Some(registers), Some(state_reg)) = (self.registers.as_ref(), self.state_reg) {
183 Some(®isters[state_reg])
184 } else {
185 None
186 }
187 }
188
189 pub fn get<T: serde::de::DeserializeOwned>(&self, field_path: &str) -> Option<T> {
192 if let (Some(registers), Some(state_reg)) = (self.registers.as_ref(), self.state_reg) {
193 let state = ®isters[state_reg];
194 self.get_nested_value(state, field_path)
195 .and_then(|v| serde_json::from_value(v.clone()).ok())
196 } else {
197 None
198 }
199 }
200
201 pub fn set<T: serde::Serialize>(&mut self, field_path: &str, value: T) {
203 if let (Some(registers), Some(state_reg)) = (self.registers.as_mut(), self.state_reg) {
204 let serialized = serde_json::to_value(value).ok();
205 if let Some(val) = serialized {
206 Self::set_nested_value_static(&mut registers[state_reg], field_path, val);
207 self.dirty_fields.insert(field_path.to_string());
209 println!(" ✓ Set field '{}' and marked as dirty", field_path);
210 }
211 } else {
212 println!(" ⚠️ Cannot set field '{}': metrics not configured (registers={}, state_reg={:?})",
213 field_path, self.registers.is_some(), self.state_reg);
214 }
215 }
216
217 pub fn increment(&mut self, field_path: &str, amount: i64) {
219 let current = self.get::<i64>(field_path).unwrap_or(0);
220 self.set(field_path, current + amount);
221 }
222
223 fn get_nested_value<'b>(&self, value: &'b serde_json::Value, path: &str) -> Option<&'b serde_json::Value> {
225 let mut current = value;
226 for segment in path.split('.') {
227 current = current.get(segment)?;
228 }
229 Some(current)
230 }
231
232 fn set_nested_value_static(value: &mut serde_json::Value, path: &str, new_value: serde_json::Value) {
234 let segments: Vec<&str> = path.split('.').collect();
235 if segments.is_empty() {
236 return;
237 }
238
239 let mut current = value;
241 for segment in &segments[..segments.len() - 1] {
242 if !current.is_object() {
243 *current = serde_json::json!({});
244 }
245 let obj = current.as_object_mut().unwrap();
246 current = obj.entry(segment.to_string()).or_insert(serde_json::json!({}));
247 }
248
249 if !current.is_object() {
251 *current = serde_json::json!({});
252 }
253 if let Some(obj) = current.as_object_mut() {
254 obj.insert(segments[segments.len() - 1].to_string(), new_value);
255 }
256 }
257
258 pub fn data<T: serde::de::DeserializeOwned>(&self, field: &str) -> Option<T> {
260 self.instruction_data
261 .and_then(|data| data.get(field))
262 .and_then(|v| serde_json::from_value(v.clone()).ok())
263 }
264
265 pub fn timestamp(&self) -> i64 {
267 self.timestamp.unwrap_or(0)
268 }
269
270 pub fn slot(&self) -> Option<u64> {
272 self.slot
273 }
274
275 pub fn signature(&self) -> Option<&str> {
277 self.signature.as_deref()
278 }
279}