bitcoind_client/
convert.rs1#![allow(missing_docs)]
2use std::convert::{TryFrom, TryInto};
3use std::str::FromStr;
4
5use bitcoin::block::Version;
6use bitcoin::consensus::encode;
7use bitcoin::hashes::hex::FromHex;
8use bitcoin::hashes::Hash;
9use bitcoin::{Block, BlockHash, CompactTarget, Work};
10use bitcoin::blockdata::block::Header as BlockHeader;
11use bitcoin::hash_types::TxMerkleNode;
12use serde::Deserialize;
13
14use crate::bitcoind_client::BlockHeaderData;
15
16pub struct JsonResponse(pub serde_json::Value);
18
19#[derive(Debug)]
21pub struct BlockchainInfo {
22 pub latest_height: usize,
23 pub latest_blockhash: BlockHash,
24}
25
26impl TryFrom<JsonResponse> for BlockchainInfo {
27 type Error = std::io::Error;
28 fn try_from(item: JsonResponse) -> std::io::Result<Self> {
29 Ok(Self {
30 latest_height: item.0["blocks"].as_u64().unwrap() as usize,
31 latest_blockhash: BlockHash::from_str(item.0["bestblockhash"].as_str().unwrap())
32 .unwrap(),
33 })
34 }
35}
36
37impl TryInto<Option<BlockHash>> for JsonResponse {
38 type Error = std::io::Error;
39
40 fn try_into(self) -> Result<Option<BlockHash>, Self::Error> {
41 match self.0.as_str() {
42 None => Ok(None),
43 Some(s) => Ok(Some(BlockHash::from_str(s).unwrap())),
44 }
45 }
46}
47
48pub fn hex_to_work(hex: &str) -> Result<Work, bitcoin::hashes::hex::HexToArrayError> {
50 let bytes = <[u8; 32]>::from_hex(hex)?;
51 Ok(Work::from_be_bytes(bytes))
52}
53
54#[derive(Deserialize)]
56struct GetHeaderResponse {
57 pub version: i32,
58 pub merkleroot: String,
59 pub time: u32,
60 pub nonce: u32,
61 pub bits: String,
62 pub previousblockhash: String,
63
64 pub chainwork: String,
65 pub height: u32,
66}
67
68impl TryFrom<GetHeaderResponse> for BlockHeaderData {
70 type Error = bitcoin::hashes::hex::HexToArrayError;
71
72 fn try_from(response: GetHeaderResponse) -> Result<Self, Self::Error> {
73 Ok(BlockHeaderData {
74 header: BlockHeader {
75 version: Version::from_consensus(response.version),
76 prev_blockhash: BlockHash::from_str(&response.previousblockhash)?,
77 merkle_root: TxMerkleNode::from_str(&response.merkleroot)?,
78 time: response.time,
79 bits: CompactTarget::from_unprefixed_hex(&response.bits).expect("error while parsing bits"),
80 nonce: response.nonce,
81 },
82 chainwork: hex_to_work(&response.chainwork)?,
83 height: response.height,
84 })
85 }
86}
87
88impl TryInto<BlockHeaderData> for JsonResponse {
91 type Error = std::io::Error;
92
93 fn try_into(self) -> std::io::Result<BlockHeaderData> {
94 let mut header = match self.0 {
95 serde_json::Value::Array(mut array) if !array.is_empty() => {
96 array.drain(..).next().unwrap()
97 }
98 serde_json::Value::Object(_) => self.0,
99 _ => {
100 return Err(std::io::Error::new(
101 std::io::ErrorKind::InvalidData,
102 "unexpected JSON type",
103 ))
104 }
105 };
106
107 if !header.is_object() {
108 return Err(std::io::Error::new(
109 std::io::ErrorKind::InvalidData,
110 "expected JSON object",
111 ));
112 }
113
114 if let None = header.get("previousblockhash") {
116 let hash: BlockHash = BlockHash::all_zeros();
117 header.as_object_mut().unwrap().insert(
118 "previousblockhash".to_string(),
119 serde_json::json!(hash.to_string()),
120 );
121 }
122
123 match serde_json::from_value::<GetHeaderResponse>(header) {
124 Err(_) => Err(std::io::Error::new(
125 std::io::ErrorKind::InvalidData,
126 "invalid header response",
127 )),
128 Ok(response) => match response.try_into() {
129 Err(_) => Err(std::io::Error::new(
130 std::io::ErrorKind::InvalidData,
131 "invalid header data",
132 )),
133 Ok(header) => Ok(header),
134 },
135 }
136 }
137}
138
139impl TryInto<Block> for JsonResponse {
141 type Error = std::io::Error;
142
143 fn try_into(self) -> std::io::Result<Block> {
144 match self.0.as_str() {
145 None => Err(std::io::Error::new(
146 std::io::ErrorKind::InvalidData,
147 "expected JSON string",
148 )),
149 Some(hex_data) => match Vec::<u8>::from_hex(hex_data) {
150 Err(_) => Err(std::io::Error::new(
151 std::io::ErrorKind::InvalidData,
152 "invalid hex data",
153 )),
154 Ok(block_data) => match encode::deserialize(&block_data) {
155 Err(_) => Err(std::io::Error::new(
156 std::io::ErrorKind::InvalidData,
157 "invalid block data",
158 )),
159 Ok(block) => Ok(block),
160 },
161 },
162 }
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169 use std::convert::TryFrom;
170
171 #[test]
172 fn header_parse_test() {
173 let response = GetHeaderResponse {
174 bits: "207fffff".to_string(),
175 version: 1,
176 previousblockhash: "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
177 merkleroot: "0000000000000000000000000000000000000000000000000000000000000000".to_string(),
178 time: 1231006505,
179 nonce: 2083236893,
180 chainwork: "000000000000000000000000000000000000000000000000000000000000000f".to_owned(),
181 height: 1,
182 };
183
184 BlockHeaderData::try_from(response).unwrap();
186 }
187}