change(rpc): support transparent p2pkh miner addresses (#5827)
* support p2pkh miner address * Tweak comments and log messages * removes duplicate/unused method Co-authored-by: teor <teor@riseup.net> Co-authored-by: Arya <aryasolhi@gmail.com>
This commit is contained in:
parent
5b054c968e
commit
12ff32f445
|
@ -2,6 +2,7 @@
|
|||
|
||||
mod address;
|
||||
mod keys;
|
||||
mod opcodes;
|
||||
mod script;
|
||||
mod serialize;
|
||||
mod utxo;
|
||||
|
|
|
@ -10,7 +10,7 @@ use sha2::Sha256;
|
|||
use crate::{
|
||||
parameters::Network,
|
||||
serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
|
||||
transparent::Script,
|
||||
transparent::{opcodes::OpCode, Script},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -254,6 +254,33 @@ impl Address {
|
|||
payload[..].copy_from_slice(&ripe_hash[..]);
|
||||
payload
|
||||
}
|
||||
|
||||
/// Given a transparent address (P2SH or a P2PKH), create a script that can be used in a coinbase
|
||||
/// transaction output.
|
||||
pub fn create_script_from_address(&self) -> Script {
|
||||
let mut script_bytes = Vec::new();
|
||||
|
||||
match self {
|
||||
// https://developer.bitcoin.org/devguide/transactions.html#pay-to-script-hash-p2sh
|
||||
Address::PayToScriptHash { .. } => {
|
||||
script_bytes.push(OpCode::Hash160 as u8);
|
||||
script_bytes.push(OpCode::Push20Bytes as u8);
|
||||
script_bytes.extend(self.hash_bytes());
|
||||
script_bytes.push(OpCode::Equal as u8);
|
||||
}
|
||||
// https://developer.bitcoin.org/devguide/transactions.html#pay-to-public-key-hash-p2pkh
|
||||
Address::PayToPublicKeyHash { .. } => {
|
||||
script_bytes.push(OpCode::Dup as u8);
|
||||
script_bytes.push(OpCode::Hash160 as u8);
|
||||
script_bytes.push(OpCode::Push20Bytes as u8);
|
||||
script_bytes.extend(self.hash_bytes());
|
||||
script_bytes.push(OpCode::EqualVerify as u8);
|
||||
script_bytes.push(OpCode::CheckSig as u8);
|
||||
}
|
||||
};
|
||||
|
||||
Script::new(&script_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
//! Zebra script opcodes.
|
||||
|
||||
/// Supported opcodes
|
||||
///
|
||||
/// <https://github.com/zcash/zcash/blob/8b16094f6672d8268ff25b2d7bddd6a6207873f7/src/script/script.h#L39>
|
||||
pub enum OpCode {
|
||||
// Opcodes used to generate P2SH scripts.
|
||||
Equal = 0x87,
|
||||
Hash160 = 0xa9,
|
||||
Push20Bytes = 0x14,
|
||||
// Additional opcodes used to generate P2PKH scripts.
|
||||
Dup = 0x76,
|
||||
EqualVerify = 0x88,
|
||||
CheckSig = 0xac,
|
||||
}
|
|
@ -6,7 +6,6 @@ use zebra_chain::{
|
|||
amount::{Amount, Error, NonNegative},
|
||||
block::Height,
|
||||
parameters::{Network, NetworkUpgrade::*},
|
||||
serialization::ZcashSerialize,
|
||||
transaction::Transaction,
|
||||
transparent::{Address, Output, Script},
|
||||
};
|
||||
|
@ -141,25 +140,14 @@ 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",
|
||||
Funding streams only support transparent 'pay to script hash' (P2SH) addresses",
|
||||
);
|
||||
|
||||
let address_hash = address
|
||||
.zcash_serialize_to_vec()
|
||||
.expect("we should get address bytes here");
|
||||
|
||||
// > The “prescribed way” to pay a transparent P2SH address is to use a standard P2SH script
|
||||
// > of the form OP_HASH160 fs.RedeemScriptHash(height) OP_EQUAL as the scriptPubKey.
|
||||
//
|
||||
// [7.10]: https://zips.z.cash/protocol/protocol.pdf#fundingstreams
|
||||
let mut script_bytes = Vec::new();
|
||||
|
||||
script_bytes.push(OpCode::Hash160 as u8);
|
||||
script_bytes.push(OpCode::Push20Bytes as u8);
|
||||
script_bytes.extend(&address_hash[2..22]);
|
||||
script_bytes.push(OpCode::Equal as u8);
|
||||
|
||||
Script::new(&script_bytes)
|
||||
address.create_script_from_address()
|
||||
}
|
||||
|
||||
/// Returns a list of outputs in `Transaction`, which have a script address equal to `Address`.
|
||||
|
@ -171,10 +159,3 @@ pub fn filter_outputs_by_address(transaction: &Transaction, address: Address) ->
|
|||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Script opcodes needed to compare the `lock_script` with the funding stream reward address.
|
||||
pub enum OpCode {
|
||||
Equal = 0x87,
|
||||
Hash160 = 0xa9,
|
||||
Push20Bytes = 0x14,
|
||||
}
|
||||
|
|
|
@ -21,10 +21,12 @@ use zebra_chain::{
|
|||
transaction::{Transaction, UnminedTx, VerifiedUnminedTx},
|
||||
transparent,
|
||||
};
|
||||
|
||||
use zebra_consensus::{
|
||||
funding_stream_address, funding_stream_values, miner_subsidy, new_coinbase_script,
|
||||
VerifyChainError, MAX_BLOCK_SIGOPS,
|
||||
funding_stream_address, funding_stream_values, miner_subsidy, VerifyChainError,
|
||||
MAX_BLOCK_SIGOPS,
|
||||
};
|
||||
|
||||
use zebra_node_services::mempool;
|
||||
|
||||
use zebra_state::{ReadRequest, ReadResponse};
|
||||
|
@ -659,7 +661,7 @@ pub fn standard_coinbase_outputs(
|
|||
|
||||
coinbase_outputs
|
||||
.iter()
|
||||
.map(|(amount, address)| (*amount, new_coinbase_script(*address)))
|
||||
.map(|(amount, address)| (*amount, address.create_script_from_address()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use zebra_chain::transparent;
|
|||
#[serde(deny_unknown_fields, default)]
|
||||
pub struct Config {
|
||||
/// The address used for miner payouts.
|
||||
/// Zebra currently only supports single-signature P2SH transparent addresses.
|
||||
/// Zebra currently only supports P2SH and P2PKH transparent addresses.
|
||||
///
|
||||
/// Zebra sends mining fees and miner rewards to this address in the
|
||||
/// `getblocktemplate` RPC coinbase transaction.
|
||||
|
|
|
@ -877,12 +877,20 @@ async fn rpc_getnetworksolps() {
|
|||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn rpc_getblocktemplate() {
|
||||
// test getblocktemplate with a miner P2SH address
|
||||
rpc_getblocktemplate_mining_address(true).await;
|
||||
// test getblocktemplate with a miner P2PKH address
|
||||
rpc_getblocktemplate_mining_address(false).await;
|
||||
}
|
||||
|
||||
#[cfg(feature = "getblocktemplate-rpcs")]
|
||||
async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) {
|
||||
use std::panic;
|
||||
|
||||
use chrono::{TimeZone, Utc};
|
||||
use chrono::TimeZone;
|
||||
|
||||
use zebra_chain::{
|
||||
amount::{Amount, NonNegative},
|
||||
amount::NonNegative,
|
||||
block::{Hash, MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION},
|
||||
chain_tip::mock::MockChainTip,
|
||||
work::difficulty::{CompactDifficulty, ExpandedDifficulty, U256},
|
||||
|
@ -911,10 +919,13 @@ async fn rpc_getblocktemplate() {
|
|||
let mut mock_sync_status = MockSyncStatus::default();
|
||||
mock_sync_status.set_is_close_to_tip(true);
|
||||
|
||||
let mining_config = get_block_template_rpcs::config::Config {
|
||||
miner_address: Some(transparent::Address::from_script_hash(Mainnet, [0x7e; 20])),
|
||||
let miner_address = match use_p2pkh {
|
||||
false => Some(transparent::Address::from_script_hash(Mainnet, [0x7e; 20])),
|
||||
true => Some(transparent::Address::from_pub_key_hash(Mainnet, [0x7e; 20])),
|
||||
};
|
||||
|
||||
let mining_config = get_block_template_rpcs::config::Config { miner_address };
|
||||
|
||||
// nu5 block height
|
||||
let fake_tip_height = NetworkUpgrade::Nu5.activation_height(Mainnet).unwrap();
|
||||
// nu5 block hash
|
||||
|
@ -1007,8 +1018,13 @@ async fn rpc_getblocktemplate() {
|
|||
assert!(get_block_template.coinbase_txn.required);
|
||||
assert!(!get_block_template.coinbase_txn.data.as_ref().is_empty());
|
||||
assert_eq!(get_block_template.coinbase_txn.depends.len(), 0);
|
||||
// TODO: should a coinbase transaction have sigops?
|
||||
if use_p2pkh {
|
||||
// there is one sig operation if miner address is p2pkh.
|
||||
assert_eq!(get_block_template.coinbase_txn.sigops, 1);
|
||||
} else {
|
||||
// everything in the coinbase is p2sh.
|
||||
assert_eq!(get_block_template.coinbase_txn.sigops, 0);
|
||||
}
|
||||
// Coinbase transaction checks for empty blocks.
|
||||
assert_eq!(
|
||||
get_block_template.coinbase_txn.fee,
|
||||
|
|
|
@ -133,11 +133,6 @@ impl RpcServer {
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue