Skip to content

Commit

Permalink
adds getblocktemplate proposal mode response type
Browse files Browse the repository at this point in the history
  • Loading branch information
arya2 committed Dec 15, 2022
1 parent 162dedb commit ee8e0d2
Show file tree
Hide file tree
Showing 6 changed files with 398 additions and 260 deletions.
501 changes: 264 additions & 237 deletions zebra-rpc/src/methods/get_block_template_rpcs.rs

Large diffs are not rendered by default.

71 changes: 60 additions & 11 deletions zebra-rpc/src/methods/get_block_template_rpcs/get_block_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,70 @@ use crate::methods::get_block_template_rpcs::{

pub use crate::methods::get_block_template_rpcs::types::get_block_template::*;

use super::types::hex_data::HexData;

// - Parameter checks

/// Returns an error if the get block template RPC `parameters` are invalid.
pub fn check_block_template_parameters(
parameters: &get_block_template::JsonParameters,
) -> Result<()> {
if parameters.data.is_some() || parameters.mode == GetBlockTemplateRequestMode::Proposal {
return Err(Error {
code: ErrorCode::InvalidParams,
message: "\"proposal\" mode is currently unsupported by Zebra".to_string(),
data: None,
});
impl JsonParameters {
/// Checks that `data` is omitted in `Template` mode or provided in `Proposal` mode,
///
/// Returns an error if there's a mismatch between the mode and whether `data` is provided.
/// Returns Ok(Some(data)) with the block proposal hexdata if in `Proposal` mode.
/// Returns Ok(None) if in `Template` mode.
pub fn block_proposal(parameters: Option<Self>) -> Result<Option<HexData>> {
let Some(parameters) = parameters else {
return Ok(None)
};

match parameters {
Self {
mode: GetBlockTemplateRequestMode::Template,
data: None,
..
} => Ok(None),

Self {
mode: GetBlockTemplateRequestMode::Proposal,
data: data @ Some(_),
..
} => Ok(data),

Self {
mode: GetBlockTemplateRequestMode::Proposal,
data: None,
..
} => Err(Error {
code: ErrorCode::InvalidParams,
message: "\"data\" parameter must be \
provided in \"proposal\" mode"
.to_string(),
data: None,
}),

Self {
mode: GetBlockTemplateRequestMode::Template,
data: Some(_),
..
} => Err(Error {
code: ErrorCode::InvalidParams,
message: "\"data\" parameter must be \
omitted in \"template\" mode"
.to_string(),
data: None,
}),
}
}

Ok(())
/// Checks if `mode` parameters is `Proposal`.
pub fn is_proposal_mode(parameters: &Option<Self>) -> bool {
matches!(
parameters,
Some(get_block_template::JsonParameters {
mode: GetBlockTemplateRequestMode::Proposal,
..
})
)
}
}

/// Returns the miner address, or an error if it is invalid.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub mod parameters;

pub use parameters::*;

/// A serialized `getblocktemplate` RPC response.
/// A serialized `getblocktemplate` RPC response in template mode.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct GetBlockTemplate {
/// The getblocktemplate RPC capabilities supported by Zebra.
Expand Down Expand Up @@ -234,3 +234,60 @@ impl GetBlockTemplate {
}
}
}

/// Error response to a `getblocktemplate` RPC request in proposal mode.
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ProposalRejectReason {
/// Block rejected as invalid
Rejected,
}

/// Response to a `getblocktemplate` RPC request in proposal mode.
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(untagged, rename_all = "kebab-case")]
pub enum ProposalResponse {
/// Block was not successfully submitted, return error
ErrorResponse {
/// Reason the proposal was invalid as-is
reject_reason: ProposalRejectReason,

/// The getblocktemplate RPC capabilities supported by Zebra.
capabilities: Vec<String>,
},

/// Block successfully proposed, returns null
Valid,
}

#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
/// A `getblocktemplate` RPC response.
pub enum Response {
/// `getblocktemplate` RPC request in template mode.
TemplateMode(Box<GetBlockTemplate>),

/// `getblocktemplate` RPC request in proposal mode.
ProposalMode(ProposalResponse),
}

impl From<ProposalRejectReason> for ProposalResponse {
fn from(reject_reason: ProposalRejectReason) -> Self {
// Convert default values
let capabilities: Vec<String> = GET_BLOCK_TEMPLATE_CAPABILITIES_FIELD
.iter()
.map(ToString::to_string)
.collect();

Self::ErrorResponse {
reject_reason,
capabilities,
}
}
}

impl From<ProposalRejectReason> for Response {
fn from(error_response: ProposalRejectReason) -> Self {
Self::ProposalMode(ProposalResponse::from(error_response))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub enum GetBlockTemplateRequestMode {
Template,

/// Indicates a request to validate block data.
/// Currently unsupported and will return an error.
Proposal,
}

Expand Down Expand Up @@ -68,14 +67,14 @@ pub struct JsonParameters {
/// 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.
/// Must be omitted when `getblocktemplate` RPC is called in "template" mode (or when `mode` is omitted).
/// Must be provided when `getblocktemplate` RPC is called in "proposal" mode.
///
/// 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`.
/// (excluding the check for a valid proof-of-work).
pub data: Option<HexData>,

/// A list of client-side supported capability features
Expand Down
14 changes: 9 additions & 5 deletions zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,12 @@ pub async fn test_responses<State, ReadState>(
.await
.respond(mempool::Response::FullTransactions(vec![]));

let get_block_template = get_block_template
let get_block_template::Response::TemplateMode(get_block_template) = get_block_template
.await
.expect("unexpected panic in getblocktemplate RPC task")
.expect("unexpected error in getblocktemplate RPC call");
.expect("unexpected error in getblocktemplate RPC call") else {
panic!("this getblocktemplate call without parameters should return the `TemplateMode` variant of the response")
};

let coinbase_tx: Transaction = get_block_template
.coinbase_txn
Expand Down Expand Up @@ -250,10 +252,12 @@ pub async fn test_responses<State, ReadState>(
.await
.respond(mempool::Response::FullTransactions(vec![]));

let get_block_template = get_block_template
let get_block_template::Response::TemplateMode(get_block_template) = get_block_template
.await
.expect("unexpected panic in getblocktemplate RPC task")
.expect("unexpected error in getblocktemplate RPC call");
.expect("unexpected error in getblocktemplate RPC call") else {
panic!("this getblocktemplate call without parameters should return the `TemplateMode` variant of the response")
};

let coinbase_tx: Transaction = get_block_template
.coinbase_txn
Expand Down Expand Up @@ -287,7 +291,7 @@ fn snapshot_rpc_getblockhash(block_hash: GetBlockHash, settings: &insta::Setting
/// Snapshot `getblocktemplate` response, using `cargo insta` and JSON serialization.
fn snapshot_rpc_getblocktemplate(
variant: &'static str,
block_template: GetBlockTemplate,
block_template: Box<GetBlockTemplate>,
coinbase_tx: Transaction,
settings: &insta::Settings,
) {
Expand Down
6 changes: 4 additions & 2 deletions zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,15 +996,17 @@ async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) {
.await
.respond(mempool::Response::FullTransactions(vec![]));

let get_block_template = get_block_template
let get_block_template::Response::TemplateMode(get_block_template) = get_block_template
.await
.unwrap_or_else(|error| match error.try_into_panic() {
Ok(panic_object) => panic::resume_unwind(panic_object),
Err(cancelled_error) => {
panic!("getblocktemplate task was unexpectedly cancelled: {cancelled_error:?}")
}
})
.expect("unexpected error in getblocktemplate RPC call");
.expect("unexpected error in getblocktemplate RPC call") else {
panic!("this getblocktemplate call without parameters should return the `TemplateMode` variant of the response")
};

assert_eq!(
get_block_template.capabilities,
Expand Down

0 comments on commit ee8e0d2

Please sign in to comment.