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