soroban_env_host_zephyr/host/
lifecycle.rs1use crate::{
2 err,
3 host::{
4 metered_clone::{MeteredAlloc, MeteredClone},
5 metered_write_xdr, ContractReentryMode, CreateContractArgs,
6 },
7 vm::Vm,
8 xdr::{
9 Asset, ContractCodeEntry, ContractDataDurability, ContractExecutable, ContractIdPreimage,
10 ContractIdPreimageFromAddress, ExtensionPoint, Hash, LedgerKey, LedgerKeyContractCode,
11 ScAddress, ScErrorCode, ScErrorType,
12 },
13 AddressObject, BytesObject, Host, HostError, Symbol, TryFromVal,
14};
15use std::rc::Rc;
16
17impl Host {
18 fn create_contract_with_id(
20 &self,
21 contract_id: Hash,
22 contract_executable: ContractExecutable,
23 ) -> Result<(), HostError> {
24 let storage_key = self.contract_instance_ledger_key(&contract_id)?;
25 if self
26 .try_borrow_storage_mut()?
27 .has_with_host(&storage_key, self, None)?
28 {
29 return Err(self.err(
30 ScErrorType::Storage,
31 ScErrorCode::ExistingValue,
32 "contract already exists",
33 &[self
34 .add_host_object(self.scbytes_from_hash(&contract_id)?)?
35 .into()],
36 ));
37 }
38 if let ContractExecutable::Wasm(wasm_hash) = &contract_executable {
42 if !self.wasm_exists(wasm_hash)? {
43 return Err(err!(
44 self,
45 (ScErrorType::Storage, ScErrorCode::MissingValue),
46 "Wasm does not exist",
47 *wasm_hash
48 ));
49 }
50 }
51 self.store_contract_instance(Some(contract_executable), None, contract_id, &storage_key)?;
52 Ok(())
53 }
54
55 fn maybe_initialize_stellar_asset_contract(
56 &self,
57 contract_id: &Hash,
58 id_preimage: &ContractIdPreimage,
59 ) -> Result<(), HostError> {
60 if let ContractIdPreimage::Asset(asset) = id_preimage {
61 let mut asset_bytes: Vec<u8> = Default::default();
62 metered_write_xdr(self.budget_ref(), asset, &mut asset_bytes)?;
63 self.call_n_internal(
64 contract_id,
65 Symbol::try_from_val(self, &"init_asset")?,
66 &[self
67 .add_host_object(self.scbytes_from_vec(asset_bytes)?)?
68 .into()],
69 ContractReentryMode::Prohibited,
70 false,
71 )?;
72 Ok(())
73 } else {
74 Ok(())
75 }
76 }
77
78 pub(crate) fn create_contract_internal(
79 &self,
80 deployer: Option<AddressObject>,
81 args: CreateContractArgs,
82 ) -> Result<AddressObject, HostError> {
83 let has_deployer = deployer.is_some();
84 if has_deployer {
85 self.try_borrow_authorization_manager()?
86 .push_create_contract_host_fn_frame(self, args.metered_clone(self)?)?;
87 }
88 let res = self.create_contract_with_optional_auth(deployer, args);
95 if has_deployer {
96 self.try_borrow_authorization_manager()?
97 .pop_frame(self, None)?;
98 }
99 res
100 }
101
102 fn create_contract_with_optional_auth(
103 &self,
104 deployer: Option<AddressObject>,
105 args: CreateContractArgs,
106 ) -> Result<AddressObject, HostError> {
107 if let Some(deployer_address) = deployer {
108 self.try_borrow_authorization_manager()?.require_auth(
109 self,
110 deployer_address,
111 Default::default(),
112 )?;
113 }
114
115 let id_preimage =
116 self.get_full_contract_id_preimage(args.contract_id_preimage.metered_clone(self)?)?;
117 let hash_id = Hash(self.metered_hash_xdr(&id_preimage)?);
118 self.create_contract_with_id(hash_id.metered_clone(self)?, args.executable)?;
119 self.maybe_initialize_stellar_asset_contract(&hash_id, &args.contract_id_preimage)?;
120 self.add_host_object(ScAddress::Contract(hash_id))
121 }
122
123 pub(crate) fn get_contract_id_hash(
124 &self,
125 deployer: AddressObject,
126 salt: BytesObject,
127 ) -> Result<Hash, HostError> {
128 let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress {
129 address: self.visit_obj(deployer, |addr: &ScAddress| addr.metered_clone(self))?,
130 salt: self.u256_from_bytesobj_input("contract_id_salt", salt)?,
131 });
132
133 let id_preimage =
134 self.get_full_contract_id_preimage(contract_id_preimage.metered_clone(self)?)?;
135 Ok(Hash(self.metered_hash_xdr(&id_preimage)?))
136 }
137
138 pub(crate) fn get_asset_contract_id_hash(&self, asset: Asset) -> Result<Hash, HostError> {
139 let id_preimage = self.get_full_contract_id_preimage(ContractIdPreimage::Asset(asset))?;
140 let id_arr: [u8; 32] = self.metered_hash_xdr(&id_preimage)?;
141 Ok(Hash(id_arr))
142 }
143
144 pub(crate) fn upload_contract_wasm(&self, wasm: Vec<u8>) -> Result<BytesObject, HostError> {
145 let hash_bytes: [u8; 32] = crypto::sha256_hash_from_bytes(wasm.as_slice(), self)?
146 .try_into()
147 .map_err(|_| {
148 self.err(
149 ScErrorType::Value,
150 ScErrorCode::InternalError,
151 "unexpected hash length",
152 &[],
153 )
154 })?;
155
156 let wasm_bytes_m: crate::xdr::BytesM = wasm.try_into().map_err(|_| {
158 self.err(
159 ScErrorType::Value,
160 ScErrorCode::ExceededLimit,
161 "Wasm code is too large",
162 &[],
163 )
164 })?;
165
166 let mut ext = crate::xdr::ContractCodeEntryExt::V0;
167
168 if cfg!(any(test, feature = "testutils")) && wasm_bytes_m.as_slice().is_empty() {
177 } else {
181 let _check_vm = Vm::new(
182 self,
183 Hash(hash_bytes.metered_clone(self)?),
184 wasm_bytes_m.as_slice(),
185 )?;
186 if self.get_ledger_protocol_version()? >= super::ModuleCache::MIN_LEDGER_VERSION {
187 _check_vm.module.cost_inputs.charge_for_parsing(self)?;
191 ext = crate::xdr::ContractCodeEntryExt::V1(crate::xdr::ContractCodeEntryV1 {
192 ext: ExtensionPoint::V0,
193 cost_inputs: crate::vm::ParsedModule::extract_refined_contract_cost_inputs(
194 self,
195 wasm_bytes_m.as_slice(),
196 )?,
197 });
198 }
199 }
200
201 let hash_obj = self.add_host_object(self.scbytes_from_slice(hash_bytes.as_slice())?)?;
202 let code_key = Rc::metered_new(
203 LedgerKey::ContractCode(LedgerKeyContractCode {
204 hash: Hash(hash_bytes.metered_clone(self)?),
205 }),
206 self,
207 )?;
208
209 let mut storage = self.try_borrow_storage_mut()?;
210
211 #[allow(unused_mut)]
213 let mut should_put_contract = !storage.has_with_host(&code_key, self, None)?;
214
215 if !should_put_contract
217 && self.get_ledger_protocol_version()? >= super::ModuleCache::MIN_LEDGER_VERSION
218 {
219 let entry = storage.get_with_host(&code_key, self, None)?;
220 if let crate::xdr::LedgerEntryData::ContractCode(ContractCodeEntry {
221 ext: old_ext,
222 ..
223 }) = &entry.data
224 {
225 should_put_contract = *old_ext != ext;
226 }
227 }
228
229 if should_put_contract {
230 let data = ContractCodeEntry {
231 hash: Hash(hash_bytes),
232 ext,
233 code: wasm_bytes_m,
234 };
235 storage.put_with_host(
236 &code_key,
237 &Host::new_contract_code(self, data)?,
238 Some(self.get_min_live_until_ledger(ContractDataDurability::Persistent)?),
239 self,
240 None,
241 )?;
242 }
243 Ok(hash_obj)
244 }
245}
246
247use super::crypto;
248#[cfg(any(test, feature = "testutils"))]
249use super::ContractFunctionSet;
250
251#[cfg(any(test, feature = "testutils"))]
253impl Host {
254 pub fn register_test_contract(
255 &self,
256 contract_address: AddressObject,
257 contract_fns: Rc<dyn ContractFunctionSet>,
258 ) -> Result<(), HostError> {
259 let contract_id = self.contract_id_from_address(contract_address)?;
260 let instance_key = self.contract_instance_ledger_key(&contract_id)?;
261 let wasm_hash_obj = self.upload_contract_wasm(vec![])?;
262 let wasm_hash = self.hash_from_bytesobj_input("wasm_hash", wasm_hash_obj)?;
263 self.store_contract_instance(
269 Some(ContractExecutable::Wasm(wasm_hash)),
270 None,
271 contract_id.clone(),
272 &instance_key,
273 )?;
274 let mut contracts = self.try_borrow_contracts_mut()?;
275 contracts.insert(contract_id, contract_fns);
276 Ok(())
277 }
278}