Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rpc): introduce getblocktemplate rpc call with stub fields #5462

Merged
merged 4 commits into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ use zebra_state::{OutputIndex, OutputLocation, TransactionLocation};
use crate::queue::Queue;

#[cfg(feature = "getblocktemplate-rpcs")]
mod get_block_template;
mod get_block_template_rpcs;

#[cfg(feature = "getblocktemplate-rpcs")]
pub use get_block_template::{GetBlockTemplateRpc, GetBlockTemplateRpcImpl};
pub use get_block_template_rpcs::{GetBlockTemplateRpc, GetBlockTemplateRpcImpl};

#[cfg(test)]
mod tests;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ use jsonrpc_core::{self, BoxFuture, Error, ErrorCode, Result};
use jsonrpc_derive::rpc;
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.
#[rpc(server)]
Expand Down Expand Up @@ -35,8 +42,19 @@ pub trait GetBlockTemplateRpc {
///
/// - If `index` is positive then index = block height.
/// - 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")]
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.
Expand Down Expand Up @@ -139,6 +157,40 @@ where
}
.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`.
Expand Down
6 changes: 6 additions & 0 deletions zebra-rpc/src/methods/get_block_template_rpcs/types.rs
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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 {}
Original file line number Diff line number Diff line change
@@ -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,
}
Original file line number Diff line number Diff line change
@@ -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,
}
Original file line number Diff line number Diff line change
@@ -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 {}
16 changes: 16 additions & 0 deletions zebra-rpc/src/methods/tests/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ async fn test_rpc_response_data_for_network(network: Network) {
.expect("We should have a GetBlockHash struct");

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);
}
}

Expand Down Expand Up @@ -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));
}

#[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.
fn network_string(network: Network) -> String {
let mut net_suffix = network.to_string();
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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
}
73 changes: 70 additions & 3 deletions zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,7 +647,7 @@ async fn rpc_getblockcount() {

// Init 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`
let get_block_count = get_block_template_rpc
Expand Down Expand Up @@ -686,7 +686,7 @@ async fn rpc_getblockcount_empty_state() {
);

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
let get_block_count = get_block_template_rpc.get_block_count();
Expand Down Expand Up @@ -730,7 +730,7 @@ async fn rpc_getblockhash() {
latest_chain_tip.clone(),
);
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
for (i, block) in blocks.iter().enumerate() {
Expand All @@ -757,3 +757,70 @@ async fn rpc_getblockhash() {

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;
}