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";
33 self.reverse_lookups
34 .get_mut(lookup_name)
35 .and_then(|t| t.lookup(pda_address))
36 }
37
38 pub fn slot(&self) -> u64 {
39 self.slot
40 }
41
42 pub fn signature(&self) -> &str {
43 &self.signature
44 }
45}
46
47pub enum KeyResolution {
49 Found(String),
51
52 QueueUntil(&'static [u8]),
55
56 Skip,
58}
59
60pub struct InstructionContext<'a> {
62 pub(crate) accounts: HashMap<String, String>,
63 #[allow(dead_code)]
64 pub(crate) state_id: u32,
65 pub(crate) reverse_lookup_tx: &'a mut dyn ReverseLookupUpdater,
66 pub(crate) pending_updates: Vec<crate::vm::PendingAccountUpdate>,
67 pub(crate) registers: Option<&'a mut Vec<crate::vm::RegisterValue>>,
68 pub(crate) state_reg: Option<crate::vm::Register>,
69 #[allow(dead_code)]
70 pub(crate) compiled_paths: Option<&'a HashMap<String, crate::metrics_context::CompiledPath>>,
71 pub(crate) instruction_data: Option<&'a serde_json::Value>,
72 pub(crate) slot: Option<u64>,
73 pub(crate) signature: Option<String>,
74 pub(crate) timestamp: Option<i64>,
75 pub(crate) dirty_tracker: crate::vm::DirtyTracker,
76}
77
78pub trait ReverseLookupUpdater {
79 fn update(
80 &mut self,
81 pda_address: String,
82 seed_value: String,
83 ) -> Vec<crate::vm::PendingAccountUpdate>;
84 fn flush_pending(&mut self, pda_address: &str) -> Vec<crate::vm::PendingAccountUpdate>;
85}
86
87impl<'a> InstructionContext<'a> {
88 pub fn new(
89 accounts: HashMap<String, String>,
90 state_id: u32,
91 reverse_lookup_tx: &'a mut dyn ReverseLookupUpdater,
92 ) -> Self {
93 Self {
94 accounts,
95 state_id,
96 reverse_lookup_tx,
97 pending_updates: Vec::new(),
98 registers: None,
99 state_reg: None,
100 compiled_paths: None,
101 instruction_data: None,
102 slot: None,
103 signature: None,
104 timestamp: None,
105 dirty_tracker: crate::vm::DirtyTracker::new(),
106 }
107 }
108
109 #[allow(clippy::too_many_arguments)]
110 pub fn with_metrics(
111 accounts: HashMap<String, String>,
112 state_id: u32,
113 reverse_lookup_tx: &'a mut dyn ReverseLookupUpdater,
114 registers: &'a mut Vec<crate::vm::RegisterValue>,
115 state_reg: crate::vm::Register,
116 compiled_paths: &'a HashMap<String, crate::metrics_context::CompiledPath>,
117 instruction_data: &'a serde_json::Value,
118 slot: Option<u64>,
119 signature: Option<String>,
120 timestamp: i64,
121 ) -> Self {
122 Self {
123 accounts,
124 state_id,
125 reverse_lookup_tx,
126 pending_updates: Vec::new(),
127 registers: Some(registers),
128 state_reg: Some(state_reg),
129 compiled_paths: Some(compiled_paths),
130 instruction_data: Some(instruction_data),
131 slot,
132 signature,
133 timestamp: Some(timestamp),
134 dirty_tracker: crate::vm::DirtyTracker::new(),
135 }
136 }
137
138 pub fn account(&self, name: &str) -> Option<String> {
140 self.accounts.get(name).cloned()
141 }
142
143 pub fn register_pda_reverse_lookup(&mut self, pda_address: &str, seed_value: &str) {
149 let pending = self
150 .reverse_lookup_tx
151 .update(pda_address.to_string(), seed_value.to_string());
152 self.pending_updates.extend(pending);
153 }
154
155 pub fn take_pending_updates(&mut self) -> Vec<crate::vm::PendingAccountUpdate> {
160 std::mem::take(&mut self.pending_updates)
161 }
162
163 pub fn dirty_tracker(&self) -> &crate::vm::DirtyTracker {
164 &self.dirty_tracker
165 }
166
167 pub fn dirty_tracker_mut(&mut self) -> &mut crate::vm::DirtyTracker {
168 &mut self.dirty_tracker
169 }
170
171 pub fn state_value(&self) -> Option<&serde_json::Value> {
173 if let (Some(registers), Some(state_reg)) = (self.registers.as_ref(), self.state_reg) {
174 Some(®isters[state_reg])
175 } else {
176 None
177 }
178 }
179
180 pub fn get<T: serde::de::DeserializeOwned>(&self, field_path: &str) -> Option<T> {
183 if let (Some(registers), Some(state_reg)) = (self.registers.as_ref(), self.state_reg) {
184 let state = ®isters[state_reg];
185 self.get_nested_value(state, field_path)
186 .and_then(|v| serde_json::from_value(v.clone()).ok())
187 } else {
188 None
189 }
190 }
191
192 pub fn set<T: serde::Serialize>(&mut self, field_path: &str, value: T) {
193 if let (Some(registers), Some(state_reg)) = (self.registers.as_mut(), self.state_reg) {
194 let serialized = serde_json::to_value(value).ok();
195 if let Some(val) = serialized {
196 Self::set_nested_value_static(&mut registers[state_reg], field_path, val);
197 self.dirty_tracker.mark_replaced(field_path);
198 println!(" ✓ Set field '{}' and marked as dirty", field_path);
199 }
200 } else {
201 println!(" ⚠️ Cannot set field '{}': metrics not configured (registers={}, state_reg={:?})",
202 field_path, self.registers.is_some(), self.state_reg);
203 }
204 }
205
206 pub fn increment(&mut self, field_path: &str, amount: i64) {
207 let current = self.get::<i64>(field_path).unwrap_or(0);
208 self.set(field_path, current + amount);
209 }
210
211 pub fn append<T: serde::Serialize>(&mut self, field_path: &str, value: T) {
212 if let (Some(registers), Some(state_reg)) = (self.registers.as_mut(), self.state_reg) {
213 let serialized = serde_json::to_value(&value).ok();
214 if let Some(val) = serialized {
215 Self::append_to_array_static(&mut registers[state_reg], field_path, val.clone());
216 self.dirty_tracker.mark_appended(field_path, val);
217 println!(
218 " ✓ Appended to '{}' and marked as appended",
219 field_path
220 );
221 }
222 } else {
223 println!(
224 " ⚠️ Cannot append to '{}': metrics not configured",
225 field_path
226 );
227 }
228 }
229
230 fn append_to_array_static(
231 value: &mut serde_json::Value,
232 path: &str,
233 new_value: serde_json::Value,
234 ) {
235 let segments: Vec<&str> = path.split('.').collect();
236 if segments.is_empty() {
237 return;
238 }
239
240 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
247 .entry(segment.to_string())
248 .or_insert(serde_json::json!({}));
249 }
250
251 let last_segment = segments[segments.len() - 1];
252 if !current.is_object() {
253 *current = serde_json::json!({});
254 }
255 let obj = current.as_object_mut().unwrap();
256 let arr = obj
257 .entry(last_segment.to_string())
258 .or_insert_with(|| serde_json::json!([]));
259 if let Some(arr) = arr.as_array_mut() {
260 arr.push(new_value);
261 }
262 }
263
264 fn get_nested_value<'b>(
265 &self,
266 value: &'b serde_json::Value,
267 path: &str,
268 ) -> Option<&'b serde_json::Value> {
269 let mut current = value;
270 for segment in path.split('.') {
271 current = current.get(segment)?;
272 }
273 Some(current)
274 }
275
276 fn set_nested_value_static(
277 value: &mut serde_json::Value,
278 path: &str,
279 new_value: serde_json::Value,
280 ) {
281 let segments: Vec<&str> = path.split('.').collect();
282 if segments.is_empty() {
283 return;
284 }
285
286 let mut current = value;
287 for segment in &segments[..segments.len() - 1] {
288 if !current.is_object() {
289 *current = serde_json::json!({});
290 }
291 let obj = current.as_object_mut().unwrap();
292 current = obj
293 .entry(segment.to_string())
294 .or_insert(serde_json::json!({}));
295 }
296
297 if !current.is_object() {
298 *current = serde_json::json!({});
299 }
300 if let Some(obj) = current.as_object_mut() {
301 obj.insert(segments[segments.len() - 1].to_string(), new_value);
302 }
303 }
304
305 pub fn data<T: serde::de::DeserializeOwned>(&self, field: &str) -> Option<T> {
307 self.instruction_data
308 .and_then(|data| data.get(field))
309 .and_then(|v| serde_json::from_value(v.clone()).ok())
310 }
311
312 pub fn timestamp(&self) -> i64 {
314 self.timestamp.unwrap_or(0)
315 }
316
317 pub fn slot(&self) -> Option<u64> {
319 self.slot
320 }
321
322 pub fn signature(&self) -> Option<&str> {
324 self.signature.as_deref()
325 }
326}