1use std::{
3 collections::{BTreeMap, BTreeSet},
4 fmt::Debug,
5};
6
7use cbor::Encode as _;
8use impl_trait_for_tuples::impl_for_tuples;
9
10use crate::{
11 context::Context,
12 dispatcher, error,
13 error::Error as _,
14 event, modules,
15 modules::core::types::{MethodHandlerInfo, ModuleInfo},
16 state::CurrentState,
17 storage,
18 storage::Prefix,
19 types::{
20 address::Address,
21 message::MessageResult,
22 transaction::{self, AuthInfo, Call, Transaction, UnverifiedTransaction},
23 },
24};
25
26pub enum DispatchResult<B, R> {
28 Handled(R),
29 Unhandled(B),
30}
31
32impl<B, R> DispatchResult<B, R> {
33 pub fn ok_or<E>(self, err: E) -> Result<R, E> {
36 match self {
37 DispatchResult::Handled(result) => Ok(result),
38 DispatchResult::Unhandled(_) => Err(err),
39 }
40 }
41
42 pub fn ok_or_else<E, F: FnOnce() -> E>(self, errf: F) -> Result<R, E> {
45 match self {
46 DispatchResult::Handled(result) => Ok(result),
47 DispatchResult::Unhandled(_) => Err(errf()),
48 }
49 }
50}
51
52#[derive(Debug)]
57pub enum CallResult {
58 Ok(cbor::Value),
60
61 Failed {
63 module: String,
64 code: u32,
65 message: String,
66 },
67
68 Aborted(dispatcher::Error),
70}
71
72impl CallResult {
73 pub fn is_success(&self) -> bool {
75 matches!(self, CallResult::Ok(_))
76 }
77
78 #[cfg(any(test, feature = "test"))]
79 pub fn unwrap(self) -> cbor::Value {
80 match self {
81 Self::Ok(v) => v,
82 Self::Failed {
83 module,
84 code,
85 message,
86 } => panic!("{module} reported failure with code {code}: {message}"),
87 Self::Aborted(e) => panic!("tx aborted with error: {e}"),
88 }
89 }
90
91 #[cfg(any(test, feature = "test"))]
92 pub fn unwrap_failed(self) -> (String, u32) {
93 match self {
94 Self::Ok(_) => panic!("call result indicates success"),
95 Self::Failed { module, code, .. } => (module, code),
96 Self::Aborted(e) => panic!("tx aborted with error: {e}"),
97 }
98 }
99}
100
101impl From<CallResult> for transaction::CallResult {
102 fn from(v: CallResult) -> Self {
103 match v {
104 CallResult::Ok(data) => Self::Ok(data),
105 CallResult::Failed {
106 module,
107 code,
108 message,
109 } => Self::Failed {
110 module,
111 code,
112 message,
113 },
114 CallResult::Aborted(err) => Self::Failed {
115 module: err.module_name().to_string(),
116 code: err.code(),
117 message: err.to_string(),
118 },
119 }
120 }
121}
122
123pub fn dispatch_call<C, B, R, E, F>(
125 ctx: &C,
126 body: cbor::Value,
127 f: F,
128) -> DispatchResult<cbor::Value, CallResult>
129where
130 C: Context,
131 B: cbor::Decode,
132 R: cbor::Encode,
133 E: error::Error,
134 F: FnOnce(&C, B) -> Result<R, E>,
135{
136 DispatchResult::Handled((|| {
137 let args = match cbor::from_value(body)
138 .map_err(|err| modules::core::Error::InvalidArgument(err.into()))
139 {
140 Ok(args) => args,
141 Err(err) => return err.into_call_result(),
142 };
143
144 match f(ctx, args) {
145 Ok(value) => CallResult::Ok(cbor::to_value(value)),
146 Err(err) => err.into_call_result(),
147 }
148 })())
149}
150
151pub fn dispatch_query<C, B, R, E, F>(
153 ctx: &C,
154 body: cbor::Value,
155 f: F,
156) -> DispatchResult<cbor::Value, Result<cbor::Value, error::RuntimeError>>
157where
158 C: Context,
159 B: cbor::Decode,
160 R: cbor::Encode,
161 E: error::Error,
162 error::RuntimeError: From<E>,
163 F: FnOnce(&C, B) -> Result<R, E>,
164{
165 DispatchResult::Handled((|| {
166 let args = cbor::from_value(body).map_err(|err| -> error::RuntimeError {
167 modules::core::Error::InvalidArgument(err.into()).into()
168 })?;
169 Ok(cbor::to_value(f(ctx, args)?))
170 })())
171}
172
173pub trait MethodHandler {
175 fn prefetch(
177 _prefixes: &mut BTreeSet<Prefix>,
178 _method: &str,
179 body: cbor::Value,
180 _auth_info: &AuthInfo,
181 ) -> DispatchResult<cbor::Value, Result<(), error::RuntimeError>> {
182 DispatchResult::Unhandled(body)
184 }
185
186 fn dispatch_call<C: Context>(
188 _ctx: &C,
189 _method: &str,
190 body: cbor::Value,
191 ) -> DispatchResult<cbor::Value, CallResult> {
192 DispatchResult::Unhandled(body)
194 }
195
196 fn dispatch_query<C: Context>(
198 _ctx: &C,
199 _method: &str,
200 args: cbor::Value,
201 ) -> DispatchResult<cbor::Value, Result<cbor::Value, error::RuntimeError>> {
202 DispatchResult::Unhandled(args)
204 }
205
206 fn dispatch_message_result<C: Context>(
208 _ctx: &C,
209 _handler_name: &str,
210 result: MessageResult,
211 ) -> DispatchResult<MessageResult, ()> {
212 DispatchResult::Unhandled(result)
214 }
215
216 fn supported_methods() -> Vec<MethodHandlerInfo> {
220 vec![]
221 }
222
223 fn is_expensive_query(_method: &str) -> bool {
225 false
226 }
227
228 fn is_allowed_private_km_query(_method: &str) -> bool {
230 false
231 }
232
233 fn is_allowed_interactive_call(_method: &str) -> bool {
236 false
237 }
238}
239
240#[impl_for_tuples(30)]
241impl MethodHandler for Tuple {
242 fn prefetch(
243 prefixes: &mut BTreeSet<Prefix>,
244 method: &str,
245 body: cbor::Value,
246 auth_info: &AuthInfo,
247 ) -> DispatchResult<cbor::Value, Result<(), error::RuntimeError>> {
248 for_tuples!( #(
250 let body = match Tuple::prefetch(prefixes, method, body, auth_info) {
251 DispatchResult::Handled(result) => return DispatchResult::Handled(result),
252 DispatchResult::Unhandled(body) => body,
253 };
254 )* );
255
256 DispatchResult::Unhandled(body)
257 }
258
259 fn dispatch_call<C: Context>(
260 ctx: &C,
261 method: &str,
262 body: cbor::Value,
263 ) -> DispatchResult<cbor::Value, CallResult> {
264 for_tuples!( #(
266 let body = match Tuple::dispatch_call::<C>(ctx, method, body) {
267 DispatchResult::Handled(result) => return DispatchResult::Handled(result),
268 DispatchResult::Unhandled(body) => body,
269 };
270 )* );
271
272 DispatchResult::Unhandled(body)
273 }
274
275 fn dispatch_query<C: Context>(
276 ctx: &C,
277 method: &str,
278 args: cbor::Value,
279 ) -> DispatchResult<cbor::Value, Result<cbor::Value, error::RuntimeError>> {
280 for_tuples!( #(
282 let args = match Tuple::dispatch_query::<C>(ctx, method, args) {
283 DispatchResult::Handled(result) => return DispatchResult::Handled(result),
284 DispatchResult::Unhandled(args) => args,
285 };
286 )* );
287
288 DispatchResult::Unhandled(args)
289 }
290
291 fn dispatch_message_result<C: Context>(
292 ctx: &C,
293 handler_name: &str,
294 result: MessageResult,
295 ) -> DispatchResult<MessageResult, ()> {
296 for_tuples!( #(
298 let result = match Tuple::dispatch_message_result::<C>(ctx, handler_name, result) {
299 DispatchResult::Handled(result) => return DispatchResult::Handled(result),
300 DispatchResult::Unhandled(result) => result,
301 };
302 )* );
303
304 DispatchResult::Unhandled(result)
305 }
306
307 fn is_expensive_query(method: &str) -> bool {
308 for_tuples!( #(
309 if Tuple::is_expensive_query(method) {
310 return true;
311 }
312 )* );
313 false
314 }
315
316 fn is_allowed_private_km_query(method: &str) -> bool {
317 for_tuples!( #(
318 if Tuple::is_allowed_private_km_query(method) {
319 return true;
320 }
321 )* );
322 false
323 }
324
325 fn is_allowed_interactive_call(method: &str) -> bool {
326 for_tuples!( #(
327 if Tuple::is_allowed_interactive_call(method) {
328 return true;
329 }
330 )* );
331 false
332 }
333}
334
335#[derive(Clone, Debug)]
337pub enum AuthDecision {
338 Continue,
340 Stop,
342}
343
344pub trait TransactionHandler {
346 fn approve_raw_tx<C: Context>(_ctx: &C, _tx: &[u8]) -> Result<(), modules::core::Error> {
349 Ok(())
351 }
352
353 fn approve_unverified_tx<C: Context>(
356 _ctx: &C,
357 _utx: &UnverifiedTransaction,
358 ) -> Result<(), modules::core::Error> {
359 Ok(())
361 }
362
363 fn decode_tx<C: Context>(
371 _ctx: &C,
372 _scheme: &str,
373 _body: &[u8],
374 ) -> Result<Option<Transaction>, modules::core::Error> {
375 Ok(None)
377 }
378
379 fn authenticate_tx<C: Context>(
383 _ctx: &C,
384 _tx: &Transaction,
385 ) -> Result<AuthDecision, modules::core::Error> {
386 Ok(AuthDecision::Continue)
388 }
389
390 fn before_handle_call<C: Context>(_ctx: &C, _call: &Call) -> Result<(), modules::core::Error> {
395 Ok(())
397 }
398
399 fn before_authorized_call_dispatch<C: Context>(
404 _ctx: &C,
405 _call: &Call,
406 ) -> Result<(), modules::core::Error> {
407 Ok(())
409 }
410
411 fn after_handle_call<C: Context>(
415 _ctx: &C,
416 result: CallResult,
417 ) -> Result<CallResult, modules::core::Error> {
418 Ok(result)
420 }
421
422 fn after_dispatch_tx<C: Context>(_ctx: &C, _tx_auth_info: &AuthInfo, _result: &CallResult) {
424 }
426}
427
428#[impl_for_tuples(30)]
429impl TransactionHandler for Tuple {
430 fn approve_raw_tx<C: Context>(ctx: &C, tx: &[u8]) -> Result<(), modules::core::Error> {
431 for_tuples!( #( Tuple::approve_raw_tx(ctx, tx)?; )* );
432 Ok(())
433 }
434
435 fn approve_unverified_tx<C: Context>(
436 ctx: &C,
437 utx: &UnverifiedTransaction,
438 ) -> Result<(), modules::core::Error> {
439 for_tuples!( #( Tuple::approve_unverified_tx(ctx, utx)?; )* );
440 Ok(())
441 }
442
443 fn decode_tx<C: Context>(
444 ctx: &C,
445 scheme: &str,
446 body: &[u8],
447 ) -> Result<Option<Transaction>, modules::core::Error> {
448 for_tuples!( #(
449 let decoded = Tuple::decode_tx(ctx, scheme, body)?;
450 if (decoded.is_some()) {
451 return Ok(decoded);
452 }
453 )* );
454 Ok(None)
455 }
456
457 fn authenticate_tx<C: Context>(
458 ctx: &C,
459 tx: &Transaction,
460 ) -> Result<AuthDecision, modules::core::Error> {
461 for_tuples!( #(
462 match Tuple::authenticate_tx(ctx, tx)? {
463 AuthDecision::Stop => return Ok(AuthDecision::Stop),
464 AuthDecision::Continue => {},
465 }
466 )* );
467
468 Ok(AuthDecision::Continue)
469 }
470
471 fn before_handle_call<C: Context>(ctx: &C, call: &Call) -> Result<(), modules::core::Error> {
472 for_tuples!( #( Tuple::before_handle_call(ctx, call)?; )* );
473 Ok(())
474 }
475
476 fn before_authorized_call_dispatch<C: Context>(
477 ctx: &C,
478 call: &Call,
479 ) -> Result<(), modules::core::Error> {
480 for_tuples!( #( Tuple::before_authorized_call_dispatch(ctx, call)?; )* );
481 Ok(())
482 }
483
484 fn after_handle_call<C: Context>(
485 ctx: &C,
486 mut result: CallResult,
487 ) -> Result<CallResult, modules::core::Error> {
488 for_tuples!( #(
489 result = Tuple::after_handle_call(ctx, result)?;
490 )* );
491 Ok(result)
492 }
493
494 fn after_dispatch_tx<C: Context>(ctx: &C, tx_auth_info: &AuthInfo, result: &CallResult) {
495 for_tuples!( #( Tuple::after_dispatch_tx(ctx, tx_auth_info, result); )* );
496 }
497}
498
499pub trait FeeProxyHandler {
501 fn resolve_payer<C: Context>(
504 ctx: &C,
505 tx: &Transaction,
506 ) -> Result<Option<Address>, modules::core::Error>;
507}
508
509#[impl_for_tuples(30)]
510impl FeeProxyHandler for Tuple {
511 fn resolve_payer<C: Context>(
512 ctx: &C,
513 tx: &Transaction,
514 ) -> Result<Option<Address>, modules::core::Error> {
515 for_tuples!( #(
516 if let Some(payer) = Tuple::resolve_payer(ctx, tx)? {
517 return Ok(Some(payer));
518 }
519 )* );
520
521 Ok(None)
522 }
523}
524
525pub trait MigrationHandler {
527 type Genesis;
532
533 fn init_or_migrate<C: Context>(
537 _ctx: &C,
538 _meta: &mut modules::core::types::Metadata,
539 _genesis: Self::Genesis,
540 ) -> bool {
541 false
543 }
544}
545
546#[allow(clippy::type_complexity)]
547#[impl_for_tuples(30)]
548impl MigrationHandler for Tuple {
549 for_tuples!( type Genesis = ( #( Tuple::Genesis ),* ); );
550
551 fn init_or_migrate<C: Context>(
552 ctx: &C,
553 meta: &mut modules::core::types::Metadata,
554 genesis: Self::Genesis,
555 ) -> bool {
556 [for_tuples!( #( Tuple::init_or_migrate(ctx, meta, genesis.Tuple) ),* )]
557 .iter()
558 .any(|x| *x)
559 }
560}
561
562pub trait BlockHandler {
564 fn begin_block<C: Context>(_ctx: &C) {
567 }
569
570 fn end_block<C: Context>(_ctx: &C) {
573 }
575}
576
577#[impl_for_tuples(30)]
578impl BlockHandler for Tuple {
579 fn begin_block<C: Context>(ctx: &C) {
580 for_tuples!( #( Tuple::begin_block(ctx); )* );
581 }
582
583 fn end_block<C: Context>(ctx: &C) {
584 for_tuples!( #( Tuple::end_block(ctx); )* );
585 }
586}
587
588pub trait InvariantHandler {
590 fn check_invariants<C: Context>(_ctx: &C) -> Result<(), modules::core::Error> {
592 Ok(())
594 }
595}
596
597#[impl_for_tuples(30)]
598impl InvariantHandler for Tuple {
599 fn check_invariants<C: Context>(ctx: &C) -> Result<(), modules::core::Error> {
601 for_tuples!( #( Tuple::check_invariants(ctx)?; )* );
602 Ok(())
603 }
604}
605
606pub trait ModuleInfoHandler {
608 fn module_info<C: Context>(_ctx: &C) -> BTreeMap<String, ModuleInfo>;
610}
611
612impl<M: Module + MethodHandler> ModuleInfoHandler for M {
613 fn module_info<C: Context>(_ctx: &C) -> BTreeMap<String, ModuleInfo> {
614 let mut info = BTreeMap::new();
615 info.insert(
616 Self::NAME.to_string(),
617 ModuleInfo {
618 version: Self::VERSION,
619 params: Self::params().into_cbor_value(),
620 methods: Self::supported_methods(),
621 },
622 );
623 info
624 }
625}
626
627#[impl_for_tuples(30)]
628impl ModuleInfoHandler for Tuple {
629 #[allow(clippy::let_and_return)]
630 fn module_info<C: Context>(ctx: &C) -> BTreeMap<String, ModuleInfo> {
631 let mut merged = BTreeMap::new();
632 for_tuples!( #(
633 merged.extend(Tuple::module_info(ctx));
634 )* );
635 merged
636 }
637}
638
639pub trait Module {
641 const NAME: &'static str;
643
644 const VERSION: u32 = 1;
646
647 type Error: error::Error + 'static;
649
650 type Event: event::Event + 'static;
652
653 type Parameters: Parameters + 'static;
655
656 fn params() -> Self::Parameters {
658 CurrentState::with_store(|store| {
659 let store = storage::PrefixStore::new(store, &Self::NAME);
660 let store = storage::TypedStore::new(store);
661 store.get(Self::Parameters::STORE_KEY).unwrap_or_default()
662 })
663 }
664
665 fn set_params(params: Self::Parameters) {
667 CurrentState::with_store(|store| {
668 let store = storage::PrefixStore::new(store, &Self::NAME);
669 let mut store = storage::TypedStore::new(store);
670 store.insert(Self::Parameters::STORE_KEY, params);
671 });
672 }
673}
674
675pub trait Parameters: Debug + Default + cbor::Encode + cbor::Decode {
677 type Error;
678
679 const STORE_KEY: &'static [u8] = &[0x00];
681
682 fn validate_basic(&self) -> Result<(), Self::Error> {
684 Ok(())
686 }
687}
688
689impl Parameters for () {
690 type Error = std::convert::Infallible;
691}
692
693#[cfg(test)]
694mod test {
695 use super::*;
696 use crate::testing::mock;
697
698 struct TestAuthContinue;
700 struct TestAuthStop;
702 struct TestAuthFail;
704
705 impl super::TransactionHandler for TestAuthContinue {
706 fn authenticate_tx<C: Context>(
707 _ctx: &C,
708 _tx: &Transaction,
709 ) -> Result<AuthDecision, modules::core::Error> {
710 Ok(AuthDecision::Continue)
711 }
712 }
713
714 impl super::TransactionHandler for TestAuthStop {
715 fn authenticate_tx<C: Context>(
716 _ctx: &C,
717 _tx: &Transaction,
718 ) -> Result<AuthDecision, modules::core::Error> {
719 Ok(AuthDecision::Stop)
720 }
721 }
722
723 impl super::TransactionHandler for TestAuthFail {
724 fn authenticate_tx<C: Context>(
725 _ctx: &C,
726 _tx: &Transaction,
727 ) -> Result<AuthDecision, modules::core::Error> {
728 Err(modules::core::Error::NotAuthenticated)
729 }
730 }
731
732 #[test]
733 fn test_authenticate_tx() {
734 let mut mock = mock::Mock::default();
735 let ctx = mock.create_ctx();
736 let tx = mock::transaction();
737
738 let result = TestAuthContinue::authenticate_tx(&ctx, &tx).unwrap();
740 assert!(matches!(result, AuthDecision::Continue));
741 let result = TestAuthStop::authenticate_tx(&ctx, &tx).unwrap();
742 assert!(matches!(result, AuthDecision::Stop));
743 let _ = TestAuthFail::authenticate_tx(&ctx, &tx).unwrap_err();
744
745 type Composed1 = (TestAuthContinue, TestAuthContinue, TestAuthContinue);
747 let result = Composed1::authenticate_tx(&ctx, &tx).unwrap();
748 assert!(matches!(result, AuthDecision::Continue));
749
750 type Composed2 = (TestAuthContinue, TestAuthStop, TestAuthContinue);
751 let result = Composed2::authenticate_tx(&ctx, &tx).unwrap();
752 assert!(matches!(result, AuthDecision::Stop));
753
754 type Composed3 = (TestAuthContinue, TestAuthStop, TestAuthFail);
755 let result = Composed3::authenticate_tx(&ctx, &tx).unwrap();
756 assert!(matches!(result, AuthDecision::Stop));
757
758 type Composed4 = (TestAuthFail, TestAuthStop, TestAuthContinue);
759 let _ = Composed4::authenticate_tx(&ctx, &tx).unwrap_err();
760
761 type Composed5 = (TestAuthContinue, TestAuthContinue, TestAuthFail);
762 let _ = Composed5::authenticate_tx(&ctx, &tx).unwrap_err();
763 }
764}