1use super::trace::ExecutionEvent;
5use crate::shim::{
6 econ::TokenAmount, fvm_shared_latest::ActorID, fvm_shared_latest::error::ExitCode,
7};
8use crate::utils::get_size::{GetSize, vec_heap_size_with_fn_helper};
9use cid::Cid;
10use fil_actors_shared::fvm_ipld_amt::{Amt, Amtv0};
11use fvm_ipld_blockstore::Blockstore;
12use fvm_ipld_encoding::RawBytes;
13use fvm_shared2::receipt::Receipt as Receipt_v2;
14use fvm_shared3::event::ActorEvent as ActorEvent_v3;
15use fvm_shared3::event::Entry as Entry_v3;
16use fvm_shared3::event::StampedEvent as StampedEvent_v3;
17pub use fvm_shared3::receipt::Receipt as Receipt_v3;
18use fvm_shared4::event::ActorEvent as ActorEvent_v4;
19use fvm_shared4::event::Entry as Entry_v4;
20use fvm_shared4::event::StampedEvent as StampedEvent_v4;
21use fvm_shared4::receipt::Receipt as Receipt_v4;
22use fvm2::executor::ApplyRet as ApplyRet_v2;
23use fvm3::executor::ApplyRet as ApplyRet_v3;
24use fvm4::executor::ApplyRet as ApplyRet_v4;
25use serde::Serialize;
26use spire_enum::prelude::delegated_enum;
27use std::borrow::Borrow as _;
28
29#[delegated_enum(impl_conversions)]
30#[derive(Clone, Debug)]
31pub enum ApplyRet {
32 V2(ApplyRet_v2),
33 V3(ApplyRet_v3),
34 V4(ApplyRet_v4),
35}
36
37impl ApplyRet {
38 pub fn failure_info(&self) -> Option<String> {
39 delegate_apply_ret!(self => |r| r.failure_info.as_ref().map(|failure| format!("{failure} (RetCode={})", self.msg_receipt().exit_code())))
40 }
41
42 pub fn miner_tip(&self) -> TokenAmount {
43 delegate_apply_ret!(self.miner_tip.borrow().into())
44 }
45
46 pub fn penalty(&self) -> TokenAmount {
47 delegate_apply_ret!(self.penalty.borrow().into())
48 }
49
50 pub fn msg_receipt(&self) -> Receipt {
51 delegate_apply_ret!(self.msg_receipt.clone().into())
52 }
53
54 pub fn refund(&self) -> TokenAmount {
55 delegate_apply_ret!(self.refund.borrow().into())
56 }
57
58 pub fn base_fee_burn(&self) -> TokenAmount {
59 delegate_apply_ret!(self.base_fee_burn.borrow().into())
60 }
61
62 pub fn over_estimation_burn(&self) -> TokenAmount {
63 delegate_apply_ret!(self.over_estimation_burn.borrow().into())
64 }
65
66 pub fn exec_trace(&self) -> Vec<ExecutionEvent> {
67 delegate_apply_ret!(self => |r| r.exec_trace.iter().cloned().map(Into::into).collect())
68 }
69
70 pub fn events(&self) -> Vec<StampedEvent> {
71 match self {
72 ApplyRet::V2(_) => Vec::<StampedEvent>::default(),
73 ApplyRet::V3(v3) => v3.events.iter().cloned().map(Into::into).collect(),
74 ApplyRet::V4(v4) => v4.events.iter().cloned().map(Into::into).collect(),
75 }
76 }
77}
78
79#[delegated_enum(impl_conversions)]
82#[derive(Clone, Debug, Serialize)]
83#[serde(untagged)]
84pub enum Receipt {
85 V2(Receipt_v2),
86 V3(Receipt_v3),
87 V4(Receipt_v4),
88}
89
90impl GetSize for Receipt {
91 fn get_heap_size(&self) -> usize {
92 delegate_receipt!(self.return_data.bytes().get_heap_size())
93 }
94}
95
96impl PartialEq for Receipt {
97 fn eq(&self, other: &Self) -> bool {
98 self.exit_code() == other.exit_code()
99 && self.return_data() == other.return_data()
100 && self.gas_used() == other.gas_used()
101 && self.events_root() == other.events_root()
102 }
103}
104
105impl Receipt {
106 pub fn exit_code(&self) -> ExitCode {
107 match self {
108 Receipt::V2(v2) => ExitCode::new(v2.exit_code.value()),
109 Receipt::V3(v3) => ExitCode::new(v3.exit_code.value()),
110 Receipt::V4(v4) => v4.exit_code,
111 }
112 }
113
114 pub fn return_data(&self) -> RawBytes {
115 delegate_receipt!(self.return_data.clone())
116 }
117
118 pub fn gas_used(&self) -> u64 {
119 match self {
120 Receipt::V2(v2) => v2.gas_used as u64,
121 Receipt::V3(v3) => v3.gas_used,
122 Receipt::V4(v4) => v4.gas_used,
123 }
124 }
125 pub fn events_root(&self) -> Option<Cid> {
126 match self {
127 Receipt::V2(_) => None,
128 Receipt::V3(v3) => v3.events_root,
129 Receipt::V4(v4) => v4.events_root,
130 }
131 }
132
133 pub fn get_receipt(
134 db: &impl Blockstore,
135 receipts: &Cid,
136 i: u64,
137 ) -> anyhow::Result<Option<Self>> {
138 if let Ok(amt) = Amtv0::load(receipts, db)
140 && let Ok(receipts) = amt.get(i)
141 {
142 return Ok(receipts.cloned().map(Receipt::V4));
143 }
144
145 let amt = Amtv0::load(receipts, db)?;
147 let receipts = amt.get(i)?;
148 Ok(receipts.cloned().map(Receipt::V2))
149 }
150
151 pub fn get_receipts(db: &impl Blockstore, receipts_cid: Cid) -> anyhow::Result<Vec<Receipt>> {
152 let mut receipts = Vec::new();
153
154 if let Ok(amt) = Amtv0::<fvm_shared4::receipt::Receipt, _>::load(&receipts_cid, db) {
156 amt.for_each(|_, receipt| {
157 receipts.push(Receipt::V4(receipt.clone()));
158 Ok(())
159 })?;
160 } else {
161 let amt = Amtv0::<fvm_shared2::receipt::Receipt, _>::load(&receipts_cid, db)?;
163 amt.for_each(|_, receipt| {
164 receipts.push(Receipt::V2(receipt.clone()));
165 Ok(())
166 })?;
167 }
168
169 Ok(receipts)
170 }
171}
172
173#[delegated_enum(impl_conversions)]
174#[derive(Clone, Debug)]
175pub enum Entry {
176 V3(Entry_v3),
177 V4(Entry_v4),
178}
179
180impl Entry {
181 #[cfg(test)]
182 pub fn new(
183 flags: crate::shim::fvm_shared_latest::event::Flags,
184 key: String,
185 codec: u64,
186 value: Vec<u8>,
187 ) -> Self {
188 Entry::V4(Entry_v4 {
189 flags,
190 key,
191 codec,
192 value,
193 })
194 }
195
196 pub fn into_parts(self) -> (u64, String, u64, Vec<u8>) {
197 delegate_entry!(self => |e| (e.flags.bits(), e.key, e.codec, e.value))
198 }
199
200 pub fn value(&self) -> &Vec<u8> {
201 delegate_entry!(self.value.borrow())
202 }
203
204 pub fn codec(&self) -> u64 {
205 delegate_entry!(self.codec)
206 }
207
208 pub fn key(&self) -> &String {
209 delegate_entry!(self.key.borrow())
210 }
211}
212
213#[delegated_enum(impl_conversions)]
214#[derive(Clone, Debug)]
215pub enum ActorEvent {
216 V3(ActorEvent_v3),
217 V4(ActorEvent_v4),
218}
219
220impl ActorEvent {
221 pub fn entries(&self) -> Vec<Entry> {
222 delegate_actor_event!(self => |e| e.entries.clone().into_iter().map(Into::into).collect())
223 }
224}
225
226#[delegated_enum(impl_conversions)]
228#[derive(Clone, Debug, Serialize)]
229#[serde(untagged)]
230pub enum StampedEvent {
231 V3(StampedEvent_v3),
232 V4(StampedEvent_v4),
233}
234
235impl GetSize for StampedEvent {
236 fn get_heap_size(&self) -> usize {
237 delegate_stamped_event!(self => |e| vec_heap_size_with_fn_helper(&e.event.entries, |e| {
238 e.key.get_heap_size() + e.value.get_heap_size()
239 }))
240 }
241}
242
243impl StampedEvent {
244 pub fn emitter(&self) -> ActorID {
246 delegate_stamped_event!(self.emitter)
247 }
248
249 pub fn event(&self) -> ActorEvent {
251 delegate_stamped_event!(self.event.clone().into())
252 }
253
254 pub fn get_events<DB: Blockstore>(
257 db: &DB,
258 events_root: &Cid,
259 ) -> anyhow::Result<Vec<StampedEvent>> {
260 let mut events = Vec::new();
261
262 if let Ok(amt) = Amt::<StampedEvent_v4, _>::load(events_root, db) {
264 amt.for_each_cacheless(|_, event| {
265 events.push(StampedEvent::V4(event.clone()));
266 Ok(())
267 })?;
268 } else {
269 let amt = Amt::<StampedEvent_v3, _>::load(events_root, db)?;
271 amt.for_each_cacheless(|_, event| {
272 events.push(StampedEvent::V3(event.clone()));
273 Ok(())
274 })?;
275 }
276
277 Ok(events)
278 }
279}
280
281#[cfg(test)]
282impl quickcheck::Arbitrary for Receipt {
283 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
284 #[derive(derive_quickcheck_arbitrary::Arbitrary, Clone)]
285 enum Helper {
286 V2 {
287 exit_code: u32,
288 return_data: Vec<u8>,
289 gas_used: i64,
290 },
291 V3 {
292 exit_code: u32,
293 return_data: Vec<u8>,
294 gas_used: u64,
295 events_root: Option<::cid::Cid>,
296 },
297 V4 {
298 exit_code: u32,
299 return_data: Vec<u8>,
300 gas_used: u64,
301 events_root: Option<::cid::Cid>,
302 },
303 }
304 match Helper::arbitrary(g) {
305 Helper::V2 {
306 exit_code,
307 return_data,
308 gas_used,
309 } => Self::V2(Receipt_v2 {
310 exit_code: exit_code.into(),
311 return_data: return_data.into(),
312 gas_used,
313 }),
314 Helper::V3 {
315 exit_code,
316 return_data,
317 gas_used,
318 events_root,
319 } => Self::V3(Receipt_v3 {
320 exit_code: exit_code.into(),
321 return_data: return_data.into(),
322 gas_used,
323 events_root,
324 }),
325 Helper::V4 {
326 exit_code,
327 return_data,
328 gas_used,
329 events_root,
330 } => Self::V4(Receipt_v4 {
331 exit_code: exit_code.into(),
332 return_data: return_data.into(),
333 gas_used,
334 events_root,
335 }),
336 }
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use quickcheck_macros::quickcheck;
344
345 #[quickcheck]
346 fn receipt_cbor_serde_serialize(receipt: Receipt) {
347 let encoded = fvm_ipld_encoding::to_vec(&receipt).unwrap();
348 let encoded2 = match &receipt {
349 Receipt::V2(v) => fvm_ipld_encoding::to_vec(v),
350 Receipt::V3(v) => fvm_ipld_encoding::to_vec(v),
351 Receipt::V4(v) => fvm_ipld_encoding::to_vec(v),
352 }
353 .unwrap();
354 assert_eq!(encoded, encoded2);
355 }
356}