tari_template_lib/component/
manager.rs

1//  Copyright 2022. The Tari Project
2//
3//  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4//  following conditions are met:
5//
6//  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7//  disclaimer.
8//
9//  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10//  following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12//  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13//  products derived from this software without specific prior written permission.
14//
15//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16//  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17//  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20//  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21//  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23use serde::{de::DeserializeOwned, Serialize};
24use tari_bor::{from_value, to_value};
25use tari_template_abi::{call_engine, EngineOp};
26use tari_template_lib_types::{bytes::Bytes, TemplateAddress};
27
28use crate::{
29    args::{CallAction, CallInvokeArg, CallMethodArg, ComponentAction, ComponentInvokeArg, ComponentRef, InvokeResult},
30    auth::ComponentAccessRules,
31    caller_context::CallerContext,
32    models::ComponentAddress,
33};
34
35/// Utility for managing components inside templates
36pub struct ComponentManager {
37    address: ComponentAddress,
38}
39
40impl ComponentManager {
41    /// Returns a new `ComponentManager` for the component specified by `address`
42    pub(crate) fn new(address: ComponentAddress) -> Self {
43        Self { address }
44    }
45
46    /// Returns the address of the component that is being managed
47    pub fn get(address: ComponentAddress) -> Self {
48        Self { address }
49    }
50
51    /// Returns the address of the component that is being called in the current instruction.
52    /// Assumes that the instruction is a call method; otherwise, it will panic
53    pub fn current() -> Self {
54        Self::new(CallerContext::current_component_address())
55    }
56
57    /// Calls a method of another component and returns the result.
58    /// This is used to call external component methods and can be used in a component method or template function
59    /// context.
60    pub fn call<T: Into<String>, R: DeserializeOwned, B: Into<Bytes>>(&self, method: T, args: Vec<B>) -> R {
61        self.call_internal(CallMethodArg {
62            component_address: self.address,
63            method: method.into(),
64            args: args.into_iter().map(Into::into).collect(),
65        })
66    }
67
68    fn call_internal<T: DeserializeOwned>(&self, arg: CallMethodArg) -> T {
69        let result = call_engine::<_, InvokeResult>(EngineOp::CallInvoke, &CallInvokeArg {
70            action: CallAction::CallMethod,
71            args: invoke_args![arg],
72        });
73
74        result
75            .decode()
76            .expect("failed to decode component call result from engine")
77    }
78
79    /// Calls a method of another component. The called method must return a unit type.
80    /// Equivalent to [`call::<_, ()>(method, args)`](ComponentManager::call).
81    pub fn invoke<T: Into<String>, B: Into<Bytes>>(&self, method: T, args: Vec<B>) {
82        self.call(method, args)
83    }
84
85    /// Get the component state
86    pub fn get_state<T: DeserializeOwned>(&self) -> T {
87        let result = call_engine::<_, InvokeResult>(EngineOp::ComponentInvoke, &ComponentInvokeArg {
88            component_ref: ComponentRef::Ref(self.address),
89            action: ComponentAction::GetState,
90            args: invoke_args![],
91        });
92
93        let component: tari_bor::Value = result.decode().expect("failed to decode component state from engine");
94        from_value(&component).expect("Failed to decode component state")
95    }
96
97    /// Update the component state
98    pub fn set_state<T: Serialize>(&self, state: T) {
99        let state = to_value(&state).expect("Failed to encode component state");
100        let _result = call_engine::<_, InvokeResult>(EngineOp::ComponentInvoke, &ComponentInvokeArg {
101            component_ref: ComponentRef::Ref(self.address),
102            action: ComponentAction::SetState,
103            args: invoke_args![state],
104        });
105    }
106
107    /// Updates access rules that determine who can invoke methods in the component
108    /// It will panic if the caller doesn't have permissions for updating access rules
109    pub fn set_access_rules(&self, access_rules: ComponentAccessRules) {
110        call_engine::<_, InvokeResult>(EngineOp::ComponentInvoke, &ComponentInvokeArg {
111            component_ref: ComponentRef::Ref(self.address),
112            action: ComponentAction::SetAccessRules,
113            args: invoke_args![access_rules],
114        });
115    }
116
117    /// Returns the template address of the component that is being managed
118    pub fn get_template_address(&self) -> TemplateAddress {
119        let result = call_engine::<_, InvokeResult>(EngineOp::ComponentInvoke, &ComponentInvokeArg {
120            component_ref: ComponentRef::Ref(self.address),
121            action: ComponentAction::GetTemplateAddress,
122            args: invoke_args![],
123        });
124
125        result
126            .decode()
127            .expect("failed to decode component template address from engine")
128    }
129
130    pub fn component_address(&self) -> ComponentAddress {
131        self.address
132    }
133}