1#![allow(non_snake_case)]
2#![allow(unused_imports)]
3
4use thruput_xdr::auth_claim::*;
5use thruput_xdr::auth_token::*;
6use thruput_xdr::contract::*;
7use thruput_xdr::event::*;
8use thruput_xdr::invoke_flag::*;
9use thruput_xdr::network::*;
10use thruput_xdr::syscall_error::*;
11use thruput_xdr::transaction::*;
12use thruput_xdr::types::*;
13use thruput_xdr::view::*;
14
15#[rustfmt::skip]
16pub mod view_gateway;
17pub use view_gateway::*;
18
19#[cfg(test)]
20mod tests {
21 use super::*;
22 use std::sync::{Arc, Mutex};
23 use thruput_rpc_api::{
24 ProgVers, ProgramError, RpcError, RpcResponse, RpcSender,
25 };
26 use xdr_common::{BytesM, StringM, VecM};
27
28 const VIEW_GATEWAY_PROG_VERS: ProgVers = ProgVers::new(0x56475759, 1);
29
30 const TEST_PROTOCOL: ProtocolCommit = *b"hogp/0123456789abcde";
32
33 struct ScriptedViewGateway;
34
35 #[allow(refining_impl_trait)]
36 impl ViewGateway_ViewGateway1 for ScriptedViewGateway {
37 async fn viewgateway_view_batch(
38 &self,
39 request: ViewGatewayBatchQuery,
40 ) -> Result<ViewGatewayBatchResult, ProgramError> {
41 assert_eq!(request.calls.0, vec![query(), query()]);
42 assert_eq!(request.min_height, Some(BlockHeight::from(42)));
43 assert_eq!(request.budget_millis, 1000);
44 assert_eq!(request.max_response_bytes, 1_048_576);
45 Ok(ViewGatewayBatchResult::ViewGatewayBatchOk {
46 payload: ViewGatewayBatchPayload {
47 height: BlockHeight::from(42),
48 results: VecM(vec![
49 TransactionViewResult {
50 gas_consumed: 17,
51 output: InvocationFrameOutput::Return {
52 return_buffer: BytesM(vec![1]),
53 },
54 events: VecM(vec![]),
55 },
56 TransactionViewResult {
57 gas_consumed: 23,
58 output: InvocationFrameOutput::Fault,
59 events: VecM(vec![]),
60 },
61 ]),
62 },
63 })
64 }
65
66 async fn viewgateway_intro(
67 &self,
68 protocol: ProtocolCommit,
69 ) -> Result<ViewGatewayIntroOutcome, ProgramError> {
70 assert_eq!(protocol, TEST_PROTOCOL);
71 Ok(ViewGatewayIntroOutcome::ViewGatewayIntroSuccess)
72 }
73
74 async fn viewgateway_view(
75 &self,
76 query: TransactionViewQuery,
77 ) -> Result<ViewGatewayResult, ProgramError> {
78 assert_eq!(
79 query.invocation.invoked_address,
80 ContractAddress::from([7u8; 32])
81 );
82 assert_eq!(*query.invocation.entrypoint, [8u8; 8]);
83 assert_eq!(query.invocation.gas_limit, 123);
84 assert_eq!(query.invocation.calldata.0, vec![9, 10]);
85 assert_eq!(
86 query.parent_address,
87 Some(ContractAddress::from([6u8; 32]))
88 );
89 assert_eq!(query.forwarded_tokens.0.len(), 0);
90 assert_eq!(
91 query.invoke_flags,
92 InvokeFlag::ReadOnly.0.cast_unsigned()
93 );
94
95 Ok(ok(InvocationFrameOutput::Return {
96 return_buffer: BytesM(vec![1, 2, 3]),
97 }))
98 }
99 }
100
101 struct LocalSender {
102 handler: Arc<dyn thruput_rpc_api::RpcHandler>,
103 calls: Arc<Mutex<Vec<(ProgVers, u32)>>>,
104 }
105
106 #[async_trait::async_trait]
107 impl RpcSender for LocalSender {
108 fn call_buf(&self) -> thruput_rpc_api::CallBuf {
109 thruput_rpc_api::CallBuf::new()
110 }
111
112 async fn send_call(
113 &self,
114 progvers: ProgVers,
115 proc: u32,
116 call: thruput_rpc_api::CallBuf,
117 ) -> Result<RpcResponse, RpcError> {
118 self.calls
119 .lock()
120 .expect("calls lock poisoned")
121 .push((progvers, proc));
122 if progvers != self.handler.prog_vers() {
123 return Err(RpcError::ProgUnavail);
124 }
125 let payload = self.handler.dispatch(proc, call.payload()).await?;
126 Ok(RpcResponse::from_payload(payload))
127 }
128 }
129
130 fn round_trip(result: ViewGatewayResult) {
131 let encoded =
132 xdrs::txs::encode(&result).expect("encode ViewGatewayResult");
133 let decoded: ViewGatewayResult =
134 xdrs::txs::decode(&encoded).expect("decode ViewGatewayResult");
135 assert_eq!(decoded, result);
136 }
137
138 fn ok(output: InvocationFrameOutput) -> ViewGatewayResult {
139 ViewGatewayResult::ViewGatewayOk {
140 payload: ViewGatewayPayload {
141 height: BlockHeight::from(42),
142 output,
143 gas_consumed: 77,
144 events: VecM(vec![]),
145 },
146 }
147 }
148
149 fn query() -> TransactionViewQuery {
150 TransactionViewQuery {
151 invocation: Invocation {
152 invoked_address: ContractAddress::from([7u8; 32]),
153 entrypoint: ContractEntrypointID::from([8u8; 8]),
154 gas_limit: 123,
155 calldata: BytesM(vec![9, 10]),
156 },
157 parent_address: Some(ContractAddress::from([6u8; 32])),
158 auth_claim: AuthClaim::kSourceAddressAuthData {
159 source_address_claim: AuthClaim_source_address_claim {
160 allowed_keys: 0,
161 required_weight: 0,
162 },
163 },
164 forwarded_tokens: VecM(vec![]),
165 invoke_flags: InvokeFlag::ReadOnly.0.cast_unsigned(),
166 }
167 }
168
169 #[test]
170 fn view_gateway_result_round_trips_return_abort_and_fault() {
171 round_trip(ok(InvocationFrameOutput::Return {
172 return_buffer: BytesM(vec![1, 2, 3]),
173 }));
174 round_trip(ok(InvocationFrameOutput::Abort {
175 abort_buffer: BytesM(vec![4, 5, 6]),
176 }));
177 round_trip(ok(InvocationFrameOutput::Fault));
178 }
179
180 #[test]
181 fn view_gateway_result_round_trips_service_failures() {
182 round_trip(ViewGatewayResult::ViewGatewayAbandoned);
183 round_trip(ViewGatewayResult::ViewGatewayInternalError);
184 }
185
186 #[test]
187 fn view_gateway_result_round_trips_unknown_default_outcome() {
188 round_trip(ViewGatewayResult::Default { outcome: 99 });
189 }
190
191 #[test]
192 fn view_gateway_types_have_json_schema() {
193 let _ = schemars::schema_for!(ViewGatewayPayload);
194 let _ = schemars::schema_for!(ViewGatewayOutcome);
195 let _ = schemars::schema_for!(ViewGatewayResult);
196 let _ = schemars::schema_for!(ViewGatewayIntroOutcome);
197 let _ = schemars::schema_for!(ViewGatewayBatchQuery);
198 let _ = schemars::schema_for!(ViewGatewayBatchResult);
199 }
200
201 #[cfg(feature = "serde")]
202 #[test]
203 fn view_gateway_result_serde_round_trips() {
204 let result = ok(InvocationFrameOutput::Return {
205 return_buffer: BytesM(vec![1, 2, 3]),
206 });
207 let json = serde_json::to_value(&result)
208 .expect("serialize ViewGatewayResult");
209 let decoded: ViewGatewayResult = serde_json::from_value(json)
210 .expect("deserialize ViewGatewayResult");
211 assert_eq!(decoded, result);
212 }
213
214 #[tokio::test]
215 async fn generated_client_dispatches_intro_view_and_batch() {
216 let calls = Arc::new(Mutex::new(Vec::new()));
217 let handler = Arc::new(ViewGateway_ViewGateway1_Dispatcher::new(
218 Box::new(ScriptedViewGateway),
219 ));
220 let client =
221 ViewGateway_ViewGateway1_Client::new(Arc::new(LocalSender {
222 handler,
223 calls: calls.clone(),
224 }));
225
226 let intro = client
227 .viewgateway_intro(&TEST_PROTOCOL)
228 .await
229 .expect("intro should round-trip through dispatcher");
230 assert_eq!(intro, ViewGatewayIntroOutcome::ViewGatewayIntroSuccess);
231
232 let result = client
233 .viewgateway_view(&query())
234 .await
235 .expect("view should round-trip through dispatcher");
236
237 match result {
238 ViewGatewayResult::ViewGatewayOk { payload } => {
239 assert_eq!(*payload.height, 42);
240 assert_eq!(
241 payload.output,
242 InvocationFrameOutput::Return {
243 return_buffer: BytesM(vec![1, 2, 3]),
244 }
245 );
246 }
247 other => panic!("unexpected view result: {other:?}"),
248 }
249
250 let batch = ViewGatewayBatchQuery {
251 calls: VecM(vec![query(), query()]),
252 min_height: Some(BlockHeight::from(42)),
253 budget_millis: 1000,
254 max_response_bytes: 1_048_576,
255 };
256 let result = client
257 .viewgateway_view_batch(&batch)
258 .await
259 .expect("batch should round-trip through dispatcher");
260 assert_eq!(
261 result,
262 ScriptedViewGateway
263 .viewgateway_view_batch(batch)
264 .await
265 .unwrap()
266 );
267 assert_eq!(
268 *calls.lock().expect("calls lock poisoned"),
269 vec![
270 (VIEW_GATEWAY_PROG_VERS, 0),
271 (VIEW_GATEWAY_PROG_VERS, 1),
272 (VIEW_GATEWAY_PROG_VERS, 2)
273 ]
274 );
275 }
276
277 #[test]
278 fn batch_failures_preserve_height_details_and_unknown_outcomes() {
279 let mut failures =
280 vec![ViewGatewayBatchResult::ViewGatewayBatchHeightUnavailable {
281 height_unavailable: ViewGatewayHeightUnavailable {
282 requested: BlockHeight::from(50),
283 observed: BlockHeight::from(42),
284 },
285 }];
286 failures.extend(
287 [1, 3, 4, 5, 6, 99]
288 .map(|outcome| ViewGatewayBatchResult::Default { outcome }),
289 );
290 for result in failures {
291 let encoded = xdrs::txs::encode(&result).unwrap();
292 let decoded: ViewGatewayBatchResult =
293 xdrs::txs::decode(&encoded).unwrap();
294 assert_eq!(decoded, result);
295 }
296 }
297}