Skip to content

Commit

Permalink
feat(transactions): add pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
slavik-pastushenko committed Feb 18, 2024
1 parent 13f600b commit 3cef01e
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 48 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ A Rust crate provides an interface for interacting with a blockchain.

- `new(difficulty, reward, fee)`: Initialize a new blockchain with the specified parameters.
- `get_transactions()`: Get a list of current transactions in the blockchain.
- `get_transaction(hash)`: Get a transaction by its hash.
- `get_transaction(hash, page, size)`: Get a transaction by its hash.
- `add_transaction(from, to, amount)`: Add a new transaction to the blockchain.
- `validate_transaction(from, amount)`: Validate a new transaction to the blockchain.
- `create_wallet(email)`: Create a new wallet with a unique email and an initial balance.
- `get_wallet_balance(address)`: Get a wallet's balance based on its address.
- `get_wallet_transactions(address)`: Get a wallet's transaction history based on its address.
- `get_wallet_transactions(address, page, size)`: Get a wallet's transaction history based on its address.
- `get_last_hash()`: Get the hash of the last block in the blockchain.
- `update_difficulty(difficulty)`: Update the mining difficulty of the blockchain.
- `update_reward(reward)`: Update the block reward.
Expand Down
71 changes: 49 additions & 22 deletions examples/api-axum/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ pub struct AppState {

/// Create a new wallet.
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateWallet {
pub struct CreateWalletInput {
/// The wallet email.
pub email: String,
}

/// Add a new transaction.
#[derive(Debug, Serialize, Deserialize)]
pub struct AddTransaction {
pub struct AddTransactionInput {
/// The sender address.
pub from: String,

Expand All @@ -39,24 +39,47 @@ pub struct AddTransaction {

/// Get the balance of a wallet.
#[derive(Debug, Serialize, Deserialize)]
pub struct GetWalletBalance {
pub struct GetWalletBalanceInput {
/// The wallet address.
pub address: String,
}

/// Get a list of transactions of a wallet.
#[derive(Debug, Serialize, Deserialize)]
pub struct GetWalletTransactionInput {
/// The wallet address.
pub address: String,

/// The page number.
pub page: usize,

/// The page size.
pub size: usize,
}

/// Get a list of transactions of a wallet.
#[derive(Debug, Serialize, Deserialize)]
pub struct GetTransactionsInput {
/// The page number.
pub page: usize,

/// The page size.
pub size: usize,
}

/// Create a new wallet.
///
/// # Arguments
///
/// * `state` - The application state.
/// * `body` - The request body.
/// - `state` - The application state.
/// - `body` - The request body.
///
/// # Returns
///
/// A new wallet address.
pub async fn create_wallet(
State(state): State<AppState>,
Json(body): Json<CreateWallet>,
Json(body): Json<CreateWalletInput>,
) -> impl IntoResponse {
let mut chain = state.chain.lock().unwrap();
let address = chain.create_wallet(body.email);
Expand All @@ -68,15 +91,15 @@ pub async fn create_wallet(
///
/// # Arguments
///
/// * `state` - The application state.
/// * `params` - The request query parameters.
/// - `state` - The application state.
/// - `params` - The request query parameters.
///
/// # Returns
///
/// The balance of the wallet.
pub async fn get_wallet_balance(
State(state): State<AppState>,
Query(params): Query<GetWalletBalance>,
Query(params): Query<GetWalletBalanceInput>,
) -> impl IntoResponse {
let chain = state.chain.lock().unwrap();
let balance = chain.get_wallet_balance(params.address);
Expand All @@ -94,18 +117,18 @@ pub async fn get_wallet_balance(
///
/// # Arguments
///
/// * `state` - The application state.
/// * `params` - The request query parameters.
/// - `state` - The application state.
/// - `params` - The request query parameters.
///
/// # Returns
///
/// The list of transactions of the wallet.
pub async fn get_wallet_transactions(
State(state): State<AppState>,
Query(params): Query<GetWalletBalance>,
Query(params): Query<GetWalletTransactionInput>,
) -> impl IntoResponse {
let chain = state.chain.lock().unwrap();
let transaction = chain.get_wallet_transactions(params.address);
let transaction = chain.get_wallet_transactions(params.address, params.page, params.size);

match transaction {
Some(transaction) => (StatusCode::OK, Json(json!({ "data": transaction }))),
Expand All @@ -120,14 +143,18 @@ pub async fn get_wallet_transactions(
///
/// # Arguments
///
/// * `state` - The application state.
/// - `state` - The application state.
/// - `params` - The request query parameters.
///
/// # Returns
///
/// All transactions.
pub async fn get_transactions(State(state): State<AppState>) -> impl IntoResponse {
let mut chain = state.chain.lock().unwrap();
let transactions = chain.get_transactions();
pub async fn get_transactions(
State(state): State<AppState>,
Query(params): Query<GetTransactionsInput>,
) -> impl IntoResponse {
let chain = state.chain.lock().unwrap();
let transactions = chain.get_transactions(params.page, params.size);

(StatusCode::OK, Json(json!({ "data": transactions })))
}
Expand All @@ -136,8 +163,8 @@ pub async fn get_transactions(State(state): State<AppState>) -> impl IntoRespons
///
/// # Arguments
///
/// * `state` - The application state.
/// * `hash` - The transaction hash.
/// - `state` - The application state.
/// - `hash` - The transaction hash.
///
/// # Returns
///
Expand All @@ -162,15 +189,15 @@ pub async fn get_transaction(
///
/// # Arguments
///
/// * `state` - The application state.
/// * `body` - The request body.
/// - `state` - The application state.
/// - `body` - The request body.
///
/// # Returns
///
/// The new transaction.
pub async fn add_transaction(
State(state): State<AppState>,
Json(body): Json<AddTransaction>,
Json(body): Json<AddTransactionInput>,
) -> impl IntoResponse {
let mut chain = state.chain.lock().unwrap();

Expand Down
4 changes: 2 additions & 2 deletions examples/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ fn main() -> std::io::Result<()> {
})
.interact()?;

let transactions = chain.get_wallet_transactions(address);
let transactions = chain.get_wallet_transactions(address, 0, 10);

match transactions {
Some(transactions) => println!("✅ Wallet transactions: {:?}", transactions),
Expand Down Expand Up @@ -189,7 +189,7 @@ fn main() -> std::io::Result<()> {
}
}
"get_transactions" => {
println!("📦 {:?}", chain.get_transactions());
println!("📦 {:?}", chain.get_transactions(0, 10));
}
"generate_block" => {
let res = chain.generate_new_block();
Expand Down
4 changes: 2 additions & 2 deletions src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ impl Block {
///
/// # Arguments
///
/// * `previous_hash` - The hash of the previous block.
/// * `difficulty` - The difficulty level of the network.
/// - `previous_hash` - The hash of the previous block.
/// - `difficulty` - The difficulty level of the network.
///
/// # Returns
///
Expand Down
50 changes: 43 additions & 7 deletions src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,27 @@ impl Chain {

/// Get a list of current transactions in the blockchain.
///
/// # Arguments
/// - `page`: The page number.
/// - `size`: The number of transactions per page.
///
/// # Returns
/// A reference to a vector containing the current transactions.
pub fn get_transactions(&mut self) -> &Vec<Transaction> {
&self.current_transactions
/// A reference to a vector containing the current transactions for the specified page.
pub fn get_transactions(&self, page: usize, size: usize) -> Vec<Transaction> {
// Calculate the total number of pages
let total_pages = (self.current_transactions.len() + size - 1) / size;

// Return an empty vector if the page is greater than the total number of pages
if page > total_pages {
return Vec::new();
}

// Calculate the start and end indices for the transactions of the current page
let start = page.saturating_sub(1) * size;
let end = start + size;

// Get the transactions for the current page
self.current_transactions[start..end.min(self.current_transactions.len())].to_vec()
}

/// Get a transaction by its hash.
Expand Down Expand Up @@ -200,10 +217,17 @@ impl Chain {
///
/// # Arguments
/// - `address`: The unique wallet address.
/// - `page`: The page number.
/// - `size`: The number of transactions per page.
///
/// # Returns
/// The wallet transaction history.
pub fn get_wallet_transactions(&self, address: String) -> Option<Vec<Transaction>> {
/// The wallet transaction history for the specified page.
pub fn get_wallet_transactions(
&self,
address: String,
page: usize,
size: usize,
) -> Option<Vec<Transaction>> {
match self
.wallets
.get(&address)
Expand All @@ -213,8 +237,20 @@ impl Chain {
Some(txs) => {
let mut result = Vec::new();

for tx in txs {
match self.get_transaction(tx) {
// Calculate the total number of pages
let total_pages = (self.current_transactions.len() + size - 1) / size;

// Return an empty vector if the page is greater than the total number of pages
if page > total_pages {
return Some(result);
}

// Calculate the start and end indices for the transactions of the current page
let start = page.saturating_sub(1) * size;
let end = start + size;

for tx in txs[start..end.min(txs.len())].iter() {
match self.get_transaction(tx.to_string()) {
Some(transaction) => result.push(transaction.to_owned()),
None => continue,
}
Expand Down
8 changes: 4 additions & 4 deletions src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ impl Transaction {
///
/// # Arguments
///
/// * `from` - The transaction sender address.
/// * `to` - The transaction receiver address.
/// * `fee` - The transaction fee.
/// * `amount` - The transaction amount.
/// - `from` - The transaction sender address.
/// - `to` - The transaction receiver address.
/// - `fee` - The transaction fee.
/// - `amount` - The transaction amount.
///
/// # Returns
///
Expand Down
6 changes: 3 additions & 3 deletions src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ impl Wallet {
///
/// # Arguments
///
/// * `email` - The email address associated with the wallet.
/// * `address` - The address uniquely identifying the wallet.
/// * `balance` - The current balance of the wallet.
/// - `email` - The email address associated with the wallet.
/// - `address` - The address uniquely identifying the wallet.
/// - `balance` - The current balance of the wallet.
///
/// # Returns
///
Expand Down
30 changes: 24 additions & 6 deletions tests/chain_integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ fn test_get_transactions() {
chain.add_transaction(from.clone(), to.clone(), 10.0);
chain.add_transaction(to.clone(), from.clone(), 20.0);

let transactions = chain.get_transactions();
let transactions = chain.get_transactions(0, 10);

assert_eq!(transactions.len(), 2);
assert_eq!(transactions[0].from, from);
Expand All @@ -163,9 +163,18 @@ fn test_get_transactions() {

#[test]
fn test_get_transactions_not_found() {
let mut chain = setup();
let chain = setup();

let transactions = chain.get_transactions(0, 10);

assert!(transactions.is_empty());
}

#[test]
fn test_get_transactions_empty_page() {
let chain = setup();

let transactions = chain.get_transactions();
let transactions = chain.get_transactions(10, 10);

assert!(transactions.is_empty());
}
Expand Down Expand Up @@ -210,7 +219,7 @@ fn test_get_wallet_transactions() {

chain.add_transaction(from.clone(), to.clone(), 10.0);

let transactions = chain.get_wallet_transactions(from).unwrap();
let transactions = chain.get_wallet_transactions(from, 0, 10).unwrap();

assert!(!transactions.is_empty());
}
Expand All @@ -221,7 +230,7 @@ fn test_get_new_wallet_transactions() {

let from = chain.create_wallet("s@mail.com".to_string());

let transactions = chain.get_wallet_transactions(from).unwrap();
let transactions = chain.get_wallet_transactions(from, 0, 10).unwrap();

assert!(transactions.is_empty());
}
Expand All @@ -230,7 +239,16 @@ fn test_get_new_wallet_transactions() {
fn test_get_wallet_transactions_not_found() {
let chain = setup();

let transactions = chain.get_wallet_transactions("address".to_string());
let transactions = chain.get_wallet_transactions("address".to_string(), 0, 10);

assert!(transactions.is_none());
}

#[test]
fn test_get_wallet_transactions_empty_page() {
let chain = setup();

let transactions = chain.get_wallet_transactions("address".to_string(), 10, 10);

assert!(transactions.is_none());
}
Expand Down

0 comments on commit 3cef01e

Please sign in to comment.