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

merge-queue: embarking main (402ed3e) and [#5884 + #5925] together #5976

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
37d5afd
adds ValidateBlock request to state
arya2 Dec 15, 2022
6eb6048
adds `Request` enum in block verifier
arya2 Dec 15, 2022
162dedb
uses new Request in references to chain verifier
arya2 Dec 15, 2022
f7c5941
adds getblocktemplate proposal mode response type
arya2 Dec 15, 2022
9e0b8d1
makes getblocktemplate-rpcs feature in zebra-consensus select getbloc…
arya2 Dec 15, 2022
8420b75
Adds PR review revisions
arya2 Dec 16, 2022
e3b6d28
adds info log in CheckBlockProposalValidity
arya2 Dec 16, 2022
95cea02
Reverts replacement of match statement
arya2 Dec 16, 2022
31472a8
adds `GetBlockTemplate::capabilities` fn
arya2 Dec 16, 2022
ba302f7
conditions calling checkpoint verifier on !request.is_proposal
arya2 Dec 16, 2022
02b6758
updates references to validate_and_commit_non_finalized
arya2 Dec 16, 2022
2faa823
adds snapshot test, updates test vectors
arya2 Dec 16, 2022
32e6ac9
adds `should_count_metrics` to NonFinalizedState
arya2 Dec 16, 2022
a2ae3fb
Returns an error from chain verifier for block proposal requests belo…
arya2 Dec 16, 2022
40d002a
adds "proposal" to GET_BLOCK_TEMPLATE_CAPABILITIES_FIELD
arya2 Dec 17, 2022
1b658a1
Merge branch 'main' into gbt-proposal-mode
arya2 Dec 19, 2022
da1b308
adds back block::Request to zebra-consensus lib
arya2 Dec 19, 2022
cb26b8a
updates snapshots
arya2 Dec 19, 2022
78e95d9
Removes unnecessary network arg
arya2 Dec 22, 2022
0c55a67
skips req in tracing intstrument for read state
arya2 Dec 22, 2022
616d280
Moves out block proposal validation to its own fn
arya2 Dec 22, 2022
8978541
corrects `difficulty_threshold_is_valid` docs
arya2 Dec 23, 2022
4551cf0
Update zebra-state/src/service.rs
arya2 Jan 5, 2023
e888039
Merge branch 'main' into gbt-proposal-mode
arya2 Jan 5, 2023
5cc7378
Apply suggestions from code review
arya2 Jan 10, 2023
67bf095
Update zebra-rpc/src/methods/get_block_template_rpcs.rs
arya2 Jan 10, 2023
f774822
check best chain tip
arya2 Jan 9, 2023
d75e17a
Update zebra-state/src/service.rs
arya2 Jan 10, 2023
9ce7d8a
Applies cleanup suggestions from code review
arya2 Jan 10, 2023
8b89cba
updates gbt acceptance test to make a block proposal
arya2 Dec 19, 2022
0bf81c7
fixes json parsing mistake
arya2 Dec 19, 2022
1d043b1
adds retries
arya2 Dec 22, 2022
da54e85
returns reject reason if there are no retries left
arya2 Dec 22, 2022
ee0fc0b
moves result deserialization to RPCRequestClient method, adds docs, m…
arya2 Jan 2, 2023
a6ac270
moves sleep(EXPECTED_TX_TIME) out of loop
arya2 Jan 2, 2023
d58d474
updates/adds info logs in retry loop
arya2 Jan 2, 2023
f105b58
Revert "moves sleep(EXPECTED_TX_TIME) out of loop"
arya2 Jan 2, 2023
fc4bd13
adds `allow(dead_code)`
arya2 Jan 3, 2023
1eb218e
Clean up some getblocktemplate difficulty code and tests
teor2345 Jan 5, 2023
5610925
Fix minimum difficulty adjustment
teor2345 Jan 6, 2023
6917382
Merge branch 'main' into fix-testnet-difficulty
mergify[bot] Jan 11, 2023
509f890
Merge branch 'main' into gbt-proposal-test
mergify[bot] Jan 12, 2023
516f17c
tests with curtime, mintime, & maxtime
arya2 Jan 12, 2023
60c7a13
Fixes doc comment
arya2 Jan 12, 2023
d2cae99
Logs error responses from chain_verifier CheckProposal requests
arya2 Jan 13, 2023
bb32c52
Removes retry loop, adds num_txs log
arya2 Jan 13, 2023
71c335f
removes verbose info log
arya2 Jan 13, 2023
f052448
sorts mempool_txs before generating merkle root
arya2 Jan 13, 2023
dc3f2fc
Merge branch 'fix-gbt-merkle-root' into gbt-proposal-test
arya2 Jan 13, 2023
088bcd8
Use clamp rather than max/min
teor2345 Jan 15, 2023
03fd7f4
Remove unused imports
teor2345 Jan 15, 2023
9842f52
Document the Zebra-specific standard rule that allows testnet miners …
teor2345 Jan 15, 2023
8c51e13
Make imports conditional on a feature
arya2 Jan 16, 2023
dd2438b
Disable new CI tests until bugs are fixed
arya2 Jan 16, 2023
8358627
Merge of #5884
mergify[bot] Jan 17, 2023
339c7e5
Merge of #5925
mergify[bot] Jan 17, 2023
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions zebra-chain/src/work/equihash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ impl Solution {

Ok(())
}

#[cfg(feature = "getblocktemplate-rpcs")]
/// Returns a [`Solution`] of `[0; SOLUTION_SIZE]` to be used in block proposals.
pub fn for_proposal() -> Self {
Self([0; SOLUTION_SIZE])
}
}

impl PartialEq<Solution> for Solution {
Expand Down
2 changes: 1 addition & 1 deletion zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1193,7 +1193,7 @@ pub enum GetBlock {
///
/// Also see the notes for the [`Rpc::get_best_block_hash`] and `get_block_hash` methods.
#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct GetBlockHash(#[serde(with = "hex")] block::Hash);
pub struct GetBlockHash(#[serde(with = "hex")] pub block::Hash);

/// Response to a `z_gettreestate` RPC request.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ where
+ Sync
+ 'static,
{
let Ok(block) = block_proposal_bytes.zcash_deserialize_into() else {
let Ok(block) = block_proposal_bytes.zcash_deserialize_into::<block::Block>() else {
return Ok(ProposalRejectReason::Rejected.into())
};

Expand All @@ -125,7 +125,14 @@ where

Ok(chain_verifier_response
.map(|_hash| ProposalResponse::Valid)
.unwrap_or_else(|_| ProposalRejectReason::Rejected.into())
.unwrap_or_else(|verify_chain_error| {
tracing::info!(
verify_chain_error,
"Got error response from chain_verifier CheckProposal request"
);

ProposalRejectReason::Rejected.into()
})
.into())
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! The `GetBlockTempate` type is the output of the `getblocktemplate` RPC method.
//! The `GetBlockTempate` type is the output of the `getblocktemplate` RPC method in the
//! default 'template' mode. See [`ProposalResponse`] for the output in 'proposal' mode.

use zebra_chain::{
amount,
Expand Down Expand Up @@ -27,8 +28,10 @@ use crate::methods::{
};

pub mod parameters;
pub mod proposal;

pub use parameters::*;
pub use proposal::*;

/// A serialized `getblocktemplate` RPC response in template mode.
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
Expand Down Expand Up @@ -283,33 +286,6 @@ 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 proposal rejected as invalid.
Rejected,
}

/// Response to a `getblocktemplate` RPC request in proposal mode.
///
/// See <https://en.bitcoin.it/wiki/BIP_0023#Block_Proposal>
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(untagged, rename_all = "kebab-case")]
pub enum ProposalResponse {
/// Block proposal was rejected as invalid, returns `reject-reason` and server `capabilities`.
ErrorResponse {
/// Reason the proposal was invalid as-is.
reject_reason: ProposalRejectReason,

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

/// Block proposal was successfully validated, returns null.
Valid,
}

#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
/// A `getblocktemplate` RPC response.
Expand All @@ -320,30 +296,3 @@ pub enum Response {
/// `getblocktemplate` RPC request in proposal mode.
ProposalMode(ProposalResponse),
}

impl From<ProposalRejectReason> for ProposalResponse {
fn from(reject_reason: ProposalRejectReason) -> Self {
Self::ErrorResponse {
reject_reason,
capabilities: GetBlockTemplate::capabilities(),
}
}
}

impl From<ProposalRejectReason> for Response {
fn from(error_response: ProposalRejectReason) -> Self {
Self::ProposalMode(ProposalResponse::from(error_response))
}
}

impl From<ProposalResponse> for Response {
fn from(proposal_response: ProposalResponse) -> Self {
Self::ProposalMode(proposal_response)
}
}

impl From<GetBlockTemplate> for Response {
fn from(template: GetBlockTemplate) -> Self {
Self::TemplateMode(Box::new(template))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//! `ProposalResponse` is the output of the `getblocktemplate` RPC method in 'proposal' mode.

use super::{GetBlockTemplate, Response};

/// Error response to a `getblocktemplate` RPC request in proposal mode.
///
/// See <https://en.bitcoin.it/wiki/BIP_0022#Appendix:_Example_Rejection_Reasons>
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ProposalRejectReason {
/// Block proposal rejected as invalid.
Rejected,
}

/// Response to a `getblocktemplate` RPC request in proposal mode.
///
/// See <https://en.bitcoin.it/wiki/BIP_0023#Block_Proposal>
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(untagged, rename_all = "kebab-case")]
pub enum ProposalResponse {
/// Block proposal was rejected as invalid, returns `reject-reason` and server `capabilities`.
ErrorResponse {
/// Reason the proposal was invalid as-is.
reject_reason: ProposalRejectReason,

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

/// Block proposal was successfully validated, returns null.
Valid,
}

impl From<ProposalRejectReason> for ProposalResponse {
fn from(reject_reason: ProposalRejectReason) -> Self {
Self::ErrorResponse {
reject_reason,
capabilities: GetBlockTemplate::capabilities(),
}
}
}

impl From<ProposalRejectReason> for Response {
fn from(error_response: ProposalRejectReason) -> Self {
Self::ProposalMode(ProposalResponse::from(error_response))
}
}

impl From<ProposalResponse> for Response {
fn from(proposal_response: ProposalResponse) -> Self {
Self::ProposalMode(proposal_response)
}
}

impl From<GetBlockTemplate> for Response {
fn from(template: GetBlockTemplate) -> Self {
Self::TemplateMode(Box::new(template))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ where
{
/// The hex-encoded serialized data for this transaction.
#[serde(with = "hex")]
pub(crate) data: SerializedTransaction,
pub data: SerializedTransaction,

/// The transaction ID of this transaction.
#[serde(with = "hex")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub async fn test_responses<State, ReadState>(
let fake_cur_time = DateTime32::from(1654008617);
// nu5 block time + 123
let fake_max_time = DateTime32::from(1654008728);
let fake_difficulty = CompactDifficulty::from(ExpandedDifficulty::from(U256::one()));

let (mock_chain_tip, mock_chain_tip_sender) = MockChainTip::new();
mock_chain_tip_sender.send_best_tip_height(fake_tip_height);
Expand Down Expand Up @@ -181,7 +182,7 @@ pub async fn test_responses<State, ReadState>(
.expect_request_that(|req| matches!(req, ReadRequest::ChainInfo))
.await
.respond(ReadResponse::ChainInfo(GetBlockTemplateChainInfo {
expected_difficulty: CompactDifficulty::from(ExpandedDifficulty::from(U256::one())),
expected_difficulty: fake_difficulty,
tip_height: fake_tip_height,
tip_hash: fake_tip_hash,
cur_time: fake_cur_time,
Expand Down Expand Up @@ -235,7 +236,7 @@ pub async fn test_responses<State, ReadState>(
.expect_request_that(|req| matches!(req, ReadRequest::ChainInfo))
.await
.respond(ReadResponse::ChainInfo(GetBlockTemplateChainInfo {
expected_difficulty: CompactDifficulty::from(ExpandedDifficulty::from(U256::one())),
expected_difficulty: fake_difficulty,
tip_height: fake_tip_height,
tip_hash: fake_tip_hash,
cur_time: fake_cur_time,
Expand Down
3 changes: 2 additions & 1 deletion zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,6 +953,7 @@ async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) {
let fake_cur_time = DateTime32::from(1654008617);
// nu5 block time + 123
let fake_max_time = DateTime32::from(1654008728);
let fake_difficulty = CompactDifficulty::from(ExpandedDifficulty::from(U256::one()));

let (mock_chain_tip, mock_chain_tip_sender) = MockChainTip::new();
mock_chain_tip_sender.send_best_tip_height(fake_tip_height);
Expand All @@ -976,7 +977,7 @@ async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) {
.expect_request_that(|req| matches!(req, ReadRequest::ChainInfo))
.await
.respond(ReadResponse::ChainInfo(GetBlockTemplateChainInfo {
expected_difficulty: CompactDifficulty::from(ExpandedDifficulty::from(U256::one())),
expected_difficulty: fake_difficulty,
tip_height: fake_tip_height,
tip_hash: fake_tip_hash,
cur_time: fake_cur_time,
Expand Down
8 changes: 3 additions & 5 deletions zebra-state/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,14 @@ pub struct GetBlockTemplateChainInfo {
/// Depends on the `tip_hash`.
pub history_tree: Arc<zebra_chain::history_tree::HistoryTree>,

// Data derived from the state tip and recent blocks.
// Data derived from the state tip and recent blocks, and the current local clock.
//
/// The expected difficulty of the candidate block.
/// Depends on the `tip_hash`.
/// Depends on the `tip_hash`, and the local clock on testnet.
pub expected_difficulty: CompactDifficulty,

// Data derived from the state tip and recent blocks, and the current local clock.
//
/// The current system time, adjusted to fit within `min_time` and `max_time`.
/// Depends on the local clock and the `tip_hash`.
/// Always depends on the local clock and the `tip_hash`.
pub cur_time: DateTime32,

/// The mininimum time the miner can use in this block.
Expand Down
55 changes: 40 additions & 15 deletions zebra-state/src/service/read/difficulty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ use crate::{
BoxError, GetBlockTemplateChainInfo,
};

/// The amount of extra time we allow for a miner to mine a standard difficulty block on testnet.
///
/// This is a Zebra-specific standard rule.
pub const EXTRA_TIME_TO_MINE_A_BLOCK: u32 = POST_BLOSSOM_POW_TARGET_SPACING * 2;

/// Returns the [`GetBlockTemplateChainInfo`] for the current best chain.
///
/// # Panics
Expand Down Expand Up @@ -117,7 +122,9 @@ pub fn solution_rate(
}

/// Do a consistency check by checking the finalized tip before and after all other database queries.
/// Returns and error if the tip obtained before and after is not the same.
///
/// Returns the state tip, recent blocks in reverse order from the tip, and the tip history tree.
/// Returns an error if the tip obtained before and after is not the same.
///
/// # Panics
///
Expand Down Expand Up @@ -173,6 +180,8 @@ fn relevant_chain_and_history_tree(

/// Returns the [`GetBlockTemplateChainInfo`] for the current best chain.
///
/// The `relevant_chain` has recent blocks in reverse order from the tip.
///
/// See [`get_block_template_chain_info()`] for details.
fn difficulty_time_and_history_tree(
relevant_chain: [Arc<Block>; POW_ADJUSTMENT_BLOCK_SPAN],
Expand Down Expand Up @@ -227,12 +236,13 @@ fn difficulty_time_and_history_tree(
network,
relevant_data.iter().cloned(),
);
let expected_difficulty = difficulty_adjustment.expected_difficulty_threshold();

let mut result = GetBlockTemplateChainInfo {
tip_hash,
tip_height,
history_tree,
expected_difficulty: difficulty_adjustment.expected_difficulty_threshold(),
expected_difficulty,
cur_time,
min_time,
max_time,
Expand All @@ -244,6 +254,8 @@ fn difficulty_time_and_history_tree(
}

/// Adjust the difficulty and time for the testnet minimum difficulty rule.
///
/// The `relevant_data` has recent block difficulties and times in reverse order from the tip.
fn adjust_difficulty_and_time_for_testnet(
result: &mut GetBlockTemplateChainInfo,
network: Network,
Expand Down Expand Up @@ -275,7 +287,8 @@ fn adjust_difficulty_and_time_for_testnet(
// There is still a small chance that miners will produce an invalid block, if they are
// just below the max time, and don't check it.

let previous_block_time = relevant_data.last().expect("has at least one block").1;
// The tip is the first relevant data block, because they are in reverse order.
let previous_block_time = relevant_data.first().expect("has at least one block").1;
let previous_block_time: DateTime32 = previous_block_time
.try_into()
.expect("valid blocks have in-range times");
Expand All @@ -296,28 +309,40 @@ fn adjust_difficulty_and_time_for_testnet(
.expect("a valid block time plus a small constant is in-range");

// If a miner is likely to find a block with the cur_time and standard difficulty
// within a target block interval or two, keep the original difficulty.
// Otherwise, try to use the minimum difficulty.
//
// This is a Zebra-specific standard rule.
//
// We don't need to undo the clamping here:
// - if cur_time is clamped to min_time, then we're more likely to have a minimum
// difficulty block, which makes mining easier;
// - if cur_time gets clamped to max_time, this is already a minimum difficulty block.
// - if cur_time gets clamped to max_time, this is almost always a minimum difficulty block.
let local_std_difficulty_limit = std_difficulty_max_time
.checked_sub(Duration32::from_seconds(
POST_BLOSSOM_POW_TARGET_SPACING * 2,
))
.checked_sub(Duration32::from_seconds(EXTRA_TIME_TO_MINE_A_BLOCK))
.expect("a valid block time minus a small constant is in-range");

if result.cur_time <= local_std_difficulty_limit {
// Standard difficulty: the max time needs to exclude min difficulty blocks
result.max_time = std_difficulty_max_time;
// Standard difficulty: the cur and max time need to exclude min difficulty blocks

// The maximum time can only be decreased, and only as far as min_time.
// The old minimum is still required by other consensus rules.
result.max_time = std_difficulty_max_time.clamp(result.min_time, result.max_time);

// The current time only needs to be decreased if the max_time decreased past it.
// Decreasing the current time can't change the difficulty.
result.cur_time = result.cur_time.clamp(result.min_time, result.max_time);
} else {
// Minimum difficulty: the min and cur time need to exclude min difficulty blocks
result.min_time = min_difficulty_min_time;
if result.cur_time < min_difficulty_min_time {
result.cur_time = min_difficulty_min_time;
}
// Minimum difficulty: the min and cur time need to exclude std difficulty blocks

// The minimum time can only be increased, and only as far as max_time.
// The old maximum is still required by other consensus rules.
result.min_time = min_difficulty_min_time.clamp(result.min_time, result.max_time);

// The current time only needs to be increased if the min_time increased past it.
result.cur_time = result.cur_time.clamp(result.min_time, result.max_time);

// And then the difficulty needs to be updated for cur_time
// And then the difficulty needs to be updated for cur_time.
result.expected_difficulty = AdjustedDifficulty::new_from_header_time(
result.cur_time.into(),
previous_block_height,
Expand Down
1 change: 1 addition & 0 deletions zebrad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ tonic-build = { version = "0.8.0", optional = true }
[dev-dependencies]
abscissa_core = { version = "0.5", features = ["testing"] }
hex = "0.4.3"
jsonrpc-core = "18.0.0"
once_cell = "1.17.0"
regex = "1.7.1"
semver = "1.0.16"
Expand Down
Loading