This repository has been archived by the owner on Jul 27, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 66
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Problem: impossible to track wallet balance (CRO-53) WIP
Solution: client-core WIP infrastructure (balance_service...) described in README
- Loading branch information
1 parent
16fc601
commit e60b90f
Showing
17 changed files
with
683 additions
and
19 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# Crypto.com Chain Client | ||
|
||
This crate exposes following functionalities for interacting with Crypto.com Chain: | ||
- Wallet creation | ||
- Address generation | ||
- Transaction syncing and storage | ||
- Balance tracking | ||
- Transaction creation and signing | ||
|
||
## Design | ||
|
||
Below is a high level design diagram of this crate: | ||
|
||
<div class = "design_diagram"> | ||
<img src="./client_design.png" alt="Client Design" /> | ||
</div> | ||
|
||
### `Storage` trait | ||
|
||
This trait declares APIs for different database operations such as `clear`, `get`, `set`, `contains_key`, etc. This | ||
crate provides a default implementation (`SledStorage`) of this trait using `Sled` embedded database. | ||
|
||
### `SecureStorage` trait | ||
|
||
This trait exposes APIs for securely getting and setting values in `Storage`. This crates is automatically implemented | ||
for all the types implementing `Storage` trait. | ||
|
||
### `Chain` trait | ||
|
||
This trait exposes APIs for communicating with Crypto.com Chain via ABCI. Currently, this crate exposes following APIs: | ||
- `query_transaction_changes`: Queries Crypto.com chain for balance changes for different `addresses` from | ||
`last_block_height`. | ||
|
||
### Services | ||
|
||
`Storage` implementation provide generic APIs over any storage backend. In order to provide intended public interface | ||
(`Wallet`) through this crate, we need specific services for handling storage of different entities, like, keys, | ||
wallets, balance, and transactions. | ||
|
||
#### `KeyService` | ||
|
||
`KeyService` exposes key related operations (`generate` and `get_keys`) on top of any `Storage` implementation. | ||
- `generate`: Generates a new private key for given `wallet_id` and encrypts it with given `passphrase` before storing. | ||
- `get_keys`: Returns all the keys stored for given `wallet_id` and decrypts them with given `passphrase`. | ||
|
||
#### `WalletService` | ||
|
||
`WalletService` exposes wallet related operations (`create` and `get`) on top of any `Storage` implementation. | ||
- `create`: Creates a new wallet and returns `wallet_id`. This function also encrypts all the data using `passphrase` | ||
before storing it in `Storage`. | ||
- `get`: Retrieves a `wallet_id` from `Storage` and decrypts it with given `passphrase`. | ||
|
||
#### `BalanceService` | ||
|
||
`BalanceService` exposes balance related operations (`sync`, `sync_all` and `get_balance`) on top of any `Storage` and | ||
`Chain` implementation. | ||
- `sync`: Updates balance for given `wallet_id` and `addresses` after querying new transactions from Crypto.com Chain. | ||
This function first retrieves current `balance` and `last_block_height` from `Storage` and then queries `Chain` for | ||
any updates since `last_block_height`. After successful query, it updates the data in `Storage`. | ||
- `sync_all`: This works in similar way as `sync` except it sets `last_block_height = 0` and queries for all the | ||
transactions since genesis block. | ||
- `get_balance`: Returns balance for a given `wallet_id` from `Storage`. | ||
|
||
### `Wallet` trait | ||
|
||
Crypto.com exposes public interface through `Wallet` trait which contains following functions with default | ||
implementations: | ||
|
||
- `new_wallet`: Creates a new wallet with given `name` and encrypts it with given `passphrase`. This function internally | ||
calls `crate` function of `WalletService`. | ||
- `get_public_keys`: Retrieves all public keys corresponding to given wallet `name` and `passphrase`. This function | ||
internally uses `KeyService` for get this information. | ||
- `get_addresses`: Retrieves all addresses corresponding to given wallet `name` and `passphrase`. This function | ||
internally uses `KeyService` for get this information. | ||
- `generate_public_key`: Generates a new public key for given wallet `name` and `passphrase`. This function internally | ||
uses `KeyService`. | ||
- `generate_address`: Generates a new address (redeem) for given wallet `name` and `passphrase`. This function | ||
internally uses `KeyService`. | ||
- `get_balance`: Retrieves current balance for given wallet `name` and `passphrase`. This function internally uses | ||
`BalanceService` to get the balance. | ||
- `sync_balance`: Synchronizes and returns current balance for given wallet `name` and `passphrase`. This function | ||
internally uses `BalanceService::sync` to synchronize balance. | ||
- `recalculate_balance`: Recalculate current balance for given wallet `name` and `passphrase` from genesis. This | ||
function internally uses `BalanceService::sync_all` to synchronize balance. | ||
|
||
## API Documentation | ||
|
||
To see this crate's API docs. Run following command from `chain` directory. | ||
``` | ||
cargo doc --package client-core --no-deps --open | ||
``` | ||
|
||
### Warning | ||
|
||
This is a work-in-progress crate and is unusable in its current state. These is no implementation for Chain ABCI client | ||
(`Chain` trait) as of now. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
//! Types for tracking balance changes | ||
mod balance_change; | ||
mod transaction_change; | ||
|
||
pub use self::balance_change::BalanceChange; | ||
pub use self::transaction_change::TransactionChange; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use std::ops::Add; | ||
|
||
use failure::ResultExt; | ||
|
||
use crate::{ErrorKind, Result}; | ||
|
||
use chain_core::init::coin::Coin; | ||
|
||
/// Incoming or Outgoing balance change | ||
#[derive(Debug)] | ||
pub enum BalanceChange { | ||
/// Represents balance addition | ||
Incoming(Coin), | ||
/// Represents balance reduction | ||
Outgoing(Coin), | ||
} | ||
|
||
#[allow(clippy::suspicious_arithmetic_impl)] | ||
impl Add<&BalanceChange> for Coin { | ||
type Output = Result<Coin>; | ||
|
||
fn add(self, other: &BalanceChange) -> Self::Output { | ||
match other { | ||
BalanceChange::Incoming(change) => { | ||
Ok((self + change).context(ErrorKind::BalanceAdditionError)?) | ||
} | ||
BalanceChange::Outgoing(change) => { | ||
Ok((self - change).context(ErrorKind::BalanceAdditionError)?) | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl Add<BalanceChange> for Coin { | ||
type Output = Result<Coin>; | ||
|
||
fn add(self, other: BalanceChange) -> Self::Output { | ||
self + &other | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn add_incoming() { | ||
let coin = Coin::zero() | ||
+ BalanceChange::Incoming(Coin::new(30).expect("Unable to create new coin")); | ||
|
||
assert_eq!( | ||
Coin::new(30).expect("Unable to create new coin"), | ||
coin.expect("Unable to add coins"), | ||
"Coins does not match" | ||
); | ||
} | ||
|
||
#[test] | ||
fn add_incoming_fail() { | ||
let coin = Coin::max() | ||
+ BalanceChange::Incoming(Coin::new(30).expect("Unable to create new coin")); | ||
|
||
assert!(coin.is_err(), "Created coin greater than max value") | ||
} | ||
|
||
#[test] | ||
fn add_outgoing() { | ||
let coin = Coin::new(40).expect("Unable to create new coin") | ||
+ BalanceChange::Outgoing(Coin::new(30).expect("Unable to create new coin")); | ||
|
||
assert_eq!( | ||
Coin::new(10).expect("Unable to create new coin"), | ||
coin.expect("Unable to add coins"), | ||
"Coins does not match" | ||
); | ||
} | ||
|
||
#[test] | ||
fn add_outgoing_fail() { | ||
let coin = Coin::zero() | ||
+ BalanceChange::Outgoing(Coin::new(30).expect("Unable to create new coin")); | ||
|
||
assert!(coin.is_err(), "Created negative coin") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
use std::ops::Add; | ||
|
||
use crate::balance::BalanceChange; | ||
use crate::Result; | ||
|
||
use chain_core::init::coin::Coin; | ||
use chain_core::tx::data::address::ExtendedAddr; | ||
use chain_core::tx::data::TxId; | ||
|
||
/// Represents balance change in a transaction | ||
#[derive(Debug)] | ||
pub struct TransactionChange { | ||
/// ID of transaction which caused this change | ||
pub transaction_id: TxId, | ||
/// Address which is affected by this change | ||
pub address: ExtendedAddr, | ||
/// Change in balance | ||
pub balance_change: BalanceChange, | ||
} | ||
|
||
impl Add<&TransactionChange> for Coin { | ||
type Output = Result<Coin>; | ||
|
||
fn add(self, other: &TransactionChange) -> Self::Output { | ||
self + &other.balance_change | ||
} | ||
} | ||
|
||
impl Add<TransactionChange> for Coin { | ||
type Output = Result<Coin>; | ||
|
||
fn add(self, other: TransactionChange) -> Self::Output { | ||
self + &other | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use chain_core::tx::data::txid_hash; | ||
|
||
fn get_transaction_change(balance_change: BalanceChange) -> TransactionChange { | ||
TransactionChange { | ||
transaction_id: txid_hash(&[0, 1, 2]), | ||
address: ExtendedAddr::BasicRedeem(Default::default()), | ||
balance_change, | ||
} | ||
} | ||
|
||
#[test] | ||
fn add_incoming() { | ||
let coin = Coin::zero() | ||
+ get_transaction_change(BalanceChange::Incoming( | ||
Coin::new(30).expect("Unable to create new coin"), | ||
)); | ||
|
||
assert_eq!( | ||
Coin::new(30).expect("Unable to create new coin"), | ||
coin.expect("Unable to add coins"), | ||
"Coins does not match" | ||
); | ||
} | ||
|
||
#[test] | ||
fn add_incoming_fail() { | ||
let coin = Coin::max() | ||
+ get_transaction_change(BalanceChange::Incoming( | ||
Coin::new(30).expect("Unable to create new coin"), | ||
)); | ||
|
||
assert!(coin.is_err(), "Created coin greater than max value") | ||
} | ||
|
||
#[test] | ||
fn add_outgoing() { | ||
let coin = Coin::new(40).expect("Unable to create new coin") | ||
+ get_transaction_change(BalanceChange::Outgoing( | ||
Coin::new(30).expect("Unable to create new coin"), | ||
)); | ||
|
||
assert_eq!( | ||
Coin::new(10).expect("Unable to create new coin"), | ||
coin.expect("Unable to add coins"), | ||
"Coins does not match" | ||
); | ||
} | ||
|
||
#[test] | ||
fn add_outgoing_fail() { | ||
let coin = Coin::zero() | ||
+ get_transaction_change(BalanceChange::Outgoing( | ||
Coin::new(30).expect("Unable to create new coin"), | ||
)); | ||
|
||
assert!(coin.is_err(), "Created negative coin") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
//! Communication between client and chain | ||
#[cfg(test)] | ||
mod mock_chain; | ||
|
||
#[cfg(test)] | ||
pub use mock_chain::MockChain; | ||
|
||
use crate::balance::TransactionChange; | ||
use crate::Result; | ||
|
||
/// Interface for a backend agnostic communication between client and chain | ||
/// | ||
/// ### Warning | ||
/// This is a WIP trait and will change in future based on requirements. | ||
pub trait Chain { | ||
/// Queries Crypto.com chain for changes for different `addresses` from `last_block_height` | ||
fn query_transaction_changes( | ||
&self, | ||
addresses: Vec<String>, | ||
last_block_height: u64, | ||
) -> Result<(Vec<TransactionChange>, u64)>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#![cfg(test)] | ||
|
||
use crate::balance::TransactionChange; | ||
use crate::{Chain, Result}; | ||
|
||
/// A mock chain client | ||
#[derive(Clone, Default)] | ||
pub struct MockChain; | ||
|
||
impl Chain for MockChain { | ||
fn query_transaction_changes( | ||
&self, | ||
_addresses: Vec<String>, | ||
_last_block_height: u64, | ||
) -> Result<(Vec<TransactionChange>, u64)> { | ||
Ok((Default::default(), Default::default())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.