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 3 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
121 changes: 121 additions & 0 deletions zebra-rpc/src/methods/get_block_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,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,8 +150,118 @@ 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()
}
}
/// Documentation to be added after we document all the individual fields.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct GetBlockTemplate {
arya2 marked this conversation as resolved.
Show resolved Hide resolved
/// 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,
}

/// 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,
}

/// Documentation and fields to be added in #5454.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Transaction {}

/// documentation and fields to be added in #5453.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct Coinbase {}

/// Given a potentially negative index, find the corresponding `Height`.
///
/// This function is used to parse the integer index argument of `get_block_hash`.
Expand Down
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::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
}
67 changes: 67 additions & 0 deletions zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::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::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;
}