Skip to content

Commit

Permalink
feat(rpc): Implement getblockchaininfo RPC method (#3891)
Browse files Browse the repository at this point in the history
* Implement `getblockchaininfo` RPC method

* add a test for `get_blockchain_info`

* fix tohex/fromhex

* move comment

* Update lightwalletd acceptance test for getblockchaininfo RPC (#3914)

* change(rpc): Return getblockchaininfo network upgrades in height order (#3915)

* Update lightwalletd acceptance test for getblockchaininfo RPC

* Update some doc comments for network upgrades

* List network upgrades in order in the getblockchaininfo RPC

Also:
- Use a constant for the "missing consensus branch ID" RPC value
- Simplify fetching consensus branch IDs
- Make RPC type derives consistent
- Update RPC type documentation

* Make RPC type derives consistent

* Fix a confusing test comment

* get hashand height at the same time

* fix estimated_height

* fix lint

* add extra check

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* fix typo

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* split test

Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>

* fix(rpc): ignore an expected error in the RPC acceptance tests (#3961)

* Add ignored regexes to test command failure regex methods

* Ignore empty chain error in getblockchaininfo

We expect this error when zebrad starts up with an empty state.

Co-authored-by: teor <teor@riseup.net>
Co-authored-by: Janito Vaqueiro Ferreira Filho <janito.vff@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored Mar 25, 2022
1 parent ed5e85f commit f687ab9
Show file tree
Hide file tree
Showing 14 changed files with 650 additions and 79 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

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

7 changes: 7 additions & 0 deletions zebra-chain/src/chain_tip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ pub trait ChainTip {
/// Return the block hash of the best chain tip.
fn best_tip_hash(&self) -> Option<block::Hash>;

/// Return the height and the hash of the best chain tip.
fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)>;

/// Return the block time of the best chain tip.
fn best_tip_block_time(&self) -> Option<DateTime<Utc>>;

Expand Down Expand Up @@ -70,6 +73,10 @@ impl ChainTip for NoChainTip {
None
}

fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)> {
None
}

fn best_tip_block_time(&self) -> Option<DateTime<Utc>> {
None
}
Expand Down
25 changes: 24 additions & 1 deletion zebra-chain/src/chain_tip/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub struct MockChainTipSender {
/// A sender that sets the `best_tip_height` of a [`MockChainTip`].
best_tip_height: watch::Sender<Option<block::Height>>,

/// A sender that sets the `best_tip_hash` of a [`MockChainTip`].
best_tip_hash: watch::Sender<Option<block::Hash>>,

/// A sender that sets the `best_tip_block_time` of a [`MockChainTip`].
best_tip_block_time: watch::Sender<Option<DateTime<Utc>>>,
}
Expand All @@ -22,6 +25,9 @@ pub struct MockChainTip {
/// A mocked `best_tip_height` value set by the [`MockChainTipSender`].
best_tip_height: watch::Receiver<Option<block::Height>>,

/// A mocked `best_tip_hash` value set by the [`MockChainTipSender`].
best_tip_hash: watch::Receiver<Option<block::Hash>>,

/// A mocked `best_tip_height` value set by the [`MockChainTipSender`].
best_tip_block_time: watch::Receiver<Option<DateTime<Utc>>>,
}
Expand All @@ -35,15 +41,18 @@ impl MockChainTip {
/// Initially, the best tip height is [`None`].
pub fn new() -> (Self, MockChainTipSender) {
let (height_sender, height_receiver) = watch::channel(None);
let (hash_sender, hash_receiver) = watch::channel(None);
let (time_sender, time_receiver) = watch::channel(None);

let mock_chain_tip = MockChainTip {
best_tip_height: height_receiver,
best_tip_hash: hash_receiver,
best_tip_block_time: time_receiver,
};

let mock_chain_tip_sender = MockChainTipSender {
best_tip_height: height_sender,
best_tip_hash: hash_sender,
best_tip_block_time: time_sender,
};

Expand All @@ -57,7 +66,14 @@ impl ChainTip for MockChainTip {
}

fn best_tip_hash(&self) -> Option<block::Hash> {
unreachable!("Method not used in tests");
*self.best_tip_hash.borrow()
}

fn best_tip_height_and_hash(&self) -> Option<(block::Height, block::Hash)> {
let height = (*self.best_tip_height.borrow())?;
let hash = (*self.best_tip_hash.borrow())?;

Some((height, hash))
}

fn best_tip_block_time(&self) -> Option<DateTime<Utc>> {
Expand All @@ -84,6 +100,13 @@ impl MockChainTipSender {
.expect("attempt to send a best tip height to a dropped `MockChainTip`");
}

/// Send a new best tip hash to the [`MockChainTip`].
pub fn send_best_tip_hash(&self, hash: impl Into<Option<block::Hash>>) {
self.best_tip_hash
.send(hash.into())
.expect("attempt to send a best tip hash to a dropped `MockChainTip`");
}

/// Send a new best tip block time to the [`MockChainTip`].
pub fn send_best_tip_block_time(&self, block_time: impl Into<Option<DateTime<Utc>>>) {
self.best_tip_block_time
Expand Down
9 changes: 9 additions & 0 deletions zebra-chain/src/parameters/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ impl Network {
(canopy_activation + ZIP_212_GRACE_PERIOD_DURATION)
.expect("ZIP-212 grace period ends at a valid block height")
}

/// Return the network name as defined in
/// [BIP70](https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki#paymentdetailspaymentrequest)
pub fn bip70_network_name(&self) -> String {
match self {
Network::Mainnet => "main".to_string(),
Network::Testnet => "test".to_string(),
}
}
}

impl Default for Network {
Expand Down
67 changes: 62 additions & 5 deletions zebra-chain/src/parameters/network_upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ use crate::block;
use crate::parameters::{Network, Network::*};

use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::ops::Bound::*;

use chrono::{DateTime, Duration, Utc};
use hex::{FromHex, ToHex};

#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
Expand Down Expand Up @@ -118,15 +120,60 @@ const FAKE_TESTNET_ACTIVATION_HEIGHTS: &[(block::Height, NetworkUpgrade)] = &[

/// The Consensus Branch Id, used to bind transactions and blocks to a
/// particular network upgrade.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ConsensusBranchId(u32);

impl ConsensusBranchId {
/// Return the hash bytes in big-endian byte-order suitable for printing out byte by byte.
///
/// Zebra displays consensus branch IDs in big-endian byte-order,
/// following the convention set by zcashd.
fn bytes_in_display_order(&self) -> [u8; 4] {
self.0.to_be_bytes()
}
}

impl From<ConsensusBranchId> for u32 {
fn from(branch: ConsensusBranchId) -> u32 {
branch.0
}
}

impl ToHex for &ConsensusBranchId {
fn encode_hex<T: FromIterator<char>>(&self) -> T {
self.bytes_in_display_order().encode_hex()
}

fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
self.bytes_in_display_order().encode_hex_upper()
}
}

impl ToHex for ConsensusBranchId {
fn encode_hex<T: FromIterator<char>>(&self) -> T {
self.bytes_in_display_order().encode_hex()
}

fn encode_hex_upper<T: FromIterator<char>>(&self) -> T {
self.bytes_in_display_order().encode_hex_upper()
}
}

impl FromHex for ConsensusBranchId {
type Error = <[u8; 4] as FromHex>::Error;

fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
let branch = <[u8; 4]>::from_hex(hex)?;
Ok(ConsensusBranchId(u32::from_be_bytes(branch)))
}
}

impl fmt::Display for ConsensusBranchId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.encode_hex::<String>())
}
}

/// Network Upgrade Consensus Branch Ids.
///
/// Branch ids are the same for mainnet and testnet. If there is a testnet
Expand Down Expand Up @@ -175,8 +222,8 @@ const TESTNET_MINIMUM_DIFFICULTY_START_HEIGHT: block::Height = block::Height(299
pub const TESTNET_MAX_TIME_START_HEIGHT: block::Height = block::Height(653_606);

impl NetworkUpgrade {
/// Returns a BTreeMap of activation heights and network upgrades for
/// `network`.
/// Returns a map between activation heights and network upgrades for `network`,
/// in ascending height order.
///
/// If the activation height of a future upgrade is not known, that
/// network upgrade does not appear in the list.
Expand All @@ -186,7 +233,7 @@ impl NetworkUpgrade {
/// When the environment variable TEST_FAKE_ACTIVATION_HEIGHTS is set
/// and it's a test build, this returns a list of fake activation heights
/// used by some tests.
pub(crate) fn activation_list(network: Network) -> BTreeMap<block::Height, NetworkUpgrade> {
pub fn activation_list(network: Network) -> BTreeMap<block::Height, NetworkUpgrade> {
let (mainnet_heights, testnet_heights) = {
#[cfg(not(feature = "zebra-test"))]
{
Expand Down Expand Up @@ -263,7 +310,7 @@ impl NetworkUpgrade {
NetworkUpgrade::activation_list(network).contains_key(&height)
}

/// Returns a BTreeMap of NetworkUpgrades and their ConsensusBranchIds.
/// Returns an unordered mapping between NetworkUpgrades and their ConsensusBranchIds.
///
/// Branch ids are the same for mainnet and testnet.
///
Expand Down Expand Up @@ -410,6 +457,16 @@ impl NetworkUpgrade {
}

impl ConsensusBranchId {
/// The value used by `zcashd` RPCs for missing consensus branch IDs.
///
/// # Consensus
///
/// This value must only be used in RPCs.
///
/// The consensus rules handle missing branch IDs by rejecting blocks and transactions,
/// so this substitute value must not be used in consensus-critical code.
pub const RPC_MISSING_ID: ConsensusBranchId = ConsensusBranchId(0);

/// Returns the current consensus branch id for `network` and `height`.
///
/// Returns None if the network has no branch id at this height.
Expand Down
18 changes: 18 additions & 0 deletions zebra-chain/src/parameters/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,21 @@ fn branch_id_consistent(network: Network) {
}
}
}

// TODO: split this file in unit.rs and prop.rs
use hex::{FromHex, ToHex};
use proptest::prelude::*;

proptest! {
#[test]
fn branch_id_hex_roundtrip(nu in any::<NetworkUpgrade>()) {
zebra_test::init();

if let Some(branch) = nu.branch_id() {
let hex_branch: String = branch.encode_hex();
let new_branch = ConsensusBranchId::from_hex(hex_branch.clone()).expect("hex branch_id should parse");
prop_assert_eq!(branch, new_branch);
prop_assert_eq!(hex_branch, new_branch.to_string());
}
}
}
4 changes: 4 additions & 0 deletions zebra-rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ zebra-network = { path = "../zebra-network" }
zebra-node-services = { path = "../zebra-node-services" }
zebra-state = { path = "../zebra-state" }

chrono = "0.4.19"
futures = "0.3.21"

# lightwalletd sends JSON-RPC requests over HTTP 1.1
Expand All @@ -21,6 +22,9 @@ hyper = { version = "0.14.17", features = ["http1", "server"] }
jsonrpc-core = "18.0.0"
jsonrpc-derive = "18.0.0"
jsonrpc-http-server = "18.0.0"
# zebra-rpc needs the preserve_order feature in serde_json, which is a dependency of jsonrpc-core
serde_json = { version = "1.0.79", features = ["preserve_order"] }
indexmap = { version = "1.8.0", features = ["serde"] }

tokio = { version = "1.17.0", features = ["time", "rt-multi-thread", "macros", "tracing"] }
tower = "0.4.12"
Expand Down
Loading

0 comments on commit f687ab9

Please sign in to comment.