stellar_base/operations/
invoke_host_function.rs1use crate::crypto::MuxedAccount;
2use crate::error::{Error, Result};
3use crate::operations::Operation;
4use crate::xdr;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct InvokeHostFunctionOperation {
27 source_account: Option<MuxedAccount>,
28 host_function: xdr::HostFunction,
29 auth: Vec<xdr::SorobanAuthorizationEntry>,
30}
31
32#[derive(Debug, Default)]
34pub struct InvokeHostFunctionOperationBuilder {
35 source_account: Option<MuxedAccount>,
36 host_function: Option<xdr::HostFunction>,
37 auth: Vec<xdr::SorobanAuthorizationEntry>,
38}
39
40impl InvokeHostFunctionOperation {
41 pub fn source_account(&self) -> &Option<MuxedAccount> {
43 &self.source_account
44 }
45
46 pub fn source_account_mut(&mut self) -> &mut Option<MuxedAccount> {
48 &mut self.source_account
49 }
50
51 pub fn host_function(&self) -> &xdr::HostFunction {
53 &self.host_function
54 }
55
56 pub fn host_function_mut(&mut self) -> &mut xdr::HostFunction {
58 &mut self.host_function
59 }
60
61 pub fn auth(&self) -> &[xdr::SorobanAuthorizationEntry] {
63 &self.auth
64 }
65
66 pub fn auth_mut(&mut self) -> &mut Vec<xdr::SorobanAuthorizationEntry> {
68 &mut self.auth
69 }
70
71 pub fn to_xdr_operation_body(&self) -> Result<xdr::OperationBody> {
73 let auth_xdr = self.auth.clone().try_into().map_err(|_| Error::XdrError)?;
74 let inner = xdr::InvokeHostFunctionOp {
75 host_function: self.host_function.clone(),
76 auth: auth_xdr,
77 };
78 Ok(xdr::OperationBody::InvokeHostFunction(inner))
79 }
80
81 pub fn from_xdr_operation_body(
83 source_account: Option<MuxedAccount>,
84 x: &xdr::InvokeHostFunctionOp,
85 ) -> Result<InvokeHostFunctionOperation> {
86 let auth: Vec<xdr::SorobanAuthorizationEntry> = x.auth.iter().cloned().collect();
87 Ok(InvokeHostFunctionOperation {
88 source_account,
89 host_function: x.host_function.clone(),
90 auth,
91 })
92 }
93}
94
95impl InvokeHostFunctionOperationBuilder {
96 pub fn new() -> Self {
98 Default::default()
99 }
100
101 pub fn with_source_account<S>(mut self, source: S) -> Self
103 where
104 S: Into<MuxedAccount>,
105 {
106 self.source_account = Some(source.into());
107 self
108 }
109
110 pub fn with_host_function(mut self, hf: xdr::HostFunction) -> Self {
112 self.host_function = Some(hf);
113 self
114 }
115
116 pub fn with_auth(mut self, auth: Vec<xdr::SorobanAuthorizationEntry>) -> Self {
118 self.auth = auth;
119 self
120 }
121
122 pub fn add_auth_entry(mut self, entry: xdr::SorobanAuthorizationEntry) -> Self {
124 self.auth.push(entry);
125 self
126 }
127
128 pub fn with_invoke_contract(
130 mut self,
131 contract_address: xdr::ScAddress,
132 function_name: xdr::ScSymbol,
133 args: Vec<xdr::ScVal>,
134 ) -> Self {
135 let args_xdr = args.try_into().unwrap_or_else(|_| xdr::VecM::default());
136 let invoke_args = xdr::InvokeContractArgs {
137 contract_address,
138 function_name,
139 args: args_xdr,
140 };
141 self.host_function = Some(xdr::HostFunction::InvokeContract(invoke_args));
142 self
143 }
144
145 pub fn with_upload_wasm(mut self, wasm: xdr::BytesM) -> Self {
147 self.host_function = Some(xdr::HostFunction::UploadContractWasm(wasm));
148 self
149 }
150
151 pub fn with_create_contract(
153 mut self,
154 contract_id_preimage: xdr::ContractIdPreimage,
155 executable: xdr::ContractExecutable,
156 ) -> Self {
157 let args = xdr::CreateContractArgs {
158 contract_id_preimage,
159 executable,
160 };
161 self.host_function = Some(xdr::HostFunction::CreateContract(args));
162 self
163 }
164
165 pub fn with_create_contract_v2(
167 mut self,
168 contract_id_preimage: xdr::ContractIdPreimage,
169 executable: xdr::ContractExecutable,
170 constructor_args: Vec<xdr::ScVal>,
171 ) -> Self {
172 let ctor_args_xdr = constructor_args
173 .try_into()
174 .unwrap_or_else(|_| xdr::VecM::default());
175 let args = xdr::CreateContractArgsV2 {
176 contract_id_preimage,
177 executable,
178 constructor_args: ctor_args_xdr,
179 };
180 self.host_function = Some(xdr::HostFunction::CreateContractV2(args));
181 self
182 }
183
184 pub fn build(self) -> Result<Operation> {
186 let host_function = self.host_function.ok_or_else(|| {
187 Error::InvalidOperation("missing host function for invoke host function".to_string())
188 })?;
189
190 if self.auth.len() > 10_000 {
192 return Err(Error::InvalidOperation(
193 "too many authorization entries for invoke host function".to_string(),
194 ));
195 }
196
197 Ok(Operation::InvokeHostFunction(InvokeHostFunctionOperation {
198 source_account: self.source_account,
199 host_function,
200 auth: self.auth,
201 }))
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use crate::operations::Operation;
209
210 use crate::xdr::{ContractId, Hash, ScAddress, ScSymbol, XDRDeserialize, XDRSerialize};
211
212 #[test]
213 fn test_invoke_host_function_invoke_contract_roundtrip() {
214 let contract_hash: [u8; 32] = [1u8; 32];
216 let addr = ScAddress::Contract(ContractId(Hash(contract_hash)));
217 let symbol: ScSymbol = "ping".to_string().try_into().unwrap();
220
221 let op = InvokeHostFunctionOperationBuilder::new()
222 .with_invoke_contract(addr, symbol, vec![])
223 .build()
224 .unwrap();
225
226 let encoded = op.xdr_base64().unwrap();
227 let decoded = Operation::from_xdr_base64(&encoded).unwrap();
228 assert_eq!(op, decoded);
229 if let Operation::InvokeHostFunction(inner) = decoded {
230 assert!(matches!(
231 inner.host_function(),
232 xdr::HostFunction::InvokeContract(_)
233 ));
234 } else {
235 panic!("expected invoke host function operation");
236 }
237 }
238}