fix(consensus): Check network and P2SH addresses for mining config and funding streams (#5620)

* Check network and P2SH addresses for mining config and funding streams

* Inline format string variables
This commit is contained in:
teor 2022-11-11 13:42:05 +10:00 committed by GitHub
parent beb45fc514
commit 67b7325582
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 20 deletions

View File

@ -223,6 +223,11 @@ impl Address {
} }
} }
/// Returns `true` if the address is `PayToScriptHash`, and `false` if it is `PayToPublicKeyHash`.
pub fn is_script_hash(&self) -> bool {
matches!(self, Address::PayToScriptHash { .. })
}
/// Returns the hash bytes for this address, regardless of the address type. /// Returns the hash bytes for this address, regardless of the address type.
/// ///
/// # Correctness /// # Correctness

View File

@ -116,35 +116,34 @@ pub fn funding_stream_address(
.expect("there is always another hash map as value for a given valid network") .expect("there is always another hash map as value for a given valid network")
.get(&receiver) .get(&receiver)
.expect("in the inner hash map there is always a vector of strings with addresses")[index]; .expect("in the inner hash map there is always a vector of strings with addresses")[index];
Address::from_str(address).expect("Address should deserialize") Address::from_str(address).expect("address should deserialize")
} }
/// Given a funding stream address, create a script and check if it is the same /// Given a funding stream P2SH address, create a script and check if it is the same
/// as the given lock_script as described in [protocol specification §7.10][7.10] /// as the given lock_script as described in [protocol specification §7.10][7.10]
/// ///
/// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams /// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
pub fn check_script_form(lock_script: &Script, address: Address) -> bool { pub fn check_script_form(lock_script: &Script, address: Address) -> bool {
// TODO: Verify P2SH multisig funding stream addresses (#5577). assert!(
// As of NU5, the funding streams do not use multisig addresses, address.is_script_hash(),
// so this is optional. "incorrect funding stream address constant: {address} \
// Zcash only supports transparent 'pay to script hash' (P2SH) addresses",
// # Consensus );
//
// > The standard redeem script hash is specified in [Bitcoin-Multisig] for P2SH multisig
// > addresses...
// [protocol specification §7.10][7.10]
//
// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
// [Bitcoin-Multisig]: https://developer.bitcoin.org/ devguide/transactions.html#multisig
// Verify a Bitcoin P2SH single address. // Verify a Bitcoin P2SH single or multisig address.
let standard_script_hash = new_coinbase_script(address); let standard_script_hash = new_coinbase_script(address);
lock_script == &standard_script_hash lock_script == &standard_script_hash
} }
/// Returns a new funding stream coinbase output lock script, which pays to `address`. /// Returns a new funding stream coinbase output lock script, which pays to the P2SH `address`.
pub fn new_coinbase_script(address: Address) -> Script { pub fn new_coinbase_script(address: Address) -> Script {
assert!(
address.is_script_hash(),
"incorrect coinbase script address: {address} \
Zebra only supports transparent 'pay to script hash' (P2SH) addresses",
);
let address_hash = address let address_hash = address
.zcash_serialize_to_vec() .zcash_serialize_to_vec()
.expect("we should get address bytes here"); .expect("we should get address bytes here");

View File

@ -1,9 +1,11 @@
use super::*; //! Tests for funding streams.
use color_eyre::Report;
use std::convert::TryFrom;
use color_eyre::Report;
use super::*;
/// Check mainnet funding stream values are correct for the entire period.
#[test] #[test]
// Check funding streams are correct in the entire period.
fn test_funding_stream_values() -> Result<(), Report> { fn test_funding_stream_values() -> Result<(), Report> {
let _init_guard = zebra_test::init(); let _init_guard = zebra_test::init();
let network = Network::Mainnet; let network = Network::Mainnet;
@ -54,3 +56,27 @@ fn test_funding_stream_values() -> Result<(), Report> {
Ok(()) Ok(())
} }
/// Check mainnet and testnet funding stream addresses are valid transparent P2SH addresses.
#[test]
fn test_funding_stream_addresses() -> Result<(), Report> {
let _init_guard = zebra_test::init();
for (network, receivers) in FUNDING_STREAM_ADDRESSES.iter() {
for (receiver, addresses) in receivers {
for address in addresses {
let address = Address::from_str(address).expect("address should deserialize");
assert_eq!(
&address.network(),
network,
"incorrect network for {receiver:?} funding stream address constant: {address}",
);
// Asserts if address is not a P2SH address.
let _script = new_coinbase_script(address);
}
}
}
Ok(())
}

View File

@ -120,6 +120,22 @@ impl RpcServer {
#[cfg(feature = "getblocktemplate-rpcs")] #[cfg(feature = "getblocktemplate-rpcs")]
{ {
// Prevent loss of miner funds due to an unsupported or incorrect address type.
if let Some(miner_address) = mining_config.miner_address {
assert_eq!(
miner_address.network(),
network,
"incorrect miner address config: {miner_address} \
network.network {network} and miner address network {} must match",
miner_address.network(),
);
assert!(
miner_address.is_script_hash(),
"incorrect miner address config: {miner_address} \
Zebra only supports transparent 'pay to script hash' (P2SH) addresses",
);
}
// Initialize the getblocktemplate rpc method handler // Initialize the getblocktemplate rpc method handler
let get_block_template_rpc_impl = GetBlockTemplateRpcImpl::new( let get_block_template_rpc_impl = GetBlockTemplateRpcImpl::new(
network, network,