change(rpc): Adds ignored jsonrequestobject argument to the getblocktemplate RPC (#5772)

* Adds ignored jsonrequestobject argument

* adds docs

returns an error for unsupported options

* returns an error when longpollid is provided

* comments out capabilities enum and uses Vec<String> instead

* fixes spelling mistake

* uncomments GetBlockTemplateCapability enum and uses allow(dead_code) instead

* adds UnknownCapability

* Apply suggestions from code review

Co-authored-by: teor <teor@riseup.net>

* adds derived traits.

* removes unused [allow(dead_code)]

* test invalid params error for block data

* add capablity to gbt vectors test

* reverts passing in default options to getblocktemplate request in vectors test

* adds a jsonrequestobject with an unknown capability to gbt acceptance test

Co-authored-by: teor <teor@riseup.net>
This commit is contained in:
Arya 2022-12-07 17:38:53 -05:00 committed by GitHub
parent 54d2b74378
commit 4664ab289c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 187 additions and 19 deletions

View File

@ -33,8 +33,9 @@ use zebra_state::{ReadRequest, ReadResponse};
use crate::methods::{
best_chain_tip_height,
get_block_template_rpcs::types::{
default_roots::DefaultRoots, get_block_template::GetBlockTemplate, hex_data::HexData,
submit_block, transaction::TransactionTemplate,
default_roots::DefaultRoots, get_block_template::GetBlockTemplate,
get_block_template_opts::GetBlockTemplateRequestMode, hex_data::HexData, submit_block,
transaction::TransactionTemplate,
},
GetBlockHash, MISSING_BLOCK_ERROR_CODE,
};
@ -42,7 +43,7 @@ use crate::methods::{
pub mod config;
pub mod constants;
pub(crate) mod types;
pub mod types;
pub(crate) mod zip317;
/// The max estimated distance to the chain tip for the getblocktemplate method.
@ -113,7 +114,10 @@ pub trait GetBlockTemplateRpc {
///
/// 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>>;
fn get_block_template(
&self,
options: Option<types::get_block_template_opts::JsonParameters>,
) -> BoxFuture<Result<GetBlockTemplate>>;
/// Submits block to the node to be validated and committed.
/// Returns the [`submit_block::Response`] for the operation, as a JSON string.
@ -292,7 +296,10 @@ where
.boxed()
}
fn get_block_template(&self) -> BoxFuture<Result<GetBlockTemplate>> {
fn get_block_template(
&self,
options: Option<types::get_block_template_opts::JsonParameters>,
) -> BoxFuture<Result<GetBlockTemplate>> {
let network = self.network;
let miner_address = self.miner_address;
@ -303,6 +310,24 @@ where
// Since this is a very large RPC, we use separate functions for each group of fields.
async move {
if let Some(options) = options {
if options.data.is_some() || options.mode == GetBlockTemplateRequestMode::Proposal {
return Err(Error {
code: ErrorCode::InvalidParams,
message: "\"proposal\" mode is currently unsupported by Zebra".to_string(),
data: None,
})
}
if options.longpollid.is_some() {
return Err(Error {
code: ErrorCode::InvalidParams,
message: "long polling is currently unsupported by Zebra".to_string(),
data: None,
})
}
}
let miner_address = miner_address.ok_or_else(|| Error {
code: ErrorCode::ServerError(0),
message: "configure mining.miner_address in zebrad.toml \

View File

@ -1,7 +1,8 @@
//! Types used in mining RPC methods.
pub(crate) mod default_roots;
pub(crate) mod get_block_template;
pub(crate) mod hex_data;
pub(crate) mod submit_block;
pub(crate) mod transaction;
pub mod default_roots;
pub mod get_block_template;
pub mod get_block_template_opts;
pub mod hex_data;
pub mod submit_block;
pub mod transaction;

View File

@ -0,0 +1,91 @@
//! Parameter types for the `getblocktemplate` RPC.
use super::hex_data::HexData;
/// Defines whether the RPC method should generate a block template or attempt to validate a block proposal.
/// `Proposal` mode is currently unsupported and will return an error.
#[derive(Clone, Debug, serde::Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum GetBlockTemplateRequestMode {
/// Indicates a request for a block template.
Template,
/// Indicates a request to validate block data.
/// Currently unsupported and will return an error.
Proposal,
}
impl Default for GetBlockTemplateRequestMode {
fn default() -> Self {
Self::Template
}
}
/// Valid `capabilities` values that indicate client-side support.
#[derive(Clone, Debug, serde::Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum GetBlockTemplateCapability {
/// Long Polling support.
/// Currently ignored by zebra.
LongPoll,
/// Information for coinbase transaction, default template data with the `coinbasetxn` field.
/// Currently ignored by zebra.
CoinbaseTxn,
/// Coinbase value, template response provides a `coinbasevalue` field and omits `coinbasetxn` field.
/// Currently ignored by zebra.
CoinbaseValue,
/// Components of the coinbase transaction.
/// Currently ignored by zebra.
CoinbaseAux,
/// Currently ignored by zcashd and zebra.
Proposal,
/// Currently ignored by zcashd and zebra.
ServerList,
/// Currently ignored by zcashd and zebra.
WorkId,
/// Unknown capability to fill in for mutations.
// TODO: Fill out valid mutations capabilities.
#[serde(other)]
UnknownCapability,
}
/// Optional argument `jsonrequestobject` for `getblocktemplate` RPC request.
///
/// The `data` field must be provided in `proposal` mode, and must be omitted in `template` mode.
/// All other fields are optional.
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, Default)]
pub struct JsonParameters {
/// Must be set to "template" or omitted, as "proposal" mode is currently unsupported.
///
/// Defines whether the RPC method should generate a block template or attempt to
/// validate block data, checking against all of the server's usual acceptance rules
/// (excluding the check for a valid proof-of-work).
// TODO: Support `proposal` mode.
#[serde(default)]
pub mode: GetBlockTemplateRequestMode,
/// Must be omitted as "proposal" mode is currently unsupported.
///
/// Hex-encoded block data to be validated and checked against the server's usual acceptance rules
/// (excluding the check for a valid proof-of-work) when `mode` is set to `proposal`.
pub data: Option<HexData>,
/// A list of client-side supported capability features
// TODO: Fill out valid mutations capabilities.
#[serde(default)]
pub capabilities: Vec<GetBlockTemplateCapability>,
/// An id to wait for, in zcashd this is the tip hash and an internal counter.
///
/// If provided, the RPC response is delayed until the mempool or chain tip block changes.
///
/// Currently unsupported and ignored by Zebra.
pub longpollid: Option<String>,
}

View File

@ -2,5 +2,5 @@
//! for the `submitblock` RPC method.
/// Deserialize hex-encoded strings to bytes.
#[derive(Debug, PartialEq, Eq, serde::Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
pub struct HexData(#[serde(with = "hex")] pub Vec<u8>);

View File

@ -7,9 +7,23 @@ use crate::methods::get_block_template_rpcs::GetBlockTemplateRpc;
/// Optional argument `jsonparametersobject` for `submitblock` RPC request
///
/// See notes for [`GetBlockTemplateRpc::submit_block`] method
#[derive(Debug, serde::Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
pub struct JsonParameters {
pub(crate) _work_id: Option<String>,
/// The workid for the block template.
///
/// > If the server provided a workid, it MUST be included with submissions,
/// currently unused.
///
/// Rationale:
///
/// > If servers allow all mutations, it may be hard to identify which job it is based on.
/// > While it may be possible to verify the submission by its content, it is much easier
/// > to compare it to the job issued. It is very easy for the miner to keep track of this.
/// > Therefore, using a "workid" is a very cheap solution to enable more mutations.
///
/// <https://en.bitcoin.it/wiki/BIP_0022#Rationale>
#[serde(rename = "workid")]
pub _work_id: Option<String>,
}
/// Response to a `submitblock` RPC request.

View File

@ -163,7 +163,7 @@ pub async fn test_responses<State, ReadState>(
})));
});
let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template());
let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template(None));
mempool
.expect_request(mempool::Request::FullTransactions)

View File

@ -865,7 +865,7 @@ async fn rpc_getblocktemplate() {
})));
});
let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template());
let get_block_template = tokio::spawn(get_block_template_rpc.get_block_template(None));
mempool
.expect_request(mempool::Request::FullTransactions)
@ -921,7 +921,7 @@ async fn rpc_getblocktemplate() {
mock_chain_tip_sender.send_estimated_distance_to_network_chain_tip(Some(200));
let get_block_template_sync_error = get_block_template_rpc
.get_block_template()
.get_block_template(None)
.await
.expect_err("needs an error when estimated distance to network chain tip is far");
@ -934,7 +934,7 @@ async fn rpc_getblocktemplate() {
mock_chain_tip_sender.send_estimated_distance_to_network_chain_tip(Some(0));
let get_block_template_sync_error = get_block_template_rpc
.get_block_template()
.get_block_template(None)
.await
.expect_err("needs an error when syncer is not close to tip");
@ -945,7 +945,7 @@ async fn rpc_getblocktemplate() {
mock_chain_tip_sender.send_estimated_distance_to_network_chain_tip(Some(200));
let get_block_template_sync_error = get_block_template_rpc
.get_block_template()
.get_block_template(None)
.await
.expect_err("needs an error when syncer is not close to tip or estimated distance to network chain tip is far");
@ -953,6 +953,39 @@ async fn rpc_getblocktemplate() {
get_block_template_sync_error.code,
ErrorCode::ServerError(-10)
);
let get_block_template_sync_error = get_block_template_rpc
.get_block_template(Some(get_block_template_rpcs::types::get_block_template_opts::JsonParameters {
mode: get_block_template_rpcs::types::get_block_template_opts::GetBlockTemplateRequestMode::Proposal,
..Default::default()
}))
.await
.expect_err("needs an error when using unsupported mode");
assert_eq!(get_block_template_sync_error.code, ErrorCode::InvalidParams);
let get_block_template_sync_error = get_block_template_rpc
.get_block_template(Some(
get_block_template_rpcs::types::get_block_template_opts::JsonParameters {
data: Some(get_block_template_rpcs::types::hex_data::HexData("".into())),
..Default::default()
},
))
.await
.expect_err("needs an error when passing in block data");
assert_eq!(get_block_template_sync_error.code, ErrorCode::InvalidParams);
let get_block_template_sync_error = get_block_template_rpc
.get_block_template(Some(
get_block_template_rpcs::types::get_block_template_opts::JsonParameters {
longpollid: Some("".to_string()),
..Default::default()
},
))
.await
.expect_err("needs an error when using unsupported option");
assert_eq!(get_block_template_sync_error.code, ErrorCode::InvalidParams);
}
#[cfg(feature = "getblocktemplate-rpcs")]

View File

@ -63,7 +63,11 @@ pub(crate) async fn run() -> Result<()> {
with a mempool that is likely empty...",
);
let getblocktemplate_response = RPCRequestClient::new(rpc_address)
.call("getblocktemplate", "[]".to_string())
.call(
"getblocktemplate",
// test that unknown capabilities are parsed as valid input
"[{\"capabilities\": [\"generation\"]}]".to_string(),
)
.await?;
let is_response_success = getblocktemplate_response.status().is_success();