radix_engine/vm/wasm_runtime/
scrypto_runtime.rs

1use crate::errors::InvokeError;
2use crate::errors::RuntimeError;
3use crate::internal_prelude::*;
4use crate::vm::wasm::*;
5use crate::vm::ScryptoVmVersion;
6use radix_engine_interface::api::actor_api::EventFlags;
7use radix_engine_interface::api::field_api::LockFlags;
8use radix_engine_interface::api::key_value_store_api::KeyValueStoreDataSchema;
9use radix_engine_interface::api::{ActorRefHandle, AttachedModuleId, FieldValue, SystemApi};
10use radix_engine_interface::types::ClientCostingEntry;
11use radix_engine_interface::types::Level;
12use radix_engine_profiling_derive::trace_resources;
13use sbor::rust::vec::Vec;
14
15/// A shim between SystemAPI and WASM, with buffer capability.
16pub struct ScryptoRuntime<'y, Y: SystemApi<RuntimeError>> {
17    api: &'y mut Y,
18    buffers: IndexMap<BufferId, Vec<u8>>,
19    next_buffer_id: BufferId,
20    package_address: PackageAddress,
21    export_name: String,
22    wasm_execution_units_buffer: u32,
23    scrypto_vm_version: ScryptoVmVersion,
24    wasm_execution_units_base: u32,
25}
26
27impl<'y, Y: SystemApi<RuntimeError>> ScryptoRuntime<'y, Y> {
28    pub fn new(
29        api: &'y mut Y,
30        package_address: PackageAddress,
31        export_name: String,
32        scrypto_vm_version: ScryptoVmVersion,
33    ) -> Self {
34        let wasm_execution_units_base = if scrypto_vm_version < ScryptoVmVersion::cuttlefish() {
35            0
36        } else {
37            // Add 28,000 base units to make sure the we do not undercharge for WASM execution,
38            // which might lead to system exploitation.
39            // This is especially important in corner-cases such as `costing::spin_loop_v2` benchmark.
40            // less frequently.
41            28000
42        };
43
44        ScryptoRuntime {
45            api,
46            buffers: index_map_new(),
47            next_buffer_id: 0,
48            package_address,
49            export_name,
50            wasm_execution_units_buffer: 0,
51            scrypto_vm_version,
52            wasm_execution_units_base,
53        }
54    }
55    pub fn parse_blueprint_id(
56        package_address: Vec<u8>,
57        blueprint_name: Vec<u8>,
58    ) -> Result<(PackageAddress, String), InvokeError<WasmRuntimeError>> {
59        let package_address = PackageAddress::try_from(package_address.as_slice())
60            .map_err(|_| WasmRuntimeError::InvalidPackageAddress)?;
61        let blueprint_name =
62            String::from_utf8(blueprint_name).map_err(|_| WasmRuntimeError::InvalidString)?;
63        Ok((package_address, blueprint_name))
64    }
65
66    #[cold]
67    fn consume_wasm_execution_exceeding_buffer(
68        &mut self,
69        n: u32,
70    ) -> Result<(), InvokeError<WasmRuntimeError>> {
71        assert!(n > self.wasm_execution_units_buffer);
72        let n_remaining_after_buffer_used = n - self.wasm_execution_units_buffer;
73        let amount_to_request =
74            n_remaining_after_buffer_used.saturating_add(WASM_EXECUTION_COST_UNITS_BUFFER);
75
76        self.api
77            .consume_cost_units(ClientCostingEntry::RunWasmCode {
78                package_address: &self.package_address,
79                export_name: &self.export_name,
80                wasm_execution_units: amount_to_request,
81            })
82            .map_err(InvokeError::downstream)?;
83        self.wasm_execution_units_buffer = amount_to_request - n_remaining_after_buffer_used;
84        Ok(())
85    }
86}
87
88impl<'y, Y: SystemApi<RuntimeError>> WasmRuntime for ScryptoRuntime<'y, Y> {
89    fn allocate_buffer(
90        &mut self,
91        buffer: Vec<u8>,
92    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
93        assert!(buffer.len() <= 0xffffffff);
94
95        let max_number_of_buffers = match self.scrypto_vm_version {
96            ScryptoVmVersion::V1_0 | ScryptoVmVersion::V1_1 => 32,
97            // Practically speaking, there is little gain of keeping multiple buffers open before
98            // [multi-value](https://github.com/WebAssembly/multi-value/blob/master/proposals/multi-value/Overview.md) is supported and used.
99            // We reduce it to `4` so that the amount of memory that a transaction can consume is reduced, which is beneficial for parallel execution.
100            ScryptoVmVersion::V1_2 => 4,
101        };
102        if self.buffers.len() >= max_number_of_buffers {
103            return Err(InvokeError::SelfError(WasmRuntimeError::TooManyBuffers));
104        }
105
106        let id = self.next_buffer_id;
107        let len = buffer.len();
108
109        self.buffers.insert(id, buffer);
110        self.next_buffer_id += 1;
111
112        Ok(Buffer::new(id, len as u32))
113    }
114
115    fn buffer_consume(
116        &mut self,
117        buffer_id: BufferId,
118    ) -> Result<Vec<u8>, InvokeError<WasmRuntimeError>> {
119        self.buffers
120            .swap_remove(&buffer_id)
121            .ok_or(InvokeError::SelfError(WasmRuntimeError::BufferNotFound(
122                buffer_id,
123            )))
124    }
125
126    fn object_call(
127        &mut self,
128        receiver: Vec<u8>,
129        ident: Vec<u8>,
130        args: Vec<u8>,
131    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
132        let receiver = NodeId(
133            TryInto::<[u8; NodeId::LENGTH]>::try_into(receiver.as_ref())
134                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
135        );
136        let ident = String::from_utf8(ident).map_err(|_| WasmRuntimeError::InvalidString)?;
137        let return_data = self.api.call_method(&receiver, ident.as_str(), args)?;
138
139        self.allocate_buffer(return_data)
140    }
141
142    fn object_call_module(
143        &mut self,
144        receiver: Vec<u8>,
145        module_id: u32,
146        ident: Vec<u8>,
147        args: Vec<u8>,
148    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
149        let receiver = NodeId(
150            TryInto::<[u8; NodeId::LENGTH]>::try_into(receiver.as_ref())
151                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
152        );
153        let ident = String::from_utf8(ident).map_err(|_| WasmRuntimeError::InvalidString)?;
154        let module_id = u8::try_from(module_id)
155            .ok()
156            .and_then(AttachedModuleId::from_repr)
157            .ok_or(WasmRuntimeError::InvalidAttachedModuleId(module_id))?;
158
159        let return_data =
160            self.api
161                .call_module_method(&receiver, module_id, ident.as_str(), args)?;
162
163        self.allocate_buffer(return_data)
164    }
165
166    fn object_call_direct(
167        &mut self,
168        receiver: Vec<u8>,
169        ident: Vec<u8>,
170        args: Vec<u8>,
171    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
172        let receiver = NodeId(
173            TryInto::<[u8; NodeId::LENGTH]>::try_into(receiver.as_ref())
174                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
175        );
176        let ident = String::from_utf8(ident).map_err(|_| WasmRuntimeError::InvalidString)?;
177        let return_data = self
178            .api
179            .call_direct_access_method(&receiver, ident.as_str(), args)?;
180
181        self.allocate_buffer(return_data)
182    }
183
184    fn blueprint_call(
185        &mut self,
186        package_address: Vec<u8>,
187        blueprint_name: Vec<u8>,
188        function_ident: Vec<u8>,
189        args: Vec<u8>,
190    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
191        let (package_address, blueprint_name) =
192            Self::parse_blueprint_id(package_address, blueprint_name)?;
193        let function_ident =
194            String::from_utf8(function_ident).map_err(|_| WasmRuntimeError::InvalidString)?;
195
196        let return_data = self.api.call_function(
197            package_address,
198            blueprint_name.as_str(),
199            &function_ident,
200            args,
201        )?;
202
203        self.allocate_buffer(return_data)
204    }
205
206    fn object_new(
207        &mut self,
208        blueprint_name: Vec<u8>,
209        object_states: Vec<u8>,
210    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
211        let blueprint_name =
212            String::from_utf8(blueprint_name).map_err(|_| WasmRuntimeError::InvalidString)?;
213        let object_states = scrypto_decode::<IndexMap<u8, FieldValue>>(&object_states)
214            .map_err(WasmRuntimeError::InvalidObjectStates)?;
215
216        let component_id = self
217            .api
218            .new_simple_object(blueprint_name.as_ref(), object_states)?;
219
220        self.allocate_buffer(component_id.to_vec())
221    }
222
223    fn address_allocate(
224        &mut self,
225        package_address: Vec<u8>,
226        blueprint_name: Vec<u8>,
227    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
228        let (package_address, blueprint_name) =
229            Self::parse_blueprint_id(package_address, blueprint_name)?;
230
231        let address_reservation_and_address = self.api.allocate_global_address(BlueprintId {
232            package_address,
233            blueprint_name,
234        })?;
235        let encoded = scrypto_encode(&address_reservation_and_address)
236            .expect("Failed to encode object address");
237
238        self.allocate_buffer(encoded)
239    }
240
241    fn address_get_reservation_address(
242        &mut self,
243        node_id: Vec<u8>,
244    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
245        let node_id = NodeId(
246            TryInto::<[u8; NodeId::LENGTH]>::try_into(node_id.as_ref())
247                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
248        );
249
250        let address = self.api.get_reservation_address(&node_id)?;
251        let address_encoded = scrypto_encode(&address).expect("Failed to encode address");
252
253        self.allocate_buffer(address_encoded)
254    }
255
256    fn globalize_object(
257        &mut self,
258        node_id: Vec<u8>,
259        modules: Vec<u8>,
260        address_reservation: Vec<u8>,
261    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
262        let node_id = NodeId(
263            TryInto::<[u8; NodeId::LENGTH]>::try_into(node_id.as_ref())
264                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
265        );
266        let modules = scrypto_decode::<IndexMap<AttachedModuleId, NodeId>>(&modules)
267            .map_err(WasmRuntimeError::InvalidModules)?;
268        let address_reservation =
269            scrypto_decode::<Option<GlobalAddressReservation>>(&address_reservation)
270                .map_err(|_| WasmRuntimeError::InvalidGlobalAddressReservation)?;
271
272        let address = self.api.globalize(node_id, modules, address_reservation)?;
273
274        self.allocate_buffer(address.to_vec())
275    }
276
277    fn key_value_store_new(
278        &mut self,
279        schema: Vec<u8>,
280    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
281        let schema = scrypto_decode::<KeyValueStoreDataSchema>(&schema)
282            .map_err(WasmRuntimeError::InvalidKeyValueStoreSchema)?;
283
284        let key_value_store_id = self.api.key_value_store_new(schema)?;
285
286        self.allocate_buffer(key_value_store_id.to_vec())
287    }
288
289    fn key_value_store_open_entry(
290        &mut self,
291        node_id: Vec<u8>,
292        key: Vec<u8>,
293        flags: u32,
294    ) -> Result<SubstateHandle, InvokeError<WasmRuntimeError>> {
295        let node_id = NodeId(
296            TryInto::<[u8; NodeId::LENGTH]>::try_into(node_id.as_ref())
297                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
298        );
299
300        let flags = LockFlags::from_bits(flags).ok_or(WasmRuntimeError::InvalidLockFlags)?;
301        let handle = self.api.key_value_store_open_entry(&node_id, &key, flags)?;
302
303        Ok(handle)
304    }
305
306    fn key_value_entry_get(
307        &mut self,
308        handle: u32,
309    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
310        let value = self.api.key_value_entry_get(handle)?;
311        self.allocate_buffer(value)
312    }
313
314    fn key_value_entry_set(
315        &mut self,
316        handle: u32,
317        data: Vec<u8>,
318    ) -> Result<(), InvokeError<WasmRuntimeError>> {
319        self.api.key_value_entry_set(handle, data)?;
320        Ok(())
321    }
322
323    fn key_value_entry_remove(
324        &mut self,
325        handle: u32,
326    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
327        let value = self.api.key_value_entry_remove(handle)?;
328        self.allocate_buffer(value)
329    }
330
331    fn key_value_entry_close(&mut self, handle: u32) -> Result<(), InvokeError<WasmRuntimeError>> {
332        self.api.key_value_entry_close(handle)?;
333        Ok(())
334    }
335
336    fn key_value_store_remove_entry(
337        &mut self,
338        node_id: Vec<u8>,
339        key: Vec<u8>,
340    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
341        let node_id = NodeId(
342            TryInto::<[u8; NodeId::LENGTH]>::try_into(node_id.as_ref())
343                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
344        );
345        let rtn = self.api.key_value_store_remove_entry(&node_id, &key)?;
346        self.allocate_buffer(rtn)
347    }
348
349    fn actor_open_field(
350        &mut self,
351        object_handle: u32,
352        field: u8,
353        flags: u32,
354    ) -> Result<SubstateHandle, InvokeError<WasmRuntimeError>> {
355        let flags = LockFlags::from_bits(flags).ok_or(WasmRuntimeError::InvalidLockFlags)?;
356        let handle = self.api.actor_open_field(object_handle, field, flags)?;
357
358        Ok(handle)
359    }
360
361    fn field_entry_read(
362        &mut self,
363        handle: SubstateHandle,
364    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
365        let substate = self.api.field_read(handle)?;
366
367        self.allocate_buffer(substate)
368    }
369
370    fn field_entry_write(
371        &mut self,
372        handle: SubstateHandle,
373        data: Vec<u8>,
374    ) -> Result<(), InvokeError<WasmRuntimeError>> {
375        self.api.field_write(handle, data)?;
376
377        Ok(())
378    }
379
380    fn field_entry_close(
381        &mut self,
382        handle: SubstateHandle,
383    ) -> Result<(), InvokeError<WasmRuntimeError>> {
384        self.api.field_close(handle)?;
385
386        Ok(())
387    }
388
389    fn actor_get_node_id(
390        &mut self,
391        actor_ref_handle: ActorRefHandle,
392    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
393        let node_id = self.api.actor_get_node_id(actor_ref_handle)?;
394
395        self.allocate_buffer(node_id.0.to_vec())
396    }
397
398    fn actor_get_package_address(&mut self) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
399        let blueprint_id = self.api.actor_get_blueprint_id()?;
400
401        self.allocate_buffer(blueprint_id.package_address.to_vec())
402    }
403
404    fn actor_get_blueprint_name(&mut self) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
405        let blueprint_id = self.api.actor_get_blueprint_id()?;
406
407        self.allocate_buffer(blueprint_id.blueprint_name.into_bytes())
408    }
409
410    #[inline]
411    fn consume_wasm_execution_units(
412        &mut self,
413        n: u32,
414    ) -> Result<(), InvokeError<WasmRuntimeError>> {
415        let n = n.saturating_add(self.wasm_execution_units_base);
416
417        if n <= self.wasm_execution_units_buffer {
418            self.wasm_execution_units_buffer -= n;
419            Ok(())
420        } else {
421            self.consume_wasm_execution_exceeding_buffer(n)
422        }
423    }
424
425    fn instance_of(
426        &mut self,
427        object_id: Vec<u8>,
428        package_address: Vec<u8>,
429        blueprint_name: Vec<u8>,
430    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
431        let object_id = NodeId(
432            TryInto::<[u8; NodeId::LENGTH]>::try_into(object_id.as_ref())
433                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
434        );
435        let (package_address, blueprint_name) =
436            Self::parse_blueprint_id(package_address, blueprint_name)?;
437        let blueprint_id = self.api.get_blueprint_id(&object_id)?;
438
439        if blueprint_id.package_address.eq(&package_address)
440            && blueprint_id.blueprint_name.eq(&blueprint_name)
441        {
442            Ok(1)
443        } else {
444            Ok(0)
445        }
446    }
447
448    fn blueprint_id(
449        &mut self,
450        object_id: Vec<u8>,
451    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
452        let object_id = NodeId(
453            TryInto::<[u8; NodeId::LENGTH]>::try_into(object_id.as_ref())
454                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
455        );
456        let blueprint_id = self.api.get_blueprint_id(&object_id)?;
457
458        let mut buf = Vec::new();
459        buf.extend(blueprint_id.package_address.as_bytes());
460        buf.extend(blueprint_id.blueprint_name.as_bytes());
461
462        self.allocate_buffer(buf)
463    }
464
465    fn get_outer_object(
466        &mut self,
467        node_id: Vec<u8>,
468    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
469        let node_id = NodeId(
470            TryInto::<[u8; NodeId::LENGTH]>::try_into(node_id.as_ref())
471                .map_err(|_| WasmRuntimeError::InvalidNodeId)?,
472        );
473        let address = self.api.get_outer_object(&node_id)?;
474
475        self.allocate_buffer(address.to_vec())
476    }
477
478    fn actor_emit_event(
479        &mut self,
480        event_name: Vec<u8>,
481        event_payload: Vec<u8>,
482        event_flags: EventFlags,
483    ) -> Result<(), InvokeError<WasmRuntimeError>> {
484        self.api.actor_emit_event(
485            String::from_utf8(event_name).map_err(|_| WasmRuntimeError::InvalidString)?,
486            event_payload,
487            event_flags,
488        )?;
489        Ok(())
490    }
491
492    fn sys_log(
493        &mut self,
494        level: Vec<u8>,
495        message: Vec<u8>,
496    ) -> Result<(), InvokeError<WasmRuntimeError>> {
497        self.api.emit_log(
498            scrypto_decode::<Level>(&level).map_err(WasmRuntimeError::InvalidLogLevel)?,
499            String::from_utf8(message).map_err(|_| WasmRuntimeError::InvalidString)?,
500        )?;
501        Ok(())
502    }
503
504    fn sys_bech32_encode_address(
505        &mut self,
506        address: Vec<u8>,
507    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
508        let address =
509            scrypto_decode::<GlobalAddress>(&address).map_err(WasmRuntimeError::InvalidAddress)?;
510        let encoded = self.api.bech32_encode_address(address)?;
511        self.allocate_buffer(encoded.into_bytes())
512    }
513
514    fn sys_panic(&mut self, message: Vec<u8>) -> Result<(), InvokeError<WasmRuntimeError>> {
515        self.api
516            .panic(String::from_utf8(message).map_err(|_| WasmRuntimeError::InvalidString)?)?;
517        Ok(())
518    }
519
520    fn sys_get_transaction_hash(&mut self) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
521        let hash = self.api.get_transaction_hash()?;
522
523        self.allocate_buffer(hash.to_vec())
524    }
525
526    fn sys_generate_ruid(&mut self) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
527        let ruid = self.api.generate_ruid()?;
528
529        self.allocate_buffer(ruid.to_vec())
530    }
531
532    fn costing_get_execution_cost_unit_limit(
533        &mut self,
534    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
535        let execution_cost_unit_limit = self.api.execution_cost_unit_limit()?;
536
537        Ok(execution_cost_unit_limit)
538    }
539
540    fn costing_get_execution_cost_unit_price(
541        &mut self,
542    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
543        let execution_cost_unit_price = self.api.execution_cost_unit_price()?;
544
545        self.allocate_buffer(
546            scrypto_encode(&execution_cost_unit_price)
547                .expect("Failed to encode execution_cost_unit_price"),
548        )
549    }
550
551    fn costing_get_finalization_cost_unit_limit(
552        &mut self,
553    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
554        let finalization_cost_unit_limit = self.api.finalization_cost_unit_limit()?;
555
556        Ok(finalization_cost_unit_limit)
557    }
558
559    fn costing_get_finalization_cost_unit_price(
560        &mut self,
561    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
562        let finalization_cost_unit_price = self.api.finalization_cost_unit_price()?;
563
564        self.allocate_buffer(
565            scrypto_encode(&finalization_cost_unit_price)
566                .expect("Failed to encode finalization_cost_unit_price"),
567        )
568    }
569
570    fn costing_get_usd_price(&mut self) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
571        let usd_price = self.api.usd_price()?;
572        self.allocate_buffer(
573            scrypto_encode(&usd_price).expect("Failed to encode finalization_cost_unit_price"),
574        )
575    }
576
577    fn costing_get_tip_percentage(&mut self) -> Result<u32, InvokeError<WasmRuntimeError>> {
578        Ok(self.api.tip_percentage_truncated()?)
579    }
580
581    fn costing_get_fee_balance(&mut self) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
582        let fee_balance = self.api.fee_balance()?;
583
584        self.allocate_buffer(scrypto_encode(&fee_balance).expect("Failed to encode fee_balance"))
585    }
586
587    /// This method is only available to packages uploaded after "Anemone"
588    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
589    #[trace_resources(log=message.len())]
590    fn crypto_utils_bls12381_v1_verify(
591        &mut self,
592        message: Vec<u8>,
593        public_key: Vec<u8>,
594        signature: Vec<u8>,
595    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
596        let public_key: Bls12381G1PublicKey =
597            scrypto_decode(&public_key).map_err(WasmRuntimeError::InvalidBlsPublicKey)?;
598        let signature: Bls12381G2Signature =
599            scrypto_decode(&signature).map_err(WasmRuntimeError::InvalidBlsSignature)?;
600
601        self.api
602            .consume_cost_units(ClientCostingEntry::Bls12381V1Verify {
603                size: message.len(),
604            })?;
605
606        Ok(verify_bls12381_v1(&message, &public_key, &signature) as u32)
607    }
608
609    /// This method is only available to packages uploaded after "Anemone"
610    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
611    #[trace_resources(log=pub_keys_and_msgs.len())]
612    fn crypto_utils_bls12381_v1_aggregate_verify(
613        &mut self,
614        pub_keys_and_msgs: Vec<u8>,
615        signature: Vec<u8>,
616    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
617        let signature: Bls12381G2Signature =
618            scrypto_decode(&signature).map_err(WasmRuntimeError::InvalidBlsSignature)?;
619        let pub_keys_and_msgs: Vec<(Bls12381G1PublicKey, Vec<u8>)> =
620            scrypto_decode(&pub_keys_and_msgs)
621                .map_err(WasmRuntimeError::InvalidBlsPublicKeyOrMessage)?;
622
623        if pub_keys_and_msgs.is_empty() {
624            return Err(InvokeError::SelfError(WasmRuntimeError::InputDataEmpty));
625        }
626
627        let sizes: Vec<usize> = pub_keys_and_msgs.iter().map(|(_, msg)| msg.len()).collect();
628
629        self.api
630            .consume_cost_units(ClientCostingEntry::Bls12381V1AggregateVerify {
631                sizes: sizes.as_slice(),
632            })?;
633
634        Ok(aggregate_verify_bls12381_v1(&pub_keys_and_msgs, &signature) as u32)
635    }
636
637    /// This method is only available to packages uploaded after "Anemone"
638    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
639    #[trace_resources(log=message.len(), log=public_keys.len())]
640    fn crypto_utils_bls12381_v1_fast_aggregate_verify(
641        &mut self,
642        message: Vec<u8>,
643        public_keys: Vec<u8>,
644        signature: Vec<u8>,
645    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
646        let public_keys: Vec<Bls12381G1PublicKey> =
647            scrypto_decode(&public_keys).map_err(WasmRuntimeError::InvalidBlsPublicKey)?;
648        let signature: Bls12381G2Signature =
649            scrypto_decode(&signature).map_err(WasmRuntimeError::InvalidBlsSignature)?;
650
651        if public_keys.is_empty() {
652            return Err(InvokeError::SelfError(WasmRuntimeError::InputDataEmpty));
653        }
654
655        self.api
656            .consume_cost_units(ClientCostingEntry::Bls12381V1FastAggregateVerify {
657                size: message.len(),
658                keys_cnt: public_keys.len(),
659            })?;
660
661        if self.scrypto_vm_version == ScryptoVmVersion::crypto_utils_v1() {
662            Ok(
663                fast_aggregate_verify_bls12381_v1_anemone(&message, &public_keys, &signature)
664                    as u32,
665            )
666        } else {
667            Ok(fast_aggregate_verify_bls12381_v1(&message, &public_keys, &signature) as u32)
668        }
669    }
670
671    /// This method is only available to packages uploaded after "Anemone"
672    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
673    #[trace_resources(log=signatures.len())]
674    fn crypto_utils_bls12381_g2_signature_aggregate(
675        &mut self,
676        signatures: Vec<u8>,
677    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
678        let signatures: Vec<Bls12381G2Signature> =
679            scrypto_decode(&signatures).map_err(WasmRuntimeError::InvalidBlsSignature)?;
680
681        if signatures.is_empty() {
682            return Err(InvokeError::SelfError(WasmRuntimeError::InputDataEmpty));
683        }
684
685        self.api
686            .consume_cost_units(ClientCostingEntry::Bls12381G2SignatureAggregate {
687                signatures_cnt: signatures.len(),
688            })?;
689
690        let agg_sig = if self.scrypto_vm_version == ScryptoVmVersion::crypto_utils_v1() {
691            Bls12381G2Signature::aggregate_anemone(&signatures)
692        } else {
693            Bls12381G2Signature::aggregate(&signatures, true)
694        }
695        .map_err(|err| RuntimeError::SystemError(SystemError::BlsError(err.to_string())))?;
696
697        self.allocate_buffer(
698            scrypto_encode(&agg_sig).expect("Failed to encode Bls12381G2Signature"),
699        )
700    }
701
702    /// This method is only available to packages uploaded after "Anemone"
703    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
704    #[trace_resources(log=data.len())]
705    fn crypto_utils_keccak256_hash(
706        &mut self,
707        data: Vec<u8>,
708    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
709        self.api
710            .consume_cost_units(ClientCostingEntry::Keccak256Hash { size: data.len() })?;
711
712        let hash = keccak256_hash(data);
713
714        self.allocate_buffer(hash.to_vec())
715    }
716
717    /// This method is only available to packages uploaded after "Cuttlefish"
718    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
719    #[trace_resources(log=data.len())]
720    fn crypto_utils_blake2b_256_hash(
721        &mut self,
722        data: Vec<u8>,
723    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
724        self.api
725            .consume_cost_units(ClientCostingEntry::Blake2b256Hash { size: data.len() })?;
726
727        let hash = blake2b_256_hash(data);
728
729        self.allocate_buffer(hash.to_vec())
730    }
731
732    /// This method is only available to packages uploaded after "Cuttlefish"
733    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
734    #[trace_resources(log=message.len())]
735    fn crypto_utils_ed25519_verify(
736        &mut self,
737        message: Vec<u8>,
738        public_key: Vec<u8>,
739        signature: Vec<u8>,
740    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
741        let public_key = Ed25519PublicKey::try_from(public_key.as_ref())
742            .map_err(WasmRuntimeError::InvalidEd25519PublicKey)?;
743        let signature = Ed25519Signature::try_from(signature.as_ref())
744            .map_err(WasmRuntimeError::InvalidEd25519Signature)?;
745
746        self.api
747            .consume_cost_units(ClientCostingEntry::Ed25519Verify {
748                size: message.len(),
749            })?;
750
751        Ok(verify_ed25519(&message, &public_key, &signature) as u32)
752    }
753
754    /// This method is only available to packages uploaded after "Cuttlefish"
755    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
756    #[trace_resources(log=message.len())]
757    fn crypto_utils_secp256k1_ecdsa_verify(
758        &mut self,
759        message: Vec<u8>,
760        public_key: Vec<u8>,
761        signature: Vec<u8>,
762    ) -> Result<u32, InvokeError<WasmRuntimeError>> {
763        let public_key = Secp256k1PublicKey::try_from(public_key.as_ref())
764            .map_err(WasmRuntimeError::InvalidSecp256k1PublicKey)?;
765        let signature = Secp256k1Signature::try_from(signature.as_ref())
766            .map_err(WasmRuntimeError::InvalidSecp256k1Signature)?;
767        let hash = Hash::try_from(message.as_slice()).map_err(WasmRuntimeError::InvalidHash)?;
768
769        self.api
770            .consume_cost_units(ClientCostingEntry::Secp256k1EcdsaVerify)?;
771
772        Ok(verify_secp256k1(&hash, &public_key, &signature) as u32)
773    }
774
775    /// This method is only available to packages uploaded after "Cuttlefish"
776    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
777    #[trace_resources]
778    fn crypto_utils_secp256k1_ecdsa_verify_and_key_recover(
779        &mut self,
780        message: Vec<u8>,
781        signature: Vec<u8>,
782    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
783        let hash = Hash::try_from(message.as_slice()).map_err(WasmRuntimeError::InvalidHash)?;
784        let signature = Secp256k1Signature::try_from(signature.as_ref())
785            .map_err(WasmRuntimeError::InvalidSecp256k1Signature)?;
786
787        self.api
788            .consume_cost_units(ClientCostingEntry::Secp256k1EcdsaKeyRecover)?;
789
790        let key = verify_and_recover_secp256k1(&hash, &signature)
791            .ok_or(WasmRuntimeError::Secp256k1KeyRecoveryError)?;
792
793        self.allocate_buffer(key.to_vec())
794    }
795
796    /// This method is only available to packages uploaded after "Cuttlefish"
797    /// protocol update due to checks in [`ScryptoV1WasmValidator::validate`].
798    #[trace_resources]
799    fn crypto_utils_secp256k1_ecdsa_verify_and_key_recover_uncompressed(
800        &mut self,
801        message: Vec<u8>,
802        signature: Vec<u8>,
803    ) -> Result<Buffer, InvokeError<WasmRuntimeError>> {
804        let hash = Hash::try_from(message.as_slice()).map_err(WasmRuntimeError::InvalidHash)?;
805        let signature = Secp256k1Signature::try_from(signature.as_ref())
806            .map_err(WasmRuntimeError::InvalidSecp256k1Signature)?;
807
808        self.api
809            .consume_cost_units(ClientCostingEntry::Secp256k1EcdsaKeyRecover)?;
810
811        let key = verify_and_recover_secp256k1_uncompressed(&hash, &signature)
812            .ok_or(WasmRuntimeError::Secp256k1KeyRecoveryError)?;
813
814        self.allocate_buffer(key.0.to_vec())
815    }
816}