1use std::{borrow::Cow, num::NonZeroU32, sync::Arc};
2
3use bytes::Bytes;
4use casper_executor_wasm_common::{
5 chain_utils,
6 entry_point::{
7 ENTRY_POINT_PAYMENT_CALLER, ENTRY_POINT_PAYMENT_DIRECT_INVOCATION_ONLY,
8 ENTRY_POINT_PAYMENT_SELF_ONWARD,
9 },
10 env_info::EnvInfo,
11 error::{
12 CallError, CALLEE_NOT_CALLABLE, CALLEE_SUCCEEDED, CALLEE_TRAPPED, HOST_ERROR_INVALID_DATA,
13 HOST_ERROR_INVALID_INPUT, HOST_ERROR_MAX_MESSAGES_PER_BLOCK_EXCEEDED,
14 HOST_ERROR_MESSAGE_TOPIC_FULL, HOST_ERROR_NOT_FOUND, HOST_ERROR_PAYLOAD_TOO_LONG,
15 HOST_ERROR_SUCCESS, HOST_ERROR_TOO_MANY_TOPICS, HOST_ERROR_TOPIC_TOO_LONG,
16 },
17 flags::ReturnFlags,
18 keyspace::{Keyspace, KeyspaceTag},
19};
20use casper_executor_wasm_interface::{
21 executor::{ExecuteError, ExecuteRequestBuilder, ExecuteResult, ExecutionKind, Executor},
22 u32_from_host_result, Caller, InternalHostError, VMError, VMResult,
23};
24use casper_storage::{
25 global_state::GlobalStateReader,
26 tracking_copy::{TrackingCopyEntityExt, TrackingCopyError, TrackingCopyExt},
27};
28use casper_types::{
29 account::AccountHash,
30 addressable_entity::{ActionThresholds, AssociatedKeys, MessageTopicError, NamedKeyAddr},
31 bytesrepr::ToBytes,
32 contract_messages::{Message, MessageAddr, MessagePayload, MessageTopicSummary},
33 AddressableEntity, BlockGlobalAddr, BlockHash, BlockTime, ByteCode, ByteCodeAddr, ByteCodeHash,
34 ByteCodeKind, CLType, CLValue, ContractRuntimeTag, Digest, EntityAddr, EntityEntryPoint,
35 EntityKind, EntryPointAccess, EntryPointAddr, EntryPointPayment, EntryPointType,
36 EntryPointValue, HashAddr, HostFunctionV2, Key, Package, PackageHash, ProtocolVersion,
37 StoredValue, URef, U512,
38};
39use either::Either;
40use num_derive::FromPrimitive;
41use num_traits::FromPrimitive;
42use tracing::{error, info, warn};
43
44use crate::{
45 abi::{CreateResult, ReadInfo},
46 context::Context,
47 system::{self, MintArgs, MintTransferArgs},
48};
49
50#[derive(Debug, Copy, Clone, FromPrimitive, PartialEq)]
51enum EntityKindTag {
52 Account = 0,
53 Contract = 1,
54}
55
56pub trait FallibleInto<T> {
57 fn try_into_wrapped(self) -> VMResult<T>;
58}
59
60impl<From, To> FallibleInto<To> for From
61where
62 To: TryFrom<From>,
63{
64 fn try_into_wrapped(self) -> VMResult<To> {
65 To::try_from(self).map_err(|_| VMError::Internal(InternalHostError::TypeConversion))
66 }
67}
68
69fn charge_gas_storage<S: GlobalStateReader, E: Executor>(
71 caller: &mut impl Caller<Context = Context<S, E>>,
72 size_bytes: usize,
73) -> VMResult<()> {
74 let storage_costs = &caller.context().storage_costs;
75 let gas_cost = storage_costs.calculate_gas_cost(size_bytes);
76 let value: u64 = gas_cost.value().try_into().map_err(|_| VMError::OutOfGas)?;
77 caller.consume_gas(value)?;
78 Ok(())
79}
80
81fn charge_host_function_call<S, E, const N: usize>(
83 caller: &mut impl Caller<Context = Context<S, E>>,
84 host_function: &HostFunctionV2<[u64; N]>,
85 weights: [u64; N],
86) -> VMResult<()>
87where
88 S: GlobalStateReader,
89 E: Executor,
90{
91 let Some(cost) = host_function.calculate_gas_cost(weights) else {
92 return Err(VMError::OutOfGas);
94 };
95
96 caller.consume_gas(cost.value().as_u64())?;
97 Ok(())
98}
99
100fn metered_write<S: GlobalStateReader, E: Executor>(
102 caller: &mut impl Caller<Context = Context<S, E>>,
103 key: Key,
104 value: StoredValue,
105) -> VMResult<()> {
106 charge_gas_storage(caller, value.serialized_length())?;
107 caller.context_mut().tracking_copy.write(key, value);
108 Ok(())
109}
110
111pub fn casper_write<S: GlobalStateReader, E: Executor>(
113 mut caller: impl Caller<Context = Context<S, E>>,
114 key_space: u64,
115 key_ptr: u32,
116 key_size: u32,
117 value_ptr: u32,
118 value_size: u32,
119) -> VMResult<u32> {
120 let write_cost = caller.context().config.host_function_costs().write;
121 charge_host_function_call(
122 &mut caller,
123 &write_cost,
124 [
125 key_space,
126 u64::from(key_ptr),
127 u64::from(key_size),
128 u64::from(value_ptr),
129 u64::from(value_size),
130 ],
131 )?;
132
133 let keyspace_tag = match KeyspaceTag::from_u64(key_space) {
134 Some(keyspace_tag) => keyspace_tag,
135 None => {
136 return Ok(HOST_ERROR_NOT_FOUND);
138 }
139 };
140
141 let key_payload_bytes = caller.memory_read(key_ptr, key_size.try_into_wrapped()?)?;
142
143 let keyspace = match keyspace_tag {
144 KeyspaceTag::State => Keyspace::State,
145 KeyspaceTag::Context => Keyspace::Context(&key_payload_bytes),
146 KeyspaceTag::NamedKey => {
147 let key_name = match std::str::from_utf8(&key_payload_bytes) {
148 Ok(key_name) => key_name,
149 Err(_) => {
150 return Ok(HOST_ERROR_INVALID_DATA);
152 }
153 };
154
155 Keyspace::NamedKey(key_name)
156 }
157 KeyspaceTag::PaymentInfo => {
158 let key_name = match std::str::from_utf8(&key_payload_bytes) {
159 Ok(key_name) => key_name,
160 Err(_) => {
161 return Ok(HOST_ERROR_INVALID_DATA);
162 }
163 };
164
165 if !caller.has_export(key_name) {
166 return Ok(HOST_ERROR_NOT_FOUND);
168 }
169
170 Keyspace::PaymentInfo(key_name)
171 }
172 };
173
174 let global_state_key = match keyspace_to_global_state_key(caller.context(), keyspace) {
175 Some(global_state_key) => global_state_key,
176 None => {
177 return Ok(HOST_ERROR_NOT_FOUND);
179 }
180 };
181
182 let value = caller.memory_read(value_ptr, value_size.try_into_wrapped()?)?;
183
184 let stored_value = match keyspace {
185 Keyspace::State | Keyspace::Context(_) | Keyspace::NamedKey(_) => {
186 StoredValue::RawBytes(value)
187 }
188 Keyspace::PaymentInfo(_) => {
189 let entry_point_payment = match value.as_slice() {
190 [ENTRY_POINT_PAYMENT_CALLER] => EntryPointPayment::Caller,
191 [ENTRY_POINT_PAYMENT_DIRECT_INVOCATION_ONLY] => {
192 EntryPointPayment::DirectInvocationOnly
193 }
194 [ENTRY_POINT_PAYMENT_SELF_ONWARD] => EntryPointPayment::SelfOnward,
195 _ => {
196 return Ok(HOST_ERROR_INVALID_INPUT);
198 }
199 };
200
201 let entry_point = EntityEntryPoint::new(
202 "_",
203 Vec::new(),
204 CLType::Unit,
205 EntryPointAccess::Public,
206 EntryPointType::Called,
207 entry_point_payment,
208 );
209 let entry_point_value = EntryPointValue::V1CasperVm(entry_point);
210 StoredValue::EntryPoint(entry_point_value)
211 }
212 };
213
214 metered_write(&mut caller, global_state_key, stored_value)?;
215
216 Ok(HOST_ERROR_SUCCESS)
217}
218
219pub fn casper_remove<S: GlobalStateReader, E: Executor>(
228 mut caller: impl Caller<Context = Context<S, E>>,
229 key_space: u64,
230 key_ptr: u32,
231 key_size: u32,
232) -> VMResult<u32> {
233 let write_cost = caller.context().config.host_function_costs().remove;
234 charge_host_function_call(
235 &mut caller,
236 &write_cost,
237 [key_space, u64::from(key_ptr), u64::from(key_size)],
238 )?;
239
240 let keyspace_tag = match KeyspaceTag::from_u64(key_space) {
241 Some(keyspace_tag) => keyspace_tag,
242 None => {
243 return Ok(HOST_ERROR_NOT_FOUND);
245 }
246 };
247
248 let key_payload_bytes = caller.memory_read(key_ptr, key_size.try_into_wrapped()?)?;
249
250 let keyspace = match keyspace_tag {
251 KeyspaceTag::State => Keyspace::State,
252 KeyspaceTag::Context => Keyspace::Context(&key_payload_bytes),
253 KeyspaceTag::NamedKey => {
254 let key_name = match std::str::from_utf8(&key_payload_bytes) {
255 Ok(key_name) => key_name,
256 Err(_) => {
257 return Ok(HOST_ERROR_INVALID_DATA);
259 }
260 };
261
262 Keyspace::NamedKey(key_name)
263 }
264 KeyspaceTag::PaymentInfo => {
265 let key_name = match std::str::from_utf8(&key_payload_bytes) {
266 Ok(key_name) => key_name,
267 Err(_) => {
268 return Ok(HOST_ERROR_INVALID_DATA);
269 }
270 };
271
272 if !caller.has_export(key_name) {
273 return Ok(HOST_ERROR_NOT_FOUND);
275 }
276
277 Keyspace::PaymentInfo(key_name)
278 }
279 };
280
281 let global_state_key = match keyspace_to_global_state_key(caller.context(), keyspace) {
282 Some(global_state_key) => global_state_key,
283 None => {
284 return Ok(HOST_ERROR_NOT_FOUND);
286 }
287 };
288
289 let global_state_read_result = caller.context_mut().tracking_copy.read(&global_state_key);
290 match global_state_read_result {
291 Ok(Some(_stored_value)) => {
292 caller.context_mut().tracking_copy.prune(global_state_key);
294 }
295 Ok(None) => {
296 return Ok(HOST_ERROR_NOT_FOUND);
298 }
299 Err(error) => {
300 error!(
306 ?error,
307 ?global_state_key,
308 "Error while attempting a read before removing value; aborting"
309 );
310 panic!("Error while attempting a read before removing value; aborting key={global_state_key:?} error={error:?}")
311 }
312 }
313
314 Ok(HOST_ERROR_SUCCESS)
315}
316
317pub fn casper_print<S: GlobalStateReader, E: Executor>(
318 mut caller: impl Caller<Context = Context<S, E>>,
319 message_ptr: u32,
320 message_size: u32,
321) -> VMResult<()> {
322 let print_cost = caller.context().config.host_function_costs().print;
323 charge_host_function_call(
324 &mut caller,
325 &print_cost,
326 [u64::from(message_ptr), u64::from(message_size)],
327 )?;
328
329 let vec = caller.memory_read(message_ptr, message_size.try_into_wrapped()?)?;
330 let msg = String::from_utf8_lossy(&vec);
331 eprintln!("⛓️ {msg}");
332 Ok(())
333}
334
335pub fn casper_read<S: GlobalStateReader, E: Executor>(
337 mut caller: impl Caller<Context = Context<S, E>>,
338 key_tag: u64,
339 key_ptr: u32,
340 key_size: u32,
341 info_ptr: u32,
342 cb_alloc: u32,
343 alloc_ctx: u32,
344) -> VMResult<u32> {
345 let read_cost = caller.context().config.host_function_costs().read;
346 charge_host_function_call(
347 &mut caller,
348 &read_cost,
349 [
350 key_tag,
351 u64::from(key_ptr),
352 u64::from(key_size),
353 u64::from(info_ptr),
354 u64::from(cb_alloc),
355 u64::from(alloc_ctx),
356 ],
357 )?;
358
359 let keyspace_tag = match KeyspaceTag::from_u64(key_tag) {
360 Some(keyspace_tag) => keyspace_tag,
361 None => {
362 return Ok(HOST_ERROR_INVALID_INPUT);
364 }
365 };
366
367 let key_payload_bytes = caller.memory_read(key_ptr, key_size.try_into_wrapped()?)?;
370
371 let keyspace = match keyspace_tag {
372 KeyspaceTag::State => Keyspace::State,
373 KeyspaceTag::Context => Keyspace::Context(&key_payload_bytes),
374 KeyspaceTag::NamedKey => {
375 let key_name = match std::str::from_utf8(&key_payload_bytes) {
376 Ok(key_name) => key_name,
377 Err(_) => {
378 return Ok(HOST_ERROR_INVALID_DATA);
379 }
380 };
381
382 Keyspace::NamedKey(key_name)
383 }
384 KeyspaceTag::PaymentInfo => {
385 let key_name = match std::str::from_utf8(&key_payload_bytes) {
386 Ok(key_name) => key_name,
387 Err(_) => {
388 return Ok(HOST_ERROR_INVALID_DATA);
389 }
390 };
391 if !caller.has_export(key_name) {
392 return Ok(HOST_ERROR_NOT_FOUND);
394 }
395 Keyspace::PaymentInfo(key_name)
396 }
397 };
398
399 let global_state_key = match keyspace_to_global_state_key(caller.context(), keyspace) {
400 Some(global_state_key) => global_state_key,
401 None => {
402 return Ok(HOST_ERROR_NOT_FOUND);
404 }
405 };
406 let global_state_read_result = caller.context_mut().tracking_copy.read(&global_state_key);
407
408 let global_state_raw_bytes: Cow<[u8]> = match global_state_read_result {
409 Ok(Some(StoredValue::RawBytes(raw_bytes))) => Cow::Owned(raw_bytes),
410 Ok(Some(StoredValue::EntryPoint(EntryPointValue::V1CasperVm(entry_point)))) => {
411 match entry_point.entry_point_payment() {
412 EntryPointPayment::Caller => Cow::Borrowed(&[ENTRY_POINT_PAYMENT_CALLER]),
413 EntryPointPayment::DirectInvocationOnly => {
414 Cow::Borrowed(&[ENTRY_POINT_PAYMENT_DIRECT_INVOCATION_ONLY])
415 }
416 EntryPointPayment::SelfOnward => Cow::Borrowed(&[ENTRY_POINT_PAYMENT_SELF_ONWARD]),
417 }
418 }
419 Ok(Some(stored_value)) => {
420 todo!("Unsupported {stored_value:?}")
427 }
428 Ok(None) => return Ok(HOST_ERROR_NOT_FOUND), Err(error) => {
430 error!(?error, "Error while reading from storage; aborting");
436 panic!("Error while reading from storage; aborting key={global_state_key:?} error={error:?}")
437 }
438 };
439
440 let out_ptr: u32 = if cb_alloc != 0 {
441 caller.alloc(cb_alloc, global_state_raw_bytes.len(), alloc_ctx)?
442 } else {
443 alloc_ctx
445 };
446
447 let read_info = ReadInfo {
448 data: out_ptr,
449 data_size: global_state_raw_bytes.len().try_into_wrapped()?,
450 };
451
452 let read_info_bytes = safe_transmute::transmute_one_to_bytes(&read_info);
453 caller.memory_write(info_ptr, read_info_bytes)?;
454 if out_ptr != 0 {
455 caller.memory_write(out_ptr, &global_state_raw_bytes)?;
456 }
457 Ok(HOST_ERROR_SUCCESS)
458}
459
460fn keyspace_to_global_state_key<S: GlobalStateReader, E: Executor>(
461 context: &Context<S, E>,
462 keyspace: Keyspace<'_>,
463) -> Option<Key> {
464 let entity_addr = context_to_entity_addr(context);
465
466 match keyspace {
467 Keyspace::State => Some(Key::State(entity_addr)),
468 Keyspace::Context(payload) => {
469 let digest = Digest::hash(payload);
470 Some(casper_types::Key::NamedKey(
471 NamedKeyAddr::new_named_key_entry(entity_addr, digest.value()),
472 ))
473 }
474 Keyspace::NamedKey(payload) => {
475 let digest = Digest::hash(payload.as_bytes());
476 Some(casper_types::Key::NamedKey(
477 NamedKeyAddr::new_named_key_entry(entity_addr, digest.value()),
478 ))
479 }
480 Keyspace::PaymentInfo(payload) => {
481 let entry_point_addr =
482 EntryPointAddr::new_v1_entry_point_addr(entity_addr, payload).ok()?;
483 Some(Key::EntryPoint(entry_point_addr))
484 }
485 }
486}
487
488fn context_to_entity_addr<S: GlobalStateReader, E: Executor>(
489 context: &Context<S, E>,
490) -> EntityAddr {
491 match context.callee {
492 Key::Account(account_hash) => EntityAddr::new_account(account_hash.value()),
493 Key::SmartContract(smart_contract_addr) => {
494 EntityAddr::new_smart_contract(smart_contract_addr)
495 }
496 _ => {
497 panic!("Unexpected callee variant: {:?}", context.callee)
499 }
500 }
501}
502
503pub fn casper_copy_input<S: GlobalStateReader, E: Executor>(
504 mut caller: impl Caller<Context = Context<S, E>>,
505 cb_alloc: u32,
506 alloc_ctx: u32,
507) -> VMResult<u32> {
508 let input = caller.context().input.clone();
509
510 let out_ptr: u32 = if cb_alloc != 0 {
511 caller.alloc(cb_alloc, input.len(), alloc_ctx)?
512 } else {
513 alloc_ctx
515 };
516
517 let copy_input_cost = caller.context().config.host_function_costs().copy_input;
518 charge_host_function_call(
519 &mut caller,
520 ©_input_cost,
521 [
522 u64::from(out_ptr),
523 input
524 .len()
525 .try_into()
526 .expect("usize is at least the same size as u64"),
527 ],
528 )?;
529
530 if out_ptr == 0 {
531 Ok(out_ptr)
532 } else {
533 caller.memory_write(out_ptr, &input)?;
534 Ok(out_ptr + (input.len() as u32))
535 }
536}
537
538pub fn casper_return<S: GlobalStateReader, E: Executor>(
540 mut caller: impl Caller<Context = Context<S, E>>,
541 flags: u32,
542 data_ptr: u32,
543 data_len: u32,
544) -> VMResult<()> {
545 let ret_cost = caller.context().config.host_function_costs().ret;
546 charge_host_function_call(
547 &mut caller,
548 &ret_cost,
549 [u64::from(data_ptr), u64::from(data_len)],
550 )?;
551
552 let flags = ReturnFlags::from_bits_retain(flags);
553 let data = if data_ptr == 0 {
554 None
555 } else {
556 let data = caller
557 .memory_read(data_ptr, data_len.try_into_wrapped()?)
558 .map(Bytes::from)?;
559 Some(data)
560 };
561 Err(VMError::Return { flags, data })
562}
563
564#[allow(clippy::too_many_arguments)]
565pub fn casper_create<S: GlobalStateReader + 'static, E: Executor + 'static>(
566 mut caller: impl Caller<Context = Context<S, E>>,
567 code_ptr: u32,
568 code_len: u32,
569 transferred_value: u64,
570 entry_point_ptr: u32,
571 entry_point_len: u32,
572 input_ptr: u32,
573 input_len: u32,
574 seed_ptr: u32,
575 seed_len: u32,
576 result_ptr: u32,
577) -> VMResult<u32> {
578 let create_cost = caller.context().config.host_function_costs().create;
579 charge_host_function_call(
580 &mut caller,
581 &create_cost,
582 [
583 u64::from(code_ptr),
584 u64::from(code_len),
585 transferred_value,
586 u64::from(entry_point_ptr),
587 u64::from(entry_point_len),
588 u64::from(input_ptr),
589 u64::from(input_len),
590 u64::from(seed_ptr),
591 u64::from(seed_len),
592 u64::from(result_ptr),
593 ],
594 )?;
595
596 let code = if code_ptr != 0 {
597 caller
598 .memory_read(code_ptr, code_len as usize)
599 .map(Bytes::from)?
600 } else {
601 caller.bytecode()
602 };
603
604 let seed = if seed_ptr != 0 {
605 if seed_len != 32 {
606 return Ok(CALLEE_NOT_CALLABLE);
607 }
608 let seed_bytes = caller.memory_read(seed_ptr, seed_len as usize)?;
609 let seed_bytes: [u8; 32] = seed_bytes.try_into().unwrap(); Some(seed_bytes)
611 } else {
612 None
613 };
614
615 let constructor_entry_point = {
617 let entry_point_ptr = NonZeroU32::new(entry_point_ptr);
618 match entry_point_ptr {
619 Some(entry_point_ptr) => {
620 let entry_point_bytes =
621 caller.memory_read(entry_point_ptr.get(), entry_point_len as _)?;
622 match String::from_utf8(entry_point_bytes) {
623 Ok(entry_point) => Some(entry_point),
624 Err(utf8_error) => {
625 error!(%utf8_error, "entry point name is not a valid utf-8 string; unable to call");
626 return Ok(CALLEE_NOT_CALLABLE);
627 }
628 }
629 }
630 None => {
631 None
633 }
634 }
635 };
636
637 let input_data: Option<Bytes> = if input_ptr == 0 {
639 None
640 } else {
641 let input_data = caller.memory_read(input_ptr, input_len as _)?.into();
642 Some(input_data)
643 };
644
645 let bytecode_hash = chain_utils::compute_wasm_bytecode_hash(&code);
646
647 let bytecode = ByteCode::new(ByteCodeKind::V2CasperWasm, code.clone().into());
648 let bytecode_addr = ByteCodeAddr::V2CasperWasm(bytecode_hash);
649
650 let mut smart_contract_package = Package::default();
652
653 let protocol_version = ProtocolVersion::V2_0_0;
654
655 let first_version =
656 smart_contract_package.next_entity_version_for(protocol_version.value().major);
657
658 let callee_addr = context_to_entity_addr(caller.context()).value();
659
660 let smart_contract_addr: HashAddr = chain_utils::compute_predictable_address(
661 caller.context().chain_name.as_bytes(),
662 callee_addr,
663 bytecode_hash,
664 seed,
665 );
666
667 let contract_hash =
668 chain_utils::compute_next_contract_hash_version(smart_contract_addr, first_version);
669
670 smart_contract_package.insert_entity_version(
671 protocol_version.value().major,
672 EntityAddr::SmartContract(contract_hash),
673 );
674
675 if caller
676 .context_mut()
677 .tracking_copy
678 .read(&Key::SmartContract(smart_contract_addr))
679 .map_err(|_| VMError::Internal(InternalHostError::TrackingCopy))?
680 .is_some()
681 {
682 return VMResult::Err(VMError::Internal(InternalHostError::ContractAlreadyExists));
683 }
684
685 metered_write(
686 &mut caller,
687 Key::SmartContract(smart_contract_addr),
688 StoredValue::SmartContract(smart_contract_package),
689 )?;
690
691 metered_write(
693 &mut caller,
694 Key::ByteCode(bytecode_addr),
695 StoredValue::ByteCode(bytecode),
696 )?;
697
698 let entity_addr = EntityAddr::SmartContract(contract_hash);
701 let addressable_entity_key = Key::AddressableEntity(entity_addr);
702
703 let address_generator = Arc::clone(&caller.context().address_generator);
705 let transaction_hash = caller.context().transaction_hash;
706 let main_purse: URef = match system::mint_mint(
707 &mut caller.context_mut().tracking_copy,
708 transaction_hash,
709 address_generator,
710 MintArgs {
711 initial_balance: U512::zero(),
712 },
713 ) {
714 Ok(uref) => uref,
715 Err(mint_error) => {
716 error!(?mint_error, "Failed to create a purse");
717 return Ok(CALLEE_TRAPPED);
718 }
719 };
720
721 let addressable_entity = AddressableEntity::new(
722 PackageHash::new(smart_contract_addr),
723 ByteCodeHash::new(bytecode_hash),
724 ProtocolVersion::V2_0_0,
725 main_purse,
726 AssociatedKeys::default(),
727 ActionThresholds::default(),
728 EntityKind::SmartContract(ContractRuntimeTag::VmCasperV2),
729 );
730
731 metered_write(
732 &mut caller,
733 addressable_entity_key,
734 StoredValue::AddressableEntity(addressable_entity),
735 )?;
736
737 let _initial_state = match constructor_entry_point {
738 Some(entry_point_name) => {
739 let gas_limit = caller
741 .gas_consumed()
742 .try_into_remaining()
743 .map_err(|_| InternalHostError::TypeConversion)?;
744
745 let execute_request = ExecuteRequestBuilder::default()
746 .with_initiator(caller.context().initiator)
747 .with_caller_key(caller.context().callee)
748 .with_gas_limit(gas_limit)
749 .with_target(ExecutionKind::Stored {
750 address: smart_contract_addr,
751 entry_point: entry_point_name,
752 })
753 .with_input(input_data.unwrap_or_default())
754 .with_transferred_value(transferred_value)
755 .with_transaction_hash(caller.context().transaction_hash)
756 .with_shared_address_generator(Arc::clone(&caller.context().address_generator))
759 .with_chain_name(caller.context().chain_name.clone())
760 .with_block_time(caller.context().block_time)
761 .with_state_hash(Digest::from_raw([0; 32])) .with_block_height(1) .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) .build()
765 .map_err(|_| InternalHostError::ExecuteRequestBuildFailure)?;
766
767 let tracking_copy_for_ctor = caller.context().tracking_copy.fork2();
768
769 match caller
770 .context()
771 .executor
772 .execute(tracking_copy_for_ctor, execute_request)
773 {
774 Ok(ExecuteResult {
775 host_error,
776 output,
777 gas_usage,
778 effects,
779 cache,
780 messages,
781 }) => {
782 caller.consume_gas(gas_usage.gas_spent())?;
784
785 if let Some(host_error) = host_error {
786 return Ok(host_error.into_u32());
787 }
788
789 caller
790 .context_mut()
791 .tracking_copy
792 .apply_changes(effects, cache, messages);
793
794 output
795 }
796 Err(ExecuteError::WasmPreparation(_preparation_error)) => {
797 todo!()
800 }
801 }
802 }
803 None => None,
804 };
805
806 let create_result = CreateResult {
807 package_address: smart_contract_addr,
808 };
809
810 let create_result_bytes = safe_transmute::transmute_one_to_bytes(&create_result);
811
812 debug_assert_eq!(
813 safe_transmute::transmute_one(create_result_bytes),
814 Ok(create_result),
815 "Sanity check", );
817
818 caller.memory_write(result_ptr, create_result_bytes)?;
819
820 Ok(CALLEE_SUCCEEDED)
821}
822
823#[allow(clippy::too_many_arguments)]
824pub fn casper_call<S: GlobalStateReader + 'static, E: Executor + 'static>(
825 mut caller: impl Caller<Context = Context<S, E>>,
826 address_ptr: u32,
827 address_len: u32,
828 transferred_value: u64,
829 entry_point_ptr: u32,
830 entry_point_len: u32,
831 input_ptr: u32,
832 input_len: u32,
833 cb_alloc: u32,
834 cb_ctx: u32,
835) -> VMResult<u32> {
836 let call_cost = caller.context().config.host_function_costs().call;
837 charge_host_function_call(
838 &mut caller,
839 &call_cost,
840 [
841 u64::from(address_ptr),
842 u64::from(address_len),
843 transferred_value,
844 u64::from(entry_point_ptr),
845 u64::from(entry_point_len),
846 u64::from(input_ptr),
847 u64::from(input_len),
848 u64::from(cb_alloc),
849 u64::from(cb_ctx),
850 ],
851 )?;
852
853 let address = caller.memory_read(address_ptr, address_len as _)?;
864 let smart_contract_addr: HashAddr = address.try_into_wrapped()?;
865
866 let input_data: Bytes = caller.memory_read(input_ptr, input_len as _)?.into();
867
868 let entry_point = {
869 let entry_point_bytes = caller.memory_read(entry_point_ptr, entry_point_len as _)?;
870 match String::from_utf8(entry_point_bytes) {
871 Ok(entry_point) => entry_point,
872 Err(utf8_error) => {
873 error!(%utf8_error, "entry point name is not a valid utf-8 string; unable to call");
874 return Ok(CALLEE_NOT_CALLABLE);
875 }
876 }
877 };
878
879 let tracking_copy = caller.context().tracking_copy.fork2();
880
881 let gas_limit = caller
883 .gas_consumed()
884 .try_into_remaining()
885 .map_err(|_| InternalHostError::TypeConversion)?;
886
887 let execute_request = ExecuteRequestBuilder::default()
888 .with_initiator(caller.context().initiator)
889 .with_caller_key(caller.context().callee)
890 .with_gas_limit(gas_limit)
891 .with_target(ExecutionKind::Stored {
892 address: smart_contract_addr,
893 entry_point,
894 })
895 .with_transferred_value(transferred_value)
896 .with_input(input_data)
897 .with_transaction_hash(caller.context().transaction_hash)
898 .with_shared_address_generator(Arc::clone(&caller.context().address_generator))
901 .with_chain_name(caller.context().chain_name.clone())
902 .with_block_time(caller.context().block_time)
903 .with_state_hash(Digest::from_raw([0; 32])) .with_block_height(1) .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) .build()
907 .map_err(|_| InternalHostError::ExecuteRequestBuildFailure)?;
908
909 let (gas_usage, host_result) = match caller
910 .context()
911 .executor
912 .execute(tracking_copy, execute_request)
913 {
914 Ok(ExecuteResult {
915 host_error,
916 output,
917 gas_usage,
918 effects,
919 cache,
920 messages,
921 }) => {
922 if let Some(output) = output {
923 let out_ptr: u32 = if cb_alloc != 0 {
924 caller.alloc(cb_alloc, output.len(), cb_ctx)?
925 } else {
926 cb_ctx
928 };
929
930 if out_ptr != 0 {
931 caller.memory_write(out_ptr, &output)?;
932 }
933 }
934
935 let host_result = match host_error {
936 Some(host_error) => Err(host_error),
937 None => {
938 caller
939 .context_mut()
940 .tracking_copy
941 .apply_changes(effects, cache, messages);
942 Ok(())
943 }
944 };
945
946 (gas_usage, host_result)
947 }
948 Err(ExecuteError::WasmPreparation(preparation_error)) => {
949 unreachable!("Preparation error: {:?}", preparation_error)
952 }
953 };
954
955 let gas_spent = gas_usage
956 .gas_limit()
957 .checked_sub(gas_usage.remaining_points())
958 .ok_or(InternalHostError::RemainingGasExceedsGasLimit)?;
959
960 caller.consume_gas(gas_spent)?;
961
962 Ok(u32_from_host_result(host_result))
963}
964
965pub fn casper_env_balance<S: GlobalStateReader, E: Executor>(
966 mut caller: impl Caller<Context = Context<S, E>>,
967 entity_kind: u32,
968 entity_addr_ptr: u32,
969 entity_addr_len: u32,
970 output_ptr: u32,
971) -> VMResult<u32> {
972 let balance_cost = caller.context().config.host_function_costs().env_balance;
973 charge_host_function_call(
974 &mut caller,
975 &balance_cost,
976 [
977 u64::from(entity_kind),
978 u64::from(entity_addr_ptr),
979 u64::from(entity_addr_len),
980 u64::from(output_ptr),
981 ],
982 )?;
983
984 let entity_key = match EntityKindTag::from_u32(entity_kind) {
985 Some(EntityKindTag::Account) => {
986 if entity_addr_len != 32 {
987 return Ok(HOST_ERROR_SUCCESS);
988 }
989 let entity_addr = caller.memory_read(entity_addr_ptr, entity_addr_len as usize)?;
990 let account_hash: AccountHash = AccountHash::new(entity_addr.try_into_wrapped()?);
991
992 let account_key = Key::Account(account_hash);
993 match caller.context_mut().tracking_copy.read(&account_key) {
994 Ok(Some(StoredValue::CLValue(clvalue))) => {
995 let addressible_entity_key = clvalue
996 .into_t::<Key>()
997 .map_err(|_| InternalHostError::TypeConversion)?;
998 Either::Right(addressible_entity_key)
999 }
1000 Ok(Some(StoredValue::Account(account))) => Either::Left(account.main_purse()),
1001 Ok(Some(other_entity)) => {
1002 error!("Unexpected entity type: {other_entity:?}");
1003 return Err(InternalHostError::UnexpectedEntityKind.into());
1004 }
1005 Ok(None) => return Ok(HOST_ERROR_SUCCESS),
1006 Err(error) => {
1007 error!("Error while reading from storage; aborting key={account_key:?} error={error:?}");
1008 return Err(InternalHostError::TrackingCopy.into());
1009 }
1010 }
1011 }
1012 Some(EntityKindTag::Contract) => {
1013 if entity_addr_len != 32 {
1014 return Ok(HOST_ERROR_SUCCESS);
1015 }
1016 let hash_bytes = caller.memory_read(entity_addr_ptr, entity_addr_len as usize)?;
1017 let hash_bytes: [u8; 32] = hash_bytes.try_into().unwrap(); let smart_contract_key = Key::SmartContract(hash_bytes);
1020 match caller.context_mut().tracking_copy.read(&smart_contract_key) {
1021 Ok(Some(StoredValue::SmartContract(smart_contract_package))) => {
1022 match smart_contract_package.versions().latest() {
1023 Some(addressible_entity_hash) => {
1024 let key = Key::AddressableEntity(EntityAddr::SmartContract(
1025 addressible_entity_hash.value(),
1026 ));
1027 Either::Right(key)
1028 }
1029 None => {
1030 warn!(
1031 ?smart_contract_key,
1032 "Unable to find latest addressible entity hash for contract"
1033 );
1034 return Ok(HOST_ERROR_SUCCESS);
1035 }
1036 }
1037 }
1038 Ok(Some(_)) => {
1039 return Ok(HOST_ERROR_SUCCESS);
1040 }
1041 Ok(None) => {
1042 return Ok(HOST_ERROR_SUCCESS);
1044 }
1045 Err(error) => {
1046 error!(
1047 hash_bytes = base16::encode_lower(&hash_bytes),
1048 ?error,
1049 "Error while reading from storage; aborting"
1050 );
1051 panic!("Error while reading from storage")
1052 }
1053 }
1054 }
1055 None => return Ok(HOST_ERROR_SUCCESS),
1056 };
1057
1058 let purse = match entity_key {
1059 Either::Left(main_purse) => main_purse,
1060 Either::Right(indirect_entity_key) => {
1061 match caller
1062 .context_mut()
1063 .tracking_copy
1064 .read(&indirect_entity_key)
1065 {
1066 Ok(Some(StoredValue::AddressableEntity(addressable_entity))) => {
1067 addressable_entity.main_purse()
1068 }
1069 Ok(Some(other_entity)) => {
1070 panic!("Unexpected entity type: {other_entity:?}")
1071 }
1072 Ok(None) => panic!("Key not found while checking balance"), Err(error) => {
1074 panic!("Error while reading from storage; aborting key={entity_key:?} error={error:?}")
1075 }
1076 }
1077 }
1078 };
1079
1080 let total_balance = caller
1081 .context_mut()
1082 .tracking_copy
1083 .get_total_balance(Key::URef(purse))
1084 .map_err(|_| InternalHostError::TotalBalanceReadFailure)?;
1085
1086 let total_balance: u64 = total_balance
1087 .value()
1088 .try_into()
1089 .map_err(|_| InternalHostError::TotalBalanceOverflow)?;
1090
1091 caller.memory_write(output_ptr, &total_balance.to_le_bytes())?;
1092 Ok(HOST_ERROR_NOT_FOUND)
1093}
1094
1095pub fn casper_transfer<S: GlobalStateReader + 'static, E: Executor>(
1096 mut caller: impl Caller<Context = Context<S, E>>,
1097 entity_addr_ptr: u32,
1098 entity_addr_len: u32,
1099 amount_ptr: u32,
1100) -> VMResult<u32> {
1101 let transfer_cost = caller.context().config.host_function_costs().transfer;
1102 charge_host_function_call(
1103 &mut caller,
1104 &transfer_cost,
1105 [
1106 u64::from(entity_addr_ptr),
1107 u64::from(entity_addr_len),
1108 u64::from(amount_ptr),
1109 ],
1110 )?;
1111
1112 if entity_addr_len != 32 {
1113 return Ok(u32_from_host_result(Err(CallError::NotCallable)));
1115 }
1116
1117 let amount = {
1118 let mut amount_bytes = [0u8; 8];
1119 caller.memory_read_into(amount_ptr, &mut amount_bytes)?;
1120 u64::from_le_bytes(amount_bytes)
1121 };
1122
1123 let (target_entity_addr, _runtime_footprint) = {
1124 let entity_addr = caller.memory_read(entity_addr_ptr, entity_addr_len as usize)?;
1125 debug_assert_eq!(entity_addr.len(), 32);
1126
1127 let account_hash: AccountHash = AccountHash::new(entity_addr.try_into().unwrap());
1129
1130 let protocol_version = ProtocolVersion::V2_0_0;
1131 let (entity_addr, runtime_footprint) = match caller
1132 .context_mut()
1133 .tracking_copy
1134 .runtime_footprint_by_account_hash(protocol_version, account_hash)
1135 {
1136 Ok((entity_addr, runtime_footprint)) => (entity_addr, runtime_footprint),
1137 Err(TrackingCopyError::KeyNotFound(key)) => {
1138 warn!(?key, "Account not found");
1139 return Ok(u32_from_host_result(Err(CallError::NotCallable)));
1140 }
1141 Err(error) => {
1142 error!(?error, "Error while reading from storage; aborting");
1143 panic!("Error while reading from storage")
1144 }
1145 };
1146 (entity_addr, runtime_footprint)
1147 };
1148
1149 let callee_addressable_entity_key = match caller.context().callee {
1150 callee_account_key @ Key::Account(_account_hash) => {
1151 match caller.context_mut().tracking_copy.read(&callee_account_key) {
1152 Ok(Some(StoredValue::CLValue(indirect))) => {
1153 indirect
1155 .into_t::<Key>()
1156 .map_err(|_| InternalHostError::TypeConversion)?
1157 }
1158 Ok(Some(other)) => panic!("should be cl value but got {other:?}"),
1159 Ok(None) => return Ok(u32_from_host_result(Err(CallError::NotCallable))),
1160 Err(error) => {
1161 error!(
1162 ?error,
1163 ?callee_account_key,
1164 "Error while reading from storage; aborting"
1165 );
1166 panic!("Error while reading from storage")
1167 }
1168 }
1169 }
1170 smart_contract_key @ Key::SmartContract(_) => {
1171 match caller.context_mut().tracking_copy.read(&smart_contract_key) {
1172 Ok(Some(StoredValue::SmartContract(smart_contract_package))) => {
1173 match smart_contract_package.versions().latest() {
1174 Some(addressible_entity_hash) => Key::AddressableEntity(
1175 EntityAddr::SmartContract(addressible_entity_hash.value()),
1176 ),
1177 None => {
1178 warn!(
1179 ?smart_contract_key,
1180 "Unable to find latest addressible entity hash for contract"
1181 );
1182 return Ok(u32_from_host_result(Err(CallError::NotCallable)));
1183 }
1184 }
1185 }
1186 Ok(Some(other)) => panic!("should be smart contract but got {other:?}"),
1187 Ok(None) => return Ok(u32_from_host_result(Err(CallError::NotCallable))),
1188 Err(error) => {
1189 error!(
1190 ?error,
1191 ?smart_contract_key,
1192 "Error while reading from storage; aborting"
1193 );
1194 panic!("Error while reading from storage")
1195 }
1196 }
1197 }
1198 other => panic!("should be account or smart contract but got {other:?}"),
1199 };
1200
1201 let callee_stored_value = caller
1202 .context_mut()
1203 .tracking_copy
1204 .read(&callee_addressable_entity_key)
1205 .map_err(|_| InternalHostError::TrackingCopy)?
1206 .ok_or(InternalHostError::AccountRecordNotFound)?;
1207 let callee_addressable_entity = callee_stored_value
1208 .into_addressable_entity()
1209 .ok_or(InternalHostError::TypeConversion)?;
1210 let callee_purse = callee_addressable_entity.main_purse();
1211
1212 let target_purse = match caller
1213 .context_mut()
1214 .tracking_copy
1215 .runtime_footprint_by_entity_addr(target_entity_addr)
1216 {
1217 Ok(runtime_footprint) => match runtime_footprint.main_purse() {
1218 Some(target_purse) => target_purse,
1219 None => todo!("create a main purse for a contract"),
1220 },
1221 Err(TrackingCopyError::KeyNotFound(key)) => {
1222 warn!(?key, "Transfer recipient not found");
1223 return Ok(u32_from_host_result(Err(CallError::NotCallable)));
1224 }
1225 Err(error) => {
1226 error!(?error, "Error while reading from storage; aborting");
1227 return Err(InternalHostError::TrackingCopy)?;
1228 }
1229 };
1230 let transaction_hash = caller.context().transaction_hash;
1233 let address_generator = Arc::clone(&caller.context().address_generator);
1234 let args = MintTransferArgs {
1235 source: callee_purse,
1236 target: target_purse,
1237 amount: U512::from(amount),
1238 maybe_to: None,
1239 id: None,
1240 };
1241
1242 let result = system::mint_transfer(
1243 &mut caller.context_mut().tracking_copy,
1244 transaction_hash,
1245 address_generator,
1246 args,
1247 );
1248
1249 Ok(u32_from_host_result(result))
1250}
1251
1252pub fn casper_upgrade<S: GlobalStateReader + 'static, E: Executor>(
1253 mut caller: impl Caller<Context = Context<S, E>>,
1254 code_ptr: u32,
1255 code_size: u32,
1256 entry_point_ptr: u32,
1257 entry_point_size: u32,
1258 input_ptr: u32,
1259 input_size: u32,
1260) -> VMResult<u32> {
1261 let upgrade_cost = caller.context().config.host_function_costs().upgrade;
1262 charge_host_function_call(
1263 &mut caller,
1264 &upgrade_cost,
1265 [
1266 u64::from(code_ptr),
1267 u64::from(code_size),
1268 u64::from(entry_point_ptr),
1269 u64::from(entry_point_size),
1270 u64::from(input_ptr),
1271 u64::from(input_size),
1272 ],
1273 )?;
1274
1275 let code = caller
1276 .memory_read(code_ptr, code_size as usize)
1277 .map(Bytes::from)?;
1278
1279 let entry_point = match NonZeroU32::new(entry_point_ptr) {
1280 Some(entry_point_ptr) => {
1281 let entry_point_bytes =
1283 caller.memory_read(entry_point_ptr.get(), entry_point_size as usize)?;
1284 match String::from_utf8(entry_point_bytes) {
1285 Ok(entry_point) => Some(entry_point),
1286 Err(utf8_error) => {
1287 error!(%utf8_error, "entry point name is not a valid utf-8 string; unable to call");
1288 return Ok(CALLEE_NOT_CALLABLE);
1289 }
1290 }
1291 }
1292 None => {
1293 None
1295 }
1296 };
1297
1298 let input_data: Option<Bytes> = if input_ptr == 0 {
1300 None
1301 } else {
1302 let input_data = caller.memory_read(input_ptr, input_size as _)?.into();
1303 Some(input_data)
1304 };
1305
1306 let (smart_contract_addr, callee_addressable_entity_key) = match caller.context().callee {
1307 Key::Account(_account_hash) => {
1308 error!("Account upgrade is not possible");
1309 return Ok(CALLEE_NOT_CALLABLE);
1310 }
1311 addressable_entity_key @ Key::SmartContract(smart_contract_addr) => {
1312 let smart_contract_key = addressable_entity_key;
1313 match caller.context_mut().tracking_copy.read(&smart_contract_key) {
1314 Ok(Some(StoredValue::SmartContract(smart_contract_package))) => {
1315 match smart_contract_package.versions().latest() {
1316 Some(addressible_entity_hash) => {
1317 let key = Key::AddressableEntity(EntityAddr::SmartContract(
1318 addressible_entity_hash.value(),
1319 ));
1320 (smart_contract_addr, key)
1321 }
1322 None => {
1323 warn!(
1324 ?smart_contract_key,
1325 "Unable to find latest addressible entity hash for contract"
1326 );
1327 return Ok(CALLEE_NOT_CALLABLE);
1328 }
1329 }
1330 }
1331 Ok(Some(other)) => panic!("should be smart contract but got {other:?}"),
1332 Ok(None) => return Ok(CALLEE_NOT_CALLABLE),
1333 Err(error) => {
1334 error!(
1335 ?error,
1336 ?smart_contract_key,
1337 "Error while reading from storage; aborting"
1338 );
1339 panic!("Error while reading from storage")
1340 }
1341 }
1342 }
1343 other => panic!("should be account or addressable entity but got {other:?}"),
1344 };
1345
1346 let callee_addressable_entity = match caller
1347 .context_mut()
1348 .tracking_copy
1349 .read(&callee_addressable_entity_key)
1350 {
1351 Ok(Some(StoredValue::AddressableEntity(addressable_entity))) => addressable_entity,
1352 Ok(Some(other_entity)) => {
1353 panic!("Unexpected entity type: {other_entity:?}")
1354 }
1355 Ok(None) => return Ok(CALLEE_NOT_CALLABLE),
1356 Err(error) => {
1357 panic!("Error while reading from storage; aborting key={callee_addressable_entity_key:?} error={error:?}")
1358 }
1359 };
1360
1361 let bytecode_key = Key::ByteCode(ByteCodeAddr::V2CasperWasm(
1368 callee_addressable_entity.byte_code_addr(),
1369 ));
1370 metered_write(
1371 &mut caller,
1372 bytecode_key,
1373 StoredValue::ByteCode(ByteCode::new(
1374 ByteCodeKind::V2CasperWasm,
1375 code.clone().into(),
1376 )),
1377 )?;
1378
1379 if let Some(entry_point_name) = entry_point {
1383 let gas_limit = caller
1385 .gas_consumed()
1386 .try_into_remaining()
1387 .map_err(|_| InternalHostError::TypeConversion)?;
1388
1389 let execute_request = ExecuteRequestBuilder::default()
1390 .with_initiator(caller.context().initiator)
1391 .with_caller_key(caller.context().callee)
1392 .with_gas_limit(gas_limit)
1393 .with_target(ExecutionKind::Stored {
1394 address: smart_contract_addr,
1395 entry_point: entry_point_name.clone(),
1396 })
1397 .with_input(input_data.unwrap_or_default())
1398 .with_transferred_value(0)
1401 .with_transaction_hash(caller.context().transaction_hash)
1402 .with_shared_address_generator(Arc::clone(&caller.context().address_generator))
1405 .with_chain_name(caller.context().chain_name.clone())
1406 .with_block_time(caller.context().block_time)
1407 .with_state_hash(Digest::from_raw([0; 32])) .with_block_height(1) .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) .build()
1411 .map_err(|_| InternalHostError::ExecuteRequestBuildFailure)?;
1412
1413 let tracking_copy_for_ctor = caller.context().tracking_copy.fork2();
1414
1415 match caller
1416 .context()
1417 .executor
1418 .execute(tracking_copy_for_ctor, execute_request)
1419 {
1420 Ok(ExecuteResult {
1421 host_error,
1422 output,
1423 gas_usage,
1424 effects,
1425 cache,
1426 messages,
1427 }) => {
1428 caller.consume_gas(gas_usage.gas_spent())?;
1430
1431 if let Some(host_error) = host_error {
1432 return Ok(host_error.into_u32());
1433 }
1434
1435 caller
1436 .context_mut()
1437 .tracking_copy
1438 .apply_changes(effects, cache, messages);
1439
1440 if let Some(output) = output {
1441 info!(
1442 ?entry_point_name,
1443 ?output,
1444 "unexpected output from migration entry point"
1445 );
1446 }
1447 }
1448 Err(ExecuteError::WasmPreparation(preparation_error)) => {
1449 error!(
1451 ?preparation_error,
1452 "Wasm preparation error while performing upgrade"
1453 );
1454 return Ok(CALLEE_NOT_CALLABLE);
1455 }
1456 }
1457 }
1458
1459 Ok(CALLEE_SUCCEEDED)
1460}
1461
1462pub fn casper_env_info<S: GlobalStateReader, E: Executor>(
1463 mut caller: impl Caller<Context = Context<S, E>>,
1464 info_ptr: u32,
1465 info_size: u32,
1466) -> VMResult<u32> {
1467 let block_time_cost = caller.context().config.host_function_costs().env_info;
1468 charge_host_function_call(
1469 &mut caller,
1470 &block_time_cost,
1471 [u64::from(info_ptr), u64::from(info_size)],
1472 )?;
1473
1474 let (caller_kind, caller_addr) = match &caller.context().caller {
1475 Key::Account(account_hash) => (EntityKindTag::Account as u32, account_hash.value()),
1476 Key::SmartContract(smart_contract_addr) => {
1477 (EntityKindTag::Contract as u32, *smart_contract_addr)
1478 }
1479 other => panic!("Unexpected caller: {other:?}"),
1480 };
1481
1482 let (callee_kind, callee_addr) = match &caller.context().callee {
1483 Key::Account(initiator_addr) => (EntityKindTag::Account as u32, initiator_addr.value()),
1484 Key::SmartContract(smart_contract_addr) => {
1485 (EntityKindTag::Contract as u32, *smart_contract_addr)
1486 }
1487 other => panic!("Unexpected callee: {other:?}"),
1488 };
1489
1490 let transferred_value = caller.context().transferred_value;
1491
1492 let block_time = caller.context().block_time.value();
1493
1494 let env_info_le = EnvInfo {
1496 caller_addr,
1497 caller_kind: caller_kind.to_le(),
1498 callee_addr,
1499 callee_kind: callee_kind.to_le(),
1500 transferred_value: transferred_value.to_le(),
1501 block_time: block_time.to_le(),
1502 };
1503
1504 let env_info_bytes = safe_transmute::transmute_one_to_bytes(&env_info_le);
1505 let write_len = env_info_bytes.len().min(info_size as usize);
1506 caller.memory_write(info_ptr, &env_info_bytes[..write_len])?;
1507
1508 Ok(HOST_ERROR_SUCCESS)
1509}
1510
1511pub fn casper_emit<S: GlobalStateReader, E: Executor>(
1512 mut caller: impl Caller<Context = Context<S, E>>,
1513 topic_name_ptr: u32,
1514 topic_name_size: u32,
1515 payload_ptr: u32,
1516 payload_size: u32,
1517) -> VMResult<u32> {
1518 let emit_host_function = caller.context().config.host_function_costs().emit;
1520
1521 charge_host_function_call(
1522 &mut caller,
1523 &emit_host_function,
1524 [
1525 u64::from(topic_name_ptr),
1526 u64::from(topic_name_size),
1527 u64::from(payload_ptr),
1528 u64::from(payload_size),
1529 ],
1530 )?;
1531
1532 if topic_name_size > caller.context().message_limits.max_topic_name_size {
1533 return Ok(HOST_ERROR_TOPIC_TOO_LONG);
1534 }
1535
1536 if payload_size > caller.context().message_limits.max_message_size {
1537 return Ok(HOST_ERROR_PAYLOAD_TOO_LONG);
1538 }
1539
1540 let topic_name = {
1541 let topic: Vec<u8> = caller.memory_read(topic_name_ptr, topic_name_size as usize)?;
1542 let Ok(topic) = String::from_utf8(topic) else {
1543 return Ok(HOST_ERROR_INVALID_DATA);
1545 };
1546 topic
1547 };
1548
1549 let payload = caller.memory_read(payload_ptr, payload_size as usize)?;
1550
1551 let entity_addr = context_to_entity_addr(caller.context());
1552
1553 let mut message_topics = caller
1554 .context_mut()
1555 .tracking_copy
1556 .get_message_topics(entity_addr)
1557 .unwrap_or_else(|error| {
1558 panic!("Error while reading from storage; aborting error={error:?}")
1559 });
1560
1561 if message_topics.len() >= caller.context().message_limits.max_topics_per_contract as usize {
1562 return Ok(HOST_ERROR_TOO_MANY_TOPICS);
1563 }
1564
1565 let topic_name_hash = Digest::hash(&topic_name).value().into();
1566
1567 match message_topics.add_topic(&topic_name, topic_name_hash) {
1568 Ok(()) => {
1569 }
1571 Err(MessageTopicError::DuplicateTopic) => {
1572 }
1575 Err(MessageTopicError::MaxTopicsExceeded) => {
1576 return Ok(HOST_ERROR_TOO_MANY_TOPICS);
1578 }
1579 Err(MessageTopicError::TopicNameSizeExceeded) => {
1580 return Ok(HOST_ERROR_TOPIC_TOO_LONG);
1582 }
1583 Err(error) => {
1584 unreachable!("Unexpected error while adding a topic: {:?}", error);
1586 }
1587 };
1588
1589 let current_block_time = caller.context().block_time;
1590 eprintln!("📩 {topic_name}: {payload:?} (at {current_block_time:?})");
1591
1592 let topic_key = Key::Message(MessageAddr::new_topic_addr(entity_addr, topic_name_hash));
1593 let prev_topic_summary = match caller.context_mut().tracking_copy.read(&topic_key) {
1594 Ok(Some(StoredValue::MessageTopic(message_topic_summary))) => message_topic_summary,
1595 Ok(Some(stored_value)) => {
1596 panic!("Unexpected stored value: {stored_value:?}");
1597 }
1598 Ok(None) => {
1599 let message_topic_summary =
1600 MessageTopicSummary::new(0, current_block_time, topic_name.clone());
1601 let summary = StoredValue::MessageTopic(message_topic_summary.clone());
1602 caller.context_mut().tracking_copy.write(topic_key, summary);
1603 message_topic_summary
1604 }
1605 Err(error) => panic!("Error while reading from storage; aborting error={error:?}"),
1606 };
1607
1608 let topic_message_index = if prev_topic_summary.blocktime() != current_block_time {
1609 for index in 1..prev_topic_summary.message_count() {
1610 let message_key = Key::message(entity_addr, topic_name_hash, index);
1611 debug_assert!(
1612 {
1613 caller
1616 .context_mut()
1617 .tracking_copy
1618 .read(&message_key)
1619 .map_err(|_| VMError::Internal(InternalHostError::TrackingCopy))?
1620 .is_some()
1621 },
1622 "Message index is not continuous"
1623 );
1624
1625 caller.context_mut().tracking_copy.prune(message_key);
1627 }
1628 0
1629 } else {
1630 prev_topic_summary.message_count()
1631 };
1632
1633 type MessageCountPair = (BlockTime, u64);
1635
1636 let block_message_index: u64 = match caller
1637 .context_mut()
1638 .tracking_copy
1639 .read(&Key::BlockGlobal(BlockGlobalAddr::MessageCount))
1640 {
1641 Ok(Some(StoredValue::CLValue(value_pair))) => {
1642 let (prev_block_time, prev_count): MessageCountPair =
1643 CLValue::into_t(value_pair).map_err(|_| InternalHostError::TypeConversion)?;
1644 if prev_block_time == current_block_time {
1645 prev_count
1646 } else {
1647 0
1648 }
1649 }
1650 Ok(Some(other)) => panic!("Unexpected stored value: {other:?}"),
1651 Ok(None) => {
1652 0
1654 }
1655 Err(error) => {
1656 panic!("Error while reading from storage; aborting error={error:?}")
1657 }
1658 };
1659
1660 let Some(topic_message_count) = topic_message_index.checked_add(1) else {
1661 return Ok(HOST_ERROR_MESSAGE_TOPIC_FULL);
1662 };
1663
1664 let Some(block_message_count) = block_message_index.checked_add(1) else {
1665 return Ok(HOST_ERROR_MAX_MESSAGES_PER_BLOCK_EXCEEDED);
1666 };
1667
1668 let message_payload = MessagePayload::Bytes(payload.into());
1670
1671 let message = Message::new(
1672 entity_addr,
1673 message_payload,
1674 topic_name,
1675 topic_name_hash,
1676 topic_message_index,
1677 block_message_index,
1678 );
1679 let topic_value = StoredValue::MessageTopic(MessageTopicSummary::new(
1680 topic_message_count,
1681 current_block_time,
1682 message.topic_name().to_owned(),
1683 ));
1684
1685 let message_key = message.message_key();
1686 let message_value = StoredValue::Message(
1687 message
1688 .checksum()
1689 .map_err(|_| InternalHostError::MessageChecksumMissing)?,
1690 );
1691 let message_count_pair: MessageCountPair = (current_block_time, block_message_count);
1692 let block_message_count_value = StoredValue::CLValue(
1693 CLValue::from_t(message_count_pair).map_err(|_| InternalHostError::TypeConversion)?,
1694 );
1695
1696 let bytes_count = topic_value.serialized_length()
1698 + message_value.serialized_length()
1699 + block_message_count_value.serialized_length();
1700 charge_gas_storage(&mut caller, bytes_count)?;
1701
1702 caller.context_mut().tracking_copy.emit_message(
1703 topic_key,
1704 topic_value,
1705 message_key,
1706 message_value,
1707 block_message_count_value,
1708 message,
1709 );
1710
1711 Ok(HOST_ERROR_SUCCESS)
1712}