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<(), modules::core::Error> {
418 Ok(())
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 result: &CallResult,
487 ) -> Result<(), modules::core::Error> {
488 let mut first_err: Option<modules::core::Error> = None;
489 for_tuples!( #(
490 match Tuple::after_handle_call(ctx, &result) {
491 Err(e) if first_err.is_none() => first_err = Some(e),
492 _ => {}
493 }
494 )* );
495
496 first_err.map_or(Ok(()), Err)
497 }
498
499 fn after_dispatch_tx<C: Context>(ctx: &C, tx_auth_info: &AuthInfo, result: &CallResult) {
500 for_tuples!( #( Tuple::after_dispatch_tx(ctx, tx_auth_info, result); )* );
501 }
502}
503
504pub trait FeeProxyHandler {
506 fn resolve_payer<C: Context>(
509 ctx: &C,
510 tx: &Transaction,
511 ) -> Result<Option<Address>, modules::core::Error>;
512}
513
514#[impl_for_tuples(30)]
515impl FeeProxyHandler for Tuple {
516 fn resolve_payer<C: Context>(
517 ctx: &C,
518 tx: &Transaction,
519 ) -> Result<Option<Address>, modules::core::Error> {
520 for_tuples!( #(
521 if let Some(payer) = Tuple::resolve_payer(ctx, tx)? {
522 return Ok(Some(payer));
523 }
524 )* );
525
526 Ok(None)
527 }
528}
529
530pub trait MigrationHandler {
532 type Genesis;
537
538 fn init_or_migrate<C: Context>(
542 _ctx: &C,
543 _meta: &mut modules::core::types::Metadata,
544 _genesis: Self::Genesis,
545 ) -> bool {
546 false
548 }
549}
550
551#[allow(clippy::type_complexity)]
552#[impl_for_tuples(30)]
553impl MigrationHandler for Tuple {
554 for_tuples!( type Genesis = ( #( Tuple::Genesis ),* ); );
555
556 fn init_or_migrate<C: Context>(
557 ctx: &C,
558 meta: &mut modules::core::types::Metadata,
559 genesis: Self::Genesis,
560 ) -> bool {
561 [for_tuples!( #( Tuple::init_or_migrate(ctx, meta, genesis.Tuple) ),* )]
562 .iter()
563 .any(|x| *x)
564 }
565}
566
567pub trait BlockHandler {
569 fn begin_block<C: Context>(_ctx: &C) {
572 }
574
575 fn end_block<C: Context>(_ctx: &C) {
578 }
580}
581
582#[impl_for_tuples(30)]
583impl BlockHandler for Tuple {
584 fn begin_block<C: Context>(ctx: &C) {
585 for_tuples!( #( Tuple::begin_block(ctx); )* );
586 }
587
588 fn end_block<C: Context>(ctx: &C) {
589 for_tuples!( #( Tuple::end_block(ctx); )* );
590 }
591}
592
593pub trait InvariantHandler {
595 fn check_invariants<C: Context>(_ctx: &C) -> Result<(), modules::core::Error> {
597 Ok(())
599 }
600}
601
602#[impl_for_tuples(30)]
603impl InvariantHandler for Tuple {
604 fn check_invariants<C: Context>(ctx: &C) -> Result<(), modules::core::Error> {
606 for_tuples!( #( Tuple::check_invariants(ctx)?; )* );
607 Ok(())
608 }
609}
610
611pub trait ModuleInfoHandler {
613 fn module_info<C: Context>(_ctx: &C) -> BTreeMap<String, ModuleInfo>;
615}
616
617impl<M: Module + MethodHandler> ModuleInfoHandler for M {
618 fn module_info<C: Context>(_ctx: &C) -> BTreeMap<String, ModuleInfo> {
619 let mut info = BTreeMap::new();
620 info.insert(
621 Self::NAME.to_string(),
622 ModuleInfo {
623 version: Self::VERSION,
624 params: Self::params().into_cbor_value(),
625 methods: Self::supported_methods(),
626 },
627 );
628 info
629 }
630}
631
632#[impl_for_tuples(30)]
633impl ModuleInfoHandler for Tuple {
634 #[allow(clippy::let_and_return)]
635 fn module_info<C: Context>(ctx: &C) -> BTreeMap<String, ModuleInfo> {
636 let mut merged = BTreeMap::new();
637 for_tuples!( #(
638 merged.extend(Tuple::module_info(ctx));
639 )* );
640 merged
641 }
642}
643
644pub trait Module {
646 const NAME: &'static str;
648
649 const VERSION: u32 = 1;
651
652 type Error: error::Error + 'static;
654
655 type Event: event::Event + 'static;
657
658 type Parameters: Parameters + 'static;
660
661 fn params() -> Self::Parameters {
663 CurrentState::with_store(|store| {
664 let store = storage::PrefixStore::new(store, &Self::NAME);
665 let store = storage::TypedStore::new(store);
666 store.get(Self::Parameters::STORE_KEY).unwrap_or_default()
667 })
668 }
669
670 fn set_params(params: Self::Parameters) {
672 CurrentState::with_store(|store| {
673 let store = storage::PrefixStore::new(store, &Self::NAME);
674 let mut store = storage::TypedStore::new(store);
675 store.insert(Self::Parameters::STORE_KEY, params);
676 });
677 }
678}
679
680pub trait Parameters: Debug + Default + cbor::Encode + cbor::Decode {
682 type Error;
683
684 const STORE_KEY: &'static [u8] = &[0x00];
686
687 fn validate_basic(&self) -> Result<(), Self::Error> {
689 Ok(())
691 }
692}
693
694impl Parameters for () {
695 type Error = std::convert::Infallible;
696}
697
698#[cfg(test)]
699mod test {
700 use super::*;
701 use crate::testing::mock;
702
703 struct TestAuthContinue;
705 struct TestAuthStop;
707 struct TestAuthFail;
709
710 impl super::TransactionHandler for TestAuthContinue {
711 fn authenticate_tx<C: Context>(
712 _ctx: &C,
713 _tx: &Transaction,
714 ) -> Result<AuthDecision, modules::core::Error> {
715 Ok(AuthDecision::Continue)
716 }
717 }
718
719 impl super::TransactionHandler for TestAuthStop {
720 fn authenticate_tx<C: Context>(
721 _ctx: &C,
722 _tx: &Transaction,
723 ) -> Result<AuthDecision, modules::core::Error> {
724 Ok(AuthDecision::Stop)
725 }
726 }
727
728 impl super::TransactionHandler for TestAuthFail {
729 fn authenticate_tx<C: Context>(
730 _ctx: &C,
731 _tx: &Transaction,
732 ) -> Result<AuthDecision, modules::core::Error> {
733 Err(modules::core::Error::NotAuthenticated)
734 }
735 }
736
737 #[test]
738 fn test_authenticate_tx() {
739 let mut mock = mock::Mock::default();
740 let ctx = mock.create_ctx();
741 let tx = mock::transaction();
742
743 let result = TestAuthContinue::authenticate_tx(&ctx, &tx).unwrap();
745 assert!(matches!(result, AuthDecision::Continue));
746 let result = TestAuthStop::authenticate_tx(&ctx, &tx).unwrap();
747 assert!(matches!(result, AuthDecision::Stop));
748 let _ = TestAuthFail::authenticate_tx(&ctx, &tx).unwrap_err();
749
750 type Composed1 = (TestAuthContinue, TestAuthContinue, TestAuthContinue);
752 let result = Composed1::authenticate_tx(&ctx, &tx).unwrap();
753 assert!(matches!(result, AuthDecision::Continue));
754
755 type Composed2 = (TestAuthContinue, TestAuthStop, TestAuthContinue);
756 let result = Composed2::authenticate_tx(&ctx, &tx).unwrap();
757 assert!(matches!(result, AuthDecision::Stop));
758
759 type Composed3 = (TestAuthContinue, TestAuthStop, TestAuthFail);
760 let result = Composed3::authenticate_tx(&ctx, &tx).unwrap();
761 assert!(matches!(result, AuthDecision::Stop));
762
763 type Composed4 = (TestAuthFail, TestAuthStop, TestAuthContinue);
764 let _ = Composed4::authenticate_tx(&ctx, &tx).unwrap_err();
765
766 type Composed5 = (TestAuthContinue, TestAuthContinue, TestAuthFail);
767 let _ = Composed5::authenticate_tx(&ctx, &tx).unwrap_err();
768 }
769}