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