Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc): Add some missing fields to transaction object #9329

Merged
merged 9 commits into from
Apr 10, 2025
Merged
22 changes: 22 additions & 0 deletions zebra-chain/src/sapling/commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use std::{fmt, io};

use bitvec::prelude::*;
use hex::ToHex;
use jubjub::ExtendedPoint;
use lazy_static::lazy_static;
use rand_core::{CryptoRng, RngCore};
Expand Down Expand Up @@ -303,6 +304,17 @@ lazy_static! {
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Default))]
pub struct NotSmallOrderValueCommitment(ValueCommitment);

impl NotSmallOrderValueCommitment {
/// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte.
///
/// Zebra displays commitment value in big-endian byte-order,
/// following the convention set by zcashd.
pub fn bytes_in_display_order(&self) -> [u8; 32] {
let mut reversed_bytes = self.0 .0.to_bytes();
reversed_bytes.reverse();
reversed_bytes
}
}
impl TryFrom<ValueCommitment> for NotSmallOrderValueCommitment {
type Error = &'static str;

Expand Down Expand Up @@ -365,6 +377,16 @@ impl ZcashDeserialize for NotSmallOrderValueCommitment {
}
}

impl ToHex for &NotSmallOrderValueCommitment {
fn encode_hex<T: FromIterator<char>>(&self) -> T {
self.bytes_in_display_order().encode_hex()
}

fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
self.bytes_in_display_order().encode_hex_upper()
}
}

#[cfg(test)]
mod tests {

Expand Down
12 changes: 12 additions & 0 deletions zebra-chain/src/sapling/note/ciphertexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ impl ZcashDeserialize for EncryptedNote {
}
}

impl From<EncryptedNote> for [u8; 580] {
fn from(note: EncryptedNote) -> Self {
note.0
}
}

/// A ciphertext component for encrypted output notes.
///
/// Corresponds to Sapling's 'outCiphertext'
Expand Down Expand Up @@ -112,6 +118,12 @@ impl ZcashDeserialize for WrappedNoteKey {
}
}

impl From<WrappedNoteKey> for [u8; 80] {
fn from(note: WrappedNoteKey) -> Self {
note.0
}
}

#[cfg(test)]
use proptest::prelude::*;
#[cfg(test)]
Expand Down
18 changes: 18 additions & 0 deletions zebra-chain/src/transaction/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -922,3 +922,21 @@ fn binding_signatures() {
assert!(at_least_one_v5_checked);
}
}

#[test]
fn test_coinbase_script() -> Result<()> {
let _init_guard = zebra_test::init();

let tx = hex::decode("0400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0503b0e72100ffffffff04e8bbe60e000000001976a914ba92ff06081d5ff6542af8d3b2d209d29ba6337c88ac40787d010000000017a914931fec54c1fea86e574462cc32013f5400b891298738c94d010000000017a914c7a4285ed7aed78d8c0e28d7f1839ccb4046ab0c87286bee000000000017a914d45cb1adffb5215a42720532a076f02c7c778c908700000000b0e721000000000000000000000000").unwrap();

let transaction = tx.zcash_deserialize_into::<Transaction>()?;

let recoded_tx = transaction.zcash_serialize_to_vec().unwrap();
assert_eq!(tx, recoded_tx);

let data = transaction.inputs()[0].coinbase_script().unwrap();
let expected = hex::decode("03b0e72100").unwrap();
assert_eq!(data, expected);

Ok(())
}
16 changes: 16 additions & 0 deletions zebra-chain/src/transparent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,22 @@ impl Input {
}
}

/// Returns the full coinbase script (the encoded height along with the
/// extra data) if this is an [`Input::Coinbase`]. Also returns `None` if
/// the coinbase is for the genesis block but does not match the expected
/// genesis coinbase data.
pub fn coinbase_script(&self) -> Option<Vec<u8>> {
match self {
Input::PrevOut { .. } => None,
Input::Coinbase { height, data, .. } => {
let mut height_and_data = Vec::new();
serialize::write_coinbase_height(*height, data, &mut height_and_data).ok()?;
height_and_data.extend(&data.0);
Some(height_and_data)
}
}
}

/// Returns the input's sequence number.
pub fn sequence(&self) -> u32 {
match self {
Expand Down
54 changes: 6 additions & 48 deletions zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#[cfg(feature = "getblocktemplate-rpcs")]
use std::collections::HashMap;
use std::{collections::HashSet, fmt::Debug, sync::Arc};
use std::{collections::HashSet, fmt::Debug};

use chrono::Utc;
use futures::{stream::FuturesOrdered, StreamExt, TryFutureExt};
Expand Down Expand Up @@ -65,6 +65,7 @@ pub mod types;
use types::GetRawMempool;
#[cfg(feature = "getblocktemplate-rpcs")]
use types::MempoolObject;
use types::TransactionObject;

#[cfg(feature = "getblocktemplate-rpcs")]
pub mod get_block_template_rpcs;
Expand Down Expand Up @@ -934,6 +935,7 @@ where
.try_into()
.expect("should be less than max block height, i32::MAX"),
),
&network,
))
})
.collect();
Expand Down Expand Up @@ -1258,6 +1260,7 @@ where
tx.transaction.clone(),
None,
None,
&self.network,
))
} else {
let hex = tx.transaction.clone().into();
Expand All @@ -1281,6 +1284,7 @@ where
tx.tx.clone(),
Some(tx.height),
Some(tx.confirmations),
&self.network,
))
} else {
let hex = tx.tx.into();
Expand Down Expand Up @@ -2393,7 +2397,7 @@ impl Default for GetBlockHash {
/// Response to a `getrawtransaction` RPC request.
///
/// See the notes for the [`Rpc::get_raw_transaction` method].
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)]
#[derive(Clone, Debug, PartialEq, serde::Serialize)]
#[serde(untagged)]
pub enum GetRawTransaction {
/// The raw transaction, encoded as hex bytes.
Expand All @@ -2408,52 +2412,6 @@ impl Default for GetRawTransaction {
}
}

/// A Transaction object as returned by `getrawtransaction` and `getblock` RPC
/// requests.
#[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 tx or `None` if the tx is in
/// the mempool.
#[serde(skip_serializing_if = "Option::is_none")]
pub height: Option<u32>,
/// The height diff between the block containing the tx and the best chain tip + 1 or `None`
/// if the tx is in the mempool.
#[serde(skip_serializing_if = "Option::is_none")]
pub confirmations: Option<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(),
),
height: Option::default(),
confirmations: Option::default(),
}
}
}

impl TransactionObject {
/// Converts `tx` and `height` into a new `GetRawTransaction` in the `verbose` format.
#[allow(clippy::unwrap_in_result)]
fn from_transaction(
tx: Arc<Transaction>,
height: Option<block::Height>,
confirmations: Option<u32>,
) -> Self {
Self {
hex: tx.into(),
height: height.map(|height| height.0),
confirmations,
}
}
}

/// Response to a `getaddressutxos` RPC request.
///
/// See the notes for the [`Rpc::get_address_utxos` method].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,45 @@ expression: block
{
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0250c30000000000002321027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875acd43000000000000017a9147d46a730d31f97b1930d3368a967c309bd4d136a8700000000",
"height": 1,
"confirmations": 10
"confirmations": 10,
"vin": [
{
"coinbase": "5100",
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.0005,
"valueZat": 50000,
"n": 0,
"scriptPubKey": {
"asm": "",
"hex": "21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac",
"reqSigs": 0,
"type": "",
"addresses": []
}
},
{
"value": 0.000125,
"valueZat": 12500,
"n": 1,
"scriptPubKey": {
"asm": "",
"hex": "a9147d46a730d31f97b1930d3368a967c309bd4d136a87",
"reqSigs": 1,
"type": "",
"addresses": [
"t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd"
]
}
}
],
"vShieldedSpend": [],
"vShieldedOutput": [],
"valueBalance": 0.0,
"valueBalanceZat": 0
}
],
"time": 1477671596,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,45 @@ expression: block
{
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff03510101ffffffff0250c30000000000002321025229e1240a21004cf8338db05679fa34753706e84f6aebba086ba04317fd8f99acd43000000000000017a914ef775f1f997f122a062fff1a2d7443abd1f9c6428700000000",
"height": 1,
"confirmations": 10
"confirmations": 10,
"vin": [
{
"coinbase": "510101",
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.0005,
"valueZat": 50000,
"n": 0,
"scriptPubKey": {
"asm": "",
"hex": "21025229e1240a21004cf8338db05679fa34753706e84f6aebba086ba04317fd8f99ac",
"reqSigs": 0,
"type": "",
"addresses": []
}
},
{
"value": 0.000125,
"valueZat": 12500,
"n": 1,
"scriptPubKey": {
"asm": "",
"hex": "a914ef775f1f997f122a062fff1a2d7443abd1f9c64287",
"reqSigs": 1,
"type": "",
"addresses": [
"t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi"
]
}
}
],
"vShieldedSpend": [],
"vShieldedOutput": [],
"valueBalance": 0.0,
"valueBalanceZat": 0
}
],
"time": 1477674473,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,45 @@ expression: block
{
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0250c30000000000002321027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875acd43000000000000017a9147d46a730d31f97b1930d3368a967c309bd4d136a8700000000",
"height": 1,
"confirmations": 10
"confirmations": 10,
"vin": [
{
"coinbase": "5100",
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.0005,
"valueZat": 50000,
"n": 0,
"scriptPubKey": {
"asm": "",
"hex": "21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac",
"reqSigs": 0,
"type": "",
"addresses": []
}
},
{
"value": 0.000125,
"valueZat": 12500,
"n": 1,
"scriptPubKey": {
"asm": "",
"hex": "a9147d46a730d31f97b1930d3368a967c309bd4d136a87",
"reqSigs": 1,
"type": "",
"addresses": [
"t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd"
]
}
}
],
"vShieldedSpend": [],
"vShieldedOutput": [],
"valueBalance": 0.0,
"valueBalanceZat": 0
}
],
"time": 1477671596,
Expand Down
Loading
Loading