From 142411508ba81c314d9c10a326a29066ec57580d Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 3 Nov 2022 13:25:01 +1000 Subject: [PATCH] 2. change(rpc): Add some transaction fields to the `getblocktemplate` RPC (#5496) * Add documentation for the getblocktemplate RPC * Add a new mempool::Request::Transactions * Add conversions from Vec to merkle::Root and AuthDataRoot * Fill in the merkle root and auth data root fields * Delete the Coinbase type, it's the same as Transaction * Fill in some other existing types * Add Hex serialization support to some zebra-chain types * Add TransactionTemplate fields and fill some in * Fix test hangs by spawning async tasks * Add temporary workaround for no transactions in the block * Encode hashes and roots as hex * Update RPC snapshots * Add a missing Request::Transactions handler * Fix doc warnings * Fix fee serialization * Update snapshots for serialization changes * Add a missing Cargo.lock change * Change depends type * Remove duplicate feature entry * Document the new RPC feature * Fix a comment typo Co-authored-by: Alfredo Garcia * Update default roots docs * Fix comment typo * Fix a comment typo Co-authored-by: Arya Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Alfredo Garcia Co-authored-by: Arya --- zebra-chain/src/amount.rs | 24 ++- zebra-chain/src/block/commitment.rs | 111 ++++++++++ zebra-chain/src/block/merkle.rs | 204 ++++++++++++++++-- zebra-chain/src/transaction/auth_digest.rs | 68 +++++- zebra-chain/src/transaction/serialize.rs | 11 + zebra-chain/src/transaction/unmined.rs | 6 +- zebra-node-services/Cargo.toml | 4 + zebra-node-services/src/mempool.rs | 8 +- zebra-rpc/Cargo.toml | 4 +- .../src/methods/get_block_template_rpcs.rs | 110 ++++++++-- .../methods/get_block_template_rpcs/types.rs | 1 - .../get_block_template_rpcs/types/coinbase.rs | 5 - .../types/default_roots.rs | 33 ++- .../types/get_block_template.rs | 67 ++++-- .../types/transaction.rs | 85 +++++++- .../tests/snapshot/get_block_template_rpcs.rs | 16 +- .../get_block_template@mainnet_10.snap | 26 ++- .../get_block_template@testnet_10.snap | 26 ++- zebra-rpc/src/methods/tests/vectors.rs | 31 +-- .../snapshots/address_balances@mainnet_1.snap | 2 +- .../snapshots/address_balances@mainnet_2.snap | 2 +- .../snapshots/address_balances@testnet_1.snap | 2 +- .../snapshots/address_balances@testnet_2.snap | 2 +- .../address_utxo_data@mainnet_1.snap | 2 +- .../address_utxo_data@mainnet_2.snap | 4 +- .../address_utxo_data@testnet_1.snap | 2 +- .../address_utxo_data@testnet_2.snap | 4 +- .../tests/snapshots/utxos@mainnet_1.snap | 4 +- .../tests/snapshots/utxos@mainnet_2.snap | 8 +- .../tests/snapshots/utxos@testnet_1.snap | 4 +- .../tests/snapshots/utxos@testnet_2.snap | 8 +- zebrad/Cargo.toml | 10 +- zebrad/src/components/mempool.rs | 16 ++ zebrad/src/lib.rs | 4 + 34 files changed, 769 insertions(+), 145 deletions(-) delete mode 100644 zebra-rpc/src/methods/get_block_template_rpcs/types/coinbase.rs diff --git a/zebra-chain/src/amount.rs b/zebra-chain/src/amount.rs index 8a130e4ad7e..b226083c738 100644 --- a/zebra-chain/src/amount.rs +++ b/zebra-chain/src/amount.rs @@ -7,7 +7,6 @@ use std::{ cmp::Ordering, - convert::{TryFrom, TryInto}, hash::{Hash, Hasher}, marker::PhantomData, ops::RangeInclusive, @@ -28,7 +27,8 @@ pub type Result = std::result::Result; /// A runtime validated type for representing amounts of zatoshis #[derive(Clone, Copy, Serialize, Deserialize)] #[serde(try_from = "i64")] -#[serde(bound = "C: Constraint")] +#[serde(into = "i64")] +#[serde(bound = "C: Constraint + Clone")] pub struct Amount( /// The inner amount value. i64, @@ -498,6 +498,26 @@ impl Constraint for NonNegative { } } +/// Marker type for `Amount` that requires negative or zero values. +/// +/// Used for coinbase transactions in `getblocktemplate` RPCs. +/// +/// ``` +/// # use zebra_chain::amount::{Constraint, MAX_MONEY, NegativeOrZero}; +/// assert_eq!( +/// NegativeOrZero::valid_range(), +/// -MAX_MONEY..=0, +/// ); +/// ``` +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct NegativeOrZero; + +impl Constraint for NegativeOrZero { + fn valid_range() -> RangeInclusive { + -MAX_MONEY..=0 + } +} + /// Number of zatoshis in 1 ZEC pub const COIN: i64 = 100_000_000; diff --git a/zebra-chain/src/block/commitment.rs b/zebra-chain/src/block/commitment.rs index f5fc6225746..4b73386b813 100644 --- a/zebra-chain/src/block/commitment.rs +++ b/zebra-chain/src/block/commitment.rs @@ -1,5 +1,6 @@ //! The Commitment enum, used for the corresponding block header field. +use hex::{FromHex, ToHex}; use thiserror::Error; use crate::{ @@ -159,6 +160,62 @@ impl From for [u8; 32] { } } +impl ChainHistoryMmrRootHash { + /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte. + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn bytes_in_display_order(&self) -> [u8; 32] { + let mut reversed_bytes = self.0; + reversed_bytes.reverse(); + reversed_bytes + } + + /// Convert bytes in big-endian byte-order into a `ChainHistoryMmrRootHash`. + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn from_bytes_in_display_order( + bytes_in_display_order: &[u8; 32], + ) -> ChainHistoryMmrRootHash { + let mut internal_byte_order = *bytes_in_display_order; + internal_byte_order.reverse(); + + ChainHistoryMmrRootHash(internal_byte_order) + } +} + +impl ToHex for &ChainHistoryMmrRootHash { + fn encode_hex>(&self) -> T { + self.bytes_in_display_order().encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + self.bytes_in_display_order().encode_hex_upper() + } +} + +impl ToHex for ChainHistoryMmrRootHash { + fn encode_hex>(&self) -> T { + (&self).encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + (&self).encode_hex_upper() + } +} + +impl FromHex for ChainHistoryMmrRootHash { + type Error = <[u8; 32] as FromHex>::Error; + + fn from_hex>(hex: T) -> Result { + let mut hash = <[u8; 32]>::from_hex(hex)?; + hash.reverse(); + + Ok(hash.into()) + } +} + /// A block commitment to chain history and transaction auth. /// - the chain history tree for all ancestors in the current network upgrade, /// and @@ -212,6 +269,60 @@ impl ChainHistoryBlockTxAuthCommitmentHash { .expect("32 byte array"); Self(hash_block_commitments) } + + /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte. + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn bytes_in_display_order(&self) -> [u8; 32] { + let mut reversed_bytes = self.0; + reversed_bytes.reverse(); + reversed_bytes + } + + /// Convert bytes in big-endian byte-order into a `ChainHistoryBlockTxAuthCommitmentHash`. + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn from_bytes_in_display_order( + bytes_in_display_order: &[u8; 32], + ) -> ChainHistoryBlockTxAuthCommitmentHash { + let mut internal_byte_order = *bytes_in_display_order; + internal_byte_order.reverse(); + + ChainHistoryBlockTxAuthCommitmentHash(internal_byte_order) + } +} + +impl ToHex for &ChainHistoryBlockTxAuthCommitmentHash { + fn encode_hex>(&self) -> T { + self.bytes_in_display_order().encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + self.bytes_in_display_order().encode_hex_upper() + } +} + +impl ToHex for ChainHistoryBlockTxAuthCommitmentHash { + fn encode_hex>(&self) -> T { + (&self).encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + (&self).encode_hex_upper() + } +} + +impl FromHex for ChainHistoryBlockTxAuthCommitmentHash { + type Error = <[u8; 32] as FromHex>::Error; + + fn from_hex>(hex: T) -> Result { + let mut hash = <[u8; 32]>::from_hex(hex)?; + hash.reverse(); + + Ok(hash.into()) + } } /// Errors that can occur when checking RootHash consensus rules. diff --git a/zebra-chain/src/block/merkle.rs b/zebra-chain/src/block/merkle.rs index 6db97dabe7d..71f46e300f3 100644 --- a/zebra-chain/src/block/merkle.rs +++ b/zebra-chain/src/block/merkle.rs @@ -1,15 +1,17 @@ //! The Bitcoin-inherited Merkle tree of transactions. -#![allow(clippy::unit_arg)] -use std::iter; -use std::{fmt, io::Write}; +use std::{fmt, io::Write, iter}; + +use hex::{FromHex, ToHex}; + +use crate::{ + serialization::sha256d, + transaction::{self, Transaction, UnminedTx, UnminedTxId}, +}; #[cfg(any(any(test, feature = "proptest-impl"), feature = "proptest-impl"))] use proptest_derive::Arbitrary; -use crate::serialization::sha256d; -use crate::transaction::{self, Transaction}; - /// The root of the Bitcoin-inherited transaction Merkle tree, binding the /// block header to the transactions in the block. /// @@ -77,6 +79,72 @@ impl fmt::Debug for Root { } } +impl From<[u8; 32]> for Root { + fn from(hash: [u8; 32]) -> Self { + Root(hash) + } +} + +impl From for [u8; 32] { + fn from(hash: Root) -> Self { + hash.0 + } +} + +impl Root { + /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte. + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn bytes_in_display_order(&self) -> [u8; 32] { + let mut reversed_bytes = self.0; + reversed_bytes.reverse(); + reversed_bytes + } + + /// Convert bytes in big-endian byte-order into a [`merkle::Root`](crate::block::merkle::Root). + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn from_bytes_in_display_order(bytes_in_display_order: &[u8; 32]) -> Root { + let mut internal_byte_order = *bytes_in_display_order; + internal_byte_order.reverse(); + + Root(internal_byte_order) + } +} + +impl ToHex for &Root { + fn encode_hex>(&self) -> T { + self.bytes_in_display_order().encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + self.bytes_in_display_order().encode_hex_upper() + } +} + +impl ToHex for Root { + fn encode_hex>(&self) -> T { + (&self).encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + (&self).encode_hex_upper() + } +} + +impl FromHex for Root { + type Error = <[u8; 32] as FromHex>::Error; + + fn from_hex>(hex: T) -> Result { + let mut hash = <[u8; 32]>::from_hex(hex)?; + hash.reverse(); + + Ok(hash.into()) + } +} + fn hash(h1: &[u8; 32], h2: &[u8; 32]) -> [u8; 32] { let mut w = sha256d::Writer::default(); w.write_all(h1).unwrap(); @@ -115,7 +183,32 @@ where } } +impl std::iter::FromIterator for Root { + fn from_iter(transactions: I) -> Self + where + I: IntoIterator, + { + transactions + .into_iter() + .map(|tx| tx.id.mined_id()) + .collect() + } +} + +impl std::iter::FromIterator for Root { + fn from_iter(tx_ids: I) -> Self + where + I: IntoIterator, + { + tx_ids.into_iter().map(|tx_id| tx_id.mined_id()).collect() + } +} + impl std::iter::FromIterator for Root { + /// # Panics + /// + /// When there are no transactions in the iterator. + /// This is impossible, because every block must have a coinbase transaction. fn from_iter(hashes: I) -> Self where I: IntoIterator, @@ -166,6 +259,71 @@ impl From for [u8; 32] { } } +impl AuthDataRoot { + /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte. + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn bytes_in_display_order(&self) -> [u8; 32] { + let mut reversed_bytes = self.0; + reversed_bytes.reverse(); + reversed_bytes + } + + /// Convert bytes in big-endian byte-order into a [`merkle::AuthDataRoot`](crate::block::merkle::AuthDataRoot). + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn from_bytes_in_display_order(bytes_in_display_order: &[u8; 32]) -> AuthDataRoot { + let mut internal_byte_order = *bytes_in_display_order; + internal_byte_order.reverse(); + + AuthDataRoot(internal_byte_order) + } +} + +impl ToHex for &AuthDataRoot { + fn encode_hex>(&self) -> T { + self.bytes_in_display_order().encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + self.bytes_in_display_order().encode_hex_upper() + } +} + +impl ToHex for AuthDataRoot { + fn encode_hex>(&self) -> T { + (&self).encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + (&self).encode_hex_upper() + } +} + +impl FromHex for AuthDataRoot { + type Error = <[u8; 32] as FromHex>::Error; + + fn from_hex>(hex: T) -> Result { + let mut hash = <[u8; 32]>::from_hex(hex)?; + hash.reverse(); + + Ok(hash.into()) + } +} + +/// The placeholder used for the [`AuthDigest`](transaction::AuthDigest) of pre-v5 transactions. +/// +/// # Consensus +/// +/// > For transaction versions before v5, a placeholder value consisting +/// > of 32 bytes of 0xFF is used in place of the authorizing data commitment. +/// > This is only used in the tree committed to by hashAuthDataRoot. +/// +/// +pub const AUTH_DIGEST_PLACEHOLDER: transaction::AuthDigest = transaction::AuthDigest([0xFF; 32]); + impl std::iter::FromIterator for AuthDataRoot where T: std::convert::AsRef, @@ -174,17 +332,33 @@ where where I: IntoIterator, { - // > For transaction versions before v5, a placeholder value consisting - // > of 32 bytes of 0xFF is used in place of the authorizing data commitment. - // > This is only used in the tree committed to by hashAuthDataRoot. - // https://zips.z.cash/zip-0244#authorizing-data-commitment transactions .into_iter() - .map(|tx| { - tx.as_ref() - .auth_digest() - .unwrap_or(transaction::AuthDigest([0xFF; 32])) - }) + .map(|tx| tx.as_ref().auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER)) + .collect() + } +} + +impl std::iter::FromIterator for AuthDataRoot { + fn from_iter(transactions: I) -> Self + where + I: IntoIterator, + { + transactions + .into_iter() + .map(|tx| tx.id.auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER)) + .collect() + } +} + +impl std::iter::FromIterator for AuthDataRoot { + fn from_iter(tx_ids: I) -> Self + where + I: IntoIterator, + { + tx_ids + .into_iter() + .map(|tx_id| tx_id.auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER)) .collect() } } diff --git a/zebra-chain/src/transaction/auth_digest.rs b/zebra-chain/src/transaction/auth_digest.rs index 37db0877ac0..909050a4210 100644 --- a/zebra-chain/src/transaction/auth_digest.rs +++ b/zebra-chain/src/transaction/auth_digest.rs @@ -2,8 +2,7 @@ use std::{fmt, sync::Arc}; -#[cfg(any(test, feature = "proptest-impl"))] -use proptest_derive::Arbitrary; +use hex::{FromHex, ToHex}; use crate::{ primitives::zcash_primitives::auth_digest, @@ -14,6 +13,9 @@ use crate::{ use super::Transaction; +#[cfg(any(test, feature = "proptest-impl"))] +use proptest_derive::Arbitrary; + /// An authorizing data commitment hash as specified in [ZIP-244]. /// /// Note: Zebra displays transaction and block hashes in big-endian byte-order, @@ -24,6 +26,29 @@ use super::Transaction; #[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))] pub struct AuthDigest(pub [u8; 32]); +impl AuthDigest { + /// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte. + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn bytes_in_display_order(&self) -> [u8; 32] { + let mut reversed_bytes = self.0; + reversed_bytes.reverse(); + reversed_bytes + } + + /// Convert bytes in big-endian byte-order into a [`transaction::AuthDigest`](crate::transaction::AuthDigest). + /// + /// Zebra displays transaction and block hashes in big-endian byte-order, + /// following the u256 convention set by Bitcoin and zcashd. + pub fn from_bytes_in_display_order(bytes_in_display_order: &[u8; 32]) -> AuthDigest { + let mut internal_byte_order = *bytes_in_display_order; + internal_byte_order.reverse(); + + AuthDigest(internal_byte_order) + } +} + impl From for AuthDigest { /// Computes the authorizing data commitment for a transaction. /// @@ -76,20 +101,47 @@ impl From<&AuthDigest> for [u8; 32] { } } +impl ToHex for &AuthDigest { + fn encode_hex>(&self) -> T { + self.bytes_in_display_order().encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + self.bytes_in_display_order().encode_hex_upper() + } +} + +impl ToHex for AuthDigest { + fn encode_hex>(&self) -> T { + (&self).encode_hex() + } + + fn encode_hex_upper>(&self) -> T { + (&self).encode_hex_upper() + } +} + +impl FromHex for AuthDigest { + type Error = <[u8; 32] as FromHex>::Error; + + fn from_hex>(hex: T) -> Result { + let mut hash = <[u8; 32]>::from_hex(hex)?; + hash.reverse(); + + Ok(hash.into()) + } +} + impl fmt::Display for AuthDigest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut reversed_bytes = self.0; - reversed_bytes.reverse(); - f.write_str(&hex::encode(reversed_bytes)) + f.write_str(&self.encode_hex::()) } } impl fmt::Debug for AuthDigest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut reversed_bytes = self.0; - reversed_bytes.reverse(); f.debug_tuple("AuthDigest") - .field(&hex::encode(reversed_bytes)) + .field(&self.encode_hex::()) .finish() } } diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index b29cd675f8b..8d7a613460e 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -5,6 +5,7 @@ use std::{borrow::Borrow, convert::TryInto, io, sync::Arc}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use halo2::pasta::{group::ff::PrimeField, pallas}; +use hex::FromHex; use crate::{ amount, @@ -1018,3 +1019,13 @@ impl From> for SerializedTransaction { Self { bytes } } } + +impl FromHex for SerializedTransaction { + type Error = as FromHex>::Error; + + fn from_hex>(hex: T) -> Result { + let bytes = >::from_hex(hex)?; + + Ok(bytes.into()) + } +} diff --git a/zebra-chain/src/transaction/unmined.rs b/zebra-chain/src/transaction/unmined.rs index 1a5d694b983..51ddacbe8e9 100644 --- a/zebra-chain/src/transaction/unmined.rs +++ b/zebra-chain/src/transaction/unmined.rs @@ -193,12 +193,12 @@ impl UnminedTxId { /// (But it might still need semantic or contextual verification.) #[derive(Clone, Debug, Eq, PartialEq)] pub struct UnminedTx { - /// A unique identifier for this unmined transaction. - pub id: UnminedTxId, - /// The unmined transaction itself. pub transaction: Arc, + /// A unique identifier for this unmined transaction. + pub id: UnminedTxId, + /// The size in bytes of the serialized transaction data pub size: usize, } diff --git a/zebra-node-services/Cargo.toml b/zebra-node-services/Cargo.toml index 1247d19c669..3553fc29baf 100644 --- a/zebra-node-services/Cargo.toml +++ b/zebra-node-services/Cargo.toml @@ -6,5 +6,9 @@ version = "1.0.0-beta.17" edition = "2021" repository = "https://github.com/ZcashFoundation/zebra" +[features] +default = [] +getblocktemplate-rpcs = [] + [dependencies] zebra-chain = { path = "../zebra-chain" } diff --git a/zebra-node-services/src/mempool.rs b/zebra-node-services/src/mempool.rs index 320032da251..3b4f66a9d88 100644 --- a/zebra-node-services/src/mempool.rs +++ b/zebra-node-services/src/mempool.rs @@ -9,6 +9,7 @@ use zebra_chain::transaction::{Hash, UnminedTx, UnminedTxId}; use crate::BoxError; mod gossip; + pub use self::gossip::Gossip; /// A mempool service request. @@ -20,7 +21,6 @@ pub use self::gossip::Gossip; /// Requests can't modify the mempool directly, /// because all mempool transactions must be verified. #[derive(Debug, Eq, PartialEq)] -#[allow(dead_code)] pub enum Request { /// Query all transaction IDs in the mempool. TransactionIds, @@ -35,6 +35,12 @@ pub enum Request { /// the [`AuthDigest`](zebra_chain::transaction::AuthDigest). TransactionsByMinedId(HashSet), + /// Get all the transactions in the mempool. + /// + /// Equivalent to `TransactionsById(TransactionIds)`. + #[cfg(feature = "getblocktemplate-rpcs")] + Transactions, + /// Query matching cached rejected transaction IDs in the mempool, /// using a unique set of [`UnminedTxId`]s. RejectedTransactionIds(HashSet), diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index c5048c1ea6e..ae200557696 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -9,8 +9,10 @@ edition = "2021" [features] default = [] +getblocktemplate-rpcs = ["zebra-state/getblocktemplate-rpcs", "zebra-node-services/getblocktemplate-rpcs"] + +# Test-only features proptest-impl = ["proptest", "proptest-derive", "zebra-chain/proptest-impl", "zebra-state/proptest-impl"] -getblocktemplate-rpcs = ["zebra-state/getblocktemplate-rpcs"] [dependencies] chrono = { version = "0.4.22", default-features = false, features = ["clock", "std"] } diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index 00e681242d2..788879b5e37 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -5,12 +5,13 @@ use jsonrpc_core::{self, BoxFuture, Error, ErrorCode, Result}; use jsonrpc_derive::rpc; use tower::{buffer::Buffer, Service, ServiceExt}; -use zebra_chain::{block::Height, chain_tip::ChainTip}; +use zebra_chain::{amount::Amount, block::Height, chain_tip::ChainTip}; use zebra_node_services::mempool; use crate::methods::{ get_block_template_rpcs::types::{ - coinbase::Coinbase, default_roots::DefaultRoots, get_block_template::GetBlockTemplate, + default_roots::DefaultRoots, get_block_template::GetBlockTemplate, + transaction::TransactionTemplate, }, GetBlockHash, MISSING_BLOCK_ERROR_CODE, }; @@ -49,13 +50,28 @@ pub trait GetBlockTemplateRpc { #[rpc(name = "getblockhash")] fn get_block_hash(&self, index: i32) -> BoxFuture>; - /// Documentation to be filled as we go. + /// Returns a block template for mining new Zcash blocks. + /// + /// # Parameters + /// + /// - `jsonrequestobject`: (string, optional) A JSON object containing arguments. /// /// zcashd reference: [`getblocktemplate`](https://zcash-rpc.github.io/getblocktemplate.html) /// /// # Notes /// - /// - This rpc method is available only if zebra is built with `--features getblocktemplate-rpcs`. + /// Arguments to this RPC are currently ignored. + /// Long polling, block proposals, server lists, and work IDs are not supported. + /// + /// Miners can make arbitrary changes to blocks, as long as: + /// - the data sent to `submitblock` is a valid Zcash block, and + /// - the parent block is a valid block that Zebra already has, or will receive soon. + /// + /// Zebra verifies blocks in parallel, and keeps recent chains in parallel, + /// so moving between chains is very cheap. (But forking a new chain may take some time, + /// until bug #4794 is fixed.) + /// + /// This rpc method is available only if zebra is built with `--features getblocktemplate-rpcs`. #[rpc(name = "getblocktemplate")] fn get_block_template(&self) -> BoxFuture>; } @@ -84,7 +100,6 @@ where // Services // /// A handle to the mempool service. - #[allow(dead_code)] mempool: Buffer, /// A handle to the state service. @@ -194,33 +209,94 @@ where } fn get_block_template(&self) -> BoxFuture> { + let mempool = self.mempool.clone(); + + // Since this is a very large RPC, we use separate functions for each group of fields. async move { - let empty_string = String::from(""); + // TODO: put this in a separate get_mempool_transactions() function + let request = mempool::Request::Transactions; + let response = mempool.oneshot(request).await.map_err(|error| Error { + code: ErrorCode::ServerError(0), + message: error.to_string(), + data: None, + })?; + + let transactions = if let mempool::Response::Transactions(transactions) = response { + // TODO: select transactions according to ZIP-317 (#5473) + transactions + } else { + unreachable!("unmatched response to a mempool::Transactions request"); + }; + + let merkle_root; + let auth_data_root; - // Returns empty `GetBlockTemplate` + // TODO: add the coinbase transaction to these lists, and delete the is_empty() check + if !transactions.is_empty() { + merkle_root = transactions.iter().cloned().collect(); + auth_data_root = transactions.iter().cloned().collect(); + } else { + merkle_root = [0; 32].into(); + auth_data_root = [0; 32].into(); + } + + let transactions = transactions.iter().map(Into::into).collect(); + + let empty_string = String::from(""); Ok(GetBlockTemplate { capabilities: vec![], + version: 0, - previous_block_hash: empty_string.clone(), - block_commitments_hash: empty_string.clone(), - light_client_root_hash: empty_string.clone(), - final_sapling_root_hash: empty_string.clone(), + + previous_block_hash: GetBlockHash([0; 32].into()), + block_commitments_hash: [0; 32].into(), + light_client_root_hash: [0; 32].into(), + final_sapling_root_hash: [0; 32].into(), default_roots: DefaultRoots { - merkle_root: empty_string.clone(), - chain_history_root: empty_string.clone(), - auth_data_root: empty_string.clone(), - block_commitments_hash: empty_string.clone(), + merkle_root, + chain_history_root: [0; 32].into(), + auth_data_root, + block_commitments_hash: [0; 32].into(), + }, + + transactions, + + // TODO: move to a separate function in the transactions module + coinbase_txn: TransactionTemplate { + // TODO: generate coinbase transaction data + data: vec![].into(), + + // TODO: calculate from transaction data + hash: [0; 32].into(), + auth_digest: [0; 32].into(), + + // Always empty for coinbase transactions. + depends: Vec::new(), + + // TODO: negative sum of transactions.*.fee + fee: Amount::zero(), + + // TODO: sigops used by the generated transaction data + sigops: 0, + + required: true, }, - transactions: vec![], - coinbase_txn: Coinbase {}, + target: empty_string.clone(), + min_time: 0, + mutable: vec![], + nonce_range: empty_string.clone(), + sigop_limit: 0, size_limit: 0, + cur_time: 0, + bits: empty_string, + height: 0, }) } diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types.rs index 6109391009e..e2477ab2c91 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/types.rs @@ -1,6 +1,5 @@ //! Types used in mining RPC methods. -pub(crate) mod coinbase; pub(crate) mod default_roots; pub(crate) mod get_block_template; pub(crate) mod transaction; diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types/coinbase.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types/coinbase.rs deleted file mode 100644 index 323f50ca105..00000000000 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types/coinbase.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! The `Coinbase` type is part of the `getblocktemplate` RPC method output. - -/// documentation and fields to be added in #5453. -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct Coinbase {} diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types/default_roots.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types/default_roots.rs index b8bf3a493bc..93272944c88 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types/default_roots.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/types/default_roots.rs @@ -1,18 +1,35 @@ //! The `DefaultRoots` type is part of the `getblocktemplate` RPC method output. +use zebra_chain::block::{ + merkle::{self, AuthDataRoot}, + ChainHistoryBlockTxAuthCommitmentHash, ChainHistoryMmrRootHash, +}; + /// Documentation to be added in #5452 or #5455. #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct DefaultRoots { - /// Add documentation. + /// The merkle root of the transaction IDs in the block. + /// Used in the new block's header. #[serde(rename = "merkleroot")] - pub merkle_root: String, - /// Add documentation. + #[serde(with = "hex")] + pub merkle_root: merkle::Root, + + /// The root of the merkle mountain range of the chain history roots from the last network upgrade to the previous block. + /// Unlike the other roots, this not cover any data from this new block, only from previous blocks. #[serde(rename = "chainhistoryroot")] - pub chain_history_root: String, - /// Add documentation. + #[serde(with = "hex")] + pub chain_history_root: ChainHistoryMmrRootHash, + + /// The merkle root of the authorizing data hashes of the transactions in the new block. #[serde(rename = "authdataroot")] - pub auth_data_root: String, - /// Add documentation. + #[serde(with = "hex")] + pub auth_data_root: AuthDataRoot, + + /// The block commitment for the new block's header. + /// This hash covers `chain_history_root` and `auth_data_root`. + /// + /// `merkle_root` has its own field in the block header. #[serde(rename = "blockcommitmentshash")] - pub block_commitments_hash: String, + #[serde(with = "hex")] + pub block_commitments_hash: ChainHistoryBlockTxAuthCommitmentHash, } diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template.rs index 76a9bb8b062..983b301171e 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/types/get_block_template.rs @@ -1,7 +1,12 @@ //! The `GetBlockTempate` type is the output of the `getblocktemplate` RPC method. -use crate::methods::get_block_template_rpcs::types::{ - coinbase::Coinbase, default_roots::DefaultRoots, transaction::Transaction, +use zebra_chain::{amount, block::ChainHistoryBlockTxAuthCommitmentHash}; + +use crate::methods::{ + get_block_template_rpcs::types::{ + default_roots::DefaultRoots, transaction::TransactionTemplate, + }, + GetBlockHash, }; /// Documentation to be added after we document all the individual fields. @@ -9,49 +14,83 @@ use crate::methods::get_block_template_rpcs::types::{ pub struct GetBlockTemplate { /// Add documentation. pub capabilities: Vec, - /// Add documentation. - pub version: usize, + + /// The version of the block format. + /// Always 4 for new Zcash blocks. + // + // TODO: add a default block version constant to zebra-chain. + pub version: u32, + /// Add documentation. #[serde(rename = "previousblockhash")] - pub previous_block_hash: String, + pub previous_block_hash: GetBlockHash, + /// Add documentation. #[serde(rename = "blockcommitmentshash")] - pub block_commitments_hash: String, + #[serde(with = "hex")] + pub block_commitments_hash: ChainHistoryBlockTxAuthCommitmentHash, + /// Add documentation. #[serde(rename = "lightclientroothash")] - pub light_client_root_hash: String, + #[serde(with = "hex")] + pub light_client_root_hash: ChainHistoryBlockTxAuthCommitmentHash, + /// Add documentation. #[serde(rename = "finalsaplingroothash")] - pub final_sapling_root_hash: String, + #[serde(with = "hex")] + pub final_sapling_root_hash: ChainHistoryBlockTxAuthCommitmentHash, + /// Add documentation. #[serde(rename = "defaultroots")] pub default_roots: DefaultRoots, - /// Add documentation. - pub transactions: Vec, - /// Add documentation. + + /// The non-coinbase transactions selected for this block template. + /// + /// TODO: select these transactions using ZIP-317 (#5473) + pub transactions: Vec>, + + /// The coinbase transaction generated from `transactions` and `height`. #[serde(rename = "coinbasetxn")] - pub coinbase_txn: Coinbase, + pub coinbase_txn: TransactionTemplate, + /// Add documentation. + // TODO: use ExpandedDifficulty type. pub target: String, + /// Add documentation. #[serde(rename = "mintime")] + // TODO: use DateTime32 type? pub min_time: u32, + /// Add documentation. pub mutable: Vec, + /// Add documentation. #[serde(rename = "noncerange")] pub nonce_range: String, + /// Add documentation. + /// + /// The same as `MAX_BLOCK_SIGOPS`. #[serde(rename = "sigoplimit")] - pub sigop_limit: u32, + pub sigop_limit: u64, + /// Add documentation. + /// + /// The same as `MAX_BLOCK_BYTES`. #[serde(rename = "sizelimit")] - pub size_limit: u32, + pub size_limit: u64, + /// Add documentation. + // TODO: use DateTime32 type? #[serde(rename = "curtime")] pub cur_time: u32, + /// Add documentation. + // TODO: use CompactDifficulty type. pub bits: String, + /// Add documentation. + // TODO: use Height type? pub height: u32, } diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/types/transaction.rs b/zebra-rpc/src/methods/get_block_template_rpcs/types/transaction.rs index 5ee0c528453..4f2b6ef4106 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs/types/transaction.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs/types/transaction.rs @@ -1,5 +1,84 @@ -//! The `Transaction` type is part of the `getblocktemplate` RPC method output. +//! The `TransactionTemplate` type is part of the `getblocktemplate` RPC method output. -/// Documentation and fields to be added in #5454. +use zebra_chain::{ + amount::{self, Amount, NonNegative}, + block::merkle::AUTH_DIGEST_PLACEHOLDER, + transaction::{self, SerializedTransaction, UnminedTx}, +}; + +/// Transaction data and fields needed to generate blocks using the `getblocktemplate` RPC. #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] -pub struct Transaction {} +#[serde(bound = "FeeConstraint: amount::Constraint + Clone")] +pub struct TransactionTemplate +where + FeeConstraint: amount::Constraint + Clone, +{ + /// The hex-encoded serialized data for this transaction. + #[serde(with = "hex")] + pub(crate) data: SerializedTransaction, + + /// The transaction ID of this transaction. + #[serde(with = "hex")] + pub(crate) hash: transaction::Hash, + + /// The authorizing data digest of a v5 transaction, or a placeholder for older versions. + #[serde(rename = "authdigest")] + #[serde(with = "hex")] + pub(crate) auth_digest: transaction::AuthDigest, + + /// The transactions in this block template that this transaction depends upon. + /// These are 1-based indexes in the `transactions` list. + /// + /// Zebra's mempool does not support transaction dependencies, so this list is always empty. + /// + /// We use `u16` because 2 MB blocks are limited to around 39,000 transactions. + pub(crate) depends: Vec, + + /// The fee for this transaction. + /// + /// Non-coinbase transactions must be `NonNegative`. + /// The Coinbase transaction `fee` is the negative sum of the fees of the transactions in + /// the block, so their fee must be `NegativeOrZero`. + // + // TODO: add a fee field to mempool transactions, based on the verifier response. + pub(crate) fee: Amount, + + /// The number of transparent signature operations in this transaction. + // + // TODO: add a sigops field to mempool transactions, based on the verifier response. + pub(crate) sigops: u64, + + /// Is this transaction required in the block? + /// + /// Coinbase transactions are required, all other transactions are not. + pub(crate) required: bool, +} + +// Convert from a mempool transaction to a transaction template. +impl From<&UnminedTx> for TransactionTemplate { + fn from(tx: &UnminedTx) -> Self { + Self { + data: tx.transaction.as_ref().into(), + hash: tx.id.mined_id(), + auth_digest: tx.id.auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER), + + // Always empty, not supported by Zebra's mempool. + depends: Vec::new(), + + // TODO: add a fee field to mempool transactions, based on the verifier response. + fee: Amount::zero(), + + // TODO: add a sigops field to mempool transactions, based on the verifier response. + sigops: 0, + + // Zebra does not require any transactions except the coinbase transaction. + required: false, + } + } +} + +impl From for TransactionTemplate { + fn from(tx: UnminedTx) -> Self { + Self::from(&tx) + } +} diff --git a/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs b/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs index b240c7c489e..ad7ce1f7ee4 100644 --- a/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs @@ -16,7 +16,7 @@ use zebra_test::mock_service::{MockService, PanicAssertion}; use crate::methods::{GetBlockHash, GetBlockTemplateRpc, GetBlockTemplateRpcImpl}; pub async fn test_responses( - mempool: MockService< + mut mempool: MockService< mempool::Request, mempool::Response, PanicAssertion, @@ -58,10 +58,18 @@ pub async fn test_responses( snapshot_rpc_getblockhash(get_block_hash, &settings); // `getblocktemplate` - let get_block_template = get_block_template_rpc - .get_block_template() + let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template()); + + mempool + .expect_request(mempool::Request::Transactions) + .await + .respond(mempool::Response::Transactions(vec![])); + + let get_block_template = get_block_template .await - .expect("We should have a GetBlockTemplate struct"); + .expect("unexpected panic in getblocktemplate RPC task") + .expect("unexpected error in getblocktemplate RPC call"); + snapshot_rpc_getblocktemplate(get_block_template, &settings); } diff --git a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@mainnet_10.snap b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@mainnet_10.snap index 5d65a33333a..cb8f20d834b 100644 --- a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@mainnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@mainnet_10.snap @@ -5,18 +5,26 @@ expression: block_template { "capabilities": [], "version": 0, - "previousblockhash": "", - "blockcommitmentshash": "", - "lightclientroothash": "", - "finalsaplingroothash": "", + "previousblockhash": "0000000000000000000000000000000000000000000000000000000000000000", + "blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000", + "lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000", + "finalsaplingroothash": "0000000000000000000000000000000000000000000000000000000000000000", "defaultroots": { - "merkleroot": "", - "chainhistoryroot": "", - "authdataroot": "", - "blockcommitmentshash": "" + "merkleroot": "0000000000000000000000000000000000000000000000000000000000000000", + "chainhistoryroot": "0000000000000000000000000000000000000000000000000000000000000000", + "authdataroot": "0000000000000000000000000000000000000000000000000000000000000000", + "blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000" }, "transactions": [], - "coinbasetxn": {}, + "coinbasetxn": { + "data": "", + "hash": "0000000000000000000000000000000000000000000000000000000000000000", + "authdigest": "0000000000000000000000000000000000000000000000000000000000000000", + "depends": [], + "fee": 0, + "sigops": 0, + "required": true + }, "target": "", "mintime": 0, "mutable": [], diff --git a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@testnet_10.snap b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@testnet_10.snap index 5d65a33333a..cb8f20d834b 100644 --- a/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@testnet_10.snap +++ b/zebra-rpc/src/methods/tests/snapshot/snapshots/get_block_template@testnet_10.snap @@ -5,18 +5,26 @@ expression: block_template { "capabilities": [], "version": 0, - "previousblockhash": "", - "blockcommitmentshash": "", - "lightclientroothash": "", - "finalsaplingroothash": "", + "previousblockhash": "0000000000000000000000000000000000000000000000000000000000000000", + "blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000", + "lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000", + "finalsaplingroothash": "0000000000000000000000000000000000000000000000000000000000000000", "defaultroots": { - "merkleroot": "", - "chainhistoryroot": "", - "authdataroot": "", - "blockcommitmentshash": "" + "merkleroot": "0000000000000000000000000000000000000000000000000000000000000000", + "chainhistoryroot": "0000000000000000000000000000000000000000000000000000000000000000", + "authdataroot": "0000000000000000000000000000000000000000000000000000000000000000", + "blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000" }, "transactions": [], - "coinbasetxn": {}, + "coinbasetxn": { + "data": "", + "hash": "0000000000000000000000000000000000000000000000000000000000000000", + "authdigest": "0000000000000000000000000000000000000000000000000000000000000000", + "depends": [], + "fee": 0, + "sigops": 0, + "required": true + }, "target": "", "mintime": 0, "mutable": [], diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index 6d075711aac..4ed4d0c4370 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -755,32 +755,21 @@ async fn rpc_getblocktemplate() { latest_chain_tip.clone(), ); - let get_block_template = get_block_template_rpc - .get_block_template() + let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template()); + + mempool + .expect_request(mempool::Request::Transactions) + .await + .respond(mempool::Response::Transactions(vec![])); + + let get_block_template = get_block_template .await - .expect("We should have a GetBlockTemplate struct"); + .expect("unexpected panic in getblocktemplate RPC task") + .expect("unexpected error in getblocktemplate RPC call"); assert!(get_block_template.capabilities.is_empty()); assert_eq!(get_block_template.version, 0); - assert!(get_block_template.previous_block_hash.is_empty()); - assert!(get_block_template.block_commitments_hash.is_empty()); - assert!(get_block_template.light_client_root_hash.is_empty()); - assert!(get_block_template.final_sapling_root_hash.is_empty()); - assert!(get_block_template.default_roots.merkle_root.is_empty()); - assert!(get_block_template - .default_roots - .chain_history_root - .is_empty()); - assert!(get_block_template.default_roots.auth_data_root.is_empty()); - assert!(get_block_template - .default_roots - .block_commitments_hash - .is_empty()); assert!(get_block_template.transactions.is_empty()); - assert_eq!( - get_block_template.coinbase_txn, - get_block_template_rpcs::types::coinbase::Coinbase {} - ); assert!(get_block_template.target.is_empty()); assert_eq!(get_block_template.min_time, 0); assert!(get_block_template.mutable.is_empty()); diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_1.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_1.snap index 616b2c42b31..00bf12a206e 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_1.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_1.snap @@ -4,7 +4,7 @@ expression: stored_address_balances --- [ ("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", AddressBalanceLocation( - balance: Amount(12500), + balance: 12500, location: OutputLocation( transaction_location: TransactionLocation( height: Height(1), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_2.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_2.snap index 14fe6eace99..e05c15d9a03 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_2.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@mainnet_2.snap @@ -4,7 +4,7 @@ expression: stored_address_balances --- [ ("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", AddressBalanceLocation( - balance: Amount(37500), + balance: 37500, location: OutputLocation( transaction_location: TransactionLocation( height: Height(1), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_1.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_1.snap index 4ea25efcd3d..4967e2765b4 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_1.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_1.snap @@ -4,7 +4,7 @@ expression: stored_address_balances --- [ ("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", AddressBalanceLocation( - balance: Amount(12500), + balance: 12500, location: OutputLocation( transaction_location: TransactionLocation( height: Height(1), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_2.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_2.snap index 6c792ba7981..c3332729c05 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_2.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_balances@testnet_2.snap @@ -4,7 +4,7 @@ expression: stored_address_balances --- [ ("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", AddressBalanceLocation( - balance: Amount(37500), + balance: 37500, location: OutputLocation( transaction_location: TransactionLocation( height: Height(1), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_1.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_1.snap index 47aaa833f4b..633ff4fd48e 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_1.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_1.snap @@ -5,7 +5,7 @@ expression: stored_address_utxos [ ("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", [ Output( - value: Amount(12500), + value: 12500, lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"), ), ]), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_2.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_2.snap index 68a8cd72988..bcc2f64bea7 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_2.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@mainnet_2.snap @@ -5,11 +5,11 @@ expression: stored_address_utxos [ ("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", [ Output( - value: Amount(12500), + value: 12500, lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"), ), Output( - value: Amount(25000), + value: 25000, lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"), ), ]), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_1.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_1.snap index e4889ecbb1d..3213143c76b 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_1.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_1.snap @@ -5,7 +5,7 @@ expression: stored_address_utxos [ ("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", [ Output( - value: Amount(12500), + value: 12500, lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"), ), ]), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_2.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_2.snap index 712850ae6b3..fdcbadc625f 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_2.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/address_utxo_data@testnet_2.snap @@ -5,11 +5,11 @@ expression: stored_address_utxos [ ("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", [ Output( - value: Amount(12500), + value: 12500, lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"), ), Output( - value: Amount(25000), + value: 25000, lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"), ), ]), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_1.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_1.snap index 0a32521109b..b7d1fae44b6 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_1.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_1.snap @@ -18,7 +18,7 @@ expression: stored_utxos output_index: OutputIndex(0), ), Some(Utxo( output: Output( - value: Amount(50000), + value: 50000, lock_script: Script("21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac"), ), height: Height(1), @@ -32,7 +32,7 @@ expression: stored_utxos output_index: OutputIndex(1), ), Some(Utxo( output: Output( - value: Amount(12500), + value: 12500, lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"), ), height: Height(1), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_2.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_2.snap index a64b3d04f63..7bb5ff79ea9 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_2.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@mainnet_2.snap @@ -18,7 +18,7 @@ expression: stored_utxos output_index: OutputIndex(0), ), Some(Utxo( output: Output( - value: Amount(50000), + value: 50000, lock_script: Script("21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac"), ), height: Height(1), @@ -32,7 +32,7 @@ expression: stored_utxos output_index: OutputIndex(1), ), Some(Utxo( output: Output( - value: Amount(12500), + value: 12500, lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"), ), height: Height(1), @@ -46,7 +46,7 @@ expression: stored_utxos output_index: OutputIndex(0), ), Some(Utxo( output: Output( - value: Amount(100000), + value: 100000, lock_script: Script("21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac"), ), height: Height(2), @@ -60,7 +60,7 @@ expression: stored_utxos output_index: OutputIndex(1), ), Some(Utxo( output: Output( - value: Amount(25000), + value: 25000, lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"), ), height: Height(2), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_1.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_1.snap index 45e2af9a200..ffe2dbb4aa4 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_1.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_1.snap @@ -18,7 +18,7 @@ expression: stored_utxos output_index: OutputIndex(0), ), Some(Utxo( output: Output( - value: Amount(50000), + value: 50000, lock_script: Script("21025229e1240a21004cf8338db05679fa34753706e84f6aebba086ba04317fd8f99ac"), ), height: Height(1), @@ -32,7 +32,7 @@ expression: stored_utxos output_index: OutputIndex(1), ), Some(Utxo( output: Output( - value: Amount(12500), + value: 12500, lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"), ), height: Height(1), diff --git a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_2.snap b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_2.snap index 2738dda5364..56b64c56a80 100644 --- a/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_2.snap +++ b/zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshots/utxos@testnet_2.snap @@ -18,7 +18,7 @@ expression: stored_utxos output_index: OutputIndex(0), ), Some(Utxo( output: Output( - value: Amount(50000), + value: 50000, lock_script: Script("21025229e1240a21004cf8338db05679fa34753706e84f6aebba086ba04317fd8f99ac"), ), height: Height(1), @@ -32,7 +32,7 @@ expression: stored_utxos output_index: OutputIndex(1), ), Some(Utxo( output: Output( - value: Amount(12500), + value: 12500, lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"), ), height: Height(1), @@ -46,7 +46,7 @@ expression: stored_utxos output_index: OutputIndex(0), ), Some(Utxo( output: Output( - value: Amount(100000), + value: 100000, lock_script: Script("2102acce9f6c16986c525fd34759d851ef5b4b85b5019a57bd59747be0ef1ba62523ac"), ), height: Height(2), @@ -60,7 +60,7 @@ expression: stored_utxos output_index: OutputIndex(1), ), Some(Utxo( output: Output( - value: Amount(25000), + value: 25000, lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"), ), height: Height(2), diff --git a/zebrad/Cargo.toml b/zebrad/Cargo.toml index 3782eb1009b..57a61da6c31 100644 --- a/zebrad/Cargo.toml +++ b/zebrad/Cargo.toml @@ -15,9 +15,15 @@ default-run = "zebrad" [features] # In release builds, don't compile debug logging code, to improve performance. default = ["release_max_level_info"] -getblocktemplate-rpcs = ["zebra-rpc/getblocktemplate-rpcs"] -# Production features that activate extra dependencies +# Production features that activate extra dependencies, or extra features in dependencies + +# Experimental mining RPC support +getblocktemplate-rpcs = [ + "zebra-rpc/getblocktemplate-rpcs", + "zebra-state/getblocktemplate-rpcs", + "zebra-node-services/getblocktemplate-rpcs", +] sentry = ["dep:sentry", "sentry-tracing"] flamegraph = ["tracing-flame", "inferno"] diff --git a/zebrad/src/components/mempool.rs b/zebrad/src/components/mempool.rs index 4d084071504..ab565b07873 100644 --- a/zebrad/src/components/mempool.rs +++ b/zebrad/src/components/mempool.rs @@ -424,6 +424,7 @@ impl Service for Mempool { async move { Ok(Response::TransactionIds(res)) }.boxed() } + Request::TransactionsById(ref ids) => { trace!(?req, "got mempool request"); @@ -445,6 +446,17 @@ impl Service for Mempool { async move { Ok(Response::Transactions(res)) }.boxed() } + #[cfg(feature = "getblocktemplate-rpcs")] + Request::Transactions => { + trace!(?req, "got mempool request"); + + let res: Vec<_> = storage.transactions().cloned().collect(); + + trace!(?req, res_count = ?res.len(), "answered mempool request"); + + async move { Ok(Response::Transactions(res)) }.boxed() + } + Request::RejectedTransactionIds(ref ids) => { trace!(?req, "got mempool request"); @@ -491,8 +503,12 @@ impl Service for Mempool { let resp = match req { // Empty Queries Request::TransactionIds => Response::TransactionIds(Default::default()), + Request::TransactionsById(_) => Response::Transactions(Default::default()), Request::TransactionsByMinedId(_) => Response::Transactions(Default::default()), + #[cfg(feature = "getblocktemplate-rpcs")] + Request::Transactions => Response::Transactions(Default::default()), + Request::RejectedTransactionIds(_) => { Response::RejectedTransactionIds(Default::default()) } diff --git a/zebrad/src/lib.rs b/zebrad/src/lib.rs index 07cd9884ff1..1dc8e2a2150 100644 --- a/zebrad/src/lib.rs +++ b/zebrad/src/lib.rs @@ -55,6 +55,10 @@ //! //! The following `zebrad` feature flags are available at compile time: //! +//! ### JSON-RPC +//! +//! * `getblocktemplate-rpcs`: Experimental mining pool RPC support (currently incomplete) +//! //! ### Metrics //! //! * `prometheus`: export metrics to prometheus.