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::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;
26
27#[derive(Clone, Debug)]
28pub enum ApplyRet {
29 V2(Box<ApplyRet_v2>),
30 V3(Box<ApplyRet_v3>),
31 V4(Box<ApplyRet_v4>),
32}
33
34impl From<ApplyRet_v2> for ApplyRet {
35 fn from(other: ApplyRet_v2) -> Self {
36 ApplyRet::V2(Box::new(other))
37 }
38}
39
40impl From<ApplyRet_v3> for ApplyRet {
41 fn from(other: ApplyRet_v3) -> Self {
42 ApplyRet::V3(Box::new(other))
43 }
44}
45
46impl From<ApplyRet_v4> for ApplyRet {
47 fn from(other: ApplyRet_v4) -> Self {
48 ApplyRet::V4(Box::new(other))
49 }
50}
51
52impl ApplyRet {
53 pub fn failure_info(&self) -> Option<String> {
54 match self {
55 ApplyRet::V2(v2) => v2.failure_info.as_ref().map(|failure| failure.to_string()),
56 ApplyRet::V3(v3) => v3.failure_info.as_ref().map(|failure| failure.to_string()),
57 ApplyRet::V4(v4) => v4.failure_info.as_ref().map(|failure| failure.to_string()),
58 }
59 .map(|e| format!("{} (RetCode={})", e, self.msg_receipt().exit_code()))
60 }
61
62 pub fn miner_tip(&self) -> TokenAmount {
63 match self {
64 ApplyRet::V2(v2) => (&v2.miner_tip).into(),
65 ApplyRet::V3(v3) => (&v3.miner_tip).into(),
66 ApplyRet::V4(v4) => (&v4.miner_tip).into(),
67 }
68 }
69
70 pub fn penalty(&self) -> TokenAmount {
71 match self {
72 ApplyRet::V2(v2) => (&v2.penalty).into(),
73 ApplyRet::V3(v3) => (&v3.penalty).into(),
74 ApplyRet::V4(v4) => (&v4.penalty).into(),
75 }
76 }
77
78 pub fn msg_receipt(&self) -> Receipt {
79 match self {
80 ApplyRet::V2(v2) => Receipt::V2(v2.msg_receipt.clone()),
81 ApplyRet::V3(v3) => Receipt::V3(v3.msg_receipt.clone()),
82 ApplyRet::V4(v4) => Receipt::V4(v4.msg_receipt.clone()),
83 }
84 }
85
86 pub fn refund(&self) -> TokenAmount {
87 match self {
88 ApplyRet::V2(v2) => (&v2.refund).into(),
89 ApplyRet::V3(v3) => (&v3.refund).into(),
90 ApplyRet::V4(v4) => (&v4.refund).into(),
91 }
92 }
93
94 pub fn base_fee_burn(&self) -> TokenAmount {
95 match self {
96 ApplyRet::V2(v2) => (&v2.base_fee_burn).into(),
97 ApplyRet::V3(v3) => (&v3.base_fee_burn).into(),
98 ApplyRet::V4(v4) => (&v4.base_fee_burn).into(),
99 }
100 }
101
102 pub fn over_estimation_burn(&self) -> TokenAmount {
103 match self {
104 ApplyRet::V2(v2) => (&v2.over_estimation_burn).into(),
105 ApplyRet::V3(v3) => (&v3.over_estimation_burn).into(),
106 ApplyRet::V4(v4) => (&v4.over_estimation_burn).into(),
107 }
108 }
109
110 pub fn exec_trace(&self) -> Vec<ExecutionEvent> {
111 match self {
112 ApplyRet::V2(v2) => v2.exec_trace.iter().cloned().map(Into::into).collect(),
113 ApplyRet::V3(v3) => v3.exec_trace.iter().cloned().map(Into::into).collect(),
114 ApplyRet::V4(v4) => v4.exec_trace.iter().cloned().map(Into::into).collect(),
115 }
116 }
117
118 pub fn events(&self) -> Vec<StampedEvent> {
119 match self {
120 ApplyRet::V2(_) => Vec::<StampedEvent>::default(),
121 ApplyRet::V3(v3) => v3.events.iter().cloned().map(Into::into).collect(),
122 ApplyRet::V4(v4) => v4.events.iter().cloned().map(Into::into).collect(),
123 }
124 }
125}
126
127#[derive(Clone, Debug, Serialize)]
130#[serde(untagged)]
131pub enum Receipt {
132 V2(Receipt_v2),
133 V3(Receipt_v3),
134 V4(Receipt_v4),
135}
136
137impl GetSize for Receipt {
138 fn get_heap_size(&self) -> usize {
139 match self {
140 Self::V2(r) => r.return_data.bytes().get_heap_size(),
141 Self::V3(r) => r.return_data.bytes().get_heap_size(),
142 Self::V4(r) => r.return_data.bytes().get_heap_size(),
143 }
144 }
145}
146
147impl PartialEq for Receipt {
148 fn eq(&self, other: &Self) -> bool {
149 self.exit_code() == other.exit_code()
150 && self.return_data() == other.return_data()
151 && self.gas_used() == other.gas_used()
152 && self.events_root() == other.events_root()
153 }
154}
155
156impl Receipt {
157 pub fn exit_code(&self) -> ExitCode {
158 match self {
159 Receipt::V2(v2) => ExitCode::new(v2.exit_code.value()),
160 Receipt::V3(v3) => ExitCode::new(v3.exit_code.value()),
161 Receipt::V4(v4) => v4.exit_code,
162 }
163 }
164
165 pub fn return_data(&self) -> RawBytes {
166 match self {
167 Receipt::V2(v2) => v2.return_data.clone(),
168 Receipt::V3(v3) => v3.return_data.clone(),
169 Receipt::V4(v4) => v4.return_data.clone(),
170 }
171 }
172
173 pub fn gas_used(&self) -> u64 {
174 match self {
175 Receipt::V2(v2) => v2.gas_used as u64,
176 Receipt::V3(v3) => v3.gas_used,
177 Receipt::V4(v4) => v4.gas_used,
178 }
179 }
180 pub fn events_root(&self) -> Option<Cid> {
181 match self {
182 Receipt::V2(_) => None,
183 Receipt::V3(v3) => v3.events_root,
184 Receipt::V4(v4) => v4.events_root,
185 }
186 }
187
188 pub fn get_receipt(
189 db: &impl Blockstore,
190 receipts: &Cid,
191 i: u64,
192 ) -> anyhow::Result<Option<Self>> {
193 if let Ok(amt) = Amtv0::load(receipts, db)
195 && let Ok(receipts) = amt.get(i)
196 {
197 return Ok(receipts.cloned().map(Receipt::V4));
198 }
199
200 let amt = Amtv0::load(receipts, db)?;
202 let receipts = amt.get(i)?;
203 Ok(receipts.cloned().map(Receipt::V2))
204 }
205
206 pub fn get_receipts(db: &impl Blockstore, receipts_cid: Cid) -> anyhow::Result<Vec<Receipt>> {
207 let mut receipts = Vec::new();
208
209 if let Ok(amt) = Amtv0::<fvm_shared4::receipt::Receipt, _>::load(&receipts_cid, db) {
211 amt.for_each(|_, receipt| {
212 receipts.push(Receipt::V4(receipt.clone()));
213 Ok(())
214 })?;
215 } else {
216 let amt = Amtv0::<fvm_shared2::receipt::Receipt, _>::load(&receipts_cid, db)?;
218 amt.for_each(|_, receipt| {
219 receipts.push(Receipt::V2(receipt.clone()));
220 Ok(())
221 })?;
222 }
223
224 Ok(receipts)
225 }
226}
227
228impl From<Receipt_v3> for Receipt {
229 fn from(other: Receipt_v3) -> Self {
230 Receipt::V3(other)
231 }
232}
233
234#[derive(Clone, Debug)]
235pub enum Entry {
236 V3(Entry_v3),
237 V4(Entry_v4),
238}
239
240impl From<Entry_v3> for Entry {
241 fn from(other: Entry_v3) -> Self {
242 Self::V3(other)
243 }
244}
245
246impl From<Entry_v4> for Entry {
247 fn from(other: Entry_v4) -> Self {
248 Self::V4(other)
249 }
250}
251
252impl Entry {
253 #[cfg(test)]
254 pub fn new(
255 flags: crate::shim::fvm_shared_latest::event::Flags,
256 key: String,
257 codec: u64,
258 value: Vec<u8>,
259 ) -> Self {
260 Entry::V4(Entry_v4 {
261 flags,
262 key,
263 codec,
264 value,
265 })
266 }
267
268 pub fn into_parts(self) -> (u64, String, u64, Vec<u8>) {
269 match self {
270 Self::V3(v3) => {
271 let Entry_v3 {
272 flags,
273 key,
274 codec,
275 value,
276 } = v3;
277 (flags.bits(), key, codec, value)
278 }
279 Self::V4(v4) => {
280 let Entry_v4 {
281 flags,
282 key,
283 codec,
284 value,
285 } = v4;
286 (flags.bits(), key, codec, value)
287 }
288 }
289 }
290
291 pub fn value(&self) -> &Vec<u8> {
292 match self {
293 Self::V3(v3) => &v3.value,
294 Self::V4(v4) => &v4.value,
295 }
296 }
297
298 pub fn codec(&self) -> u64 {
299 match self {
300 Self::V3(v3) => v3.codec,
301 Self::V4(v4) => v4.codec,
302 }
303 }
304
305 pub fn key(&self) -> &String {
306 match self {
307 Self::V3(v3) => &v3.key,
308 Self::V4(v4) => &v4.key,
309 }
310 }
311}
312
313#[derive(Clone, Debug)]
314pub enum ActorEvent {
315 V3(ActorEvent_v3),
316 V4(ActorEvent_v4),
317}
318
319impl From<ActorEvent_v3> for ActorEvent {
320 fn from(other: ActorEvent_v3) -> Self {
321 ActorEvent::V3(other)
322 }
323}
324
325impl From<ActorEvent_v4> for ActorEvent {
326 fn from(other: ActorEvent_v4) -> Self {
327 ActorEvent::V4(other)
328 }
329}
330
331impl ActorEvent {
332 pub fn entries(&self) -> Vec<Entry> {
333 match self {
334 Self::V3(v3) => v3.entries.clone().into_iter().map(Into::into).collect(),
335 Self::V4(v4) => v4.entries.clone().into_iter().map(Into::into).collect(),
336 }
337 }
338}
339
340#[derive(Clone, Debug, Serialize)]
342pub enum StampedEvent {
343 V3(StampedEvent_v3),
344 V4(StampedEvent_v4),
345}
346
347impl GetSize for StampedEvent {
348 fn get_heap_size(&self) -> usize {
349 match self {
350 Self::V3(e) => vec_heap_size_with_fn_helper(&e.event.entries, |e| {
351 e.key.get_heap_size() + e.value.get_heap_size()
352 }),
353 Self::V4(e) => vec_heap_size_with_fn_helper(&e.event.entries, |e| {
354 e.key.get_heap_size() + e.value.get_heap_size()
355 }),
356 }
357 }
358}
359
360impl From<StampedEvent_v3> for StampedEvent {
361 fn from(other: StampedEvent_v3) -> Self {
362 StampedEvent::V3(other)
363 }
364}
365
366impl From<StampedEvent_v4> for StampedEvent {
367 fn from(other: StampedEvent_v4) -> Self {
368 StampedEvent::V4(other)
369 }
370}
371
372impl StampedEvent {
373 pub fn emitter(&self) -> ActorID {
375 match self {
376 Self::V3(v3) => v3.emitter,
377 Self::V4(v4) => v4.emitter,
378 }
379 }
380
381 pub fn event(&self) -> ActorEvent {
383 match self {
384 Self::V3(v3) => v3.event.clone().into(),
385 Self::V4(v4) => v4.event.clone().into(),
386 }
387 }
388}
389
390#[cfg(test)]
391impl quickcheck::Arbitrary for Receipt {
392 fn arbitrary(g: &mut quickcheck::Gen) -> Self {
393 #[derive(derive_quickcheck_arbitrary::Arbitrary, Clone)]
394 enum Helper {
395 V2 {
396 exit_code: u32,
397 return_data: Vec<u8>,
398 gas_used: i64,
399 },
400 V3 {
401 exit_code: u32,
402 return_data: Vec<u8>,
403 gas_used: u64,
404 events_root: Option<::cid::Cid>,
405 },
406 V4 {
407 exit_code: u32,
408 return_data: Vec<u8>,
409 gas_used: u64,
410 events_root: Option<::cid::Cid>,
411 },
412 }
413 match Helper::arbitrary(g) {
414 Helper::V2 {
415 exit_code,
416 return_data,
417 gas_used,
418 } => Self::V2(Receipt_v2 {
419 exit_code: exit_code.into(),
420 return_data: return_data.into(),
421 gas_used,
422 }),
423 Helper::V3 {
424 exit_code,
425 return_data,
426 gas_used,
427 events_root,
428 } => Self::V3(Receipt_v3 {
429 exit_code: exit_code.into(),
430 return_data: return_data.into(),
431 gas_used,
432 events_root,
433 }),
434 Helper::V4 {
435 exit_code,
436 return_data,
437 gas_used,
438 events_root,
439 } => Self::V4(Receipt_v4 {
440 exit_code: exit_code.into(),
441 return_data: return_data.into(),
442 gas_used,
443 events_root,
444 }),
445 }
446 }
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452 use quickcheck_macros::quickcheck;
453
454 #[quickcheck]
455 fn receipt_cbor_serde_serialize(receipt: Receipt) {
456 let encoded = fvm_ipld_encoding::to_vec(&receipt).unwrap();
457 let encoded2 = match &receipt {
458 Receipt::V2(v) => fvm_ipld_encoding::to_vec(v),
459 Receipt::V3(v) => fvm_ipld_encoding::to_vec(v),
460 Receipt::V4(v) => fvm_ipld_encoding::to_vec(v),
461 }
462 .unwrap();
463 assert_eq!(encoded, encoded2);
464 }
465}