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