casper_executor_wasm_host/
host.rs

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
55/// Consumes a set amount of gas for the specified storage value.
56fn 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
67/// Consumes a set amount of gas for the specified host function and weights
68fn 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        // Overflowing gas calculation means gas limit was exceeded
80        return Err(VMError::OutOfGas);
81    };
82
83    caller.consume_gas(cost.value().as_u64())?;
84    Ok(())
85}
86
87/// Writes a message to the global state and charges for storage used.
88fn 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
98/// Write value under a key.
99pub 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            // Unknown keyspace received, return error
118            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                    // TODO: Invalid key name encoding
132                    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                // Missing wasm export, unable to perform global state write
148                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            // Unknown keyspace received, return error
159            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                    // Invalid entry point payment variant
178                    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
214/// Write value under a key.
215pub 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            // Unknown keyspace received, return error
242            return Ok(HOST_ERROR_INVALID_INPUT);
243        }
244    };
245
246    // TODO: Opportunity for optimization: don't read data under key_ptr if given key space does not
247    // require it.
248    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                // Missing wasm export, unable to perform global state read
272                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            // Unknown keyspace received, return error
282            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: Backwards compatibility with old EE, although it's not clear if we should do it
300            // at the storage level. Since new VM has storage isolated from the Wasm
301            // (i.e. we have Keyspace on the wasm which gets converted to a global state `Key`).
302            // I think if we were to pursue this we'd add a new `Keyspace` enum variant for each old
303            // VM supported Key types (i.e. URef, Dictionary perhaps) for some period of time, then
304            // deprecate this.
305            todo!("Unsupported {stored_value:?}")
306        }
307        Ok(None) => return Ok(HOST_ERROR_NOT_FOUND), // Entry does not exists
308        Err(error) => {
309            // To protect the network against potential non-determinism (i.e. one validator runs out
310            // of space or just faces I/O issues that other validators may not have) we're simply
311            // aborting the process, hoping that once the node goes back online issues are resolved
312            // on the validator side. TODO: We should signal this to the contract
313            // runtime somehow, and let validator nodes skip execution.
314            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        // treats alloc_ctx as data
323        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            // This should never happen, as the caller is always an account or a smart contract.
377            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        // treats alloc_ctx as data
393        alloc_ctx
394    };
395
396    let copy_input_cost = caller.context().config.host_function_costs().copy_input;
397    charge_host_function_call(&mut caller, &copy_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
407/// Returns from the execution of a smart contract with an optional flags.
408pub 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(); // SAFETY: We checked for length.
481        Some(seed_bytes)
482    } else {
483        None
484    };
485
486    // For calling a constructor
487    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                // No constructor to be called
503                None
504            }
505        }
506    };
507
508    // Pass input data when calling a constructor. It's optional, as constructors aren't required
509    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    // 1. Store package hash
522    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    // 2. Store wasm
567    metered_write(
568        &mut caller,
569        Key::ByteCode(bytecode_addr),
570        StoredValue::ByteCode(bytecode),
571    )?;
572
573    // 3. Store addressable entity
574
575    let entity_addr = EntityAddr::SmartContract(contract_hash);
576    let addressable_entity_key = Key::AddressableEntity(entity_addr);
577
578    // TODO: abort(str) as an alternative to trap
579    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            // Take the gas spent so far and use it as a limit for the new VM.
617            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                // We're using shared address generator there as we need to preserve and advance the
634                // state of deterministic address generator across chain of calls.
635                .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])) // TODO: Carry on state root hash
639                .with_block_height(1) // TODO: Carry on block height
640                .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) // TODO: Carry on parent block hash
641                .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                    // output
660                    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                    // This is a bug in the EE, as it should have been caught during the preparation
675                    // phase when the contract was stored in the global state.
676                    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", // NOTE: Remove these guards with sufficient test coverage
693    );
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    // 1. Look up address in the storage
731    // 1a. if it's legacy contract, wire up old EE, pretend you're 1.x. Input data would be
732    // "RuntimeArgs". Serialized output of the call has to be passed as output. Value is ignored as
733    // you can't pass value (tokens) to called contracts. 1b. if it's new contract, wire up
734    // another VM as according to the bytecode format. 2. Depends on the VM used (old or new) at
735    // this point either entry point is validated (i.e. EE returned error) or will be validated as
736    // for now. 3. If entry point is valid, call it, transfer the value, pass the input data. If
737    // it's invalid, return error. 4. Output data is captured by calling `cb_alloc`.
738    // let vm = VM::new();
739    // vm.
740    let address = caller.memory_read(address_ptr, address_len as _)?;
741    let smart_contract_addr: HashAddr = address.try_into().unwrap(); // TODO: Error handling
742
743    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    // Take the gas spent so far and use it as a limit for the new VM.
765    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        // We're using shared address generator there as we need to preserve and advance the state
782        // of deterministic address generator across chain of calls.
783        .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])) // TODO: Carry on state root hash
787        .with_block_height(1) // TODO: Carry on block height
788        .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) // TODO: Carry on parent block hash
789        .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                    // treats alloc_ctx as data
810                    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            // This is a bug in the EE, as it should have been caught during the preparation phase
833            // when the contract was stored in the global state.
834            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    // TODO: Decide whether we want to return the full address and entity kind or just the 32 bytes
862    // "unified".
863    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(); // SAFETY: We checked for length.
942
943            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::AddressableEntity(*addressible_entity_hash))
952                            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                    // Not found, balance is 0
968                    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"), //return Ok(0),
998                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        // Invalid entity address; failing to proceed with the transfer
1033        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        // SAFETY: entity_addr is 32 bytes long
1047        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                    // is it an account?
1073
1074                    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    // We don't execute anything as it does not make sense to execute an account as there
1149    // are no entry points.
1150    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            // There's upgrade entry point to be called
1200            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            // No constructor to be called
1212            None
1213        }
1214    };
1215
1216    // Pass input data when calling a constructor. It's optional, as constructors aren't required
1217    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    // 1. Ensure that the new code is valid (maybe?)
1280    // TODO: Is validating new code worth it if the user pays for the storage anyway? Should we
1281    // protect users against invalid code?
1282
1283    // 2. Update the code therefore making hash(new_code) != addressable_entity.bytecode_addr (aka
1284    //    hash(old_code))
1285    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    // 3. Execute upgrade routine (if specified)
1298    // this code should handle reading old state, and saving new state
1299
1300    if let Some(entry_point_name) = entry_point {
1301        // Take the gas spent so far and use it as a limit for the new VM.
1302        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            // Upgrade entry point is executed with zero value as it does not seem to make sense to
1317            // be able to transfer anything.
1318            .with_transferred_value(0)
1319            .with_transaction_hash(caller.context().transaction_hash)
1320            // We're using shared address generator there as we need to preserve and advance the
1321            // state of deterministic address generator across chain of calls.
1322            .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])) // TODO: Carry on state root hash
1326            .with_block_height(1) // TODO: Carry on block height
1327            .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) // TODO: Carry on parent block hash
1328            .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                // output
1347                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                // Unable to call contract because the wasm is broken.
1368                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
1380// TODO: Should this be blocktime instead of block_time? [Consistency]
1381// If addressed, make sure to fix this in chainspec too
1382pub 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    // Charge for parameter weights.
1400    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            // Not a valid UTF-8 string
1420            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            // New topic is created
1446        }
1447        Err(MessageTopicError::DuplicateTopic) => {
1448            // We're lazily creating message topics and this operation is idempotent. Therefore
1449            // already existing topic is not an issue.
1450        }
1451        Err(MessageTopicError::MaxTopicsExceeded) => {
1452            // We're validating the size of topics before adding them
1453            return Ok(HOST_ERROR_TOO_MANY_TOPICS);
1454        }
1455        Err(MessageTopicError::TopicNameSizeExceeded) => {
1456            // We're validating the length of topic before adding it
1457            return Ok(HOST_ERROR_TOPIC_TOO_LONG);
1458        }
1459        Err(error) => {
1460            // These error variants are non_exhaustive, and we should handle them explicitly.
1461            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                    // NOTE: This assertion is to ensure that the message index is continuous, and
1490                    // the previous messages are pruned properly.
1491                    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            // Prune the previous messages
1502            caller.context_mut().tracking_copy.prune(message_key);
1503        }
1504        0
1505    } else {
1506        prev_topic_summary.message_count()
1507    };
1508
1509    // Data stored in the global state associated with the message block.
1510    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            // No messages in current block yet
1529            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    // Under v2 runtime messages are only limited to bytes.
1545    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    // Charge for amount as measured by serialized length
1569    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}