change(rpc): add validateaddress method (#6086)
* adds validate_address method * Adds snapshot/vectors tests * Checks that the address is transparent * Removes unused pubkey/scriptPubKey fields * adds snapshot for invalid addresses, updates network mismatch log * simplifies is_transparent method * Returns isvalid: false instead of conversion error
This commit is contained in:
parent
dc43dca06f
commit
0793eaf687
|
|
@ -5488,6 +5488,7 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"uint",
|
"uint",
|
||||||
"x25519-dalek",
|
"x25519-dalek",
|
||||||
|
"zcash_address",
|
||||||
"zcash_encoding",
|
"zcash_encoding",
|
||||||
"zcash_history",
|
"zcash_history",
|
||||||
"zcash_note_encryption",
|
"zcash_note_encryption",
|
||||||
|
|
@ -5610,6 +5611,7 @@ dependencies = [
|
||||||
"tower",
|
"tower",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
|
"zcash_address",
|
||||||
"zebra-chain",
|
"zebra-chain",
|
||||||
"zebra-consensus",
|
"zebra-consensus",
|
||||||
"zebra-network",
|
"zebra-network",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ default = []
|
||||||
# Production features that activate extra functionality
|
# Production features that activate extra functionality
|
||||||
|
|
||||||
# Experimental mining RPC support
|
# Experimental mining RPC support
|
||||||
getblocktemplate-rpcs = []
|
getblocktemplate-rpcs = [
|
||||||
|
"zcash_address",
|
||||||
|
]
|
||||||
|
|
||||||
# Test-only features
|
# Test-only features
|
||||||
|
|
||||||
|
|
@ -89,6 +91,9 @@ ed25519-zebra = "3.1.0"
|
||||||
redjubjub = "0.5.0"
|
redjubjub = "0.5.0"
|
||||||
reddsa = "0.4.0"
|
reddsa = "0.4.0"
|
||||||
|
|
||||||
|
# Experimental feature getblocktemplate-rpcs
|
||||||
|
zcash_address = { version = "0.2.0", optional = true }
|
||||||
|
|
||||||
# Optional testing dependencies
|
# Optional testing dependencies
|
||||||
proptest = { version = "0.10.1", optional = true }
|
proptest = { version = "0.10.1", optional = true }
|
||||||
proptest-derive = { version = "0.3.0", optional = true }
|
proptest-derive = { version = "0.3.0", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,13 @@
|
||||||
//! whose functionality is implemented elsewhere.
|
//! whose functionality is implemented elsewhere.
|
||||||
|
|
||||||
mod proofs;
|
mod proofs;
|
||||||
|
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
mod address;
|
||||||
|
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
pub use address::Address;
|
||||||
|
|
||||||
pub use ed25519_zebra as ed25519;
|
pub use ed25519_zebra as ed25519;
|
||||||
pub use reddsa;
|
pub use reddsa;
|
||||||
pub use redjubjub;
|
pub use redjubjub;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
//! `zcash_address` conversion to `zebra_chain` address types.
|
||||||
|
//!
|
||||||
|
//! Usage: <https://docs.rs/zcash_address/0.2.0/zcash_address/trait.TryFromAddress.html#examples>
|
||||||
|
|
||||||
|
use zcash_primitives::sapling;
|
||||||
|
|
||||||
|
use crate::{orchard, parameters::Network, transparent, BoxError};
|
||||||
|
|
||||||
|
/// Zcash address variants
|
||||||
|
// TODO: Add Sprout addresses
|
||||||
|
pub enum Address {
|
||||||
|
/// Transparent address
|
||||||
|
Transparent(transparent::Address),
|
||||||
|
|
||||||
|
/// Sapling address
|
||||||
|
Sapling {
|
||||||
|
/// Address' network
|
||||||
|
network: Network,
|
||||||
|
|
||||||
|
/// Sapling address
|
||||||
|
address: sapling::PaymentAddress,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Unified address
|
||||||
|
Unified {
|
||||||
|
/// Address' network
|
||||||
|
network: Network,
|
||||||
|
|
||||||
|
/// Transparent address
|
||||||
|
transparent_address: transparent::Address,
|
||||||
|
|
||||||
|
/// Sapling address
|
||||||
|
sapling_address: sapling::PaymentAddress,
|
||||||
|
|
||||||
|
/// Orchard address
|
||||||
|
orchard_address: orchard::Address,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<zcash_address::Network> for Network {
|
||||||
|
// TODO: better error type
|
||||||
|
type Error = BoxError;
|
||||||
|
|
||||||
|
fn try_from(network: zcash_address::Network) -> Result<Self, Self::Error> {
|
||||||
|
match network {
|
||||||
|
zcash_address::Network::Main => Ok(Network::Mainnet),
|
||||||
|
zcash_address::Network::Test => Ok(Network::Testnet),
|
||||||
|
zcash_address::Network::Regtest => Err("unsupported Zcash network parameters".into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Network> for zcash_address::Network {
|
||||||
|
fn from(network: Network) -> Self {
|
||||||
|
match network {
|
||||||
|
Network::Mainnet => zcash_address::Network::Main,
|
||||||
|
Network::Testnet => zcash_address::Network::Test,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl zcash_address::TryFromAddress for Address {
|
||||||
|
// TODO: crate::serialization::SerializationError
|
||||||
|
type Error = BoxError;
|
||||||
|
|
||||||
|
fn try_from_transparent_p2pkh(
|
||||||
|
network: zcash_address::Network,
|
||||||
|
data: [u8; 20],
|
||||||
|
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
|
||||||
|
Ok(Self::Transparent(transparent::Address::from_pub_key_hash(
|
||||||
|
network.try_into()?,
|
||||||
|
data,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_transparent_p2sh(
|
||||||
|
network: zcash_address::Network,
|
||||||
|
data: [u8; 20],
|
||||||
|
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
|
||||||
|
Ok(Self::Transparent(transparent::Address::from_script_hash(
|
||||||
|
network.try_into()?,
|
||||||
|
data,
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_from_sapling(
|
||||||
|
network: zcash_address::Network,
|
||||||
|
data: [u8; 43],
|
||||||
|
) -> Result<Self, zcash_address::ConversionError<Self::Error>> {
|
||||||
|
let network = network.try_into()?;
|
||||||
|
sapling::PaymentAddress::from_bytes(&data)
|
||||||
|
.map(|address| Self::Sapling { address, network })
|
||||||
|
.ok_or_else(|| BoxError::from("not a valid sapling address").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add sprout and unified/orchard converters
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Address {
|
||||||
|
/// Returns the network for the address.
|
||||||
|
pub fn network(&self) -> Network {
|
||||||
|
match &self {
|
||||||
|
Self::Transparent(address) => address.network(),
|
||||||
|
Self::Sapling { network, .. } | Self::Unified { network, .. } => *network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the address is PayToScriptHash
|
||||||
|
/// Returns false if the address is PayToPublicKeyHash or shielded.
|
||||||
|
pub fn is_script_hash(&self) -> bool {
|
||||||
|
match &self {
|
||||||
|
Self::Transparent(address) => address.is_script_hash(),
|
||||||
|
Self::Sapling { .. } | Self::Unified { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if address is of the [`Address::Transparent`] variant.
|
||||||
|
/// Returns false if otherwise.
|
||||||
|
pub fn is_transparent(&self) -> bool {
|
||||||
|
matches!(self, Self::Transparent(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ default = []
|
||||||
# Experimental mining RPC support
|
# Experimental mining RPC support
|
||||||
getblocktemplate-rpcs = [
|
getblocktemplate-rpcs = [
|
||||||
"rand",
|
"rand",
|
||||||
|
"zcash_address",
|
||||||
"zebra-consensus/getblocktemplate-rpcs",
|
"zebra-consensus/getblocktemplate-rpcs",
|
||||||
"zebra-state/getblocktemplate-rpcs",
|
"zebra-state/getblocktemplate-rpcs",
|
||||||
"zebra-node-services/getblocktemplate-rpcs",
|
"zebra-node-services/getblocktemplate-rpcs",
|
||||||
|
|
@ -58,6 +59,8 @@ serde = { version = "1.0.152", features = ["serde_derive"] }
|
||||||
|
|
||||||
# Experimental feature getblocktemplate-rpcs
|
# Experimental feature getblocktemplate-rpcs
|
||||||
rand = { version = "0.8.5", package = "rand", optional = true }
|
rand = { version = "0.8.5", package = "rand", optional = true }
|
||||||
|
# ECC deps used by getblocktemplate-rpcs feature
|
||||||
|
zcash_address = { version = "0.2.0", optional = true }
|
||||||
|
|
||||||
# Test-only feature proptest-impl
|
# Test-only feature proptest-impl
|
||||||
proptest = { version = "0.10.1", optional = true }
|
proptest = { version = "0.10.1", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,15 @@ use jsonrpc_core::{self, BoxFuture, Error, ErrorCode, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use tower::{buffer::Buffer, Service, ServiceExt};
|
use tower::{buffer::Buffer, Service, ServiceExt};
|
||||||
|
|
||||||
|
use zcash_address;
|
||||||
|
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
amount::Amount,
|
amount::Amount,
|
||||||
block::{self, Block, Height},
|
block::{self, Block, Height},
|
||||||
chain_sync_status::ChainSyncStatus,
|
chain_sync_status::ChainSyncStatus,
|
||||||
chain_tip::ChainTip,
|
chain_tip::ChainTip,
|
||||||
parameters::Network,
|
parameters::Network,
|
||||||
|
primitives,
|
||||||
serialization::ZcashDeserializeInto,
|
serialization::ZcashDeserializeInto,
|
||||||
transparent,
|
transparent,
|
||||||
};
|
};
|
||||||
|
|
@ -43,6 +46,7 @@ use crate::methods::{
|
||||||
peer_info::PeerInfo,
|
peer_info::PeerInfo,
|
||||||
submit_block,
|
submit_block,
|
||||||
subsidy::{BlockSubsidy, FundingStream},
|
subsidy::{BlockSubsidy, FundingStream},
|
||||||
|
validate_address,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
height_from_signed_int, GetBlockHash, MISSING_BLOCK_ERROR_CODE,
|
height_from_signed_int, GetBlockHash, MISSING_BLOCK_ERROR_CODE,
|
||||||
|
|
@ -167,6 +171,13 @@ pub trait GetBlockTemplateRpc {
|
||||||
#[rpc(name = "getpeerinfo")]
|
#[rpc(name = "getpeerinfo")]
|
||||||
fn get_peer_info(&self) -> BoxFuture<Result<Vec<PeerInfo>>>;
|
fn get_peer_info(&self) -> BoxFuture<Result<Vec<PeerInfo>>>;
|
||||||
|
|
||||||
|
/// Checks if a zcash address is valid.
|
||||||
|
/// Returns information about the given address if valid.
|
||||||
|
///
|
||||||
|
/// zcashd reference: [`validateaddress`](https://zcash.github.io/rpc/validateaddress.html)
|
||||||
|
#[rpc(name = "validateaddress")]
|
||||||
|
fn validate_address(&self, address: String) -> BoxFuture<Result<validate_address::Response>>;
|
||||||
|
|
||||||
/// Returns the block subsidy reward of the block at `height`, taking into account the mining slow start.
|
/// Returns the block subsidy reward of the block at `height`, taking into account the mining slow start.
|
||||||
/// Returns an error if `height` is less than the height of the first halving for the current network.
|
/// Returns an error if `height` is less than the height of the first halving for the current network.
|
||||||
///
|
///
|
||||||
|
|
@ -788,6 +799,51 @@ where
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_address(
|
||||||
|
&self,
|
||||||
|
raw_address: String,
|
||||||
|
) -> BoxFuture<Result<validate_address::Response>> {
|
||||||
|
let network = self.network;
|
||||||
|
|
||||||
|
async move {
|
||||||
|
let Ok(address) = raw_address
|
||||||
|
.parse::<zcash_address::ZcashAddress>() else {
|
||||||
|
return Ok(validate_address::Response::invalid());
|
||||||
|
};
|
||||||
|
|
||||||
|
let address = match address
|
||||||
|
.convert::<primitives::Address>() {
|
||||||
|
Ok(address) => address,
|
||||||
|
Err(err) => {
|
||||||
|
tracing::debug!(?err, "conversion error");
|
||||||
|
return Ok(validate_address::Response::invalid());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// we want to match zcashd's behaviour
|
||||||
|
if !address.is_transparent() {
|
||||||
|
return Ok(validate_address::Response::invalid());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(if address.network() == network {
|
||||||
|
validate_address::Response {
|
||||||
|
address: Some(raw_address),
|
||||||
|
is_valid: true,
|
||||||
|
is_script: Some(address.is_script_hash()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tracing::info!(
|
||||||
|
?network,
|
||||||
|
address_network = ?address.network(),
|
||||||
|
"invalid address in validateaddress RPC: Zebra's configured network must match address network"
|
||||||
|
);
|
||||||
|
|
||||||
|
validate_address::Response::invalid()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_block_subsidy(&self, height: Option<u32>) -> BoxFuture<Result<BlockSubsidy>> {
|
fn get_block_subsidy(&self, height: Option<u32>) -> BoxFuture<Result<BlockSubsidy>> {
|
||||||
let latest_chain_tip = self.latest_chain_tip.clone();
|
let latest_chain_tip = self.latest_chain_tip.clone();
|
||||||
let network = self.network;
|
let network = self.network;
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,5 @@ pub mod peer_info;
|
||||||
pub mod submit_block;
|
pub mod submit_block;
|
||||||
pub mod subsidy;
|
pub mod subsidy;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
pub mod validate_address;
|
||||||
pub mod zec;
|
pub mod zec;
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
//! for the `submitblock` RPC method.
|
//! for the `submitblock` RPC method.
|
||||||
|
|
||||||
/// Deserialize hex-encoded strings to bytes.
|
/// Deserialize hex-encoded strings to bytes.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct HexData(#[serde(with = "hex")] pub Vec<u8>);
|
pub struct HexData(#[serde(with = "hex")] pub Vec<u8>);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
//! Response type for the `validateaddress` RPC.
|
||||||
|
|
||||||
|
/// `validateaddress` response
|
||||||
|
#[derive(Clone, Default, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Response {
|
||||||
|
/// Whether the address is valid.
|
||||||
|
///
|
||||||
|
/// If not, this is the only property returned.
|
||||||
|
#[serde(rename = "isvalid")]
|
||||||
|
pub is_valid: bool,
|
||||||
|
|
||||||
|
/// The zcash address that has been validated.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub address: Option<String>,
|
||||||
|
|
||||||
|
/// If the key is a script.
|
||||||
|
#[serde(rename = "isscript")]
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub is_script: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
/// Creates an empty response with `isvalid` of false.
|
||||||
|
pub fn invalid() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,7 @@ use crate::methods::{
|
||||||
peer_info::PeerInfo,
|
peer_info::PeerInfo,
|
||||||
submit_block,
|
submit_block,
|
||||||
subsidy::BlockSubsidy,
|
subsidy::BlockSubsidy,
|
||||||
|
validate_address,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tests::utils::fake_history_tree,
|
tests::utils::fake_history_tree,
|
||||||
|
|
@ -371,6 +372,24 @@ pub async fn test_responses<State, ReadState>(
|
||||||
.expect("unexpected error in submitblock RPC call");
|
.expect("unexpected error in submitblock RPC call");
|
||||||
|
|
||||||
snapshot_rpc_submit_block_invalid(submit_block, &settings);
|
snapshot_rpc_submit_block_invalid(submit_block, &settings);
|
||||||
|
|
||||||
|
// `validateaddress`
|
||||||
|
let founder_address = match network {
|
||||||
|
Network::Mainnet => "t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR",
|
||||||
|
Network::Testnet => "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi",
|
||||||
|
};
|
||||||
|
|
||||||
|
let validate_address = get_block_template_rpc
|
||||||
|
.validate_address(founder_address.to_string())
|
||||||
|
.await
|
||||||
|
.expect("We should have a validate_address::Response");
|
||||||
|
snapshot_rpc_validateaddress("basic", validate_address, &settings);
|
||||||
|
|
||||||
|
let validate_address = get_block_template_rpc
|
||||||
|
.validate_address("".to_string())
|
||||||
|
.await
|
||||||
|
.expect("We should have a validate_address::Response");
|
||||||
|
snapshot_rpc_validateaddress("invalid", validate_address, &settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Snapshot `getblockcount` response, using `cargo insta` and JSON serialization.
|
/// Snapshot `getblockcount` response, using `cargo insta` and JSON serialization.
|
||||||
|
|
@ -436,3 +455,14 @@ fn snapshot_rpc_getpeerinfo(get_peer_info: Vec<PeerInfo>, settings: &insta::Sett
|
||||||
fn snapshot_rpc_getnetworksolps(get_network_sol_ps: u64, settings: &insta::Settings) {
|
fn snapshot_rpc_getnetworksolps(get_network_sol_ps: u64, settings: &insta::Settings) {
|
||||||
settings.bind(|| insta::assert_json_snapshot!("get_network_sol_ps", get_network_sol_ps));
|
settings.bind(|| insta::assert_json_snapshot!("get_network_sol_ps", get_network_sol_ps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Snapshot `validateaddress` response, using `cargo insta` and JSON serialization.
|
||||||
|
fn snapshot_rpc_validateaddress(
|
||||||
|
variant: &'static str,
|
||||||
|
validate_address: validate_address::Response,
|
||||||
|
settings: &insta::Settings,
|
||||||
|
) {
|
||||||
|
settings.bind(|| {
|
||||||
|
insta::assert_json_snapshot!(format!("validate_address_{variant}"), validate_address)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
|
expression: validate_address
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"isvalid": true,
|
||||||
|
"address": "t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR",
|
||||||
|
"isscript": true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
|
expression: validate_address
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"isvalid": true,
|
||||||
|
"address": "t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi",
|
||||||
|
"isscript": true
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
|
expression: validate_address
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"isvalid": false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
source: zebra-rpc/src/methods/tests/snapshot/get_block_template_rpcs.rs
|
||||||
|
expression: validate_address
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"isvalid": false
|
||||||
|
}
|
||||||
|
|
@ -1290,3 +1290,48 @@ async fn rpc_submitblock_errors() {
|
||||||
|
|
||||||
// See zebrad::tests::acceptance::submit_block for success case.
|
// See zebrad::tests::acceptance::submit_block for success case.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn rpc_validateaddress() {
|
||||||
|
use get_block_template_rpcs::types::validate_address;
|
||||||
|
use zebra_chain::{chain_sync_status::MockSyncStatus, chain_tip::mock::MockChainTip};
|
||||||
|
use zebra_network::address_book_peers::MockAddressBookPeers;
|
||||||
|
|
||||||
|
let _init_guard = zebra_test::init();
|
||||||
|
|
||||||
|
let (mock_chain_tip, _mock_chain_tip_sender) = MockChainTip::new();
|
||||||
|
|
||||||
|
// Init RPC
|
||||||
|
let get_block_template_rpc = get_block_template_rpcs::GetBlockTemplateRpcImpl::new(
|
||||||
|
Mainnet,
|
||||||
|
Default::default(),
|
||||||
|
Buffer::new(MockService::build().for_unit_tests(), 1),
|
||||||
|
MockService::build().for_unit_tests(),
|
||||||
|
mock_chain_tip,
|
||||||
|
MockService::build().for_unit_tests(),
|
||||||
|
MockSyncStatus::default(),
|
||||||
|
MockAddressBookPeers::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let validate_address = get_block_template_rpc
|
||||||
|
.validate_address("t3fqvkzrrNaMcamkQMwAyHRjfDdM2xQvDTR".to_string())
|
||||||
|
.await
|
||||||
|
.expect("we should have a validate_address::Response");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
validate_address.is_valid,
|
||||||
|
"Mainnet founder address should be valid on Mainnet"
|
||||||
|
);
|
||||||
|
|
||||||
|
let validate_address = get_block_template_rpc
|
||||||
|
.validate_address("t2UNzUUx8mWBCRYPRezvA363EYXyEpHokyi".to_string())
|
||||||
|
.await
|
||||||
|
.expect("We should have a validate_address::Response");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
validate_address,
|
||||||
|
validate_address::Response::invalid(),
|
||||||
|
"Testnet founder address should be invalid on Mainnet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue