1pub(crate) mod diagnostic;
2mod internal;
3pub(crate) mod system_events;
4pub(crate) use internal::{
5 EventError, InternalDiagnosticArg, InternalDiagnosticEvent, InternalEventsBuffer,
6};
7use crate::{
9 num::{i256_from_pieces, u256_from_pieces},
10 xdr::{
11 ClaimableBalanceId, ContractEventBody, ContractEventType, ContractExecutable, Hash, PoolId,
12 PublicKey::PublicKeyTypeEd25519, ScAddress, ScContractInstance, ScVal,
13 },
14 Error, Host, HostError, Val, VecObject,
15};
16pub(crate) use internal::{InternalContractEvent, InternalEvent};
17
18#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct HostEvent {
21 pub event: crate::xdr::ContractEvent,
22 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 .0);
36 write!(f, "{}", strkey)
37 }
38 ScAddress::MuxedAccount(muxed_account) => {
39 let strkey = stellar_strkey::ed25519::MuxedAccount {
40 ed25519: muxed_account.ed25519.0,
41 id: muxed_account.id,
42 };
43 write!(f, "{}", strkey)
44 }
45 ScAddress::ClaimableBalance(ClaimableBalanceId::ClaimableBalanceIdTypeV0(Hash(cb_id))) => {
49 let strkey = stellar_strkey::ClaimableBalance::V0(*cb_id);
50 write!(f, "{}", strkey)
51 }
52 ScAddress::LiquidityPool(PoolId(Hash(pool_id))) => {
53 let strkey = stellar_strkey::LiquidityPool(*pool_id);
54 write!(f, "{}", strkey)
55 }
56 }
57}
58
59fn display_scval(scv: &ScVal, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match scv {
61 ScVal::Bool(v) => write!(f, "{}", v),
62 ScVal::Void => write!(f, "Void"),
63 ScVal::Error(e) => write!(f, "{:?}", Error::from_scerror(e.clone())),
64 ScVal::U32(v) => write!(f, "{}", v),
65 ScVal::I32(v) => write!(f, "{}", v),
66 ScVal::U64(v) => write!(f, "{}", v),
67 ScVal::I64(v) => write!(f, "{}", v),
68 ScVal::Timepoint(v) => write!(f, "TimePoint({})", v.0),
69 ScVal::Duration(v) => write!(f, "Duration({})", v.0),
70 ScVal::U128(v) => write!(f, "{}", u128::from(v)),
71 ScVal::I128(v) => write!(f, "{}", i128::from(v)),
72 ScVal::U256(v) => write!(
73 f,
74 "{}",
75 u256_from_pieces(v.hi_hi, v.hi_lo, v.lo_hi, v.lo_lo)
76 ),
77 ScVal::I256(v) => write!(
78 f,
79 "{}",
80 i256_from_pieces(v.hi_hi, v.hi_lo, v.lo_hi, v.lo_lo)
81 ),
82 ScVal::Bytes(v) => write!(f, "Bytes({})", v.0),
83 ScVal::String(v) => write!(f, "\"{}\"", v.0),
84 ScVal::Symbol(v) => write!(f, "{}", v.0),
85 ScVal::Vec(None) => write!(f, "[]"),
86 ScVal::Vec(Some(vec)) => {
87 write!(f, "[")?;
88 for (i, e) in vec.0.iter().enumerate() {
89 if i != 0 {
90 write!(f, ", ")?;
91 }
92 display_scval(e, f)?;
93 }
94 write!(f, "]")
95 }
96 ScVal::Map(None) => write!(f, "{{}}"),
97 ScVal::Map(Some(pairs)) => {
98 write!(f, "{{")?;
99 for (i, e) in pairs.0.iter().enumerate() {
100 if i != 0 {
101 write!(f, ", ")?;
102 }
103 display_scval(&e.key, f)?;
104 write!(f, ": ")?;
105 display_scval(&e.val, f)?;
106 }
107 write!(f, "}}")
108 }
109 ScVal::Address(addr) => display_address(addr, f),
110 ScVal::LedgerKeyContractInstance => write!(f, "LedgerKeyContractInstance"),
111 ScVal::LedgerKeyNonce(n) => {
112 write!(f, "LedgerKeyNonce({})", n.nonce)
113 }
114 ScVal::ContractInstance(ScContractInstance {
115 executable: ContractExecutable::Wasm(hash),
116 ..
117 }) => {
118 write!(f, "ContractInstance(Wasm({}))", hash)
119 }
120 ScVal::ContractInstance(ScContractInstance {
121 executable: ContractExecutable::StellarAsset,
122 ..
123 }) => write!(f, "ContractInstance(StellarAsset)"),
124 }
125}
126
127impl core::fmt::Display for HostEvent {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 if self.failed_call {
130 write!(f, "[Failed {} Event (not emitted)] ", self.event.type_)?;
131 } else {
132 write!(f, "[{} Event] ", self.event.type_)?;
133 }
134 match &self.event.contract_id {
135 None => (),
136 Some(hash) => {
137 let strkey = stellar_strkey::Contract(hash.0 .0);
138 write!(f, "contract:{}, ", strkey)?
139 }
140 }
141 match &self.event.body {
142 ContractEventBody::V0(ceb) => {
143 write!(f, "topics:[")?;
144
145 let mut is_fn_call = false;
146 for (i, topic) in ceb.topics.iter().enumerate() {
147 if i != 0 {
148 write!(f, ", ")?;
149 }
150
151 if i == 1 && is_fn_call {
155 if let ScVal::Bytes(bytes) = topic {
156 let try_convert_to_hash =
157 TryInto::<[u8; 32]>::try_into(bytes.0.clone());
158 if let Ok(contract_id) = try_convert_to_hash {
159 let strkey = stellar_strkey::Contract(contract_id);
160 write!(f, "{}", strkey)?;
161 continue;
162 }
163 }
164 }
165
166 display_scval(topic, f)?;
167
168 if i == 0 {
169 if let ScVal::Symbol(first_topic_str) = topic {
170 if first_topic_str.0.as_slice() == "fn_call".as_bytes() {
171 is_fn_call = true;
172 }
173 }
174 }
175 }
176 write!(f, "], data:")?;
177 display_scval(&ceb.data, f)
178 }
179 }
180 }
181}
182
183#[test]
184fn host_event_contract_id_is_strkey() {
185 use crate::xdr::{
186 AccountId, ContractEvent, ContractEventBody, ContractEventType, ContractEventV0,
187 ContractId, ExtensionPoint, Hash, PublicKey,
188 };
189 let he = HostEvent {
190 event: ContractEvent {
191 ext: ExtensionPoint::V0,
192 contract_id: Some(ContractId(Hash([0; 32]))),
193 type_: ContractEventType::Diagnostic,
194 body: ContractEventBody::V0(ContractEventV0 {
195 topics: vec![ScVal::Address(ScAddress::Account(AccountId(
196 PublicKey::PublicKeyTypeEd25519([0; 32].into()),
197 )))]
198 .try_into()
199 .unwrap(),
200 data: ScVal::Void,
201 }),
202 },
203 failed_call: false,
204 };
205 assert_eq!(
206 format!("{}", he),
207 "[Diagnostic Event] contract:CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSC4, topics:[GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF], data:Void"
208 );
209}
210
211#[derive(Clone, Debug, Default)]
213pub struct Events(pub Vec<HostEvent>);
214
215impl Host {
216 pub(crate) fn with_events_mut<F, U>(&self, f: F) -> Result<U, HostError>
217 where
218 F: FnOnce(&mut InternalEventsBuffer) -> Result<U, HostError>,
219 {
220 f(&mut *self.try_borrow_events_mut()?)
221 }
222
223 pub fn get_events(&self) -> Result<Events, HostError> {
224 self.try_borrow_events()?.externalize(self)
225 }
226
227 #[cfg(any(test, feature = "testutils"))]
228 pub fn get_contract_events(&self) -> Result<Events, HostError> {
229 let events = self.get_events()?;
230 Ok(Events(
231 events
232 .0
233 .into_iter()
234 .filter(|e| {
235 matches!(
236 e.event.type_,
237 ContractEventType::Contract | ContractEventType::System
238 )
239 })
240 .collect(),
241 ))
242 }
243
244 #[cfg(any(test, feature = "testutils"))]
245 pub fn get_diagnostic_events(&self) -> Result<Events, HostError> {
246 self.try_borrow_events()?.externalize_diagnostics(self)
247 }
248
249 pub(crate) fn record_contract_event(
251 &self,
252 type_: ContractEventType,
253 topics: VecObject,
254 data: Val,
255 ) -> Result<(), HostError> {
256 let ce = InternalContractEvent {
257 type_,
258 contract_id: self.bytesobj_from_internal_contract_id()?,
259 topics,
260 data,
261 };
262 self.with_events_mut(|events| Ok(events.record(InternalEvent::Contract(ce), self)))?
263 }
264}