1mod config;
8pub mod feature;
9
10use blake2b_simd::Params;
11use dusk_core::abi::{CONTRACT_ID_BYTES, ContractError, ContractId, Metadata};
12use dusk_core::stake::STAKE_CONTRACT;
13use dusk_core::transfer::data::ContractBytecode;
14use dusk_core::transfer::{TRANSFER_CONTRACT, Transaction};
15use piecrust::{CallReceipt, Error, Session};
16use wasmparser::*;
17
18pub use config::Config;
19
20pub fn execute(
68 session: &mut Session,
69 tx: &Transaction,
70 config: &Config,
71) -> Result<CallReceipt<Result<Vec<u8>, ContractError>>, Error> {
72 tx.phoenix_fee_check()
73 .map_err(|e| Error::Panic(e.legacy_to_string()))?;
74
75 if config.phoenix_refund_check {
76 tx.phoenix_refund_check()
77 .map_err(|e| Error::Panic(e.legacy_to_string()))?;
78 }
79
80 tx.deploy_check(
83 config.gas_per_deploy_byte,
84 config.min_deploy_gas_price,
85 config.min_deploy_points,
86 )
87 .map_err(|e| Error::Panic(e.legacy_to_string()))?;
88
89 if let Some(contract_deploy) = tx.deploy() {
90 match (config.disable_wasm32, config.disable_wasm64) {
91 (true, true) => Err(Error::Panic(
92 "contract deployment is not enabled in the VM".into(),
93 )),
94 (true, false) if !is_wasm64(&contract_deploy.bytecode.bytes) => {
95 Err(Error::Panic("32-bit wasm is not enabled in the VM".into()))
96 }
97 (false, true) if is_wasm64(&contract_deploy.bytecode.bytes) => {
98 Err(Error::Panic("64-bit wasm is not enabled in the VM".into()))
99 }
100 _ => Ok(()),
101 }?
102 }
103
104 if config.disable_3rd_party {
105 if let Some(call) = tx.call() {
106 if call.contract != TRANSFER_CONTRACT
107 && call.contract != STAKE_CONTRACT
108 {
109 return Err(Error::Panic(
110 "3rd party contracts are not enabled in the VM".into(),
111 ));
112 }
113 }
114 }
115
116 let blob_min_charge = tx
117 .blob_check(config.gas_per_blob)
118 .map_err(|e| Error::Panic(e.legacy_to_string()))?;
119
120 if blob_min_charge.is_some() && !config.with_blob {
121 return Err(Error::Panic(
122 "Blob processing is not enabled in the VM".into(),
123 ));
124 }
125
126 if config.with_public_sender {
127 let _ = session
128 .set_meta(Metadata::PUBLIC_SENDER, tx.moonlight_sender().copied());
129 }
130
131 let stripped_tx = tx.blob_to_memo().or(tx.strip_off_bytecode());
132
133 let mut receipt = session
136 .call::<_, Result<Vec<u8>, ContractError>>(
137 TRANSFER_CONTRACT,
138 "spend_and_execute",
139 stripped_tx.as_ref().unwrap_or(tx),
140 tx.gas_limit(),
141 )
142 .inspect_err(|_| {
143 clear_session(session, config);
144 })?;
145
146 contract_deploy(session, tx, config, &mut receipt);
148
149 if let Some(blob_min_charge) = blob_min_charge {
152 if receipt.gas_spent < blob_min_charge {
153 receipt.gas_spent = blob_min_charge;
154 }
155 }
156
157 if receipt.data.is_err() {
159 receipt.gas_spent = receipt.gas_limit;
160 }
161
162 let refund_receipt = session
166 .call::<_, ()>(
167 TRANSFER_CONTRACT,
168 "refund",
169 &receipt.gas_spent,
170 u64::MAX,
171 )
172 .expect("Refunding must succeed");
173
174 receipt.events.extend(refund_receipt.events);
175
176 clear_session(session, config);
177
178 Ok(receipt)
179}
180
181fn is_wasm64(bytecode: &[u8]) -> bool {
182 for payload in Parser::new(0).parse_all(bytecode).flatten() {
183 if let Payload::MemorySection(section) = payload {
184 return section
185 .into_iter()
186 .any(|memory| memory.is_ok_and(|m| m.memory64));
187 }
188 }
189 false
190}
191
192fn clear_session(session: &mut Session, config: &Config) {
193 if config.with_public_sender {
194 let _ = session.remove_meta(Metadata::PUBLIC_SENDER);
195 }
196}
197
198fn contract_deploy(
208 session: &mut Session,
209 tx: &Transaction,
210 config: &Config,
211 receipt: &mut CallReceipt<Result<Vec<u8>, ContractError>>,
212) {
213 if let Some(deploy) = tx.deploy() {
214 let gas_per_deploy_byte = config.gas_per_deploy_byte;
215 let min_deploy_points = config.min_deploy_points;
216
217 let gas_left = tx.gas_limit() - receipt.gas_spent;
218 if receipt.data.is_ok() {
219 let deploy_charge =
220 tx.deploy_charge(gas_per_deploy_byte, min_deploy_points);
221 let min_gas_limit = receipt.gas_spent + deploy_charge;
222 if gas_left < min_gas_limit {
223 receipt.data = Err(ContractError::OutOfGas);
224 } else if !verify_bytecode_hash(&deploy.bytecode) {
225 receipt.data = Err(ContractError::Panic(
226 "failed bytecode hash check".into(),
227 ))
228 } else {
229 let result = session.deploy_raw(
230 Some(gen_contract_id(
231 &deploy.bytecode.bytes,
232 deploy.nonce,
233 &deploy.owner,
234 )),
235 deploy.bytecode.bytes.as_slice(),
236 deploy.init_args.clone(),
237 deploy.owner.clone(),
238 gas_left,
239 );
240 match result {
241 Ok(_) => receipt.gas_spent += deploy_charge,
243 Err(err) => {
244 let msg = format!("failed deployment: {err:?}");
245 receipt.data = Err(ContractError::Panic(msg))
246 }
247 }
248 }
249 }
250 }
251}
252
253fn verify_bytecode_hash(bytecode: &ContractBytecode) -> bool {
255 let computed: [u8; 32] = blake3::hash(bytecode.bytes.as_slice()).into();
256
257 bytecode.hash == computed
258}
259
260pub fn gen_contract_id(
276 bytes: impl AsRef<[u8]>,
277 nonce: u64,
278 owner: impl AsRef<[u8]>,
279) -> ContractId {
280 let mut hasher = Params::new().hash_length(CONTRACT_ID_BYTES).to_state();
281 hasher.update(bytes.as_ref());
282 hasher.update(&nonce.to_le_bytes()[..]);
283 hasher.update(owner.as_ref());
284 let hash_bytes: [u8; CONTRACT_ID_BYTES] = hasher
285 .finalize()
286 .as_bytes()
287 .try_into()
288 .expect("the hash result is exactly `CONTRACT_ID_BYTES` long");
289 ContractId::from_bytes(hash_bytes)
290}
291
292#[cfg(test)]
293mod tests {
294 use alloc::vec;
295
296 use ff as _;
299 use hex as _;
300 use once_cell as _;
301 use rand::rngs::StdRng;
302 use rand::{RngCore, SeedableRng};
303
304 use super::*;
305
306 #[test]
307 fn test_gen_contract_id() {
308 let mut rng = StdRng::seed_from_u64(42);
309
310 let mut bytes = vec![0; 1000];
311 rng.fill_bytes(&mut bytes);
312
313 let nonce = rng.next_u64();
314
315 let mut owner = vec![0, 100];
316 rng.fill_bytes(&mut owner);
317
318 let contract_id =
319 gen_contract_id(bytes.as_slice(), nonce, owner.as_slice());
320
321 assert_eq!(
322 contract_id.as_bytes(),
323 [
324 45, 168, 182, 39, 119, 137, 168, 140, 114, 21, 120, 158, 34,
325 126, 244, 221, 151, 72, 109, 178, 82, 229, 84, 128, 92, 123,
326 135, 74, 23, 224, 119, 133
327 ]
328 );
329 }
330}