Skip to content

Commit

Permalink
feat(chain): restructure the crate
Browse files Browse the repository at this point in the history
  • Loading branch information
slavik-pastushenko committed Feb 18, 2024
1 parent 7f54b32 commit 4268adf
Show file tree
Hide file tree
Showing 13 changed files with 956 additions and 732 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ jobs:
- uses: actions/checkout@v4
- name: Install tarpaulin
run: cargo install cargo-tarpaulin
- name: Build
run: cargo build --verbose
- name: Run build
run: cargo build
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run lint
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ name = "blockchain"
path = "src/lib.rs"

[dependencies]
chrono = "0.4.31"
chrono = "0.4.34"
rand = "0.8.5"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
sha2 = "0.10.8"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Slavik Pastushenko
Copyright (c) 2024 Slavik Pastushenko

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
64 changes: 63 additions & 1 deletion examples/api-axum/src/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::{Arc, Mutex};

use axum::{
extract::{Path, Query, State},
http::StatusCode,
Expand All @@ -7,30 +9,51 @@ use axum::{
use blockchain::Chain;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::{Arc, Mutex};

/// The application state.
#[derive(Clone)]
pub struct AppState {
/// The blockchain.
pub chain: Arc<Mutex<Chain>>,
}

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

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

/// The receiver address.
pub to: String,

/// The transaction amount.
pub amount: f64,
}

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

/// Create a new wallet.
///
/// # Arguments
///
/// * `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>,
Expand All @@ -41,6 +64,16 @@ pub async fn create_wallet(
(StatusCode::OK, Json(json!({ "data": address })))
}

/// Get the balance of a wallet.
///
/// # Arguments
///
/// * `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>,
Expand All @@ -58,13 +91,32 @@ pub async fn get_wallet_balance(
(StatusCode::OK, Json(json!({ "data": balance })))
}

/// Get all transactions.
///
/// # Arguments
///
/// * `state` - The application state.
///
/// # 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();

(StatusCode::OK, Json(json!({ "data": transactions })))
}

/// Get a transaction.
///
/// # Arguments
///
/// * `state` - The application state.
/// * `hash` - The transaction hash.
///
/// # Returns
///
/// The transaction.
pub async fn get_transaction(
State(state): State<AppState>,
Path(hash): Path<String>,
Expand All @@ -82,6 +134,16 @@ pub async fn get_transaction(
(StatusCode::OK, Json(json!({ "data": transaction })))
}

/// Add a new transaction.
///
/// # Arguments
///
/// * `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>,
Expand Down
12 changes: 7 additions & 5 deletions examples/api-axum/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use crate::handlers::AppState;
use std::{
net::SocketAddr,
sync::{Arc, Mutex},
};

use axum::{
routing::{get, post},
Router,
};
use blockchain::Chain;
use std::{
net::SocketAddr,
sync::{Arc, Mutex},
};

use crate::handlers::AppState;

mod handlers;

Expand Down
1 change: 1 addition & 0 deletions examples/cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use blockchain::Chain;
use cliclack::spinner;

/// The main function.
fn main() -> std::io::Result<()> {
cliclack::clear_screen()?;

Expand Down
114 changes: 114 additions & 0 deletions src/block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use chrono::Utc;
use serde::{Deserialize, Serialize};

use crate::{Chain, Transaction};

/// Identifier of a particular block on an entire blockchain.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockHeader {
/// Timestamp at which a block was mined.
pub timestamp: i64,

/// Integer to achieve the network's difficulty.
pub nonce: u32,

/// Hash of a previous block.
pub previous_hash: String,

/// Merkel root hash.
pub merkle: String,

/// Current difficulty level of the network.
pub difficulty: f64,
}

/// Data storage in a blockchain.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Block {
/// Information about the block and the miner.
pub header: BlockHeader,

/// Total amount of transactions.
pub count: usize,

/// An amount of transactions.
pub transactions: Vec<Transaction>,
}

impl Block {
/// Create a new block.
///
/// # Arguments
///
/// * `previous_hash` - The hash of the previous block.
/// * `difficulty` - The difficulty level of the network.
///
/// # Returns
///
/// A new block with the given previous hash and difficulty.
pub fn new(previous_hash: String, difficulty: f64) -> Self {
// Create a new block header
let header = BlockHeader {
nonce: 0,
difficulty,
previous_hash,
merkle: String::new(),
timestamp: Utc::now().timestamp(),
};

// Create a new block
Block {
header,
count: 0,
transactions: vec![],
}
}

/// Perform the proof-of-work process to mine a block.
///
/// # Arguments
/// - `header`: A mutable reference to the block header to be mined.
pub fn proof_of_work(header: &mut BlockHeader) {
loop {
let hash = Chain::hash(header);
let slice = &hash[..header.difficulty as usize];

match slice.parse::<u32>() {
Ok(val) => {
if val != 0 {
header.nonce += 1;
} else {
break;
}
}
Err(_) => {
header.nonce += 1;

continue;
}
};
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_proof_of_work() {
let mut block = Block::new("0".to_string(), 1.0);
Block::proof_of_work(&mut block.header);

assert_eq!(block.header.difficulty, 1.0);
assert!(!block.header.previous_hash.is_empty());
}

#[test]
fn test_new_block() {
let block = Block::new("0".to_string(), 3.0);

assert_eq!(block.count, 0);
assert_eq!(block.transactions.len(), 0);
}
}
Loading

0 comments on commit 4268adf

Please sign in to comment.