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