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::{
|
use crate::methods::{
|
||||||
best_chain_tip_height,
|
best_chain_tip_height,
|
||||||
get_block_template_rpcs::types::{
|
get_block_template_rpcs::types::{
|
||||||
default_roots::DefaultRoots, get_block_template::GetBlockTemplate, hex_data::HexData,
|
default_roots::DefaultRoots, get_block_template::GetBlockTemplate,
|
||||||
submit_block, transaction::TransactionTemplate,
|
get_block_template_opts::GetBlockTemplateRequestMode, hex_data::HexData, submit_block,
|
||||||
|
transaction::TransactionTemplate,
|
||||||
},
|
},
|
||||||
GetBlockHash, MISSING_BLOCK_ERROR_CODE,
|
GetBlockHash, MISSING_BLOCK_ERROR_CODE,
|
||||||
};
|
};
|
||||||
|
|
@ -42,7 +43,7 @@ use crate::methods::{
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
|
||||||
pub(crate) mod types;
|
pub mod types;
|
||||||
pub(crate) mod zip317;
|
pub(crate) mod zip317;
|
||||||
|
|
||||||
/// The max estimated distance to the chain tip for the getblocktemplate method.
|
/// 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`.
|
/// This rpc method is available only if zebra is built with `--features getblocktemplate-rpcs`.
|
||||||
#[rpc(name = "getblocktemplate")]
|
#[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.
|
/// Submits block to the node to be validated and committed.
|
||||||
/// Returns the [`submit_block::Response`] for the operation, as a JSON string.
|
/// Returns the [`submit_block::Response`] for the operation, as a JSON string.
|
||||||
|
|
@ -292,7 +296,10 @@ where
|
||||||
.boxed()
|
.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 network = self.network;
|
||||||
let miner_address = self.miner_address;
|
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.
|
// Since this is a very large RPC, we use separate functions for each group of fields.
|
||||||
async move {
|
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 {
|
let miner_address = miner_address.ok_or_else(|| Error {
|
||||||
code: ErrorCode::ServerError(0),
|
code: ErrorCode::ServerError(0),
|
||||||
message: "configure mining.miner_address in zebrad.toml \
|
message: "configure mining.miner_address in zebrad.toml \
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
//! Types used in mining RPC methods.
|
//! Types used in mining RPC methods.
|
||||||
|
|
||||||
pub(crate) mod default_roots;
|
pub mod default_roots;
|
||||||
pub(crate) mod get_block_template;
|
pub mod get_block_template;
|
||||||
pub(crate) mod hex_data;
|
pub mod get_block_template_opts;
|
||||||
pub(crate) mod submit_block;
|
pub mod hex_data;
|
||||||
pub(crate) mod transaction;
|
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.
|
//! for the `submitblock` RPC method.
|
||||||
|
|
||||||
/// Deserialize hex-encoded strings to bytes.
|
/// 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>);
|
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
|
/// Optional argument `jsonparametersobject` for `submitblock` RPC request
|
||||||
///
|
///
|
||||||
/// See notes for [`GetBlockTemplateRpc::submit_block`] method
|
/// See notes for [`GetBlockTemplateRpc::submit_block`] method
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
|
||||||
pub struct JsonParameters {
|
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.
|
/// 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
|
mempool
|
||||||
.expect_request(mempool::Request::FullTransactions)
|
.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
|
mempool
|
||||||
.expect_request(mempool::Request::FullTransactions)
|
.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));
|
mock_chain_tip_sender.send_estimated_distance_to_network_chain_tip(Some(200));
|
||||||
let get_block_template_sync_error = get_block_template_rpc
|
let get_block_template_sync_error = get_block_template_rpc
|
||||||
.get_block_template()
|
.get_block_template(None)
|
||||||
.await
|
.await
|
||||||
.expect_err("needs an error when estimated distance to network chain tip is far");
|
.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));
|
mock_chain_tip_sender.send_estimated_distance_to_network_chain_tip(Some(0));
|
||||||
let get_block_template_sync_error = get_block_template_rpc
|
let get_block_template_sync_error = get_block_template_rpc
|
||||||
.get_block_template()
|
.get_block_template(None)
|
||||||
.await
|
.await
|
||||||
.expect_err("needs an error when syncer is not close to tip");
|
.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));
|
mock_chain_tip_sender.send_estimated_distance_to_network_chain_tip(Some(200));
|
||||||
let get_block_template_sync_error = get_block_template_rpc
|
let get_block_template_sync_error = get_block_template_rpc
|
||||||
.get_block_template()
|
.get_block_template(None)
|
||||||
.await
|
.await
|
||||||
.expect_err("needs an error when syncer is not close to tip or estimated distance to network chain tip is far");
|
.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,
|
get_block_template_sync_error.code,
|
||||||
ErrorCode::ServerError(-10)
|
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")]
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,11 @@ pub(crate) async fn run() -> Result<()> {
|
||||||
with a mempool that is likely empty...",
|
with a mempool that is likely empty...",
|
||||||
);
|
);
|
||||||
let getblocktemplate_response = RPCRequestClient::new(rpc_address)
|
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?;
|
.await?;
|
||||||
|
|
||||||
let is_response_success = getblocktemplate_response.status().is_success();
|
let is_response_success = getblocktemplate_response.status().is_success();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue