123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- use crate::{
- storage::{Batch, Storage},
- transaction::Type,
- AccountId, Amount, AssetManager, Error, Payment, Status, Transaction, TransactionId,
- };
- use std::{cmp::Ordering, collections::HashMap};
- #[derive(Clone, Debug)]
- pub struct Ledger<'a, B, S>
- where
- B: Batch<'a>,
- S: Storage<'a, B> + Sync + Send,
- {
- storage: S,
- asset_manager: AssetManager,
- _phantom: std::marker::PhantomData<&'a B>,
- }
- impl<'a, B, S> Ledger<'a, B, S>
- where
- B: Batch<'a>,
- S: Storage<'a, B> + Sync + Send,
- {
- pub fn new(storage: S, asset_manager: AssetManager) -> Self {
- Self {
- storage,
- asset_manager,
- _phantom: std::marker::PhantomData,
- }
- }
- pub fn parse_amount(&self, asset: &str, amount: &str) -> Result<Amount, Error> {
- Ok(self.asset_manager.human_amount_by_name(asset, amount)?)
- }
- /// Selects the unspent payments to be used as inputs of the new transaction.
- ///
- /// This function also returns a list of transactions that will be used as
- /// exchanged transactions, to make sure the main transaction doesn't hold
- /// extra funds, by splitting any large unspent payments into two, one that
- /// matches exactly the needed amount, and another one that will be used as
- /// change. These exchange transaction are internal transactions and they
- /// are created as settled.
- async fn create_inputs_to_pay_from_accounts(
- &self,
- payments: Vec<(AccountId, Amount)>,
- ) -> Result<(Vec<Transaction>, Vec<Payment>), Error> {
- let mut to_spend = HashMap::new();
- for (account_id, amount) in payments.into_iter() {
- let id = (account_id, *amount.asset());
- if let Some(value) = to_spend.get_mut(&id) {
- *value += amount.cents();
- } else {
- to_spend.insert(id, amount.cents());
- }
- }
- let mut change_transactions = vec![];
- let mut payments: Vec<Payment> = vec![];
- for ((account, asset), mut to_spend_cents) in to_spend.into_iter() {
- let iterator = self
- .storage
- .get_unspent_payments(&account, asset.id, to_spend_cents)
- .await?;
- for payment in iterator.into_iter() {
- let cents = payment.amount.cents();
- to_spend_cents -= cents;
- payments.push(payment);
- match to_spend_cents.cmp(&0) {
- Ordering::Equal => {
- // No change amount, we are done with this input
- break;
- }
- Ordering::Less => {
- // There is a change amount, we need to split the last
- // input into two payment_ids into the same accounts in
- // a transaction that will settle immediately, otherwise
- // the change amount will be unspentable until this
- // transaction settles. By doing so the current
- // operation will have no change and it can safely take
- // its time to settle without making any change amount
- // unspentable.
- let to_spend_cents = to_spend_cents.abs();
- let input = payments
- .pop()
- .ok_or(Error::InsufficientBalance(account.clone(), asset.id))?;
- let split_input = Transaction::new(
- "Exchange transaction".to_owned(),
- // Set the change transaction as settled. This is an
- // internal transaction to split an existing payment
- // into two. Since this is an internal transaction it
- // can be settled immediately.
- //
- // Because this internal transaction is being settled
- // immediately, the other payment can be used right away,
- // otherwise it would be locked until the main
- // transaction settles.
- Status::Settled,
- Type::Internal,
- vec![input],
- vec![
- (account.clone(), asset.new_amount(cents - to_spend_cents)),
- (account.clone(), asset.new_amount(to_spend_cents)),
- ],
- )
- .await?;
- // Spend the new payment
- payments.push(split_input.creates()[0].clone());
- // Return the split payment transaction to be executed
- // later as a pre-requisite for the new transaction
- change_transactions.push(split_input);
- // Go to the next payment input or exit the loop
- break;
- }
- _ => {
- // We need more funds, continue to the selecting the
- // available payment if any
- }
- }
- }
- if to_spend_cents > 0 {
- // We don't have enough payment to cover the to_spend_cents
- // Return an insufficient balance error
- return Err(Error::InsufficientBalance(account, asset.id));
- }
- }
- Ok((change_transactions, payments))
- }
- /// Creates a new transaction and returns it.
- ///
- /// The input is pretty simple, take this amounts from these given accounts
- /// and send them to these accounts (and amounts). The job of this function
- /// is to figure it out how to do it. This function will make sure that each
- /// account has enough balance, selecting the unspent payments from each
- /// account that will be spent. It will also return a list of transactions
- /// that will be used to return the change to the accounts, these accounts
- /// can be settled immediately so no other funds required to perform the
- /// transaction are locked.
- ///
- /// This functions performs read only operations on top of the storage layer
- /// and it will guarantee execution (meaning that it will not lock any
- /// funds, so these transactions may fail if any selected payment is spent
- /// between the time the transaction is created and executed).
- ///
- /// A NewTransaction struct is returned, the change_transactions should be
- /// executed and set as settled before the transaction is executed,
- /// otherwise it will fail. A failure in any execution will render the
- /// entire operation as failed but no funds will be locked.
- pub async fn new_transaction(
- &'a self,
- reference: String,
- status: Status,
- from: Vec<(AccountId, Amount)>,
- to: Vec<(AccountId, Amount)>,
- ) -> Result<Transaction, Error> {
- let (change_transactions, payments) = self.create_inputs_to_pay_from_accounts(from).await?;
- for mut change_tx in change_transactions.into_iter() {
- change_tx.persist(&self.storage).await?;
- }
- let mut transaction =
- Transaction::new(reference, status, Type::Transaction, payments, to).await?;
- transaction.persist(&self.storage).await?;
- Ok(transaction)
- }
- /// Return the balances from a given account
- ///
- /// The balance is a vector of Amounts, one for each asset. The balance will
- /// return only spendable amounts, meaning that any amount that is locked in
- /// a transaction will not be returned.
- ///
- /// TODO: Return locked funds as well.
- pub async fn get_balance(&self, account: &AccountId) -> Result<Vec<Amount>, Error> {
- Ok(self.storage.get_balance(account).await?)
- }
- pub async fn deposit(
- &'a self,
- account: &AccountId,
- amount: Amount,
- status: Status,
- reference: String,
- ) -> Result<Transaction, Error> {
- let mut transaction =
- Transaction::new_external_deposit(reference, status, vec![(account.clone(), amount)])?;
- println!("{}", serde_json::to_string_pretty(&transaction).unwrap());
- transaction.persist(&self.storage).await?;
- Ok(transaction)
- }
- pub async fn withdrawal(
- &'a self,
- account: &AccountId,
- amount: Amount,
- status: Status,
- reference: String,
- ) -> Result<Transaction, Error> {
- let (change_transactions, payments) = self
- .create_inputs_to_pay_from_accounts(vec![(account.clone(), amount)])
- .await?;
- for mut change_tx in change_transactions.into_iter() {
- change_tx.persist(&self.storage).await?;
- }
- let mut transaction = Transaction::new_external_withdrawal(reference, status, payments)?;
- transaction.persist(&self.storage).await?;
- Ok(transaction)
- }
- /// Returns the transaction object by a given transaction id
- pub async fn get_transaction(
- &'a self,
- transaction_id: &TransactionId,
- ) -> Result<Transaction, Error> {
- Ok(self
- .storage
- .get_transaction(transaction_id)
- .await?
- .try_into()?)
- }
- /// Returns all transactions from a given account. It can be optionally be
- /// sorted by transaction type. The transactions are sorted from newest to
- /// oldest.
- pub async fn get_transactions(
- &'a self,
- account_id: &AccountId,
- typ: Option<Type>,
- ) -> Result<Vec<Transaction>, Error> {
- let r = self
- .storage
- .get_transactions(account_id, typ)
- .await?
- .into_iter()
- .map(|x| x.try_into())
- .collect::<Result<Vec<Transaction>, _>>()?;
- Ok(r)
- }
- /// Attemps to change the status of a given transaction id. On success the
- /// new transaction object is returned, otherwise an error is returned.
- pub async fn change_status(
- &'a self,
- transaction_id: &TransactionId,
- new_status: Status,
- ) -> Result<Transaction, Error> {
- let mut tx: Transaction = self
- .storage
- .get_transaction(transaction_id)
- .await?
- .try_into()?;
- tx.change_status(new_status)?;
- tx.persist(&self.storage).await?;
- Ok(tx)
- }
- }
|