1use serde::{Serialize, Deserialize};
2use bitcoin::hashes::hex::ToHex;
3use bitcoin::{Script, TxIn, TxOut, Address, Network, AddressType, Transaction};
4
5use super::*;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct RestStatus {
9 pub blocks: i32,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct RestScriptSig {
14 pub asm: String,
15 pub hex: String,
16}
17
18impl RestScriptSig {
19 pub fn new(script: &Script) -> Self {
20 Self {
21 asm: script.asm(),
22 hex: hex::encode(script.as_bytes()),
23 }
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(rename_all = "camelCase")]
29pub struct RestVin {
30 pub txid: String,
31 pub vout: u32,
32 pub script_sig: RestScriptSig,
33 pub txinwitness: Vec<String>,
34 pub sequence: u32,
35 pub value: u64,
36 pub address: Option<String>,
37}
38
39impl RestVin {
40 pub fn new(txin: &TxIn, previous_txout: &Option<TxOut>, config: &Config) -> Self {
41 Self {
42 txid: txin.previous_output.txid.to_string(),
43 vout: txin.previous_output.vout,
44 script_sig: RestScriptSig::new(&txin.script_sig),
45 txinwitness: txin.witness.iter().map(hex::encode).collect(),
46 sequence: txin.sequence,
47 value: match previous_txout {
48 Some(pt) => pt.value,
49 None => 0,
50 },
51 address: match previous_txout {
52 Some(previous_txout) => script_to_address_string(&previous_txout.script_pubkey, config),
53 None => None,
54 },
55 }
56 }
57}
58
59#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
60pub struct RestScriptPubKey {
61 pub asm: String,
62 pub hex: String,
63 pub r#type: String,
64 pub address: Option<String>,
65}
66
67impl RestScriptPubKey {
68 pub fn new(script_pubkey: &Script, config: &Config) -> Self {
69 let address = Address::from_script(&script_pubkey, Network::Bitcoin );
70 let address_str = script_to_address_string(&script_pubkey, config);
71 Self {
72 asm: script_pubkey.asm(),
73 hex: hex::encode(script_pubkey.as_bytes()),
74 r#type: address.map_or("unknown", |address| address.address_type().map_or("unknown", |address_type| {
75 match address_type {
76 AddressType::P2pkh => "pubkeyhash",
77 AddressType::P2sh => "scripthash",
78 AddressType::P2wpkh => "witnesspubkeyhash",
79 AddressType::P2wsh => "witnessscripthash",
80 }
81 })).to_string(),
82 address: address_str,
83 }
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(rename_all = "camelCase")]
89pub struct RestUtxo {
90 pub txid: String,
91 pub vout: u32,
92 pub script_pub_key: RestScriptPubKey,
93 pub value: u64,
94}
95
96impl RestUtxo {
97 pub fn new(value: &UtxoServerValue, tx: &Transaction, config: &Config) -> Self {
98 let previous_output = &tx.output[value.vout as usize];
99 Self {
100 txid: value.txid.to_string(),
101 vout: value.vout,
102 script_pub_key: RestScriptPubKey::new(&previous_output.script_pubkey, config),
103 value: previous_output.value,
104 }
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase")]
110pub struct RestVout {
111 pub value: u64,
112 pub n: usize,
113 pub script_pub_key: RestScriptPubKey,
114}
115
116impl RestVout {
117 pub fn new(txout: &TxOut, n: usize, config: &Config) -> Self {
118 Self {
119 value: txout.value,
120 n,
121 script_pub_key: RestScriptPubKey::new(&txout.script_pubkey, config),
122 }
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127#[serde(rename_all = "camelCase")]
128pub struct RestTx {
129 pub confirmed_height: Option<u32>,
130 pub hex: String,
131 pub txid: String,
132 pub hash: String,
133 pub size: usize,
134 pub vsize: usize,
135 pub weight: usize,
136 pub version: i32,
137 pub locktime: u32,
138 pub vin: Vec<RestVin>,
139 pub vout: Vec<RestVout>,
140 pub fee: i64,
141 }
143
144#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
145pub struct RestBlockHeader {
146 pub height: u32,
147 pub header: String,
148 pub hash: String,
149 pub version: i32,
150 pub previousblockhash: String,
151 pub merkleroot: String,
152 pub time: u32,
153 pub bits: String,
154 pub difficulty: f64,
155 pub nonce: u32,
156 pub size: u32,
157 pub strippedsize: u32,
158 pub weight: u32,
159 pub ntxs: usize,
160}
161
162impl RestBlockHeader {
163 pub fn from_block_content(block_content: &BlockContentDBValue, config: &Config) -> Self {
164 let block_header = &block_content.block_header;
165 Self {
166 height : block_content.height,
167 header : hex::encode(consensus_encode(&block_header)),
168 hash : block_header.block_hash().to_string(),
169 version : block_header.version,
170 previousblockhash: block_header.prev_blockhash.to_string(),
171 merkleroot : block_header.merkle_root.to_string(),
172 time : block_header.time,
173 bits : format!("{:x}", block_header.bits),
174 difficulty : get_difficulty(block_header, config),
175 nonce : block_header.nonce,
176 size : block_content.size,
177 strippedsize : block_content.strippedsize,
178 weight : block_content.weight,
179 ntxs : block_content.txids.len(),
180 }
181 }
182}
183
184#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185pub struct RestBlockWithTxids {
186 pub height: u32,
187 pub header: String,
188 pub hash: String,
189 pub version: i32,
190 pub previousblockhash: String,
191 pub merkleroot: String,
192 pub time: u32,
193 pub bits: String,
194 pub difficulty: f64,
195 pub nonce: u32,
196 pub size: u32,
197 pub strippedsize: u32,
198 pub weight: u32,
199 pub txids: Vec<String>,
200}
201
202impl RestBlockWithTxids {
203 pub fn from_block_content(block_content: &BlockContentDBValue, config: &Config) -> Self {
204 let rest_block_header = RestBlockHeader::from_block_content(block_content, config);
205 Self {
206 height : rest_block_header.height,
207 header : rest_block_header.header,
208 hash : rest_block_header.hash,
209 version : rest_block_header.version,
210 previousblockhash: rest_block_header.previousblockhash,
211 merkleroot : rest_block_header.merkleroot,
212 time : rest_block_header.time,
213 bits : rest_block_header.bits,
214 difficulty : rest_block_header.difficulty,
215 nonce : rest_block_header.nonce,
216 size : rest_block_header.size,
217 strippedsize : rest_block_header.strippedsize,
218 weight : rest_block_header.weight,
219 txids : block_content.txids.iter().map(|txid| txid.to_hex()).collect::<Vec<String>>(),
220 }
221 }
222}
223
224#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
225pub struct RestBlockWithTxs {
226 pub height: u32,
227 pub header: String,
228 pub hash: String,
229 pub version: i32,
230 pub previousblockhash: String,
231 pub merkleroot: String,
232 pub time: u32,
233 pub bits: String,
234 pub difficulty: f64,
235 pub nonce: u32,
236 pub size: u32,
237 pub strippedsize: u32,
238 pub weight: u32,
239 pub txs: Vec<RestTx>,
240}
241
242impl RestBlockWithTxs {
243 pub fn from_block_content(tx_db: &TxDB, block_content: &BlockContentDBValue, config: &Config) -> Self {
244 let rest_block_header = RestBlockHeader::from_block_content(block_content, config);
245 let txs = block_content.txids.iter().map(|txid| {
248 tx_db.get_as_rest(txid, config).unwrap()
249 }).collect::<Vec<RestTx>>();
250 Self {
251 height : rest_block_header.height,
252 header : rest_block_header.header,
253 hash : rest_block_header.hash,
254 version : rest_block_header.version,
255 previousblockhash: rest_block_header.previousblockhash,
256 merkleroot : rest_block_header.merkleroot,
257 time : rest_block_header.time,
258 bits : rest_block_header.bits,
259 difficulty : rest_block_header.difficulty,
260 nonce : rest_block_header.nonce,
261 size : rest_block_header.size,
262 strippedsize : rest_block_header.strippedsize,
263 weight : rest_block_header.weight,
264 txs,
265 }
266 }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270#[serde(rename_all = "camelCase")]
271pub struct RestRichListEntry {
272 pub script_pub_key: RestScriptPubKey,
273 pub value: u64,
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
277pub struct RestBlockSummary {
278 pub hash : String,
279 pub time : u32,
280 pub nonce : u32,
281 pub size : u32,
282 pub strippedsize: u32,
283 pub weight : u32,
284 pub txcount : usize,
285}
286
287impl RestBlockSummary {
288 pub fn new(block: &BlockContentDBValue) -> Self {
289 Self {
290 hash : block.block_header.block_hash().to_string(),
291 time : block.block_header.time,
292 nonce : block.block_header.nonce,
293 size : block.size,
294 strippedsize: block.strippedsize,
295 weight : block.weight,
296 txcount : block.txids.len(),
297 }
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304 #[test]
305 fn rest() {
306 let tx_db = TxDB::new("test/rest", true);
307 let regtest_blocks = fixtures::regtest_blocks();
308 for (height, block) in regtest_blocks.iter().enumerate() {
309 for tx in block.txdata.iter() {
310 tx_db.put_tx(tx, Some(height as u32)).unwrap();
311 }
312 }
313 let config = config_example("rbtc");
314 let block_rest = RestBlockWithTxs::from_block_content(&tx_db, &BlockContentDBValue::new(102, ®test_blocks[102]), &config);
315 let block_rest_json = serde_json::to_string(&block_rest).unwrap();
316 println!("{}", block_rest_json);
317 let block_json = r#"{"height":102,"header":"00000020f4a34bc39e46acbf6ad1cd786d978718b4ef94002eb080e86f383b77798b8b1e197bc47e4d72c8cc02c78a6c89a45db03f9906c1a60448bbeecf7b84566fc66eca41e560ffff7f2001000000","hash":"25263a195c89fae46d08558b1b501617aac630a6854000e19eb7ada6c39e6e0b","version":536870912,"previousblockhash":"1e8b8b79773b386fe880b02e0094efb41887976d78cdd16abfac469ec34ba3f4","merkleroot":"6ec66f56847bcfeebb4804a6c106993fb05da4896c8ac702ccc8724d7ec47b19","time":1625637322,"bits":"207fffff","difficulty":4.6565423739069247e-10,"nonce":1,"size":842,"strippedsize":481,"weight":2285,"txs":[{"confirmedHeight":102,"hex":"020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff0401660101ffffffff02547a062a0100000016001497033ca70d45fe6d49310859e132a9df98f976250000000000000000266a24aa21a9edaaed4e9155661fb1a2bfc87c1458ab0eebc25bf8f0b8126ccc9214c407ebd34f0120000000000000000000000000000000000000000000000000000000000000000000000000","txid":"b903046c3fb719a2a404e3b749609414b4f914b9cbf2eadf289c8ed2120aaebc","hash":"cb01519e3eee67593f7f0eb955a82a3aa6fc1c83364c50e7c6891b14320d88c7","size":169,"vsize":142,"weight":568,"version":2,"locktime":0,"vin":[{"txid":"0000000000000000000000000000000000000000000000000000000000000000","vout":4294967295,"scriptSig":{"asm":"OP_PUSHBYTES_1 66 OP_PUSHBYTES_1 01","hex":"01660101"},"txinwitness":["0000000000000000000000000000000000000000000000000000000000000000"],"sequence":4294967295,"value":0,"address":null}],"vout":[{"value":5000034900,"n":0,"scriptPubKey":{"asm":"OP_0 OP_PUSHBYTES_20 97033ca70d45fe6d49310859e132a9df98f97625","hex":"001497033ca70d45fe6d49310859e132a9df98f97625","type":"witnesspubkeyhash","address":"bcrt1qjupnefcdghlx6jf3ppv7zv4fm7v0ja39dzzwvd"}},{"value":0,"n":1,"scriptPubKey":{"asm":"OP_RETURN OP_PUSHBYTES_36 aa21a9edaaed4e9155661fb1a2bfc87c1458ab0eebc25bf8f0b8126ccc9214c407ebd34f","hex":"6a24aa21a9edaaed4e9155661fb1a2bfc87c1458ab0eebc25bf8f0b8126ccc9214c407ebd34f","type":"unknown","address":null}}],"fee":-5000034900},{"confirmedHeight":102,"hex":"02000000000101592f96fe043aaa22cdcb6f6e710946aa0af25dff4536759f6873965555a6660c0000000000fdffffff02ecd90f2401000000160014629ef06211f8e75e223b6338228a025bd15b0db900e1f50500000000160014f0e9ede24bceb0c16fdce952279d8094dc6b3f5802473044022057f0dde0e8d094034f47a136ae22a433e0216d5b1cfa3a03d8d259660148744702204377d2dbf3e442128c03097af4d4fa56b52b0cc7d9c24511b99d45a47e8df2d7012102b934d90a0aa5e0b73e04d00df3a8633ee16d24dad9ab21697f1dc5feb43fdbed65000000","txid":"e6a6a7db6faadbfc0815fd3a78c3b5d15cb733dde8aca0e911b6f565ea24ee73","hash":"4fdf235851b72bb7ddc8f6d86e43797bb42e3601ee1eb1e8d8a0e157b3c81af6","size":222,"vsize":141,"weight":561,"version":2,"locktime":101,"vin":[{"txid":"0c66a655559673689f753645ff5df20aaa4609716e6fcbcd22aa3a04fe962f59","vout":0,"scriptSig":{"asm":"","hex":""},"txinwitness":["3044022057f0dde0e8d094034f47a136ae22a433e0216d5b1cfa3a03d8d259660148744702204377d2dbf3e442128c03097af4d4fa56b52b0cc7d9c24511b99d45a47e8df2d701","02b934d90a0aa5e0b73e04d00df3a8633ee16d24dad9ab21697f1dc5feb43fdbed"],"sequence":4294967293,"value":5000000000,"address":"bcrt1qjupnefcdghlx6jf3ppv7zv4fm7v0ja39dzzwvd"}],"vout":[{"value":4899985900,"n":0,"scriptPubKey":{"asm":"OP_0 OP_PUSHBYTES_20 629ef06211f8e75e223b6338228a025bd15b0db9","hex":"0014629ef06211f8e75e223b6338228a025bd15b0db9","type":"witnesspubkeyhash","address":"bcrt1qv200qcs3lrn4ug3mvvuz9zszt0g4krde3uqyzy"}},{"value":100000000,"n":1,"scriptPubKey":{"asm":"OP_0 OP_PUSHBYTES_20 f0e9ede24bceb0c16fdce952279d8094dc6b3f58","hex":"0014f0e9ede24bceb0c16fdce952279d8094dc6b3f58","type":"witnesspubkeyhash","address":"bcrt1q7r57mcjte6cvzm7ua9fz08vqjnwxk06c2v6jdv"}}],"fee":14100},{"confirmedHeight":102,"hex":"0200000000010273ee24ea65f5b611e9a0ace8dd33b75cd1b5c3783afd1508fcdbaa6fdba7a6e60000000000fdffffff73ee24ea65f5b611e9a0ace8dd33b75cd1b5c3783afd1508fcdbaa6fdba7a6e60100000000fdffffff02001110240100000016001413bb0bcb776f3e15fa8800877552044d4db27b96ac58f50500000000160014261b6555a3cb5d3593c0275ff46f31c36e42a4c70247304402207a89cf2b2d7620ace221894746c4a72f5fd5dd5bbe9e56018b182eaf046e9766022050a14136c20402b281c244a918856b9e866b5fc77182334dd6242993e2144b7d0121034968df50370db27d51b294cba769ec47e0accb3a582c312549303c555ca834d102473044022058a1f0d9b8bde83c28954a1cfac6a3f43f8f8b53529df47d0ef2f7d66f8f42bb022026993b9ba88bedff19ec3f70d32bab177d35e0570f8c86a9b2eac5b359189c1d012103d0d5b793a8a23ff2e92b6204c15f72fd51765ad6c01f6ebd9e712adee11bda4065000000","txid":"29704e049abbacf3222cd8af9654afe6c7c91d5d9f2faa5d2f0f3cd5de3a812d","hash":"fb3551865d89fae25b8bcc5a02da25ad91d6fe1c0d4fd391b4c7b1b69e9ece63","size":370,"vsize":208,"weight":832,"version":2,"locktime":101,"vin":[{"txid":"e6a6a7db6faadbfc0815fd3a78c3b5d15cb733dde8aca0e911b6f565ea24ee73","vout":0,"scriptSig":{"asm":"","hex":""},"txinwitness":["304402207a89cf2b2d7620ace221894746c4a72f5fd5dd5bbe9e56018b182eaf046e9766022050a14136c20402b281c244a918856b9e866b5fc77182334dd6242993e2144b7d01","034968df50370db27d51b294cba769ec47e0accb3a582c312549303c555ca834d1"],"sequence":4294967293,"value":4899985900,"address":"bcrt1qv200qcs3lrn4ug3mvvuz9zszt0g4krde3uqyzy"},{"txid":"e6a6a7db6faadbfc0815fd3a78c3b5d15cb733dde8aca0e911b6f565ea24ee73","vout":1,"scriptSig":{"asm":"","hex":""},"txinwitness":["3044022058a1f0d9b8bde83c28954a1cfac6a3f43f8f8b53529df47d0ef2f7d66f8f42bb022026993b9ba88bedff19ec3f70d32bab177d35e0570f8c86a9b2eac5b359189c1d01","03d0d5b793a8a23ff2e92b6204c15f72fd51765ad6c01f6ebd9e712adee11bda40"],"sequence":4294967293,"value":100000000,"address":"bcrt1q7r57mcjte6cvzm7ua9fz08vqjnwxk06c2v6jdv"}],"vout":[{"value":4900000000,"n":0,"scriptPubKey":{"asm":"OP_0 OP_PUSHBYTES_20 13bb0bcb776f3e15fa8800877552044d4db27b96","hex":"001413bb0bcb776f3e15fa8800877552044d4db27b96","type":"witnesspubkeyhash","address":"bcrt1qzwashjmhdulpt75gqzrh25syf4xmy7uk6clm0p"}},{"value":99965100,"n":1,"scriptPubKey":{"asm":"OP_0 OP_PUSHBYTES_20 261b6555a3cb5d3593c0275ff46f31c36e42a4c7","hex":"0014261b6555a3cb5d3593c0275ff46f31c36e42a4c7","type":"witnesspubkeyhash","address":"bcrt1qycdk24dredwnty7qya0lgme3cdhy9fx83qc9wd"}}],"fee":20800}]}"#;
318 assert_eq!(block_rest_json, block_json);
319 }
320}