bitcoind_request/command/get_block.rs
1use crate::client::Client;
2/*
3getblock "blockhash" ( verbosity )
4
5If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.
6If verbosity is 1, returns an Object with information about block <hash>.
7If verbosity is 2, returns an Object with information about block <hash> and information about each transaction.
8
9Arguments:
101. blockhash (string, required) The block hash
112. verbosity (numeric, optional, default=1) 0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data
12
13Result (for verbosity = 0):
14"hex" (string) A string that is serialized, hex-encoded data for block 'hash'
15
16Result (for verbosity = 1):
17{ (json object)
18 "hash" : "hex", (string) the block hash (same as provided)
19 "confirmations" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain
20 "size" : n, (numeric) The block size
21 "strippedsize" : n, (numeric) The block size excluding witness data
22 "weight" : n, (numeric) The block weight as defined in BIP 141
23 "height" : n, (numeric) The block height or index
24 "version" : n, (numeric) The block version
25 "versionHex" : "hex", (string) The block version formatted in hexadecimal
26 "merkleroot" : "hex", (string) The merkle root
27 "tx" : [ (json array) The transaction ids
28 "hex", (string) The transaction id
29 ...
30 ],
31 "time" : xxx, (numeric) The block time expressed in UNIX epoch time
32 "mediantime" : xxx, (numeric) The median block time expressed in UNIX epoch time
33 "nonce" : n, (numeric) The nonce
34 "bits" : "hex", (string) The bits
35 "difficulty" : n, (numeric) The difficulty
36 "chainwork" : "hex", (string) Expected number of hashes required to produce the chain up to this block (in hex)
37 "nTx" : n, (numeric) The number of transactions in the block
38 "previousblockhash" : "hex", (string) The hash of the previous block
39 "nextblockhash" : "hex" (string) The hash of the next block
40}
41
42Result (for verbosity = 2):
43{ (json object)
44 ..., Same output as verbosity = 1
45 "tx" : [ (json array)
46 { (json object)
47 ... The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 "tx" result
48 },
49 ...
50 ]
51}
52
53Examples:
54> bitcoin-cli getblock "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"
55> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblock", "params": ["00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09"]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
56*/
57use crate::command::{request::request, CallableCommand};
58use crate::Blockhash;
59use serde::{Deserialize, Serialize};
60use serde_json::value::to_raw_value;
61
62#[derive(Serialize, Deserialize, Debug)]
63#[serde(rename_all = "camelCase")]
64#[serde(untagged)]
65pub enum Vin {
66 Coinbase(CoinbaseVin),
67 NonCoinbase(NonCoinbaseVin),
68}
69
70#[derive(Serialize, Deserialize, Debug)]
71pub struct HexEncodedWitnessData(pub String);
72
73#[derive(Serialize, Deserialize, Debug)]
74#[serde(rename_all = "camelCase")]
75pub struct CoinbaseVin {
76 pub coinbase: String,
77 pub sequence: u64, // The script sequence number
78 pub txinwitness: Option<Vec<HexEncodedWitnessData>>, // hex-encoded witness data (if any)
79}
80
81#[derive(Serialize, Deserialize, Debug)]
82#[serde(rename_all = "camelCase")]
83pub struct ScriptSig {
84 pub asm: String, // "asm", NOT A HEX
85 pub hex: String, // "hex", hex
86}
87
88#[derive(Serialize, Deserialize, Debug)]
89#[serde(rename_all = "camelCase")]
90pub struct NonCoinbaseVin {
91 pub txid: String, // "hex" The transaction id
92 pub vout: u64, // The output number
93 pub script_sig: ScriptSig,
94 pub sequence: u64, // The script sequence number
95 // TODO: Why is this optional?
96 pub txinwitness: Option<Vec<HexEncodedWitnessData>>,
97}
98
99#[derive(Serialize, Deserialize, Debug)]
100pub struct ScriptPubKey {
101 pub asm: String,
102 pub hex: String,
103 pub address: Option<String>,
104 // TODO: Can't use "type" as a key because it's a reserved word in Rust.
105 #[serde(rename = "type")]
106 pub type_: String,
107}
108#[derive(Serialize, Deserialize, Debug)]
109#[serde(rename_all = "camelCase")]
110pub struct Vout {
111 pub value: f64,
112 pub n: i64,
113 pub script_pub_key: ScriptPubKey,
114}
115#[derive(Serialize, Deserialize, Debug)]
116pub struct DecodeRawTransactionResponse {
117 pub in_active_chain: Option<bool>,
118 pub hex: String, // "hex" The serialized, hex-encoded data for 'txid'
119 pub txid: String, // "hex" The transaction id (same as provided)
120 pub hash: String, // "hex" The transaction hash (differs from txid for witness transactions)
121 pub size: u64, // The serialized transaction size
122 pub vsize: u64, // The virtual transaction size (differs from size for witness transactions)
123 pub weight: u64, // The transaction's weight (between vsize*4-3 and vsize*4)
124 pub version: u64, // The version
125 pub locktime: u64, // The lock time
126 pub vin: Vec<Vin>,
127 pub vout: Vec<Vout>,
128}
129
130// TODO: I don't think this belongs in this package. We should focus on RPC request and responses
131// and abstract a better data layer into another package.
132impl DecodeRawTransactionResponse {
133 pub fn is_coinbase_transaction(&self) -> bool {
134 match self.vin.first().unwrap() {
135 Vin::Coinbase(_x) => true,
136 Vin::NonCoinbase(_x) => false,
137 }
138 }
139}
140
141#[derive(Serialize, Deserialize, Debug)]
142#[serde(untagged)]
143pub enum GetBlockCommandTransactionResponse {
144 Raw(DecodeRawTransactionResponse),
145 Id(String),
146}
147
148#[derive(Serialize, Deserialize, Debug)]
149#[serde(untagged)]
150pub enum GetBlockCommandResponse {
151 BlockHash(String),
152 Block(Block),
153}
154
155#[derive(Serialize, Deserialize, Debug)]
156#[serde(rename_all = "camelCase")]
157pub struct Block {
158 pub hash: String, // "hex" (string) the block hash (same as provided)
159 pub confirmations: i64, // The number of confirmations, or -1 if the block is not on the main chain
160 pub size: u64, // The block size
161 pub strippedsize: u64, // The block size excluding witness data
162 pub weight: u64, // The block weight as defined in BIP 141
163 pub height: u64, // The block height or index
164 pub version: u64, // (numeric) The block version
165 pub version_hex: String, // "hex" The block version formatted in hexadecimal
166 pub merkleroot: String, // "hex" The merkle root
167 pub tx: Vec<GetBlockCommandTransactionResponse>, // "hex" The transaction ids
168 pub time: u64, // "unix epoch time" The block time expressed in UNIX epoch time
169 pub mediantime: u64, // "unix epoch time" The median block time expressed in UNIX epoch time
170 pub nonce: u64, // The nonce
171 pub bits: String, // "hex" The bits
172 pub difficulty: f64, // The difficulty
173 pub chainwork: String, // "hex" Expected number of hashes required to produce the chain up to this block (in hex)
174 pub n_tx: u64, // The number of transactions in the block
175 pub previousblockhash: Option<String>, // The hash of the previous block
176 // TODO: Why isn't this always there?
177 pub nextblockhash: Option<String>, // The hash of the next block
178}
179
180type GetBlockAsSerializedHextEncodedDataCommandResponse = String;
181
182pub enum GetBlockCommandVerbosity {
183 SerializedHexEncodedData, // argument of 0
184 BlockObjectWithoutTransactionInformation, // argument of 1
185 BlockObjectWithTransactionInformation, // argument of 2
186}
187
188pub struct GetBlockCommand {
189 blockhash: Blockhash,
190 verbosity: GetBlockCommandVerbosity,
191}
192impl GetBlockCommand {
193 pub fn new(blockhash: Blockhash) -> Self {
194 GetBlockCommand {
195 blockhash,
196 verbosity: GetBlockCommandVerbosity::BlockObjectWithoutTransactionInformation,
197 }
198 }
199 pub fn verbosity(mut self, verbosity: GetBlockCommandVerbosity) -> Self {
200 self.verbosity = verbosity;
201 self
202 }
203}
204
205// TODO: This will only work for GetBlockCommandVerbosity::BlockObjectWithoutTransactionInformation
206// because the json response has a different structure it returns for each verbosity option.
207// For example, GetBlockCommandVerbosity::BlockObjectWithTransactionInformation will return
208// an array for 'tx' field with full transaction structure, instead of only hashes for the
209// transaction. To accomplish this, we need to figure out how to have serde handle
210// conditional responses and map them to appropriate structs.
211impl CallableCommand for GetBlockCommand {
212 type Response = GetBlockCommandResponse;
213 fn call(&self, client: &Client) -> Result<Self::Response, jsonrpc::Error> {
214 let verbosity_arg = match self.verbosity {
215 GetBlockCommandVerbosity::SerializedHexEncodedData => 0,
216 GetBlockCommandVerbosity::BlockObjectWithoutTransactionInformation => 1,
217 GetBlockCommandVerbosity::BlockObjectWithTransactionInformation => 2,
218 };
219 let blockhash_arg = &self.blockhash.0;
220 let blockhash_arg_raw_value = to_raw_value(&blockhash_arg).unwrap();
221 let verbosity_arg_raw_value = to_raw_value(&verbosity_arg).unwrap();
222 let command = "getblock";
223 let params = vec![blockhash_arg_raw_value, verbosity_arg_raw_value];
224 let r = request(client, command, params);
225 let response: GetBlockCommandResponse = r.result()?;
226 Ok(response)
227 }
228}