Skip to content

Commit

Permalink
getblock: return tx objects with verbosity=2
Browse files Browse the repository at this point in the history
  • Loading branch information
conradoplg committed Dec 12, 2024
1 parent 568b25e commit b290ce7
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 32 deletions.
82 changes: 61 additions & 21 deletions zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,7 @@ where
None
};

let self_clone = self.clone();
async move {
let hash_or_height: HashOrHeight = hash_or_height.parse().map_server_error()?;

Expand Down Expand Up @@ -839,11 +840,30 @@ where
zebra_state::ReadResponse::TransactionIdsForBlock(tx_ids) => tx_ids
.ok_or_server_error("Block not found")?
.iter()
.map(|tx_id| tx_id.encode_hex())
.map(|tx_id| GetBlockTransaction::Hash(*tx_id))
.collect(),
_ => unreachable!("unmatched response to a transaction_ids_for_block request"),
};

let tx = if verbosity >= 2 {
let mut tx_obj_vec = vec![];
for txid in tx {
let GetBlockTransaction::Hash(txid) = txid else {
unreachable!("must be a Hash")
};
let get_tx_result: Result<GetRawTransaction> = self_clone
.get_raw_transaction(txid.encode_hex(), Some(1))
.await;
let GetRawTransaction::Object(tx_obj) = get_tx_result? else {
unreachable!("must return Object");
};
tx_obj_vec.push(GetBlockTransaction::Object(tx_obj));
}
tx_obj_vec
} else {
tx
};

let orchard_tree_response = futs.next().await.expect("`futs` should not be empty");
let zebra_state::ReadResponse::OrchardTree(orchard_tree) =
orchard_tree_response.map_server_error()?
Expand Down Expand Up @@ -1771,11 +1791,9 @@ pub enum GetBlock {

// `chainhistoryroot` would be here. Undocumented. TODO: decide if we want to support it
//
/// List of transaction IDs in block order, hex-encoded.
//
// TODO: use a typed Vec<transaction::Hash> here
// TODO: support Objects
tx: Vec<String>,
/// List of transactions in block order, hex-encoded if verbosity=1 or
/// as objects if verbosity=2.
tx: Vec<GetBlockTransaction>,

/// The height of the requested block.
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -1803,7 +1821,7 @@ pub enum GetBlock {
difficulty: Option<f64>,

// `chainwork` would be here, but we don't plan on supporting it
// `anchor` would be here. Undocumented. TODO: decide if we want to support it
// `anchor` would be here. Not planned to be supported.
// `chainSupply` would be here, TODO: implement
// `valuePools` would be here, TODO: implement
//
Expand Down Expand Up @@ -1844,6 +1862,17 @@ impl Default for GetBlock {
}
}

#[derive(Clone, Debug, PartialEq, serde::Serialize)]
#[serde(untagged)]
/// The transaction list in a `getblock` call. Can be a list of transaction
/// IDs or the full transaction details depending on verbosity.
pub enum GetBlockTransaction {
/// The transaction hash, hex-encoded.
Hash(#[serde(with = "hex")] transaction::Hash),
/// The block object.
Object(TransactionObject),
}

/// Response to a `getblockheader` RPC request.
///
/// See the notes for the [`Rpc::get_block_header`] method.
Expand Down Expand Up @@ -1987,22 +2016,33 @@ pub enum GetRawTransaction {
/// The raw transaction, encoded as hex bytes.
Raw(#[serde(with = "hex")] SerializedTransaction),
/// The transaction object.
Object {
/// The raw transaction, encoded as hex bytes.
#[serde(with = "hex")]
hex: SerializedTransaction,
/// The height of the block in the best chain that contains the transaction, or -1 if
/// the transaction is in the mempool.
height: i32,
/// The confirmations of the block in the best chain that contains the transaction,
/// or 0 if the transaction is in the mempool.
confirmations: u32,
},
Object(TransactionObject),
}

impl Default for GetRawTransaction {
fn default() -> Self {
Self::Object {
Self::Object(TransactionObject::default())
}
}

/// A Transaction object as returnedb by `getrawtransaction` RPC request.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
pub struct TransactionObject {
/// The raw transaction, encoded as hex bytes.
#[serde(with = "hex")]
pub hex: SerializedTransaction,
/// The height of the block in the best chain that contains the transaction, or -1 if
/// the transaction is in the mempool.
pub height: i32,
/// The confirmations of the block in the best chain that contains the transaction,
/// or 0 if the transaction is in the mempool.
pub confirmations: u32,
// TODO: many fields not yet supported
}

impl Default for TransactionObject {
fn default() -> Self {
Self {
hex: SerializedTransaction::from(
[0u8; zebra_chain::transaction::MIN_TRANSPARENT_TX_SIZE as usize].to_vec(),
),
Expand Down Expand Up @@ -2080,7 +2120,7 @@ impl GetRawTransaction {
verbose: bool,
) -> Self {
if verbose {
GetRawTransaction::Object {
GetRawTransaction::Object(TransactionObject {
hex: tx.into(),
height: match height {
Some(height) => height
Expand All @@ -2090,7 +2130,7 @@ impl GetRawTransaction {
None => -1,
},
confirmations,
}
})
} else {
GetRawTransaction::Raw(tx.into())
}
Expand Down
16 changes: 8 additions & 8 deletions zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ async fn rpc_getblock() {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.map(|tx| GetBlockTransaction::Hash(tx.hash()))
.collect(),
trees,
size: None,
Expand Down Expand Up @@ -219,7 +219,7 @@ async fn rpc_getblock() {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.map(|tx| GetBlockTransaction::Hash(tx.hash()))
.collect(),
trees,
size: None,
Expand Down Expand Up @@ -262,7 +262,7 @@ async fn rpc_getblock() {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.map(|tx| GetBlockTransaction::Hash(tx.hash()))
.collect(),
trees,
size: None,
Expand Down Expand Up @@ -305,7 +305,7 @@ async fn rpc_getblock() {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.map(|tx| GetBlockTransaction::Hash(tx.hash()))
.collect(),
trees,
size: None,
Expand Down Expand Up @@ -348,7 +348,7 @@ async fn rpc_getblock() {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.map(|tx| GetBlockTransaction::Hash(tx.hash()))
.collect(),
trees,
size: None,
Expand Down Expand Up @@ -391,7 +391,7 @@ async fn rpc_getblock() {
tx: block
.transactions
.iter()
.map(|tx| tx.hash().encode_hex())
.map(|tx| GetBlockTransaction::Hash(tx.hash()))
.collect(),
trees,
size: None,
Expand Down Expand Up @@ -766,11 +766,11 @@ async fn rpc_getrawtransaction() {
}

let (response, _) = futures::join!(get_tx_verbose_1_req, make_mempool_req(tx_hash));
let GetRawTransaction::Object {
let GetRawTransaction::Object(TransactionObject {
hex,
height,
confirmations,
} = response.expect("We should have a GetRawTransaction struct")
}) = response.expect("We should have a GetRawTransaction struct")
else {
unreachable!("Should return a Raw enum")
};
Expand Down
6 changes: 3 additions & 3 deletions zebra-rpc/src/tests/vectors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Fixed Zebra RPC serialization test vectors.
use crate::methods::{GetBlock, GetRawTransaction};
use crate::methods::{GetBlock, GetRawTransaction, TransactionObject};

#[test]
pub fn test_transaction_serialization() {
Expand All @@ -10,11 +10,11 @@ pub fn test_transaction_serialization() {

assert_eq!(j, expected_json);

let expected_tx = GetRawTransaction::Object {
let expected_tx = GetRawTransaction::Object(TransactionObject {
hex: vec![0x42].into(),
height: 1,
confirmations: 0,
};
});
let expected_json = r#"{"hex":"42","height":1,"confirmations":0}"#;
let j = serde_json::to_string(&expected_tx).unwrap();

Expand Down

0 comments on commit b290ce7

Please sign in to comment.