casper_executor_wasm_host/
host.rs

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