oasis_runtime_sdk/modules/accounts/
mod.rs

1//! Accounts module.
2use std::{
3    cmp::Ordering,
4    collections::{BTreeMap, BTreeSet},
5    convert::TryInto,
6};
7
8use num_traits::Zero;
9use once_cell::sync::Lazy;
10use thiserror::Error;
11
12use crate::{
13    context::Context,
14    core::common::quantity::Quantity,
15    handler, migration,
16    module::{self, FeeProxyHandler, Module as _, Parameters as _},
17    modules::{
18        self,
19        core::{Error as CoreError, API as _},
20    },
21    runtime::Runtime,
22    sdk_derive,
23    sender::SenderMeta,
24    state::{CurrentState, Mode},
25    storage::{self, Prefix},
26    types::{
27        address::{Address, SignatureAddressSpec},
28        token,
29        transaction::{AuthInfo, Transaction},
30    },
31};
32
33pub mod fee;
34#[cfg(test)]
35pub(crate) mod test;
36pub mod types;
37
38/// Unique module name.
39const MODULE_NAME: &str = "accounts";
40
41/// Maximum delta that the transaction nonce can be in the future from the current nonce to still
42/// be accepted during transaction checks.
43const MAX_CHECK_NONCE_FUTURE_DELTA: u64 = 5;
44
45/// Errors emitted by the accounts module.
46#[derive(Error, Debug, oasis_runtime_sdk_macros::Error)]
47pub enum Error {
48    #[error("invalid argument")]
49    #[sdk_error(code = 1)]
50    InvalidArgument,
51
52    #[error("insufficient balance")]
53    #[sdk_error(code = 2)]
54    InsufficientBalance,
55
56    #[error("forbidden by policy")]
57    #[sdk_error(code = 3)]
58    Forbidden,
59
60    #[error("not found")]
61    #[sdk_error(code = 4)]
62    NotFound,
63
64    #[error("core: {0}")]
65    #[sdk_error(transparent)]
66    Core(#[from] modules::core::Error),
67}
68
69/// Events emitted by the accounts module.
70#[derive(Debug, cbor::Encode, oasis_runtime_sdk_macros::Event)]
71#[cbor(untagged)]
72pub enum Event {
73    #[sdk_event(code = 1)]
74    Transfer {
75        from: Address,
76        to: Address,
77        amount: token::BaseUnits,
78    },
79
80    #[sdk_event(code = 2)]
81    Burn {
82        owner: Address,
83        amount: token::BaseUnits,
84    },
85
86    #[sdk_event(code = 3)]
87    Mint {
88        owner: Address,
89        amount: token::BaseUnits,
90    },
91}
92
93/// Gas costs.
94#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
95pub struct GasCosts {
96    pub tx_transfer: u64,
97}
98
99/// Parameters for the accounts module.
100#[derive(Clone, Default, Debug, cbor::Encode, cbor::Decode)]
101pub struct Parameters {
102    pub transfers_disabled: bool,
103    pub gas_costs: GasCosts,
104
105    #[cbor(optional)]
106    pub debug_disable_nonce_check: bool,
107
108    #[cbor(optional)]
109    pub denomination_infos: BTreeMap<token::Denomination, types::DenominationInfo>,
110}
111
112/// Errors emitted during rewards parameter validation.
113#[derive(Error, Debug)]
114pub enum ParameterValidationError {
115    #[error("debug option used: {0}")]
116    DebugOptionUsed(String),
117}
118
119impl module::Parameters for Parameters {
120    type Error = ParameterValidationError;
121
122    #[cfg(not(feature = "unsafe-allow-debug"))]
123    fn validate_basic(&self) -> Result<(), Self::Error> {
124        if self.debug_disable_nonce_check {
125            return Err(ParameterValidationError::DebugOptionUsed(
126                "debug_disable_nonce_check".to_string(),
127            ));
128        }
129
130        Ok(())
131    }
132}
133
134/// Genesis state for the accounts module.
135#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
136pub struct Genesis {
137    pub parameters: Parameters,
138    pub accounts: BTreeMap<Address, types::Account>,
139    pub balances: BTreeMap<Address, BTreeMap<token::Denomination, u128>>,
140    pub total_supplies: BTreeMap<token::Denomination, u128>,
141}
142
143/// Interface that can be called from other modules.
144pub trait API {
145    /// Transfer an amount from one account to the other.
146    fn transfer(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error>;
147
148    /// Transfer an amount from one account to the other without emitting an event.
149    fn transfer_silent(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error>;
150
151    /// Mint new tokens, increasing the total supply.
152    fn mint(to: Address, amount: &token::BaseUnits) -> Result<(), Error>;
153
154    /// Burn existing tokens, decreasing the total supply.
155    fn burn(from: Address, amount: &token::BaseUnits) -> Result<(), Error>;
156
157    /// Sets an account's nonce.
158    fn set_nonce(address: Address, nonce: u64);
159
160    /// Fetch an account's current nonce.
161    fn get_nonce(address: Address) -> Result<u64, Error>;
162
163    /// Increments an account's nonce.
164    fn inc_nonce(address: Address);
165
166    /// Sets an account's balance of the given denomination.
167    ///
168    /// # Warning
169    ///
170    /// This method is dangerous as it can result in invariant violations.
171    fn set_balance(address: Address, amount: &token::BaseUnits);
172
173    /// Fetch an account's balance of the given denomination.
174    fn get_balance(address: Address, denomination: token::Denomination) -> Result<u128, Error>;
175
176    /// Ensures that the given account has at least the specified balance.
177    fn ensure_balance(address: Address, amount: &token::BaseUnits) -> Result<(), Error> {
178        let balance = Self::get_balance(address, amount.denomination().clone())?;
179        if balance < amount.amount() {
180            Err(Error::InsufficientBalance)
181        } else {
182            Ok(())
183        }
184    }
185
186    /// Get allowance for an address and denomination.
187    ///
188    /// The allowance is the amount an account owner allows another account to
189    /// spend from the owner's account for a given denomination.
190    ///
191    /// Note that the API user is responsible for taking allowances into
192    /// account, the transfer functions in this API do not.
193    fn get_allowance(
194        owner: Address,
195        beneficiary: Address,
196        denomination: token::Denomination,
197    ) -> Result<u128, Error>;
198
199    /// Set a user's allowance for spending tokens for the given denomination
200    /// from the owner's account.
201    fn set_allowance(owner: Address, beneficiary: Address, amount: &token::BaseUnits);
202
203    /// Fetch an account's current balances.
204    fn get_balances(address: Address) -> Result<types::AccountBalances, Error>;
205
206    /// Fetch addresses.
207    fn get_addresses(denomination: token::Denomination) -> Result<Vec<Address>, Error>;
208
209    /// Fetch total supplies.
210    fn get_total_supplies() -> Result<BTreeMap<token::Denomination, u128>, Error>;
211
212    /// Fetch the total supply for the given denomination.
213    fn get_total_supply(denomination: token::Denomination) -> Result<u128, Error>;
214
215    /// Sets the total supply for the given denomination.
216    ///
217    /// # Warning
218    ///
219    /// This method is dangerous as it can result in invariant violations.
220    fn set_total_supply(amount: &token::BaseUnits);
221
222    /// Fetch information about a denomination.
223    fn get_denomination_info(
224        denomination: &token::Denomination,
225    ) -> Result<types::DenominationInfo, Error>;
226
227    /// Moves the amount into the per-transaction fee accumulator.
228    fn charge_tx_fee(from: Address, amount: &token::BaseUnits) -> Result<(), modules::core::Error>;
229
230    /// Indicates that the unused portion of the transaction fee should be refunded after the
231    /// transaction completes (even in case it fails).
232    fn set_refund_unused_tx_fee(refund: bool);
233
234    /// Take the flag indicating that the unused portion of the transaction fee should be refunded
235    /// after the transaction completes is set.
236    ///
237    /// After calling this method the flag is reset to `false`.
238    fn take_refund_unused_tx_fee() -> bool;
239
240    /// Check transaction signer account nonces.
241    /// Return payer address.
242    fn check_signer_nonces<C: Context>(
243        ctx: &C,
244        tx_auth_info: &AuthInfo,
245    ) -> Result<Address, modules::core::Error>;
246
247    /// Update transaction signer account nonces.
248    fn update_signer_nonces<C: Context>(
249        ctx: &C,
250        tx_auth_info: &AuthInfo,
251    ) -> Result<(), modules::core::Error>;
252}
253
254/// State schema constants.
255pub mod state {
256    /// Map of account addresses to account metadata.
257    pub const ACCOUNTS: &[u8] = &[0x01];
258    /// Map of account addresses to map of denominations to balances.
259    pub const BALANCES: &[u8] = &[0x02];
260    /// Map of total supplies (per denomination).
261    pub const TOTAL_SUPPLY: &[u8] = &[0x03];
262    /// Map of allowances (per denomination).
263    pub const ALLOWANCES: &[u8] = &[0x04];
264}
265
266pub struct Module;
267
268/// Module's address that has the common pool.
269///
270/// oasis1qz78phkdan64g040cvqvqpwkplfqf6tj6uwcsh30
271pub static ADDRESS_COMMON_POOL: Lazy<Address> =
272    Lazy::new(|| Address::from_module(MODULE_NAME, "common-pool"));
273/// Module's address that has the fee accumulator.
274///
275/// oasis1qp3r8hgsnphajmfzfuaa8fhjag7e0yt35cjxq0u4
276pub static ADDRESS_FEE_ACCUMULATOR: Lazy<Address> =
277    Lazy::new(|| Address::from_module(MODULE_NAME, "fee-accumulator"));
278
279/// This is needed to properly iterate over the BALANCES map.
280#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
281struct AddressWithDenomination(Address, token::Denomination);
282
283#[derive(Error, Debug)]
284enum AWDError {
285    #[error("malformed address")]
286    MalformedAddress,
287
288    #[error("malformed denomination")]
289    MalformedDenomination,
290}
291
292impl std::convert::TryFrom<&[u8]> for AddressWithDenomination {
293    type Error = AWDError;
294
295    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
296        let address =
297            Address::try_from(&bytes[..Address::SIZE]).map_err(|_| AWDError::MalformedAddress)?;
298        let denomination = token::Denomination::try_from(&bytes[Address::SIZE..])
299            .map_err(|_| AWDError::MalformedDenomination)?;
300        Ok(AddressWithDenomination(address, denomination))
301    }
302}
303
304impl Module {
305    /// Add given amount of tokens to the specified account's balance.
306    fn add_amount(addr: Address, amount: &token::BaseUnits) -> Result<(), Error> {
307        if amount.amount() == 0 {
308            return Ok(());
309        }
310
311        CurrentState::with_store(|store| {
312            let store = storage::PrefixStore::new(store, &MODULE_NAME);
313            let balances = storage::PrefixStore::new(store, &state::BALANCES);
314            let mut account = storage::TypedStore::new(storage::PrefixStore::new(balances, &addr));
315            let mut value: u128 = account.get(amount.denomination()).unwrap_or_default();
316
317            value = value
318                .checked_add(amount.amount())
319                .ok_or(Error::InvalidArgument)?;
320            account.insert(amount.denomination(), value);
321            Ok(())
322        })
323    }
324
325    /// Subtract given amount of tokens from the specified account's balance.
326    fn sub_amount(addr: Address, amount: &token::BaseUnits) -> Result<(), Error> {
327        if amount.amount() == 0 {
328            return Ok(());
329        }
330
331        CurrentState::with_store(|store| {
332            let store = storage::PrefixStore::new(store, &MODULE_NAME);
333            let balances = storage::PrefixStore::new(store, &state::BALANCES);
334            let mut account = storage::TypedStore::new(storage::PrefixStore::new(balances, &addr));
335            let mut value: u128 = account.get(amount.denomination()).unwrap_or_default();
336
337            value = value
338                .checked_sub(amount.amount())
339                .ok_or(Error::InsufficientBalance)?;
340            account.insert(amount.denomination(), value);
341            Ok(())
342        })
343    }
344
345    /// Increment the total supply for the given amount.
346    fn inc_total_supply(amount: &token::BaseUnits) -> Result<(), Error> {
347        if amount.amount() == 0 {
348            return Ok(());
349        }
350
351        CurrentState::with_store(|store| {
352            let store = storage::PrefixStore::new(store, &MODULE_NAME);
353            let mut total_supplies =
354                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
355            let mut total_supply: u128 = total_supplies
356                .get(amount.denomination())
357                .unwrap_or_default();
358
359            total_supply = total_supply
360                .checked_add(amount.amount())
361                .ok_or(Error::InvalidArgument)?;
362            total_supplies.insert(amount.denomination(), total_supply);
363            Ok(())
364        })
365    }
366
367    /// Decrement the total supply for the given amount.
368    fn dec_total_supply(amount: &token::BaseUnits) -> Result<(), Error> {
369        if amount.amount() == 0 {
370            return Ok(());
371        }
372
373        CurrentState::with_store(|store| {
374            let store = storage::PrefixStore::new(store, &MODULE_NAME);
375            let mut total_supplies =
376                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
377            let mut total_supply: u128 = total_supplies
378                .get(amount.denomination())
379                .unwrap_or_default();
380
381            total_supply = total_supply
382                .checked_sub(amount.amount())
383                .ok_or(Error::InsufficientBalance)?;
384            total_supplies.insert(amount.denomination(), total_supply);
385            Ok(())
386        })
387    }
388
389    /// Get all balances.
390    fn get_all_balances() -> Result<BTreeMap<Address, BTreeMap<token::Denomination, u128>>, Error> {
391        CurrentState::with_store(|store| {
392            let store = storage::PrefixStore::new(store, &MODULE_NAME);
393            let balances =
394                storage::TypedStore::new(storage::PrefixStore::new(store, &state::BALANCES));
395
396            // Unfortunately, we can't just return balances.iter().collect() here,
397            // because the stored format doesn't match -- we need this workaround
398            // instead.
399
400            let balmap: BTreeMap<AddressWithDenomination, u128> = balances.iter().collect();
401
402            let mut b: BTreeMap<Address, BTreeMap<token::Denomination, u128>> = BTreeMap::new();
403
404            for (addrden, amt) in &balmap {
405                let addr = &addrden.0;
406                let den = &addrden.1;
407
408                // Fetch existing account's balances or insert blank ones.
409                let addr_bals = b.entry(*addr).or_default();
410
411                // Add to given denomination's balance or insert it if new.
412                addr_bals
413                    .entry(den.clone())
414                    .and_modify(|a| *a += amt)
415                    .or_insert_with(|| *amt);
416            }
417
418            Ok(b)
419        })
420    }
421}
422
423/// Context key for the per-transaction unused fee refund decision.
424const CONTEXT_KEY_TX_FEE_REFUND_UNUSED: &str = "accounts.TxRefundUnusedFee";
425/// Context key for the per block fee manager.
426const CONTEXT_KEY_FEE_MANAGER: &str = "accounts.FeeManager";
427
428impl API for Module {
429    fn transfer(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error> {
430        if CurrentState::with_env(|env| env.is_check_only()) || amount.amount() == 0 {
431            return Ok(());
432        }
433
434        Self::transfer_silent(from, to, amount)?;
435
436        // Emit a transfer event.
437        CurrentState::with(|state| {
438            state.emit_event(Event::Transfer {
439                from,
440                to,
441                amount: amount.clone(),
442            })
443        });
444
445        Ok(())
446    }
447
448    fn transfer_silent(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error> {
449        // Subtract from source account.
450        Self::sub_amount(from, amount)?;
451        // Add to destination account.
452        Self::add_amount(to, amount)?;
453
454        Ok(())
455    }
456
457    fn mint(to: Address, amount: &token::BaseUnits) -> Result<(), Error> {
458        if CurrentState::with_env(|env| env.is_check_only()) || amount.amount() == 0 {
459            return Ok(());
460        }
461
462        // Add to destination account.
463        Self::add_amount(to, amount)?;
464
465        // Increase total supply.
466        Self::inc_total_supply(amount)?;
467
468        // Emit a mint event.
469        CurrentState::with(|state| {
470            state.emit_event(Event::Mint {
471                owner: to,
472                amount: amount.clone(),
473            });
474        });
475
476        Ok(())
477    }
478
479    fn burn(from: Address, amount: &token::BaseUnits) -> Result<(), Error> {
480        if CurrentState::with_env(|env| env.is_check_only()) || amount.amount() == 0 {
481            return Ok(());
482        }
483
484        // Remove from target account.
485        Self::sub_amount(from, amount)?;
486
487        // Decrease total supply.
488        Self::dec_total_supply(amount)
489            .expect("target account had enough balance so total supply should not underflow");
490
491        // Emit a burn event.
492        CurrentState::with(|state| {
493            state.emit_event(Event::Burn {
494                owner: from,
495                amount: amount.clone(),
496            });
497        });
498
499        Ok(())
500    }
501
502    fn set_nonce(address: Address, nonce: u64) {
503        CurrentState::with_store(|store| {
504            let store = storage::PrefixStore::new(store, &MODULE_NAME);
505            let mut accounts =
506                storage::TypedStore::new(storage::PrefixStore::new(store, &state::ACCOUNTS));
507            let mut account: types::Account = accounts.get(address).unwrap_or_default();
508            account.nonce = nonce;
509            accounts.insert(address, account);
510        })
511    }
512
513    fn get_nonce(address: Address) -> Result<u64, Error> {
514        CurrentState::with_store(|store| {
515            let store = storage::PrefixStore::new(store, &MODULE_NAME);
516            let accounts =
517                storage::TypedStore::new(storage::PrefixStore::new(store, &state::ACCOUNTS));
518            let account: types::Account = accounts.get(address).unwrap_or_default();
519            Ok(account.nonce)
520        })
521    }
522
523    fn inc_nonce(address: Address) {
524        CurrentState::with_store(|store| {
525            let store = storage::PrefixStore::new(store, &MODULE_NAME);
526            let mut accounts =
527                storage::TypedStore::new(storage::PrefixStore::new(store, &state::ACCOUNTS));
528            let mut account: types::Account = accounts.get(address).unwrap_or_default();
529            account.nonce = account.nonce.saturating_add(1);
530            accounts.insert(address, account);
531        })
532    }
533
534    fn set_balance(address: Address, amount: &token::BaseUnits) {
535        CurrentState::with_store(|store| {
536            let store = storage::PrefixStore::new(store, &MODULE_NAME);
537            let balances = storage::PrefixStore::new(store, &state::BALANCES);
538            let mut account =
539                storage::TypedStore::new(storage::PrefixStore::new(balances, &address));
540            account.insert(amount.denomination(), amount.amount());
541        });
542    }
543
544    fn get_balance(address: Address, denomination: token::Denomination) -> Result<u128, Error> {
545        CurrentState::with_store(|store| {
546            let store = storage::PrefixStore::new(store, &MODULE_NAME);
547            let balances = storage::PrefixStore::new(store, &state::BALANCES);
548            let account = storage::TypedStore::new(storage::PrefixStore::new(balances, &address));
549
550            Ok(account.get(denomination).unwrap_or_default())
551        })
552    }
553
554    fn get_balances(address: Address) -> Result<types::AccountBalances, Error> {
555        CurrentState::with_store(|store| {
556            let store = storage::PrefixStore::new(store, &MODULE_NAME);
557            let balances = storage::PrefixStore::new(store, &state::BALANCES);
558            let account = storage::TypedStore::new(storage::PrefixStore::new(balances, &address));
559
560            Ok(types::AccountBalances {
561                balances: account.iter().collect(),
562            })
563        })
564    }
565
566    fn get_allowance(
567        owner: Address,
568        beneficiary: Address,
569        denomination: token::Denomination,
570    ) -> Result<u128, Error> {
571        CurrentState::with_store(|store| {
572            let store = storage::PrefixStore::new(store, &MODULE_NAME);
573            let allowances = storage::PrefixStore::new(store, &state::ALLOWANCES);
574            let for_owner = storage::PrefixStore::new(allowances, &owner);
575            let for_beneficiary =
576                storage::TypedStore::new(storage::PrefixStore::new(for_owner, &beneficiary));
577
578            Ok(for_beneficiary.get(denomination).unwrap_or_default())
579        })
580    }
581
582    fn set_allowance(owner: Address, beneficiary: Address, amount: &token::BaseUnits) {
583        CurrentState::with_store(|store| {
584            let store = storage::PrefixStore::new(store, &MODULE_NAME);
585            let allowances = storage::PrefixStore::new(store, &state::ALLOWANCES);
586            let for_owner = storage::PrefixStore::new(allowances, &owner);
587            let mut for_beneficiary =
588                storage::TypedStore::new(storage::PrefixStore::new(for_owner, &beneficiary));
589
590            for_beneficiary.insert(amount.denomination(), amount.amount());
591        })
592    }
593
594    fn get_addresses(denomination: token::Denomination) -> Result<Vec<Address>, Error> {
595        CurrentState::with_store(|store| {
596            let store = storage::PrefixStore::new(store, &MODULE_NAME);
597            let balances: BTreeMap<AddressWithDenomination, Quantity> =
598                storage::TypedStore::new(storage::PrefixStore::new(store, &state::BALANCES))
599                    .iter()
600                    .collect();
601
602            Ok(balances
603                .into_keys()
604                .filter(|bal| bal.1 == denomination)
605                .map(|bal| bal.0)
606                .collect())
607        })
608    }
609
610    fn get_total_supplies() -> Result<BTreeMap<token::Denomination, u128>, Error> {
611        CurrentState::with_store(|store| {
612            let store = storage::PrefixStore::new(store, &MODULE_NAME);
613            let ts =
614                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
615
616            Ok(ts.iter().collect())
617        })
618    }
619
620    fn get_total_supply(denomination: token::Denomination) -> Result<u128, Error> {
621        CurrentState::with_store(|store| {
622            let store = storage::PrefixStore::new(store, &MODULE_NAME);
623            let ts =
624                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
625            Ok(ts.get(denomination).unwrap_or_default())
626        })
627    }
628
629    fn set_total_supply(amount: &token::BaseUnits) {
630        CurrentState::with_store(|store| {
631            let store = storage::PrefixStore::new(store, &MODULE_NAME);
632            let mut total_supplies =
633                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
634            total_supplies.insert(amount.denomination(), amount.amount());
635        });
636    }
637
638    fn get_denomination_info(
639        denomination: &token::Denomination,
640    ) -> Result<types::DenominationInfo, Error> {
641        Self::params()
642            .denomination_infos
643            .get(denomination)
644            .cloned()
645            .ok_or(Error::NotFound)
646    }
647
648    fn charge_tx_fee(from: Address, amount: &token::BaseUnits) -> Result<(), modules::core::Error> {
649        if CurrentState::with_env(|env| env.is_simulation()) {
650            return Ok(());
651        }
652
653        Self::sub_amount(from, amount).map_err(|_| modules::core::Error::InsufficientFeeBalance)?;
654
655        CurrentState::with(|state| {
656            state
657                .block_value::<fee::FeeManager>(CONTEXT_KEY_FEE_MANAGER)
658                .or_default()
659                .record_fee(from, amount);
660        });
661
662        Ok(())
663    }
664
665    fn set_refund_unused_tx_fee(refund: bool) {
666        CurrentState::with(|state| {
667            if state.env().is_simulation() {
668                return;
669            }
670
671            state
672                .block_value(CONTEXT_KEY_TX_FEE_REFUND_UNUSED)
673                .set(refund);
674        });
675    }
676
677    fn take_refund_unused_tx_fee() -> bool {
678        CurrentState::with(|state| {
679            if state.env().is_simulation() {
680                return false;
681            }
682
683            state
684                .block_value(CONTEXT_KEY_TX_FEE_REFUND_UNUSED)
685                .take()
686                .unwrap_or(false)
687        })
688    }
689
690    fn check_signer_nonces<C: Context>(
691        _ctx: &C,
692        auth_info: &AuthInfo,
693    ) -> Result<Address, modules::core::Error> {
694        let mode = CurrentState::with_env(|env| env.mode());
695
696        // TODO: Optimize the check/update pair so that the accounts are
697        // fetched only once.
698        let params = Self::params();
699        let sender = CurrentState::with_store(|store| {
700            // Fetch information about each signer.
701            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
702            let accounts =
703                storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::ACCOUNTS));
704            let mut sender = None;
705            for si in auth_info.signer_info.iter() {
706                let address = si.address_spec.address();
707                let account: types::Account = accounts.get(address).unwrap_or_default();
708
709                // First signer pays for the fees and is considered the sender.
710                if sender.is_none() {
711                    sender = Some(SenderMeta {
712                        address,
713                        tx_nonce: si.nonce,
714                        state_nonce: account.nonce,
715                    });
716                }
717
718                // When nonce checking is disabled, skip the rest of the checks.
719                if params.debug_disable_nonce_check {
720                    continue;
721                }
722
723                // Check signer nonce against the corresponding account nonce.
724                match si.nonce.cmp(&account.nonce) {
725                    Ordering::Less => {
726                        // In the past and will never become valid, reject.
727                        return Err(modules::core::Error::InvalidNonce);
728                    }
729                    Ordering::Equal => {} // Ok.
730                    Ordering::Greater => {
731                        // In the future.
732                        match mode {
733                            Mode::Check => {}
734                            Mode::PreSchedule => {
735                                // Reject with a separate error that will make
736                                // the scheduler skip the transaction.
737                                return Err(modules::core::Error::FutureNonce);
738                            }
739                            _ => {
740                                return Err(modules::core::Error::InvalidNonce);
741                            }
742                        }
743
744                        // If too much in the future, reject.
745                        if si.nonce - account.nonce > MAX_CHECK_NONCE_FUTURE_DELTA {
746                            return Err(modules::core::Error::InvalidNonce);
747                        }
748                    }
749                }
750            }
751
752            Ok(sender)
753        })?;
754
755        // Configure the sender.
756        let sender = sender.expect("at least one signer is always present");
757        let sender_address = sender.address;
758        if mode == Mode::Check {
759            <C::Runtime as Runtime>::Core::set_sender_meta(sender);
760        }
761
762        Ok(sender_address)
763    }
764
765    fn update_signer_nonces<C: Context>(
766        _ctx: &C,
767        auth_info: &AuthInfo,
768    ) -> Result<(), modules::core::Error> {
769        CurrentState::with_store(|store| {
770            // Fetch information about each signer.
771            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
772            let mut accounts =
773                storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::ACCOUNTS));
774            for si in auth_info.signer_info.iter() {
775                let address = si.address_spec.address();
776                let mut account: types::Account = accounts.get(address).unwrap_or_default();
777
778                // Update nonce.
779                account.nonce = account
780                    .nonce
781                    .checked_add(1)
782                    .ok_or(modules::core::Error::InvalidNonce)?; // Should never overflow.
783                accounts.insert(address, account);
784            }
785            Ok(())
786        })
787    }
788}
789
790#[sdk_derive(Module)]
791impl Module {
792    const NAME: &'static str = MODULE_NAME;
793    type Error = Error;
794    type Event = Event;
795    type Parameters = Parameters;
796    type Genesis = Genesis;
797
798    #[migration(init)]
799    pub fn init(genesis: Genesis) {
800        CurrentState::with_store(|store| {
801            // Create accounts.
802            let mut store = storage::PrefixStore::new(store, &MODULE_NAME);
803            let mut accounts =
804                storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::ACCOUNTS));
805            for (address, account) in genesis.accounts {
806                accounts.insert(address, account);
807            }
808
809            // Create balances.
810            let mut balances = storage::PrefixStore::new(&mut store, &state::BALANCES);
811            let mut computed_total_supply: BTreeMap<token::Denomination, u128> = BTreeMap::new();
812            for (address, denominations) in genesis.balances.iter() {
813                let mut account =
814                    storage::TypedStore::new(storage::PrefixStore::new(&mut balances, &address));
815                for (denomination, value) in denominations {
816                    account.insert(denomination, value);
817
818                    // Update computed total supply.
819                    computed_total_supply
820                        .entry(denomination.clone())
821                        .and_modify(|v| *v += value)
822                        .or_insert_with(|| *value);
823                }
824            }
825
826            // Validate and set total supply.
827            let mut total_supplies = storage::TypedStore::new(storage::PrefixStore::new(
828                &mut store,
829                &state::TOTAL_SUPPLY,
830            ));
831            for (denomination, total_supply) in genesis.total_supplies.iter() {
832                let computed = computed_total_supply
833                    .remove(denomination)
834                    .expect("unexpected total supply");
835                assert!(
836                    &computed == total_supply,
837                    "unexpected total supply (expected: {total_supply} got: {computed})",
838                );
839
840                total_supplies.insert(denomination, total_supply);
841            }
842            if let Some((denomination, total_supply)) = computed_total_supply.iter().next() {
843                panic!("missing expected total supply: {total_supply} {denomination}",);
844            }
845        });
846
847        // Validate genesis parameters.
848        genesis
849            .parameters
850            .validate_basic()
851            .expect("invalid genesis parameters");
852
853        // Set genesis parameters.
854        Self::set_params(genesis.parameters);
855    }
856
857    #[handler(prefetch = "accounts.Transfer")]
858    fn prefetch_transfer(
859        add_prefix: &mut dyn FnMut(Prefix),
860        body: cbor::Value,
861        auth_info: &AuthInfo,
862    ) -> Result<(), crate::error::RuntimeError> {
863        let args: types::Transfer = cbor::from_value(body).map_err(|_| Error::InvalidArgument)?;
864        let from = auth_info.signer_info[0].address_spec.address();
865
866        // Prefetch accounts 'to'.
867        add_prefix(Prefix::from(
868            [MODULE_NAME.as_bytes(), state::ACCOUNTS, args.to.as_ref()].concat(),
869        ));
870        add_prefix(Prefix::from(
871            [MODULE_NAME.as_bytes(), state::BALANCES, args.to.as_ref()].concat(),
872        ));
873        // Prefetch accounts 'from'.
874        add_prefix(Prefix::from(
875            [MODULE_NAME.as_bytes(), state::ACCOUNTS, from.as_ref()].concat(),
876        ));
877        add_prefix(Prefix::from(
878            [MODULE_NAME.as_bytes(), state::BALANCES, from.as_ref()].concat(),
879        ));
880
881        Ok(())
882    }
883
884    #[handler(call = "accounts.Transfer")]
885    fn tx_transfer<C: Context>(_ctx: &C, body: types::Transfer) -> Result<(), Error> {
886        let params = Self::params();
887
888        // Reject transfers when they are disabled.
889        if params.transfers_disabled {
890            return Err(Error::Forbidden);
891        }
892
893        <C::Runtime as Runtime>::Core::use_tx_gas(params.gas_costs.tx_transfer)?;
894
895        let tx_caller_address = CurrentState::with_env(|env| env.tx_caller_address());
896        Self::transfer(tx_caller_address, body.to, &body.amount)?;
897
898        Ok(())
899    }
900
901    #[handler(query = "accounts.Nonce")]
902    fn query_nonce<C: Context>(_ctx: &C, args: types::NonceQuery) -> Result<u64, Error> {
903        Self::get_nonce(args.address)
904    }
905
906    #[handler(query = "accounts.Addresses", expensive)]
907    fn query_addresses<C: Context>(
908        _ctx: &C,
909        args: types::AddressesQuery,
910    ) -> Result<Vec<Address>, Error> {
911        Self::get_addresses(args.denomination)
912    }
913
914    #[handler(query = "accounts.Balances")]
915    fn query_balances<C: Context>(
916        _ctx: &C,
917        args: types::BalancesQuery,
918    ) -> Result<types::AccountBalances, Error> {
919        Self::get_balances(args.address)
920    }
921
922    #[handler(query = "accounts.DenominationInfo")]
923    fn query_denomination_info<C: Context>(
924        _ctx: &C,
925        args: types::DenominationInfoQuery,
926    ) -> Result<types::DenominationInfo, Error> {
927        Self::get_denomination_info(&args.denomination)
928    }
929}
930
931impl module::TransactionHandler for Module {
932    fn authenticate_tx<C: Context>(
933        ctx: &C,
934        tx: &Transaction,
935    ) -> Result<module::AuthDecision, modules::core::Error> {
936        // Check nonces.
937        let default_payer = Self::check_signer_nonces(ctx, &tx.auth_info)?;
938
939        // Attempt to resolve a proxy fee payer if set.
940        let payer =
941            <C::Runtime as Runtime>::FeeProxy::resolve_payer(ctx, tx)?.unwrap_or(default_payer);
942
943        // Charge the specified amount of fees.
944        if !tx.auth_info.fee.amount.amount().is_zero() {
945            if CurrentState::with_env(|env| env.is_check_only()) {
946                // Do not update balances during transaction checks. In case of checks, only do it
947                // after all the other checks have already passed as otherwise retrying the
948                // transaction will not be possible.
949                Self::ensure_balance(payer, &tx.auth_info.fee.amount)
950                    .map_err(|_| modules::core::Error::InsufficientFeeBalance)?;
951
952                // Make sure to record the payer during transaction checks.
953                CurrentState::with(|state| {
954                    state
955                        .block_value::<fee::FeeManager>(CONTEXT_KEY_FEE_MANAGER)
956                        .or_default()
957                        .record_fee(payer, &tx.auth_info.fee.amount);
958                });
959            } else {
960                // Actually perform the move.
961                Self::charge_tx_fee(payer, &tx.auth_info.fee.amount)?;
962            }
963
964            let gas_price = tx.auth_info.fee.gas_price();
965            // Set transaction priority.
966            <C::Runtime as Runtime>::Core::set_priority(gas_price.try_into().unwrap_or(u64::MAX));
967        }
968
969        // Do not update nonces early during transaction checks. In case of checks, only do it after
970        // all the other checks have already passed as otherwise retrying the transaction will not
971        // be possible.
972        if !CurrentState::with_env(|env| env.is_check_only()) {
973            Self::update_signer_nonces(ctx, &tx.auth_info)?;
974        }
975
976        Ok(module::AuthDecision::Continue)
977    }
978
979    fn after_handle_call<C: Context>(
980        _ctx: &C,
981        result: module::CallResult,
982    ) -> Result<module::CallResult, modules::core::Error> {
983        // Check whether unused part of the fee should be refunded.
984        let refund_fee = if Self::take_refund_unused_tx_fee() {
985            let remaining_gas = <C::Runtime as Runtime>::Core::remaining_tx_gas();
986            let gas_price = CurrentState::with_env(|env| env.tx_auth_info().fee.gas_price());
987
988            gas_price.saturating_mul(remaining_gas.into())
989        } else {
990            0
991        };
992
993        CurrentState::with(|state| {
994            let mgr = state
995                .block_value::<fee::FeeManager>(CONTEXT_KEY_FEE_MANAGER)
996                .or_default();
997
998            // Update the per-tx fee accumulator. State must be updated in `after_dispatch_tx` as
999            // otherwise any state updates may be reverted in case call result is a failure.
1000            mgr.record_refund(refund_fee);
1001
1002            // Emit event for paid fee.
1003            let tx_fee = mgr.tx_fee().cloned().unwrap_or_default();
1004            if tx_fee.amount() > 0 {
1005                state.emit_unconditional_event(Event::Transfer {
1006                    from: tx_fee.payer(),
1007                    to: *ADDRESS_FEE_ACCUMULATOR,
1008                    amount: token::BaseUnits::new(tx_fee.amount(), tx_fee.denomination()),
1009                });
1010            }
1011        });
1012
1013        Ok(result)
1014    }
1015
1016    fn after_dispatch_tx<C: Context>(
1017        ctx: &C,
1018        tx_auth_info: &AuthInfo,
1019        result: &module::CallResult,
1020    ) {
1021        // Move transaction fees into the per-block fee accumulator.
1022        let fee_updates = CurrentState::with(|state| {
1023            let mgr = state
1024                .block_value::<fee::FeeManager>(CONTEXT_KEY_FEE_MANAGER)
1025                .or_default();
1026            mgr.commit_tx()
1027        });
1028        // Refund any fees. This needs to happen after tx dispatch to ensure state is updated.
1029        Self::add_amount(fee_updates.payer, &fee_updates.refund).unwrap();
1030
1031        if !CurrentState::with_env(|env| env.is_check_only()) {
1032            // Do nothing further outside transaction checks.
1033            return;
1034        }
1035        if !matches!(result, module::CallResult::Ok(_)) {
1036            // Do nothing in case the call failed to allow retries.
1037            return;
1038        }
1039
1040        // Update payer balance.
1041        Self::sub_amount(fee_updates.payer, &tx_auth_info.fee.amount).unwrap(); // Already checked.
1042
1043        // Update nonces.
1044        Self::update_signer_nonces(ctx, tx_auth_info).unwrap();
1045    }
1046}
1047
1048impl module::BlockHandler for Module {
1049    fn end_block<C: Context>(ctx: &C) {
1050        // Determine the fees that are available for disbursement from the last block.
1051        let mut previous_fees = Self::get_balances(*ADDRESS_FEE_ACCUMULATOR)
1052            .expect("get_balances must succeed")
1053            .balances;
1054
1055        // Drain previous fees from the fee accumulator.
1056        for (denom, remainder) in &previous_fees {
1057            Self::sub_amount(
1058                *ADDRESS_FEE_ACCUMULATOR,
1059                &token::BaseUnits::new(*remainder, denom.clone()),
1060            )
1061            .expect("sub_amount must succeed");
1062        }
1063
1064        // Disburse transaction fees to entities controlling all the good nodes in the committee.
1065        let addrs: Vec<Address> = ctx
1066            .runtime_round_results()
1067            .good_compute_entities
1068            .iter()
1069            .map(|pk| Address::from_sigspec(&SignatureAddressSpec::Ed25519(pk.into())))
1070            .collect();
1071
1072        if !addrs.is_empty() {
1073            let amounts: Vec<_> = previous_fees
1074                .iter()
1075                .filter_map(|(denom, fee)| {
1076                    let fee = fee
1077                        .checked_div(addrs.len() as u128)
1078                        .expect("addrs is non-empty");
1079
1080                    // Filter out zero-fee entries to avoid needless operations.
1081                    if fee.is_zero() {
1082                        None
1083                    } else {
1084                        Some(token::BaseUnits::new(fee, denom.clone()))
1085                    }
1086                })
1087                .collect();
1088
1089            for address in addrs {
1090                for amount in &amounts {
1091                    let remaining = previous_fees
1092                        .get_mut(amount.denomination())
1093                        .expect("designated denomination should be there");
1094                    *remaining = remaining
1095                        .checked_sub(amount.amount())
1096                        .expect("there should be enough to disburse");
1097
1098                    Self::add_amount(address, amount)
1099                        .expect("add_amount must succeed for fee disbursement");
1100
1101                    // Emit transfer event for fee disbursement.
1102                    CurrentState::with(|state| {
1103                        state.emit_event(Event::Transfer {
1104                            from: *ADDRESS_FEE_ACCUMULATOR,
1105                            to: address,
1106                            amount: amount.clone(),
1107                        });
1108                    });
1109                }
1110            }
1111        }
1112
1113        // Transfer remainder to a common pool account.
1114        for (denom, remainder) in previous_fees.into_iter() {
1115            if remainder.is_zero() {
1116                continue;
1117            }
1118
1119            let amount = token::BaseUnits::new(remainder, denom);
1120            Self::add_amount(*ADDRESS_COMMON_POOL, &amount)
1121                .expect("add_amount must succeed for transfer to common pool");
1122
1123            // Emit transfer event for fee disbursement.
1124            CurrentState::with(|state| {
1125                state.emit_event(Event::Transfer {
1126                    from: *ADDRESS_FEE_ACCUMULATOR,
1127                    to: *ADDRESS_COMMON_POOL,
1128                    amount,
1129                })
1130            });
1131        }
1132
1133        // Fees for the active block should be transferred to the fee accumulator address.
1134        let block_fees = CurrentState::with(|state| {
1135            let mgr = state
1136                .block_value::<fee::FeeManager>(CONTEXT_KEY_FEE_MANAGER)
1137                .take()
1138                .unwrap_or_default();
1139            mgr.commit_block().into_iter()
1140        });
1141
1142        for (denom, amount) in block_fees {
1143            Self::add_amount(
1144                *ADDRESS_FEE_ACCUMULATOR,
1145                &token::BaseUnits::new(amount, denom),
1146            )
1147            .expect("add_amount must succeed for transfer to fee accumulator")
1148        }
1149    }
1150}
1151
1152impl module::InvariantHandler for Module {
1153    /// Check invariants.
1154    fn check_invariants<C: Context>(_ctx: &C) -> Result<(), CoreError> {
1155        // All account balances should sum up to the total supply for their
1156        // corresponding denominations.
1157
1158        #[allow(clippy::or_fun_call)]
1159        let balances = Self::get_all_balances().or(Err(CoreError::InvariantViolation(
1160            "unable to get balances of all accounts".to_string(),
1161        )))?;
1162        #[allow(clippy::or_fun_call)]
1163        let total_supplies = Self::get_total_supplies().or(Err(CoreError::InvariantViolation(
1164            "unable to get total supplies".to_string(),
1165        )))?;
1166
1167        // First, compute total supplies based on account balances.
1168        let mut computed_ts: BTreeMap<token::Denomination, u128> = BTreeMap::new();
1169
1170        for bals in balances.values() {
1171            for (den, amt) in bals {
1172                computed_ts
1173                    .entry(den.clone())
1174                    .and_modify(|a| *a += amt)
1175                    .or_insert_with(|| *amt);
1176            }
1177        }
1178
1179        // Now check if the computed and given total supplies match.
1180        for (den, ts) in &total_supplies {
1181            // Return error if total supplies have a denomination that we
1182            // didn't encounter when computing total supplies based on account
1183            // balances.
1184            #[allow(clippy::or_fun_call)]
1185            let computed = computed_ts
1186                .remove(den)
1187                .ok_or(CoreError::InvariantViolation(
1188                    "unexpected denomination".to_string(),
1189                ))?;
1190
1191            if &computed != ts {
1192                // Computed and actual total supplies don't match.
1193                return Err(CoreError::InvariantViolation(format!(
1194                    "computed and actual total supplies don't match (computed={computed}, actual={ts})",
1195                )));
1196            }
1197        }
1198
1199        // There should be no remaining denominations in the computed supplies,
1200        // because that would mean that accounts have denominations that don't
1201        // appear in the total supplies table, which would obviously be wrong.
1202        if computed_ts.is_empty() {
1203            Ok(())
1204        } else {
1205            Err(CoreError::InvariantViolation(
1206                "encountered denomination that isn't present in total supplies table".to_string(),
1207            ))
1208        }
1209    }
1210}