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:
parent
54d2b74378
commit
4664ab289c
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>,
|
||||
}
|
|
@ -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>);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue