1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright (c) Zefchain Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Types and macros useful for writing an application contract.

mod conversions_from_wit;
mod conversions_to_wit;
#[cfg(not(with_testing))]
mod runtime;
#[cfg(with_testing)]
mod test_runtime;
#[doc(hidden)]
pub mod wit;

#[cfg(not(with_testing))]
pub use self::runtime::ContractRuntime;
#[cfg(with_testing)]
pub use self::test_runtime::MockContractRuntime;
#[doc(hidden)]
pub use self::wit::export_contract;
use crate::{log::ContractLogger, util::BlockingWait};

/// Inside tests, use the [`MockContractRuntime`] instead of the real [`ContractRuntime`].
#[cfg(with_testing)]
pub type ContractRuntime<Application> = MockContractRuntime<Application>;

/// Declares an implementation of the [`Contract`][`crate::Contract`] trait, exporting it from the
/// Wasm module.
///
/// Generates the necessary boilerplate for implementing the contract WIT interface, exporting the
/// necessary resource types and functions so that the host can call the application contract.
#[macro_export]
macro_rules! contract {
    ($contract:ident) => {
        #[doc(hidden)]
        static mut CONTRACT: Option<$contract> = None;

        /// Export the contract interface.
        $crate::export_contract!($contract with_types_in $crate::contract::wit);

        /// Mark the contract type to be exported.
        impl $crate::contract::wit::exports::linera::app::contract_entrypoints::Guest
            for $contract
        {
            fn instantiate(argument: Vec<u8>) {
                use $crate::util::BlockingWait;
                $crate::contract::run_async_entrypoint::<$contract, _, _>(
                    unsafe { &mut CONTRACT },
                    move |contract| {
                        let argument = $crate::serde_json::from_slice(&argument)
                            .expect("Failed to deserialize instantiation argument");

                        contract.instantiate(argument).blocking_wait()
                    },
                )
            }

            fn execute_operation(operation: Vec<u8>) -> Vec<u8> {
                use $crate::util::BlockingWait;
                $crate::contract::run_async_entrypoint::<$contract, _, _>(
                    unsafe { &mut CONTRACT },
                    move |contract| {
                        let operation: <$contract as $crate::abi::ContractAbi>::Operation =
                            $crate::bcs::from_bytes(&operation)
                                .expect("Failed to deserialize operation");

                        let response = contract.execute_operation(operation).blocking_wait();

                        $crate::bcs::to_bytes(&response)
                            .expect("Failed to serialize contract's `Response`")
                    },
                )
            }

            fn execute_message(message: Vec<u8>) {
                use $crate::util::BlockingWait;
                $crate::contract::run_async_entrypoint::<$contract, _, _>(
                    unsafe { &mut CONTRACT },
                    move |contract| {
                        let message: <$contract as $crate::Contract>::Message =
                            $crate::bcs::from_bytes(&message)
                                .expect("Failed to deserialize message");

                        contract.execute_message(message).blocking_wait()
                    },
                )
            }

            fn finalize() {
                use $crate::util::BlockingWait;

                let contract = unsafe { CONTRACT.take() }
                    .expect("Calling `store` on a `Contract` instance that wasn't loaded");

                contract.store().blocking_wait();
            }
        }

        /// Stub of a `main` entrypoint so that the binary doesn't fail to compile on targets other
        /// than WebAssembly.
        #[cfg(not(target_arch = "wasm32"))]
        fn main() {}
    };
}

/// Runs an asynchronous entrypoint in a blocking manner, by repeatedly polling the entrypoint
/// future.
pub fn run_async_entrypoint<Contract, Output, RawOutput>(
    contract: &mut Option<Contract>,
    entrypoint: impl FnOnce(&mut Contract) -> Output + Send,
) -> RawOutput
where
    Contract: crate::Contract,
    Output: Into<RawOutput> + Send + 'static,
{
    ContractLogger::install();

    let contract =
        contract.get_or_insert_with(|| Contract::load(ContractRuntime::new()).blocking_wait());

    entrypoint(contract).into()
}