From 4ccd0741b61e020ab0d95a8eac9b6f1831e802bc Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Tue, 8 Nov 2022 19:55:11 -0300 Subject: [PATCH] feature(rpc): add fixed values to getblocktemplate response (#5558) * add fixed values to getblocktemplate rpc call response * suggestion: Avoid new uses of lazy_static (#5559) * Avoid using lazy_static * Add some missing documentation Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> * minor fixes * move docs to struct * add fixed values to coinbase tx Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> Co-authored-by: teor --- zebra-chain/src/block.rs | 2 +- zebra-chain/src/block/header.rs | 6 +++ zebra-chain/src/block/serialize.rs | 4 +- zebra-consensus/src/lib.rs | 2 +- .../src/methods/get_block_template_rpcs.rs | 36 +++++++++++------ .../get_block_template_rpcs/constants.rs | 7 ++++ .../types/default_roots.rs | 5 ++- .../types/get_block_template.rs | 40 +++++++++++-------- .../tests/snapshot/get_block_template_rpcs.rs | 9 ++--- .../get_block_template@mainnet_10.snap | 15 ++++--- .../get_block_template@testnet_10.snap | 15 ++++--- zebra-rpc/src/methods/tests/vectors.rs | 22 +++++++--- 12 files changed, 109 insertions(+), 54 deletions(-) create mode 100644 zebra-rpc/src/methods/get_block_template_rpcs/constants.rs diff --git a/zebra-chain/src/block.rs b/zebra-chain/src/block.rs index 390f5fac7b1..a7242641160 100644 --- a/zebra-chain/src/block.rs +++ b/zebra-chain/src/block.rs @@ -34,7 +34,7 @@ pub use commitment::{ ChainHistoryBlockTxAuthCommitmentHash, ChainHistoryMmrRootHash, Commitment, CommitmentError, }; pub use hash::Hash; -pub use header::{BlockTimeError, CountedHeader, Header}; +pub use header::{BlockTimeError, CountedHeader, Header, ZCASH_BLOCK_VERSION}; pub use height::Height; pub use serialize::{SerializedBlock, MAX_BLOCK_BYTES}; diff --git a/zebra-chain/src/block/header.rs b/zebra-chain/src/block/header.rs index e30f3eb12e9..a1492b566ba 100644 --- a/zebra-chain/src/block/header.rs +++ b/zebra-chain/src/block/header.rs @@ -146,6 +146,12 @@ const BLOCK_HEADER_LENGTH: usize = /// A CountedHeader has BLOCK_HEADER_LENGTH bytes + 1 or more bytes for the transaction count pub(crate) const MIN_COUNTED_HEADER_LEN: usize = BLOCK_HEADER_LENGTH + 1; +/// The Zcash accepted block version. +/// +/// The consensus rules do not force the block version to be this value but just equal or greater than it. +/// However, it is suggested that submitted block versions to be of this exact value. +pub const ZCASH_BLOCK_VERSION: u32 = 4; + impl TrustedPreallocate for CountedHeader { fn max_allocation() -> u64 { // Every vector type requires a length field of at least one byte for de/serialization. diff --git a/zebra-chain/src/block/serialize.rs b/zebra-chain/src/block/serialize.rs index 8c0907aee99..ad9558000e0 100644 --- a/zebra-chain/src/block/serialize.rs +++ b/zebra-chain/src/block/serialize.rs @@ -13,7 +13,7 @@ use crate::{ work::{difficulty::CompactDifficulty, equihash}, }; -use super::{merkle, Block, CountedHeader, Hash, Header}; +use super::{header::ZCASH_BLOCK_VERSION, merkle, Block, CountedHeader, Hash, Header}; /// The maximum size of a Zcash block, in bytes. /// @@ -77,7 +77,7 @@ impl ZcashDeserialize for Header { // > The block version number MUST be greater than or equal to 4. // // https://zips.z.cash/protocol/protocol.pdf#blockheader - if version < 4 { + if version < ZCASH_BLOCK_VERSION { return Err(SerializationError::Parse("version must be at least 4")); } diff --git a/zebra-consensus/src/lib.rs b/zebra-consensus/src/lib.rs index 1c5fa20886b..cb5d9480a54 100644 --- a/zebra-consensus/src/lib.rs +++ b/zebra-consensus/src/lib.rs @@ -46,7 +46,7 @@ pub mod chain; #[allow(missing_docs)] pub mod error; -pub use block::VerifyBlockError; +pub use block::{VerifyBlockError, MAX_BLOCK_SIGOPS}; pub use chain::VerifyChainError; pub use checkpoint::{ CheckpointList, VerifyCheckpointError, MAX_CHECKPOINT_BYTE_COUNT, MAX_CHECKPOINT_HEIGHT_GAP, diff --git a/zebra-rpc/src/methods/get_block_template_rpcs.rs b/zebra-rpc/src/methods/get_block_template_rpcs.rs index a0c921ade2d..348fd9500cf 100644 --- a/zebra-rpc/src/methods/get_block_template_rpcs.rs +++ b/zebra-rpc/src/methods/get_block_template_rpcs.rs @@ -13,14 +13,16 @@ use zebra_chain::{ block::{ self, merkle::{self, AuthDataRoot}, - Block, + Block, MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION, }, chain_tip::ChainTip, parameters::Network, serialization::ZcashDeserializeInto, transaction::{UnminedTx, VerifiedUnminedTx}, }; -use zebra_consensus::{BlockError, VerifyBlockError, VerifyChainError, VerifyCheckpointError}; +use zebra_consensus::{ + BlockError, VerifyBlockError, VerifyChainError, VerifyCheckpointError, MAX_BLOCK_SIGOPS, +}; use zebra_node_services::mempool; use crate::methods::{ @@ -33,6 +35,7 @@ use crate::methods::{ }; pub mod config; +pub mod constants; pub(crate) mod types; /// getblocktemplate RPC method signatures. @@ -273,12 +276,12 @@ where TODO: create a method Transaction::new_v5_coinbase(network, tip_height, miner_fee), and call it here. - */ + */ let coinbase_tx = if mempool_txs.is_empty() { let empty_string = String::from(""); return Ok(GetBlockTemplate { capabilities: vec![], - version: 0, + version: ZCASH_BLOCK_VERSION, previous_block_hash: GetBlockHash([0; 32].into()), block_commitments_hash: [0; 32].into(), light_client_root_hash: [0; 32].into(), @@ -301,10 +304,13 @@ where }, target: empty_string.clone(), min_time: 0, - mutable: vec![], - nonce_range: empty_string.clone(), - sigop_limit: 0, - size_limit: 0, + mutable: constants::GET_BLOCK_TEMPLATE_MUTABLE_FIELD + .iter() + .map(ToString::to_string) + .collect(), + nonce_range: constants::GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD.to_string(), + sigop_limit: MAX_BLOCK_SIGOPS, + size_limit: MAX_BLOCK_BYTES, cur_time: 0, bits: empty_string, height: 0, @@ -323,7 +329,7 @@ where Ok(GetBlockTemplate { capabilities: vec![], - version: 0, + version: ZCASH_BLOCK_VERSION, previous_block_hash: GetBlockHash([0; 32].into()), block_commitments_hash: [0; 32].into(), @@ -344,12 +350,16 @@ where min_time: 0, - mutable: vec![], + mutable: constants::GET_BLOCK_TEMPLATE_MUTABLE_FIELD + .iter() + .map(ToString::to_string) + .collect(), + + nonce_range: constants::GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD.to_string(), - nonce_range: empty_string.clone(), + sigop_limit: MAX_BLOCK_SIGOPS, - sigop_limit: 0, - size_limit: 0, + size_limit: MAX_BLOCK_BYTES, cur_time: 0, diff --git a/zebra-rpc/src/methods/get_block_template_rpcs/constants.rs b/zebra-rpc/src/methods/get_block_template_rpcs/constants.rs new file mode 100644 index 00000000000..2db3ed05ef7 --- /dev/null +++ b/zebra-rpc/src/methods/get_block_template_rpcs/constants.rs @@ -0,0 +1,7 @@ +//! Constant values used in mining rpcs methods. + +/// A range of valid nonces that goes from `u32::MIN` to `u32::MAX` as a string. +pub const GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD: &str = "00000000ffffffff"; + +/// A hardcoded list of fields that the miner can change from the block. +pub const GET_BLOCK_TEMPLATE_MUTABLE_FIELD: &[&str] = &["time", "transactions", "prevblock"]; 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 93272944c88..20e1f341b24 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 @@ -5,7 +5,10 @@ use zebra_chain::block::{ ChainHistoryBlockTxAuthCommitmentHash, ChainHistoryMmrRootHash, }; -/// Documentation to be added in #5452 or #5455. +/// The block header roots for [`GetBlockTemplate.transactions`]. +/// +/// If the transactions in the block template are modified, these roots must be recalculated +/// [according to the specification](https://zcash.github.io/rpc/getblocktemplate.html). #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct DefaultRoots { /// The merkle root of the transaction IDs in the block. 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 983b301171e..63840d15a47 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 @@ -12,35 +12,47 @@ use crate::methods::{ /// Documentation to be added after we document all the individual fields. #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)] pub struct GetBlockTemplate { - /// Add documentation. + /// The getblocktemplate RPC capabilities supported by Zebra. + /// + /// At the moment, Zebra does not support any of the extra capabilities from the specification: + /// - `proposal`: + /// - `longpoll`: + /// - `serverlist`: pub capabilities: Vec, /// 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. + /// The hash of the previous block. #[serde(rename = "previousblockhash")] pub previous_block_hash: GetBlockHash, - /// Add documentation. + /// The block commitment for the new block's header. + /// + /// Same as [`DefaultRoots.block_commitments_hash`], see that field for details. #[serde(rename = "blockcommitmentshash")] #[serde(with = "hex")] pub block_commitments_hash: ChainHistoryBlockTxAuthCommitmentHash, - /// Add documentation. + /// Legacy backwards-compatibility header root field. + /// + /// Same as [`DefaultRoots.block_commitments_hash`], see that field for details. #[serde(rename = "lightclientroothash")] #[serde(with = "hex")] pub light_client_root_hash: ChainHistoryBlockTxAuthCommitmentHash, - /// Add documentation. + /// Legacy backwards-compatibility header root field. + /// + /// Same as [`DefaultRoots.block_commitments_hash`], see that field for details. #[serde(rename = "finalsaplingroothash")] #[serde(with = "hex")] pub final_sapling_root_hash: ChainHistoryBlockTxAuthCommitmentHash, - /// Add documentation. + /// The block header roots for [`GetBlockTemplate.transactions`]. + /// + /// If the transactions in the block template are modified, these roots must be recalculated + /// [according to the specification](https://zcash.github.io/rpc/getblocktemplate.html). #[serde(rename = "defaultroots")] pub default_roots: DefaultRoots, @@ -62,22 +74,18 @@ pub struct GetBlockTemplate { // TODO: use DateTime32 type? pub min_time: u32, - /// Add documentation. + /// Hardcoded list of block fields the miner is allowed to change. pub mutable: Vec, - /// Add documentation. + /// A range of valid nonces that goes from `u32::MIN` to `u32::MAX`. #[serde(rename = "noncerange")] pub nonce_range: String, - /// Add documentation. - /// - /// The same as `MAX_BLOCK_SIGOPS`. + /// Max legacy signature operations in the block. #[serde(rename = "sigoplimit")] pub sigop_limit: u64, - /// Add documentation. - /// - /// The same as `MAX_BLOCK_BYTES`. + /// Max block size in bytes #[serde(rename = "sizelimit")] pub size_limit: u64, 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 daec0e53d2c..6ddd6ccf53e 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 @@ -15,7 +15,9 @@ use zebra_state::LatestChainTip; use zebra_test::mock_service::{MockService, PanicAssertion}; use crate::methods::{ - get_block_template_rpcs::types::{hex_data::HexData, submit_block}, + get_block_template_rpcs::types::{ + get_block_template::GetBlockTemplate, hex_data::HexData, submit_block, + }, GetBlockHash, GetBlockTemplateRpc, GetBlockTemplateRpcImpl, }; @@ -122,10 +124,7 @@ fn snapshot_rpc_getblockhash(block_hash: GetBlockHash, settings: &insta::Setting } /// Snapshot `getblocktemplate` response, using `cargo insta` and JSON serialization. -fn snapshot_rpc_getblocktemplate( - block_template: crate::methods::get_block_template_rpcs::types::get_block_template::GetBlockTemplate, - settings: &insta::Settings, -) { +fn snapshot_rpc_getblocktemplate(block_template: GetBlockTemplate, settings: &insta::Settings) { settings.bind(|| insta::assert_json_snapshot!("get_block_template", block_template)); } 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 cb8f20d834b..21fd8d57239 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 @@ -1,10 +1,11 @@ --- source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs +assertion_line: 128 expression: block_template --- { "capabilities": [], - "version": 0, + "version": 4, "previousblockhash": "0000000000000000000000000000000000000000000000000000000000000000", "blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000", "lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000", @@ -27,10 +28,14 @@ expression: block_template }, "target": "", "mintime": 0, - "mutable": [], - "noncerange": "", - "sigoplimit": 0, - "sizelimit": 0, + "mutable": [ + "time", + "transactions", + "prevblock" + ], + "noncerange": "00000000ffffffff", + "sigoplimit": 20000, + "sizelimit": 2000000, "curtime": 0, "bits": "", "height": 0 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 cb8f20d834b..21fd8d57239 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 @@ -1,10 +1,11 @@ --- source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs +assertion_line: 128 expression: block_template --- { "capabilities": [], - "version": 0, + "version": 4, "previousblockhash": "0000000000000000000000000000000000000000000000000000000000000000", "blockcommitmentshash": "0000000000000000000000000000000000000000000000000000000000000000", "lightclientroothash": "0000000000000000000000000000000000000000000000000000000000000000", @@ -27,10 +28,14 @@ expression: block_template }, "target": "", "mintime": 0, - "mutable": [], - "noncerange": "", - "sigoplimit": 0, - "sizelimit": 0, + "mutable": [ + "time", + "transactions", + "prevblock" + ], + "noncerange": "00000000ffffffff", + "sigoplimit": 20000, + "sizelimit": 2000000, "curtime": 0, "bits": "", "height": 0 diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs index 9a5907a5504..647e5be185a 100644 --- a/zebra-rpc/src/methods/tests/vectors.rs +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -782,6 +782,12 @@ async fn rpc_getblockhash() { async fn rpc_getblocktemplate() { use std::panic; + use crate::methods::get_block_template_rpcs::constants::{ + GET_BLOCK_TEMPLATE_MUTABLE_FIELD, GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD, + }; + use zebra_chain::block::{MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION}; + use zebra_consensus::MAX_BLOCK_SIGOPS; + let _init_guard = zebra_test::init(); // Create a continuous chain of mainnet blocks from genesis @@ -835,14 +841,20 @@ async fn rpc_getblocktemplate() { .expect("unexpected error in getblocktemplate RPC call"); assert!(get_block_template.capabilities.is_empty()); - assert_eq!(get_block_template.version, 0); + assert_eq!(get_block_template.version, ZCASH_BLOCK_VERSION); assert!(get_block_template.transactions.is_empty()); assert!(get_block_template.target.is_empty()); assert_eq!(get_block_template.min_time, 0); - assert!(get_block_template.mutable.is_empty()); - assert!(get_block_template.nonce_range.is_empty()); - assert_eq!(get_block_template.sigop_limit, 0); - assert_eq!(get_block_template.size_limit, 0); + assert_eq!( + get_block_template.mutable, + GET_BLOCK_TEMPLATE_MUTABLE_FIELD.to_vec() + ); + assert_eq!( + get_block_template.nonce_range, + GET_BLOCK_TEMPLATE_NONCE_RANGE_FIELD + ); + assert_eq!(get_block_template.sigop_limit, MAX_BLOCK_SIGOPS); + assert_eq!(get_block_template.size_limit, MAX_BLOCK_BYTES); assert_eq!(get_block_template.cur_time, 0); assert!(get_block_template.bits.is_empty()); assert_eq!(get_block_template.height, 0);