feat(rpc): introduce getblocktemplate rpc call with stub fields (#5462)
* introduce getblocktemplate rpc call * remove optional `longpollid` field * add underscore * create modules for types
This commit is contained in:
parent
a3302850d6
commit
fae9473076
|
@ -35,10 +35,10 @@ use zebra_state::{OutputIndex, OutputLocation, TransactionLocation};
|
||||||
use crate::queue::Queue;
|
use crate::queue::Queue;
|
||||||
|
|
||||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
mod get_block_template;
|
mod get_block_template_rpcs;
|
||||||
|
|
||||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
pub use get_block_template::{GetBlockTemplateRpc, GetBlockTemplateRpcImpl};
|
pub use get_block_template_rpcs::{GetBlockTemplateRpc, GetBlockTemplateRpcImpl};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
@ -6,7 +6,14 @@ use jsonrpc_core::{self, BoxFuture, Error, ErrorCode, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use tower::{Service, ServiceExt};
|
use tower::{Service, ServiceExt};
|
||||||
|
|
||||||
use crate::methods::{GetBlockHash, MISSING_BLOCK_ERROR_CODE};
|
pub(crate) mod types;
|
||||||
|
|
||||||
|
use crate::methods::{
|
||||||
|
get_block_template_rpcs::types::{
|
||||||
|
coinbase::Coinbase, default_roots::DefaultRoots, get_block_template::GetBlockTemplate,
|
||||||
|
},
|
||||||
|
GetBlockHash, MISSING_BLOCK_ERROR_CODE,
|
||||||
|
};
|
||||||
|
|
||||||
/// getblocktemplate RPC method signatures.
|
/// getblocktemplate RPC method signatures.
|
||||||
#[rpc(server)]
|
#[rpc(server)]
|
||||||
|
@ -35,8 +42,19 @@ pub trait GetBlockTemplateRpc {
|
||||||
///
|
///
|
||||||
/// - If `index` is positive then index = block height.
|
/// - If `index` is positive then index = block height.
|
||||||
/// - If `index` is negative then -1 is the last known valid block.
|
/// - If `index` is negative then -1 is the last known valid block.
|
||||||
|
/// - This rpc method is available only if zebra is built with `--features getblocktemplate-rpcs`.
|
||||||
#[rpc(name = "getblockhash")]
|
#[rpc(name = "getblockhash")]
|
||||||
fn get_block_hash(&self, index: i32) -> BoxFuture<Result<GetBlockHash>>;
|
fn get_block_hash(&self, index: i32) -> BoxFuture<Result<GetBlockHash>>;
|
||||||
|
|
||||||
|
/// Documentation to be filled as we go.
|
||||||
|
///
|
||||||
|
/// 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`.
|
||||||
|
#[rpc(name = "getblocktemplate")]
|
||||||
|
fn get_block_template(&self) -> BoxFuture<Result<GetBlockTemplate>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RPC method implementations.
|
/// RPC method implementations.
|
||||||
|
@ -139,6 +157,40 @@ where
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_block_template(&self) -> BoxFuture<Result<GetBlockTemplate>> {
|
||||||
|
async move {
|
||||||
|
let empty_string = String::from("");
|
||||||
|
|
||||||
|
// Returns empty `GetBlockTemplate`
|
||||||
|
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(),
|
||||||
|
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(),
|
||||||
|
},
|
||||||
|
transactions: vec![],
|
||||||
|
coinbase_txn: Coinbase {},
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a potentially negative index, find the corresponding `Height`.
|
/// Given a potentially negative index, find the corresponding `Height`.
|
|
@ -0,0 +1,6 @@
|
||||||
|
//! 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;
|
|
@ -0,0 +1,5 @@
|
||||||
|
//! 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 {}
|
|
@ -0,0 +1,18 @@
|
||||||
|
//! The `DefaultRoots` type is part of the `getblocktemplate` RPC method output.
|
||||||
|
|
||||||
|
/// Documentation to be added in #5452 or #5455.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct DefaultRoots {
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "merkleroot")]
|
||||||
|
pub merkle_root: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "chainhistoryroot")]
|
||||||
|
pub chain_history_root: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "authdataroot")]
|
||||||
|
pub auth_data_root: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "blockcommitmentshash")]
|
||||||
|
pub block_commitments_hash: String,
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
//! 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub capabilities: Vec<String>,
|
||||||
|
/// Add documentation.
|
||||||
|
pub version: usize,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "previousblockhash")]
|
||||||
|
pub previous_block_hash: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "blockcommitmentshash")]
|
||||||
|
pub block_commitments_hash: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "lightclientroothash")]
|
||||||
|
pub light_client_root_hash: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "finalsaplingroothash")]
|
||||||
|
pub final_sapling_root_hash: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "defaultroots")]
|
||||||
|
pub default_roots: DefaultRoots,
|
||||||
|
/// Add documentation.
|
||||||
|
pub transactions: Vec<Transaction>,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "coinbasetxn")]
|
||||||
|
pub coinbase_txn: Coinbase,
|
||||||
|
/// Add documentation.
|
||||||
|
pub target: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "mintime")]
|
||||||
|
pub min_time: u32,
|
||||||
|
/// Add documentation.
|
||||||
|
pub mutable: Vec<String>,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "noncerange")]
|
||||||
|
pub nonce_range: String,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "sigoplimit")]
|
||||||
|
pub sigop_limit: u32,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "sizelimit")]
|
||||||
|
pub size_limit: u32,
|
||||||
|
/// Add documentation.
|
||||||
|
#[serde(rename = "curtime")]
|
||||||
|
pub cur_time: u32,
|
||||||
|
/// Add documentation.
|
||||||
|
pub bits: String,
|
||||||
|
/// Add documentation.
|
||||||
|
pub height: u32,
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
//! The `Transaction` type is part of the `getblocktemplate` RPC method output.
|
||||||
|
|
||||||
|
/// Documentation and fields to be added in #5454.
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Transaction {}
|
|
@ -191,6 +191,13 @@ async fn test_rpc_response_data_for_network(network: Network) {
|
||||||
.expect("We should have a GetBlockHash struct");
|
.expect("We should have a GetBlockHash struct");
|
||||||
|
|
||||||
snapshot_rpc_getblockhash(get_block_hash, &settings);
|
snapshot_rpc_getblockhash(get_block_hash, &settings);
|
||||||
|
|
||||||
|
// `getblocktemplate`
|
||||||
|
let get_block_template = get_block_template_rpc
|
||||||
|
.get_block_template()
|
||||||
|
.await
|
||||||
|
.expect("We should have a GetBlockTemplate struct");
|
||||||
|
snapshot_rpc_getblocktemplate(get_block_template, &settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,6 +299,15 @@ fn snapshot_rpc_getblockhash(block_hash: GetBlockHash, settings: &insta::Setting
|
||||||
settings.bind(|| insta::assert_json_snapshot!("get_block_hash", block_hash));
|
settings.bind(|| insta::assert_json_snapshot!("get_block_hash", block_hash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
/// 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,
|
||||||
|
) {
|
||||||
|
settings.bind(|| insta::assert_json_snapshot!("get_block_template", block_template));
|
||||||
|
}
|
||||||
|
|
||||||
/// Utility function to convert a `Network` to a lowercase string.
|
/// Utility function to convert a `Network` to a lowercase string.
|
||||||
fn network_string(network: Network) -> String {
|
fn network_string(network: Network) -> String {
|
||||||
let mut net_suffix = network.to_string();
|
let mut net_suffix = network.to_string();
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot.rs
|
||||||
|
assertion_line: 308
|
||||||
|
expression: block_template
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"capabilities": [],
|
||||||
|
"version": 0,
|
||||||
|
"previousblockhash": "",
|
||||||
|
"blockcommitmentshash": "",
|
||||||
|
"lightclientroothash": "",
|
||||||
|
"finalsaplingroothash": "",
|
||||||
|
"defaultroots": {
|
||||||
|
"merkleroot": "",
|
||||||
|
"chainhistoryroot": "",
|
||||||
|
"authdataroot": "",
|
||||||
|
"blockcommitmentshash": ""
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"coinbasetxn": {},
|
||||||
|
"target": "",
|
||||||
|
"mintime": 0,
|
||||||
|
"mutable": [],
|
||||||
|
"noncerange": "",
|
||||||
|
"sigoplimit": 0,
|
||||||
|
"sizelimit": 0,
|
||||||
|
"curtime": 0,
|
||||||
|
"bits": "",
|
||||||
|
"height": 0
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot.rs
|
||||||
|
assertion_line: 308
|
||||||
|
expression: block_template
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"capabilities": [],
|
||||||
|
"version": 0,
|
||||||
|
"previousblockhash": "",
|
||||||
|
"blockcommitmentshash": "",
|
||||||
|
"lightclientroothash": "",
|
||||||
|
"finalsaplingroothash": "",
|
||||||
|
"defaultroots": {
|
||||||
|
"merkleroot": "",
|
||||||
|
"chainhistoryroot": "",
|
||||||
|
"authdataroot": "",
|
||||||
|
"blockcommitmentshash": ""
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"coinbasetxn": {},
|
||||||
|
"target": "",
|
||||||
|
"mintime": 0,
|
||||||
|
"mutable": [],
|
||||||
|
"noncerange": "",
|
||||||
|
"sigoplimit": 0,
|
||||||
|
"sizelimit": 0,
|
||||||
|
"curtime": 0,
|
||||||
|
"bits": "",
|
||||||
|
"height": 0
|
||||||
|
}
|
|
@ -647,7 +647,7 @@ async fn rpc_getblockcount() {
|
||||||
|
|
||||||
// Init RPC
|
// Init RPC
|
||||||
let get_block_template_rpc =
|
let get_block_template_rpc =
|
||||||
get_block_template::GetBlockTemplateRpcImpl::new(latest_chain_tip.clone(), read_state);
|
get_block_template_rpcs::GetBlockTemplateRpcImpl::new(latest_chain_tip.clone(), read_state);
|
||||||
|
|
||||||
// Get the tip height using RPC method `get_block_count`
|
// Get the tip height using RPC method `get_block_count`
|
||||||
let get_block_count = get_block_template_rpc
|
let get_block_count = get_block_template_rpc
|
||||||
|
@ -686,7 +686,7 @@ async fn rpc_getblockcount_empty_state() {
|
||||||
);
|
);
|
||||||
|
|
||||||
let get_block_template_rpc =
|
let get_block_template_rpc =
|
||||||
get_block_template::GetBlockTemplateRpcImpl::new(latest_chain_tip.clone(), read_state);
|
get_block_template_rpcs::GetBlockTemplateRpcImpl::new(latest_chain_tip.clone(), read_state);
|
||||||
|
|
||||||
// Get the tip height using RPC method `get_block_count
|
// Get the tip height using RPC method `get_block_count
|
||||||
let get_block_count = get_block_template_rpc.get_block_count();
|
let get_block_count = get_block_template_rpc.get_block_count();
|
||||||
|
@ -730,7 +730,7 @@ async fn rpc_getblockhash() {
|
||||||
latest_chain_tip.clone(),
|
latest_chain_tip.clone(),
|
||||||
);
|
);
|
||||||
let get_block_template_rpc =
|
let get_block_template_rpc =
|
||||||
get_block_template::GetBlockTemplateRpcImpl::new(latest_chain_tip, read_state);
|
get_block_template_rpcs::GetBlockTemplateRpcImpl::new(latest_chain_tip, read_state);
|
||||||
|
|
||||||
// Query the hashes using positive indexes
|
// Query the hashes using positive indexes
|
||||||
for (i, block) in blocks.iter().enumerate() {
|
for (i, block) in blocks.iter().enumerate() {
|
||||||
|
@ -757,3 +757,70 @@ async fn rpc_getblockhash() {
|
||||||
|
|
||||||
mempool.expect_no_requests().await;
|
mempool.expect_no_requests().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn rpc_getblocktemplate() {
|
||||||
|
let _init_guard = zebra_test::init();
|
||||||
|
|
||||||
|
// Create a continuous chain of mainnet blocks from genesis
|
||||||
|
let blocks: Vec<Arc<Block>> = zebra_test::vectors::CONTINUOUS_MAINNET_BLOCKS
|
||||||
|
.iter()
|
||||||
|
.map(|(_height, block_bytes)| block_bytes.zcash_deserialize_into().unwrap())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
|
||||||
|
// Create a populated state service
|
||||||
|
let (_state, read_state, latest_chain_tip, _chain_tip_change) =
|
||||||
|
zebra_state::populated_state(blocks.clone(), Mainnet).await;
|
||||||
|
|
||||||
|
// Init RPCs
|
||||||
|
let _rpc = RpcImpl::new(
|
||||||
|
"RPC test",
|
||||||
|
Mainnet,
|
||||||
|
false,
|
||||||
|
Buffer::new(mempool.clone(), 1),
|
||||||
|
Buffer::new(read_state.clone(), 1),
|
||||||
|
latest_chain_tip.clone(),
|
||||||
|
);
|
||||||
|
let get_block_template_rpc =
|
||||||
|
get_block_template_rpcs::GetBlockTemplateRpcImpl::new(latest_chain_tip, read_state);
|
||||||
|
|
||||||
|
let get_block_template = get_block_template_rpc
|
||||||
|
.get_block_template()
|
||||||
|
.await
|
||||||
|
.expect("We should have a GetBlockTemplate struct");
|
||||||
|
|
||||||
|
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());
|
||||||
|
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.cur_time, 0);
|
||||||
|
assert!(get_block_template.bits.is_empty());
|
||||||
|
assert_eq!(get_block_template.height, 0);
|
||||||
|
|
||||||
|
mempool.expect_no_requests().await;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue