Skip to main content

oasis_runtime_sdk_contracts/
lib.rs

1//! Smart contracts module.
2#![deny(rust_2018_idioms)]
3#![forbid(unsafe_code)]
4#![cfg_attr(all(feature = "benchmarks", test), feature(test))]
5
6#[cfg(test)]
7extern crate alloc;
8
9use std::{convert::TryInto, io::Read};
10
11use thiserror::Error;
12
13use oasis_contract_sdk_types::storage::StoreKind;
14use oasis_runtime_sdk::{
15    self as sdk,
16    context::Context,
17    core::common::crypto::hash::Hash,
18    handler, migration, module,
19    module::Module as _,
20    modules,
21    modules::{accounts::API as _, core::API as _},
22    runtime::Runtime,
23    sdk_derive,
24    state::CurrentState,
25    storage,
26    storage::Store,
27    types::transaction::CallFormat,
28};
29
30use crate::store::with_instance_raw_store;
31
32mod abi;
33mod code;
34mod results;
35mod store;
36#[cfg(test)]
37mod test;
38pub mod types;
39mod wasm;
40
41/// Unique module name.
42const MODULE_NAME: &str = "contracts";
43
44/// Errors emitted by the contracts module.
45#[derive(Error, Debug, sdk::Error)]
46pub enum Error {
47    #[error("invalid argument")]
48    #[sdk_error(code = 1)]
49    InvalidArgument,
50
51    #[error("code too large (size: {0} max: {1})")]
52    #[sdk_error(code = 2)]
53    CodeTooLarge(u32, u32),
54
55    #[error("code is malformed")]
56    #[sdk_error(code = 3)]
57    CodeMalformed,
58
59    #[error("specified ABI is not supported")]
60    #[sdk_error(code = 4)]
61    UnsupportedABI,
62
63    #[error("code is missing required ABI export: {0}")]
64    #[sdk_error(code = 5)]
65    CodeMissingRequiredExport(String),
66
67    #[error("code declares reserved ABI export: {0}")]
68    #[sdk_error(code = 6)]
69    CodeDeclaresReservedExport(String),
70
71    #[error("code declares start function")]
72    #[sdk_error(code = 7)]
73    CodeDeclaresStartFunction,
74
75    #[error("code declares too many memories")]
76    #[sdk_error(code = 8)]
77    CodeDeclaresTooManyMemories,
78
79    #[error("code {0} not found")]
80    #[sdk_error(code = 9)]
81    CodeNotFound(u64),
82
83    #[error("instance {0} not found")]
84    #[sdk_error(code = 10)]
85    InstanceNotFound(u64),
86
87    #[error("module loading failed")]
88    #[sdk_error(code = 11)]
89    ModuleLoadingFailed,
90
91    #[error("execution failed: {0}")]
92    #[sdk_error(code = 12)]
93    ExecutionFailed(#[source] anyhow::Error),
94
95    #[error("forbidden by policy")]
96    #[sdk_error(code = 13)]
97    Forbidden,
98
99    #[error("function not supported")]
100    #[sdk_error(code = 14)]
101    Unsupported,
102
103    #[error("insufficient balance in caller account")]
104    #[sdk_error(code = 15)]
105    InsufficientCallerBalance,
106
107    // Error code 16 is reserved.
108    #[error("result size exceeded (size: {0} max: {1})")]
109    #[sdk_error(code = 17)]
110    ResultTooLarge(u32, u32),
111
112    #[error("too many subcalls (count: {0} max: {1})")]
113    #[sdk_error(code = 18)]
114    TooManySubcalls(u16, u16),
115
116    #[error("instance is already using code {0}")]
117    #[sdk_error(code = 19)]
118    CodeAlreadyUpgraded(u64),
119
120    #[error("abort: {0}")]
121    #[sdk_error(code = 20, abort)]
122    Abort(#[from] sdk::dispatcher::Error),
123
124    #[error("storage: key too large (size: {0} max: {1})")]
125    #[sdk_error(code = 21)]
126    StorageKeyTooLarge(u32, u32),
127
128    #[error("storage: value too large (size: {0} max: {1})")]
129    #[sdk_error(code = 22)]
130    StorageValueTooLarge(u32, u32),
131
132    #[error("crypto: msg too large (size: {0} max: {1})")]
133    #[sdk_error(code = 23)]
134    CryptoMsgTooLarge(u32, u32),
135
136    #[error("crypto: malformed public key")]
137    #[sdk_error(code = 24)]
138    CryptoMalformedPublicKey,
139
140    #[error("code declares multiple sub-versions")]
141    #[sdk_error(code = 25)]
142    CodeDeclaresMultipleSubVersions,
143
144    #[error("crypto: malformed private key")]
145    #[sdk_error(code = 26)]
146    CryptoMalformedPrivateKey,
147
148    #[error("crypto: malformed encryption key")]
149    #[sdk_error(code = 27)]
150    CryptoMalformedKey,
151
152    #[error("crypto: malformed nonce")]
153    #[sdk_error(code = 28)]
154    CryptoMalformedNonce,
155
156    #[error("crypto: key derivation function failure")]
157    #[sdk_error(code = 29)]
158    CryptoKeyDerivationFunctionFailure,
159
160    #[error("module uses floating point data or operations")]
161    #[sdk_error(code = 30)]
162    ModuleUsesFloatingPoint,
163
164    #[error("code declares too many functions")]
165    #[sdk_error(code = 31)]
166    CodeDeclaresTooManyFunctions,
167
168    #[error("code declares too many locals")]
169    #[sdk_error(code = 32)]
170    CodeDeclaresTooManyLocals,
171
172    #[error("code declares too deep nesting")]
173    #[sdk_error(code = 33)]
174    CodeDeclaresTooDeepNesting,
175
176    #[error("core: {0}")]
177    #[sdk_error(transparent)]
178    Core(#[from] modules::core::Error),
179
180    #[error("contract error: {0}")]
181    #[sdk_error(transparent)]
182    Contract(#[from] wasm::ContractError),
183}
184
185/// Events emitted by the contracts module.
186#[derive(Debug, cbor::Encode, sdk::Event)]
187#[cbor(untagged)]
188pub enum Event {}
189
190/// Gas costs.
191#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
192pub struct GasCosts {
193    pub tx_upload: u64,
194    pub tx_upload_per_byte: u64,
195    pub tx_instantiate: u64,
196    pub tx_call: u64,
197    pub tx_upgrade: u64,
198    pub tx_change_upgrade_policy: u64,
199
200    // Subcalls.
201    pub subcall_dispatch: u64,
202
203    // Storage operations.
204    pub wasm_public_storage_get_base: u64,
205    pub wasm_public_storage_insert_base: u64,
206    pub wasm_public_storage_remove_base: u64,
207    pub wasm_public_storage_key_byte: u64,
208    pub wasm_public_storage_value_byte: u64,
209    pub wasm_confidential_storage_get_base: u64,
210    pub wasm_confidential_storage_insert_base: u64,
211    pub wasm_confidential_storage_remove_base: u64,
212    pub wasm_confidential_storage_key_byte: u64,
213    pub wasm_confidential_storage_value_byte: u64,
214    pub wasm_env_query_base: u64,
215
216    // Crypto operations.
217    pub wasm_crypto_ecdsa_recover: u64,
218    pub wasm_crypto_signature_verify_ed25519: u64,
219    pub wasm_crypto_signature_verify_secp256k1: u64,
220    pub wasm_crypto_signature_verify_sr25519: u64,
221    pub wasm_crypto_x25519_derive_symmetric: u64,
222    pub wasm_crypto_deoxysii_base: u64,
223    pub wasm_crypto_deoxysii_byte: u64,
224    pub wasm_crypto_random_bytes_base: u64,
225    pub wasm_crypto_random_bytes_byte: u64,
226}
227
228impl Default for GasCosts {
229    fn default() -> Self {
230        // The below assume a batch gas limit of 1_000_000_000 ~ 1s.
231        GasCosts {
232            tx_upload: 30_000_000,
233            tx_upload_per_byte: 400,
234            tx_instantiate: 100_000,
235            tx_call: 50_000,
236            tx_upgrade: 50_000,
237            tx_change_upgrade_policy: 30_000,
238
239            subcall_dispatch: 1_000,
240
241            wasm_public_storage_get_base: 5_000,
242            wasm_public_storage_insert_base: 8_400,
243            wasm_public_storage_remove_base: 6_400,
244            wasm_public_storage_key_byte: 3_000,
245            wasm_public_storage_value_byte: 300,
246            wasm_confidential_storage_get_base: 10_000,
247            wasm_confidential_storage_insert_base: 16_800,
248            wasm_confidential_storage_remove_base: 12_800,
249            wasm_confidential_storage_key_byte: 3_500,
250            wasm_confidential_storage_value_byte: 400,
251            wasm_env_query_base: 100,
252
253            wasm_crypto_ecdsa_recover: 500_000,
254            wasm_crypto_signature_verify_ed25519: 500_000,
255            wasm_crypto_signature_verify_secp256k1: 500_000,
256            wasm_crypto_signature_verify_sr25519: 500_000,
257            wasm_crypto_x25519_derive_symmetric: 250_000,
258            wasm_crypto_deoxysii_base: 1_000,
259            wasm_crypto_deoxysii_byte: 3,
260            wasm_crypto_random_bytes_base: 1_000,
261            wasm_crypto_random_bytes_byte: 3,
262        }
263    }
264}
265
266/// Parameters for the contracts module.
267#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
268pub struct Parameters {
269    pub max_code_size: u32,
270    pub max_stack_size: u32,
271    pub max_memory_pages: u32,
272
273    pub max_wasm_functions: u32,
274    pub max_wasm_locals: u32,
275
276    pub max_subcall_depth: u16,
277    pub max_subcall_count: u16,
278
279    pub max_result_size_bytes: u32,
280    pub max_query_size_bytes: u32,
281    pub max_storage_key_size_bytes: u32,
282    pub max_storage_value_size_bytes: u32,
283    pub max_crypto_signature_verify_message_size_bytes: u32,
284
285    pub gas_costs: GasCosts,
286}
287
288impl Default for Parameters {
289    fn default() -> Self {
290        Parameters {
291            max_code_size: 1024 * 1024, // 1 MiB
292            max_stack_size: 60 * 1024,  // 60 KiB
293            max_memory_pages: 160,      // 10 MiB
294
295            max_wasm_functions: 10_000,
296            max_wasm_locals: 256_000,
297
298            max_subcall_depth: 8,
299            max_subcall_count: 16,
300
301            max_result_size_bytes: 1024, // 1 KiB
302            max_query_size_bytes: 1024,  // 1 KiB
303            max_storage_key_size_bytes: 64,
304            max_storage_value_size_bytes: 16 * 1024, // 16 KiB
305            max_crypto_signature_verify_message_size_bytes: 16 * 1024, // 16KiB
306
307            gas_costs: Default::default(),
308        }
309    }
310}
311
312impl module::Parameters for Parameters {
313    type Error = std::convert::Infallible;
314}
315
316/// Genesis state for the contracts module.
317#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
318pub struct Genesis {
319    pub parameters: Parameters,
320}
321
322/// Local configuration that can be provided by the node operator.
323#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
324pub struct LocalConfig {
325    /// Gas limit for custom queries that invoke smart contracts.
326    #[cbor(optional)]
327    pub query_custom_max_gas: u64,
328
329    /// Maximum number of items per page in InstanceRawStorage query result.
330    #[cbor(optional)]
331    pub max_instance_raw_storage_query_items: u64,
332}
333
334impl Default for LocalConfig {
335    fn default() -> Self {
336        Self {
337            query_custom_max_gas: 10_000_000,
338            max_instance_raw_storage_query_items: 100,
339        }
340    }
341}
342
343/// State schema constants.
344pub mod state {
345    /// Next code identifier (u64).
346    pub const NEXT_CODE_IDENTIFIER: &[u8] = &[0x01];
347    /// Next instance identifier (u64).
348    pub const NEXT_INSTANCE_IDENTIFIER: &[u8] = &[0x02];
349    /// Information about uploaded code.
350    pub const CODE_INFO: &[u8] = &[0x03];
351    /// Information about the deployed contract instance.
352    pub const INSTANCE_INFO: &[u8] = &[0x04];
353    /// Per-instance key/value store.
354    pub const INSTANCE_STATE: &[u8] = &[0x05];
355
356    /// Uploaded code.
357    pub const CODE: &[u8] = &[0xFF];
358}
359
360/// Module configuration.
361pub trait Config: 'static {}
362
363pub struct Module<Cfg: Config> {
364    _cfg: std::marker::PhantomData<Cfg>,
365}
366
367impl<Cfg: Config> Module<Cfg> {
368    /// Loads code information for the specified code identifier.
369    fn load_code_info(code_id: types::CodeId) -> Result<types::Code, Error> {
370        CurrentState::with_store(|store| {
371            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
372            let code_info_store =
373                storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::CODE_INFO));
374            let code_info = code_info_store
375                .get(code_id.to_storage_key())
376                .ok_or_else(|| Error::CodeNotFound(code_id.as_u64()))?;
377
378            Ok(code_info)
379        })
380    }
381
382    /// Stores specified code information.
383    fn store_code_info(code_info: types::Code) -> Result<(), Error> {
384        CurrentState::with_store(|store| {
385            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
386            let mut code_info_store =
387                storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::CODE_INFO));
388            code_info_store.insert(code_info.id.to_storage_key(), code_info);
389
390            Ok(())
391        })
392    }
393
394    /// Loads specified instance information.
395    fn load_instance_info(instance_id: types::InstanceId) -> Result<types::Instance, Error> {
396        CurrentState::with_store(|store| {
397            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
398            let instance_info_store = storage::TypedStore::new(storage::PrefixStore::new(
399                &mut store,
400                &state::INSTANCE_INFO,
401            ));
402            let instance_info = instance_info_store
403                .get(instance_id.to_storage_key())
404                .ok_or_else(|| Error::InstanceNotFound(instance_id.as_u64()))?;
405
406            Ok(instance_info)
407        })
408    }
409
410    /// Stores specified instance information.
411    fn store_instance_info(instance_info: types::Instance) -> Result<(), Error> {
412        CurrentState::with_store(|store| {
413            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
414            let mut instance_info_store = storage::TypedStore::new(storage::PrefixStore::new(
415                &mut store,
416                &state::INSTANCE_INFO,
417            ));
418            instance_info_store.insert(instance_info.id.to_storage_key(), instance_info);
419
420            Ok(())
421        })
422    }
423}
424
425#[sdk_derive(Module)]
426impl<Cfg: Config> Module<Cfg> {
427    const NAME: &'static str = MODULE_NAME;
428    type Error = Error;
429    type Event = Event;
430    type Parameters = Parameters;
431    type Genesis = Genesis;
432
433    #[migration(init)]
434    fn init(genesis: Genesis) {
435        // Set genesis parameters.
436        Self::set_params(genesis.parameters);
437    }
438
439    #[handler(call = "contracts.Upload")]
440    pub fn tx_upload<C: Context>(
441        ctx: &C,
442        body: types::Upload,
443    ) -> Result<types::UploadResult, Error> {
444        let params = Self::params();
445        let uploader = CurrentState::with_env(|env| env.tx_caller_address());
446
447        // Validate code size.
448        let code_size: u32 = body
449            .code
450            .len()
451            .try_into()
452            .map_err(|_| Error::CodeTooLarge(u32::MAX, params.max_code_size))?;
453        if code_size > params.max_code_size {
454            return Err(Error::CodeTooLarge(code_size, params.max_code_size));
455        }
456
457        // Account for base gas.
458        <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_upload)?;
459        <C::Runtime as Runtime>::Core::use_tx_gas(
460            params
461                .gas_costs
462                .tx_upload_per_byte
463                .saturating_mul(body.code.len() as u64),
464        )?;
465
466        // Decompress code.
467        let mut code = Vec::with_capacity(body.code.len());
468        let decoder = snap::read::FrameDecoder::new(body.code.as_slice());
469        decoder
470            .take(params.max_code_size.into())
471            .read_to_end(&mut code)
472            .map_err(|_| Error::CodeMalformed)?;
473
474        // Account for extra gas needed after decompression.
475        let plain_code_size: u32 = code.len().try_into().unwrap();
476        <C::Runtime as Runtime>::Core::use_tx_gas(
477            params
478                .gas_costs
479                .tx_upload_per_byte
480                .saturating_mul(plain_code_size.saturating_sub(code_size) as u64),
481        )?;
482
483        if !ctx.should_execute_contracts() {
484            // Only fast checks are allowed.
485            return Ok(types::UploadResult::default());
486        }
487
488        // Validate and transform the code.
489        let (code, abi_info) = wasm::validate_and_transform::<Cfg, C>(&code, body.abi, &params)?;
490        let hash = Hash::digest_bytes(&code);
491
492        // Validate code size again and account for any instrumentation. This is here to avoid any
493        // incentives in generating code that gets maximally inflated after instrumentation.
494        let inst_code_size: u32 = code
495            .len()
496            .try_into()
497            .map_err(|_| Error::CodeTooLarge(u32::MAX, params.max_code_size))?;
498        if inst_code_size > params.max_code_size {
499            return Err(Error::CodeTooLarge(inst_code_size, params.max_code_size));
500        }
501        <C::Runtime as Runtime>::Core::use_tx_gas(
502            params
503                .gas_costs
504                .tx_upload_per_byte
505                .saturating_mul(inst_code_size.saturating_sub(plain_code_size) as u64),
506        )?;
507
508        // Assign next identifier.
509        let id = CurrentState::with_store(|store| {
510            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
511            let mut tstore = storage::TypedStore::new(&mut store);
512            let id: types::CodeId = tstore.get(state::NEXT_CODE_IDENTIFIER).unwrap_or_default();
513            tstore.insert(state::NEXT_CODE_IDENTIFIER, id.increment());
514            id
515        });
516
517        // Store information about uploaded code.
518        let code_info = types::Code {
519            id,
520            hash,
521            abi: body.abi,
522            abi_sv: abi_info.abi_sv,
523            uploader,
524            instantiate_policy: body.instantiate_policy,
525        };
526        Self::store_code(&code_info, &code)?;
527        Self::store_code_info(code_info)?;
528
529        Ok(types::UploadResult { id })
530    }
531
532    #[handler(call = "contracts.Instantiate")]
533    pub fn tx_instantiate<C: Context>(
534        ctx: &C,
535        body: types::Instantiate,
536    ) -> Result<types::InstantiateResult, Error> {
537        let params = Self::params();
538        let creator = CurrentState::with_env(|env| env.tx_caller_address());
539
540        <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_instantiate)?;
541
542        if !ctx.should_execute_contracts() {
543            // Only fast checks are allowed.
544            return Ok(types::InstantiateResult::default());
545        }
546
547        // Load code information, enforce instantiation policy and load the code.
548        let code_info = Self::load_code_info(body.code_id)?;
549        code_info.instantiate_policy.enforce(&creator)?;
550        let code = Self::load_code(&code_info)?;
551
552        // Assign next identifier.
553        let id = CurrentState::with_store(|store| {
554            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
555            let mut tstore = storage::TypedStore::new(&mut store);
556            let id: types::InstanceId = tstore
557                .get(state::NEXT_INSTANCE_IDENTIFIER)
558                .unwrap_or_default();
559            tstore.insert(state::NEXT_INSTANCE_IDENTIFIER, id.increment());
560            id
561        });
562
563        // Store instance information.
564        let instance_info = types::Instance {
565            id,
566            code_id: body.code_id,
567            creator,
568            upgrades_policy: body.upgrades_policy,
569        };
570        Self::store_instance_info(instance_info.clone())?;
571
572        // Transfer any attached tokens.
573        for tokens in &body.tokens {
574            <C::Runtime as Runtime>::Accounts::transfer(creator, instance_info.address(), tokens)
575                .map_err(|_| Error::InsufficientCallerBalance)?
576        }
577        // Run instantiation function.
578        let contract = wasm::Contract {
579            code_info: &code_info,
580            code: &code,
581            instance_info: &instance_info,
582        };
583        let mut exec_ctx = abi::ExecutionContext::new(
584            &params,
585            &code_info,
586            &instance_info,
587            <C::Runtime as Runtime>::Core::remaining_tx_gas(),
588            creator,
589            CurrentState::with_env(|env| env.is_read_only()),
590            CurrentState::with_env(|env| env.tx_call_format()),
591            ctx,
592        );
593        let result = wasm::instantiate::<Cfg, C>(&mut exec_ctx, &contract, &body);
594
595        let result = results::process_execution_result(ctx, result)?;
596        results::process_execution_success::<Cfg, C>(ctx, &params, &contract, result)?;
597        Ok(types::InstantiateResult { id })
598    }
599
600    #[handler(call = "contracts.Call", allow_interactive)]
601    pub fn tx_call<C: Context>(ctx: &C, body: types::Call) -> Result<types::CallResult, Error> {
602        let params = Self::params();
603        let caller = CurrentState::with_env(|env| env.tx_caller_address());
604
605        <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_call)?;
606
607        if !ctx.should_execute_contracts() {
608            // Only fast checks are allowed.
609            return Ok(types::CallResult::default());
610        }
611
612        // Load instance information and code.
613        let instance_info = Self::load_instance_info(body.id)?;
614        let code_info = Self::load_code_info(instance_info.code_id)?;
615        let code = Self::load_code(&code_info)?;
616
617        // Transfer any attached tokens.
618        for tokens in &body.tokens {
619            <C::Runtime as Runtime>::Accounts::transfer(caller, instance_info.address(), tokens)
620                .map_err(|_| Error::InsufficientCallerBalance)?
621        }
622        // Run call function.
623        let contract = wasm::Contract {
624            code_info: &code_info,
625            code: &code,
626            instance_info: &instance_info,
627        };
628        let mut exec_ctx = abi::ExecutionContext::new(
629            &params,
630            &code_info,
631            &instance_info,
632            <C::Runtime as Runtime>::Core::remaining_tx_gas(),
633            caller,
634            CurrentState::with_env(|env| env.is_read_only()),
635            CurrentState::with_env(|env| env.tx_call_format()),
636            ctx,
637        );
638        let result = wasm::call::<Cfg, C>(&mut exec_ctx, &contract, &body);
639
640        let result = results::process_execution_result(ctx, result)?;
641        let data = results::process_execution_success::<Cfg, C>(ctx, &params, &contract, result)?;
642        Ok(types::CallResult(data))
643    }
644
645    #[handler(call = "contracts.ChangeUpgradePolicy")]
646    pub fn tx_change_upgrade_policy<C: Context>(
647        _ctx: &C,
648        body: types::ChangeUpgradePolicy,
649    ) -> Result<(), Error> {
650        let params = Self::params();
651        let caller = CurrentState::with_env(|env| env.tx_caller_address());
652
653        <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_change_upgrade_policy)?;
654
655        if CurrentState::with_env(|env| env.is_check_only()) {
656            return Ok(());
657        }
658
659        // Load instance information.
660        let mut instance_info = Self::load_instance_info(body.id)?;
661        instance_info.upgrades_policy.enforce(&caller)?;
662
663        // Change upgrade policy.
664        instance_info.upgrades_policy = body.upgrades_policy;
665        Self::store_instance_info(instance_info.clone())?;
666
667        Ok(())
668    }
669
670    #[handler(call = "contracts.Upgrade")]
671    pub fn tx_upgrade<C: Context>(ctx: &C, body: types::Upgrade) -> Result<(), Error> {
672        let params = Self::params();
673        let caller = CurrentState::with_env(|env| env.tx_caller_address());
674
675        <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_upgrade)?;
676
677        if !ctx.should_execute_contracts() {
678            // Only fast checks are allowed.
679            return Ok(());
680        }
681
682        // Load instance information and code.
683        let mut instance_info = Self::load_instance_info(body.id)?;
684        instance_info.upgrades_policy.enforce(&caller)?;
685        if instance_info.code_id == body.code_id {
686            return Err(Error::CodeAlreadyUpgraded(body.code_id.as_u64()));
687        }
688        let code_info = Self::load_code_info(instance_info.code_id)?;
689        let code = Self::load_code(&code_info)?;
690
691        // Transfer any attached tokens.
692        for tokens in &body.tokens {
693            <C::Runtime as Runtime>::Accounts::transfer(caller, instance_info.address(), tokens)
694                .map_err(|_| Error::InsufficientCallerBalance)?
695        }
696        // Run pre-upgrade function on the previous contract.
697        let contract = wasm::Contract {
698            code_info: &code_info,
699            code: &code,
700            instance_info: &instance_info,
701        };
702        let mut exec_ctx = abi::ExecutionContext::new(
703            &params,
704            &code_info,
705            &instance_info,
706            <C::Runtime as Runtime>::Core::remaining_tx_gas(),
707            caller,
708            CurrentState::with_env(|env| env.is_read_only()),
709            CurrentState::with_env(|env| env.tx_call_format()),
710            ctx,
711        );
712        // Pre-upgrade invocation must succeed for the upgrade to proceed.
713        let result = wasm::pre_upgrade::<Cfg, C>(&mut exec_ctx, &contract, &body);
714
715        results::process_execution_result(ctx, result)?;
716
717        // Update the contract code.
718        instance_info.code_id = body.code_id;
719        let code_info = Self::load_code_info(instance_info.code_id)?;
720        let code = Self::load_code(&code_info)?;
721        Self::store_instance_info(instance_info.clone())?;
722
723        let contract = wasm::Contract {
724            code_info: &code_info,
725            code: &code,
726            instance_info: &instance_info,
727        };
728        let mut exec_ctx = abi::ExecutionContext::new(
729            &params,
730            &code_info,
731            &instance_info,
732            <C::Runtime as Runtime>::Core::remaining_tx_gas(),
733            caller,
734            CurrentState::with_env(|env| env.is_read_only()),
735            CurrentState::with_env(|env| env.tx_call_format()),
736            ctx,
737        );
738
739        // Run post-upgrade function on the new contract.
740        let result = wasm::post_upgrade::<Cfg, C>(&mut exec_ctx, &contract, &body);
741        results::process_execution_result(ctx, result)?;
742        Ok(())
743    }
744
745    #[handler(query = "contracts.Code")]
746    pub fn query_code<C: Context>(_ctx: &C, args: types::CodeQuery) -> Result<types::Code, Error> {
747        Self::load_code_info(args.id)
748    }
749
750    #[handler(query = "contracts.CodeStorage")]
751    pub fn query_code_storage<C: Context>(
752        _ctx: &C,
753        args: types::CodeStorageQuery,
754    ) -> Result<types::CodeStorageQueryResult, Error> {
755        let code_info = Self::load_code_info(args.id)?;
756        let code = Self::load_code(&code_info)?;
757
758        Ok(types::CodeStorageQueryResult { code })
759    }
760
761    #[handler(query = "contracts.Instance")]
762    pub fn query_instance<C: Context>(
763        _ctx: &C,
764        args: types::InstanceQuery,
765    ) -> Result<types::Instance, Error> {
766        Self::load_instance_info(args.id)
767    }
768
769    #[handler(query = "contracts.InstanceStorage")]
770    pub fn query_instance_storage<C: Context>(
771        ctx: &C,
772        args: types::InstanceStorageQuery,
773    ) -> Result<types::InstanceStorageQueryResult, Error> {
774        let instance_info = Self::load_instance_info(args.id)?;
775        // NOTE: We can only access the public store here.
776        let value = store::with_instance_store(ctx, &instance_info, StoreKind::Public, |store| {
777            store.get(&args.key)
778        })?;
779
780        Ok(types::InstanceStorageQueryResult { value })
781    }
782
783    #[handler(query = "contracts.InstanceRawStorage", expensive)]
784    pub fn query_instance_raw_storage<C: Context>(
785        ctx: &C,
786        args: types::InstanceRawStorageQuery,
787    ) -> Result<types::InstanceRawStorageQueryResult, Error> {
788        let cfg: LocalConfig = ctx.local_config(MODULE_NAME).unwrap_or_default();
789        let limit: usize = args
790            .limit
791            .unwrap_or(u64::MAX)
792            .min(cfg.max_instance_raw_storage_query_items)
793            .try_into()
794            .map_err(|_| Error::InvalidArgument)?;
795        let offset: usize = args
796            .offset
797            .unwrap_or(0)
798            .try_into()
799            .map_err(|_| Error::InvalidArgument)?;
800
801        let instance_info = Self::load_instance_info(args.id)?;
802        // Convert contracts API StoreKind to internal storage StoreKind.
803        let sk: StoreKind = (args.store_kind as u32)
804            .try_into()
805            .map_err(|_| Error::InvalidArgument)?;
806
807        let items: Vec<(Vec<u8>, Vec<u8>)> = with_instance_raw_store(&instance_info, sk, |store| {
808            store
809                .iter()
810                // Shave off first 32 bytes of the key to get the contract instance-level key name.
811                .filter(|(k, _)| k.len() >= 32)
812                .map(|(k, v)| (k[32..].to_vec(), v.to_vec()))
813                .skip(offset)
814                .take(limit)
815                .collect()
816        });
817
818        Ok(types::InstanceRawStorageQueryResult { items })
819    }
820
821    #[handler(query = "contracts.PublicKey")]
822    pub fn query_public_key<C: Context>(
823        _ctx: &C,
824        _args: types::PublicKeyQuery,
825    ) -> Result<types::PublicKeyQueryResult, Error> {
826        Err(Error::Unsupported)
827    }
828
829    #[handler(query = "contracts.Custom", expensive)]
830    pub fn query_custom<C: Context>(
831        ctx: &C,
832        args: types::CustomQuery,
833    ) -> Result<types::CustomQueryResult, Error> {
834        let params = Self::params();
835
836        // Load instance information and code.
837        let instance_info = Self::load_instance_info(args.id)?;
838        let code_info = Self::load_code_info(instance_info.code_id)?;
839        let code = Self::load_code(&code_info)?;
840
841        // Load local configuration.
842        let cfg: LocalConfig = ctx.local_config(MODULE_NAME).unwrap_or_default();
843
844        // Run query function.
845        let contract = wasm::Contract {
846            code_info: &code_info,
847            code: &code,
848            instance_info: &instance_info,
849        };
850        let mut exec_ctx = abi::ExecutionContext::new(
851            &params,
852            &code_info,
853            &instance_info,
854            cfg.query_custom_max_gas,
855            Default::default(), // No caller for queries.
856            true,
857            CallFormat::Plain,
858            ctx,
859        );
860        let result = wasm::query::<Cfg, C>(&mut exec_ctx, &contract, &args).inner?; // No need to handle gas.
861
862        Ok(types::CustomQueryResult(result.data))
863    }
864}
865
866impl<Cfg: Config> module::TransactionHandler for Module<Cfg> {}
867impl<Cfg: Config> module::BlockHandler for Module<Cfg> {}
868impl<Cfg: Config> module::InvariantHandler for Module<Cfg> {}