radix_engine/vm/wasm_runtime/
scrypto_runtime.rs

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