1#![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
41const MODULE_NAME: &str = "contracts";
43
44#[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("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#[derive(Debug, cbor::Encode, sdk::Event)]
187#[cbor(untagged)]
188pub enum Event {}
189
190#[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 pub subcall_dispatch: u64,
202
203 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 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 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#[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, max_stack_size: 60 * 1024, max_memory_pages: 160, 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, max_query_size_bytes: 1024, max_storage_key_size_bytes: 64,
304 max_storage_value_size_bytes: 16 * 1024, max_crypto_signature_verify_message_size_bytes: 16 * 1024, gas_costs: Default::default(),
308 }
309 }
310}
311
312impl module::Parameters for Parameters {
313 type Error = std::convert::Infallible;
314}
315
316#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
318pub struct Genesis {
319 pub parameters: Parameters,
320}
321
322#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
324pub struct LocalConfig {
325 #[cbor(optional)]
327 pub query_custom_max_gas: u64,
328
329 #[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
343pub mod state {
345 pub const NEXT_CODE_IDENTIFIER: &[u8] = &[0x01];
347 pub const NEXT_INSTANCE_IDENTIFIER: &[u8] = &[0x02];
349 pub const CODE_INFO: &[u8] = &[0x03];
351 pub const INSTANCE_INFO: &[u8] = &[0x04];
353 pub const INSTANCE_STATE: &[u8] = &[0x05];
355
356 pub const CODE: &[u8] = &[0xFF];
358}
359
360pub trait Config: 'static {}
362
363pub struct Module<Cfg: Config> {
364 _cfg: std::marker::PhantomData<Cfg>,
365}
366
367impl<Cfg: Config> Module<Cfg> {
368 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 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 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 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 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 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 <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 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 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 return Ok(types::UploadResult::default());
486 }
487
488 let (code, abi_info) = wasm::validate_and_transform::<Cfg, C>(&code, body.abi, ¶ms)?;
490 let hash = Hash::digest_bytes(&code);
491
492 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 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 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 return Ok(types::InstantiateResult::default());
545 }
546
547 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 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 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 for tokens in &body.tokens {
574 <C::Runtime as Runtime>::Accounts::transfer(creator, instance_info.address(), tokens)
575 .map_err(|_| Error::InsufficientCallerBalance)?
576 }
577 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 ¶ms,
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, ¶ms, &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 return Ok(types::CallResult::default());
610 }
611
612 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 for tokens in &body.tokens {
619 <C::Runtime as Runtime>::Accounts::transfer(caller, instance_info.address(), tokens)
620 .map_err(|_| Error::InsufficientCallerBalance)?
621 }
622 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 ¶ms,
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, ¶ms, &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 let mut instance_info = Self::load_instance_info(body.id)?;
661 instance_info.upgrades_policy.enforce(&caller)?;
662
663 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 return Ok(());
680 }
681
682 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 for tokens in &body.tokens {
693 <C::Runtime as Runtime>::Accounts::transfer(caller, instance_info.address(), tokens)
694 .map_err(|_| Error::InsufficientCallerBalance)?
695 }
696 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 ¶ms,
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 let result = wasm::pre_upgrade::<Cfg, C>(&mut exec_ctx, &contract, &body);
714
715 results::process_execution_result(ctx, result)?;
716
717 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 ¶ms,
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 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 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 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 .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 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 let cfg: LocalConfig = ctx.local_config(MODULE_NAME).unwrap_or_default();
843
844 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 ¶ms,
852 &code_info,
853 &instance_info,
854 cfg.query_custom_max_gas,
855 Default::default(), true,
857 CallFormat::Plain,
858 ctx,
859 );
860 let result = wasm::query::<Cfg, C>(&mut exec_ctx, &contract, &args).inner?; 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> {}