snap_coin/api/
api_server.rs

1use 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
24/// Server for hosting a Snap Coin API
25pub struct Server {
26    port: u32,
27    node: Arc<RwLock<Node>>,
28}
29
30impl Server {
31    /// Create a new server, do not listen for connections yet
32    pub fn new(port: u32, node: Arc<RwLock<Node>>) -> Self {
33        Server { port, node }
34    }
35
36    /// Handle a incoming connection
37    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    /// Start listening for clients
158    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}