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 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#[derive(Clone, Debug)]
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);
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 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#[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 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}