snap_coin/api/
api_server.rs1use std::sync::Arc;
2
3use futures::io;
4use thiserror::Error;
5use tokio::{
6 io::AsyncWriteExt,
7 net::{TcpListener, TcpStream},
8 sync::RwLock,
9};
10
11use crate::{
12 api::requests::{Request, Response},
13 blockchain_data_provider::BlockchainDataProvider,
14 economics::get_block_reward,
15 node::node::Node,
16};
17
18#[derive(Error, Debug)]
19pub enum ApiError {
20 #[error("")]
21 IOError(#[from] io::Error),
22}
23
24pub struct Server {
26 port: u32,
27 node: Arc<RwLock<Node>>,
28}
29
30impl Server {
31 pub fn new(port: u32, node: Arc<RwLock<Node>>) -> Self {
33 Server { port, node }
34 }
35
36 async fn connection(mut stream: TcpStream, node: Arc<RwLock<Node>>) {
38 loop {
39 if let Err(e) = async {
40 let request = Request::decode_from_stream(&mut stream).await?;
41 let response = match request {
42 Request::Height => Response::Height {
43 height: node.read().await.blockchain.get_height() as u64,
44 },
45 Request::Block { block_hash } => Response::Block {
46 block: node.read().await.blockchain.get_block_by_hash(&block_hash),
47 },
48 Request::BlockHash { height } => Response::BlockHash {
49 hash: node
50 .read()
51 .await
52 .blockchain
53 .get_block_hash_by_height(height as usize)
54 .copied(),
55 },
56 Request::Transaction { transaction_id } => {
57 let node_guard = node.read().await;
58 let mut found = None;
59
60 for block_hash in node_guard.blockchain.get_all_blocks() {
61 if let Some(block) = node_guard.blockchain.get_block_by_hash(block_hash)
62 {
63 for transaction in block.transactions {
64 if transaction.transaction_id.unwrap() == transaction_id {
65 found = Some(transaction);
66 break;
67 }
68 }
69 }
70 if found.is_some() {
71 break;
72 }
73 }
74
75 Response::Transaction { transaction: found }
76 }
77 Request::TransactionsOfAddress { address } => {
78 let node_guard = node.read().await;
79 let mut transactions = vec![];
80
81 for block_hash in node_guard.blockchain.get_all_blocks() {
82 if let Some(block) =
83 node_guard.blockchain.get_block_by_hash(block_hash)
84 {
85 for transaction in block.transactions {
86 if transaction.outputs.iter().any(|i| i.receiver == address) {
87 transactions.push(transaction.transaction_id.unwrap());
88 break;
89 }
90 }
91 }
92 }
93 Response::TransactionsOfAddress { transactions }
94 }
95 Request::AvailableUTXOs { address } => Response::AvailableUTXOs {
96 available_inputs: node
97 .read()
98 .await
99 .blockchain
100 .get_available_transaction_outputs(address).await?,
101 },
102 Request::Balance { address } => Response::Balance {
103 balance: node
104 .read()
105 .await
106 .blockchain
107 .get_utxos()
108 .calculate_confirmed_balance(address),
109 },
110 Request::Reward => Response::Reward {
111 reward: get_block_reward(node.read().await.blockchain.get_height()),
112 },
113 Request::Peers => {
114 let node_guard = node.read().await;
115
116 let mut peers = vec![];
117 for peer in &node_guard.peers {
118 peers.push(peer.read().await.address);
119 }
120 Response::Peers { peers }
121 }
122 Request::Mempool => Response::Mempool {
123 mempool: node.read().await.mempool.get_mempool().await,
124 },
125 Request::NewBlock { new_block } => Response::NewBlock {
126 status: Node::submit_block(node.clone(), new_block).await,
127 },
128 Request::NewTransaction { new_transaction } => Response::NewTransaction {
129 status: Node::submit_transaction(node.clone(), new_transaction).await,
130 },
131 Request::Difficulty => Response::Difficulty {
132 transaction_difficulty: node
133 .read()
134 .await
135 .blockchain
136 .get_transaction_difficulty(),
137 block_difficulty: node.read().await.blockchain.get_block_difficulty(),
138 },
139 Request::BlockHeight { hash } => Response::BlockHeight {
140 height: node.read().await.blockchain.get_height_by_hash(&hash),
141 },
142 };
143 let response_buf = response.encode()?;
144
145 stream.write_all(&response_buf).await?;
146
147 Ok::<(), anyhow::Error>(())
148 }
149 .await
150 {
151 Node::log(format!("API client error: {}", e));
152 break;
153 }
154 }
155 }
156
157 pub async fn listen(&self) -> Result<(), ApiError> {
159 let listener = match TcpListener::bind(format!("127.0.0.1:{}", self.port)).await {
160 Ok(l) => l,
161 Err(_) => TcpListener::bind("127.0.0.1:0").await?,
162 };
163 Node::log(format!(
164 "API Server listening on 127.0.0.1:{}",
165 listener.local_addr()?.port()
166 ));
167 let node = self.node.clone();
168 tokio::spawn(async move {
169 loop {
170 if let Err(e) = async {
171 let (stream, _) = listener.accept().await?;
172
173 tokio::spawn(Server::connection(stream, node.clone()));
174
175 Ok::<(), ApiError>(())
176 }
177 .await
178 {
179 Node::log(format!("API client failed to connect: {}", e))
180 }
181 }
182 });
183
184 Ok(())
185 }
186}