cap_common/
bucket.rs

1use crate::did::*;
2use crate::transaction::Event;
3use crate::TransactionList;
4use certified_vars::hashtree::{fork, fork_hash};
5use certified_vars::{AsHashTree, Hash, HashTree, Map, Seq};
6use ic_kit::candid::CandidType;
7use ic_kit::Principal;
8use serde::{Deserialize, Serialize};
9
10#[derive(CandidType, Serialize, Deserialize)]
11pub struct Bucket {
12    bucket: TransactionList,
13    buckets: Map<TransactionId, Principal>,
14    next_canisters: Seq<BucketId>,
15    contract: TokenContractId,
16}
17
18impl Bucket {
19    /// Create a new bucket.
20    pub fn new(contract: TokenContractId, offset: u64) -> Self {
21        Self {
22            bucket: TransactionList::new(contract, offset),
23            buckets: Map::new(),
24            next_canisters: Seq::new(),
25            contract,
26        }
27    }
28
29    pub fn with_transaction_list(list: TransactionList) -> Self {
30        let contract = *list.contract_id();
31        Self {
32            bucket: list,
33            buckets: Map::new(),
34            next_canisters: Seq::new(),
35            contract,
36        }
37    }
38
39    pub fn get_next_canisters(&self, arg: WithWitnessArg) -> GetNextCanistersResponse {
40        let witness = match arg.witness {
41            false => None,
42            true => Some(
43                fork(
44                    HashTree::Pruned(fork_hash(
45                        &self.bucket.root_hash(),
46                        &self.buckets.root_hash(),
47                    )),
48                    self.next_canisters.as_hash_tree(),
49                )
50                .into(),
51            ),
52        };
53
54        let canisters = self.next_canisters.as_vec().clone();
55
56        GetNextCanistersResponse { canisters, witness }
57    }
58
59    pub fn get_transaction(&self, arg: WithIdArg) -> GetTransactionResponse {
60        let witness = match arg.witness {
61            false => None,
62            true => Some(
63                fork(
64                    fork(
65                        self.bucket.witness_transaction(arg.id),
66                        HashTree::Pruned(self.buckets.root_hash()),
67                    ),
68                    HashTree::Pruned(self.next_canisters.root_hash()),
69                )
70                .into(),
71            ),
72        };
73
74        let event = self.bucket.get_transaction(arg.id);
75
76        // TODO(qti3e) We're going to be in this release, take another look.
77        // We are not multi-canistered yet.
78        GetTransactionResponse::Found(event.cloned(), witness)
79    }
80
81    pub fn get_transactions(&self, arg: GetTransactionsArg) -> GetTransactionsResponseBorrowed {
82        let page = arg
83            .page
84            .unwrap_or_else(|| self.bucket.last_page_for_contract(&self.contract));
85
86        let witness = match arg.witness {
87            false => None,
88            true => Some(
89                fork(
90                    fork(
91                        self.bucket
92                            .witness_transactions_for_contract(&self.contract, page),
93                        HashTree::Pruned(self.buckets.root_hash()),
94                    ),
95                    HashTree::Pruned(self.next_canisters.root_hash()),
96                )
97                .into(),
98            ),
99        };
100
101        let events = self
102            .bucket
103            .get_transactions_for_contract(&self.contract, page);
104
105        GetTransactionsResponseBorrowed {
106            data: events,
107            page,
108            witness,
109        }
110    }
111
112    pub fn get_user_transactions(
113        &self,
114        arg: GetUserTransactionsArg,
115    ) -> GetTransactionsResponseBorrowed {
116        let page = arg
117            .page
118            .unwrap_or_else(|| self.bucket.last_page_for_user(&arg.user));
119
120        let witness = match arg.witness {
121            false => None,
122            true => Some(
123                fork(
124                    fork(
125                        self.bucket.witness_transactions_for_user(&arg.user, page),
126                        HashTree::Pruned(self.buckets.root_hash()),
127                    ),
128                    HashTree::Pruned(self.next_canisters.root_hash()),
129                )
130                .into(),
131            ),
132        };
133
134        let events = self.bucket.get_transactions_for_user(&arg.user, page);
135
136        GetTransactionsResponseBorrowed {
137            data: events,
138            page,
139            witness,
140        }
141    }
142
143    pub fn get_token_transactions(
144        &self,
145        arg: GetTokenTransactionsArg,
146    ) -> GetTransactionsResponseBorrowed {
147        let page = arg
148            .page
149            .unwrap_or_else(|| self.bucket.last_page_for_token(&arg.token_id));
150
151        let witness = match arg.witness {
152            false => None,
153            true => Some(
154                fork(
155                    fork(
156                        self.bucket
157                            .witness_transactions_for_token(&arg.token_id, page),
158                        HashTree::Pruned(self.buckets.root_hash()),
159                    ),
160                    HashTree::Pruned(self.next_canisters.root_hash()),
161                )
162                .into(),
163            ),
164        };
165
166        let events = self.bucket.get_transactions_for_token(&arg.token_id, page);
167
168        GetTransactionsResponseBorrowed {
169            data: events,
170            page,
171            witness,
172        }
173    }
174
175    pub fn get_bucket_for(&self, arg: WithIdArg) -> GetBucketResponse {
176        let id_witness = self.buckets.witness(&arg.id);
177        let id = id_witness
178            .get_leaf_values()
179            .get(0)
180            .map(|bytes| Principal::from_slice(bytes))
181            .unwrap_or_else(ic_kit::ic::id);
182
183        let witness = match arg.witness {
184            false => None,
185            true => Some(
186                fork(
187                    fork(HashTree::Pruned(self.bucket.root_hash()), id_witness),
188                    HashTree::Pruned(self.next_canisters.root_hash()),
189                )
190                .into(),
191            ),
192        };
193
194        GetBucketResponse {
195            canister: id,
196            witness,
197        }
198    }
199
200    pub fn size(&self) -> u64 {
201        self.bucket.size()
202    }
203
204    pub fn contract_id(&self) -> &Principal {
205        &self.contract
206    }
207
208    #[inline]
209    pub fn insert(&mut self, event: Event) -> u64 {
210        self.bucket.insert(event)
211    }
212
213    #[inline]
214    pub fn set_next_canisters(&mut self, canisters: Vec<Principal>) {
215        self.next_canisters = canisters.into();
216    }
217}
218
219impl AsHashTree for Bucket {
220    fn root_hash(&self) -> Hash {
221        fork_hash(
222            &fork_hash(&self.bucket.root_hash(), &self.buckets.root_hash()),
223            &self.next_canisters.root_hash(),
224        )
225    }
226
227    fn as_hash_tree(&self) -> HashTree<'_> {
228        fork(
229            fork(self.bucket.as_hash_tree(), self.buckets.as_hash_tree()),
230            self.next_canisters.as_hash_tree(),
231        )
232    }
233}