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("core: {0}")]
173 #[sdk_error(transparent)]
174 Core(#[from] modules::core::Error),
175
176 #[error("contract error: {0}")]
177 #[sdk_error(transparent)]
178 Contract(#[from] wasm::ContractError),
179}
180
181#[derive(Debug, cbor::Encode, sdk::Event)]
183#[cbor(untagged)]
184pub enum Event {}
185
186#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
188pub struct GasCosts {
189 pub tx_upload: u64,
190 pub tx_upload_per_byte: u64,
191 pub tx_instantiate: u64,
192 pub tx_call: u64,
193 pub tx_upgrade: u64,
194 pub tx_change_upgrade_policy: u64,
195
196 pub subcall_dispatch: u64,
198
199 pub wasm_public_storage_get_base: u64,
201 pub wasm_public_storage_insert_base: u64,
202 pub wasm_public_storage_remove_base: u64,
203 pub wasm_public_storage_key_byte: u64,
204 pub wasm_public_storage_value_byte: u64,
205 pub wasm_confidential_storage_get_base: u64,
206 pub wasm_confidential_storage_insert_base: u64,
207 pub wasm_confidential_storage_remove_base: u64,
208 pub wasm_confidential_storage_key_byte: u64,
209 pub wasm_confidential_storage_value_byte: u64,
210 pub wasm_env_query_base: u64,
211
212 pub wasm_crypto_ecdsa_recover: u64,
214 pub wasm_crypto_signature_verify_ed25519: u64,
215 pub wasm_crypto_signature_verify_secp256k1: u64,
216 pub wasm_crypto_signature_verify_sr25519: u64,
217 pub wasm_crypto_x25519_derive_symmetric: u64,
218 pub wasm_crypto_deoxysii_base: u64,
219 pub wasm_crypto_deoxysii_byte: u64,
220 pub wasm_crypto_random_bytes_base: u64,
221 pub wasm_crypto_random_bytes_byte: u64,
222}
223
224impl Default for GasCosts {
225 fn default() -> Self {
226 GasCosts {
228 tx_upload: 30_000_000,
229 tx_upload_per_byte: 400,
230 tx_instantiate: 100_000,
231 tx_call: 50_000,
232 tx_upgrade: 50_000,
233 tx_change_upgrade_policy: 30_000,
234
235 subcall_dispatch: 1_000,
236
237 wasm_public_storage_get_base: 5_000,
238 wasm_public_storage_insert_base: 8_400,
239 wasm_public_storage_remove_base: 6_400,
240 wasm_public_storage_key_byte: 3_000,
241 wasm_public_storage_value_byte: 300,
242 wasm_confidential_storage_get_base: 10_000,
243 wasm_confidential_storage_insert_base: 16_800,
244 wasm_confidential_storage_remove_base: 12_800,
245 wasm_confidential_storage_key_byte: 3_500,
246 wasm_confidential_storage_value_byte: 400,
247 wasm_env_query_base: 100,
248
249 wasm_crypto_ecdsa_recover: 500_000,
250 wasm_crypto_signature_verify_ed25519: 500_000,
251 wasm_crypto_signature_verify_secp256k1: 500_000,
252 wasm_crypto_signature_verify_sr25519: 500_000,
253 wasm_crypto_x25519_derive_symmetric: 250_000,
254 wasm_crypto_deoxysii_base: 1_000,
255 wasm_crypto_deoxysii_byte: 3,
256 wasm_crypto_random_bytes_base: 1_000,
257 wasm_crypto_random_bytes_byte: 3,
258 }
259 }
260}
261
262#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
264pub struct Parameters {
265 pub max_code_size: u32,
266 pub max_stack_size: u32,
267 pub max_memory_pages: u32,
268
269 pub max_wasm_functions: u32,
270 pub max_wasm_locals: u32,
271
272 pub max_subcall_depth: u16,
273 pub max_subcall_count: u16,
274
275 pub max_result_size_bytes: u32,
276 pub max_query_size_bytes: u32,
277 pub max_storage_key_size_bytes: u32,
278 pub max_storage_value_size_bytes: u32,
279 pub max_crypto_signature_verify_message_size_bytes: u32,
280
281 pub gas_costs: GasCosts,
282}
283
284impl Default for Parameters {
285 fn default() -> Self {
286 Parameters {
287 max_code_size: 1024 * 1024, max_stack_size: 60 * 1024, max_memory_pages: 160, max_wasm_functions: 10_000,
292 max_wasm_locals: 256_000,
293
294 max_subcall_depth: 8,
295 max_subcall_count: 16,
296
297 max_result_size_bytes: 1024, max_query_size_bytes: 1024, max_storage_key_size_bytes: 64,
300 max_storage_value_size_bytes: 16 * 1024, max_crypto_signature_verify_message_size_bytes: 16 * 1024, gas_costs: Default::default(),
304 }
305 }
306}
307
308impl module::Parameters for Parameters {
309 type Error = std::convert::Infallible;
310}
311
312#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
314pub struct Genesis {
315 pub parameters: Parameters,
316}
317
318#[derive(Clone, Debug, cbor::Encode, cbor::Decode)]
320pub struct LocalConfig {
321 #[cbor(optional)]
323 pub query_custom_max_gas: u64,
324
325 #[cbor(optional)]
327 pub max_instance_raw_storage_query_items: u64,
328}
329
330impl Default for LocalConfig {
331 fn default() -> Self {
332 Self {
333 query_custom_max_gas: 10_000_000,
334 max_instance_raw_storage_query_items: 100,
335 }
336 }
337}
338
339pub mod state {
341 pub const NEXT_CODE_IDENTIFIER: &[u8] = &[0x01];
343 pub const NEXT_INSTANCE_IDENTIFIER: &[u8] = &[0x02];
345 pub const CODE_INFO: &[u8] = &[0x03];
347 pub const INSTANCE_INFO: &[u8] = &[0x04];
349 pub const INSTANCE_STATE: &[u8] = &[0x05];
351
352 pub const CODE: &[u8] = &[0xFF];
354}
355
356pub trait Config: 'static {}
358
359pub struct Module<Cfg: Config> {
360 _cfg: std::marker::PhantomData<Cfg>,
361}
362
363impl<Cfg: Config> Module<Cfg> {
364 fn load_code_info(code_id: types::CodeId) -> Result<types::Code, Error> {
366 CurrentState::with_store(|store| {
367 let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
368 let code_info_store =
369 storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::CODE_INFO));
370 let code_info = code_info_store
371 .get(code_id.to_storage_key())
372 .ok_or_else(|| Error::CodeNotFound(code_id.as_u64()))?;
373
374 Ok(code_info)
375 })
376 }
377
378 fn store_code_info(code_info: types::Code) -> Result<(), Error> {
380 CurrentState::with_store(|store| {
381 let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
382 let mut code_info_store =
383 storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::CODE_INFO));
384 code_info_store.insert(code_info.id.to_storage_key(), code_info);
385
386 Ok(())
387 })
388 }
389
390 fn load_instance_info(instance_id: types::InstanceId) -> Result<types::Instance, Error> {
392 CurrentState::with_store(|store| {
393 let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
394 let instance_info_store = storage::TypedStore::new(storage::PrefixStore::new(
395 &mut store,
396 &state::INSTANCE_INFO,
397 ));
398 let instance_info = instance_info_store
399 .get(instance_id.to_storage_key())
400 .ok_or_else(|| Error::InstanceNotFound(instance_id.as_u64()))?;
401
402 Ok(instance_info)
403 })
404 }
405
406 fn store_instance_info(instance_info: types::Instance) -> Result<(), Error> {
408 CurrentState::with_store(|store| {
409 let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
410 let mut instance_info_store = storage::TypedStore::new(storage::PrefixStore::new(
411 &mut store,
412 &state::INSTANCE_INFO,
413 ));
414 instance_info_store.insert(instance_info.id.to_storage_key(), instance_info);
415
416 Ok(())
417 })
418 }
419}
420
421#[sdk_derive(Module)]
422impl<Cfg: Config> Module<Cfg> {
423 const NAME: &'static str = MODULE_NAME;
424 type Error = Error;
425 type Event = Event;
426 type Parameters = Parameters;
427 type Genesis = Genesis;
428
429 #[migration(init)]
430 fn init(genesis: Genesis) {
431 Self::set_params(genesis.parameters);
433 }
434
435 #[handler(call = "contracts.Upload")]
436 pub fn tx_upload<C: Context>(
437 ctx: &C,
438 body: types::Upload,
439 ) -> Result<types::UploadResult, Error> {
440 let params = Self::params();
441 let uploader = CurrentState::with_env(|env| env.tx_caller_address());
442
443 let code_size: u32 = body
445 .code
446 .len()
447 .try_into()
448 .map_err(|_| Error::CodeTooLarge(u32::MAX, params.max_code_size))?;
449 if code_size > params.max_code_size {
450 return Err(Error::CodeTooLarge(code_size, params.max_code_size));
451 }
452
453 <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_upload)?;
455 <C::Runtime as Runtime>::Core::use_tx_gas(
456 params
457 .gas_costs
458 .tx_upload_per_byte
459 .saturating_mul(body.code.len() as u64),
460 )?;
461
462 let mut code = Vec::with_capacity(body.code.len());
464 let decoder = snap::read::FrameDecoder::new(body.code.as_slice());
465 decoder
466 .take(params.max_code_size.into())
467 .read_to_end(&mut code)
468 .map_err(|_| Error::CodeMalformed)?;
469
470 let plain_code_size: u32 = code.len().try_into().unwrap();
472 <C::Runtime as Runtime>::Core::use_tx_gas(
473 params
474 .gas_costs
475 .tx_upload_per_byte
476 .saturating_mul(plain_code_size.saturating_sub(code_size) as u64),
477 )?;
478
479 if !ctx.should_execute_contracts() {
480 return Ok(types::UploadResult::default());
482 }
483
484 let (code, abi_info) = wasm::validate_and_transform::<Cfg, C>(&code, body.abi, ¶ms)?;
486 let hash = Hash::digest_bytes(&code);
487
488 let inst_code_size: u32 = code
491 .len()
492 .try_into()
493 .map_err(|_| Error::CodeTooLarge(u32::MAX, params.max_code_size))?;
494 if inst_code_size > params.max_code_size {
495 return Err(Error::CodeTooLarge(inst_code_size, params.max_code_size));
496 }
497 <C::Runtime as Runtime>::Core::use_tx_gas(
498 params
499 .gas_costs
500 .tx_upload_per_byte
501 .saturating_mul(inst_code_size.saturating_sub(plain_code_size) as u64),
502 )?;
503
504 let id = CurrentState::with_store(|store| {
506 let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
507 let mut tstore = storage::TypedStore::new(&mut store);
508 let id: types::CodeId = tstore.get(state::NEXT_CODE_IDENTIFIER).unwrap_or_default();
509 tstore.insert(state::NEXT_CODE_IDENTIFIER, id.increment());
510 id
511 });
512
513 let code_info = types::Code {
515 id,
516 hash,
517 abi: body.abi,
518 abi_sv: abi_info.abi_sv,
519 uploader,
520 instantiate_policy: body.instantiate_policy,
521 };
522 Self::store_code(&code_info, &code)?;
523 Self::store_code_info(code_info)?;
524
525 Ok(types::UploadResult { id })
526 }
527
528 #[handler(call = "contracts.Instantiate")]
529 pub fn tx_instantiate<C: Context>(
530 ctx: &C,
531 body: types::Instantiate,
532 ) -> Result<types::InstantiateResult, Error> {
533 let params = Self::params();
534 let creator = CurrentState::with_env(|env| env.tx_caller_address());
535
536 <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_instantiate)?;
537
538 if !ctx.should_execute_contracts() {
539 return Ok(types::InstantiateResult::default());
541 }
542
543 let code_info = Self::load_code_info(body.code_id)?;
545 code_info.instantiate_policy.enforce(&creator)?;
546 let code = Self::load_code(&code_info)?;
547
548 let id = CurrentState::with_store(|store| {
550 let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
551 let mut tstore = storage::TypedStore::new(&mut store);
552 let id: types::InstanceId = tstore
553 .get(state::NEXT_INSTANCE_IDENTIFIER)
554 .unwrap_or_default();
555 tstore.insert(state::NEXT_INSTANCE_IDENTIFIER, id.increment());
556 id
557 });
558
559 let instance_info = types::Instance {
561 id,
562 code_id: body.code_id,
563 creator,
564 upgrades_policy: body.upgrades_policy,
565 };
566 Self::store_instance_info(instance_info.clone())?;
567
568 for tokens in &body.tokens {
570 <C::Runtime as Runtime>::Accounts::transfer(creator, instance_info.address(), tokens)
571 .map_err(|_| Error::InsufficientCallerBalance)?
572 }
573 let contract = wasm::Contract {
575 code_info: &code_info,
576 code: &code,
577 instance_info: &instance_info,
578 };
579 let mut exec_ctx = abi::ExecutionContext::new(
580 ¶ms,
581 &code_info,
582 &instance_info,
583 <C::Runtime as Runtime>::Core::remaining_tx_gas(),
584 creator,
585 CurrentState::with_env(|env| env.is_read_only()),
586 CurrentState::with_env(|env| env.tx_call_format()),
587 ctx,
588 );
589 let result = wasm::instantiate::<Cfg, C>(&mut exec_ctx, &contract, &body);
590
591 let result = results::process_execution_result(ctx, result)?;
592 results::process_execution_success::<Cfg, C>(ctx, ¶ms, &contract, result)?;
593 Ok(types::InstantiateResult { id })
594 }
595
596 #[handler(call = "contracts.Call", allow_interactive)]
597 pub fn tx_call<C: Context>(ctx: &C, body: types::Call) -> Result<types::CallResult, Error> {
598 let params = Self::params();
599 let caller = CurrentState::with_env(|env| env.tx_caller_address());
600
601 <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_call)?;
602
603 if !ctx.should_execute_contracts() {
604 return Ok(types::CallResult::default());
606 }
607
608 let instance_info = Self::load_instance_info(body.id)?;
610 let code_info = Self::load_code_info(instance_info.code_id)?;
611 let code = Self::load_code(&code_info)?;
612
613 for tokens in &body.tokens {
615 <C::Runtime as Runtime>::Accounts::transfer(caller, instance_info.address(), tokens)
616 .map_err(|_| Error::InsufficientCallerBalance)?
617 }
618 let contract = wasm::Contract {
620 code_info: &code_info,
621 code: &code,
622 instance_info: &instance_info,
623 };
624 let mut exec_ctx = abi::ExecutionContext::new(
625 ¶ms,
626 &code_info,
627 &instance_info,
628 <C::Runtime as Runtime>::Core::remaining_tx_gas(),
629 caller,
630 CurrentState::with_env(|env| env.is_read_only()),
631 CurrentState::with_env(|env| env.tx_call_format()),
632 ctx,
633 );
634 let result = wasm::call::<Cfg, C>(&mut exec_ctx, &contract, &body);
635
636 let result = results::process_execution_result(ctx, result)?;
637 let data = results::process_execution_success::<Cfg, C>(ctx, ¶ms, &contract, result)?;
638 Ok(types::CallResult(data))
639 }
640
641 #[handler(call = "contracts.ChangeUpgradePolicy")]
642 pub fn tx_change_upgrade_policy<C: Context>(
643 ctx: &C,
644 body: types::ChangeUpgradePolicy,
645 ) -> Result<(), Error> {
646 let params = Self::params();
647 let caller = CurrentState::with_env(|env| env.tx_caller_address());
648
649 <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_change_upgrade_policy)?;
650
651 if CurrentState::with_env(|env| env.is_check_only()) {
652 return Ok(());
653 }
654
655 let mut instance_info = Self::load_instance_info(body.id)?;
657 instance_info.upgrades_policy.enforce(&caller)?;
658
659 instance_info.upgrades_policy = body.upgrades_policy;
661 Self::store_instance_info(instance_info.clone())?;
662
663 Ok(())
664 }
665
666 #[handler(call = "contracts.Upgrade")]
667 pub fn tx_upgrade<C: Context>(ctx: &C, body: types::Upgrade) -> Result<(), Error> {
668 let params = Self::params();
669 let caller = CurrentState::with_env(|env| env.tx_caller_address());
670
671 <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_upgrade)?;
672
673 if !ctx.should_execute_contracts() {
674 return Ok(());
676 }
677
678 let mut instance_info = Self::load_instance_info(body.id)?;
680 instance_info.upgrades_policy.enforce(&caller)?;
681 if instance_info.code_id == body.code_id {
682 return Err(Error::CodeAlreadyUpgraded(body.code_id.as_u64()));
683 }
684 let code_info = Self::load_code_info(instance_info.code_id)?;
685 let code = Self::load_code(&code_info)?;
686
687 for tokens in &body.tokens {
689 <C::Runtime as Runtime>::Accounts::transfer(caller, instance_info.address(), tokens)
690 .map_err(|_| Error::InsufficientCallerBalance)?
691 }
692 let contract = wasm::Contract {
694 code_info: &code_info,
695 code: &code,
696 instance_info: &instance_info,
697 };
698 let mut exec_ctx = abi::ExecutionContext::new(
699 ¶ms,
700 &code_info,
701 &instance_info,
702 <C::Runtime as Runtime>::Core::remaining_tx_gas(),
703 caller,
704 CurrentState::with_env(|env| env.is_read_only()),
705 CurrentState::with_env(|env| env.tx_call_format()),
706 ctx,
707 );
708 let result = wasm::pre_upgrade::<Cfg, C>(&mut exec_ctx, &contract, &body);
710
711 results::process_execution_result(ctx, result)?;
712
713 instance_info.code_id = body.code_id;
715 let code_info = Self::load_code_info(instance_info.code_id)?;
716 let code = Self::load_code(&code_info)?;
717 Self::store_instance_info(instance_info.clone())?;
718
719 let contract = wasm::Contract {
720 code_info: &code_info,
721 code: &code,
722 instance_info: &instance_info,
723 };
724 let mut exec_ctx = abi::ExecutionContext::new(
725 ¶ms,
726 &code_info,
727 &instance_info,
728 <C::Runtime as Runtime>::Core::remaining_tx_gas(),
729 caller,
730 CurrentState::with_env(|env| env.is_read_only()),
731 CurrentState::with_env(|env| env.tx_call_format()),
732 ctx,
733 );
734
735 let result = wasm::post_upgrade::<Cfg, C>(&mut exec_ctx, &contract, &body);
737 results::process_execution_result(ctx, result)?;
738 Ok(())
739 }
740
741 #[handler(query = "contracts.Code")]
742 pub fn query_code<C: Context>(_ctx: &C, args: types::CodeQuery) -> Result<types::Code, Error> {
743 Self::load_code_info(args.id)
744 }
745
746 #[handler(query = "contracts.CodeStorage")]
747 pub fn query_code_storage<C: Context>(
748 _ctx: &C,
749 args: types::CodeStorageQuery,
750 ) -> Result<types::CodeStorageQueryResult, Error> {
751 let code_info = Self::load_code_info(args.id)?;
752 let code = Self::load_code(&code_info)?;
753
754 Ok(types::CodeStorageQueryResult { code })
755 }
756
757 #[handler(query = "contracts.Instance")]
758 pub fn query_instance<C: Context>(
759 _ctx: &C,
760 args: types::InstanceQuery,
761 ) -> Result<types::Instance, Error> {
762 Self::load_instance_info(args.id)
763 }
764
765 #[handler(query = "contracts.InstanceStorage")]
766 pub fn query_instance_storage<C: Context>(
767 ctx: &C,
768 args: types::InstanceStorageQuery,
769 ) -> Result<types::InstanceStorageQueryResult, Error> {
770 let instance_info = Self::load_instance_info(args.id)?;
771 let value = store::with_instance_store(ctx, &instance_info, StoreKind::Public, |store| {
773 store.get(&args.key)
774 })?;
775
776 Ok(types::InstanceStorageQueryResult { value })
777 }
778
779 #[handler(query = "contracts.InstanceRawStorage", expensive)]
780 pub fn query_instance_raw_storage<C: Context>(
781 ctx: &C,
782 args: types::InstanceRawStorageQuery,
783 ) -> Result<types::InstanceRawStorageQueryResult, Error> {
784 let cfg: LocalConfig = ctx.local_config(MODULE_NAME).unwrap_or_default();
785 let limit: usize = args
786 .limit
787 .unwrap_or(u64::MAX)
788 .min(cfg.max_instance_raw_storage_query_items)
789 .try_into()
790 .map_err(|_| Error::InvalidArgument)?;
791 let offset: usize = args
792 .offset
793 .unwrap_or(0)
794 .try_into()
795 .map_err(|_| Error::InvalidArgument)?;
796
797 let instance_info = Self::load_instance_info(args.id)?;
798 let sk: StoreKind = (args.store_kind as u32)
800 .try_into()
801 .map_err(|_| Error::InvalidArgument)?;
802
803 let items: Vec<(Vec<u8>, Vec<u8>)> = with_instance_raw_store(&instance_info, sk, |store| {
804 store
805 .iter()
806 .filter(|(k, _)| k.len() >= 32)
808 .map(|(k, v)| (k[32..].to_vec(), v.to_vec()))
809 .skip(offset)
810 .take(limit)
811 .collect()
812 });
813
814 Ok(types::InstanceRawStorageQueryResult { items })
815 }
816
817 #[handler(query = "contracts.PublicKey")]
818 pub fn query_public_key<C: Context>(
819 _ctx: &C,
820 _args: types::PublicKeyQuery,
821 ) -> Result<types::PublicKeyQueryResult, Error> {
822 Err(Error::Unsupported)
823 }
824
825 #[handler(query = "contracts.Custom", expensive)]
826 pub fn query_custom<C: Context>(
827 ctx: &C,
828 args: types::CustomQuery,
829 ) -> Result<types::CustomQueryResult, Error> {
830 let params = Self::params();
831
832 let instance_info = Self::load_instance_info(args.id)?;
834 let code_info = Self::load_code_info(instance_info.code_id)?;
835 let code = Self::load_code(&code_info)?;
836
837 let cfg: LocalConfig = ctx.local_config(MODULE_NAME).unwrap_or_default();
839
840 let contract = wasm::Contract {
842 code_info: &code_info,
843 code: &code,
844 instance_info: &instance_info,
845 };
846 let mut exec_ctx = abi::ExecutionContext::new(
847 ¶ms,
848 &code_info,
849 &instance_info,
850 cfg.query_custom_max_gas,
851 Default::default(), true,
853 CallFormat::Plain,
854 ctx,
855 );
856 let result = wasm::query::<Cfg, C>(&mut exec_ctx, &contract, &args).inner?; Ok(types::CustomQueryResult(result.data))
859 }
860}
861
862impl<Cfg: Config> module::TransactionHandler for Module<Cfg> {}
863impl<Cfg: Config> module::BlockHandler for Module<Cfg> {}
864impl<Cfg: Config> module::InvariantHandler for Module<Cfg> {}