casper_storage/system/
mint.rs1pub(crate) mod detail;
2mod mint_native;
4pub mod runtime_provider;
6pub mod storage_provider;
8pub mod system_provider;
10
11use num_rational::Ratio;
12use num_traits::CheckedMul;
13
14use casper_types::{
15 account::AccountHash,
16 system::{
17 mint::{Error, ROUND_SEIGNIORAGE_RATE_KEY, TOTAL_SUPPLY_KEY},
18 Caller,
19 },
20 Key, PublicKey, URef, U512,
21};
22
23use crate::system::mint::{
24 runtime_provider::RuntimeProvider, storage_provider::StorageProvider,
25 system_provider::SystemProvider,
26};
27
28pub trait Mint: RuntimeProvider + StorageProvider + SystemProvider {
30 fn mint(&mut self, initial_balance: U512) -> Result<URef, Error> {
33 let caller = self.get_caller();
34 let is_empty_purse = initial_balance.is_zero();
35 if !is_empty_purse && caller != PublicKey::System.to_account_hash() {
36 return Err(Error::InvalidNonEmptyPurseCreation);
37 }
38
39 let purse_uref: URef = self.new_uref(())?;
40 self.write_balance(purse_uref, initial_balance)?;
41
42 if !is_empty_purse {
43 let total_supply_uref = match self.get_key(TOTAL_SUPPLY_KEY) {
45 None => {
46 return Err(Error::TotalSupplyNotFound);
48 }
49 Some(Key::URef(uref)) => uref,
50 Some(_) => return Err(Error::MissingKey),
51 };
52 self.add(total_supply_uref, initial_balance)?;
54 }
55
56 Ok(purse_uref)
57 }
58
59 fn burn(&mut self, purse: URef, amount: U512) -> Result<(), Error> {
61 if !purse.is_writeable() {
62 return Err(Error::InvalidAccessRights);
63 }
64 if !self.is_valid_uref(&purse) {
65 return Err(Error::ForgedReference);
66 }
67
68 let source_available_balance: U512 = match self.balance(purse)? {
69 Some(source_balance) => source_balance,
70 None => return Err(Error::PurseNotFound),
71 };
72
73 let new_balance = source_available_balance
74 .checked_sub(amount)
75 .unwrap_or_else(U512::zero);
76 self.write_balance(purse, new_balance)?;
78 let burned_amount = source_available_balance.saturating_sub(new_balance);
80 detail::reduce_total_supply_unsafe(self, burned_amount)
81 }
82
83 fn reduce_total_supply(&mut self, amount: U512) -> Result<(), Error> {
86 let caller = self.get_caller();
88 if caller != PublicKey::System.to_account_hash() {
89 return Err(Error::InvalidTotalSupplyReductionAttempt);
90 }
91
92 detail::reduce_total_supply_unsafe(self, amount)
93 }
94
95 fn balance(&mut self, purse: URef) -> Result<Option<U512>, Error> {
97 match self.available_balance(purse)? {
98 some @ Some(_) => Ok(some),
99 None => Err(Error::PurseNotFound),
100 }
101 }
102
103 fn transfer(
105 &mut self,
106 maybe_to: Option<AccountHash>,
107 source: URef,
108 target: URef,
109 amount: U512,
110 id: Option<u64>,
111 ) -> Result<(), Error> {
112 if !self.allow_unrestricted_transfers() {
113 let registry = self
114 .get_system_entity_registry()
115 .map_err(|_| Error::UnableToGetSystemRegistry)?;
116 let immediate_caller = self.get_immediate_caller();
117 match immediate_caller {
118 Some(Caller::Entity { entity_addr, .. })
119 if registry.exists(&entity_addr.value()) =>
120 {
121 }
124
125 Some(Caller::Initiator { account_hash: _ })
126 if self.is_called_from_standard_payment() =>
127 {
128 }
131
132 Some(Caller::Initiator { account_hash })
133 if account_hash == PublicKey::System.to_account_hash() =>
134 {
135 }
137
138 Some(Caller::Initiator { account_hash }) => {
139 let is_source_admin = self.is_administrator(&account_hash);
142 match maybe_to {
143 Some(to) => {
144 let maybe_account = self.runtime_footprint_by_account_hash(to);
145
146 match maybe_account {
147 Ok(Some(runtime_footprint)) => {
148 let addr = if let Some(uref) = runtime_footprint.main_purse() {
152 uref.addr()
153 } else {
154 return Err(Error::InvalidContext);
155 };
156
157 if addr != target.addr() {
158 return Err(Error::DisabledUnrestrictedTransfers);
159 }
160 let is_target_system_account =
161 to == PublicKey::System.to_account_hash();
162 let is_target_administrator = self.is_administrator(&to);
163 if !(is_source_admin
164 || is_target_system_account
165 || is_target_administrator)
166 {
167 return Err(Error::DisabledUnrestrictedTransfers);
168 }
169 }
170 Ok(None) => {
171 if !is_source_admin {
176 return Err(Error::DisabledUnrestrictedTransfers);
177 }
178 }
179 Err(_) => {
180 return Err(Error::Storage);
181 }
182 }
183 }
184 None => {
185 if !is_source_admin {
186 return Err(Error::DisabledUnrestrictedTransfers);
187 }
188 }
189 }
190 }
191
192 Some(Caller::Entity {
193 package_hash: _,
194 entity_addr: _,
195 }) => {
196 if self.get_caller() != PublicKey::System.to_account_hash()
197 && !self.is_administrator(&self.get_caller())
198 {
199 return Err(Error::DisabledUnrestrictedTransfers);
200 }
201 }
202
203 Some(Caller::SmartContract {
204 contract_package_hash: _,
205 contract_hash: _,
206 }) => {
207 if self.get_caller() != PublicKey::System.to_account_hash()
208 && !self.is_administrator(&self.get_caller())
209 {
210 return Err(Error::DisabledUnrestrictedTransfers);
211 }
212 }
213
214 None => {
215 return Err(Error::DisabledUnrestrictedTransfers);
217 }
218 }
219 }
220
221 if !source.is_writeable() || !target.is_addable() {
222 return Err(Error::InvalidAccessRights);
226 }
227 let source_available_balance: U512 = match self.available_balance(source)? {
228 Some(source_balance) => source_balance,
229 None => return Err(Error::SourceNotFound),
230 };
231 if amount > source_available_balance {
232 return Err(Error::InsufficientFunds);
234 }
235 let source_total_balance = self.total_balance(source)?;
236 if source_available_balance > source_total_balance {
237 panic!("available balance can never be greater than total balance");
238 }
239 if self.available_balance(target)?.is_none() {
240 return Err(Error::DestNotFound);
241 }
242 let addr = match self.get_main_purse() {
243 None => return Err(Error::InvalidURef),
244 Some(uref) => uref.addr(),
245 };
246 if self.get_caller() != PublicKey::System.to_account_hash() && addr == source.addr() {
247 if amount > self.get_approved_spending_limit() {
248 return Err(Error::UnapprovedSpendingAmount);
249 }
250 self.sub_approved_spending_limit(amount);
251 }
252
253 let new_balance = source_total_balance.saturating_sub(amount);
255 self.write_balance(source, new_balance)?;
256 self.add_balance(target, amount)?;
257 self.record_transfer(maybe_to, source, target, amount, id)?;
258 Ok(())
259 }
260
261 fn read_base_round_reward(&mut self) -> Result<U512, Error> {
263 let total_supply_uref = match self.get_key(TOTAL_SUPPLY_KEY) {
264 Some(Key::URef(uref)) => uref,
265 Some(_) => return Err(Error::MissingKey),
266 None => return Err(Error::MissingKey),
267 };
268 let total_supply: U512 = self
269 .read(total_supply_uref)?
270 .ok_or(Error::TotalSupplyNotFound)?;
271
272 let round_seigniorage_rate_uref = match self.get_key(ROUND_SEIGNIORAGE_RATE_KEY) {
273 Some(Key::URef(uref)) => uref,
274 Some(_) => return Err(Error::MissingKey),
275 None => return Err(Error::MissingKey),
276 };
277 let round_seigniorage_rate: Ratio<U512> = self
278 .read(round_seigniorage_rate_uref)?
279 .ok_or(Error::TotalSupplyNotFound)?;
280
281 round_seigniorage_rate
282 .checked_mul(&Ratio::from(total_supply))
283 .map(|ratio| ratio.to_integer())
284 .ok_or(Error::ArithmeticOverflow)
285 }
286
287 fn mint_into_existing_purse(
290 &mut self,
291 existing_purse: URef,
292 amount: U512,
293 ) -> Result<(), Error> {
294 let caller = self.get_caller();
295 if caller != PublicKey::System.to_account_hash() {
296 return Err(Error::InvalidContext);
297 }
298 if amount.is_zero() {
299 return Ok(());
301 }
302 if !self.purse_exists(existing_purse)? {
303 return Err(Error::PurseNotFound);
304 }
305 self.add_balance(existing_purse, amount)?;
306 let total_supply_uref = match self.get_key(TOTAL_SUPPLY_KEY) {
308 None => {
309 return Err(Error::TotalSupplyNotFound);
313 }
314 Some(Key::URef(uref)) => uref,
315 Some(_) => return Err(Error::MissingKey),
316 };
317 self.add(total_supply_uref, amount)?;
319 Ok(())
320 }
321
322 fn purse_exists(&mut self, uref: URef) -> Result<bool, Error>;
324}