diff options
| author | littleblack111 <littleblack11111@gmail.com> | 2025-10-20 23:05:54 +0800 |
|---|---|---|
| committer | littleblack111 <littleblack11111@gmail.com> | 2025-10-20 23:05:54 +0800 |
| commit | ff590ac70e7a858a64d7a4835a22f6d94b31b886 (patch) | |
| tree | 393b93be66011f43083d9e789434416402ed95b1 /src | |
init
Diffstat (limited to 'src')
| -rw-r--r-- | src/blocks.rs | 88 | ||||
| -rw-r--r-- | src/client.rs | 15 | ||||
| -rw-r--r-- | src/lib.rs | 9 | ||||
| -rw-r--r-- | src/main.rs | 16 | ||||
| -rw-r--r-- | src/network.rs | 205 | ||||
| -rw-r--r-- | src/transaction.rs | 10 |
6 files changed, 343 insertions, 0 deletions
diff --git a/src/blocks.rs b/src/blocks.rs new file mode 100644 index 0000000..39abde2 --- /dev/null +++ b/src/blocks.rs @@ -0,0 +1,88 @@ +use std::{ + hash::{DefaultHasher, Hash, Hasher}, + ops::Deref, +}; + +use serde::{Deserialize, Serialize}; + +use crate::{ZERO_PREFIX_AMOUNT, transaction::Transaction}; + +#[derive(Deserialize, Serialize, Clone, Copy)] +pub struct Block { + pub prev_hash: u64, + pub trans: Transaction, + pub pow: Option<u64>, +} + +impl Block { + fn new(prev_hash: Option<u64>, trans: Transaction) -> Self { + Self { + prev_hash: prev_hash.unwrap_or(0), // special blocks + trans, + pow: None, + } + } + + fn calc_pow(&self) -> u64 { + for i in 0.. { + // reset every loop + let mut hasher = DefaultHasher::new(); + (&self.prev_hash, &self.trans, i).hash(&mut hasher); + let hashed = hasher.finish(); + if hashed + .to_string() + .starts_with( + &"0".repeat(ZERO_PREFIX_AMOUNT) + .to_string(), + ) + { + return hashed; + } + } + unreachable!() + } + + pub fn calc_set_pow(&mut self) { + self.pow = Some(self.calc_pow()); + } +} + +impl Hash for Block { + fn hash<H: Hasher>(&self, state: &mut H) { + self.prev_hash + .hash(state); + self.trans + .hash(state); + if self + .pow + .is_none() + { + Self::calc_pow(self).hash(state); + } else { + self.pow + .hash(state); + } + } +} + +#[derive(Default, Deserialize, Serialize, Clone)] +pub struct BlockChain { + blocks: Vec<Block>, +} + +impl BlockChain { + pub fn new(blocks: Vec<Block>) -> Self { + Self { + blocks, + } + } + + pub fn store(&mut self, block: Block) {} +} + +impl Deref for BlockChain { + type Target = Vec<Block>; + fn deref(&self) -> &Self::Target { + &self.blocks + } +} diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..7919b94 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,15 @@ +use rand::Rng; +use serde::{Deserialize, Serialize}; + +#[derive(Hash, Deserialize, Serialize, Clone, Copy)] +pub struct Client { + id: u32, +} + +impl Default for Client { + fn default() -> Self { + Self { + id: rand::rng().random(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0545aa5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,9 @@ +pub mod blocks; + +pub mod transaction; + +pub mod client; + +pub mod network; + +const ZERO_PREFIX_AMOUNT: usize = 5; // TODO: make this dynamic diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..096b533 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,16 @@ +use std::{sync::Arc, thread::sleep, time::Duration}; + +use bitcoin::{blocks::BlockChain, network::Network}; +use tokio::sync::Mutex; + +#[tokio::main] +async fn main() { + let bc = Arc::new(Mutex::new(BlockChain::default())); + let network = Network::new(bc).await; + network + .lock() + .await + .broadcast(bitcoin::network::Request::Ibd(None)) + .await; + sleep(Duration::from_secs(100)); +} diff --git a/src/network.rs b/src/network.rs new file mode 100644 index 0000000..82190a5 --- /dev/null +++ b/src/network.rs @@ -0,0 +1,205 @@ +use std::{ + net::SocketAddr, + sync::{Arc, Weak}, +}; + +use serde::{Deserialize, Serialize}; + +use futures::{SinkExt, StreamExt}; +use tokio::{ + net::{TcpListener, TcpStream}, + spawn, + sync::Mutex, +}; +use tokio_serde::{Framed, formats::Json}; +use tokio_util::codec::Framed as TokioFramed; +use tokio_util::codec::LengthDelimitedCodec; + +use crate::{ + blocks::{Block, BlockChain}, + client::Client, +}; + +#[derive(Deserialize, Serialize, Clone)] +pub enum Request { + Block(Block), + Ibd(Option<BlockChain>), +} + +pub struct Network { + this: Weak<Mutex<Self>>, + me: Client, + listener: TcpListener, + peers: Vec<Arc<Mutex<Peer>>>, + blockchain: Arc<Mutex<BlockChain>>, +} + +impl Network { + pub async fn new(blockchain: Arc<Mutex<BlockChain>>) -> Arc<Mutex<Self>> { + let this = Arc::new(Mutex::new(Self { + me: Client::default(), + this: Weak::new(), + listener: TcpListener::bind("0.0.0.0:6767") + .await + .unwrap(), + peers: Vec::default(), + blockchain, + })); + this.lock() + .await + .this = Arc::downgrade(&this); + this + } + + async fn try_peer(&mut self) { + self.peers + .push(Arc::new(Mutex::new(Peer::new( + self.this + .clone(), + TcpStream::connect("192.168.1.100") + .await + .unwrap(), + )))) + } + + async fn start(&self) { + let this = self + .this + .clone(); + spawn(async move { + if let Some(parent) = this.upgrade() { + let mut net = parent + .lock() + .await; + net.listen() + .await; + } + }); + } + + async fn listen(&mut self) -> ! { + loop { + let (stream, addr) = self + .listener + .accept() + .await + .unwrap(); + + let peer = Arc::new(Mutex::new(Peer::new( + self.this + .clone(), + stream, + ))); + + self.peers + .push(Arc::clone(&peer)); + + spawn(async move { + peer.lock() + .await + .start() + .await; + }); + } + } + + pub async fn broadcast(&mut self, data: Request) { + for p in &self.peers { + p.lock() + .await + .framed + .send(data.clone()) + .await + .unwrap(); + } + } +} + +struct Peer { + parent: Weak<Mutex<Network>>, + framed: Framed<TokioFramed<TcpStream, LengthDelimitedCodec>, Request, Request, Json<Request, Request>>, +} + +impl Peer { + fn new(parent: Weak<Mutex<Network>>, stream: TcpStream) -> Self { + Self { + parent, + framed: Framed::new(TokioFramed::new(stream, LengthDelimitedCodec::new()), Json::default()), + } + } + + async fn start(&mut self) { + loop { + while let Some(req) = self + .framed + .next() + .await + { + self.handle(req.unwrap()) + .await; + } + } + } + + async fn handle(&mut self, req: Request) { + match req { + Request::Block(mut block) => match block.pow { + Some(_) => self + .parent + .upgrade() + .unwrap() + .lock() + .await + .blockchain + .lock() + .await + .store(block), + None => { + block.calc_set_pow(); + let parent = self + .parent + .upgrade() + .unwrap(); + let mut net = parent + .lock() + .await; + net.broadcast(Request::Block(block)) + .await; + } + }, + Request::Ibd(bc) => match bc { + Some(_bc) => { + let parent = self + .parent + .upgrade() + .unwrap(); + let net = parent + .lock() + .await; + let mut _bc_lock = net + .blockchain + .lock() + .await; + } + None => { + let parent = self + .parent + .upgrade() + .unwrap(); + let mut net = parent + .lock() + .await; + let bc = { + let guard = net + .blockchain + .lock() + .await; + guard.clone() + }; + net.broadcast(Request::Ibd(Some(bc))) + .await; + } + }, + } + } +} diff --git a/src/transaction.rs b/src/transaction.rs new file mode 100644 index 0000000..4baf60f --- /dev/null +++ b/src/transaction.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; + +use crate::client::Client; + +#[derive(Hash, Deserialize, Serialize, Clone, Copy)] +pub struct Transaction { + from: Client, + to: Client, + amount: u32, +} |
