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<UnminedTx> 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 <oxarbitrage@gmail.com> * Update default roots docs * Fix comment typo * Fix a comment typo Co-authored-by: Arya <aryasolhi@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com> Co-authored-by: Arya <aryasolhi@gmail.com>
This commit is contained in:
parent
26d0455d02
commit
142411508b
|
@ -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<T, E = Error> = std::result::Result<T, E>;
|
|||
/// 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<C = NegativeAllowed>(
|
||||
/// 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<i64> {
|
||||
-MAX_MONEY..=0
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of zatoshis in 1 ZEC
|
||||
pub const COIN: i64 = 100_000_000;
|
||||
|
||||
|
|
|
@ -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<ChainHistoryMmrRootHash> 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<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()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToHex for ChainHistoryMmrRootHash {
|
||||
fn encode_hex<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex()
|
||||
}
|
||||
|
||||
fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex_upper()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromHex for ChainHistoryMmrRootHash {
|
||||
type Error = <[u8; 32] as FromHex>::Error;
|
||||
|
||||
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
||||
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<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()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToHex for ChainHistoryBlockTxAuthCommitmentHash {
|
||||
fn encode_hex<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex()
|
||||
}
|
||||
|
||||
fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex_upper()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromHex for ChainHistoryBlockTxAuthCommitmentHash {
|
||||
type Error = <[u8; 32] as FromHex>::Error;
|
||||
|
||||
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
||||
let mut hash = <[u8; 32]>::from_hex(hex)?;
|
||||
hash.reverse();
|
||||
|
||||
Ok(hash.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when checking RootHash consensus rules.
|
||||
|
|
|
@ -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<Root> 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<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()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToHex for Root {
|
||||
fn encode_hex<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex()
|
||||
}
|
||||
|
||||
fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex_upper()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromHex for Root {
|
||||
type Error = <[u8; 32] as FromHex>::Error;
|
||||
|
||||
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
||||
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<UnminedTx> for Root {
|
||||
fn from_iter<I>(transactions: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = UnminedTx>,
|
||||
{
|
||||
transactions
|
||||
.into_iter()
|
||||
.map(|tx| tx.id.mined_id())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::FromIterator<UnminedTxId> for Root {
|
||||
fn from_iter<I>(tx_ids: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = UnminedTxId>,
|
||||
{
|
||||
tx_ids.into_iter().map(|tx_id| tx_id.mined_id()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::FromIterator<transaction::Hash> 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<I>(hashes: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = transaction::Hash>,
|
||||
|
@ -166,6 +259,71 @@ impl From<AuthDataRoot> 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<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()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToHex for AuthDataRoot {
|
||||
fn encode_hex<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex()
|
||||
}
|
||||
|
||||
fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex_upper()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromHex for AuthDataRoot {
|
||||
type Error = <[u8; 32] as FromHex>::Error;
|
||||
|
||||
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
||||
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.
|
||||
///
|
||||
/// <https://zips.z.cash/zip-0244#authorizing-data-commitment>
|
||||
pub const AUTH_DIGEST_PLACEHOLDER: transaction::AuthDigest = transaction::AuthDigest([0xFF; 32]);
|
||||
|
||||
impl<T> std::iter::FromIterator<T> for AuthDataRoot
|
||||
where
|
||||
T: std::convert::AsRef<Transaction>,
|
||||
|
@ -174,17 +332,33 @@ where
|
|||
where
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
// > 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<UnminedTx> for AuthDataRoot {
|
||||
fn from_iter<I>(transactions: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = UnminedTx>,
|
||||
{
|
||||
transactions
|
||||
.into_iter()
|
||||
.map(|tx| tx.id.auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::FromIterator<UnminedTxId> for AuthDataRoot {
|
||||
fn from_iter<I>(tx_ids: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = UnminedTxId>,
|
||||
{
|
||||
tx_ids
|
||||
.into_iter()
|
||||
.map(|tx_id| tx_id.auth_digest().unwrap_or(AUTH_DIGEST_PLACEHOLDER))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Transaction> 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<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()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToHex for AuthDigest {
|
||||
fn encode_hex<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex()
|
||||
}
|
||||
|
||||
fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
|
||||
(&self).encode_hex_upper()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromHex for AuthDigest {
|
||||
type Error = <[u8; 32] as FromHex>::Error;
|
||||
|
||||
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
||||
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::<String>())
|
||||
}
|
||||
}
|
||||
|
||||
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::<String>())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Vec<u8>> for SerializedTransaction {
|
|||
Self { bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromHex for SerializedTransaction {
|
||||
type Error = <Vec<u8> as FromHex>::Error;
|
||||
|
||||
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
||||
let bytes = <Vec<u8>>::from_hex(hex)?;
|
||||
|
||||
Ok(bytes.into())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Transaction>,
|
||||
|
||||
/// A unique identifier for this unmined transaction.
|
||||
pub id: UnminedTxId,
|
||||
|
||||
/// The size in bytes of the serialized transaction data
|
||||
pub size: usize,
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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<Hash>),
|
||||
|
||||
/// 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<UnminedTxId>),
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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<Result<GetBlockHash>>;
|
||||
|
||||
/// 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<Result<GetBlockTemplate>>;
|
||||
}
|
||||
|
@ -84,7 +100,6 @@ where
|
|||
// Services
|
||||
//
|
||||
/// A handle to the mempool service.
|
||||
#[allow(dead_code)]
|
||||
mempool: Buffer<Mempool, mempool::Request>,
|
||||
|
||||
/// A handle to the state service.
|
||||
|
@ -194,33 +209,94 @@ where
|
|||
}
|
||||
|
||||
fn get_block_template(&self) -> BoxFuture<Result<GetBlockTemplate>> {
|
||||
async move {
|
||||
let empty_string = String::from("");
|
||||
let mempool = self.mempool.clone();
|
||||
|
||||
// Returns empty `GetBlockTemplate`
|
||||
// Since this is a very large RPC, we use separate functions for each group of fields.
|
||||
async move {
|
||||
// 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;
|
||||
|
||||
// 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: vec![],
|
||||
coinbase_txn: Coinbase {},
|
||||
|
||||
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,
|
||||
},
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {}
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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<String>,
|
||||
/// 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<Transaction>,
|
||||
/// Add documentation.
|
||||
|
||||
/// The non-coinbase transactions selected for this block template.
|
||||
///
|
||||
/// TODO: select these transactions using ZIP-317 (#5473)
|
||||
pub transactions: Vec<TransactionTemplate<amount::NonNegative>>,
|
||||
|
||||
/// The coinbase transaction generated from `transactions` and `height`.
|
||||
#[serde(rename = "coinbasetxn")]
|
||||
pub coinbase_txn: Coinbase,
|
||||
pub coinbase_txn: TransactionTemplate<amount::NegativeOrZero>,
|
||||
|
||||
/// 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<String>,
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
|
|
@ -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<FeeConstraint>
|
||||
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<u16>,
|
||||
|
||||
/// 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<FeeConstraint>,
|
||||
|
||||
/// 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<NonNegative> {
|
||||
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<UnminedTx> for TransactionTemplate<NonNegative> {
|
||||
fn from(tx: UnminedTx) -> Self {
|
||||
Self::from(&tx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use zebra_test::mock_service::{MockService, PanicAssertion};
|
|||
use crate::methods::{GetBlockHash, GetBlockTemplateRpc, GetBlockTemplateRpcImpl};
|
||||
|
||||
pub async fn test_responses<State>(
|
||||
mempool: MockService<
|
||||
mut mempool: MockService<
|
||||
mempool::Request,
|
||||
mempool::Response,
|
||||
PanicAssertion,
|
||||
|
@ -58,10 +58,18 @@ pub async fn test_responses<State>(
|
|||
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
|
||||
.expect("We should have a GetBlockTemplate struct");
|
||||
.respond(mempool::Response::Transactions(vec![]));
|
||||
|
||||
let get_block_template = get_block_template
|
||||
.await
|
||||
.expect("unexpected panic in getblocktemplate RPC task")
|
||||
.expect("unexpected error in getblocktemplate RPC call");
|
||||
|
||||
snapshot_rpc_getblocktemplate(get_block_template, &settings);
|
||||
}
|
||||
|
||||
|
|
|
@ -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": [],
|
||||
|
|
|
@ -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": [],
|
||||
|
|
|
@ -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
|
||||
.expect("We should have a GetBlockTemplate struct");
|
||||
.respond(mempool::Response::Transactions(vec![]));
|
||||
|
||||
let get_block_template = get_block_template
|
||||
.await
|
||||
.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());
|
||||
|
|
|
@ -4,7 +4,7 @@ expression: stored_address_balances
|
|||
---
|
||||
[
|
||||
("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", AddressBalanceLocation(
|
||||
balance: Amount(12500),
|
||||
balance: 12500,
|
||||
location: OutputLocation(
|
||||
transaction_location: TransactionLocation(
|
||||
height: Height(1),
|
||||
|
|
|
@ -4,7 +4,7 @@ expression: stored_address_balances
|
|||
---
|
||||
[
|
||||
("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", AddressBalanceLocation(
|
||||
balance: Amount(37500),
|
||||
balance: 37500,
|
||||
location: OutputLocation(
|
||||
transaction_location: TransactionLocation(
|
||||
height: Height(1),
|
||||
|
|
|
@ -4,7 +4,7 @@ expression: stored_address_balances
|
|||
---
|
||||
[
|
||||
("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", AddressBalanceLocation(
|
||||
balance: Amount(12500),
|
||||
balance: 12500,
|
||||
location: OutputLocation(
|
||||
transaction_location: TransactionLocation(
|
||||
height: Height(1),
|
||||
|
|
|
@ -4,7 +4,7 @@ expression: stored_address_balances
|
|||
---
|
||||
[
|
||||
("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", AddressBalanceLocation(
|
||||
balance: Amount(37500),
|
||||
balance: 37500,
|
||||
location: OutputLocation(
|
||||
transaction_location: TransactionLocation(
|
||||
height: Height(1),
|
||||
|
|
|
@ -5,7 +5,7 @@ expression: stored_address_utxos
|
|||
[
|
||||
("t3Vz22vK5z2LcKEdg16Yv4FFneEL1zg9ojd", [
|
||||
Output(
|
||||
value: Amount(12500),
|
||||
value: 12500,
|
||||
lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"),
|
||||
),
|
||||
]),
|
||||
|
|
|
@ -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"),
|
||||
),
|
||||
]),
|
||||
|
|
|
@ -5,7 +5,7 @@ expression: stored_address_utxos
|
|||
[
|
||||
("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi", [
|
||||
Output(
|
||||
value: Amount(12500),
|
||||
value: 12500,
|
||||
lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"),
|
||||
),
|
||||
]),
|
||||
|
|
|
@ -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"),
|
||||
),
|
||||
]),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -424,6 +424,7 @@ impl Service<Request> for Mempool {
|
|||
|
||||
async move { Ok(Response::TransactionIds(res)) }.boxed()
|
||||
}
|
||||
|
||||
Request::TransactionsById(ref ids) => {
|
||||
trace!(?req, "got mempool request");
|
||||
|
||||
|
@ -445,6 +446,17 @@ impl Service<Request> 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<Request> 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())
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue