tari_template_lib/models/
proof.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 std::collections::BTreeSet;
24
25use serde::{Deserialize, Serialize};
26use tari_bor::BorTag;
27use tari_template_abi::{call_engine, rust::fmt, EngineOp};
28
29use crate::{
30    args::{InvokeResult, ProofAction, ProofInvokeArg, ProofRef},
31    models::{BinaryTag, NonFungibleId, ResourceAddress},
32    prelude::ResourceType,
33    types::Amount,
34};
35
36const TAG: u64 = BinaryTag::ProofId.as_u64();
37
38/// The unique identification of a proof during a transaction execution
39#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Hash)]
40#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
41pub struct ProofId(BorTag<u32, TAG>);
42
43impl From<u32> for ProofId {
44    fn from(value: u32) -> Self {
45        Self(BorTag::new(value))
46    }
47}
48
49impl fmt::Display for ProofId {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        write!(f, "ProofId({})", self.0.inner())
52    }
53}
54
55/// Allows a user to prove ownership of a resource. Proofs only live during the execution of a transaction.
56/// The main use case is to prove that the user has a specific badge during cross-template calls
57#[derive(Debug, Clone, Serialize, Deserialize)]
58#[serde(transparent)]
59pub struct Proof {
60    id: ProofId,
61}
62
63impl Proof {
64    pub const fn from_id(id: ProofId) -> Self {
65        Self { id }
66    }
67
68    pub fn id(&self) -> ProofId {
69        self.id
70    }
71
72    pub fn resource_address(&self) -> ResourceAddress {
73        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
74            proof_ref: ProofRef::Ref(self.id),
75            action: ProofAction::GetResourceAddress,
76            args: invoke_args![],
77        });
78
79        resp.decode()
80            .expect("Proof GetResourceAddress returned invalid resource address")
81    }
82
83    pub fn resource_type(&self) -> ResourceType {
84        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
85            proof_ref: ProofRef::Ref(self.id),
86            action: ProofAction::GetResourceType,
87            args: invoke_args![],
88        });
89
90        resp.decode()
91            .expect("Proof GetResourceType returned invalid resource type")
92    }
93
94    pub fn get_non_fungibles(&self) -> BTreeSet<NonFungibleId> {
95        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
96            proof_ref: ProofRef::Ref(self.id),
97            action: ProofAction::GetNonFungibles,
98            args: invoke_args![],
99        });
100
101        resp.decode()
102            .expect("Proof GetNonFungibles returned invalid non-fungibles")
103    }
104
105    pub fn amount(&self) -> Amount {
106        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
107            proof_ref: ProofRef::Ref(self.id),
108            action: ProofAction::GetAmount,
109            args: invoke_args![],
110        });
111
112        resp.decode().expect("Proof GetAmount returned invalid amount")
113    }
114
115    #[must_use = "ProofAccess must used"]
116    pub fn authorize(&self) -> ProofAccess {
117        self.try_authorize().expect("Proof authorization failed")
118    }
119
120    pub fn authorize_with<F: FnOnce() -> R, R>(&self, f: F) -> R {
121        let _auth = self.try_authorize().expect("Proof authorization failed");
122        f()
123    }
124
125    /// Try to authorize the proof. If the proof cannot be authorized, this will return an error.
126    pub fn try_authorize(&self) -> Result<ProofAccess, NotAuthorized> {
127        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
128            proof_ref: ProofRef::Ref(self.id),
129            action: ProofAction::Authorize,
130            args: invoke_args![],
131        });
132
133        resp.decode::<Result<(), NotAuthorized>>()
134            .expect("Proof Access error")?;
135        Ok(ProofAccess { id: self.id })
136    }
137
138    /// Drop/destroy this proof
139    pub fn drop(self) {
140        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
141            proof_ref: ProofRef::Ref(self.id),
142            action: ProofAction::Drop,
143            args: invoke_args![],
144        });
145
146        resp.decode().expect("Proof drop error")
147    }
148
149    pub fn assert_resource(&self, resource_address: ResourceAddress) {
150        assert_eq!(
151            self.resource_address(),
152            resource_address,
153            "Proof of resource did not match {resource_address}"
154        );
155    }
156}
157
158/// Returned when a proof cannot be authorized
159#[derive(Debug, Serialize, Deserialize)]
160pub struct NotAuthorized;
161
162/// TODO: Clean this up
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct ProofAccess {
165    pub id: ProofId,
166}
167
168impl Drop for ProofAccess {
169    fn drop(&mut self) {
170        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
171            proof_ref: ProofRef::Ref(self.id),
172            action: ProofAction::DropAuthorize,
173            args: invoke_args![],
174        });
175
176        resp.decode::<()>()
177            .unwrap_or_else(|_| panic!("Drop failed for proof {}", self.id));
178    }
179}
180
181/// A RAII type that holds a reference to a proof that is authorized for use.
182/// Once this is dropped the proof goes out of scope and can no longer be used.
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct ProofAuth {
185    pub id: ProofId,
186}
187
188impl Drop for ProofAuth {
189    fn drop(&mut self) {
190        let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
191            proof_ref: ProofRef::Ref(self.id),
192            action: ProofAction::Drop,
193            args: invoke_args![],
194        });
195
196        resp.decode::<()>()
197            .unwrap_or_else(|_| panic!("Drop failed for proof {}", self.id));
198    }
199}