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::{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(bytes) => {
469            let digest = Digest::hash(bytes);
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    let protocol_version_major = protocol_version.value().major;
655
656    let callee_addr = context_to_entity_addr(caller.context()).value();
657
658    let smart_contract_addr: HashAddr = chain_utils::compute_predictable_address(
659        caller.context().chain_name.as_bytes(),
660        callee_addr,
661        bytecode_hash,
662        seed,
663    );
664
665    smart_contract_package.insert_entity_version(
666        protocol_version_major,
667        EntityAddr::SmartContract(smart_contract_addr),
668    );
669
670    if caller
671        .context_mut()
672        .tracking_copy
673        .read(&Key::SmartContract(smart_contract_addr))
674        .map_err(|_| VMError::Internal(InternalHostError::TrackingCopy))?
675        .is_some()
676    {
677        return VMResult::Err(VMError::Internal(InternalHostError::ContractAlreadyExists));
678    }
679
680    metered_write(
681        &mut caller,
682        Key::SmartContract(smart_contract_addr),
683        StoredValue::SmartContract(smart_contract_package),
684    )?;
685
686    // 2. Store wasm
687    metered_write(
688        &mut caller,
689        Key::ByteCode(bytecode_addr),
690        StoredValue::ByteCode(bytecode),
691    )?;
692
693    // 3. Store addressable entity
694
695    let entity_addr = EntityAddr::SmartContract(smart_contract_addr);
696    let addressable_entity_key = Key::AddressableEntity(entity_addr);
697
698    // TODO: abort(str) as an alternative to trap
699    let address_generator = Arc::clone(&caller.context().address_generator);
700    let transaction_hash = caller.context().transaction_hash;
701    let main_purse: URef = match system::mint_mint(
702        &mut caller.context_mut().tracking_copy,
703        transaction_hash,
704        address_generator,
705        MintArgs {
706            initial_balance: U512::zero(),
707        },
708    ) {
709        Ok(uref) => uref,
710        Err(mint_error) => {
711            error!(?mint_error, "Failed to create a purse");
712            return Ok(CALLEE_TRAPPED);
713        }
714    };
715
716    let addressable_entity = AddressableEntity::new(
717        PackageHash::new(smart_contract_addr),
718        ByteCodeHash::new(bytecode_hash),
719        ProtocolVersion::V2_0_0,
720        main_purse,
721        AssociatedKeys::default(),
722        ActionThresholds::default(),
723        EntityKind::SmartContract(ContractRuntimeTag::VmCasperV2),
724    );
725
726    metered_write(
727        &mut caller,
728        addressable_entity_key,
729        StoredValue::AddressableEntity(addressable_entity),
730    )?;
731
732    let _initial_state = match constructor_entry_point {
733        Some(entry_point_name) => {
734            // Take the gas spent so far and use it as a limit for the new VM.
735            let gas_limit = caller
736                .gas_consumed()
737                .try_into_remaining()
738                .map_err(|_| InternalHostError::TypeConversion)?;
739
740            let execute_request = ExecuteRequestBuilder::default()
741                .with_initiator(caller.context().initiator)
742                .with_caller_key(caller.context().callee)
743                .with_gas_limit(gas_limit)
744                .with_target(ExecutionKind::Stored {
745                    address: smart_contract_addr,
746                    entry_point: entry_point_name.clone(),
747                })
748                .with_input(input_data.unwrap_or_default())
749                .with_transferred_value(transferred_value)
750                .with_transaction_hash(caller.context().transaction_hash)
751                // We're using shared address generator there as we need to preserve and advance the
752                // state of deterministic address generator across chain of calls.
753                .with_shared_address_generator(Arc::clone(&caller.context().address_generator))
754                .with_chain_name(caller.context().chain_name.clone())
755                .with_block_time(caller.context().block_time)
756                .with_state_hash(Digest::from_raw([0; 32])) // TODO: Carry on state root hash
757                .with_block_height(1) // TODO: Carry on block height
758                .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) // TODO: Carry on parent block hash
759                .build()
760                .map_err(|_| InternalHostError::ExecuteRequestBuildFailure)?;
761
762            let tracking_copy_for_ctor = caller.context().tracking_copy.fork2();
763
764            match caller
765                .context()
766                .executor
767                .execute(tracking_copy_for_ctor, execute_request)
768            {
769                Ok(ExecuteResult {
770                    host_error,
771                    output,
772                    gas_usage,
773                    effects,
774                    cache,
775                    messages,
776                }) => {
777                    // output
778                    caller.consume_gas(gas_usage.gas_spent())?;
779
780                    if let Some(host_error) = host_error {
781                        return Ok(host_error.into_u32());
782                    }
783
784                    caller
785                        .context_mut()
786                        .tracking_copy
787                        .apply_changes(effects, cache, messages);
788
789                    output
790                }
791                Err(execute_error) => {
792                    // This is a bug in the EE, as it should have been caught during the preparation
793                    // phase when the contract was stored in the global state.
794                    error!(?execute_error, "Failed to execute constructor entry point");
795                    return Err(VMError::Execute(execute_error));
796                }
797            }
798        }
799        None => None,
800    };
801
802    let create_result = CreateResult {
803        package_address: smart_contract_addr,
804    };
805
806    let create_result_bytes = safe_transmute::transmute_one_to_bytes(&create_result);
807
808    debug_assert_eq!(
809        safe_transmute::transmute_one(create_result_bytes),
810        Ok(create_result),
811        "Sanity check", // NOTE: Remove these guards with sufficient test coverage
812    );
813
814    caller.memory_write(result_ptr, create_result_bytes)?;
815
816    Ok(CALLEE_SUCCEEDED)
817}
818
819#[allow(clippy::too_many_arguments)]
820pub fn casper_call<S: GlobalStateReader + 'static, E: Executor + 'static>(
821    mut caller: impl Caller<Context = Context<S, E>>,
822    address_ptr: u32,
823    address_len: u32,
824    transferred_value: u64,
825    entry_point_ptr: u32,
826    entry_point_len: u32,
827    input_ptr: u32,
828    input_len: u32,
829    cb_alloc: u32,
830    cb_ctx: u32,
831) -> VMResult<u32> {
832    let call_cost = caller.context().config.host_function_costs().call;
833    charge_host_function_call(
834        &mut caller,
835        &call_cost,
836        [
837            u64::from(address_ptr),
838            u64::from(address_len),
839            transferred_value,
840            u64::from(entry_point_ptr),
841            u64::from(entry_point_len),
842            u64::from(input_ptr),
843            u64::from(input_len),
844            u64::from(cb_alloc),
845            u64::from(cb_ctx),
846        ],
847    )?;
848
849    // 1. Look up address in the storage
850    // 1a. if it's legacy contract, wire up old EE, pretend you're 1.x. Input data would be
851    // "RuntimeArgs". Serialized output of the call has to be passed as output. Value is ignored as
852    // you can't pass value (tokens) to called contracts. 1b. if it's new contract, wire up
853    // another VM as according to the bytecode format. 2. Depends on the VM used (old or new) at
854    // this point either entry point is validated (i.e. EE returned error) or will be validated as
855    // for now. 3. If entry point is valid, call it, transfer the value, pass the input data. If
856    // it's invalid, return error. 4. Output data is captured by calling `cb_alloc`.
857    // let vm = VM::new();
858    // vm.
859    let address = caller.memory_read(address_ptr, address_len as _)?;
860    let smart_contract_addr: HashAddr = address.try_into_wrapped()?;
861
862    let input_data: Bytes = caller.memory_read(input_ptr, input_len as _)?.into();
863
864    let entry_point = {
865        let entry_point_bytes = caller.memory_read(entry_point_ptr, entry_point_len as _)?;
866        match String::from_utf8(entry_point_bytes) {
867            Ok(entry_point) => entry_point,
868            Err(utf8_error) => {
869                error!(%utf8_error, "entry point name is not a valid utf-8 string; unable to call");
870                return Ok(CALLEE_NOT_CALLABLE);
871            }
872        }
873    };
874
875    let tracking_copy = caller.context().tracking_copy.fork2();
876
877    // Take the gas spent so far and use it as a limit for the new VM.
878    let gas_limit = caller
879        .gas_consumed()
880        .try_into_remaining()
881        .map_err(|_| InternalHostError::TypeConversion)?;
882
883    let execute_request = ExecuteRequestBuilder::default()
884        .with_initiator(caller.context().initiator)
885        .with_caller_key(caller.context().callee)
886        .with_gas_limit(gas_limit)
887        .with_target(ExecutionKind::Stored {
888            address: smart_contract_addr,
889            entry_point: entry_point.clone(),
890        })
891        .with_transferred_value(transferred_value)
892        .with_input(input_data)
893        .with_transaction_hash(caller.context().transaction_hash)
894        // We're using shared address generator there as we need to preserve and advance the state
895        // of deterministic address generator across chain of calls.
896        .with_shared_address_generator(Arc::clone(&caller.context().address_generator))
897        .with_chain_name(caller.context().chain_name.clone())
898        .with_block_time(caller.context().block_time)
899        .with_state_hash(Digest::from_raw([0; 32])) // TODO: Carry on state root hash
900        .with_block_height(1) // TODO: Carry on block height
901        .with_parent_block_hash(BlockHash::new(Digest::from_raw([0; 32]))) // TODO: Carry on parent block hash
902        .build()
903        .map_err(|_| InternalHostError::ExecuteRequestBuildFailure)?;
904
905    let (gas_usage, host_result) = match caller
906        .context()
907        .executor
908        .execute(tracking_copy, execute_request)
909    {
910        Ok(ExecuteResult {
911            host_error,
912            output,
913            gas_usage,
914            effects,
915            cache,
916            messages,
917        }) => {
918            if let Some(output) = output {
919                let out_ptr: u32 = if cb_alloc != 0 {
920                    caller.alloc(cb_alloc, output.len(), cb_ctx)?
921                } else {
922                    // treats alloc_ctx as data
923                    cb_ctx
924                };
925
926                if out_ptr != 0 {
927                    caller.memory_write(out_ptr, &output)?;
928                }
929            }
930
931            let host_result = match host_error {
932                Some(host_error) => Err(host_error),
933                None => {
934                    caller
935                        .context_mut()
936                        .tracking_copy
937                        .apply_changes(effects, cache, messages);
938                    Ok(())
939                }
940            };
941
942            (gas_usage, host_result)
943        }
944        Err(execute_error) => {
945            error!(
946                ?execute_error,
947                ?smart_contract_addr,
948                ?entry_point,
949                "Failed to execute entry point"
950            );
951            return Err(VMError::Execute(execute_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(execute_error) => {
1449                // Unable to call contract because of execution error or internal host error.
1450                // This usually means an internal error that should not happen and has to be handled
1451                // by the contract runtime.
1452                error!(
1453                    ?execute_error,
1454                    ?entry_point_name,
1455                    smart_contract_addr = base16::encode_lower(&smart_contract_addr),
1456                    "Failed to execute upgrade entry point"
1457                );
1458                return Err(VMError::Execute(execute_error));
1459            }
1460        }
1461    }
1462
1463    Ok(CALLEE_SUCCEEDED)
1464}
1465
1466pub fn casper_env_info<S: GlobalStateReader, E: Executor>(
1467    mut caller: impl Caller<Context = Context<S, E>>,
1468    info_ptr: u32,
1469    info_size: u32,
1470) -> VMResult<u32> {
1471    let block_time_cost = caller.context().config.host_function_costs().env_info;
1472    charge_host_function_call(
1473        &mut caller,
1474        &block_time_cost,
1475        [u64::from(info_ptr), u64::from(info_size)],
1476    )?;
1477
1478    let (caller_kind, caller_addr) = match &caller.context().caller {
1479        Key::Account(account_hash) => (EntityKindTag::Account as u32, account_hash.value()),
1480        Key::SmartContract(smart_contract_addr) => {
1481            (EntityKindTag::Contract as u32, *smart_contract_addr)
1482        }
1483        other => panic!("Unexpected caller: {other:?}"),
1484    };
1485
1486    let (callee_kind, callee_addr) = match &caller.context().callee {
1487        Key::Account(initiator_addr) => (EntityKindTag::Account as u32, initiator_addr.value()),
1488        Key::SmartContract(smart_contract_addr) => {
1489            (EntityKindTag::Contract as u32, *smart_contract_addr)
1490        }
1491        other => panic!("Unexpected callee: {other:?}"),
1492    };
1493
1494    let transferred_value = caller.context().transferred_value;
1495
1496    let block_time = caller.context().block_time.value();
1497
1498    // `EnvInfo` in little-endian representation.
1499    let env_info_le = EnvInfo {
1500        caller_addr,
1501        caller_kind: caller_kind.to_le(),
1502        callee_addr,
1503        callee_kind: callee_kind.to_le(),
1504        transferred_value: transferred_value.to_le(),
1505        block_time: block_time.to_le(),
1506    };
1507
1508    let env_info_bytes = safe_transmute::transmute_one_to_bytes(&env_info_le);
1509    let write_len = env_info_bytes.len().min(info_size as usize);
1510    caller.memory_write(info_ptr, &env_info_bytes[..write_len])?;
1511
1512    Ok(HOST_ERROR_SUCCESS)
1513}
1514
1515pub fn casper_emit<S: GlobalStateReader, E: Executor>(
1516    mut caller: impl Caller<Context = Context<S, E>>,
1517    topic_name_ptr: u32,
1518    topic_name_size: u32,
1519    payload_ptr: u32,
1520    payload_size: u32,
1521) -> VMResult<u32> {
1522    // Charge for parameter weights.
1523    let emit_host_function = caller.context().config.host_function_costs().emit;
1524
1525    charge_host_function_call(
1526        &mut caller,
1527        &emit_host_function,
1528        [
1529            u64::from(topic_name_ptr),
1530            u64::from(topic_name_size),
1531            u64::from(payload_ptr),
1532            u64::from(payload_size),
1533        ],
1534    )?;
1535
1536    if topic_name_size > caller.context().message_limits.max_topic_name_size {
1537        return Ok(HOST_ERROR_TOPIC_TOO_LONG);
1538    }
1539
1540    if payload_size > caller.context().message_limits.max_message_size {
1541        return Ok(HOST_ERROR_PAYLOAD_TOO_LONG);
1542    }
1543
1544    let topic_name = {
1545        let topic: Vec<u8> = caller.memory_read(topic_name_ptr, topic_name_size as usize)?;
1546        let Ok(topic) = String::from_utf8(topic) else {
1547            // Not a valid UTF-8 string
1548            return Ok(HOST_ERROR_INVALID_DATA);
1549        };
1550        topic
1551    };
1552
1553    let payload = caller.memory_read(payload_ptr, payload_size as usize)?;
1554
1555    let entity_addr = context_to_entity_addr(caller.context());
1556
1557    let mut message_topics = caller
1558        .context_mut()
1559        .tracking_copy
1560        .get_message_topics(entity_addr)
1561        .unwrap_or_else(|error| {
1562            panic!("Error while reading from storage; aborting error={error:?}")
1563        });
1564
1565    if message_topics.len() >= caller.context().message_limits.max_topics_per_contract as usize {
1566        return Ok(HOST_ERROR_TOO_MANY_TOPICS);
1567    }
1568
1569    let topic_name_hash = Digest::hash(&topic_name).value().into();
1570
1571    match message_topics.add_topic(&topic_name, topic_name_hash) {
1572        Ok(()) => {
1573            // New topic is created
1574        }
1575        Err(MessageTopicError::DuplicateTopic) => {
1576            // We're lazily creating message topics and this operation is idempotent. Therefore
1577            // already existing topic is not an issue.
1578        }
1579        Err(MessageTopicError::MaxTopicsExceeded) => {
1580            // We're validating the size of topics before adding them
1581            return Ok(HOST_ERROR_TOO_MANY_TOPICS);
1582        }
1583        Err(MessageTopicError::TopicNameSizeExceeded) => {
1584            // We're validating the length of topic before adding it
1585            return Ok(HOST_ERROR_TOPIC_TOO_LONG);
1586        }
1587        Err(error) => {
1588            // These error variants are non_exhaustive, and we should handle them explicitly.
1589            unreachable!("Unexpected error while adding a topic: {:?}", error);
1590        }
1591    };
1592
1593    let current_block_time = caller.context().block_time;
1594    eprintln!("📩 {topic_name}: {payload:?} (at {current_block_time:?})");
1595
1596    let topic_key = Key::Message(MessageAddr::new_topic_addr(entity_addr, topic_name_hash));
1597    let prev_topic_summary = match caller.context_mut().tracking_copy.read(&topic_key) {
1598        Ok(Some(StoredValue::MessageTopic(message_topic_summary))) => message_topic_summary,
1599        Ok(Some(stored_value)) => {
1600            panic!("Unexpected stored value: {stored_value:?}");
1601        }
1602        Ok(None) => {
1603            let message_topic_summary =
1604                MessageTopicSummary::new(0, current_block_time, topic_name.clone());
1605            let summary = StoredValue::MessageTopic(message_topic_summary.clone());
1606            caller.context_mut().tracking_copy.write(topic_key, summary);
1607            message_topic_summary
1608        }
1609        Err(error) => panic!("Error while reading from storage; aborting error={error:?}"),
1610    };
1611
1612    let topic_message_index = if prev_topic_summary.blocktime() != current_block_time {
1613        for index in 1..prev_topic_summary.message_count() {
1614            let message_key = Key::message(entity_addr, topic_name_hash, index);
1615            debug_assert!(
1616                {
1617                    // NOTE: This assertion is to ensure that the message index is continuous, and
1618                    // the previous messages are pruned properly.
1619                    caller
1620                        .context_mut()
1621                        .tracking_copy
1622                        .read(&message_key)
1623                        .map_err(|_| VMError::Internal(InternalHostError::TrackingCopy))?
1624                        .is_some()
1625                },
1626                "Message index is not continuous"
1627            );
1628
1629            // Prune the previous messages
1630            caller.context_mut().tracking_copy.prune(message_key);
1631        }
1632        0
1633    } else {
1634        prev_topic_summary.message_count()
1635    };
1636
1637    // Data stored in the global state associated with the message block.
1638    type MessageCountPair = (BlockTime, u64);
1639
1640    let block_message_index: u64 = match caller
1641        .context_mut()
1642        .tracking_copy
1643        .read(&Key::BlockGlobal(BlockGlobalAddr::MessageCount))
1644    {
1645        Ok(Some(StoredValue::CLValue(value_pair))) => {
1646            let (prev_block_time, prev_count): MessageCountPair =
1647                CLValue::into_t(value_pair).map_err(|_| InternalHostError::TypeConversion)?;
1648            if prev_block_time == current_block_time {
1649                prev_count
1650            } else {
1651                0
1652            }
1653        }
1654        Ok(Some(other)) => panic!("Unexpected stored value: {other:?}"),
1655        Ok(None) => {
1656            // No messages in current block yet
1657            0
1658        }
1659        Err(error) => {
1660            panic!("Error while reading from storage; aborting error={error:?}")
1661        }
1662    };
1663
1664    let Some(topic_message_count) = topic_message_index.checked_add(1) else {
1665        return Ok(HOST_ERROR_MESSAGE_TOPIC_FULL);
1666    };
1667
1668    let Some(block_message_count) = block_message_index.checked_add(1) else {
1669        return Ok(HOST_ERROR_MAX_MESSAGES_PER_BLOCK_EXCEEDED);
1670    };
1671
1672    // Under v2 runtime messages are only limited to bytes.
1673    let message_payload = MessagePayload::Bytes(payload.into());
1674
1675    let message = Message::new(
1676        entity_addr,
1677        message_payload,
1678        topic_name,
1679        topic_name_hash,
1680        topic_message_index,
1681        block_message_index,
1682    );
1683    let topic_value = StoredValue::MessageTopic(MessageTopicSummary::new(
1684        topic_message_count,
1685        current_block_time,
1686        message.topic_name().to_owned(),
1687    ));
1688
1689    let message_key = message.message_key();
1690    let message_value = StoredValue::Message(
1691        message
1692            .checksum()
1693            .map_err(|_| InternalHostError::MessageChecksumMissing)?,
1694    );
1695    let message_count_pair: MessageCountPair = (current_block_time, block_message_count);
1696    let block_message_count_value = StoredValue::CLValue(
1697        CLValue::from_t(message_count_pair).map_err(|_| InternalHostError::TypeConversion)?,
1698    );
1699
1700    // Charge for amount as measured by serialized length
1701    let bytes_count = topic_value.serialized_length()
1702        + message_value.serialized_length()
1703        + block_message_count_value.serialized_length();
1704    charge_gas_storage(&mut caller, bytes_count)?;
1705
1706    caller.context_mut().tracking_copy.emit_message(
1707        topic_key,
1708        topic_value,
1709        message_key,
1710        message_value,
1711        block_message_count_value,
1712        message,
1713    );
1714
1715    Ok(HOST_ERROR_SUCCESS)
1716}