rosetta_server_bitcoin/
lib.rs

1use anyhow::Result;
2use bitcoincore_rpc_async::{Auth, Client, RpcApi};
3use rosetta_server::crypto::address::Address;
4use rosetta_server::crypto::PublicKey;
5use rosetta_server::types::{
6    Block, BlockIdentifier, CallRequest, Coin, PartialBlockIdentifier, Transaction,
7    TransactionIdentifier,
8};
9use rosetta_server::{BlockchainClient, BlockchainConfig};
10use serde_json::Value;
11
12pub struct BitcoinClient {
13    config: BlockchainConfig,
14    client: Client,
15    genesis_block: BlockIdentifier,
16}
17
18#[async_trait::async_trait]
19impl BlockchainClient for BitcoinClient {
20    type MetadataParams = ();
21    type Metadata = ();
22
23    fn create_config(network: &str) -> Result<BlockchainConfig> {
24        rosetta_config_bitcoin::config(network)
25    }
26
27    async fn new(config: BlockchainConfig, addr: &str) -> Result<Self> {
28        let client = Client::new(
29            addr.to_string(),
30            Auth::UserPass("rosetta".into(), "rosetta".into()),
31        )
32        .await?;
33        let genesis = client.get_block_hash(0).await?;
34        let genesis_block = BlockIdentifier {
35            index: 0,
36            hash: hex::encode(genesis.as_ref()),
37        };
38        Ok(Self {
39            config,
40            client,
41            genesis_block,
42        })
43    }
44
45    fn config(&self) -> &BlockchainConfig {
46        &self.config
47    }
48
49    fn genesis_block(&self) -> &BlockIdentifier {
50        &self.genesis_block
51    }
52
53    async fn node_version(&self) -> Result<String> {
54        let info = self.client.get_network_info().await?;
55        let major = info.version / 10000;
56        let rest = info.version % 10000;
57        let minor = rest / 100;
58        let patch = rest % 100;
59        Ok(format!("{major}.{minor}.{patch}"))
60    }
61
62    async fn current_block(&self) -> Result<BlockIdentifier> {
63        let index = self.client.get_block_count().await?;
64        let hash = self.client.get_block_hash(index).await?;
65        Ok(BlockIdentifier {
66            index,
67            hash: hex::encode(hash.as_ref()),
68        })
69    }
70
71    async fn balance(&self, _address: &Address, _block: &BlockIdentifier) -> Result<u128> {
72        todo!()
73    }
74
75    async fn coins(&self, _address: &Address, _block: &BlockIdentifier) -> Result<Vec<Coin>> {
76        todo!()
77    }
78
79    async fn faucet(&self, _address: &Address, _value: u128) -> Result<Vec<u8>> {
80        todo!()
81    }
82
83    async fn metadata(
84        &self,
85        _public_key: &PublicKey,
86        _options: &Self::MetadataParams,
87    ) -> Result<Self::Metadata> {
88        Ok(())
89    }
90
91    async fn submit(&self, _transaction: &[u8]) -> Result<Vec<u8>> {
92        todo!()
93    }
94
95    async fn block(&self, _block: &PartialBlockIdentifier) -> Result<Block> {
96        anyhow::bail!("not implemented")
97    }
98
99    async fn block_transaction(
100        &self,
101        _block: &BlockIdentifier,
102        _tx: &TransactionIdentifier,
103    ) -> Result<Transaction> {
104        anyhow::bail!("not implemented")
105    }
106
107    async fn call(&self, _req: &CallRequest) -> Result<Value> {
108        anyhow::bail!("not implemented")
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[tokio::test]
117    async fn test_network_list() -> Result<()> {
118        let config = rosetta_config_bitcoin::config("regtest")?;
119        rosetta_server::tests::network_list(config).await
120    }
121
122    #[tokio::test]
123    async fn test_network_options() -> Result<()> {
124        let config = rosetta_config_bitcoin::config("regtest")?;
125        rosetta_server::tests::network_options::<BitcoinClient>(config).await
126    }
127
128    #[tokio::test]
129    async fn test_network_status() -> Result<()> {
130        let config = rosetta_config_bitcoin::config("regtest")?;
131        rosetta_server::tests::network_status::<BitcoinClient>(config).await
132    }
133
134    #[tokio::test]
135    #[ignore]
136    async fn test_account() -> Result<()> {
137        let config = rosetta_config_bitcoin::config("regtest")?;
138        rosetta_server::tests::account(config).await
139    }
140
141    #[tokio::test]
142    #[ignore]
143    async fn test_construction() -> Result<()> {
144        let config = rosetta_config_bitcoin::config("regtest")?;
145        rosetta_server::tests::construction(config).await
146    }
147}