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}