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    modules::core::{Error as CoreError, API as _},
19    runtime::Runtime,
20    sdk_derive,
21    sender::SenderMeta,
22    state::CurrentState,
23    storage,
24    storage::Prefix,
25    types::{
26        address::{Address, SignatureAddressSpec},
27        token,
28        transaction::{AuthInfo, Transaction},
29    },
30};
31
32pub mod fee;
33#[cfg(test)]
34pub(crate) mod test;
35pub mod types;
36
37/// Unique module name.
38const MODULE_NAME: &str = "accounts";
39
40/// Maximum delta that the transaction nonce can be in the future from the current nonce to still
41/// be accepted during transaction checks.
42const MAX_CHECK_NONCE_FUTURE_DELTA: u64 = 0; // Increase once supported in Oasis Core.
43
44/// Errors emitted by the accounts module.
45#[derive(Error, Debug, oasis_runtime_sdk_macros::Error)]
46pub enum Error {
47    #[error("invalid argument")]
48    #[sdk_error(code = 1)]
49    InvalidArgument,
50
51    #[error("insufficient balance")]
52    #[sdk_error(code = 2)]
53    InsufficientBalance,
54
55    #[error("forbidden by policy")]
56    #[sdk_error(code = 3)]
57    Forbidden,
58
59    #[error("not found")]
60    #[sdk_error(code = 4)]
61    NotFound,
62
63    #[error("core: {0}")]
64    #[sdk_error(transparent)]
65    Core(#[from] modules::core::Error),
66}
67
68/// Events emitted by the accounts module.
69#[derive(Debug, cbor::Encode, oasis_runtime_sdk_macros::Event)]
70#[cbor(untagged)]
71pub enum Event {
72    #[sdk_event(code = 1)]
73    Transfer {
74        from: Address,
75        to: Address,
76        amount: token::BaseUnits,
77    },
78
79    #[sdk_event(code = 2)]
80    Burn {
81        owner: Address,
82        amount: token::BaseUnits,
83    },
84
85    #[sdk_event(code = 3)]
86    Mint {
87        owner: Address,
88        amount: token::BaseUnits,
89    },
90}
91
92/// Gas costs.
93#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
94pub struct GasCosts {
95    pub tx_transfer: u64,
96}
97
98/// Parameters for the accounts module.
99#[derive(Clone, Default, Debug, cbor::Encode, cbor::Decode)]
100pub struct Parameters {
101    pub transfers_disabled: bool,
102    pub gas_costs: GasCosts,
103
104    #[cbor(optional)]
105    pub debug_disable_nonce_check: bool,
106
107    #[cbor(optional)]
108    pub denomination_infos: BTreeMap<token::Denomination, types::DenominationInfo>,
109}
110
111/// Errors emitted during rewards parameter validation.
112#[derive(Error, Debug)]
113pub enum ParameterValidationError {
114    #[error("debug option used: {0}")]
115    DebugOptionUsed(String),
116}
117
118impl module::Parameters for Parameters {
119    type Error = ParameterValidationError;
120
121    #[cfg(not(feature = "unsafe-allow-debug"))]
122    fn validate_basic(&self) -> Result<(), Self::Error> {
123        if self.debug_disable_nonce_check {
124            return Err(ParameterValidationError::DebugOptionUsed(
125                "debug_disable_nonce_check".to_string(),
126            ));
127        }
128
129        Ok(())
130    }
131}
132
133/// Genesis state for the accounts module.
134#[derive(Clone, Debug, Default, cbor::Encode, cbor::Decode)]
135pub struct Genesis {
136    pub parameters: Parameters,
137    pub accounts: BTreeMap<Address, types::Account>,
138    pub balances: BTreeMap<Address, BTreeMap<token::Denomination, u128>>,
139    pub total_supplies: BTreeMap<token::Denomination, u128>,
140}
141
142/// Interface that can be called from other modules.
143pub trait API {
144    /// Transfer an amount from one account to the other.
145    fn transfer(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error>;
146
147    /// Transfer an amount from one account to the other without emitting an event.
148    fn transfer_silent(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error>;
149
150    /// Mint new tokens, increasing the total supply.
151    fn mint(to: Address, amount: &token::BaseUnits) -> Result<(), Error>;
152
153    /// Burn existing tokens, decreasing the total supply.
154    fn burn(from: Address, amount: &token::BaseUnits) -> Result<(), Error>;
155
156    /// Sets an account's nonce.
157    fn set_nonce(address: Address, nonce: u64);
158
159    /// Fetch an account's current nonce.
160    fn get_nonce(address: Address) -> Result<u64, Error>;
161
162    /// Increments an account's nonce.
163    fn inc_nonce(address: Address);
164
165    /// Sets an account's balance of the given denomination.
166    ///
167    /// # Warning
168    ///
169    /// This method is dangerous as it can result in invariant violations.
170    fn set_balance(address: Address, amount: &token::BaseUnits);
171
172    /// Fetch an account's balance of the given denomination.
173    fn get_balance(address: Address, denomination: token::Denomination) -> Result<u128, Error>;
174
175    /// Ensures that the given account has at least the specified balance.
176    fn ensure_balance(address: Address, amount: &token::BaseUnits) -> Result<(), Error> {
177        let balance = Self::get_balance(address, amount.denomination().clone())?;
178        if balance < amount.amount() {
179            Err(Error::InsufficientBalance)
180        } else {
181            Ok(())
182        }
183    }
184
185    /// Get allowance for an address and denomination.
186    ///
187    /// The allowance is the amount an account owner allows another account to
188    /// spend from the owner's account for a given denomination.
189    ///
190    /// Note that the API user is responsible for taking allowances into
191    /// account, the transfer functions in this API do not.
192    fn get_allowance(
193        owner: Address,
194        beneficiary: Address,
195        denomination: token::Denomination,
196    ) -> Result<u128, Error>;
197
198    /// Set a user's allowance for spending tokens for the given denomination
199    /// from the owner's account.
200    fn set_allowance(owner: Address, beneficiary: Address, amount: &token::BaseUnits);
201
202    /// Fetch an account's current balances.
203    fn get_balances(address: Address) -> Result<types::AccountBalances, Error>;
204
205    /// Fetch addresses.
206    fn get_addresses(denomination: token::Denomination) -> Result<Vec<Address>, Error>;
207
208    /// Fetch total supplies.
209    fn get_total_supplies() -> Result<BTreeMap<token::Denomination, u128>, Error>;
210
211    /// Fetch the total supply for the given denomination.
212    fn get_total_supply(denomination: token::Denomination) -> Result<u128, Error>;
213
214    /// Sets the total supply for the given denomination.
215    ///
216    /// # Warning
217    ///
218    /// This method is dangerous as it can result in invariant violations.
219    fn set_total_supply(amount: &token::BaseUnits);
220
221    /// Fetch information about a denomination.
222    fn get_denomination_info(
223        denomination: &token::Denomination,
224    ) -> Result<types::DenominationInfo, Error>;
225
226    /// Moves the amount into the per-transaction fee accumulator.
227    fn charge_tx_fee(from: Address, amount: &token::BaseUnits) -> Result<(), modules::core::Error>;
228
229    /// Indicates that the unused portion of the transaction fee should be refunded after the
230    /// transaction completes (even in case it fails).
231    fn set_refund_unused_tx_fee(refund: bool);
232
233    /// Take the flag indicating that the unused portion of the transaction fee should be refunded
234    /// after the transaction completes is set.
235    ///
236    /// After calling this method the flag is reset to `false`.
237    fn take_refund_unused_tx_fee() -> bool;
238
239    /// Check transaction signer account nonces.
240    /// Return payer address.
241    fn check_signer_nonces<C: Context>(
242        ctx: &C,
243        tx_auth_info: &AuthInfo,
244    ) -> Result<Address, modules::core::Error>;
245
246    /// Update transaction signer account nonces.
247    fn update_signer_nonces<C: Context>(
248        ctx: &C,
249        tx_auth_info: &AuthInfo,
250    ) -> Result<(), modules::core::Error>;
251}
252
253/// State schema constants.
254pub mod state {
255    /// Map of account addresses to account metadata.
256    pub const ACCOUNTS: &[u8] = &[0x01];
257    /// Map of account addresses to map of denominations to balances.
258    pub const BALANCES: &[u8] = &[0x02];
259    /// Map of total supplies (per denomination).
260    pub const TOTAL_SUPPLY: &[u8] = &[0x03];
261    /// Map of allowances (per denomination).
262    pub const ALLOWANCES: &[u8] = &[0x04];
263}
264
265pub struct Module;
266
267/// Module's address that has the common pool.
268///
269/// oasis1qz78phkdan64g040cvqvqpwkplfqf6tj6uwcsh30
270pub static ADDRESS_COMMON_POOL: Lazy<Address> =
271    Lazy::new(|| Address::from_module(MODULE_NAME, "common-pool"));
272/// Module's address that has the fee accumulator.
273///
274/// oasis1qp3r8hgsnphajmfzfuaa8fhjag7e0yt35cjxq0u4
275pub static ADDRESS_FEE_ACCUMULATOR: Lazy<Address> =
276    Lazy::new(|| Address::from_module(MODULE_NAME, "fee-accumulator"));
277
278/// This is needed to properly iterate over the BALANCES map.
279#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
280struct AddressWithDenomination(Address, token::Denomination);
281
282#[derive(Error, Debug)]
283enum AWDError {
284    #[error("malformed address")]
285    MalformedAddress,
286
287    #[error("malformed denomination")]
288    MalformedDenomination,
289}
290
291impl std::convert::TryFrom<&[u8]> for AddressWithDenomination {
292    type Error = AWDError;
293
294    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
295        let address =
296            Address::try_from(&bytes[..Address::SIZE]).map_err(|_| AWDError::MalformedAddress)?;
297        let denomination = token::Denomination::try_from(&bytes[Address::SIZE..])
298            .map_err(|_| AWDError::MalformedDenomination)?;
299        Ok(AddressWithDenomination(address, denomination))
300    }
301}
302
303impl Module {
304    /// Add given amount of tokens to the specified account's balance.
305    fn add_amount(addr: Address, amount: &token::BaseUnits) -> Result<(), Error> {
306        if amount.amount() == 0 {
307            return Ok(());
308        }
309
310        CurrentState::with_store(|store| {
311            let store = storage::PrefixStore::new(store, &MODULE_NAME);
312            let balances = storage::PrefixStore::new(store, &state::BALANCES);
313            let mut account = storage::TypedStore::new(storage::PrefixStore::new(balances, &addr));
314            let mut value: u128 = account.get(amount.denomination()).unwrap_or_default();
315
316            value = value
317                .checked_add(amount.amount())
318                .ok_or(Error::InvalidArgument)?;
319            account.insert(amount.denomination(), value);
320            Ok(())
321        })
322    }
323
324    /// Subtract given amount of tokens from the specified account's balance.
325    fn sub_amount(addr: Address, amount: &token::BaseUnits) -> Result<(), Error> {
326        if amount.amount() == 0 {
327            return Ok(());
328        }
329
330        CurrentState::with_store(|store| {
331            let store = storage::PrefixStore::new(store, &MODULE_NAME);
332            let balances = storage::PrefixStore::new(store, &state::BALANCES);
333            let mut account = storage::TypedStore::new(storage::PrefixStore::new(balances, &addr));
334            let mut value: u128 = account.get(amount.denomination()).unwrap_or_default();
335
336            value = value
337                .checked_sub(amount.amount())
338                .ok_or(Error::InsufficientBalance)?;
339            account.insert(amount.denomination(), value);
340            Ok(())
341        })
342    }
343
344    /// Increment the total supply for the given amount.
345    fn inc_total_supply(amount: &token::BaseUnits) -> Result<(), Error> {
346        if amount.amount() == 0 {
347            return Ok(());
348        }
349
350        CurrentState::with_store(|store| {
351            let store = storage::PrefixStore::new(store, &MODULE_NAME);
352            let mut total_supplies =
353                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
354            let mut total_supply: u128 = total_supplies
355                .get(amount.denomination())
356                .unwrap_or_default();
357
358            total_supply = total_supply
359                .checked_add(amount.amount())
360                .ok_or(Error::InvalidArgument)?;
361            total_supplies.insert(amount.denomination(), total_supply);
362            Ok(())
363        })
364    }
365
366    /// Decrement the total supply for the given amount.
367    fn dec_total_supply(amount: &token::BaseUnits) -> Result<(), Error> {
368        if amount.amount() == 0 {
369            return Ok(());
370        }
371
372        CurrentState::with_store(|store| {
373            let store = storage::PrefixStore::new(store, &MODULE_NAME);
374            let mut total_supplies =
375                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
376            let mut total_supply: u128 = total_supplies
377                .get(amount.denomination())
378                .unwrap_or_default();
379
380            total_supply = total_supply
381                .checked_sub(amount.amount())
382                .ok_or(Error::InsufficientBalance)?;
383            total_supplies.insert(amount.denomination(), total_supply);
384            Ok(())
385        })
386    }
387
388    /// Get all balances.
389    fn get_all_balances() -> Result<BTreeMap<Address, BTreeMap<token::Denomination, u128>>, Error> {
390        CurrentState::with_store(|store| {
391            let store = storage::PrefixStore::new(store, &MODULE_NAME);
392            let balances =
393                storage::TypedStore::new(storage::PrefixStore::new(store, &state::BALANCES));
394
395            // Unfortunately, we can't just return balances.iter().collect() here,
396            // because the stored format doesn't match -- we need this workaround
397            // instead.
398
399            let balmap: BTreeMap<AddressWithDenomination, u128> = balances.iter().collect();
400
401            let mut b: BTreeMap<Address, BTreeMap<token::Denomination, u128>> = BTreeMap::new();
402
403            for (addrden, amt) in &balmap {
404                let addr = &addrden.0;
405                let den = &addrden.1;
406
407                // Fetch existing account's balances or insert blank ones.
408                let addr_bals = b.entry(*addr).or_default();
409
410                // Add to given denomination's balance or insert it if new.
411                addr_bals
412                    .entry(den.clone())
413                    .and_modify(|a| *a += amt)
414                    .or_insert_with(|| *amt);
415            }
416
417            Ok(b)
418        })
419    }
420}
421
422/// Context key for the per-transaction unused fee refund decision.
423const CONTEXT_KEY_TX_FEE_REFUND_UNUSED: &str = "accounts.TxRefundUnusedFee";
424/// Context key for the per block fee manager.
425const CONTEXT_KEY_FEE_MANAGER: &str = "accounts.FeeManager";
426
427impl API for Module {
428    fn transfer(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error> {
429        if CurrentState::with_env(|env| env.is_check_only()) || amount.amount() == 0 {
430            return Ok(());
431        }
432
433        Self::transfer_silent(from, to, amount)?;
434
435        // Emit a transfer event.
436        CurrentState::with(|state| {
437            state.emit_event(Event::Transfer {
438                from,
439                to,
440                amount: amount.clone(),
441            })
442        });
443
444        Ok(())
445    }
446
447    fn transfer_silent(from: Address, to: Address, amount: &token::BaseUnits) -> Result<(), Error> {
448        // Subtract from source account.
449        Self::sub_amount(from, amount)?;
450        // Add to destination account.
451        Self::add_amount(to, amount)?;
452
453        Ok(())
454    }
455
456    fn mint(to: Address, amount: &token::BaseUnits) -> Result<(), Error> {
457        if CurrentState::with_env(|env| env.is_check_only()) || amount.amount() == 0 {
458            return Ok(());
459        }
460
461        // Add to destination account.
462        Self::add_amount(to, amount)?;
463
464        // Increase total supply.
465        Self::inc_total_supply(amount)?;
466
467        // Emit a mint event.
468        CurrentState::with(|state| {
469            state.emit_event(Event::Mint {
470                owner: to,
471                amount: amount.clone(),
472            });
473        });
474
475        Ok(())
476    }
477
478    fn burn(from: Address, amount: &token::BaseUnits) -> Result<(), Error> {
479        if CurrentState::with_env(|env| env.is_check_only()) || amount.amount() == 0 {
480            return Ok(());
481        }
482
483        // Remove from target account.
484        Self::sub_amount(from, amount)?;
485
486        // Decrease total supply.
487        Self::dec_total_supply(amount)
488            .expect("target account had enough balance so total supply should not underflow");
489
490        // Emit a burn event.
491        CurrentState::with(|state| {
492            state.emit_event(Event::Burn {
493                owner: from,
494                amount: amount.clone(),
495            });
496        });
497
498        Ok(())
499    }
500
501    fn set_nonce(address: Address, nonce: u64) {
502        CurrentState::with_store(|store| {
503            let store = storage::PrefixStore::new(store, &MODULE_NAME);
504            let mut accounts =
505                storage::TypedStore::new(storage::PrefixStore::new(store, &state::ACCOUNTS));
506            let mut account: types::Account = accounts.get(address).unwrap_or_default();
507            account.nonce = nonce;
508            accounts.insert(address, account);
509        })
510    }
511
512    fn get_nonce(address: Address) -> Result<u64, Error> {
513        CurrentState::with_store(|store| {
514            let store = storage::PrefixStore::new(store, &MODULE_NAME);
515            let accounts =
516                storage::TypedStore::new(storage::PrefixStore::new(store, &state::ACCOUNTS));
517            let account: types::Account = accounts.get(address).unwrap_or_default();
518            Ok(account.nonce)
519        })
520    }
521
522    fn inc_nonce(address: Address) {
523        CurrentState::with_store(|store| {
524            let store = storage::PrefixStore::new(store, &MODULE_NAME);
525            let mut accounts =
526                storage::TypedStore::new(storage::PrefixStore::new(store, &state::ACCOUNTS));
527            let mut account: types::Account = accounts.get(address).unwrap_or_default();
528            account.nonce = account.nonce.saturating_add(1);
529            accounts.insert(address, account);
530        })
531    }
532
533    fn set_balance(address: Address, amount: &token::BaseUnits) {
534        CurrentState::with_store(|store| {
535            let store = storage::PrefixStore::new(store, &MODULE_NAME);
536            let balances = storage::PrefixStore::new(store, &state::BALANCES);
537            let mut account =
538                storage::TypedStore::new(storage::PrefixStore::new(balances, &address));
539            account.insert(amount.denomination(), amount.amount());
540        });
541    }
542
543    fn get_balance(address: Address, denomination: token::Denomination) -> Result<u128, Error> {
544        CurrentState::with_store(|store| {
545            let store = storage::PrefixStore::new(store, &MODULE_NAME);
546            let balances = storage::PrefixStore::new(store, &state::BALANCES);
547            let account = storage::TypedStore::new(storage::PrefixStore::new(balances, &address));
548
549            Ok(account.get(denomination).unwrap_or_default())
550        })
551    }
552
553    fn get_balances(address: Address) -> Result<types::AccountBalances, Error> {
554        CurrentState::with_store(|store| {
555            let store = storage::PrefixStore::new(store, &MODULE_NAME);
556            let balances = storage::PrefixStore::new(store, &state::BALANCES);
557            let account = storage::TypedStore::new(storage::PrefixStore::new(balances, &address));
558
559            Ok(types::AccountBalances {
560                balances: account.iter().collect(),
561            })
562        })
563    }
564
565    fn get_allowance(
566        owner: Address,
567        beneficiary: Address,
568        denomination: token::Denomination,
569    ) -> Result<u128, Error> {
570        CurrentState::with_store(|store| {
571            let store = storage::PrefixStore::new(store, &MODULE_NAME);
572            let allowances = storage::PrefixStore::new(store, &state::ALLOWANCES);
573            let for_owner = storage::PrefixStore::new(allowances, &owner);
574            let for_beneficiary =
575                storage::TypedStore::new(storage::PrefixStore::new(for_owner, &beneficiary));
576
577            Ok(for_beneficiary.get(denomination).unwrap_or_default())
578        })
579    }
580
581    fn set_allowance(owner: Address, beneficiary: Address, amount: &token::BaseUnits) {
582        CurrentState::with_store(|store| {
583            let store = storage::PrefixStore::new(store, &MODULE_NAME);
584            let allowances = storage::PrefixStore::new(store, &state::ALLOWANCES);
585            let for_owner = storage::PrefixStore::new(allowances, &owner);
586            let mut for_beneficiary =
587                storage::TypedStore::new(storage::PrefixStore::new(for_owner, &beneficiary));
588
589            for_beneficiary.insert(amount.denomination(), amount.amount());
590        })
591    }
592
593    fn get_addresses(denomination: token::Denomination) -> Result<Vec<Address>, Error> {
594        CurrentState::with_store(|store| {
595            let store = storage::PrefixStore::new(store, &MODULE_NAME);
596            let balances: BTreeMap<AddressWithDenomination, Quantity> =
597                storage::TypedStore::new(storage::PrefixStore::new(store, &state::BALANCES))
598                    .iter()
599                    .collect();
600
601            Ok(balances
602                .into_keys()
603                .filter(|bal| bal.1 == denomination)
604                .map(|bal| bal.0)
605                .collect())
606        })
607    }
608
609    fn get_total_supplies() -> Result<BTreeMap<token::Denomination, u128>, Error> {
610        CurrentState::with_store(|store| {
611            let store = storage::PrefixStore::new(store, &MODULE_NAME);
612            let ts =
613                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
614
615            Ok(ts.iter().collect())
616        })
617    }
618
619    fn get_total_supply(denomination: token::Denomination) -> Result<u128, Error> {
620        CurrentState::with_store(|store| {
621            let store = storage::PrefixStore::new(store, &MODULE_NAME);
622            let ts =
623                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
624            Ok(ts.get(denomination).unwrap_or_default())
625        })
626    }
627
628    fn set_total_supply(amount: &token::BaseUnits) {
629        CurrentState::with_store(|store| {
630            let store = storage::PrefixStore::new(store, &MODULE_NAME);
631            let mut total_supplies =
632                storage::TypedStore::new(storage::PrefixStore::new(store, &state::TOTAL_SUPPLY));
633            total_supplies.insert(amount.denomination(), amount.amount());
634        });
635    }
636
637    fn get_denomination_info(
638        denomination: &token::Denomination,
639    ) -> Result<types::DenominationInfo, Error> {
640        Self::params()
641            .denomination_infos
642            .get(denomination)
643            .cloned()
644            .ok_or(Error::NotFound)
645    }
646
647    fn charge_tx_fee(from: Address, amount: &token::BaseUnits) -> Result<(), modules::core::Error> {
648        if CurrentState::with_env(|env| env.is_simulation()) {
649            return Ok(());
650        }
651
652        Self::sub_amount(from, amount).map_err(|_| modules::core::Error::InsufficientFeeBalance)?;
653
654        CurrentState::with(|state| {
655            state
656                .block_value::<fee::FeeManager>(CONTEXT_KEY_FEE_MANAGER)
657                .or_default()
658                .record_fee(from, amount);
659        });
660
661        Ok(())
662    }
663
664    fn set_refund_unused_tx_fee(refund: bool) {
665        CurrentState::with(|state| {
666            if state.env().is_simulation() {
667                return;
668            }
669
670            state
671                .block_value(CONTEXT_KEY_TX_FEE_REFUND_UNUSED)
672                .set(refund);
673        });
674    }
675
676    fn take_refund_unused_tx_fee() -> bool {
677        CurrentState::with(|state| {
678            if state.env().is_simulation() {
679                return false;
680            }
681
682            state
683                .block_value(CONTEXT_KEY_TX_FEE_REFUND_UNUSED)
684                .take()
685                .unwrap_or(false)
686        })
687    }
688
689    fn check_signer_nonces<C: Context>(
690        _ctx: &C,
691        auth_info: &AuthInfo,
692    ) -> Result<Address, modules::core::Error> {
693        let is_pre_schedule = CurrentState::with_env(|env| env.is_pre_schedule());
694        let is_check_only = CurrentState::with_env(|env| env.is_check_only());
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                        // If too much in the future, reject.
732                        if si.nonce - account.nonce > MAX_CHECK_NONCE_FUTURE_DELTA {
733                            return Err(modules::core::Error::InvalidNonce);
734                        }
735
736                        // If in the future and this is before scheduling, reject with separate error
737                        // that will make the scheduler skip the transaction.
738                        if is_pre_schedule {
739                            return Err(modules::core::Error::FutureNonce);
740                        }
741
742                        // If in the future and this is during execution, reject.
743                        if !is_check_only {
744                            return Err(modules::core::Error::InvalidNonce);
745                        }
746
747                        // If in the future and this is during checks, accept.
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 is_check_only {
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}