soroban_env_host/events/
mod.rs

1pub(crate) mod diagnostic;
2mod internal;
3pub(crate) mod system_events;
4pub(crate) use internal::{
5    EventError, InternalDiagnosticArg, InternalDiagnosticEvent, InternalEventsBuffer,
6};
7// expose them as pub use for benches
8use crate::{
9    num::{i256_from_pieces, u256_from_pieces},
10    xdr::{
11        ContractEventBody, ContractEventType, ContractExecutable, PublicKey::PublicKeyTypeEd25519,
12        ScAddress, ScContractInstance, ScVal,
13    },
14    Error, Host, HostError, Val, VecObject,
15};
16pub(crate) use internal::{InternalContractEvent, InternalEvent};
17
18/// The external representation of a host event.
19#[derive(Clone, Debug)]
20pub struct HostEvent {
21    pub event: crate::xdr::ContractEvent,
22    // failed_call keeps track of if the call this event was emitted in failed
23    pub failed_call: bool,
24}
25
26fn display_address(addr: &ScAddress, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27    match addr {
28        ScAddress::Account(acct) => match &acct.0 {
29            PublicKeyTypeEd25519(e) => {
30                let strkey = stellar_strkey::ed25519::PublicKey(e.0);
31                write!(f, "{}", strkey)
32            }
33        },
34        ScAddress::Contract(hash) => {
35            let strkey = stellar_strkey::Contract(hash.0);
36            write!(f, "{}", strkey)
37        }
38    }
39}
40
41fn display_scval(scv: &ScVal, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42    match scv {
43        ScVal::Bool(v) => write!(f, "{}", v),
44        ScVal::Void => write!(f, "Void"),
45        ScVal::Error(e) => write!(f, "{:?}", Error::from_scerror(e.clone())),
46        ScVal::U32(v) => write!(f, "{}", v),
47        ScVal::I32(v) => write!(f, "{}", v),
48        ScVal::U64(v) => write!(f, "{}", v),
49        ScVal::I64(v) => write!(f, "{}", v),
50        ScVal::Timepoint(v) => write!(f, "TimePoint({})", v.0),
51        ScVal::Duration(v) => write!(f, "Duration({})", v.0),
52        ScVal::U128(v) => write!(f, "{}", u128::from(v)),
53        ScVal::I128(v) => write!(f, "{}", i128::from(v)),
54        ScVal::U256(v) => write!(
55            f,
56            "{}",
57            u256_from_pieces(v.hi_hi, v.hi_lo, v.lo_hi, v.lo_lo)
58        ),
59        ScVal::I256(v) => write!(
60            f,
61            "{}",
62            i256_from_pieces(v.hi_hi, v.hi_lo, v.lo_hi, v.lo_lo)
63        ),
64        ScVal::Bytes(v) => write!(f, "Bytes({})", v.0),
65        ScVal::String(v) => write!(f, "\"{}\"", v.0),
66        ScVal::Symbol(v) => write!(f, "{}", v.0),
67        ScVal::Vec(None) => write!(f, "[]"),
68        ScVal::Vec(Some(vec)) => {
69            write!(f, "[")?;
70            for (i, e) in vec.0.iter().enumerate() {
71                if i != 0 {
72                    write!(f, ", ")?;
73                }
74                display_scval(e, f)?;
75            }
76            write!(f, "]")
77        }
78        ScVal::Map(None) => write!(f, "{{}}"),
79        ScVal::Map(Some(pairs)) => {
80            write!(f, "{{")?;
81            for (i, e) in pairs.0.iter().enumerate() {
82                if i != 0 {
83                    write!(f, ", ")?;
84                }
85                display_scval(&e.key, f)?;
86                write!(f, ": ")?;
87                display_scval(&e.val, f)?;
88            }
89            write!(f, "}}")
90        }
91        ScVal::Address(addr) => display_address(addr, f),
92        ScVal::LedgerKeyContractInstance => write!(f, "LedgerKeyContractInstance"),
93        ScVal::LedgerKeyNonce(n) => {
94            write!(f, "LedgerKeyNonce({})", n.nonce)
95        }
96        ScVal::ContractInstance(ScContractInstance {
97            executable: ContractExecutable::Wasm(hash),
98            ..
99        }) => {
100            write!(f, "ContractInstance(Wasm({}))", hash)
101        }
102        ScVal::ContractInstance(ScContractInstance {
103            executable: ContractExecutable::StellarAsset,
104            ..
105        }) => write!(f, "ContractInstance(StellarAsset)"),
106    }
107}
108
109impl core::fmt::Display for HostEvent {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        if self.failed_call {
112            write!(f, "[Failed {} Event (not emitted)] ", self.event.type_)?;
113        } else {
114            write!(f, "[{} Event] ", self.event.type_)?;
115        }
116        match &self.event.contract_id {
117            None => (),
118            Some(hash) => {
119                let strkey = stellar_strkey::Contract(hash.0);
120                write!(f, "contract:{}, ", strkey)?
121            }
122        }
123        match &self.event.body {
124            ContractEventBody::V0(ceb) => {
125                write!(f, "topics:[")?;
126
127                let mut is_fn_call = false;
128                for (i, topic) in ceb.topics.iter().enumerate() {
129                    if i != 0 {
130                        write!(f, ", ")?;
131                    }
132
133                    // The second topic of the fn_call event is the contract id as ScBytes,
134                    // but we want to display it as a C key instead, so this block
135                    // tries to deduce if the event is the fn_call event.
136                    if i == 1 && is_fn_call {
137                        if let ScVal::Bytes(bytes) = topic {
138                            let try_convert_to_hash =
139                                TryInto::<[u8; 32]>::try_into(bytes.0.clone());
140                            if let Ok(contract_id) = try_convert_to_hash {
141                                let strkey = stellar_strkey::Contract(contract_id);
142                                write!(f, "{}", strkey)?;
143                                continue;
144                            }
145                        }
146                    }
147
148                    display_scval(topic, f)?;
149
150                    if i == 0 {
151                        if let ScVal::Symbol(first_topic_str) = topic {
152                            if first_topic_str.0.as_slice() == "fn_call".as_bytes() {
153                                is_fn_call = true;
154                            }
155                        }
156                    }
157                }
158                write!(f, "], data:")?;
159                display_scval(&ceb.data, f)
160            }
161        }
162    }
163}
164
165#[test]
166fn host_event_contract_id_is_strkey() {
167    use crate::xdr::{
168        AccountId, ContractEvent, ContractEventBody, ContractEventType, ContractEventV0,
169        ExtensionPoint, Hash, PublicKey,
170    };
171    let he = HostEvent {
172        event: ContractEvent {
173            ext: ExtensionPoint::V0,
174            contract_id: Some(Hash([0; 32])),
175            type_: ContractEventType::Diagnostic,
176            body: ContractEventBody::V0(ContractEventV0 {
177                topics: vec![ScVal::Address(ScAddress::Account(AccountId(
178                    PublicKey::PublicKeyTypeEd25519([0; 32].into()),
179                )))]
180                .try_into()
181                .unwrap(),
182                data: ScVal::Void,
183            }),
184        },
185        failed_call: false,
186    };
187    assert_eq!(
188        format!("{}", he),
189        "[Diagnostic Event] contract:CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4, topics:[GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF], data:Void"
190    );
191}
192
193/// The external representation of events in the chronological order.
194#[derive(Clone, Debug, Default)]
195pub struct Events(pub Vec<HostEvent>);
196
197impl Host {
198    pub(crate) fn with_events_mut<F, U>(&self, f: F) -> Result<U, HostError>
199    where
200        F: FnOnce(&mut InternalEventsBuffer) -> Result<U, HostError>,
201    {
202        f(&mut *self.try_borrow_events_mut()?)
203    }
204
205    pub fn get_events(&self) -> Result<Events, HostError> {
206        self.try_borrow_events()?.externalize(self)
207    }
208
209    #[cfg(any(test, feature = "testutils"))]
210    pub fn get_diagnostic_events(&self) -> Result<Events, HostError> {
211        self.try_borrow_events()?.externalize_diagnostics(self)
212    }
213
214    // Records a contract event.
215    pub(crate) fn record_contract_event(
216        &self,
217        type_: ContractEventType,
218        topics: VecObject,
219        data: Val,
220    ) -> Result<(), HostError> {
221        let ce = InternalContractEvent {
222            type_,
223            contract_id: self.bytesobj_from_internal_contract_id()?,
224            topics,
225            data,
226        };
227        self.with_events_mut(|events| Ok(events.record(InternalEvent::Contract(ce), self)))?
228    }
229}