chia_query/peer/
translate.rs1use chia::protocol::{Bytes32, CoinState, HeaderBlock, Program, RespondAdditions, RespondRemovals};
5
6use crate::types::{
7 AdditionsAndRemovals, BlockRecord, ChiaQueryError, Coin, CoinRecord, CoinSpend, FeeEstimate,
8 TxStatus,
9};
10
11pub fn parse_hex(s: &str) -> Result<Vec<u8>, ChiaQueryError> {
16 let s = s.strip_prefix("0x").unwrap_or(s);
17 hex::decode(s).map_err(|e| ChiaQueryError::InvalidRequest(format!("bad hex: {e}")))
18}
19
20pub fn parse_bytes32(s: &str) -> Result<Bytes32, ChiaQueryError> {
21 let bytes = parse_hex(s)?;
22 let arr: [u8; 32] = bytes
23 .try_into()
24 .map_err(|_| ChiaQueryError::InvalidRequest("expected 32 bytes".into()))?;
25 Ok(Bytes32::new(arr))
26}
27
28pub fn hex32(b: &Bytes32) -> String {
29 format!("0x{}", hex::encode(b.as_ref()))
30}
31
32pub fn hex_bytes(b: &[u8]) -> String {
33 format!("0x{}", hex::encode(b))
34}
35
36pub fn coin_state_to_record(cs: &CoinState) -> CoinRecord {
41 CoinRecord {
42 coin: Coin::from_protocol(&cs.coin),
43 confirmed_block_index: cs.created_height.unwrap_or(0),
44 spent_block_index: cs.spent_height.unwrap_or(0),
45 spent: cs.spent_height.is_some(),
46 coinbase: false,
48 timestamp: 0,
49 }
50}
51
52pub fn coin_states_to_records(states: &[CoinState]) -> Vec<CoinRecord> {
53 states.iter().map(coin_state_to_record).collect()
54}
55
56pub fn make_coin_spend(
61 coin: &chia::protocol::Coin,
62 puzzle: &Program,
63 solution: &Program,
64) -> CoinSpend {
65 CoinSpend {
66 coin: Coin::from_protocol(coin),
67 puzzle_reveal: hex_bytes(puzzle.as_ref()),
68 solution: hex_bytes(solution.as_ref()),
69 }
70}
71
72pub fn make_fee_estimate(estimates: Vec<f64>, target_times: Vec<u64>) -> FeeEstimate {
78 FeeEstimate {
79 estimates,
80 target_times,
81 current_fee_rate: 0.0,
82 mempool_size: 0,
83 mempool_fees: 0,
84 mempool_max_size: 0,
85 num_spends: 0,
86 full_node_synced: false,
87 peak_height: 0,
88 last_peak_timestamp: 0,
89 last_block_cost: 0,
90 fees_last_block: 0,
91 fee_rate_last_block: 0.0,
92 last_tx_block_height: 0,
93 node_time_utc: 0,
94 }
95}
96
97pub fn ack_to_tx_status(status: u8) -> TxStatus {
102 let label = match status {
103 1 => "SUCCESS",
104 2 => "PENDING",
105 3 => "FAILED",
106 _ => "UNKNOWN",
107 };
108 TxStatus {
109 status: label.to_string(),
110 success: status == 1 || status == 2,
111 }
112}
113
114pub fn header_block_to_block_record(hb: &HeaderBlock) -> BlockRecord {
119 let rcb = &hb.reward_chain_block;
120 let foliage = &hb.foliage;
121
122 let timestamp = hb.foliage_transaction_block.as_ref().map(|ft| ft.timestamp);
123
124 BlockRecord {
125 header_hash: hex32(&foliage.reward_block_hash),
126 height: rcb.height,
127 weight: rcb.weight as u64,
128 prev_hash: hex32(&foliage.prev_block_hash),
129 total_iters: rcb.total_iters as u64,
130 signage_point_index: rcb.signage_point_index,
131 farmer_puzzle_hash: hex32(&foliage.foliage_block_data.farmer_reward_puzzle_hash),
132 pool_puzzle_hash: String::new(), timestamp,
134 fees: None, extra: serde_json::Value::Null,
136 }
137}
138
139pub fn additions_removals_to_response(
144 additions_resp: &RespondAdditions,
145 removals_resp: &RespondRemovals,
146 height: u32,
147) -> AdditionsAndRemovals {
148 let additions: Vec<CoinRecord> = additions_resp
149 .coins
150 .iter()
151 .flat_map(|(_ph, coins)| {
152 coins.iter().map(|c| CoinRecord {
153 coin: Coin::from_protocol(c),
154 confirmed_block_index: height,
155 spent_block_index: 0,
156 spent: false,
157 coinbase: false,
158 timestamp: 0,
159 })
160 })
161 .collect();
162
163 let removals: Vec<CoinRecord> = removals_resp
164 .coins
165 .iter()
166 .filter_map(|(_name, maybe_coin)| {
167 maybe_coin.as_ref().map(|c| CoinRecord {
168 coin: Coin::from_protocol(c),
169 confirmed_block_index: 0,
170 spent_block_index: height,
171 spent: true,
172 coinbase: false,
173 timestamp: 0,
174 })
175 })
176 .collect();
177
178 AdditionsAndRemovals {
179 additions,
180 removals,
181 }
182}